diff options
author | joe <joe@jerkface.net> | 2016-04-25 20:20:45 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2016-04-25 20:20:45 -0400 |
commit | 6860098ed8f8b56eb5058e0c9c427abaa57021bf (patch) | |
tree | defc0ae2c6bcd08f489628be0633f99e6254a218 /lib/LengthPrefixedBE.hs | |
parent | 3c8536fd92043283d20b9e19ae488e7fe64af236 (diff) |
more work on cokiki (ssh-client)
Diffstat (limited to 'lib/LengthPrefixedBE.hs')
-rw-r--r-- | lib/LengthPrefixedBE.hs | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/lib/LengthPrefixedBE.hs b/lib/LengthPrefixedBE.hs new file mode 100644 index 0000000..0ccd0e2 --- /dev/null +++ b/lib/LengthPrefixedBE.hs | |||
@@ -0,0 +1,90 @@ | |||
1 | module LengthPrefixedBE | ||
2 | ( LengthPrefixedBE(..) | ||
3 | , encode_bigendian | ||
4 | , decode_bigendian | ||
5 | ) where | ||
6 | |||
7 | import qualified Data.ByteString.Lazy as L | ||
8 | import Data.Bits | ||
9 | import Data.Binary | ||
10 | import Data.Binary.Get | ||
11 | import Data.Binary.Put (putWord32be, putLazyByteString) | ||
12 | import Data.Int | ||
13 | |||
14 | {- | ||
15 | From RFC4251... | ||
16 | |||
17 | string | ||
18 | |||
19 | Arbitrary length binary string. Strings are allowed to contain | ||
20 | arbitrary binary data, including null characters and 8-bit | ||
21 | characters. They are stored as a uint32 containing its length | ||
22 | (number of bytes that follow) and zero (= empty string) or more | ||
23 | bytes that are the value of the string. Terminating null | ||
24 | characters are not used. | ||
25 | |||
26 | mpint ( LengthPrefixedBE ) | ||
27 | |||
28 | Represents multiple precision integers in two's complement format, | ||
29 | stored as a string, 8 bits per byte, MSB first. Negative numbers | ||
30 | have the value 1 as the most significant bit of the first byte of | ||
31 | the data partition. If the most significant bit would be set for | ||
32 | a positive number, the number MUST be preceded by a zero byte. | ||
33 | Unnecessary leading bytes with the value 0 or 255 MUST NOT be | ||
34 | included. The value zero MUST be stored as a string with zero | ||
35 | bytes of data. | ||
36 | -} | ||
37 | |||
38 | newtype LengthPrefixedBE = LengthPrefixedBE Integer | ||
39 | |||
40 | instance Binary LengthPrefixedBE where | ||
41 | |||
42 | put (LengthPrefixedBE n) = do | ||
43 | putWord32be len | ||
44 | putLazyByteString bytes | ||
45 | where | ||
46 | bytes = encode_bigendian n | ||
47 | len = fromIntegral (L.length bytes) :: Word32 | ||
48 | |||
49 | get = do | ||
50 | len <- get | ||
51 | bs <- getLazyByteString (word32_to_int64 len) | ||
52 | return . LengthPrefixedBE $ decode_bigendian bs | ||
53 | where | ||
54 | word32_to_int64 :: Word32 -> Int64 | ||
55 | word32_to_int64 = fromIntegral | ||
56 | |||
57 | |||
58 | |||
59 | encode_bigendian :: (Integral a, Bits a) => a -> L.ByteString | ||
60 | encode_bigendian n = | ||
61 | if (bit /= sbyte) | ||
62 | then sbyte `L.cons` bytes | ||
63 | else bytes | ||
64 | where | ||
65 | bytes = L.reverse $ unroll n | ||
66 | sbyte :: Word8 | ||
67 | sbyte = if n<0 then 0xFF else 0 | ||
68 | bit = if L.null bytes | ||
69 | then 0x00 | ||
70 | else fromIntegral ((fromIntegral (L.head bytes) :: Int8) `shiftR` 7) | ||
71 | |||
72 | unroll :: (Integral a, Bits a) => a -> L.ByteString | ||
73 | unroll = L.unfoldr step | ||
74 | -- TODO: Is reversing L.unfoldr more or less efficient | ||
75 | -- than using Data.List.unfoldr ? | ||
76 | -- Probably Data.ByteString.Lazy should export an unfoldrEnd | ||
77 | -- function that efficiently unfolds reversed bytestrings. | ||
78 | where | ||
79 | step 0 = Nothing | ||
80 | step (-1) = Nothing | ||
81 | step i = Just (fromIntegral i, i `shiftR` 8) | ||
82 | |||
83 | decode_bigendian :: (Num a, Bits a) => L.ByteString -> a | ||
84 | decode_bigendian bs = if isneg then n - 256^(L.length bs) | ||
85 | else n | ||
86 | where | ||
87 | n = L.foldl (\a b -> a `shiftL` 8 .|. fromIntegral b) 0 bs | ||
88 | isneg = not (L.null bs) && L.head bs .&. 0x80 /= 0 | ||
89 | |||
90 | |||