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
|
.TH LIBMPUIO 3 "September 2026" "libmpuio" "LIBMPUIO Programmer's Manual"
.SH NAME
libmpuio \- UCS-2/UTF-8 stdio-style I/O for LIBMPU arbitrary-precision numbers
.SH LIBRARY
LIBMPUIO companion library for LIBMPU.
.SH SYNOPSIS
.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 DESCRIPTION
LIBMPUIO provides a stdio-like stream interface without using libc
.B FILE
objects. Its public stream type is
.B mpu_FILE
and real-file streams are backed by POSIX file descriptors.
.PP
Public character and string interfaces use the LIBMPU scalar typedefs
.BR __mpu_char8_t ,
.BR __mpu_char16_t ,
.B __mpu_char32_t
and
.BR __mpu_size_t ,
which are made available through
.BR <libmpu.h> .
Text inside the I/O API is strict UCS-2 in
.BR __mpu_char16_t .
Real-file text is converted to or from UTF-8 at the stream boundary.
Surrogates and UTF-8 scalar values above U+FFFF are rejected with
.BR EILSEQ .
Raw
.BR mpu_fread (3)
and
.BR mpu_fwrite (3)
operate on bytes and perform no text conversion.
.PP
Formatted MPU integer, real, and complex I/O supports configured sizes through
.B MPU_REAL_IO_LIMIT
(currently up to 65536 bits). Formatted I/O is not restricted by
.BR MPU_MATH_FN_LIMIT .
.SH FORMATTED TYPE MODEL
Formatted I/O deliberately distinguishes ordinary C objects from libmpu
objects. Ordinary conversions such as
.B %d
or
.B %g
use the normal variadic C rules and fetch values of the types described by the
conversion and standard length modifiers. Small integer arguments are subject
to the usual default argument promotions.
.PP
Libmpu conversions use the historical
.B z<bits>
or
.B Z<bits>
size modifier. Both spellings are equivalent and lower-case
.B z
is reserved by LIBMPUIO for this purpose; the standard C
.B z
length modifier for
.B size_t
is not implemented. If no size digits follow, 128 bits are assumed. The set
of size modifiers present in a build is compiled directly from
.B MPU_REAL_IO_LIMIT
in
.BR <libmpu.h> .
The argument is storage addressed through a pointer, while
.I bits
states the exact MPU object size. Libmpu's public arbitrary-precision typedefs
are arrays of bytes, so an object name naturally decays to a pointer when used
as a function-call argument.
.PP
A variadic
.B va_list
does not contain general run-time type/size metadata. Therefore LIBMPUIO does
not attempt to infer an MPU object's size from its address: the explicit
.B z/Z<bits>
modifier is the formatted-I/O type contract. Ordinary C objects should use
ordinary C conversions; MPU conversions should normally be used with libmpu
objects of the matching configured size. See
.BR mpu_printf (3)
and
.BR mpu_scanf (3)
for examples and the detailed argument rules.
.SH LOCALE AND ORDINARY FLOATING I/O
Ordinary C floating conversions
.B %e/%E/%f/%F/%g/%G
follow the active
.B LC_NUMERIC
radix convention. Output uses the host libc formatter and decodes the
resulting locale multibyte field to strict UCS-2 instead
of widening individual output bytes. Ordinary scanf tokenization obtains the locale decimal-point string
with
.BR localeconv (3),
recognizes that UCS-2 sequence in input, and presents the matching locale
multibyte token to
.BR strtold (3).
This makes ordinary floating printf/scanf behavior symmetric for locales whose
radix character is not
.BR . .
.PP
Lowercase
.B %a
is reserved by LIBMPUIO for current-locale multibyte strings. MPU real and
complex conversions remain governed by the LIBMPU formatted-number grammar
rather than the ordinary libc floating tokenizer.
.SH STREAM MODEL
A stream contains buffering state, EOF/error indicators, pushback state, a
recursive stream mutex, and a backend jump table. LIBMPUIO deliberately has
three independent backends: descriptor-backed files with external UTF-8 text,
native UCS-2 string/memory streams, and arbitrary byte-oriented callback
streams opened by mpu_fopencookie(3). All three use the same formatted engines
through the lower-level internal FILE_plus/jump-table abstraction.
.PP
For real text files, logical positions are external UTF-8 byte offsets.
Buffered read-ahead and UCS-2 pushback are accounted for by
.BR mpu_ftello (3).
.SH LOCKING MODEL
Ordinary stream functions acquire the stream's recursive mutex. Functions
whose names end in
.B _unlocked
do not. The unlocked forms are intended for a region already protected by
.BR mpu_flockfile (3),
or for code that otherwise guarantees exclusive stream access.
.PP
The unlocked distinction changes locking only. Buffering, validation,
UTF-8/UCS-2 conversion, return values, error indicators, and backend behavior
are otherwise the same as for the corresponding locked operation.
.PP
The global stream list used by
.BR mpu_fflush(NULL)
and
.BR mpu_flushlbf()
is protected only while a stable snapshot is built. Streams in that snapshot
carry internal lifetime pins, so the global list lock is released before an
individual stream mutex is acquired or backend code is entered. A close
unlinks the stream from future snapshots first and defers object destruction
while an earlier snapshot still owns a pin. Application code must still
synchronize an explicit close against arbitrary ordinary concurrent use of
the same stream pointer.
.SH FUNCTION GROUPS
.TP
.B Stream creation and lifetime
.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 memory streams
.BR mpu_fmemopen (3),
.BR mpu_open_memstream (3).
.TP
.B Diagnostics
.BR mpu_perror (3),
.BR mpu_strerror_r (3).
.TP
.B Raw binary I/O
.BR mpu_fread (3),
.BR mpu_fwrite (3),
and unlocked variants.
.TP
.B Character and UCS-2 string I/O
.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 Formatted output
.BR mpu_printf (3),
.BR mpu_fprintf (3),
.BR mpu_sprintf (3),
.BR mpu_snprintf (3),
.BR mpu_asprintf (3),
.BR mpu_dprintf (3)
and va_list/unlocked forms.
.TP
.B Formatted input
.BR mpu_scanf (3),
.BR mpu_fscanf (3),
.BR mpu_sscanf (3)
and va_list/unlocked forms.
.TP
.B Positioning
.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 Buffering, indicators, and explicit locking
.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 RETURN VALUES AND ERRORS
Each family follows stdio-style return conventions described on its manual
page. System-call failures preserve useful
.B errno
values. Stream failures set the stream error indicator; end of input sets EOF
only when an actual end-of-file condition is observed.
.SH NOTES
LIBMPUIO is a companion to LIBMPU, not a replacement ABI for libc stdio.
Do not cast between
.B mpu_FILE *
and libc
.BR FILE * .
.SH STREAM-STATE HARDENING
All three native backends participate in the same active
READING/WRITING state model. In particular, UCS-2 memory streams establish
WRITING state on text output, allowing mpu_fflush(NULL) to publish an active
mpu_open_memstream() just like the corresponding explicit stream flush.
.PP
Ordinary mpu_puts(), mpu_rewind(), and mpu_fileno() hold the stream mutex for
their complete logical operation; their explicitly unlocked counterparts,
where provided, remain caller-synchronized operations. Command-pipe streams
are not eligible for mpu_freopen() and retain their required mpu_pclose()
lifecycle.
.PP
Stream construction propagates recursive-mutex initialization errors directly,
and a standard stream whose mutex could not be initialized is not inserted
into the global stream list. File and cookie buffer replacement also clears
directional pointers before an allocation failure can expose stale addresses.
.SH STREAM-LIST LIFETIME HARDENING
All-stream traversal uses a pinned snapshot instead of
holding the global stream-list mutex while backend operations execute. This
keeps descriptor, string, and cookie streams alive across each traversal while
removing the global list lock from user-defined cookie callback execution.
.PP
If mpu_fclose() encounters a stream still present in an earlier snapshot, it
removes that stream from the live list and completes backend shutdown, but
defers destruction of the FILE object and recursive mutex until the final
snapshot pin is released. A later snapshot cannot acquire a new pin on the
unlinked stream.
.PP
Private UCS-2 streams created internally for sprintf/snprintf and sscanf
engines are not members of the public global stream list. Public
mpu_fmemopen() and mpu_open_memstream() streams remain registered and therefore
continue to participate in mpu_fflush(NULL) and mpu_flushlbf() as appropriate.
.SH MEMORY-SAFETY AND BUFFER-STATE HARDENING
Descriptor and cookie setvbuf replacement is transactional
with respect to allocation failure: replacement storage is prepared first, so
a failed allocation does not destroy the previous usable buffer state. The
unused internal _offset cache was removed; logical positions continue to be
derived from the backend position together with buffered read/write and UCS-2
pushback state.
.PP
The project-root Makefile builds only the
production shared library. Normal regression tests are built independently in
tests/, and AddressSanitizer, UndefinedBehaviorSanitizer and ThreadSanitizer
stress suites are built independently in tests/asan, tests/ubsan and tests/tsan.
Each test directory builds its own local libmpuio.so and runs its programs with
LD_LIBRARY_PATH=. Sanitizer instrumentation therefore never requires changing
or rebuilding the production objects in another mode.
.SH SEE ALSO
.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 INTERNAL SYMBOL VISIBILITY
Internal cross-module declarations in the private LIBMPUIO headers are ordinary \fBextern\fP declarations. Internal symbol helpers are defined in \fBmpu-symbols.h\fP. In the C file that defines each private cross-module function or data object, \fB__mpu_hidden_decl(name)\fP is emitted after the definition. The macro applies GCC \fBvisibility("hidden")\fP only when both \fBSHARED\fP and \fBHAVE_HIDDEN_VISIBILITY_ATTRIBUTE\fP are defined; the latter is supplied by \fBconfig.h\fP. This keeps compiler-feature policy out of the private interface declarations. File-local helpers remain \fBstatic\fP.
.PP
This rule also covers the private jump tables and stream-list state: \fB__mpu_IO_file_jumps\fP, \fB__mpu_IO_str_jumps\fP, \fB__mpu_IO_list_all\fP, and \fB__mpu_IO_list_lock\fP. Therefore no symbol with the private \fB__mpu_\fP prefix is exported by the shared library.
.PP
The hidden declarations define an ABI boundary only; documented public \fBmpu_*\fP functions and public stream objects remain exported.
|