diff --git a/binrep.cabal b/binrep.cabal index 88ea957..3c3aa54 100644 --- a/binrep.cabal +++ b/binrep.cabal @@ -37,13 +37,13 @@ library Binrep.BLen Binrep.CBLen Binrep.CBLen.Generic - Binrep.Common.Class.Generic Binrep.Common.Class.TypeErrors Binrep.Common.Via.Prim Binrep.Extra.HexByteString Binrep.Generic Binrep.Get Binrep.Put + Binrep.Put.Struct Binrep.Type.Byte Binrep.Type.Magic Binrep.Type.NullPadded diff --git a/bytezap/TODO.md b/bytezap/TODO.md deleted file mode 100644 index a8014cb..0000000 --- a/bytezap/TODO.md +++ /dev/null @@ -1,25 +0,0 @@ -# bytezap to-dos -## Challenges with unbuffered serialization -### Floats to decimal ASCII -Serializing floats to ASCII efficiently is hard. Most of the recent fast -algorithms `malloc` ~25 bytes at the start then tell you how long it actually -was. I see little in the way of "figure out for cheap how many characters long -this float will be once serialized". - -mason wins with its builtin grisu3. I think beating that is Hard. bytestring -actually uses ryu internally! But via a Haskell implementation. -[ryu](https://github.com/ulfjack/ryu/blob/master/ryu/d2s.c) has some stuff. -[Dragonbox](https://github.com/jk-jeon/dragonbox) is better, but it's C++. Most -likely I am on my own here. Perhaps I can figure out some shortcuts in grisu3 to -calculate serialized length without doing *all* the work. If I can avoid -`malloc`ing, it would be a winner. - -Perhaps I simply pray to SPJ for good caching behaviour. Running the inner -serializer in `blen` *should* mean it can be reused for `put`, and providing -they occur close to each other (which they do in `runPut`), I could see it -happening. - -### Ints to decimal ASCII -This should be OK I think? I wonder how fast we can go, though. Cool C++ lib at -[jeaiii/itoa](https://github.com/jeaiii/itoa). Probably a waste of time, use -bytestring's prims along with a fast `BLen` check (I wrote one before I think). diff --git a/src/Binrep/Common/Class/Generic.hs b/src/Binrep/Common/Class/Generic.hs deleted file mode 100644 index 9ede360..0000000 --- a/src/Binrep/Common/Class/Generic.hs +++ /dev/null @@ -1,6 +0,0 @@ -module Binrep.Common.Class.Generic where - --- | Internal tag for binrep generics, required for generic-data-functions. --- --- Not instantiated. -data BinrepG diff --git a/src/Binrep/Put.hs b/src/Binrep/Put.hs index 15f3218..4759bb4 100644 --- a/src/Binrep/Put.hs +++ b/src/Binrep/Put.hs @@ -1,14 +1,10 @@ {-# LANGUAGE UndecidableInstances #-} -- required below GHC 9.6 -{-# OPTIONS_GHC -fno-warn-orphans #-} -- for generic data op instance -{-# LANGUAGE AllowAmbiguousTypes #-} -- TODO tmp module Binrep.Put where import Binrep.BLen ( BLen(blen) ) import Data.Functor.Identity import Bytezap.Poke -import Bytezap.Struct qualified as Struct -import Bytezap.Struct.Generic qualified as Struct import Raehik.Compat.Data.Primitive.Types ( Prim', sizeOf ) import Binrep.Util.ByteOrder import Raehik.Compat.Data.Primitive.Types.Endian ( ByteSwap ) @@ -17,7 +13,7 @@ import Binrep.Common.Via.Prim ( ViaPrim(..) ) import Data.ByteString qualified as B import Binrep.Common.Class.TypeErrors ( ENoSum, ENoEmpty ) -import GHC.TypeLits ( TypeError, KnownNat ) +import GHC.TypeLits ( TypeError ) import Data.Void import Data.Word @@ -30,17 +26,11 @@ import Generic.Data.Rep.Assert import Control.Monad.ST ( RealWorld ) -import Binrep.Common.Class.Generic ( BinrepG ) -import Binrep.CBLen - -type Putter = Poke RealWorld -type PutterC = Struct.Poke RealWorld +import Binrep.Put.Struct ( PutC(putC) ) +type Putter = Poke RealWorld class Put a where put :: a -> Putter --- | constant size putter -class PutC a where putC :: a -> PutterC - runPut :: (BLen a, Put a) => a -> B.ByteString runPut a = unsafeRunPokeBS (blen a) (put a) @@ -69,23 +59,6 @@ putGenericSum => (String -> Putter) -> a -> Putter putGenericSum = genericFoldMapSum @'SumOnly @asserts @Put -instance Struct.GPokeBase BinrepG where - type GPokeBaseSt BinrepG = RealWorld - type GPokeBaseC BinrepG a = PutC a - gPokeBase = Struct.unPoke . putC - type KnownSizeOf' BinrepG a = KnownNat (CBLen a) - sizeOf' = reifyCBLenProxy# - -putGenericStruct - :: forall a - . ( Generic a, Struct.GPoke BinrepG (Rep a) ) - => a -> PutterC -putGenericStruct = Struct.Poke . Struct.gPoke @BinrepG . from - -instance Prim' a => PutC (ViaPrim a) where - putC = Struct.prim . unViaPrim - {-# INLINE putC #-} - instance Prim' a => Put (ViaPrim a) where put = fromStructPoke (sizeOf (undefined :: a)) . putC {-# INLINE put #-} diff --git a/src/Binrep/Put/Struct.hs b/src/Binrep/Put/Struct.hs new file mode 100644 index 0000000..e1dc2ff --- /dev/null +++ b/src/Binrep/Put/Struct.hs @@ -0,0 +1,33 @@ +module Binrep.Put.Struct where + +import Bytezap.Struct qualified as Struct +import Bytezap.Struct.Generic qualified as Struct +import Control.Monad.ST ( RealWorld ) +import Raehik.Compat.Data.Primitive.Types ( Prim' ) +import GHC.Generics +import Binrep.CBLen +import Binrep.Common.Via.Prim ( ViaPrim(..) ) +import GHC.TypeLits ( KnownNat ) + +type PutterC = Struct.Poke RealWorld + +-- | constant size putter +class PutC a where putC :: a -> PutterC + +instance Struct.GPokeBase PutC where + type GPokeBaseSt PutC = RealWorld + type GPokeBaseC PutC a = PutC a + gPokeBase = Struct.unPoke . putC + type KnownSizeOf' PutC a = KnownNat (CBLen a) + sizeOf' = reifyCBLenProxy# + +-- | Serialize a term of the struct-like type @a@ via its 'Generic' instance. +putGenericStruct + :: forall a + . ( Generic a, Struct.GPoke PutC (Rep a) ) + => a -> PutterC +putGenericStruct = Struct.Poke . Struct.gPoke @PutC . from + +instance Prim' a => PutC (ViaPrim a) where + putC = Struct.prim . unViaPrim + {-# INLINE putC #-}