Помощь - Поиск - Пользователи - Календарь
Полная версия: Потоковая конвертация между UTF-8 и UTF-32
Форум «Всё о Паскале» > НИФ СП > Переносимый Ассемблер (Си)
OCTAGRAM
GNAT -gnat95

Выбранная тактика поддержки Юникода такова, что с системными файловыми API мы не морочимся, а вот с содержимым заморочиться можно и нужно. Уж байтики прочитанные-то понять надо суметь.

Требуется реализовать пассивный интерфейс конвертации туда и обратно по типу iconv. iconv принимает на вход буфер, на выход буфер, и сообщает позицию, где остановились чтение и запись. Также возвращается причина остановки. Реальный сишный iconv, если смотреть его API, ещё принимает размеры входного и выходного буфера, в нашем случае нужно полагаться на возможности языка Ада передавать длины массива и, если, допустим, из буфера нужно читать не сначала или в буфер писать не с начала, нарезать нужный кусочек массива синтаксисом Массив (Индекс .. Индекс).

ВАЖНО: всевозможные функции, возвращающие массивы, как правило, делают их с начальным индексом 1, но полагаться на это запрещено. Начинать перебирать любой массив нужно с 'First, причём, с правильного 'First, соответствующего массиву, а то я как раз в библиотеке GNAT где-то напоролся в конвертации UTF, что там 'First не от того массива берётся.

Под пассивностью подразумевается, что функция конвертации не выделяет память, а только пишет, куда ей дали.

У конвертации есть некоторое состояние. Я думаю, будет достаточно объявить type Conversion_UTF_32_To_UTF_8 is private; и type Conversion_UTF_8_To_UTF_32 is private;

Внутри состояние конечного автомата и недособранный 32битный символ, предпочтительно, Interfaces.Unsigned_32.

От реализации я бы очень хотел реализации разгона, когда оба буфера позволяют. В общем случае работа функции конвертации может происходить так:
  • побайтовая обработка
  • разгон
  • побайтовая обработка

На старте побайтовая обработка нужна, если состояние, в котором вызвали конвертацию, было не основным (готовность прочитать новый символ). В конце побайтовая обработка нужна, если ещё есть, что конвертировать во входном буфере, но пространство для манёвра во входном и выходном буферах не позволяет продолжать разгон. Может быть, его достаточно, но мы не знаем. Например, на входе один байт и на выходе место для одного 32битного символа, и байт оказался ASCII. Всё хорошо, но для разгона резерв недостаточен.

Чтобы разогнаться, нужно быть в основном состоянии и всё время разгона пребывать в нём, и чтобы на входе и выходе было пространство для любых возможных ситуаций. Например, при чтении UTF-8, чтобы поддерживать чтение суррогатных пар, закодированных в UTF-8, нужен резерв 6 октетов во входном буфере. А при записи, так как сейчас нам, кажется, не нужно писать в таком ублюдочном формате, хватит и 4 октетов в выходном буфере. На 32битные символы ограничения 1 штука. Но, правда, если на выходе уже нет места даже для одного 32битного символа, а на входе ещё есть байты, кажется правильным побайтово прочитать все байты, кроме последнего, наработав состояние.

Требуется поддержать:
валидный UTF-8 и валидный UTF-32
ничего сверх 17 стандартных плоскостей Юникода валидным не является
и также ситуацию, когда в UTF-8 закодированы две суррогатные пары, но только в таком виде

То есть, одиночные суррогатные пары — это не валидно.

BOM никакой особой обработки не требует. Если он встретился на входе, он как любой другой символ перетекает на выход.

Тип данных 32битных строк — String_32, который переименованный Wide_Wide_String. Октеты в Stream_Element_Array ожидаются.

Нужны тесты, которые по-всякому ровно и неровно по границам пихают данные корректные и некорректные.
Sergey Dukov
Если только UTF-8 <--> UTF-32, то посмотрите внимательно реализацию пакета Unbouded_Array. Может быть всё что нужно там уже есть. Все определения символов и строк специфицированы в файлах Ada_Magic_Forward.ads, Ada_Magic_Forward.Character_32s.ads и Ada_Magic_Forward.Strings.ads неким универсальным способом. Тип Character_32 я с самого начала определил как беззнаковый (мне для преобразования нужны были bitwize-операции). Все операции преобразования реализованы в пакете Unbounded_array. При преобразованиях определены способы реагирования на BOM. Если в UTF-8 строке недостаёт символов, генерируется исключения. Может просто дополнить пакет Unbouded_Array процедурами работы с бинарными потоками ЮНИКОДА? В проверку валидности включить проверку на присутствие символа в исключаемом диапазоне ЮНИКОДА?
OCTAGRAM
Цитата(Sergey Dukov @ 31.10.2019 11:08) *
Тип Character_32 я с самого начала определил как беззнаковый (мне для преобразования нужны были bitwize-операции)


Нет, подход не верный. Нужно через 'Pos и 'Val приводить к Interfaces.Unsigned_32.

Стоит сделать уточнение, что в режиме Ada 2005+ тоже должно быть возможно собрать, подменив какие-то пакеты, чтоб не делали искусственные типы, а брали стандартные. В этом случае Wide_Wide_* в явном виде будет по всей программе использоваться, и слетит вся битовая арифметика.

Потоковое преобразование сложнее, чем отдельных строк. Конвертор должен иметь возможность, если хватает места на выходе, обработать вход до упора, чтоб не осталось ничего. При конвертации самодостаточных строк такие нюансы не могут встретиться.

То, что относится к потокам, в массивы класть не стоит. Скорее, конвертацию можно в одном пакете собрать и массивов и фрагментов потоков.


И ещё, я забыл, а теперь вспомнил. При конвертации строк есть возможность сделать холостой пробег для выяснения длины. При конвертации потоков это бы тоже пригодилось. Вместо записи выходного массива просто узнать, сколько единиц информации потребуется, чтобы разместить всё.
Sergey Dukov
При конвертации строк холостой проход у меня делается всегда, чтобы определить можно ли использовать существующий буфер или разместить новый. При конвертации потоков можно бы было использовать циклический буфер, но это бы нарушило понятие стандартного потока. Может быть расширить понятие бинарного потока для циклических буферов? Алгоритмы циклических буферов широко используются в системном программировании и в цифровых фильтрах. Собственно преобразование потоков по сути и есть классический цифровой фильтр.
OCTAGRAM
Требуемый програмный интерфейс достаточно универсален, чтоб можно было закодировать поверх него и циклический буфер тоже. Если последний раз остановились где-то посередине, передаём срез в конце массива в конвертор. Потом передаём срез в начале массива в конвертор.
Sergey Dukov
А не подскажете, есть ли сейчас в стандартной библиотеке АДы конвейеры FIFO?
OCTAGRAM
Очередь и куча там есть, но это для многопоточных программ.

http://www.ada-auth.org/standards/12rat/html/Rat12-8-6.html

Я так понимаю, Ada.Containers.Bounded_Synchronized_Queues будет именно циклической.

Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.