Проблема: иногда мы должны чередовать несколько потоков в один,
в этом случае необходимо указать способ идентификации блока
границы в потоке. Какой формат будет хорошим
для такой задачи?
(Вся обработка должна быть чисто последовательной и операции ввода-вывода
по блокам и выровнено.)
Со стороны декодирования лучше всего иметь префиксы длины
для блоков. Но на стороне кодирования требуется либо произвольный доступ
выводить файл (искать начало потока и писать заголовок), или
возможность кэшировать целые потоки, что в общем случае невозможно.
Кроме того, мы можем добавить заголовки длины (+ некоторые флаги) к
блоки кэшируемого размера. Это, безусловно, решение, но обработка намного больше
сложнее, чем [1], особенно при кодировании (при условии, что операции ввода-вывода
сделано с выровненными блоками фиксированного размера).
Ну, одна из возможных реализаций - записать 0 байт в буфер,
затем поток данных до его заполнения. Таким образом, префикс байта = 0 будет означать, что
затем идут потоковые данные bufsize-1, и! = 0 будет означать, что
там меньше ... в этом случае мы сможем вставить другой префикс
байт, если достигнут конец потока. Это будет работать только с bufsize = 32k
или так, потому что в противном случае длина блока потребовала бы 3+ байта для хранения,
и была бы проблема с обработкой случая с концом потока
когда в буфере есть только один байт свободного места.
(Одним из решений этой проблемы будет сохранение 2-байтовых префиксов для каждого буфера
и добавление 3-го байта при необходимости; другое - обеспечить 2-байтовую кодировку.
для некоторых специальных длин блоков, таких как bufsize-2).
В любом случае это не так хорошо, потому что даже 1 дополнительный байт на 64 Кб накопит
на заметное количество с большими файлами (1526 байт на 100M). Также жесткое кодирование
размера блока в формат тоже плохо.
Префикс Escape. Например. EC 4B A7 00 = EC 4B A7, EC 4B A7 01 = конец потока.
Теперь это действительно легко кодировать, но декодирование довольно болезненно - требует
грязный конечный автомат даже для извлечения отдельных байтов.
Но в целом это добавляет минимум накладных расходов, так что, похоже, нам все еще нужно найти
хорошая реализация для буферизованного декодирования.
Префикс Escape со всеми одинаковыми байтами (например, FF FF FF). Гораздо проще проверить,
но выполнение одного и того же байта в потоке может привести к огромным издержкам (например, 25%),
и не исключено, что для управляющего кода выбрано любое значение байта.
Постфикс Escape. Сохраните байт полезной нагрузки перед маркером - затем декодером
просто нужно пропустить 1 байт перед замаскированным маркером и 4 байта для контрольного кода.
Так что это в основном вводит фиксированную 4-байтовую задержку для декодера, в то время как [3]
имеет сложный путь, где байты маркера должны быть возвращены один за другим.
Тем не менее, с [3] кодировщик намного проще (просто нужно написать дополнительный 0
когда маркер совпадает), и это на самом деле не упрощает обработку буфера.
Обновление: На самом деле я уверен, что вариант [3] или [5] я бы использовал,
Я только перечислил другие варианты в надежде получить больше альтернатив (например, это
было бы хорошо, если бы избыточность была в среднем 1 бит на блок). Итак, главный вопрос
atm - это способ анализа потока для [3] ... текущий конечный автомат выглядит так:
int saved_c;
int o_last, c_last;
int GetByte( FILE* f ) {
int c;
Start:
if( o_last>=10 ) {
if( c_last>=(o_last-10) ) { c=saved_c; o_last=0; }
else c=byte("\xEC\x4B\xA7"[c_last++]);
} else {
c = getc(f);
if( o_last<3 ) {
if( char(c)==("\xEC\x4B\xA7"[o_last]) ) { o_last++; goto Start; }
else if( o_last>0 ) { saved_c=c; c_last=0; o_last+=10; goto Start; } // 11,12
// else just return c
} else {
if( c>0 ) { c=-1-c, o_last=0; printf( "c=%i\n", c ); }
else { saved_c=0xA7; c_last=0; o_last+=10-1; goto Start; } // 12
}
}
return c;
}
и, конечно, уродливо (и медленно)