module Pairing.Serialize where
import Protolude hiding (putByteString)
import Pairing.Point
import Data.ByteString.Builder
import Data.ByteString as B
import Data.Binary.Get
import Data.Binary.Put (Put, putWord8, runPut, putByteString)
import Control.Error
import Pairing.ByteRepr
import Pairing.CyclicGroup
class ToCompressedForm a where
serializeCompressed :: a -> Maybe ByteString
class ToUncompressedForm a where
serializeUncompressed :: a -> Maybe ByteString
header :: Word8 -> Put
header n = putWord8 0 >> putWord8 n
elementToUncompressedForm :: (ByteRepr a) => a -> Maybe LByteString
elementToUncompressedForm a = do
repr <- mkRepr a
pure $ runPut $ do
header 4
putByteString repr
toUncompressedForm :: (ByteRepr a) => Point a -> Maybe LByteString
toUncompressedForm (Point x y) = do
rx <- mkRepr x
ry <- mkRepr y
pure $ runPut $ do
header 4
putByteString rx
putByteString ry
toUncompressedForm Infinity = pure $ runPut (header 1)
toCompressedForm :: (ByteRepr a, FromX a, Eq a) => Point a -> Maybe LByteString
toCompressedForm (Point x y) = do
ny <- yFromX x True
let yform = if ny == y then 3 else 2
rx <- mkRepr x
pure (runPut $ header yform >> putByteString rx)
toCompressedForm Infinity = Just (toLazyByteString (word8 0 <> word8 1))
pointFromByteString :: (Show a, Validate (Point a), ByteRepr a, FromX a) => a -> LByteString -> Either Text (Point a)
pointFromByteString a = parseBS fromByteStringGet
where
fromByteStringGet = do
ctype <- getCompressionType
processCompressed a ctype
processCompressed :: forall a . (ByteRepr a, FromX a) => a -> Word8 -> Get (Maybe (Point a))
processCompressed one ct
| ct == 4 = do
xbs <- getByteString rlen
ybs <- getByteString rlen
pure (buildPoint one xbs ybs)
| ct == 2 = fromCompressed False
| ct == 3 = fromCompressed True
| ct == 1 = pure (Just Infinity)
| otherwise = pure Nothing
where
rlen = reprLength one
fromCompressed largestY = runMaybeT $ do
xbs <- lift $ getByteString rlen
x <- hoistMaybe $ fromRepr one xbs
y <- hoistMaybe $ yFromX x largestY
pure (Point x y)
buildPoint :: ByteRepr a => a -> ByteString -> ByteString -> Maybe (Point a)
buildPoint one xbs ybs = do
x <- fromRepr one xbs
y <- fromRepr one ybs
pure (Point x y)
getCompressionType :: Get Word8
getCompressionType = getWord8 >> getWord8
elementReadUncompressed :: (Validate a, Show a, ByteRepr a) => a -> LByteString -> Either Text a
elementReadUncompressed ele = parseBS runc
where
runc = do
ctype <- getCompressionType
if ctype == 4 then do
bs <- getByteString (reprLength ele)
pure (fromRepr ele bs)
else
pure Nothing
parseBS :: (Validate a, Show a) => Get (Maybe a) -> LByteString -> Either Text a
parseBS f bs = do
(_, _, mpt) <- first (\(_,_,err) -> toS err) (runGetOrFail f bs)
case mpt of
Just pt -> if isValidElement pt then (Right pt) else Left ("Element was not valid after deserialisation: " <> show pt)
Nothing -> Left "Point could not be parsed"