summaryrefslogtreecommitdiff
path: root/lib/LengthPrefixedBE.hs
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2016-04-25 20:20:45 -0400
committerjoe <joe@jerkface.net>2016-04-25 20:20:45 -0400
commit6860098ed8f8b56eb5058e0c9c427abaa57021bf (patch)
treedefc0ae2c6bcd08f489628be0633f99e6254a218 /lib/LengthPrefixedBE.hs
parent3c8536fd92043283d20b9e19ae488e7fe64af236 (diff)
more work on cokiki (ssh-client)
Diffstat (limited to 'lib/LengthPrefixedBE.hs')
-rw-r--r--lib/LengthPrefixedBE.hs90
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 @@
1module LengthPrefixedBE
2 ( LengthPrefixedBE(..)
3 , encode_bigendian
4 , decode_bigendian
5 ) where
6
7import qualified Data.ByteString.Lazy as L
8import Data.Bits
9import Data.Binary
10import Data.Binary.Get
11import Data.Binary.Put (putWord32be, putLazyByteString)
12import 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
38newtype LengthPrefixedBE = LengthPrefixedBE Integer
39
40instance 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
59encode_bigendian :: (Integral a, Bits a) => a -> L.ByteString
60encode_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
83decode_bigendian :: (Num a, Bits a) => L.ByteString -> a
84decode_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