dfinity-radix-tree-0.6.0: src/DFINITY/RadixTree/Types.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS -Wall #-}
module DFINITY.RadixTree.Types
( RadixBuffer
, RadixCache
, RadixDatabase(..)
, RadixError(..)
, RadixNode(..)
, RadixPrefix(..)
, RadixProof(..)
, RadixRoot
, RadixSearchResult
, RadixTree(..)
)
where
import Codec.Serialise as CBOR
import Codec.Serialise.Decoding
import Codec.Serialise.Encoding
import Control.DeepSeq
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.ST
import Data.Bool
import Data.ByteString.Base16 as Base16
import Data.ByteString.Char8 as Strict
import Data.ByteString.Short
import Data.Data
import Data.Default.Class
import Data.IORef
import Data.List as List
import Data.List.NonEmpty
import Data.LruCache
import Data.Map.Strict as Map
import Data.Maybe
import Data.STRef
import Data.Word
import Database.LevelDB as LevelDB
import Text.Printf
import DFINITY.RadixTree.Bits
import DFINITY.RadixTree.Serialise
type RadixBuffer = Map RadixRoot RadixNode
type RadixCache = LruCache RadixRoot RadixNode
class Monad m => RadixDatabase m database where
load :: database -> ByteString -> m (Maybe ByteString)
store :: database -> ByteString -> ByteString -> m ()
instance RadixDatabase (ST s) (STRef s (Map ByteString ByteString)) where
load db key = Map.lookup key <$> readSTRef db
store db key val = modifySTRef' db $ Map.insert key val
instance MonadIO m => RadixDatabase m (IORef (Map ByteString ByteString)) where
load db key = liftIO $ Map.lookup key <$> readIORef db
store db key val = liftIO $ modifyIORef' db $ Map.insert key val
instance MonadIO m => RadixDatabase m LevelDB.DB where
load db = LevelDB.get db LevelDB.defaultReadOptions
store db = LevelDB.put db LevelDB.defaultWriteOptions
data RadixError = InvalidArgument String | StateRootDoesNotExist RadixRoot
deriving (Data, Eq, Show)
instance Exception RadixError
data RadixNode =
RadixNode
{ _radixPrefix :: Maybe RadixPrefix
, _radixLeft :: Maybe RadixRoot
, _radixRight :: Maybe RadixRoot
, _radixLeaf :: Maybe ByteString
} deriving (Data, Eq)
instance NFData RadixNode where
rnf RadixNode {..} =
rnf _radixPrefix `seq`
rnf _radixLeft `seq`
rnf _radixRight `seq`
rnf _radixLeaf `seq`
()
instance Default RadixNode where
def = RadixNode Nothing Nothing Nothing Nothing
instance Serialise RadixNode where
encode RadixNode {..} =
encodeListLen len
<> encodeMaybe CBOR.encode _radixPrefix
<> encodeMaybe encodeSide left
<> encodeMaybe encodeSide right
<> maybe mempty encodeBytes _radixLeaf
where
len = bool 3 4 $ isJust _radixLeaf
left = fromShort <$> _radixLeft
right = fromShort <$> _radixRight
decode = do
len <- decodeListLen
_radixPrefix <- decodeMaybe CBOR.decode
_radixLeft <- decodeMaybe $ toShort <$> decodeSide
_radixRight <- decodeMaybe $ toShort <$> decodeSide
_radixLeaf <- decodeLeaf len
pure $ RadixNode {..}
instance Show RadixNode where
show RadixNode {..} =
case show <$> _radixLeaf of
Nothing -> printf "@[\ESC[97m%s\ESC[0m,\ESC[96m%s\ESC[0m,\ESC[96m%s\ESC[0m]" prefix left right
Just leaf -> printf "@[\ESC[97m%s\ESC[0m,\ESC[96m%s\ESC[0m,\ESC[96m%s\ESC[0m,\ESC[97m%s\ESC[0m]" prefix left right leaf
where
prefix = maybe "null" show _radixPrefix
left = maybe "null" pretty $ fromShort <$> _radixLeft
right = maybe "null" pretty $ fromShort <$> _radixRight
pretty = List.take 8 . Strict.unpack . Base16.encode
data RadixPrefix =
RadixPrefix
{ _radixBitLen :: Int
, _radixName :: ShortByteString
} deriving (Data, Eq)
instance Bitable RadixPrefix where
toBits RadixPrefix {..} = List.take _radixBitLen $ toBits _radixName
fromBits bits = RadixPrefix bitLen name
where
bitLen = List.length bits
name = fromBits bits
instance NFData RadixPrefix where
rnf RadixPrefix {..} =
rnf _radixBitLen `seq`
rnf _radixName `seq`
()
instance Serialise RadixPrefix where
encode RadixPrefix {..} =
encodeListLen 2
<> encodeInt _radixBitLen
<> encodeShort _radixName
decode = do
void decodeListLen
RadixPrefix <$> decodeInt <*> decodeShort
instance Show RadixPrefix where
show = List.map compress . toBits
where compress = bool '0' '1'
data RadixProof =
RadixProof
{ _radixPath :: NonEmpty RadixNode
, _radixValue :: ByteString
} deriving (Data, Eq, Show)
instance NFData RadixProof where
rnf RadixProof {..} =
rnf _radixPath `seq`
rnf _radixValue `seq`
()
instance Serialise RadixProof where
encode RadixProof {..} =
CBOR.encode path
where
RadixNode {..} :| nodes = _radixPath
node = RadixNode _radixPrefix _radixLeft _radixRight $ Just _radixValue
path = node :| nodes
decode = do
RadixNode {..} :| nodes <- CBOR.decode
case _radixLeaf of
Nothing -> fail "no proof value"
Just value -> do
let node = RadixNode _radixPrefix _radixLeft _radixRight Nothing
let path = node :| nodes
pure $ RadixProof path value
type RadixRoot = ShortByteString
type RadixSearchResult = (NonEmpty RadixRoot, NonEmpty RadixNode, NonEmpty [Bool], [Bool], [Bool], RadixCache)
data RadixTree database =
RadixTree
{ _radixBuffer :: RadixBuffer
, _radixCache :: RadixCache
, _radixCacheSize :: Int
, _radixCheckpoint :: RadixRoot
, _radixDatabase :: database
, _radixNonce :: Word32
, _radixRoot :: RadixRoot
}