borsh 0.2.0 → 0.3.0
raw patch · 11 files changed
+415/−123 lines, 11 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
- Codec.Borsh: Struct :: a -> Struct a
- Codec.Borsh: newtype Struct a
+ Codec.Borsh: AsEnum :: a -> AsEnum a
+ Codec.Borsh: AsStruct :: a -> AsStruct a
+ Codec.Borsh: KnownImpliesMax :: a -> KnownImpliesMax a
+ Codec.Borsh: [getEnum] :: AsEnum a -> a
+ Codec.Borsh: [getKnownImpliesMax] :: KnownImpliesMax a -> a
+ Codec.Borsh: borshMaxSize :: BorshMaxSize a => Proxy a -> Word32
+ Codec.Borsh: class BorshMaxSize (a :: Type)
+ Codec.Borsh: newtype AsEnum a
+ Codec.Borsh: newtype AsStruct a
+ Codec.Borsh: newtype KnownImpliesMax a
- Codec.Borsh: [SizeKnown] :: Int -> Size 'HasKnownSize
+ Codec.Borsh: [SizeKnown] :: Word32 -> Size 'HasKnownSize
- Codec.Borsh: [getStruct] :: Struct a -> a
+ Codec.Borsh: [getStruct] :: AsStruct a -> a
- Codec.Borsh: borshSize :: (BorshSize a, StaticBorshSize a ~ SumKnownSize (Code a), BorshSizeSum (Code a)) => Proxy a -> Size (StaticBorshSize a)
+ Codec.Borsh: borshSize :: BorshSize a => Proxy a -> Size (StaticBorshSize a)
- Codec.Borsh: decodeBorsh :: (FromBorsh a, Generic a, BorshSizeSum (Code a), All2 FromBorsh (Code a)) => Decoder s a
+ Codec.Borsh: decodeBorsh :: FromBorsh a => Decoder s a
- Codec.Borsh: encodeBorsh :: (ToBorsh a, Generic a, BorshSizeSum (Code a), All2 ToBorsh (Code a)) => Encoder a
+ Codec.Borsh: encodeBorsh :: ToBorsh a => Encoder a
- Codec.Borsh: type StaticBorshSize a = SumKnownSize (Code a);
+ Codec.Borsh: type StaticBorshSize a :: KnownSize;
Files
- CHANGELOG.md +9/−0
- borsh.cabal +1/−1
- src/Codec/Borsh.hs +17/−10
- src/Codec/Borsh/Class.hs +278/−79
- src/Codec/Borsh/Incremental/Decoder.hs +6/−1
- test/Test/Codec/Borsh/ExampleType/BTree.hs +2/−12
- test/Test/Codec/Borsh/ExampleType/NTree.hs +3/−12
- test/Test/Codec/Borsh/ExampleType/SimpleStructs.hs +11/−3
- test/Test/Codec/Borsh/Size.hs +17/−1
- test/Test/Codec/Borsh/Util/QuickCheck.hs +12/−0
- test/Test/Codec/Borsh/Util/RandomType.hs +59/−4
CHANGELOG.md view
@@ -1,5 +1,14 @@ # Revision history for borsh +## 0.3.0 -- 2023-03-17++* Introduce `BorshMaxSize`+* Rename `Struct` (for deriving-via) to `AsStruct`+* Introduce `AsEnum` (for deriving-via) alongside `AsStruct`+ (this is also the reason for the renaming: `Enum` is in the prelude.)+* Remove the generic defaults for `ToBorsh`, `FromBorsh`, `BorshSize` and+ `MaxBorshSize`; users should instead use `AsEnum` or `AsStruct` (#10).+ ## 0.2.0 -- 2022-11-15 * Remove dependency on `memory`
borsh.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: borsh-version: 0.2.0+version: 0.3.0 synopsis: Implementation of BORSH serialisation description: This package provides type classes and combinators for
src/Codec/Borsh.hs view
@@ -13,20 +13,27 @@ , Size(..) , KnownSize(..) , BorshSizeSum(..)+ , BorshMaxSize(..) -- * Deriving-via support- , Struct(..)+ , AsEnum(..)+ , AsStruct(..)+ , KnownImpliesMax(..) ) where import Codec.Borsh.Class- ( BorshSizeSum(..),- Struct(..),- FromBorsh(..),- ToBorsh(..),- BorshSize(..),- Size(..),- KnownSize(..),- serialiseBorsh,- deserialiseBorsh )+ ( BorshSizeSum(..)+ , AsEnum(..)+ , AsStruct(..)+ , FromBorsh(..)+ , ToBorsh(..)+ , BorshSize(..)+ , Size(..)+ , KnownSize(..)+ , BorshMaxSize(..)+ , KnownImpliesMax(..)+ , serialiseBorsh+ , deserialiseBorsh+ ) import Codec.Borsh.Encoding (Encoder(..)) import Codec.Borsh.Incremental (Decoder) import Codec.Borsh.Incremental.Monad (DeserialiseFailure(..))
src/Codec/Borsh/Class.hs view
@@ -5,12 +5,15 @@ ToBorsh(..) , FromBorsh(..) -- ** Deriving-via support- , Struct(..)+ , AsEnum(..)+ , AsStruct(..)+ , KnownImpliesMax(..) -- * Size information , KnownSize(..) , Size(..) , BorshSize(..) , BorshSizeSum(..)+ , BorshMaxSize(..) -- * Derived functionality , serialiseBorsh , deserialiseBorsh@@ -50,7 +53,7 @@ -- | The statically known size of encodings of values of a particular type. data Size (a :: KnownSize) where- SizeKnown :: Int -> Size 'HasKnownSize+ SizeKnown :: Word32 -> Size 'HasKnownSize SizeVariable :: Size 'HasVariableSize deriving instance Show (Size a)@@ -58,20 +61,33 @@ class BorshSize (a :: Type) where type StaticBorshSize a :: KnownSize- type StaticBorshSize a = SumKnownSize (Code a) - -- | Size of the Borsh encoding, if known ahead of time+ -- | Size of the Borsh encoding, if known statically (independent of a value) --- -- See 'encodeBorsh' for discussion of the generic instance.+ -- There is no generic default implementation of 'borshSize'; instead use+ -- deriving-via using 'AsStruct' or 'AsEnum'. borshSize :: Proxy a -> Size (StaticBorshSize a) - default borshSize ::- ( StaticBorshSize a ~ SumKnownSize (Code a)- , BorshSizeSum (Code a)- )- => Proxy a -> Size (StaticBorshSize a)- borshSize _ = borshSizeSum (Proxy @(Code a))+class BorshMaxSize (a :: Type) where+ -- | Maximum size of the Borsh encoding+ --+ -- There is no generic default implementation of 'borshMaxSize'; instead use+ -- deriving-via using 'AsStruct' or 'AsEnum'.+ --+ -- However, while it is possible to use deriving-via to derive 'BorshMaxSize'+ -- for your own types, recursive types should /not/ be given an instance (and+ -- the derived function will not terminate).+ borshMaxSize :: Proxy a -> Word32 +-- | If the size of a type's Borsh encoding is statically known then we also+-- know the maximum size of the encoding. Useful for deriving-via.+newtype KnownImpliesMax a = KnownImpliesMax { getKnownImpliesMax :: a }++instance ( BorshSize a+ , StaticBorshSize a ~ 'HasKnownSize+ ) => BorshMaxSize (KnownImpliesMax a) where+ borshMaxSize _ = case borshSize (Proxy @a) of SizeKnown n -> n+ {------------------------------------------------------------------------------- Definition -------------------------------------------------------------------------------}@@ -79,33 +95,22 @@ class BorshSize a => ToBorsh a where -- | Encoder to Borsh --- -- NOTE: The default generic encoder uses the Borsh encoding for enums,- -- and will therefore use constructor tag; see 'Struct' for detailed- -- discussion. Since the spec mandates the presence of that constructor tag,- -- the generic encoder/decoder does not apply to types without constructors.+ -- There is no generic default implementation of 'encodeBorsh'; instead use+ -- deriving-via using 'AsStruct' or 'AsEnum'. encodeBorsh :: Encoder a - default encodeBorsh ::- (Generic a, BorshSizeSum (Code a), All2 ToBorsh (Code a))- => Encoder a- encodeBorsh = Encoder $ runEncoder encodeBorsh . from- class BorshSize a => FromBorsh a where -- | Decode from Borsh --- -- See 'encodeBorsh' for discussion of the generic instance.+ -- There is no generic default implementation of 'decodeBorsh'; instead use+ -- deriving-via using 'AsStruct' or 'AsEnum'. decodeBorsh :: Decoder s a - default decodeBorsh ::- (Generic a, BorshSizeSum (Code a), All2 FromBorsh (Code a))- => Decoder s a- decodeBorsh = to <$> decodeBorsh- {-------------------------------------------------------------------------------- Structs+ Enums -------------------------------------------------------------------------------} --- | Deriving-via support for structs+-- | Deriving-via support for enums (general ADTs) -- -- The Borsh spec <https://borsh.io/> mandates that enums have a tag indicating -- the constructor, even when there is only a single constructor in the enum.@@ -114,34 +119,76 @@ -- the only difference between them is that a struct is an enum with a single -- constructor. ----- The default generic encoder en decoder you get in 'ToBorsh' and 'FromBorsh'--- will therefore add the tag, independent of the number of constructors. If--- you want the encoding of a struct, without the tag, you need to use deriving--- via:+-- The generic encoder en decoder you get in 'ToBorsh' and 'FromBorsh' when+-- deriving via @AsEnum@ will therefore add the tag, independent of the number of+-- constructors: --+-- > data MyEnum = ..+-- > deriving (BorshSize, ToBorsh, FromBorsh) via AsEnum MyEnum+--+-- If you want the encoding of a struct, without the tag, you need to derive via+-- 'AsStruct'.+newtype AsEnum a = AsEnum { getEnum :: a }++instance BorshSizeSum (Code a) => BorshSize (AsEnum a) where+ type StaticBorshSize (AsEnum a) = SumKnownSize (Code a)+ borshSize _ = borshSizeSum (Proxy @(Code a))++instance ( Generic a+ , All2 BorshMaxSize (Code a)+ ) => BorshMaxSize (AsEnum a) where+ borshMaxSize _ = borshMaxSize (Proxy @(SOP I (Code a)))++instance ( Generic a+ , BorshSizeSum (Code a)+ , All2 ToBorsh (Code a)+ ) => ToBorsh (AsEnum a) where+ encodeBorsh = contramap (from . getEnum) encodeBorsh++instance ( Generic a+ , BorshSizeSum (Code a)+ , All2 FromBorsh (Code a)+ ) => FromBorsh (AsEnum a) where+ decodeBorsh = fmap (AsEnum . to) decodeBorsh++{-------------------------------------------------------------------------------+ Structs+-------------------------------------------------------------------------------}++-- | Deriving-via support for structs+--+-- Usage:+-- -- > data MyStruct = ..--- > deriving (BorshSize, ToBorsh, FromBorsh) via Struct MyStruct+-- > deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct MyStruct -- -- NOTE: Doing so may have consequences for forwards compatibility: if a tag -- is present, additional constructors can be added without invalidating the--- encoding of existing constructors.-newtype Struct a = Struct { getStruct :: a }+-- encoding of existing constructors. See also 'AsEnum'.+newtype AsStruct a = AsStruct { getStruct :: a } -instance (IsProductType a xs, All BorshSize xs) => BorshSize (Struct a) where- type StaticBorshSize (Struct a) = ProdKnownSize (ProductCode a)+instance ( IsProductType a xs+ , All BorshSize xs+ ) => BorshSize (AsStruct a) where+ type StaticBorshSize (AsStruct a) = ProdKnownSize (ProductCode a) borshSize _ = sizeOfProd (Proxy @(ProductCode a)) instance ( IsProductType a xs+ , All BorshMaxSize xs+ ) => BorshMaxSize (AsStruct a) where+ borshMaxSize _ = borshMaxSize (Proxy @(NP I (ProductCode a)))++instance ( IsProductType a xs , All BorshSize xs , All ToBorsh xs- ) => ToBorsh (Struct a) where+ ) => ToBorsh (AsStruct a) where encodeBorsh = contramap (productTypeFrom . getStruct) encodeBorsh instance ( IsProductType a xs , All BorshSize xs , All FromBorsh xs- ) => FromBorsh (Struct a) where- decodeBorsh = fmap (Struct . productTypeTo) decodeBorsh+ ) => FromBorsh (AsStruct a) where+ decodeBorsh = fmap (AsStruct . productTypeTo) decodeBorsh {------------------------------------------------------------------------------- Derived functionality@@ -220,11 +267,8 @@ type StaticBorshSize Text = 'HasVariableSize borshSize _ = SizeVariable -instance BorshSize [a] where- -- Use generic defaults--instance BorshSize (Maybe a) where- -- Use generic defaults+deriving via AsEnum [a] instance BorshSize [a]+deriving via AsEnum (Maybe a) instance BorshSize (Maybe a) instance BorshSize (Set a) where type StaticBorshSize (Set a) = 'HasVariableSize@@ -243,6 +287,54 @@ borshSize _ = borshSizeSum (Proxy @xss) {-------------------------------------------------------------------------------+ Maximum sizes+-------------------------------------------------------------------------------}++deriving via KnownImpliesMax Word8 instance BorshMaxSize Word8+deriving via KnownImpliesMax Word16 instance BorshMaxSize Word16+deriving via KnownImpliesMax Word32 instance BorshMaxSize Word32+deriving via KnownImpliesMax Word64 instance BorshMaxSize Word64+deriving via KnownImpliesMax Word128 instance BorshMaxSize Word128++deriving via KnownImpliesMax Int8 instance BorshMaxSize Int8+deriving via KnownImpliesMax Int16 instance BorshMaxSize Int16+deriving via KnownImpliesMax Int32 instance BorshMaxSize Int32+deriving via KnownImpliesMax Int64 instance BorshMaxSize Int64+deriving via KnownImpliesMax Int128 instance BorshMaxSize Int128++deriving via KnownImpliesMax Float instance BorshMaxSize Float+deriving via KnownImpliesMax Double instance BorshMaxSize Double++instance ( KnownNat n+ , BorshMaxSize a+ ) => BorshMaxSize (FixedSizeArray n a) where+ borshMaxSize _ = fromIntegral (natVal (Proxy @n)) * borshMaxSize (Proxy @a)++deriving via AsEnum (Maybe a) instance BorshMaxSize a => BorshMaxSize (Maybe a)++instance (All2 BorshMaxSize xss, All SListI xss) => BorshMaxSize (SOP I xss) where+ borshMaxSize _ = aux . hcollapse $ sizes+ where+ aux :: [[Word32]] -> Word32+ aux [] = 1+ aux xs = 1 + maximum (map sum xs)++ borshMaxSize' :: forall a. (BorshMaxSize a) => K Word32 a+ borshMaxSize' = K $ borshMaxSize (Proxy @a)++ sizes :: POP (K Word32) xss+ sizes = hcpure (Proxy @BorshMaxSize) borshMaxSize'++instance (All BorshMaxSize xs) => BorshMaxSize (NP I xs) where+ borshMaxSize _ = sum . hcollapse $ sizes+ where+ borshMaxSize' :: forall a. (BorshMaxSize a) => K Word32 a+ borshMaxSize' = K $ borshMaxSize (Proxy @a)++ sizes :: NP (K Word32) xs+ sizes = hcpure (Proxy @BorshMaxSize) borshMaxSize'++{------------------------------------------------------------------------------- ToBorsh instances -------------------------------------------------------------------------------} @@ -334,25 +426,32 @@ -- size 0 -deriving via Struct () instance BorshSize ()-deriving via Struct () instance ToBorsh ()-deriving via Struct () instance FromBorsh ()+deriving via AsStruct () instance BorshSize ()+deriving via AsStruct () instance BorshMaxSize ()+deriving via AsStruct () instance ToBorsh ()+deriving via AsStruct () instance FromBorsh () -- size 2 -deriving via Struct (a, b)+deriving via AsStruct (a, b) instance ( BorshSize a , BorshSize b ) => BorshSize (a, b)-deriving via Struct (a, b)+deriving via AsStruct (a, b) instance+ ( BorshMaxSize a+ , BorshMaxSize b+ )+ => BorshMaxSize (a, b)+deriving via AsStruct (a, b)+ instance ( ToBorsh a , ToBorsh b ) => ToBorsh (a, b)-deriving via Struct (a, b)+deriving via AsStruct (a, b) instance ( FromBorsh a , FromBorsh b@@ -361,21 +460,28 @@ -- size 3 -deriving via Struct (a, b, c)+deriving via AsStruct (a, b, c) instance ( BorshSize a , BorshSize b , BorshSize c ) => BorshSize (a, b, c)-deriving via Struct (a, b, c)+deriving via AsStruct (a, b, c) instance+ ( BorshMaxSize a+ , BorshMaxSize b+ , BorshMaxSize c+ )+ => BorshMaxSize (a, b, c)+deriving via AsStruct (a, b, c)+ instance ( ToBorsh a , ToBorsh b , ToBorsh c ) => ToBorsh (a, b, c)-deriving via Struct (a, b, c)+deriving via AsStruct (a, b, c) instance ( FromBorsh a , FromBorsh b@@ -385,7 +491,7 @@ -- size 4 -deriving via Struct (a, b, c, d)+deriving via AsStruct (a, b, c, d) instance ( BorshSize a , BorshSize b@@ -393,15 +499,23 @@ , BorshSize d ) => BorshSize (a, b, c, d)-deriving via Struct (a, b, c, d)+deriving via AsStruct (a, b, c, d) instance+ ( BorshMaxSize a+ , BorshMaxSize b+ , BorshMaxSize c+ , BorshMaxSize d+ )+ => BorshMaxSize (a, b, c, d)+deriving via AsStruct (a, b, c, d)+ instance ( ToBorsh a , ToBorsh b , ToBorsh c , ToBorsh d ) => ToBorsh (a, b, c, d)-deriving via Struct (a, b, c, d)+deriving via AsStruct (a, b, c, d) instance ( FromBorsh a , FromBorsh b@@ -412,7 +526,7 @@ -- size 5 -deriving via Struct (a, b, c, d, e)+deriving via AsStruct (a, b, c, d, e) instance ( BorshSize a , BorshSize b@@ -421,8 +535,17 @@ , BorshSize e ) => BorshSize (a, b, c, d, e)-deriving via Struct (a, b, c, d, e)+deriving via AsStruct (a, b, c, d, e) instance+ ( BorshMaxSize a+ , BorshMaxSize b+ , BorshMaxSize c+ , BorshMaxSize d+ , BorshMaxSize e+ )+ => BorshMaxSize (a, b, c, d, e)+deriving via AsStruct (a, b, c, d, e)+ instance ( ToBorsh a , ToBorsh b , ToBorsh c@@ -430,7 +553,7 @@ , ToBorsh e ) => ToBorsh (a, b, c, d, e)-deriving via Struct (a, b, c, d, e)+deriving via AsStruct (a, b, c, d, e) instance ( FromBorsh a , FromBorsh b@@ -442,7 +565,7 @@ -- size 6 -deriving via Struct (a, b, c, d, e, f)+deriving via AsStruct (a, b, c, d, e, f) instance ( BorshSize a , BorshSize b@@ -452,8 +575,18 @@ , BorshSize f ) => BorshSize (a, b, c, d, e, f)-deriving via Struct (a, b, c, d, e, f)+deriving via AsStruct (a, b, c, d, e, f) instance+ ( BorshMaxSize a+ , BorshMaxSize b+ , BorshMaxSize c+ , BorshMaxSize d+ , BorshMaxSize e+ , BorshMaxSize f+ )+ => BorshMaxSize (a, b, c, d, e, f)+deriving via AsStruct (a, b, c, d, e, f)+ instance ( ToBorsh a , ToBorsh b , ToBorsh c@@ -462,7 +595,7 @@ , ToBorsh f ) => ToBorsh (a, b, c, d, e, f)-deriving via Struct (a, b, c, d, e, f)+deriving via AsStruct (a, b, c, d, e, f) instance ( FromBorsh a , FromBorsh b@@ -475,7 +608,7 @@ -- size 7 -deriving via Struct (a, b, c, d, e, f, g)+deriving via AsStruct (a, b, c, d, e, f, g) instance ( BorshSize a , BorshSize b@@ -486,8 +619,19 @@ , BorshSize g ) => BorshSize (a, b, c, d, e, f, g)-deriving via Struct (a, b, c, d, e, f, g)+deriving via AsStruct (a, b, c, d, e, f, g) instance+ ( BorshMaxSize a+ , BorshMaxSize b+ , BorshMaxSize c+ , BorshMaxSize d+ , BorshMaxSize e+ , BorshMaxSize f+ , BorshMaxSize g+ )+ => BorshMaxSize (a, b, c, d, e, f, g)+deriving via AsStruct (a, b, c, d, e, f, g)+ instance ( ToBorsh a , ToBorsh b , ToBorsh c@@ -497,7 +641,7 @@ , ToBorsh g ) => ToBorsh (a, b, c, d, e, f, g)-deriving via Struct (a, b, c, d, e, f, g)+deriving via AsStruct (a, b, c, d, e, f, g) instance ( FromBorsh a , FromBorsh b@@ -511,7 +655,7 @@ -- size 8 -deriving via Struct (a, b, c, d, e, f, g, h)+deriving via AsStruct (a, b, c, d, e, f, g, h) instance ( BorshSize a , BorshSize b@@ -523,8 +667,20 @@ , BorshSize h ) => BorshSize (a, b, c, d, e, f, g, h)-deriving via Struct (a, b, c, d, e, f, g, h)+deriving via AsStruct (a, b, c, d, e, f, g, h) instance+ ( BorshMaxSize a+ , BorshMaxSize b+ , BorshMaxSize c+ , BorshMaxSize d+ , BorshMaxSize e+ , BorshMaxSize f+ , BorshMaxSize g+ , BorshMaxSize h+ )+ => BorshMaxSize (a, b, c, d, e, f, g, h)+deriving via AsStruct (a, b, c, d, e, f, g, h)+ instance ( ToBorsh a , ToBorsh b , ToBorsh c@@ -535,7 +691,7 @@ , ToBorsh h ) => ToBorsh (a, b, c, d, e, f, g, h)-deriving via Struct (a, b, c, d, e, f, g, h)+deriving via AsStruct (a, b, c, d, e, f, g, h) instance ( FromBorsh a , FromBorsh b@@ -550,7 +706,7 @@ -- size 9 -deriving via Struct (a, b, c, d, e, f, g, h, i)+deriving via AsStruct (a, b, c, d, e, f, g, h, i) instance ( BorshSize a , BorshSize b@@ -563,8 +719,21 @@ , BorshSize i ) => BorshSize (a, b, c, d, e, f, g, h, i)-deriving via Struct (a, b, c, d, e, f, g, h, i)+deriving via AsStruct (a, b, c, d, e, f, g, h, i) instance+ ( BorshMaxSize a+ , BorshMaxSize b+ , BorshMaxSize c+ , BorshMaxSize d+ , BorshMaxSize e+ , BorshMaxSize f+ , BorshMaxSize g+ , BorshMaxSize h+ , BorshMaxSize i+ )+ => BorshMaxSize (a, b, c, d, e, f, g, h, i)+deriving via AsStruct (a, b, c, d, e, f, g, h, i)+ instance ( ToBorsh a , ToBorsh b , ToBorsh c@@ -576,7 +745,7 @@ , ToBorsh i ) => ToBorsh (a, b, c, d, e, f, g, h, i)-deriving via Struct (a, b, c, d, e, f, g, h, i)+deriving via AsStruct (a, b, c, d, e, f, g, h, i) instance ( FromBorsh a , FromBorsh b@@ -592,7 +761,7 @@ -- size 10 -deriving via Struct (a, b, c, d, e, f, g, h, i, j)+deriving via AsStruct (a, b, c, d, e, f, g, h, i, j) instance ( BorshSize a , BorshSize b@@ -606,8 +775,22 @@ , BorshSize j ) => BorshSize (a, b, c, d, e, f, g, h, i, j)-deriving via Struct (a, b, c, d, e, f, g, h, i, j)+deriving via AsStruct (a, b, c, d, e, f, g, h, i, j) instance+ ( BorshMaxSize a+ , BorshMaxSize b+ , BorshMaxSize c+ , BorshMaxSize d+ , BorshMaxSize e+ , BorshMaxSize f+ , BorshMaxSize g+ , BorshMaxSize h+ , BorshMaxSize i+ , BorshMaxSize j+ )+ => BorshMaxSize (a, b, c, d, e, f, g, h, i, j)+deriving via AsStruct (a, b, c, d, e, f, g, h, i, j)+ instance ( ToBorsh a , ToBorsh b , ToBorsh c@@ -620,7 +803,7 @@ , ToBorsh j ) => ToBorsh (a, b, c, d, e, f, g, h, i, j)-deriving via Struct (a, b, c, d, e, f, g, h, i, j)+deriving via AsStruct (a, b, c, d, e, f, g, h, i, j) instance ( FromBorsh a , FromBorsh b@@ -669,6 +852,8 @@ type StaticBorshSize Char = 'HasKnownSize borshSize _ = SizeKnown 4 +deriving via KnownImpliesMax Char instance BorshMaxSize Char+ instance ToBorsh Char where encodeBorsh = encodeChar @@ -681,6 +866,8 @@ type StaticBorshSize Bool = 'HasKnownSize borshSize _ = SizeKnown 1 +deriving via KnownImpliesMax Bool instance BorshMaxSize Bool+ instance ToBorsh Bool where encodeBorsh = encodeBool @@ -689,10 +876,22 @@ -- Either -deriving instance BorshSize (Either a b)-deriving instance (ToBorsh a, ToBorsh b) => ToBorsh (Either a b)-deriving instance (FromBorsh a, FromBorsh b) => FromBorsh (Either a b)+deriving+ via AsEnum (Either a b)+ instance BorshSize (Either a b) +deriving+ via AsEnum (Either a b)+ instance (BorshMaxSize a, BorshMaxSize b) => BorshMaxSize (Either a b)++deriving+ via AsEnum (Either a b)+ instance (ToBorsh a, ToBorsh b) => ToBorsh (Either a b)++deriving+ via AsEnum (Either a b)+ instance (FromBorsh a, FromBorsh b) => FromBorsh (Either a b)+ {------------------------------------------------------------------------------- Internal auxiliary: size of products and sums-of-products -------------------------------------------------------------------------------}@@ -749,7 +948,7 @@ instance All BorshSize xs => BorshSizeSum '[xs] where borshSizeSum _ = -- This assumes the presence of the constructor tag- -- (see detailed discussion in 'Struct')+ -- (see detailed discussion in 'AsStruct') case sizeOfProd (Proxy @xs) of SizeKnown sz -> SizeKnown (sz + 1) SizeVariable -> SizeVariable
src/Codec/Borsh/Incremental/Decoder.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE CPP #-}+ {-# OPTIONS_GHC -Wno-incomplete-patterns #-} module Codec.Borsh.Incremental.Decoder (@@ -16,9 +18,12 @@ import Control.Applicative import Control.Monad-import Control.Monad.Fail import Control.Monad.ST import Data.Word++#if !MIN_VERSION_base(4,13,0)+import Control.Monad.Fail+#endif import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L
test/Test/Codec/Borsh/ExampleType/BTree.hs view
@@ -15,18 +15,8 @@ -- | Binary trees data BTree a = BTip | BLeaf a | BNode (BTree a) (BTree a)- deriving (- Show- , Eq- , Ord- , Functor- , Foldable- , Traversable- , GHC.Generic- , Generic- , BorshSize- , FromBorsh- )+ deriving (Show, Eq, Ord, Functor, Foldable, Traversable, GHC.Generic, Generic)+ deriving (BorshSize, FromBorsh) via AsEnum (BTree a) -- Manual ToBorsh instance as a sort of "golden" test for the derived FromBorsh -- instance
test/Test/Codec/Borsh/ExampleType/NTree.hs view
@@ -15,18 +15,9 @@ -- | N-ary trees data NTree a = NLeaf | NNode a [NTree a]- deriving (- Show- , Eq- , Ord- , Functor- , Foldable- , Traversable- , GHC.Generic- , Generic- , BorshSize- , ToBorsh- )+ deriving (Show, Eq, Ord, Functor, Foldable, Traversable, GHC.Generic, Generic)+ deriving (BorshSize, ToBorsh) via AsEnum (NTree a)+ -- Manual FromBorsh instance as a sort of "golden" test for the derived ToBorsh -- instance instance FromBorsh a => FromBorsh (NTree a) where
test/Test/Codec/Borsh/ExampleType/SimpleStructs.hs view
@@ -19,7 +19,11 @@ data PolyStruct a = Poly a a a deriving (Show, Eq, Ord, GHC.Generic, Generic)- deriving (BorshSize, ToBorsh, FromBorsh) via Struct (PolyStruct a)+ deriving ( BorshSize+ , BorshMaxSize+ , ToBorsh+ , FromBorsh+ ) via AsStruct (PolyStruct a) instance Arbitrary a => Arbitrary (PolyStruct a) where arbitrary = Poly <$> arbitrary <*> arbitrary <*> arbitrary@@ -31,7 +35,11 @@ data SimpleStruct1 = Struct1 Word8 () Word64 deriving (Show, Eq, Ord, GHC.Generic, Generic)- deriving (BorshSize, ToBorsh, FromBorsh) via Struct SimpleStruct1+ deriving ( BorshSize+ , BorshMaxSize+ , ToBorsh+ , FromBorsh+ ) via AsStruct SimpleStruct1 instance Arbitrary SimpleStruct1 where arbitrary = Struct1 <$> arbitrary <*> arbitrary <*> arbitrary@@ -48,7 +56,7 @@ data SimpleStruct2 = Struct2 () SimpleStruct1 Word16 deriving (Show, Eq, Ord, GHC.Generic, Generic)- deriving (ToBorsh, FromBorsh) via Struct SimpleStruct2+ deriving (BorshMaxSize, ToBorsh, FromBorsh) via AsStruct SimpleStruct2 instance BorshSize SimpleStruct2 where type StaticBorshSize SimpleStruct2 = 'HasKnownSize
test/Test/Codec/Borsh/Size.hs view
@@ -1,6 +1,7 @@ module Test.Codec.Borsh.Size (tests) where import Data.Proxy+import Data.SOP.Dict import Test.Tasty import Test.Tasty.QuickCheck @@ -9,10 +10,12 @@ import Codec.Borsh import Test.Codec.Borsh.Util.RandomType+import Test.Codec.Borsh.Util.QuickCheck tests :: TestTree tests = testGroup "Test.Codec.Borsh.Size" [- testProperty "size" test_size+ testProperty "size" test_size+ , testProperty "maxSize" test_maxSize ] test_size :: SomeBorshValue -> Property@@ -24,5 +27,18 @@ case borshSize (Proxy @a) of SizeVariable -> label "Trivial" $ True SizeKnown n -> L.length (serialiseBorsh val) === fromIntegral n+++test_maxSize :: SomeBorshValue -> Property+test_maxSize =+ \(SomeValue typ val) -> aux typ val+ where+ aux :: forall a. ToBorsh a => BorshType a -> a -> Property+ aux typ val =+ case borshTypeMaxSize typ of+ Nothing -> label "Trivial" $ True+ Just Dict ->+ L.length (serialiseBorsh val)+ `assertLE` fromIntegral (borshMaxSize (Proxy @a))
test/Test/Codec/Borsh/Util/QuickCheck.hs view
@@ -5,6 +5,8 @@ -- * Generators , split2 , splitN+ -- * Assertions+ , assertLE ) where import Control.Monad@@ -49,3 +51,13 @@ (xs',y:ys) -> return $ xs' ++ (x:y):ys +{-------------------------------------------------------------------------------+ Assertions+-------------------------------------------------------------------------------}++assertLE :: (Show a, Ord a) => a -> a -> Property+assertLE x y =+ if x <= y then+ property True+ else+ counterexample (show y ++ " > " ++ show x) $ property False
test/Test/Codec/Borsh/Util/RandomType.hs view
@@ -1,16 +1,15 @@ -- 11 iterations needed for GHC 9.2.2 {-# OPTIONS_GHC -fconstraint-solver-iterations=11 #-}+{-# OPTIONS_GHC -Wno-redundant-constraints #-} module Test.Codec.Borsh.Util.RandomType ( -- * Types BorshType(..) , SomeBorshType(..)+ , borshTypeMaxSize+ -- * Values of those types , SomeBorshValue(..)-- -- TODO remove these- , arbitraryValue- , arbitraryType ) where import Control.Monad@@ -327,6 +326,7 @@ BtEnum :: ( All2 CanTest (xs ': xss) , All (Compose Show (NP BorshType)) (xs ': xss)+ , All SListI (xs ': xss) ) => -- POP: For every constructor, and every argument to every constructor, -- what is the type of that argument?@@ -359,6 +359,61 @@ SomeType :: CanTest a => BorshType a -> SomeBorshType deriving instance Show SomeBorshType++{-------------------------------------------------------------------------------+ Maximum size+-------------------------------------------------------------------------------}++borshTypeMaxSize :: BorshType a -> Maybe (Dict BorshMaxSize a)+borshTypeMaxSize = \case+ BtSimple bt -> borshSimpleTypeMaxSize bt+ BtArray _ bt -> do+ Dict <- borshTypeMaxSize bt+ return Dict+ BtVec _ -> Nothing+ BtOption bt -> do+ Dict <- borshTypeMaxSize bt+ return Dict+ BtHashSet _ -> Nothing+ BtHashMap _ _ -> Nothing+ BtStruct xs -> do+ Dict <- fmap all_NP . hsequence' $ hmap (Comp . borshTypeMaxSize) xs+ return Dict+ BtEnum xss -> do+ Dict <- fmap all_POP . hsequence' $ hmap (Comp . borshTypeMaxSize) xss+ return Dict+ BtBTree _ -> Nothing+ BtNTree _ -> Nothing+ BtPolyStruct bt -> do+ Dict <- borshTypeMaxSize bt+ return Dict+ BtEither bt1 bt2 -> do+ Dict <- borshTypeMaxSize bt1+ Dict <- borshTypeMaxSize bt2+ return Dict++borshSimpleTypeMaxSize :: BorshSimpleType a -> Maybe (Dict BorshMaxSize a)+borshSimpleTypeMaxSize = \case+ BtU8 -> Just Dict+ BtU16 -> Just Dict+ BtU32 -> Just Dict+ BtU64 -> Just Dict+ BtU128 -> Just Dict+ BtI8 -> Just Dict+ BtI16 -> Just Dict+ BtI32 -> Just Dict+ BtI64 -> Just Dict+ BtI128 -> Just Dict+ BtF32 -> Just Dict+ BtF64 -> Just Dict+ BtText -> Nothing+ BtUnit -> Just Dict+ BtSimpleList -> Nothing+ BtSimpleStruct1 -> Just Dict+ BtSimpleStruct2 -> Just Dict+ BtByteString -> Nothing+ BtChar -> Just Dict+ BtBool -> Just Dict {------------------------------------------------------------------------------- Shrinking types