1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
|
.TH LIBMPUIO 3 "Сентябрь 2026" "libmpuio" "Руководство программиста LIBMPUIO"
.SH ИМЯ
libmpuio \- ввод-вывод в стиле stdio с UCS-2/UTF-8 для чисел LIBMPU произвольной точности
.SH БИБЛИОТЕКА
Сопутствующая библиотека LIBMPUIO для LIBMPU.
.SH ОБЗОР
.nf
.B #include <libmpuio.h>
.PP
.B extern mpu_FILE *mpu_stdin;
.B extern mpu_FILE *mpu_stdout;
.B extern mpu_FILE *mpu_stderr;
.fi
.SH ОПИСАНИЕ
LIBMPUIO предоставляет потоковый интерфейс в стиле stdio без использования
объектов libc
.BR FILE .
Её публичный тип потока \-
.BR mpu_FILE ,
а потоки реальных файлов работают поверх POSIX-дескрипторов.
.PP
Публичные символьные и строковые интерфейсы используют скалярные typedef LIBMPU
.BR __mpu_char8_t ,
.BR __mpu_char16_t ,
.B __mpu_char32_t
и
.BR __mpu_size_t ,
доступные через
.BR <libmpu.h> .
Текст внутри I/O API представлен строгим UCS-2 в
.BR __mpu_char16_t .
Текст реального файла преобразуется в UTF-8 или из UTF-8 на границе потока.
Суррогаты и скалярные значения UTF-8 выше U+FFFF отвергаются с
.BR EILSEQ .
Сырые операции
.BR mpu_fread (3)
и
.BR mpu_fwrite (3)
работают с байтами и не выполняют преобразование текста.
.PP
Форматированный I/O целых, вещественных и комплексных чисел MPU поддерживает
настроенные размеры до
.B MPU_REAL_IO_LIMIT
(в настоящее время до 65536 бит). Форматированный I/O не ограничивается
.BR MPU_MATH_FN_LIMIT .
.SH МОДЕЛЬ ТИПОВ ФОРМАТИРОВАННОГО I/O
Форматированный I/O намеренно различает обычные объекты C и объекты libmpu.
Обычные преобразования, например
.B %d
или
.BR %g ,
следуют стандартным вариадическим правилам C и извлекают значения типов,
определяемых преобразованием и стандартными модификаторами длины. Малые целые
аргументы подвергаются обычным преобразованиям аргументов по умолчанию.
.PP
Преобразования libmpu используют исторический модификатор размера
.B z<bits>
или
.BR Z<bits> .
Обе формы эквивалентны, а строчная
.B z
зарезервирована LIBMPUIO именно для этой цели; стандартный модификатор длины C
.B z
для
.B size_t
не реализован. Если после модификатора размера нет цифр, предполагается 128 бит.
Набор модификаторов размера, присутствующих в конкретной сборке, компилируется
непосредственно по значению
.B MPU_REAL_IO_LIMIT
из
.BR <libmpu.h> .
Аргумент представляет собой хранилище, адресуемое через указатель, а
.I bits
задаёт точный размер объекта MPU. Публичные typedef libmpu для чисел
произвольной точности являются массивами байтов, поэтому имя объекта естественно
преобразуется в указатель при передаче аргументом функции.
.PP
Вариадический
.B va_list
не содержит универсальных метаданных типа/размера времени выполнения. Поэтому
LIBMPUIO не пытается определить размер объекта MPU по его адресу: явный
модификатор
.B z/Z<bits>
является контрактом типа форматированного I/O. Обычные объекты C должны
использовать обычные преобразования C; преобразования MPU обычно следует
применять к объектам libmpu соответствующего настроенного размера. Примеры и
подробные правила аргументов см. в
.BR mpu_printf (3)
и
.BR mpu_scanf (3).
.SH ЛОКАЛЬ И ОБЫЧНЫЙ ВВОД-ВЫВОД ВЕЩЕСТВЕННЫХ ЧИСЕЛ
Обычные вещественные преобразования C
.B %e/%E/%f/%F/%g/%G
следуют активному соглашению о десятичном разделителе
.BR LC_NUMERIC .
При выводе используется formatter libc хост-системы, после чего полученное
многобайтовое поле текущей локали декодируется в строгий UCS-2 вместо простого
расширения отдельных байтов вывода. При обычном scanf токенизатор получает
строку десятичного разделителя локали через
.BR localeconv (3),
распознаёт соответствующую последовательность UCS-2 во входе и передаёт
соответствующий многобайтовый токен локали функции
.BR strtold (3).
Это делает обычное поведение printf/scanf для вещественных чисел симметричным в
локалях, где десятичный разделитель отличается от
.BR . .
.PP
Строчная
.B %a
зарезервирована LIBMPUIO для многобайтовых строк текущей локали. Вещественные
и комплексные преобразования MPU по-прежнему подчиняются грамматике
форматированных чисел LIBMPU, а не обычному floating-tokenizer libc.
.SH МОДЕЛЬ ПОТОКА
Поток содержит состояние буферизации, индикаторы EOF/ошибки, состояние pushback,
рекурсивный mutex потока и таблицу переходов backend. LIBMPUIO намеренно имеет
три независимых backend: файлы на основе дескрипторов с внешним текстом UTF-8,
нативные строковые/памятные потоки UCS-2 и произвольные байтовые callback-потоки,
открываемые mpu_fopencookie(3). Все три используют одни и те же механизмы
форматирования через более низкоуровневую внутреннюю абстракцию
FILE_plus/jump-table.
.PP
Для реальных текстовых файлов логические позиции являются внешними байтовыми
смещениями UTF-8. Упреждающее чтение в буфер и pushback UCS-2 учитываются
функцией
.BR mpu_ftello (3).
.SH МОДЕЛЬ БЛОКИРОВОК
Обычные потоковые функции захватывают рекурсивный mutex потока. Функции, имена
которых заканчиваются на
.BR _unlocked ,
этого не делают. Unlocked-формы предназначены для участка, уже защищённого
.BR mpu_flockfile (3),
либо для кода, который иным способом гарантирует эксклюзивный доступ к потоку.
.PP
Различие unlocked меняет только блокировку. Буферизация, проверка,
преобразование UTF-8/UCS-2, возвращаемые значения, индикаторы ошибок и поведение
backend в остальном совпадают с соответствующей блокируемой операцией.
.PP
Глобальный список потоков, используемый
.BR mpu_fflush(NULL)
и
.BR mpu_flushlbf(),
защищается только на время построения стабильного snapshot. Потоки в snapshot
несут внутренние ссылки жизненного цикла, поэтому блокировка глобального списка
освобождается до захвата mutex отдельного потока или входа в код backend.
Закрытие сначала удаляет поток из будущих snapshot и откладывает уничтожение
объекта, пока более ранний snapshot всё ещё удерживает ссылку. Код приложения
всё равно обязан синхронизировать явное закрытие с произвольным обычным
параллельным использованием того же указателя на поток.
.SH ГРУППЫ ФУНКЦИЙ
.TP
.B Создание и жизненный цикл потока
.BR mpu_fopen (3),
.BR mpu_fdopen (3),
.BR mpu_freopen (3),
.BR mpu_tmpfile (3),
.BR mpu_popen (3),
.BR mpu_pclose (3),
.BR mpu_fopencookie (3),
.BR mpu_fclose (3),
.BR mpu_fflush (3),
.BR mpu_flushlbf (3),
.BR mpu_fpurge (3),
.BR mpu_fileno (3).
.TP
.B Потоки памяти UCS-2
.BR mpu_fmemopen (3),
.BR mpu_open_memstream (3).
.TP
.B Диагностика
.BR mpu_perror (3),
.BR mpu_strerror_r (3).
.TP
.B Сырой двоичный I/O
.BR mpu_fread (3),
.BR mpu_fwrite (3)
и unlocked-варианты.
.TP
.B Ввод-вывод символов и строк UCS-2
.BR mpu_fgetc (3),
.BR mpu_getc (3),
.BR mpu_getchar (3),
.BR mpu_ungetc (3),
.BR mpu_fputc (3),
.BR mpu_putc (3),
.BR mpu_putchar (3),
.BR mpu_fgets (3),
.BR mpu_fputs (3),
.BR mpu_puts (3).
.TP
.B Форматированный вывод
.BR mpu_printf (3),
.BR mpu_fprintf (3),
.BR mpu_sprintf (3),
.BR mpu_snprintf (3),
.BR mpu_asprintf (3),
.BR mpu_dprintf (3)
и формы va_list/unlocked.
.TP
.B Форматированный ввод
.BR mpu_scanf (3),
.BR mpu_fscanf (3),
.BR mpu_sscanf (3)
и формы va_list/unlocked.
.TP
.B Позиционирование
.BR mpu_fseek (3),
.BR mpu_fseeko (3),
.BR mpu_ftell (3),
.BR mpu_ftello (3),
.BR mpu_fgetpos (3),
.BR mpu_fsetpos (3),
.BR mpu_rewind (3).
.TP
.B Буферизация, индикаторы и явная блокировка
.BR mpu_setvbuf (3),
.BR mpu_setbuf (3),
.BR mpu_feof (3),
.BR mpu_ferror (3),
.BR mpu_clearerr (3),
.BR mpu_fpurge (3),
.BR mpu_flockfile (3),
.BR mpu_ftrylockfile (3),
.BR mpu_funlockfile (3).
.SH ВОЗВРАЩАЕМЫЕ ЗНАЧЕНИЯ И ОШИБКИ
Каждое семейство следует соглашениям возврата в стиле stdio, описанным на его
странице руководства. Ошибки системных вызовов сохраняют полезные значения
.BR errno .
Ошибки потока устанавливают индикатор ошибки; конец ввода устанавливает EOF
только когда фактически обнаружено условие конца файла.
.SH ПРИМЕЧАНИЯ
LIBMPUIO является сопутствующей библиотекой LIBMPU, а не ABI-заменой libc stdio.
Не выполняйте приведение между
.B mpu_FILE *
и libc
.BR FILE * .
.SH УСИЛЕНИЕ МОДЕЛИ СОСТОЯНИЯ ПОТОКА
Все три нативных backend участвуют в единой модели активного состояния
READING/WRITING. В частности, потоки памяти UCS-2 устанавливают состояние
WRITING при текстовом выводе, что позволяет mpu_fflush(NULL) публиковать
активный mpu_open_memstream() так же, как соответствующий явный flush потока.
.PP
Обычные mpu_puts(), mpu_rewind() и mpu_fileno() удерживают mutex потока на всю
логическую операцию; их явно unlocked-аналоги, где они предусмотрены, остаются
операциями, синхронизацию которых обеспечивает вызывающая сторона. Командные
потоки нельзя передавать mpu_freopen(), и они сохраняют обязательный жизненный
цикл через mpu_pclose().
.PP
Создание потока напрямую распространяет ошибки инициализации рекурсивного mutex,
а стандартный поток, mutex которого не удалось инициализировать, не добавляется
в глобальный список потоков. Замена буфера файла и cookie также очищает
указатели направления до того, как ошибка выделения памяти могла бы открыть
устаревшие адреса.
.SH УСИЛЕНИЕ ЖИЗНЕННОГО ЦИКЛА СПИСКА ПОТОКОВ
Обход всех потоков использует snapshot с удержанием ссылок вместо удержания
mutex глобального списка во время операций backend. Это сохраняет живыми
потоки дескрипторов, строк и cookie на протяжении обработки каждого элемента,
одновременно убирая блокировку глобального списка из пользовательских callback
cookie.
.PP
Если mpu_fclose() встречает поток, который всё ещё присутствует в более раннем
snapshot, функция удаляет этот поток из живого списка и завершает shutdown
backend, но откладывает уничтожение объекта FILE и рекурсивного mutex до
освобождения последней ссылки snapshot. Более поздний snapshot не может
получить новую ссылку на уже удалённый из списка поток.
.PP
Приватные потоки UCS-2, создаваемые внутри для движков sprintf/snprintf и
sscanf, не являются членами публичного глобального списка потоков. Публичные
mpu_fmemopen() и mpu_open_memstream() остаются зарегистрированными и потому
продолжают участвовать в mpu_fflush(NULL) и mpu_flushlbf(), когда это
применимо.
.SH УСИЛЕНИЕ БЕЗОПАСНОСТИ ПАМЯТИ И СОСТОЯНИЯ БУФЕРА
Замена setvbuf для descriptor- и cookie-потоков транзакционна относительно
ошибки выделения памяти: новое хранилище подготавливается заранее, поэтому
неудачное выделение не разрушает прежнее пригодное состояние буфера.
Неиспользуемый внутренний cache _offset был удалён; логические позиции
по-прежнему выводятся из позиции backend вместе с состоянием буферизованного
чтения/записи и pushback UCS-2.
.PP
Makefile в корне проекта собирает только production shared library. Обычные
регрессионные тесты независимо собираются в tests/, а stress-наборы
AddressSanitizer, UndefinedBehaviorSanitizer и ThreadSanitizer \- независимо в
tests/asan, tests/ubsan и tests/tsan. Каждый каталог тестов собирает собственную
локальную libmpuio.so и запускает программы с LD_LIBRARY_PATH=. Поэтому
инструментирование sanitizer никогда не требует изменения или пересборки
production-объектов в другом режиме.
.SH СМ. ТАКЖЕ
.BR mpu_fopen (3),
.BR mpu_fmemopen (3),
.BR mpu_fopencookie (3),
.BR mpu_fpurge (3),
.BR mpu_perror (3),
.BR mpu_fgetc (3),
.BR mpu_fread (3),
.BR mpu_fseek (3),
.BR mpu_printf (3),
.BR mpu_scanf (3),
.BR mpu_flockfile (3),
.BR mpu_unlocked (3)
.SH ВИДИМОСТЬ ВНУТРЕННИХ СИМВОЛОВ
Внутренние межмодульные объявления в приватных заголовках LIBMPUIO являются
обычными объявлениями BexternP. Вспомогательные макросы внутренних символов
определены в Bmpu-symbols.hP. В C-файле, определяющем каждую приватную
межмодульную функцию или объект данных, после определения используется
B__mpu_hidden_decl(name)P. Макрос применяет GCC-атрибут
Bvisibility("hidden")P только когда одновременно определены BSHAREDP и
BHAVE_HIDDEN_VISIBILITY_ATTRIBUTEP; последний предоставляется через
Bconfig.hP. Таким образом, политика возможностей компилятора не попадает в
приватные объявления интерфейса. Вспомогательные функции уровня файла остаются
BstaticP.
.PP
Это правило также охватывает приватные jump-table и состояние списка потоков:
B__mpu_IO_file_jumpsP, B__mpu_IO_str_jumpsP,
B__mpu_IO_list_allP и B__mpu_IO_list_lockP. Поэтому shared library не
экспортирует ни одного символа с приватным префиксом B__mpu_P.
.PP
Hidden-объявления определяют только границу ABI; документированные публичные
функции Bmpu_*P и публичные объекты потоков остаются экспортируемыми.
|