{-# OPTIONS_HADDOCK not-home #-} -- | Public API for serialisation of keys, blobs and values -- module Database.LSMTree.Internal.Serialise.Class ( -- * SerialiseKey SerialiseKey (..) , serialiseKeyIdentity , serialiseKeyIdentityUpToSlicing , SerialiseKeyOrderPreserving , serialiseKeyPreservesOrdering -- * SerialiseValue , SerialiseValue (..) , serialiseValueIdentity , serialiseValueIdentityUpToSlicing -- * RawBytes , RawBytes (..) , packSlice -- * Errors , requireBytesExactly ) where import qualified Data.ByteString as BS import qualified Data.ByteString.Builder as B import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Short.Internal as SBS import qualified Data.ByteString.UTF8 as UTF8 import Data.Int (Int16, Int32, Int64, Int8) import Data.Monoid (Sum (..)) import qualified Data.Primitive as P import qualified Data.Vector.Primitive as VP import Data.Void (Void, absurd) import Data.Word (Word16, Word32, Word64, Word8) import Database.LSMTree.Internal.ByteString (byteArrayToSBS) import Database.LSMTree.Internal.Primitive import Database.LSMTree.Internal.RawBytes (RawBytes (..)) import qualified Database.LSMTree.Internal.RawBytes as RB import Database.LSMTree.Internal.Vector import Numeric (showInt) {------------------------------------------------------------------------------- SerialiseKey -------------------------------------------------------------------------------} {- | Serialisation of keys. Instances should satisfy the following laws: [Identity] @'deserialiseKey' ('serialiseKey' x) == x@ [Identity up to slicing] @'deserialiseKey' ('packSlice' prefix ('serialiseKey' x) suffix) == x@ -} class SerialiseKey k where serialiseKey :: k -> RawBytes -- TODO: 'deserialiseKey' is only strictly necessary for range queries. -- It might make sense to move it to a separate class, which could also -- require total deserialisation (potentially using 'Either'). deserialiseKey :: RawBytes -> k -- | Test the __Identity__ law for the 'SerialiseKey' class serialiseKeyIdentity :: (Eq k, SerialiseKey k) => k -> Bool serialiseKeyIdentity x = deserialiseKey (serialiseKey x) == x -- | Test the __Identity up to slicing__ law for the 'SerialiseKey' class serialiseKeyIdentityUpToSlicing :: (Eq k, SerialiseKey k) => RawBytes -> k -> RawBytes -> Bool serialiseKeyIdentityUpToSlicing prefix x suffix = deserialiseKey (packSlice prefix (serialiseKey x) suffix) == x {- | Order-preserving serialisation of keys. Table data is sorted by /serialised/ keys. Range lookups and cursors return entries in this order. If serialisation does not preserve the ordering of /unserialised/ keys, then range lookups and cursors return entries out of order. If the 'SerialiseKey' instance for a type preserves the ordering, then it can safely be given an instance of 'SerialiseKeyOrderPreserving'. These should satisfy the following law: [Order-preserving] @x \`'compare'\` y == 'serialiseKey' x \`'compare'\` 'serialiseKey' y@ Serialised keys are lexicographically ordered. To satisfy the __Order-preserving__ law, keys should be serialised into a big-endian format. -} class SerialiseKey k => SerialiseKeyOrderPreserving k where -- | Test the __Order-preserving__ law for the 'SerialiseKeyOrderPreserving' class serialiseKeyPreservesOrdering :: (Ord k, SerialiseKey k) => k -> k -> Bool serialiseKeyPreservesOrdering x y = x `compare` y == serialiseKey x `compare` serialiseKey y {------------------------------------------------------------------------------- SerialiseValue -------------------------------------------------------------------------------} {- | Serialisation of values and blobs. Instances should satisfy the following laws: [Identity] @'deserialiseValue' ('serialiseValue' x) == x@ [Identity up to slicing] @'deserialiseValue' ('packSlice' prefix ('serialiseValue' x) suffix) == x@ -} class SerialiseValue v where serialiseValue :: v -> RawBytes deserialiseValue :: RawBytes -> v -- | Test the __Identity__ law for the 'SerialiseValue' class serialiseValueIdentity :: (Eq v, SerialiseValue v) => v -> Bool serialiseValueIdentity x = deserialiseValue (serialiseValue x) == x -- | Test the __Identity up to slicing__ law for the 'SerialiseValue' class serialiseValueIdentityUpToSlicing :: (Eq v, SerialiseValue v) => RawBytes -> v -> RawBytes -> Bool serialiseValueIdentityUpToSlicing prefix x suffix = deserialiseValue (packSlice prefix (serialiseValue x) suffix) == x {------------------------------------------------------------------------------- RawBytes -------------------------------------------------------------------------------} -- | @'packSlice' prefix x suffix@ makes @x@ into a slice with @prefix@ bytes on -- the left and @suffix@ bytes on the right. packSlice :: RawBytes -> RawBytes -> RawBytes -> RawBytes packSlice prefix x suffix = RB.take (RB.size x) (RB.drop (RB.size prefix) (prefix <> x <> suffix)) {------------------------------------------------------------------------------- Errors -------------------------------------------------------------------------------} -- | @'requireBytesExactly' tyName expected actual x@ requireBytesExactly :: String -> Int -> Int -> a -> a requireBytesExactly tyName expected actual x | expected == actual = x | otherwise = error $ showString "deserialise " . showString tyName . showString ": expected " . showInt expected . showString " bytes, but got " . showInt actual $ "" {------------------------------------------------------------------------------- Int -------------------------------------------------------------------------------} {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Int8 where serialiseKey x = RB.RawBytes $ byteVectorFromPrim x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int8" 1 len $ indexInt8Array ba off {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Int8 where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int8" 1 len $ indexInt8Array ba off {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Int16 where serialiseKey x = RB.RawBytes $ byteVectorFromPrim $ byteSwapInt16 x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int16" 2 len $ byteSwapInt16 (indexWord8ArrayAsInt16 ba off) {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Int16 where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int16" 2 len $ indexWord8ArrayAsInt16 ba off {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Int32 where serialiseKey x = RB.RawBytes $ byteVectorFromPrim $ byteSwapInt32 x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int32" 4 len $ byteSwapInt32 (indexWord8ArrayAsInt32 ba off) {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Int32 where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int32" 4 len $ indexWord8ArrayAsInt32 ba off {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Int64 where serialiseKey x = RB.RawBytes $ byteVectorFromPrim $ byteSwapInt64 x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int64" 8 len $ byteSwapInt64 (indexWord8ArrayAsInt64 ba off) {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Int64 where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int64" 8 len $ indexWord8ArrayAsInt64 ba off {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Int where serialiseKey x = RB.RawBytes $ byteVectorFromPrim $ byteSwapInt x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int" 8 len $ byteSwapInt (indexWord8ArrayAsInt ba off) {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Int where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Int" 8 len $ indexWord8ArrayAsInt ba off {------------------------------------------------------------------------------- Word -------------------------------------------------------------------------------} {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Word8 where serialiseKey x = RB.RawBytes $ byteVectorFromPrim x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word8" 1 len (indexWord8Array ba off) instance SerialiseKeyOrderPreserving Word8 {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Word8 where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word8" 1 len $ indexWord8Array ba off {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Word16 where serialiseKey x = RB.RawBytes $ byteVectorFromPrim $ byteSwapWord16 x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word16" 2 len $ byteSwapWord16 (indexWord8ArrayAsWord16 ba off) instance SerialiseKeyOrderPreserving Word16 {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Word16 where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word16" 2 len $ indexWord8ArrayAsWord16 ba off {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Word32 where serialiseKey x = RB.RawBytes $ byteVectorFromPrim $ byteSwapWord32 x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word32" 4 len $ byteSwapWord32 (indexWord8ArrayAsWord32 ba off) instance SerialiseKeyOrderPreserving Word32 {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Word32 where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word32" 4 len $ indexWord8ArrayAsWord32 ba off {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Word64 where serialiseKey x = RB.RawBytes $ byteVectorFromPrim $ byteSwapWord64 x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word64" 8 len $ byteSwapWord64 (indexWord8ArrayAsWord64 ba off) instance SerialiseKeyOrderPreserving Word64 {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Word64 where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word64" 8 len $ indexWord8ArrayAsWord64 ba off {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(1)\). -} instance SerialiseKey Word where serialiseKey x = RB.RawBytes $ byteVectorFromPrim $ byteSwapWord x deserialiseKey (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word" 8 len $ byteSwapWord (indexWord8ArrayAsWord ba off) instance SerialiseKeyOrderPreserving Word {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(1)\). -} instance SerialiseValue Word where serialiseValue x = RB.RawBytes $ byteVectorFromPrim $ x deserialiseValue (RawBytes (VP.Vector off len ba)) = requireBytesExactly "Word" 8 len $ indexWord8ArrayAsWord ba off {------------------------------------------------------------------------------- String -------------------------------------------------------------------------------} {- | @'serialiseKey'@: \(O(n)\). @'deserialiseKey'@: \(O(n)\). The 'String' is (de)serialised as UTF-8. -} instance SerialiseKey String where -- TODO: Optimise. The performance is \(O(n) + O(n)\) but it could be \(O(n)\). serialiseKey = serialiseKey . UTF8.fromString deserialiseKey = UTF8.toString . deserialiseKey instance SerialiseKeyOrderPreserving String {- | @'serialiseKey'@: \(O(n)\). @'deserialiseKey'@: \(O(n)\). The 'String' is (de)serialised as UTF-8. -} instance SerialiseValue String where -- TODO: Optimise. The performance is \(O(n) + O(n)\) but it could be \(O(n)\). serialiseValue = serialiseValue . UTF8.fromString deserialiseValue = UTF8.toString . deserialiseValue {------------------------------------------------------------------------------- ByteString -------------------------------------------------------------------------------} {- | @'serialiseKey'@: \(O(n)\). @'deserialiseKey'@: \(O(n)\). -} instance SerialiseKey LBS.ByteString where -- TODO: Optimise. The performance is \(O(n) + O(n)\) but it could be \(O(n)\). serialiseKey = serialiseKey . LBS.toStrict deserialiseKey = B.toLazyByteString . RB.builder instance SerialiseKeyOrderPreserving LBS.ByteString {- | @'serialiseValue'@: \(O(n)\). @'deserialiseValue'@: \(O(n)\). -} instance SerialiseValue LBS.ByteString where -- TODO: Optimise. The performance is \(O(n) + O(n)\) but it could be \(O(n)\). serialiseValue = serialiseValue . LBS.toStrict deserialiseValue = B.toLazyByteString . RB.builder {- | @'serialiseKey'@: \(O(n)\). @'deserialiseKey'@: \(O(n)\). -} instance SerialiseKey BS.ByteString where serialiseKey = serialiseKey . SBS.toShort deserialiseKey = SBS.fromShort . deserialiseKey instance SerialiseKeyOrderPreserving BS.ByteString {- | @'serialiseValue'@: \(O(n)\). @'deserialiseValue'@: \(O(n)\). -} instance SerialiseValue BS.ByteString where serialiseValue = serialiseValue . SBS.toShort deserialiseValue = SBS.fromShort . deserialiseValue {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(n)\). -} instance SerialiseKey SBS.ShortByteString where serialiseKey = RB.fromShortByteString deserialiseKey = byteArrayToSBS . RB.force instance SerialiseKeyOrderPreserving SBS.ShortByteString {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(n)\). -} instance SerialiseValue SBS.ShortByteString where serialiseValue = RB.fromShortByteString deserialiseValue = byteArrayToSBS . RB.force {------------------------------------------------------------------------------- ByteArray -------------------------------------------------------------------------------} {- | @'serialiseKey'@: \(O(1)\). @'deserialiseKey'@: \(O(n)\). -} instance SerialiseKey P.ByteArray where serialiseKey ba = RB.fromByteArray 0 (P.sizeofByteArray ba) ba deserialiseKey = RB.force {- | @'serialiseValue'@: \(O(1)\). @'deserialiseValue'@: \(O(n)\). -} instance SerialiseValue P.ByteArray where serialiseValue ba = RB.fromByteArray 0 (P.sizeofByteArray ba) ba deserialiseValue = RB.force {------------------------------------------------------------------------------- Void -------------------------------------------------------------------------------} -- | The implementation of 'deserialiseKey' throws an exception. instance SerialiseKey Void where serialiseKey = absurd deserialiseKey = error "deserialiseKey: cannot deserialise into Void" -- | The implementation of 'deserialiseValue' throws an exception. instance SerialiseValue Void where serialiseValue = absurd deserialiseValue = error "deserialiseValue: cannot deserialise into Void" {------------------------------------------------------------------------------- Sum -------------------------------------------------------------------------------} {- | An instance for 'Sum' which is transparent to the serialisation of the value type. __NOTE:__ If you want to serialise @'Sum' a@ differently from @a@, you must use another newtype wrapper. -} instance SerialiseValue a => SerialiseValue (Sum a) where serialiseValue (Sum v) = serialiseValue v deserialiseValue = Sum . deserialiseValue