2012-02-28 2 views
0

(Отказ от ответственности: Я понимаю, что это массивная стена с текстом, но я сделал все возможное, чтобы сварить вещи до сути. Если вы знакомы с libtiff, это не очень сложный вопрос)Путаница при реализации _TIFFVGetField при добавлении пользовательского TAG

Я уже задал этот вопрос в списке рассылки libtiff, но я подумал, что у меня тоже есть хорошие шансы, если есть кто-нибудь, кто работал с библиотекой.

Я добавляю свои собственные встроенные тег в библиотеку, используя документацию здесь: http://libtiff.maptools.org/addingtags.html

Итак, я добавил запись в массив TIFFFieldInfo, определенном в верхней части tif_dirinfo.c как так:

{ 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. Единственное сохранение благодати заключается в том, что аргумент, указанный в 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_argsN раз, где N - это количество элементов в массиве. Из того, что я вижу, список переменных содержит только один аргумент (адрес указателя).Это неопределенное поведение согласно стандартной (важный бит в начале):

Если нет никакого фактического Следующий аргумент, или если тип не совместим с типом фактический следующий аргумент (как повышен в соответствии с объявления по умолчанию), поведение не определено.

Правильная версия будет

*va_arg(ap, int32**) = td_xxx; 

Это устанавливает ранее неинициализированный указатель на массив, который является действительным. Мне не нравится, что он указывает на сами данные, а не на копию, но что угодно; по крайней мере, это не сбой и дает правильный результат.

Меня беспокоит то, что у меня что-то не хватает. Это программное обеспечение устарело и используется многими, многими людьми. Таким образом, называя это ошибкой, мне кажется, что я виню крах компилятора, который почти всегда ошибается.

Однако я не могу объяснить, как это правильно, в частности, как библиотека записывает то, что возвращает va_arg при вызове более одного раза.

Любая помощь была бы принята с благодарностью. Заранее спасибо.

ответ

0

Итак, ответ здесь в конечном итоге закончился тем, что libtiff полагается на UB для этого. Хотя технически это UB, я не мог найти реализацию va_arg, не сделать что-то вроде этого:

(*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) 

Таким образом, до тех пор, как t меньше исходного размера аргумента (как здесь) , вы можете позвонить по телефону va_arg более одного раза безопасно.

В итоге я просто установил аргумент указателю, который привел к моим данным, и он работает. Мне не нравится предоставлять доступ непосредственно к данным заголовка, но без существенного изменения библиотеки это был мой единственный вариант.