NSFItemScan関数の限界(その1) - Noteクラス - 「NotesPeek」をQtでリメイク

今回は、リッチテキストから少し離れて、文書からアイテム(フィールド)データを取得するプロセスについての考察をします。

Notes文書(Note)には、いろいろな種類のアイテムが格納されます。文書のプロパティやNotesPeekのように、文書内のアイテムをすべて読み込むためには、NSFItemScan関数を使います。

// nsfnote.h

typedef STATUS (LNCALLBACKPTR NSFITEMSCANPROC)(
    WORD Spare,
    WORD ItemFlags,
    char far *Name, WORD NameLength,
    void far *Value, DWORD ValueLength,
    void far *RoutineParameter);

STATUS LNPUBLIC NSFItemScan (
    NOTEHANDLE hNote,
    NSFITEMSCANPROC ActionRoutine,
    void far *RoutineParameter);

事前にNSFITEMSCANPROCのような引数を取るコールバック関数を用意しておき、アイテムごとにその関数を呼び出すことで、すべてのアイテムに対して処理をすることができます。なお、NSFItemScanではアイテムの変更処理はできないので、アイテムデータの更新をしたい場合は、NSFItemScan関数の呼び出しを終えてからということになります。

さて、タイトルにも書いたNSFItemScan関数の限界とは何か、その前置きとして、重複アイテムについて軽く触れておきます。

Notesの文書には通常、アイテムの名前は重複しない作りのように見えます。フォームの設計をしているときなどは当たり前の話ですが、技術的には同名アイテムが重複することは可能です。理由の1つに、1アイテム当たり64キロバイトの壁の突破があります。文書にアイテムを追加する関数に「NSFItemAppend 」というのがあります。

// nsfnote.h

STATUS LNPUBLIC NSFItemAppend (
    NOTEHANDLE hNote, WORD ItemFlags,
    const char far *Name, WORD NameLength,
    WORD DataType,
    const void far *Value, DWORD ValueLength);

ValueLengthという引数の型がDWORDなので、4GBまでいけるのではと錯覚を起こしそうですが、APIのリファレンスマニュアルには次のように明確に書かれています。

The length of the item’s value (excluding the data type word). The length must not exceed 64K bytes. If the ITEM_SUMMARY flag is set in item_flags, then value_len must not exceed 32K.

(訳) (ValueLengthは)項目の値の長さです(データタイプを表すWORD型を除く)。長さは64Kバイトを超えてはなりません。item_flagsにITEM_SUMMARYフラグが設定されている場合、value_len(ValueLength)は32Kを超えてはなりません。

ビューなどで使うためにサマリーフラグを立てている場合は、さらに32キロバイトを超えてはいけないのです。しかし、テキストデータは32kBや64kBを超えることは意外にあることだと思いますし、リッチテキストならなおのことです(文書プロパティでBodyアイテムがいくつもあるのを目の当たりにしたことがあると思います)。実は、この壁を越えるために名前重複アイテムが使われています。前出のNSFItemAppend関数は、文書内に同名アイテムがあると、同じ名前だけど特殊なIDを振って文書内に同名アイテムを保存します。なお、1つのアイテムデータをどこで区切るかは関数の呼び出し側に責任があるようです。Notesユーザーがこれを意識することなく使えているのは、Notesクライアントが適当なところで区切って保管してくれているようです。

もう1つ、同名アイテムが存在する理由に、添付ファイルがあります。添付ファイルの本体は「$FILE」というフィールドに保管されますが、添付ファイルがいくつもあると、その数分だけ$FILEアイテムが存在します。添付ファイルがどれほど大きくても、$FILEアイテム1つに1ファイルが保管されます(先程とはまるで違いますね)。ファイルの添付処理はファイルシステムから直接行われるので、呼び出し側は特にサイズを気にする必要はないわけです。

同名アイテムは、添付ファイルであれば1アイテムを1ファイルとして、それ以外であれば、分割されたデータを合成して64キロバイトの壁を越える手段として使われています。

前置きが長くなりましたが、本件のリメイクプロジェクトで文書内のアイテムを取得するに当たり、この同名アイテムの存在が、NSFItemScan関数にある不具合をもたらしていました。次回、その不具合と対処方法について考察していきます。