binrep-0.1.0: src/Binrep/Predicates/NullPadTo.hs
{-# LANGUAGE OverloadedStrings #-}
module Binrep.Predicates.NullPadTo where
import Binrep.Codec
import Binrep.ByteLen
import Binrep.Util ( tshow )
import Refined
import Refined.WithRefine
import Data.Serialize
import GHC.TypeNats
import Numeric.Natural
import GHC.Natural ( minusNaturalMaybe )
import GHC.Exts ( proxy#, Proxy# )
import Data.Typeable
import Data.ByteString qualified as BS
data NullPadTo (n :: Nat)
instance KnownNat n => ByteLen (WithRefine 'Enforced (NullPadTo n) a) where
blen = const n
where n = natVal' (proxy# :: Proxy# n)
instance (ByteLen a, KnownNat n) => Predicate (NullPadTo n) a where
validate p a
| len > n
= throwRefineOtherException (typeRep p) $
"too long: " <> tshow len <> " > " <> tshow n
| otherwise = success
where
n = natVal' (proxy# :: Proxy# n)
len = blen a
-- | predicate is inherently enforced due to checking length to calculate how
-- many succeeding nulls to parse
--
-- Note that the consumer probably doesn't care about the content of the
-- padding, just that the data is chunked correctly. I figure we care about
-- correctness here, so it'd be nice to know about the padding well-formedness
-- (i.e. that it's all nulls).
instance (BinaryCodec a, ByteLen a, KnownNat n)
=> BinaryCodec (WithRefine 'Enforced (NullPadTo n) a) where
fromBin = do
a <- fromBin
let len = blen a
case minusNaturalMaybe n len of
Nothing -> fail $ "too long: " <> show len <> " > " <> show n
Just nullstrLen -> do
getNNulls nullstrLen
return $ reallyUnsafeEnforce a
where
n = natVal' (proxy# :: Proxy# n)
toBin wrnpa = do
let npa = unWithRefine wrnpa
toBin npa
let paddingLength = n - blen npa
putByteString $ BS.replicate (fromIntegral paddingLength) 0x00
where
n = natVal' (proxy# :: Proxy# n)
getNNulls :: Natural -> Get ()
getNNulls = \case 0 -> return ()
n -> getWord8 >>= \case
0x00 -> getNNulls $ n-1
nonNull -> do
offset <- bytesRead
fail $ "expected null, found: "<> show nonNull
<> " at offset " <> show offset
<> ", " <> show n <> " more nulls to go"