module Data.Acid.Archive
( Entry
, Entries(..)
, putEntries
, packEntries
, readEntries
, entriesToList
, entriesToListNoFail
) where
import Data.Acid.CRC
import qualified Data.ByteString.Lazy as Lazy
import qualified Data.ByteString as Strict
import Data.Binary.Get
import Data.Binary.Builder
import Data.Monoid
type Entry = Lazy.ByteString
data Entries = Done | Next Entry Entries | Fail String
deriving (Show)
entriesToList :: Entries -> [Entry]
entriesToList Done = []
entriesToList (Next entry next) = entry : entriesToList next
entriesToList (Fail msg) = fail msg
entriesToListNoFail :: Entries -> [Entry]
entriesToListNoFail Done = []
entriesToListNoFail (Next entry next) = entry : entriesToListNoFail next
entriesToListNoFail Fail{} = []
putEntry :: Entry -> Builder
putEntry content
= putWord64le contentLength `mappend`
putWord16le contentHash `mappend`
fromLazyByteString content
where contentLength = fromIntegral $ Lazy.length content
contentHash = crc16 content
putEntries :: [Entry] -> Builder
putEntries = mconcat . map putEntry
packEntries :: [Entry] -> Lazy.ByteString
packEntries = toLazyByteString . putEntries
readEntries :: Lazy.ByteString -> Entries
readEntries bs
| Lazy.null bs
= Done
| Lazy.length header < headerSize
= Fail "Incomplete header."
| Lazy.length content /= fromIntegral contentLength
= Fail "Insuficient content."
| crc16 content /= contentHash
= Fail "Invalid hash"
| otherwise
= Next content (readEntries rest)
where header = Lazy.take headerSize bs
headerSize = 10
contentLength = fromIntegral $ runGet getWord64le header
contentHash = runGet getWord16le $ Lazy.drop 8 header
content = Lazy.take contentLength $ Lazy.drop headerSize bs
rest = Lazy.drop (contentLength+headerSize) bs
lazyToStrict :: Lazy.ByteString -> Strict.ByteString
lazyToStrict = Strict.concat . Lazy.toChunks