Haskell中未定义长度列表的二进制序列化

mig*_*yte 19 binary serialization haskell stream lazy-evaluation

我一直在使用Data.Binary将数据序列化为文件.在我的应用程序中,我逐步向这些文件添加项目.两个最流行的序列化包,二进制和谷物,两个序列化列表作为计数后跟列表项.因此,我无法附加到我的序列化文件.我目前读取整个文件,反序列化列表,附加到列表,重新序列化列表,并将其写回文件.但是,我的数据集越来越大,我开始耗尽内存.我可以绕过拆箱我的数据结构以获得一些空间,但这种方法不能扩展.

一种解决方案是使文件格式下降和更改以更改初始计数,然后只需附加我的元素.但这并不令人满意,更不用说由于打破抽象而对文件格式的未来变化敏感.Iteratees/Enumerators在这里成为一个有吸引力的选择.我找了一个库,将它们与二进制序列化相结合,但没有找到任何东西.有谁知道这是否已经完成?如果没有,那么这个库是否有用?或者我错过了什么?

Don*_*art 7

所以我说坚持下去,Data.Binary但为可增长的列表编写一个新的实例.这是当前(严格)实例:

instance Binary a => Binary [a] where
    put l  = put (length l) >> mapM_ put l
    get    = do n <- get :: Get Int
                getMany n

-- | 'getMany n' get 'n' elements in order, without blowing the stack.
getMany :: Binary a => Int -> Get [a]
getMany n = go [] n
 where
    go xs 0 = return $! reverse xs
    go xs i = do x <- get
                 x `seq` go (x:xs) (i-1)
{-# INLINE getMany #-}
Run Code Online (Sandbox Code Playgroud)

现在,允许您流式传输(以二进制形式)附加到文件的版本需要急切或懒惰.懒惰的版本是最微不足道的.就像是:

import Data.Binary

newtype Stream a = Stream { unstream :: [a] }

instance Binary a => Binary (Stream a) where

    put (Stream [])     = putWord8 0
    put (Stream (x:xs)) = putWord8 1 >> put x >> put (Stream xs)

    get = do
        t <- getWord8
        case t of
            0 -> return (Stream [])
            1 -> do x         <- get
                    Stream xs <- get
                    return (Stream (x:xs))
Run Code Online (Sandbox Code Playgroud)

适当的按摩适用于流式传输.现在,为了处理静默附加,我们需要能够0在添加更多元素之前寻找文件的末尾并覆盖最终标记.