module Data.Serialize.RLP.Internal (
RLPEncodeable(..),
toBigEndian,
toBigEndianS,
fromBigEndian,
fromBigEndianS,
toByteString,
toByteStringS,
fromByteString,
fromByteStringS,
RLPT(..)
) where
import Data.Binary.Get
import Data.Binary.Put
import qualified Data.ByteString as DBS
import qualified Data.ByteString.Char8 as DBSC
import qualified Data.ByteString.Lazy as DBSL
import qualified Data.ByteString.Lazy.Char8 as DBSLC
data RLPT = RLPL [RLPT] | RLPB DBS.ByteString
deriving (Show, Eq)
toBigEndian :: Int -> DBSL.ByteString
toBigEndian = DBSLC.dropWhile (== '\NUL') . runPut . putInt64be . fromIntegral
toBigEndianS :: Int -> DBS.ByteString
toBigEndianS = DBSL.toStrict . toBigEndian
fromBigEndian :: DBSL.ByteString -> Int
fromBigEndian bs = fromIntegral . runGet getInt64be $ bs'
where bs' = case () of
_ | DBSL.length bs >= 8 -> bs
| otherwise -> DBSLC.append (DBSLC.pack $ b) bs
where b = take (8 - (fromIntegral . DBSL.length $ bs)) (repeat '\NUL')
fromBigEndianS :: DBS.ByteString -> Int
fromBigEndianS = fromBigEndian . DBSL.fromStrict
toByteString :: String -> DBSL.ByteString
toByteString = DBSLC.pack
toByteStringS :: String -> DBS.ByteString
toByteStringS = DBSC.pack
fromByteString :: DBSL.ByteString -> String
fromByteString = DBSLC.unpack
fromByteStringS :: DBS.ByteString -> String
fromByteStringS = DBSC.unpack
rlpSplit :: DBSL.ByteString -> [DBSL.ByteString]
rlpSplit x
| DBSL.null x = []
| DBSL.head x < 192 =
case () of
_ | DBSL.head x < 128 -> (DBSL.singleton . DBSL.head $ x) : (rlpSplit $ DBSL.tail x)
| DBSL.head x < 183 ->
let size = (fromIntegral $ DBSL.head x) - 128 :: Int in
let total = size + 1 in
(DBSL.take (fromIntegral total) x) : (rlpSplit $ DBSL.drop (fromIntegral total) x)
| otherwise ->
let sizeSize = (fromIntegral $ DBSL.head x) - 183 :: Int in
let size = fromBigEndian . DBSL.take (fromIntegral sizeSize) . DBSL.tail $ x :: Int in
let total = sizeSize + size + 1 :: Int in
(DBSL.take (fromIntegral total) x) : (rlpSplit $ DBSL.drop (fromIntegral total) x)
| DBSL.head x == 192 = (DBSL.singleton $ DBSL.head x) : (rlpSplit $ DBSL.tail x)
| DBSL.head x < 247 =
let size = (fromIntegral $ DBSL.head x) - 192 :: Int in
let total = size + 1 in
(DBSL.take (fromIntegral total) x) : (rlpSplit $ DBSL.drop (fromIntegral total) x)
| otherwise =
let sizeSize = (fromIntegral $ DBSL.head x) - 247 :: Int in
let size = fromBigEndian . DBSL.take (fromIntegral sizeSize) . DBSL.tail $ x :: Int in
let total = sizeSize + size + 1 :: Int in
(DBSL.take (fromIntegral total) x) : (rlpSplit $ DBSL.drop (fromIntegral total) x)
class RLPEncodeable a where
rlpEncodeI' :: a -> Put
rlpEncodeI :: a -> DBSL.ByteString
rlpEncodeI = runPut . rlpEncodeI'
rlpDecodeI' :: Get a
rlpDecodeI :: DBSL.ByteString -> Maybe a
rlpDecodeI x = let r = runGetOrFail rlpDecodeI' x in
case r of
Left _ -> Nothing
Right (_, _, s) -> Just s
instance RLPEncodeable RLPT where
rlpEncodeI' (RLPB bs) = rlpEncodeI' bs
rlpEncodeI' (RLPL t) = case () of
_ | DBSL.length dat < 56 -> (putWord8 . fromIntegral $ 192 + DBSL.length dat)
<> (putLazyByteString dat)
| otherwise -> (putWord8 . fromIntegral $ 247 + DBSL.length l)
<> (putLazyByteString l)
<> (putLazyByteString dat)
where l = toBigEndian . fromIntegral . DBSL.length $ dat
where dat = DBSL.concat . map rlpEncodeI $ t
rlpDecodeI' = do
i <- getWord8
case () of
_ | i < 192 -> do
ls <- getRemainingLazyByteString
return . RLPB . (\(Just x) -> x) . rlpDecodeI $ DBSL.cons i ls
| i == 192 -> do
return $ RLPL []
| i < 247 -> do
ls <- getLazyByteString . fromIntegral $ i - 192
return $ RLPL . map ((\(Just x) -> x) . rlpDecodeI) . rlpSplit $ ls
| otherwise -> do
ls <- getLazyByteString . fromIntegral $ i - 247
let k = fromBigEndian ls
ls' <- getLazyByteString . fromIntegral $ k
return $ RLPL . map ((\(Just x) -> x) . rlpDecodeI) . rlpSplit $ ls'
instance RLPEncodeable DBS.ByteString where
rlpEncodeI' bs
| (DBS.length bs == 1) && (DBS.head bs < (fromIntegral (128 :: Integer))) = putByteString bs
| DBS.length bs < 56 = (putWord8 . fromIntegral $ 128 + DBS.length bs)
<> (putByteString bs)
| otherwise = (putWord8 . fromIntegral $ 183 + DBSL.length l)
<> (putLazyByteString l)
<> (putByteString bs)
where l = toBigEndian . DBS.length $ bs
rlpDecodeI' = do
i <- getWord8
case () of
_ | i < 128 -> return $ DBS.singleton i
| i < 183 -> do
ls <- getByteString . fromIntegral $ i - 128
return ls
| i < 192 -> do
sbe <- getLazyByteString . fromIntegral $ i - 183
ls <- getByteString . fromBigEndian $ sbe
return ls
| otherwise -> undefined
instance RLPEncodeable Int where
rlpEncodeI' = rlpEncodeI' . DBSL.toStrict . toBigEndian
rlpDecodeI' = do
b <- rlpDecodeI' :: Get DBS.ByteString
return . fromBigEndian . DBSL.fromStrict $ b