Blame Data/ASN1/Internal.hs

Packit ae7d4f
-- |
Packit ae7d4f
-- Module      : Data.ASN1.Internal
Packit ae7d4f
-- License     : BSD-style
Packit ae7d4f
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
Packit ae7d4f
-- Stability   : experimental
Packit ae7d4f
-- Portability : unknown
Packit ae7d4f
--
Packit ae7d4f
module Data.ASN1.Internal
Packit ae7d4f
    ( uintOfBytes
Packit ae7d4f
    , intOfBytes
Packit ae7d4f
    , bytesOfUInt
Packit ae7d4f
    , bytesOfInt
Packit ae7d4f
    , putVarEncodingIntegral
Packit ae7d4f
    ) where
Packit ae7d4f
Packit ae7d4f
import Data.Word
Packit ae7d4f
import Data.Bits
Packit ae7d4f
import Data.ByteString (ByteString)
Packit ae7d4f
import qualified Data.ByteString as B
Packit ae7d4f
Packit ae7d4f
{- | uintOfBytes returns the number of bytes and the unsigned integer represented by the bytes -}
Packit ae7d4f
uintOfBytes :: ByteString -> (Int, Integer)
Packit ae7d4f
uintOfBytes b = (B.length b, B.foldl (\acc n -> (acc `shiftL` 8) + fromIntegral n) 0 b)
Packit ae7d4f
Packit ae7d4f
--bytesOfUInt i = B.unfoldr (\x -> if x == 0 then Nothing else Just (fromIntegral (x .&. 0xff), x `shiftR` 8)) i
Packit ae7d4f
bytesOfUInt :: Integer -> [Word8]
Packit ae7d4f
bytesOfUInt x = reverse (list x)
Packit ae7d4f
    where list i = if i <= 0xff then [fromIntegral i] else (fromIntegral i .&. 0xff) : list (i `shiftR` 8)
Packit ae7d4f
Packit ae7d4f
{- | intOfBytes returns the number of bytes in the list and
Packit ae7d4f
   the represented integer by a two's completement list of bytes -}
Packit ae7d4f
intOfBytes :: ByteString -> (Int, Integer)
Packit ae7d4f
intOfBytes b
Packit ae7d4f
    | B.length b == 0   = (0, 0)
Packit ae7d4f
    | otherwise         = (len, if isNeg then -(maxIntLen - v + 1) else v)
Packit ae7d4f
    where
Packit ae7d4f
        (len, v)  = uintOfBytes b
Packit ae7d4f
        maxIntLen = 2 ^ (8 * len) - 1
Packit ae7d4f
        isNeg     = testBit (B.head b) 7
Packit ae7d4f
Packit ae7d4f
{- | bytesOfInt convert an integer into a two's completemented list of bytes -}
Packit ae7d4f
bytesOfInt :: Integer -> [Word8]
Packit ae7d4f
bytesOfInt i
Packit ae7d4f
    | i > 0      = if testBit (head uints) 7 then 0 : uints else uints
Packit ae7d4f
    | i == 0     = [0]
Packit ae7d4f
    | otherwise  = if testBit (head nints) 7 then nints else 0xff : nints
Packit ae7d4f
    where
Packit ae7d4f
        uints = bytesOfUInt (abs i)
Packit ae7d4f
        nints = reverse $ plusOne $ reverse $ map complement $ uints
Packit ae7d4f
        plusOne []     = [1]
Packit ae7d4f
        plusOne (x:xs) = if x == 0xff then 0 : plusOne xs else (x+1) : xs
Packit ae7d4f
Packit ae7d4f
{- ASN1 often uses a particular kind of 7-bit encoding of integers like
Packit ae7d4f
   in the case of long tags or encoding of integer component of OID's.
Packit ae7d4f
   Use this function for such an encoding. Assumes a positive integer.
Packit ae7d4f
Packit ae7d4f
   Here is the description of the algorithm of the above encoding:
Packit ae7d4f
Packit ae7d4f
   1. The integer is chunked up into 7-bit groups. Each of these 7bit
Packit ae7d4f
      chunks are encoded as a single octet.
Packit ae7d4f
Packit ae7d4f
   2. All the octets except the last one has its 8th bit set.
Packit ae7d4f
-}
Packit ae7d4f
putVarEncodingIntegral :: (Bits i, Integral i) => i -> ByteString
Packit ae7d4f
putVarEncodingIntegral i = B.reverse $ B.unfoldr genOctets (i,True)
Packit ae7d4f
    where genOctets (x,first)
Packit ae7d4f
            | x > 0     =
Packit ae7d4f
                let out = fromIntegral (x .&. 0x7F) .|. (if first then 0 else 0x80) in
Packit ae7d4f
                Just (out, (shiftR x 7, False))
Packit ae7d4f
            | otherwise = Nothing