-- | Sequences which are stored compactly in memory
-- by serialising their contents as a @ByteString@.
module Data.PackedSequence(PackedSequence, empty, null, size, fromList, toList, uncons) where

import Prelude hiding (null)
import Data.Serialize
import Data.ByteString(ByteString)
import qualified Data.ByteString as BS
import Data.List(unfoldr)

-- | A sequence, stored in a serialised form
data PackedSequence a =
  Seq {-# UNPACK #-} !Int {-# UNPACK #-} !ByteString
  deriving PackedSequence a -> PackedSequence a -> Bool
forall a. PackedSequence a -> PackedSequence a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PackedSequence a -> PackedSequence a -> Bool
$c/= :: forall a. PackedSequence a -> PackedSequence a -> Bool
== :: PackedSequence a -> PackedSequence a -> Bool
$c== :: forall a. PackedSequence a -> PackedSequence a -> Bool
Eq

-- | An empty sequence.
empty :: PackedSequence a
empty :: forall a. PackedSequence a
empty = forall a. Int -> ByteString -> PackedSequence a
Seq Int
0 ByteString
BS.empty

-- | Is a given sequency empty?
null :: PackedSequence a -> Bool
null :: forall a. PackedSequence a -> Bool
null PackedSequence a
s = forall a. PackedSequence a -> Int
size PackedSequence a
s forall a. Eq a => a -> a -> Bool
== Int
0

-- | Find the number of items in a sequence.
size :: PackedSequence a -> Int
size :: forall a. PackedSequence a -> Int
size (Seq Int
n ByteString
_) = Int
n

-- | Convert a list into a sequence.
{-# INLINEABLE fromList #-}
fromList :: Serialize a => [a] -> PackedSequence a
fromList :: forall a. Serialize a => [a] -> PackedSequence a
fromList [a]
xs = forall a. Int -> ByteString -> PackedSequence a
Seq (forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
xs) (Put -> ByteString
runPut (forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ forall t. Serialize t => Putter t
put [a]
xs))

-- | Convert a sequence into a list.
{-# INLINEABLE toList #-}
toList :: Serialize a => PackedSequence a -> [a]
toList :: forall a. Serialize a => PackedSequence a -> [a]
toList = forall b a. (b -> Maybe (a, b)) -> b -> [a]
unfoldr forall a.
Serialize a =>
PackedSequence a -> Maybe (a, PackedSequence a)
uncons

-- | Find and remove the first value from a sequence.
{-# INLINEABLE uncons #-}
uncons :: Serialize a => PackedSequence a -> Maybe (a, PackedSequence a)
uncons :: forall a.
Serialize a =>
PackedSequence a -> Maybe (a, PackedSequence a)
uncons (Seq Int
0 ByteString
_) = forall a. Maybe a
Nothing
uncons (Seq Int
n ByteString
bs) =
  forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ case forall a.
Get a -> ByteString -> Int -> Either String (a, ByteString)
runGetState forall t. Serialize t => Get t
get ByteString
bs Int
0 of
    Left String
err -> forall a. HasCallStack => String -> a
error String
err
    Right (a
x, ByteString
bs) -> (a
x, forall a. Int -> ByteString -> PackedSequence a
Seq (Int
nforall a. Num a => a -> a -> a
-Int
1) ByteString
bs)