module Binrep.Example.Tar where import Binrep import Binrep.Generic import Binrep.Type.NullPadded import Binrep.Type.AsciiNat import GHC.Generics ( Generic ) import Data.Word ( Word8 ) import GHC.TypeNats import Data.ByteString qualified as B import FlatParse.Basic qualified as FP type BS = B.ByteString -- | The naturals in tars are sized octal ASCII digit strings that end with a -- null byte (and may start with leading ASCII zeroes). The size includes the -- terminating null, so you get @n-1@ digits. What a farce. -- -- Don't use this constructor directly! The size must be checked to ensure it -- fits. newtype TarNat n = TarNat { getTarNat :: AsciiNat 8 } deriving stock (Generic, Show, Eq) instance KnownNat n => BLen (TarNat n) where type CBLen (TarNat n) = n -- | No need to check for underflow etc. as TarNat guarantees good sizing. instance KnownNat n => Put (TarNat n) where put (TarNat an) = put pfxNulls <> put an <> put @Word8 0x00 where pfxNulls = B.replicate (fromIntegral pfxNullCount) 0x30 pfxNullCount = n - blen an - 1 n = typeNatToBLen @n instance KnownNat n => Get (TarNat n) where get = do an <- FP.isolate (fromIntegral (n - 1)) get get @Word8 >>= \case 0x00 -> return $ TarNat an w -> eBase $ EExpectedByte 0x00 w where n = typeNatToBLen @n -- Partial header data Tar = Tar { tarFileName :: NullPadded 100 BS , tarFileMode :: TarNat 8 , tarFileUIDOwner :: TarNat 8 , tarFileUIDGroup :: TarNat 8 , tarFileFileSize :: TarNat 12 , tarFileLastMod :: TarNat 12 } deriving stock (Generic, Show, Eq) instance BLen Tar where blen = blenGeneric cNoSum instance Put Tar where put = putGeneric cNoSum instance Get Tar where get = getGeneric cNoSum