b-tree-0.1.4: src/BTree/Types.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE RecordWildCards #-}
module BTree.Types where
import Control.Applicative
import Data.Maybe (fromMaybe)
import GHC.Generics
import Control.Monad (when)
import Data.Int
import Prelude
import Data.Binary
import Data.Binary.Get
import Data.Binary.Put
import Control.Lens
import qualified Data.Vector as V
import Data.Vector.Binary
import qualified Data.ByteString as BS
-- | An offset within the stream
type Offset = Int64
-- | The number of entries in a B-tree
type Size = Word64
-- | The maximum number of children of a B-tree inner node
type Order = Word64
-- | @'OnDisk' a@ is a reference to an object of type @a@ on disk.
-- The offset does not include the header; e.g. the first object after
-- the header is located at offset 0.
newtype OnDisk a = OnDisk Offset
deriving (Show, Eq, Ord)
instance Binary (OnDisk a) where
get = OnDisk <$> get
{-# INLINE get #-}
put (OnDisk off) = put off
{-# INLINE put #-}
-- | A tree leaf (e.g. key/value pair)
data BLeaf k e = BLeaf !k !e
deriving (Generic, Functor)
deriving instance (Show k, Show e) => Show (BLeaf k e)
-- | This only compares on the keys
instance (Eq k) => Eq (BLeaf k e) where
BLeaf a _ == BLeaf b _ = a == b
-- | This only compares on the keys
instance Ord k => Ord (BLeaf k e) where
compare (BLeaf a _) (BLeaf b _) = compare a b
{-# INLINE compare #-}
instance (Binary k, Binary e) => Binary (BLeaf k e) where
get = BLeaf <$> get <*> get
{-# INLINE get #-}
put (BLeaf k e) = put k >> put e
{-# INLINE put #-}
-- | @'BTree' k f e@ is a B* tree of key type @k@ with elements of type @e@.
-- Subtree references are contained within a type @f@.
--
-- The 'Node' constructor contains a left child, and a list of key/child pairs
-- where each child's keys are greater than or equal to the given key.
data BTree k f e = Node (f (BTree k f e)) (V.Vector (k, f (BTree k f e)))
| Leaf !(BLeaf k e)
deriving (Generic)
deriving instance (Show e, Show k, Show (f (BTree k f e))) => Show (BTree k f e)
deriving instance (Eq e, Eq k, Eq (f (BTree k f e))) => Eq (BTree k f e)
instance (Binary k, Binary (f (BTree k f e)), Binary e)
=> Binary (BTree k f e) where
get = do typ <- getWord8
case typ of
0 -> Node <$> get <*> getChildren
1 -> bleaf <$> get <*> get
_ -> fail "BTree.Types/get: Unknown node type"
where bleaf k v = Leaf (BLeaf k v)
getChildren =
genericGetVectorWith (fromIntegral <$> getWord32be) ((,) <$> get <*> get)
{-# INLINE get #-}
-- some versions of binary don't inline the Binary (,) instance, pitiful
-- performance ensues
put (Node e0 es) = do
putWord8 0
put e0
genericPutVectorWith (putWord32be . fromIntegral) (\(a,b) -> put a >> put b) es
put (Leaf (BLeaf k0 e)) = putWord8 1 >> put k0 >> put e
{-# INLINE put #-}
magic :: Word64
magic = 0xdeadbeefbbbbcccc
-- | B-tree file header
data BTreeHeader k e = BTreeHeader { _btMagic :: !Word64
, _btVersion :: !Word64
, _btOrder :: !Order
, _btSize :: !Size
, _btRoot :: !(Maybe (OnDisk (BTree k OnDisk e)))
-- ^ 'Nothing' represents an empty tree
}
deriving (Show, Eq, Generic)
makeLenses ''BTreeHeader
-- | It is critical that this encoding is of fixed size
instance Binary (BTreeHeader k e) where
get = do
_btMagic <- get
_btVersion <- get
_btOrder <- get
_btSize <- get
root <- get
let _btRoot = if root == OnDisk 0 then Nothing else Just root
return BTreeHeader {..}
put (BTreeHeader {..}) = do
put _btMagic
put _btVersion
put _btOrder
put _btSize
put $ fromMaybe (OnDisk 0) _btRoot
validateHeader :: BTreeHeader k e -> Either String ()
validateHeader hdr = do
when (hdr^.btMagic /= magic) $ Left "Invalid magic number"
when (hdr^.btVersion > 1) $ Left "Invalid version"
-- | A read-only B-tree for lookups
data LookupTree k e = LookupTree { _ltData :: !BS.ByteString
, _ltHeader :: !(BTreeHeader k e)
}
makeLenses ''LookupTree