module Data.Hash.SL2.ByteVector
( ByteVector()
, singleton, fromList
, empty, append
, cons, snoc
, null, hash, length
, ViewL(..), viewl, viewl1
, ViewR(..), viewr, viewr1
, splitBefore, splitAt
, reverse, map
, foldl, foldr, foldl', foldr'
) where
import Prelude hiding (concat, null, reverse, map, length, foldl, foldr, splitAt)
import Data.Coerce
import qualified Data.Foldable as Foldable
import Data.Monoid
import Data.Hash.SL2 (Hash)
import qualified Data.Hash.SL2 as Hash
import Data.Hash.SL2.Chunk (Chunk(..), fromByteString)
import Data.FingerTree (FingerTree, (<|), (|>))
import qualified Data.FingerTree as FingerTree
import qualified Data.ByteString as ByteString
data Measure = Measure
{ getHash :: !Hash
, getLength :: !Integer
}
instance Monoid Measure where
mempty = Measure mempty 0
mappend a b = Measure (getHash a <> getHash b) (getLength a + getLength b)
mconcat as = Measure (mconcat $ fmap getHash as) (sum $ fmap getLength as)
newtype MeasuredChunk = MeasuredChunk
{ getChunk :: Chunk
} deriving (Eq, Ord)
instance FingerTree.Measured Measure MeasuredChunk where
measure b = Measure (getChunkHash $ coerce b) (fromIntegral . ByteString.length . getChunkBytes $ coerce b)
newtype ByteVector = ByteVector
{ getTree :: FingerTree Measure MeasuredChunk
} deriving (Monoid)
instance Eq ByteVector where
a == b = getHash (FingerTree.measure $ getTree a) == getHash (FingerTree.measure $ getTree b)
instance Ord ByteVector where
compare a b = compare (getHash . FingerTree.measure $ getTree a) (getHash . FingerTree.measure $ getTree b)
empty :: ByteVector
empty = ByteVector mempty
singleton :: Chunk -> ByteVector
singleton = ByteVector . FingerTree.singleton . coerce
fromList :: [Chunk] -> ByteVector
fromList = ByteVector . FingerTree.fromList . fmap coerce
append :: ByteVector -> ByteVector -> ByteVector
append a b = ByteVector $ getTree a <> getTree b
cons :: Chunk -> ByteVector -> ByteVector
cons c v = ByteVector $ coerce c <| coerce v
snoc :: ByteVector -> Chunk -> ByteVector
snoc v c = ByteVector $ coerce v |> coerce c
null :: ByteVector -> Bool
null v = getHash measure == Hash.unit && getLength measure == 0
where measure = FingerTree.measure (getTree v)
hash :: ByteVector -> Hash
hash = getHash . FingerTree.measure . getTree
length :: ByteVector -> Integer
length = getLength . FingerTree.measure . getTree
data ViewL = EmptyL | MostL Chunk ByteVector
deriving (Eq, Ord)
viewl :: ByteVector -> ViewL
viewl v = case FingerTree.viewl (getTree v) of
FingerTree.EmptyL -> EmptyL
most FingerTree.:< rest -> MostL (coerce most) (coerce rest)
viewl1 :: ByteVector -> ViewL
viewl1 v = case viewl v of
EmptyL -> EmptyL
MostL most rest | mempty == most -> viewl1 rest
| otherwise -> MostL most rest
data ViewR = EmptyR | MostR ByteVector Chunk
deriving (Eq, Ord)
viewr :: ByteVector -> ViewR
viewr v = case FingerTree.viewr (getTree v) of
FingerTree.EmptyR -> EmptyR
rest FingerTree.:> most -> MostR (coerce rest) (coerce most)
viewr1 :: ByteVector -> ViewR
viewr1 v = case viewr v of
EmptyR -> EmptyR
MostR rest most | mempty == most -> viewr1 rest
| otherwise -> MostR rest most
splitBefore :: Integer -> ByteVector -> (ByteVector, ByteVector)
splitBefore i v = (coerce left, coerce right)
where (left, right) = FingerTree.split (\m -> getLength m >= i) (getTree v)
splitAt :: Integer -> ByteVector -> (ByteVector, ByteVector)
splitAt i v = splitView (viewl right)
where (left, right) = splitBefore i v
splitView EmptyL = (left, right)
splitView (MostL most rest) = (snoc left (fromByteString left'), cons (fromByteString right') rest)
where (left', right') = ByteString.splitAt (fromIntegral $ i length left) . getChunkBytes $ coerce most
foldl :: (a -> Chunk -> a) -> a -> ByteVector -> a
foldl f a v = Foldable.foldl (\a' -> f a' . getChunk) a (getTree v)
foldr :: (Chunk -> a -> a) -> a -> ByteVector -> a
foldr f a v = Foldable.foldr (\c a' -> f (getChunk c) a') a (getTree v)
foldl' :: (a -> Chunk -> a) -> a -> ByteVector -> a
foldl' f a v = Foldable.foldl' (\a' -> f a' . getChunk) a (getTree v)
foldr' :: (Chunk -> a -> a) -> a -> ByteVector -> a
foldr' f a v = Foldable.foldr' (\c a' -> f (getChunk c) a') a (getTree v)
map :: (Chunk -> Chunk) -> ByteVector -> ByteVector
map f v = ByteVector (FingerTree.fmap' (coerce . f . getChunk) $ coerce v)
reverse :: ByteVector -> ByteVector
reverse v = ByteVector $ FingerTree.reverse $ FingerTree.fmap' (coerce . reverseChunk . coerce) (getTree v)
where reverseChunk c = let r = ByteString.reverse (getChunkBytes c) in Chunk (Hash.hash r) r