module Data.Serialize.RLP.Internal (
RLPEncodeable(..),
toBigEndian,
toBigEndianS,
fromBigEndian,
fromBigEndianS,
toByteString,
toByteStringS,
fromByteString,
fromByteStringS,
unJust,
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
import qualified Data.List as DL
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 -> Either String Int
fromBigEndian bs = case bs'' of
Left (_, _, msg) -> Left ("can't decode from Big-Endian: " ++ msg)
Right (_, _, a) -> Right $ fromIntegral a
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')
bs'' = runGetOrFail getInt64be $ bs'
fromBigEndianS :: DBS.ByteString -> Either String 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 -> Either String [DBSL.ByteString]
rlpSplit x
| DBSL.null x = Right []
| DBSL.head x < 192 =
case () of
_ | DBSL.head x < 128 ->
let aux = rlpSplit $ DBSL.tail x in
case aux of
Left m -> Left m
Right v -> Right $ (DBSL.singleton . DBSL.head $ x) : v
| DBSL.head x < 183 ->
let size = (fromIntegral $ DBSL.head x) - 128 :: Int in
let total = size + 1 in
let aux = rlpSplit $ DBSL.drop (fromIntegral total) x in
case aux of
Left m -> Left m
Right v -> Right $ (DBSL.take (fromIntegral total) x) : v
| otherwise ->
let sizeSize = (fromIntegral $ DBSL.head x) - 183 :: Int in
let size = fromBigEndian . DBSL.take (fromIntegral sizeSize) . DBSL.tail $ x in
case size of
Left m -> Left m
Right v ->
let total = sizeSize + v + 1 :: Int in
let aux = rlpSplit $ DBSL.drop (fromIntegral total) x in
case aux of
Left m -> Left m
Right v' -> Right $ (DBSL.take (fromIntegral total) x) : v'
| DBSL.head x == 192 =
let aux = (rlpSplit $ DBSL.tail x) in
case aux of
Left m -> Left m
Right v -> Right $ (DBSL.singleton $ DBSL.head x) : v
| DBSL.head x < 247 =
let size = (fromIntegral $ DBSL.head x) - 192 :: Int in
let total = size + 1 in
let aux = rlpSplit $ DBSL.drop (fromIntegral total) x in
case aux of
Left m -> Left m
Right v -> Right $ (DBSL.take (fromIntegral total) x) : v
| otherwise =
let sizeSize = (fromIntegral $ DBSL.head x) - 247 :: Int in
let size = fromBigEndian . DBSL.take (fromIntegral sizeSize) . DBSL.tail $ x in
case size of
Left m -> Left m
Right v ->
let total = sizeSize + v + 1 :: Int in
let aux = rlpSplit $ DBSL.drop (fromIntegral total) x in
case aux of
Left m -> Left m
Right v' -> Right $ (DBSL.take (fromIntegral total) x) : v'
unJust :: Maybe a -> a
unJust (Just x) = x
unJust _ = undefined
class RLPEncodeable a where
rlpEncodeI' :: a -> Put
rlpEncodeI :: a -> DBSL.ByteString
rlpEncodeI = runPut . rlpEncodeI'
rlpDecodeI' :: Get a
rlpDecodeI :: DBSL.ByteString -> Either String a
rlpDecodeI x = let r = runGetOrFail rlpDecodeI' x in
case r of
Left (_, _, m) -> Left m
Right (_, _, s) -> Right 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
let r = rlpDecodeI $ DBSL.cons i ls
case r of
Left m -> fail m
Right v -> return . RLPB $ v
| i == 192 -> do
return $ RLPL []
| i < 247 -> do
ls <- getLazyByteString . fromIntegral $ i - 192
let k = rlpSplit ls
case k of
Left m -> fail m
Right v -> do
let k' = map rlpDecodeI v
let k'' = map (\e -> case e of
Left m -> m
Right _ -> "") k'
case all null k'' of
True -> return $ RLPL . map (\(Right x) -> x) $ k'
_ -> fail (DL.intercalate ", " k'')
| otherwise -> do
ls <- getLazyByteString . fromIntegral $ i - 247
let k = fromBigEndian ls
case k of
Left m -> fail m
Right v -> do
ls' <- getLazyByteString . fromIntegral $ v
let k' = rlpSplit ls'
case k' of
Left m' -> fail m'
Right v' -> do
let k'' = map rlpDecodeI v'
let k'3 = map (\e -> case e of
Left m -> m
Right _ -> "") k''
case all null k'3 of
True -> return $ RLPL . map (\(Right x) -> x) $ k''
_ -> fail (DL.intercalate ", " k'3)
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
let k = fromBigEndian sbe
case k of
Left m -> fail m
Right v -> do
ls <- getByteString v
return ls
| otherwise -> fail "Decoding a ByteString with head >= 192"
instance RLPEncodeable Int where
rlpEncodeI' = rlpEncodeI' . toBigEndianS
rlpDecodeI' = do
b <- rlpDecodeI' :: Get DBS.ByteString
case DBSC.head b of
'\NUL' -> fail "leading zeroes found when decoding an integer"
_ -> do
let k = fromBigEndianS b
case k of
Left m -> fail m
Right v -> return v