我正在编写一个与大型(10-1000 GB)内存映射二进制文件交互的应用程序,该文件本质上保存着一堆相互引用的对象。我想出了一种读/写这些数据的机制,它很有效,但丑陋且冗长(imo)。
问:有没有更优雅的方式来实现我所做的事情?
我有一个用于结构化数据的类型类,其中有一种方法可以将结构读入 Haskell 数据类型(DataOp
是围绕 IO
的 ReaderT
)。
class DBStruct a where
structRead :: Addr a -> DataOp a
为了使其更具可读性,我有另一个类型类,它定义了结构成员的去向:
class DBStruct st => StructMem structTy valTy name | structTy name -> valTy where
offset :: structTy -> valTy -> name -> Int64
我有一些辅助函数,它们使用偏移方法来读取/写入结构元素、从存储的引用中读取结构以及延迟延迟结构读取(以允许延迟读取整个文件)。
这样做的问题是它涉及大量的重复使用。对于一个结构,我首先必须定义 Haskell 类型:
data RowBlock = RowBlock {rbNext :: Maybe RowBlock
,rbPrev :: Maybe RowBlock
,rbRows :: [RowTy]
}
然后名称
类型:
data Next = Next
data Prev = Prev
data Count = Count
newtype Row = Row Int64
然后是每个结构成员的实例:
instance StructMem RowBlock (Maybe (Addr RowBlock)) Next where offset _ _ _ = 0
instance StructMem RowBlock (Maybe (Addr RowBlock)) Prev where offset _ _ _ = 8
instance StructMem RowBlock Int64 Count where offset _ _ _ = 16
instance StructMem RowBlock RowTy Row where offset _ _ (Row n) = 24 + n * 8
然后是结构体读取方法:
instance DBStruct RowBlock where
structRead a = do
n <- elemMaybePtr a Next
p <- elemMaybePtr a Prev
c <- elemRead a Count
rs <- mapM (elemRead a . Row) [0 .. c-1]
return $ RowBlock n p rs
所以我真正完成的只是以更详细(且更慢)的方式重新实现 C 结构。如果这在保持类型安全的同时更加简洁,我会更高兴。这肯定是一个常见的问题。
我能想到的一些可能的替代方案是:
- 放弃内存映射文件并使用
Data.Binary
,以正常方式将ByteStrings
写入磁盘。 - 使用
deriving Generic
创建通用读写函数 - Overload Functional References
- 用单透镜做一些神奇的事情。
最佳答案
您可以尝试将 Data.Binary 与您的 Ptr 一起使用。
写作:
使用 Data.Binary 构建 ByteString。 ByteString 是一个元组 (ForeignPtr Word8, Int, Int),它保存数据存储的地址、偏移量和长度。您可以使用 Data.ByteString.Internal 包获取 toForeignPtr,它将为您解压元组。 foreign.ForeignPtr 提供了 withForeignPtr,它采用一个通过指针执行 IO 操作的函数。在那里你可以将字节串存储memcpy(Data.ByteString.Internal中也提供了绑定(bind))到从mmap获得的mmaped Ptr。
供阅读:
您可以使用 Data.ByteString.Internal 的 fromForiegnPtr 将 Ptr 转换为字节串。这基本上就是 mmap 库所做的事情,但是您可以一次执行一条记录,而不是对整个区域进行记录。一旦你有了内存上的 ByteString View ,你就可以用 Data.Binary 来解压它。
另一个选择是利用 ByteString 在 Data.Vector.Storable.ByteString 中有一个替代实现,它允许您使用现在使用的 Storable 接口(interface)将它们读/写到映射的 Ptr。接口(interface)和基本类型与 Data.ByteString 是同构的,但它有 Storable 实例。
关于haskell - 使用 Haskell 存储大型结构化二进制数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18106958/