(Отказ от ответственности: я понимаю, что это огромная стена текста, но я приложил все усилия, чтобы свести все к основам. Если вы знакомы с libtiff, это не очень сложный вопрос)
Я уже задавал этот вопрос в списке рассылки libtiff, но я подумал, что у меня также может быть здесь хороший шанс, если есть кто-то, кто работал с библиотекой.
Я добавляю свой встроенныйтеги к библиотеке, используя документацию здесь: http://libtiff.maptools.org/addingtags.html
Итак, я добавил запись в массив TIFFFieldInfo, определенный в верхней части tif_dirinfo.c, примерно так:
{ TIFFTAG_CUSTOM_XXX, 4, 4, TIFF_SLONG, FIELD_XXX, 1, 0, "XXX" },
I тогдадобавил поле в структуру 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;
Насколько я могу судить, это совершенно неверно.Намерение здесь состоит в том, чтобы заполнить ввод в списке переменных аргументов, основываясь на массиве целых чисел.Единственное преимущество в том, что аргумент, указанный в va_list
, всегда имеет тип int32
, а код YcBr использует два 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
и указателем на неинициализированный указатель.
Здесь есть две проблемы.
Во-первых, вот как библиотека извлекает данные (мой код, но опять же, следуя уже существующему шаблону)
*va_arg(ap, int32*) = td->td_xxx[0];
Это кажется неверным.Это установка исходного указателя на значение типа int.Я предположил, что, возможно, в следующем примере (TIFFTAG_YCBCRSUBSAMPLING) эти целые числа были на самом деле адресами.Так хорошо, но даже если это так, хотя есть и другая проблема.
Библиотека вызывает va_args
N
раз, где N
- количество элементов в массиве.Из того, что я вижу, список переменных-аргументов содержит только один аргумент (адрес указателя).Это неопределенное поведение в соответствии со стандартом (важный бит в начале):
Если фактического следующего аргумента нет или если тип не совместим с типом фактического следующего аргумента (в соответствии с продвижением аргументов по умолчанию), поведение не определено.
Правильная версия будет
*va_arg(ap, int32**) = td_xxx;
Это устанавливает ранее неинициализированный указатель на массив, который действителен.Мне не нравится, что он указывает на сами данные, а не на копию, но что угодно;по крайней мере, это не дает сбой и дает мне правильный результат.
Меня беспокоит то, что мне не хватает чего-то тонкого.Это программное обеспечение устарело и используется многими, многими людьми.Поэтому, называя это ошибкой, я чувствую, что виню в сбое компилятора, который почти всегда неверен.
Однако я не могу объяснить, каким образом это правильно, в частности, как библиотека записывает то, что va_arg
возвращает при вызове более одного раза.
Любая помощь будет принята с благодарностью.Заранее спасибо.