.TH MPU_FOPENCOOKIE 3 "Сентябрь 2026" "libmpuio" "Руководство программиста LIBMPUIO" .SH ИМЯ mpu_fopencookie \- открыть произвольный callback-объект как поток LIBMPUIO .SH ОБЗОР .nf #include typedef ssize_t mpu_cookie_read_function_t( void *cookie, char *buf, size_t size ); typedef ssize_t mpu_cookie_write_function_t( void *cookie, const char *buf, size_t size ); typedef int mpu_cookie_seek_function_t( void *cookie, off_t *position, int whence ); typedef int mpu_cookie_close_function_t( void *cookie ); typedef struct mpu_cookie_io_functions_t { mpu_cookie_read_function_t *read; mpu_cookie_write_function_t *write; mpu_cookie_seek_function_t *seek; mpu_cookie_close_function_t *close; } mpu_cookie_io_functions_t; mpu_FILE *mpu_fopencookie( void *cookie, const char *mode, mpu_cookie_io_functions_t io_functions ); .fi .SH ОПИСАНИЕ .B mpu_fopencookie создаёт поток LIBMPUIO, внешний байтовый транспорт которого предоставляется callback-функциями вызывающей стороны. Непрозрачный указатель .I cookie сохраняется потоком и передаётся без изменений каждой callback-функции. .PP Cookie-backend является третьим независимым backend LIBMPUIO. Он не заменяет ни файловый backend на основе дескрипторов, ни нативный строковый backend UCS-2, используемый .BR mpu_fmemopen (3) и .BR mpu_open_memstream (3). Все три backend используют общие механизмы .B mpu_FILE для форматирования, буферизации, блокировки и состояния через внутреннюю абстракцию FILE_plus/jump-table. .PP Синтаксис .I mode совпадает со строгой грамматикой r/w/a с необязательными + и b, принимаемой .BR mpu_fopen (3). Режим чтения требует ненулевую callback-функцию .IR read , а режим записи \- ненулевую callback-функцию .IR write . Режим append дополнительно требует .IR seek , чтобы LIBMPUIO могла размещать каждую новую последовательность вывода в логическом конце. .SH CALLBACK-ФУНКЦИИ Callback-функция .I read копирует не более .I size внешних байтов в .I buf и возвращает число полученных байтов. Ноль означает конец ввода. Отрицательное значение означает ошибку и должно оставлять полезное значение в .BR errno . Возврат большего числа байтов, чем было запрошено, трактуется как ошибка I/O. .PP Callback-функция .I write потребляет не более .I size байтов из .I buf и возвращает число принятых байтов. Отрицательное значение означает ошибку. Возврат нуля для ненулевого запроса либо значения больше запрошенного трактуется как .BR EIO . LIBMPUIO может вызывать callback многократно для завершения буферизованного вывода. .PP Callback-функция .I seek получает запрошенную позицию в .I *position и одно из значений .BR MPU_SEEK_SET , .BR MPU_SEEK_CUR или .BR MPU_SEEK_END . При успехе она записывает новую внешнюю байтовую позицию обратно в .I *position и возвращает ноль. Ненулевой результат означает ошибку. Если callback seek не предоставлена, операции позиционирования завершаются с .BR ESPIPE . .PP Необязательная callback-функция .I close вызывается один раз из .BR mpu_fclose (3). Ноль означает успех; ненулевой результат делает close неуспешным. Сам пользовательский cookie остаётся определяемым вызывающей стороной: LIBMPUIO освобождает только свой приватный дескриптор callback, но не объект, на который указывает .IR cookie . .SH МОДЕЛЬ ТЕКСТА И СЫРЫХ БАЙТОВ Callback-функции cookie ориентированы на байты. Они работают на той же границе внешнего представления, что и файловый backend. Поэтому текстовые функции, например .BR mpu_fgetc (3), .BR mpu_fputs (3) и форматированный I/O, декодируют или кодируют UTF-8 между callback-функциями и строгим UCS-2 .BR __mpu_char16_t . Некорректный UTF-8, суррогатные значения и скалярные значения выше U+FFFF сохраняют обычную для LIBMPUIO семантику .BR EILSEQ . .PP .BR mpu_fread (3) и .BR mpu_fwrite (3) работают непосредственно с байтовым потоком callback и не выполняют преобразование текста. .SH БУФЕРИЗАЦИЯ Cookie-потоки используют обычный механизм буферизации LIBMPUIO и поддерживают .BR mpu_setvbuf (3), .BR mpu_setbuf (3) и .BR mpu_setlinebuf (3). Поэтому размеры передач callback не обязаны совпадать с отдельными публичными вызовами чтения/записи. .PP Если читаемый update-поток имеет непрочитанный буферизованный ввод, переход к выводу требует callback seek, чтобы перед записью восстановить нижележащий объект в логическую позицию. .SH ПОЗИЦИОНИРОВАНИЕ Если .I seek предоставлена, .BR mpu_fseek (3), .BR mpu_fseeko (3), .BR mpu_ftell (3), .BR mpu_ftello (3), .BR mpu_fgetpos (3) и .BR mpu_fsetpos (3) работают во внешних байтовых позициях. Упреждающее чтение в буфер, ожидающий вывод и pushback UCS-2 учитываются теми же правилами логической позиции, что и для реальных UTF-8-файлов. .PP Cookie-потоки не имеют POSIX-дескриптора; поэтому .BR mpu_fileno (3) завершается с .BR EBADF . .SH ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ .B mpu_fopencookie возвращает новый поток при успехе и NULL при ошибке. .SH ОШИБКИ .B EINVAL возвращается для недопустимого режима или набора callback, который не может обеспечить запрошенный режим доступа. Режим append без callback seek также отвергается. .B ESPIPE возвращается при запросе позиционирования для непозиционируемого cookie. .B EIO используется для невозможных значений числа переданных байтов callback. В остальных случаях ошибки callback сохраняют её значение .B errno и, где требуется, устанавливают индикатор ошибки потока. .PP Callback, сообщающая об ошибке, отвечает за установку .B errno в причину этой ошибки. В частности, значение errno неудачной callback close сохраняется mpu_fclose(), если это первая ошибка на пути закрытия. .SH ПОТОКОБЕЗОПАСНОСТЬ Обычные операции над cookie-потоком захватывают стандартный рекурсивный mutex потока. Это сериализует доступ LIBMPUIO к набору callback одного потока, но не делает пользовательский cookie-объект безопасным относительно несвязанного прямого доступа или доступа через другой поток. .SH ПРИМЕЧАНИЯ В отличие от реализации, строящей memory streams поверх публичного cookie API, LIBMPUIO намеренно сохраняет три независимых backend: .IP 1. 3 файловый backend \- нативный внешний файловый I/O UTF-8; .IP 2. 3 строковый backend \- нативный I/O памяти UCS-2; .IP 3. 3 cookie-backend \- произвольный пользовательский байтовый I/O. .PP В частности, .B mpu_fmemopen не реализован через .BR mpu_fopencookie . Оба являются клиентами более низкоуровневой внутренней абстракции FILE_plus/jump-table. .SH РЕЕНТЕРАБЕЛЬНОСТЬ CALLBACK LIBMPUIO не удерживает блокировку глобального списка потоков во время вызова cookie-callback read, write, seek или close. Поэтому callback может создавать или закрывать другие потоки LIBMPUIO без взаимной блокировки глобального обхода всех потоков. Потоки, уже захваченные таким обходом, удерживаются внутренней ссылкой жизненного цикла до тех пор, пока обход их не пройдёт. .PP Callback не должна рекурсивно закрывать или иным образом повторно входить в тот же cookie-поток, backend-операция которого сейчас выполняется. .SH ПРИМЕР Следующая небольшая программа создаёт cookie-поток только для записи, который перенаправляет внешние байты в стандартный вывод и одновременно подсчитывает их. Пример показывает границу текстового представления LIBMPUIO: formatter считает символы UCS-2, а callback cookie видит закодированные байты UTF-8. .PP .nf #include #include #include #include #include struct counting_sink { size_t bytes; }; static ssize_t counting_write( void *cookie, const char *buf, size_t size ) { struct counting_sink *sink = cookie; size_t done = 0; while( done < size ) { ssize_t n = write(STDOUT_FILENO, buf + done, size - done); if( n < 0 ) { if (errno == EINTR) continue; return -1; } if( n == 0 ) { errno = EIO; return -1; } done += (size_t)n; } sink->bytes += done; return (ssize_t)done; } int main(void) { struct counting_sink sink = { 0 }; mpu_cookie_io_functions_t io = { .read = NULL, .write = counting_write, .seek = NULL, .close = NULL }; mpu_FILE *fp; int nchars; __mpu_init(); fp = mpu_fopencookie( &sink, "w", io ); if( fp == NULL ) return 1; nchars = mpu_fprintf( fp, MPU_UCS2("value = %d, pi = %c\\n"), 42, (int)0x03c0 ); if( nchars < 0 || mpu_fclose(fp) != 0 ) return 1; mpu_printf( MPU_UCS2("UCS-2 chars: %d, UTF-8 bytes: %ju\\n"), nchars, (uintmax_t)sink.bytes ); __mpu_free_context(); return 0; } .fi .PP Для приведённой форматированной строки число символов равно 19, тогда как callback наблюдает 20 внешних байтов, поскольку U+03C0 кодируется двухбайтовой последовательностью UTF-8. В примере намеренно используется непозиционируемый cookie только для записи; для режима .B "w" callback seek не требуется. .SH СМ. ТАКЖЕ .BR libmpuio (3), .BR mpu_fopen (3), .BR mpu_fmemopen (3), .BR mpu_fread (3), .BR mpu_fseek (3), .BR mpu_printf (3), .BR mpu_scanf (3)