{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}

module FoundationDB.Versionstamp.Internal where

import Control.DeepSeq (NFData (..))
import Data.ByteString (ByteString)
import Data.Serialize.Get
import Data.Serialize.Put
import Data.Word (Word16, Word64)
import GHC.Generics (Generic)

-- | Represents whether a versionstamp is complete (has been written to FDB and
-- thus contains a full version integer) or incomplete (contains a user-provided
-- version, but has not yet been committed to FDB).
data VersionstampCompleteness = Complete | Incomplete

-- | Represents a version stamp. Version stamps consist of
-- * An 8-byte transaction version
-- * A 2-byte transaction batch order
-- * A 2-byte user version
--
-- The first ten bytes are assigned by FoundationDB to each transaction in such
-- a way that each transaction is numbered in a
-- <https://en.wikipedia.org/wiki/Serializability serializable> order.
--
-- The last two bytes can be used by the user to further distinguish between
-- multiple entities or keys that were committed in one transaction.
data Versionstamp (a :: VersionstampCompleteness) where
  -- | A complete version stamp, consisting of 'TransactionVersionstamp', and a
  -- user version set by the user.
  CompleteVersionstamp ::
    TransactionVersionstamp ->
    Word16 ->
    Versionstamp 'Complete
  -- | A version stamp that has not yet been associated with a completed
  -- transaction. Such a version stamp does not yet have an associated
  -- transaction version and transaction batch order, but does have a user
  -- version.
  IncompleteVersionstamp :: Word16 -> Versionstamp 'Incomplete

deriving instance Show (Versionstamp a)

deriving instance Eq (Versionstamp a)

deriving instance Ord (Versionstamp a)

instance Bounded (Versionstamp 'Complete) where
  minBound :: Versionstamp 'Complete
minBound = TransactionVersionstamp -> Word16 -> Versionstamp 'Complete
CompleteVersionstamp TransactionVersionstamp
forall a. Bounded a => a
minBound Word16
forall a. Bounded a => a
minBound
  maxBound :: Versionstamp 'Complete
maxBound = TransactionVersionstamp -> Word16 -> Versionstamp 'Complete
CompleteVersionstamp TransactionVersionstamp
forall a. Bounded a => a
maxBound Word16
forall a. Bounded a => a
maxBound

instance NFData (Versionstamp a) where
  rnf :: Versionstamp a -> ()
rnf (CompleteVersionstamp TransactionVersionstamp
tv Word16
w) = TransactionVersionstamp -> ()
forall a. NFData a => a -> ()
rnf TransactionVersionstamp
tv () -> () -> ()
`seq` Word16
w Word16 -> () -> ()
`seq` ()
  rnf (IncompleteVersionstamp Word16
w) = Word16
w Word16 -> () -> ()
`seq` ()

-- | A 'TransactionVersionstamp' consists of a monotonically-increasing
-- 8-byte transaction version and a 2-byte transaction batch order. Each
-- committed transaction has an associated 'TransactionVersionstamp'.
data TransactionVersionstamp = TransactionVersionstamp Word64 Word16
  deriving (Int -> TransactionVersionstamp -> ShowS
[TransactionVersionstamp] -> ShowS
TransactionVersionstamp -> String
(Int -> TransactionVersionstamp -> ShowS)
-> (TransactionVersionstamp -> String)
-> ([TransactionVersionstamp] -> ShowS)
-> Show TransactionVersionstamp
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [TransactionVersionstamp] -> ShowS
$cshowList :: [TransactionVersionstamp] -> ShowS
show :: TransactionVersionstamp -> String
$cshow :: TransactionVersionstamp -> String
showsPrec :: Int -> TransactionVersionstamp -> ShowS
$cshowsPrec :: Int -> TransactionVersionstamp -> ShowS
Show, ReadPrec [TransactionVersionstamp]
ReadPrec TransactionVersionstamp
Int -> ReadS TransactionVersionstamp
ReadS [TransactionVersionstamp]
(Int -> ReadS TransactionVersionstamp)
-> ReadS [TransactionVersionstamp]
-> ReadPrec TransactionVersionstamp
-> ReadPrec [TransactionVersionstamp]
-> Read TransactionVersionstamp
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [TransactionVersionstamp]
$creadListPrec :: ReadPrec [TransactionVersionstamp]
readPrec :: ReadPrec TransactionVersionstamp
$creadPrec :: ReadPrec TransactionVersionstamp
readList :: ReadS [TransactionVersionstamp]
$creadList :: ReadS [TransactionVersionstamp]
readsPrec :: Int -> ReadS TransactionVersionstamp
$creadsPrec :: Int -> ReadS TransactionVersionstamp
Read, TransactionVersionstamp -> TransactionVersionstamp -> Bool
(TransactionVersionstamp -> TransactionVersionstamp -> Bool)
-> (TransactionVersionstamp -> TransactionVersionstamp -> Bool)
-> Eq TransactionVersionstamp
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
$c/= :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
== :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
$c== :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
Eq, Eq TransactionVersionstamp
Eq TransactionVersionstamp
-> (TransactionVersionstamp -> TransactionVersionstamp -> Ordering)
-> (TransactionVersionstamp -> TransactionVersionstamp -> Bool)
-> (TransactionVersionstamp -> TransactionVersionstamp -> Bool)
-> (TransactionVersionstamp -> TransactionVersionstamp -> Bool)
-> (TransactionVersionstamp -> TransactionVersionstamp -> Bool)
-> (TransactionVersionstamp
    -> TransactionVersionstamp -> TransactionVersionstamp)
-> (TransactionVersionstamp
    -> TransactionVersionstamp -> TransactionVersionstamp)
-> Ord TransactionVersionstamp
TransactionVersionstamp -> TransactionVersionstamp -> Bool
TransactionVersionstamp -> TransactionVersionstamp -> Ordering
TransactionVersionstamp
-> TransactionVersionstamp -> TransactionVersionstamp
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: TransactionVersionstamp
-> TransactionVersionstamp -> TransactionVersionstamp
$cmin :: TransactionVersionstamp
-> TransactionVersionstamp -> TransactionVersionstamp
max :: TransactionVersionstamp
-> TransactionVersionstamp -> TransactionVersionstamp
$cmax :: TransactionVersionstamp
-> TransactionVersionstamp -> TransactionVersionstamp
>= :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
$c>= :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
> :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
$c> :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
<= :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
$c<= :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
< :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
$c< :: TransactionVersionstamp -> TransactionVersionstamp -> Bool
compare :: TransactionVersionstamp -> TransactionVersionstamp -> Ordering
$ccompare :: TransactionVersionstamp -> TransactionVersionstamp -> Ordering
$cp1Ord :: Eq TransactionVersionstamp
Ord, (forall x.
 TransactionVersionstamp -> Rep TransactionVersionstamp x)
-> (forall x.
    Rep TransactionVersionstamp x -> TransactionVersionstamp)
-> Generic TransactionVersionstamp
forall x. Rep TransactionVersionstamp x -> TransactionVersionstamp
forall x. TransactionVersionstamp -> Rep TransactionVersionstamp x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep TransactionVersionstamp x -> TransactionVersionstamp
$cfrom :: forall x. TransactionVersionstamp -> Rep TransactionVersionstamp x
Generic, TransactionVersionstamp -> ()
(TransactionVersionstamp -> ()) -> NFData TransactionVersionstamp
forall a. (a -> ()) -> NFData a
rnf :: TransactionVersionstamp -> ()
$crnf :: TransactionVersionstamp -> ()
NFData, TransactionVersionstamp
TransactionVersionstamp
-> TransactionVersionstamp -> Bounded TransactionVersionstamp
forall a. a -> a -> Bounded a
maxBound :: TransactionVersionstamp
$cmaxBound :: TransactionVersionstamp
minBound :: TransactionVersionstamp
$cminBound :: TransactionVersionstamp
Bounded)

putTransactionVersionstamp :: Putter TransactionVersionstamp
putTransactionVersionstamp :: Putter TransactionVersionstamp
putTransactionVersionstamp (TransactionVersionstamp Word64
tv Word16
tb) = do
  Putter Word64
putWord64be Word64
tv
  Putter Word16
putWord16be Word16
tb

encodeTransactionVersionstamp :: TransactionVersionstamp -> ByteString
encodeTransactionVersionstamp :: TransactionVersionstamp -> ByteString
encodeTransactionVersionstamp = Put -> ByteString
runPut (Put -> ByteString)
-> Putter TransactionVersionstamp
-> TransactionVersionstamp
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Putter TransactionVersionstamp
putTransactionVersionstamp

putVersionstamp :: Putter (Versionstamp a)
putVersionstamp :: Putter (Versionstamp a)
putVersionstamp (CompleteVersionstamp TransactionVersionstamp
tvs Word16
uv) = do
  Putter TransactionVersionstamp
putTransactionVersionstamp TransactionVersionstamp
tvs
  Putter Word16
putWord16be Word16
uv
putVersionstamp (IncompleteVersionstamp Word16
uv) = do
  Putter Word64
putWord64be Word64
forall a. Bounded a => a
maxBound
  Putter Word16
putWord16be Word16
forall a. Bounded a => a
maxBound
  Putter Word16
putWord16be Word16
uv

-- | Encodes a versionstamp into a bytestring. You probably don't need this;
-- see the facilities in "FoundationDB.Layer.Tuple" for a more flexible
-- alternative.
encodeVersionstamp :: Versionstamp a -> ByteString
encodeVersionstamp :: Versionstamp a -> ByteString
encodeVersionstamp = Put -> ByteString
runPut (Put -> ByteString)
-> (Versionstamp a -> Put) -> Versionstamp a -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Versionstamp a -> Put
forall (a :: VersionstampCompleteness). Putter (Versionstamp a)
putVersionstamp

getTransactionVersionstamp :: Get TransactionVersionstamp
getTransactionVersionstamp :: Get TransactionVersionstamp
getTransactionVersionstamp =
  Word64 -> Word16 -> TransactionVersionstamp
TransactionVersionstamp (Word64 -> Word16 -> TransactionVersionstamp)
-> Get Word64 -> Get (Word16 -> TransactionVersionstamp)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word64
getWord64be Get (Word16 -> TransactionVersionstamp)
-> Get Word16 -> Get TransactionVersionstamp
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Get Word16
getWord16be

getVersionstampComplete :: Get (Versionstamp 'Complete)
getVersionstampComplete :: Get (Versionstamp 'Complete)
getVersionstampComplete =
  TransactionVersionstamp -> Word16 -> Versionstamp 'Complete
CompleteVersionstamp (TransactionVersionstamp -> Word16 -> Versionstamp 'Complete)
-> Get TransactionVersionstamp
-> Get (Word16 -> Versionstamp 'Complete)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get TransactionVersionstamp
getTransactionVersionstamp Get (Word16 -> Versionstamp 'Complete)
-> Get Word16 -> Get (Versionstamp 'Complete)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Get Word16
getWord16be

-- | Decode a versionstamp from a raw bytestring. You probably don't need this;
-- see the facilities in "FoundationDB.Layer.Tuple" for a more flexible
-- alternative.
decodeVersionstamp :: ByteString -> Maybe (Versionstamp 'Complete)
decodeVersionstamp :: ByteString -> Maybe (Versionstamp 'Complete)
decodeVersionstamp ByteString
bs = case Get (Versionstamp 'Complete)
-> ByteString -> Either String (Versionstamp 'Complete)
forall a. Get a -> ByteString -> Either String a
runGet Get (Versionstamp 'Complete)
getVersionstampComplete ByteString
bs of
  Left String
_ -> Maybe (Versionstamp 'Complete)
forall a. Maybe a
Nothing
  Right Versionstamp 'Complete
x -> Versionstamp 'Complete -> Maybe (Versionstamp 'Complete)
forall a. a -> Maybe a
Just Versionstamp 'Complete
x

decodeTransactionVersionstamp :: ByteString -> Maybe TransactionVersionstamp
decodeTransactionVersionstamp :: ByteString -> Maybe TransactionVersionstamp
decodeTransactionVersionstamp ByteString
bs = case Get TransactionVersionstamp
-> ByteString -> Either String TransactionVersionstamp
forall a. Get a -> ByteString -> Either String a
runGet Get TransactionVersionstamp
getTransactionVersionstamp ByteString
bs of
  Left String
_ -> Maybe TransactionVersionstamp
forall a. Maybe a
Nothing
  Right TransactionVersionstamp
x -> TransactionVersionstamp -> Maybe TransactionVersionstamp
forall a. a -> Maybe a
Just TransactionVersionstamp
x