From 6860098ed8f8b56eb5058e0c9c427abaa57021bf Mon Sep 17 00:00:00 2001 From: joe Date: Mon, 25 Apr 2016 20:20:45 -0400 Subject: more work on cokiki (ssh-client) --- lib/LengthPrefixedBE.hs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 lib/LengthPrefixedBE.hs (limited to 'lib/LengthPrefixedBE.hs') 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 @@ +module LengthPrefixedBE + ( LengthPrefixedBE(..) + , encode_bigendian + , decode_bigendian + ) where + +import qualified Data.ByteString.Lazy as L +import Data.Bits +import Data.Binary +import Data.Binary.Get +import Data.Binary.Put (putWord32be, putLazyByteString) +import Data.Int + +{- + From RFC4251... + + string + + Arbitrary length binary string. Strings are allowed to contain + arbitrary binary data, including null characters and 8-bit + characters. They are stored as a uint32 containing its length + (number of bytes that follow) and zero (= empty string) or more + bytes that are the value of the string. Terminating null + characters are not used. + + mpint ( LengthPrefixedBE ) + + Represents multiple precision integers in two's complement format, + stored as a string, 8 bits per byte, MSB first. Negative numbers + have the value 1 as the most significant bit of the first byte of + the data partition. If the most significant bit would be set for + a positive number, the number MUST be preceded by a zero byte. + Unnecessary leading bytes with the value 0 or 255 MUST NOT be + included. The value zero MUST be stored as a string with zero + bytes of data. +-} + +newtype LengthPrefixedBE = LengthPrefixedBE Integer + +instance Binary LengthPrefixedBE where + + put (LengthPrefixedBE n) = do + putWord32be len + putLazyByteString bytes + where + bytes = encode_bigendian n + len = fromIntegral (L.length bytes) :: Word32 + + get = do + len <- get + bs <- getLazyByteString (word32_to_int64 len) + return . LengthPrefixedBE $ decode_bigendian bs + where + word32_to_int64 :: Word32 -> Int64 + word32_to_int64 = fromIntegral + + + +encode_bigendian :: (Integral a, Bits a) => a -> L.ByteString +encode_bigendian n = + if (bit /= sbyte) + then sbyte `L.cons` bytes + else bytes + where + bytes = L.reverse $ unroll n + sbyte :: Word8 + sbyte = if n<0 then 0xFF else 0 + bit = if L.null bytes + then 0x00 + else fromIntegral ((fromIntegral (L.head bytes) :: Int8) `shiftR` 7) + + unroll :: (Integral a, Bits a) => a -> L.ByteString + unroll = L.unfoldr step + -- TODO: Is reversing L.unfoldr more or less efficient + -- than using Data.List.unfoldr ? + -- Probably Data.ByteString.Lazy should export an unfoldrEnd + -- function that efficiently unfolds reversed bytestrings. + where + step 0 = Nothing + step (-1) = Nothing + step i = Just (fromIntegral i, i `shiftR` 8) + +decode_bigendian :: (Num a, Bits a) => L.ByteString -> a +decode_bigendian bs = if isneg then n - 256^(L.length bs) + else n + where + n = L.foldl (\a b -> a `shiftL` 8 .|. fromIntegral b) 0 bs + isneg = not (L.null bs) && L.head bs .&. 0x80 /= 0 + + -- cgit v1.2.3