(免責事項:これは巨大なテキストの壁だと思いますが、私は物事を本質にまで煮詰めるために最善を尽くしました。libtiffに精通している場合、これはそれほど複雑な質問ではありません)
すでにlibtiffメーリングリストでこの質問をしましたが、図書館で働いたことのある人がいれば、ここでもチャンスがあるのではないかと思いました。
http://libtiff.maptools.org/addingtags.htmlのドキュメントを使用して、独自の組み込みタグをライブラリに追加しています。
そこで、tif_dirinfo.cの先頭に定義されているTIFFFieldInfo配列に次のようにエントリを追加しました。
{ TIFFTAG_CUSTOM_XXX, 4, 4, TIFF_SLONG, FIELD_XXX, 1, 0, "XXX" },
次に、 :TIFFDirectory
で定義された構造にフィールドを追加しました。tif_dir.h
typedef struct {
/* ... */
int32 td_xxx[4];
} TIFFDirectory;
今、私は先に進んで、指示通りに修正_TIFFVSetField
しました。_TIFFVGetField
これは私が問題にぶつかったところです。
ライブラリにすでに存在するパターンを模倣する際に(TIFFTAG_YCBCRSUBSAMPLING
私が行っていることに類似しているの実装を参照)、次のコードを次のように追加しました_TIFFVGetField
。
/* existing, standard tag for reference */
case TIFFTAG_YCBCRSUBSAMPLING:
*va_arg(ap, uint16*) = td->td_ycbcrsubsampling[0];
*va_arg(ap, uint16*) = td->td_ycbcrsubsampling[1];
break;
/* my new tag */
case TIFFTAG_CUSTOM_XXX:
*va_arg(ap, int32*) = td->td_xxx[0];
*va_arg(ap, int32*) = td->td_xxx[1];
*va_arg(ap, int32*) = td->td_xxx[2];
*va_arg(ap, int32*) = td->td_xxx[3];
break;
私が知る限り、これは完全に間違っています。ここでの目的は、intの配列に基づいて変数引数リストに入力を入力することです。1つの節約の恩恵は、で提供される引数va_list
が常に型int32
であり、YcBrコードが2つint16
の'を使用することです。したがって、それは機能しますが、その実装をコピーすることはできません。
_TIFFVGetField
最終的にから呼び出さTIFFWriteNormalTag
れtif_dirwrite.c
ます。関連するコードは次のとおりです。
case TIFF_LONG:
case TIFF_SLONG:
case TIFF_IFD:
if (fip->field_passcount) {
uint32* lp;
if (wc == (uint16) TIFF_VARIABLE2) {
TIFFGetField(tif, fip->field_tag, &wc2, &lp);
TDIRSetEntryCount(tif,dir, wc2);
} else { /* Assume TIFF_VARIABLE */
TIFFGetField(tif, fip->field_tag, &wc, &lp);
TDIRSetEntryCount(tif,dir, wc);
}
if (!TIFFWriteLongArray(tif, dir, lp))
return 0;
} else {
if (wc == 1) {
uint32 wp;
/* XXX handle LONG->SHORT conversion */
TIFFGetField(tif, fip->field_tag, &wp);
TDIRSetEntryOff(tif,dir, wp);
} else {
/* ---------------------------------------------------- */
/* this is the code that is called in my scenario */
/* ---------------------------------------------------- */
uint32* lp;
TIFFGetField(tif, fip->field_tag, &lp);
if (!TIFFWriteLongArray(tif, dir, lp))
return 0;
}
}
break;
したがって、初期化されていないポインタlp
が宣言され、そのアドレスがに渡されTIFFGetField
ます。これにより、va_list(lp
唯一の引数として)が設定され、TIFFVGetField
が呼び出されます。これは_TIFFVGetField
、指定さva_list
れたポインタと初期化されていないポインタへのポインタを使用して呼び出します。
ここには2つの問題があります。
まず、これはライブラリがデータを抽出する方法です(私のコードですが、ここでも、すでに存在するパターンに従います)
*va_arg(ap, int32*) = td->td_xxx[0];
これは正しくないようです。元のポインタをintの値に設定しています。おそらく、私がフォローしている例(TIFFTAG_YCBCRSUBSAMPLING)では、これらの整数は実際にはアドレスであると考えました。大丈夫ですが、それでも別の問題があります。
ライブラリはva_args
N
timesを呼び出します。ここN
で、は配列内の要素の数です。私が見るところ、可変引数リストには単一の引数(ポインターのアドレス)しか含まれていません。これは、標準による未定義の動作です(最初の重要なビット):
実際の次の引数がない場合、またはタイプが実際の次の引数のタイプと互換性がない場合(デフォルトの引数の昇格に従って昇格)、動作は定義されていません。
正しいバージョンは
*va_arg(ap, int32**) = td_xxx;
これにより、以前に初期化されていないポインタが配列に設定されます。これは有効です。コピーではなくデータ自体を指すのは好きではありませんが、何でも構いません。少なくともクラッシュせず、正しい結果が得られます。
ここでの私の懸念は、微妙な何かが欠けていることです。このソフトウェアは古く、多くの人々によって使用されています。そのため、これをバグと呼ぶと、コンパイラのクラッシュを非難するように感じますが、これはほとんどの場合間違っています。
ただし、これが正しい方法、具体的には、ライブラリがva_arg
複数回呼び出されたときに返されるものに書き込む方法を推測することはできません。
どんな助けでも大歓迎です。前もって感謝します。