2013-08-07 34 views
5

我正在編寫一個應用程序,它與大型(10-1000 GB)內存映射二進制文件進行交互,基本上擁有一堆相互引用的對象。我想出了一種讀取/寫入這種數據的機制,這種數據是有效的,但是醜陋而冗長(imo)。用Haskell存儲大型結構化二進制數據

問:有沒有更優雅的方式來實現我所做的?

我對結構化數據類型類一個,其中一個方法,它讀取結構成一個Haskell數據類型(DataOpReaderT周圍IO)。

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] 
         } 

然後name類型:

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
  • 做些什麼神奇與一元的鏡片。

編輯:SSCCE as requested

+0

您能否提供一個可以編譯的簡化自包含示例? –

+0

@PetrPudlák今天晚些時候我可以做到這一點,如果你認爲它會幫助人們提出答案。但是,這意味着更多的是一個架構問題,而不是爲什麼 - 我的代碼工作問題;我發佈的代碼意味着比其他任何內容更能說明我目前的架構。 – Dan

+1

是的,[SSCCE](http://www.sscce.org/)將幫助我更好地理解您當前的設計。 –

回答

1

您可以嘗試使用Data.Binary與師生比。

對於寫作:

使用Data.Binary建立一個字節串。 ByteString是一個元組(ForeignPtr Word8,Int,Int),用於存儲數據存儲的地址,偏移量和長度。你可以使用Data.ByteString.Internal包得到toForeignPtr,它會爲你解開元組。國外。ForeignPtr提供withForeignPtr,它採用通過指針執行IO操作的函數。在那裏,您可以將memcpy(Data.ByteString.Internal中提供的綁定)存儲到您從mmap獲得的mmapped Ptr中。

對於閱讀:

您可以使用Data.ByteString.Internal的fromForiegnPtr把一個PTR成字節串。這基本上是mmap庫所做的,但是你可以一次完成一個記錄,而不是整個區域。一旦你在內存上有ByteString視圖,你可以用Data.Binary解壓它。

另一種選擇是利用ByteString在Data.Vector.Storable.ByteString中有一個替代實現,它使您可以使用現在使用的Storable接口來讀/寫它們到mmaped的Ptrs 。接口和基本類型同構於Data.ByteString之一,但它具有Storable實例。

+0

雖然我需要能夠寫入文件。 – Dan

相關問題