1+ {-# LANGUAGE DeriveAnyClass #-}
12{-# LANGUAGE FlexibleInstances #-}
3+ {-# LANGUAGE ViewPatterns #-}
24
35module PlutusCore.Value (
4- Value (.. ),
6+ Value , -- Do not expose data constructor
7+ NestedMap ,
8+ unpack ,
9+ pack ,
510 empty ,
611 fromList ,
712 toList ,
813 totalSize ,
14+ maxInnerSize ,
915) where
1016
1117import Codec.Serialise (Serialise )
@@ -14,33 +20,77 @@ import Data.Bifunctor
1420import Data.ByteString (ByteString )
1521import Data.ByteString.Base64 qualified as Base64
1622import Data.Hashable (Hashable )
23+ import Data.IntMap.Strict (IntMap )
24+ import Data.IntMap.Strict qualified as IntMap
1725import Data.Map.Strict (Map )
1826import Data.Map.Strict qualified as Map
1927import Data.Text.Encoding qualified as Text
28+ import GHC.Generics
2029
2130import PlutusPrelude (Pretty (.. ))
2231
23- newtype Value = Value { unValue :: Map ByteString (Map ByteString Integer )}
24- deriving newtype (Eq , Show , Hashable , Serialise , NFData )
32+ type NestedMap = Map ByteString (Map ByteString Integer )
33+
34+ -- | The underlying type of the UPLC built-in type @Value@.
35+ data Value
36+ = Value
37+ ! NestedMap
38+ {- ^ Map from (currency symbol, token name) to amount.
39+
40+ Invariants: no empty inner map, and no zero amount.
41+ -}
42+ !(IntMap Int )
43+ {- ^ Map from size to the number of inner maps that have that size,
44+ useful for efficient retrieval of the size of the largest inner map.
45+
46+ Invariant: all values are positive.
47+ -}
48+ {- # UNPACK #-} !Int
49+ -- ^ Total size, i.e., sum total of inner map sizes
50+ deriving stock (Eq , Show , Generic )
51+ deriving anyclass (Hashable , Serialise , NFData )
52+
53+ {-| Unpack a `Value` into a map from (currency symbol, token name) to amount.
54+
55+ The map is guaranteed to not contain empty inner map or zero amount.
56+ -}
57+ unpack :: Value -> NestedMap
58+ unpack (Value v _ _) = v
59+
60+ {-| Pack a map from (currency symbol, token name) to amount into a `Value`.
61+
62+ The map will be filtered so that it does not contain empty inner map or zero amount.
63+ -}
64+ pack :: NestedMap -> Value
65+ pack (normalize -> v) = Value v sizes size
66+ where
67+ sizes = Map. foldr' (IntMap. alter (maybe (Just 1 ) (Just . (+ 1 ))) . Map. size) mempty v
68+ size = Map. foldr' ((+) . Map. size) 0 v
69+
70+ {-| Total size, i.e., the number of distinct `(currency symbol, token name)` pairs
71+ contained in the `Value`.
72+ -}
73+ totalSize :: Value -> Int
74+ totalSize (Value _ _ size) = size
75+
76+ -- | Size of the largest inner map.
77+ maxInnerSize :: Value -> Int
78+ maxInnerSize (Value _ sizes _) = maybe 0 fst (IntMap. lookupMax sizes)
2579
2680empty :: Value
27- empty = Value mempty
81+ empty = Value mempty mempty 0
2882
2983toList :: Value -> [(ByteString , [(ByteString , Integer )])]
30- toList = Map. toList . Map. map Map. toList . unValue
84+ toList = Map. toList . Map. map Map. toList . unpack
3185
3286fromList :: [(ByteString , [(ByteString , Integer )])] -> Value
3387fromList =
34- normalize
35- . Value
88+ pack
3689 . Map. fromListWith (Map. unionWith (+) )
3790 . fmap (second (Map. fromListWith (+) ))
3891
39- normalize :: Value -> Value
40- normalize = Value . Map. filter (not . Map. null ) . Map. map (Map. filter (/= 0 )) . unValue
41-
42- totalSize :: Value -> Int
43- totalSize = Map. foldl' (\ n -> (n + ) . Map. size) 0 . unValue
92+ normalize :: NestedMap -> NestedMap
93+ normalize = Map. filter (not . Map. null ) . Map. map (Map. filter (/= 0 ))
4494
4595instance Pretty Value where
4696 pretty = pretty . fmap (bimap toText (fmap (first toText))) . toList
0 commit comments