module Crypto.Number.Serialize
( i2osp
, os2ip
, i2ospOf
, i2ospOf_
, lengthBytes
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Internal as B
import Data.Bits
import Foreign.Storable
import Foreign.Ptr
divMod256 :: Integer -> (Integer, Integer)
divMod256 n = (n `shiftR` 8, n .&. 0xff)
os2ip :: ByteString -> Integer
os2ip = B.foldl' (\a b -> (256 * a) .|. (fromIntegral b)) 0
i2osp :: Integer -> ByteString
i2osp m
| m < 0 = error "i2osp: cannot convert a negative integer to a bytestring"
| otherwise = B.reverse $ B.unfoldr fdivMod256 m
where fdivMod256 0 = Nothing
fdivMod256 n = Just (fromIntegral a,b) where (b,a) = divMod256 n
i2ospOf :: Int -> Integer -> Maybe ByteString
i2ospOf len m
| lenbytes < len = Just $ B.replicate (len lenbytes) 0 `B.append` bytes
| lenbytes == len = Just bytes
| otherwise = Nothing
where
lenbytes = B.length bytes
bytes = i2osp m
i2ospOf_ :: Int -> Integer -> ByteString
i2ospOf_ len m = B.unsafeCreate len fillPtr
where fillPtr srcPtr = loop m (srcPtr `plusPtr` (len1))
where loop n ptr = do
let (nn,a) = divMod256 n
poke ptr (fromIntegral a)
if ptr == srcPtr
then return ()
else (if nn == 0 then fillerLoop else loop nn) (ptr `plusPtr` (1))
fillerLoop ptr = do
poke ptr 0
if ptr == srcPtr
then return ()
else fillerLoop (ptr `plusPtr` (1))
lengthBytes :: Integer -> Int
lengthBytes n
| n < 256 = 1
| otherwise = 1 + lengthBytes (n `shiftR` 8)