2013-08-07 26 views
5

私は大規模な(10-1000 GB)メモリマップバイナリファイルと相互作用するアプリケーションを作成しています。私はこのデータを読み書きする仕組みを考え出しましたが、これは効果的ですが、醜く冗長です(imo)。Haskellで大規模な構造化バイナリデータを保存する

Q:私が行ったことを達成するためのよりエレガントな方法はありますか?

私はHaskellのデータ型(DataOpIO周り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 

を私は構造要素を書き込み/読み出し用のオフセット方法を使用するいくつかのヘルパー関数を持っています、格納された参照から構造を読み込み、構造読み取りを遅らせる(ファイル全体の読み込みを遅らせる)ためのものです。

この問題は、使用に多くの繰り返しが含まれているということです。次に、構造体を

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 

:そして

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 

各構造部材のためのインスタンスを一つの構造のために、私は最初Haskellのタイプを定義する必要があります読み取り方法:

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構造体をはるかに冗長な(そして遅い)方法で再実装することだけです。タイプの安全性を維持しながら、これがより簡潔であれば私はもっと幸せです。確かにこれは一般的に遭遇する問題です。

  • ディッチのメモリマップドファイルやディスクへの通常の方法をByteStringsを書いて、Data.Binaryを使用します。

    私は考えることができるいくつかの可能な選択肢があります。

  • モナドのレンズとの不思議な何かをする
  • Overload Functional References
  • の機能を一般的な読み取りを作成し、書き込みに使用deriving Generic

EDIT:SSCCE as requested

+0

コンパイルすることができる、簡単で自立的な例を用意してください。 –

+0

@PetrPudlákもし人々が答えを出すのを助けると思うなら、今日後でそれをすることができます。しかし、これは、なぜ私のコードの問題ではないのかという建築上の疑問であることを意味していました。投稿したコードは、私の現在のアーキテクチャを他のものよりも具体的に説明するためのものです。 – Dan

+1

はい、[SSCCE](http://www.sscce.org/)は、現在のデザインをよりよく理解するのに役立ちます。 –

答えて

1

あなたのPTRSでData.Binaryを使用してみてください。

書き込み用:

ByteStringを構築するには、Data.Binaryを使用します。 ByteStringは、データが格納されているアドレス、オフセット、および長さを保持するタプル(ForeignPtr Word8、Int、Int)です。 Data.ByteString.Internalパッケージを使用してtoForeignPtrを取得すると、タプルがアンパックされます。外国人。ForeignPtrはwithForeignPtrを提供します。これは、ポインタを介してIOアクションを実行する関数を取ります。そこにmemcpy(このバインディングはData.ByteString.Internalでも提供されています)mmapから取得したmmapされたPtrへのバイトストレージが可能です。読書のために

あなたがバイト文字列へのPtrをオンにするData.ByteString.InternalのfromForiegnPtrを使用することができます。これは基本的にmmapライブラリの機能ですが、リージョン全体ではなく一度にレコードを実行できます。メモリ上のByteStringビューを取得したら、Data.Binaryで展開できます。

もう一つの選択肢は、ByteStringがData.Vector.Storable.ByteStringの代わりの実装を持っていることを利用することです。これは、今使用しているStorableインターフェイスを使って、mmaped Ptrs 。インターフェイスと基本型はData.ByteString型と同型ですが、Storableインスタンスがあります。

+0

私もファイルに書き込むことができる必要があります。 – Dan

関連する問題