バッファ操作

バッファ抽象データ型

バッファはメモリを抽象化したもので、データの読み書きに用いる。

Bufferクラス

バッファのインターフェースは Buffer抽象クラスで定義されている。

多数の関数があるが、大きく以下のように分類できる:
  • 読み書きサイズに関わる操作
  • シーケンシャルな読み書き
  • ランダムアクセス
  • メモリへの直アクセス

以降の節で、よく使う関数を簡単に説明する。

読み書き可能なサイズに関わる操作

clear()
バッファに書かれた内容を空にする。
isEmpty()
データが書かれているかどうかをテストする。
(0 < getReadableSize()) と同じ。
getReadableSize()
読み込み可能サイズ(= 書き込まれたデータのうち、まだ読まれていないサイズ)を返す。
getWritableSize()
書き込み可能サイズを返す。
ensure()
書き込み可能領域を確保する。確保できたら真を返す。
現実装のバッファは固定長になっているので、ensure() をしてもバッファの拡大は行われない。よって、書き込み可能サイズの確認(size < getWritableSize())と同じ意味になる。

シーケンシャルな読み書き

skipWrite()
書き込み位置を進める。進めた分の内容は既にバッファにあるままに残す。
直アクセスでデータを書いた後や、単純にダミーデータを書く場合に用いる。
wrietArray()
連続メモリ領域を書き込む。
writeInt8() 他
int8_t 他のデータを書き込む。
skipReadAll()
読み込み可能サイズを全て読み飛ばす。
skipRead()
読み込み位置を進める。
直アクセスでデータを読んだ後や、不要なデータを飛ばす場合に用いる。
readArray()
連続メモリ領域へ読み込む。
readInt8() 他
int8_t 他のデータを読み込む。

ランダムアクセス

ランダムアクセスは、シーケンシャルアクセス操作を使った後に読み書き位置(カーソル)を戻すことで行う。カーソルを操作するメソッドが定義されている。

カーソルはユーザーの用意する Cursor オブジェクトに記憶する。 一つの Cursor に一つの位置を記憶させることができる。複数の Cursor を用いれば複数の位置を記憶できる。

mark()
Cursor オブジェクトに位置情報を記憶する。
resume()
Cursor の位置へ復帰する。一度 mark() したカーソルへ何度でも復帰することができる。
unmark()
位置記憶を消去する。
ランダムアクセスは基本的に以下のように実現する:
  1. Cursor オブジェクトを生成する。普通に作れば良い。
  2. mark(Cuursor*) で位置を記憶する
  3. シーケンシャル操作で読み書きを行う。
  4. resume(Cursor*) で mark した位置へ戻る
resume() で位置を戻しても、データ内容は復帰されずにそのまま残る。
注:

一つでも mark() されているバッファは、読み込み完了した領域の再利用が行われない。 メモリ効率のためには、不要になった記憶は解除する必要がある。

解除は unmark() か Cursor のデストラクタで行うことができる。

メモリへの直アクセス

効率的なデータ読み書きのために、バッファ内部のメモリへ直にアクセスすることができる。

geetReadBuffers()
IoVec へ読み込み領域のメモリ位置をセットする。
getWriteBuffers()
IoVec へ書き込み領域のメモリ位置をセットする。
struct IoVec {
  void* ptr;
  uint32_t size;
};

getReadBuffers(), getWriteBuffers() をして得たメモリへ読み書きした場合、skipWrite(), skipRead() を呼んで明示的に位置を移動させるのを忘れないように。

なお、型指定の読み書きAPI(writeUInt8() など)を使った場合の、メモリ上の表現は隠蔽されている。 バイトオーダーやアラインメントで異なるであろうし、将来的に簡単な圧縮を行うかもしれない。 このため、getReadBuffers/getWriteBuffers で得たメモリ内容の操作は、単純にバイト列として扱うべきである。