LibMPUIO – библиотека на языке C, предназначенная для программ, использующих LibMPU и числа большой разрядности. По модели интерфейса она близка к стандартному stdio: библиотека предоставляет собственный тип потока mpu_FILE, стандартные потоки, файловый ввод‑вывод, буферизацию, позиционирование, семейства printf/scanf, memory streams, command pipes, cookie streams и _unlocked‑варианты функций. При этом LibMPUIO не является ABI‑заменой libc stdio и использует собственную строгую текстовую модель.
Overview
Основная задача LibMPUIO – объединить в одном интерфейсе обычные объекты языка C и объекты LibMPU: целые, вещественные и комплексные числа произвольной точности. Обычный printf() не знает типы mpu_int4096_t, mpu_real16384_t или mpu_complex128_t. LibMPUIO расширяет format grammar модификатором z/Z<bits> и собственными real/complex conversions, сохраняя привычную потоковую модель.
mpu_printf( MPU_UCS2( "value = %d\n" ), 42 ); mpu_printf( MPU_UCS2( "n = %#Z4096x\n" ), n ); mpu_printf( MPU_UCS2( "r = %.64.4Z16384R\n" ), r );
Текущий выпуск LibMPUIO 1.0.4 рассчитан на совместную работу с LibMPU 1.0.25. Эта версия также содержит locale‑independent классификацию строгого UCS‑2 по Unicode 18.0.0.
Text Model
Главное архитектурное отличие LibMPUIO находится в представлении текста. Внутренний Text API использует __mpu_char16_t и строгий UCS‑2, а реальные файлы, терминалы, descriptor‑backed streams, cookie streams и pipes имеют внешнюю UTF‑8 границу. Raw I/O остаётся отдельной веткой API и не выполняет перекодирование.
application
|
| UCS-2: __mpu_char16_t
v
+----------------+
| LibMPUIO |
+--------+-------+
|
+-------------------+------------------+
| | |
real file memory stream cookie stream
UTF-8 UCS-2 external bytes
| | |
file / console memory callbacks
input : UTF-8 bytes -> strict decoder -> UCS-2 characters output: UCS-2 characters -> UTF-8 encoder -> bytes
Strict UCS‑2
Один элемент __mpu_char16_t представляет один допустимый Unicode scalar value из Basic Multilingual Plane, то есть диапазона U+0000...U+FFFF, за исключением surrogate code units U+D800...U+DFFF. Внутреннее представление не является UTF‑16 с surrogate pairs. Символы выше U+FFFF корректны в UTF‑8, но не могут быть помещены в один UCS‑2 code unit и при таком преобразовании приводят к EILSEQ.
__mpu_char16_t text[] = MPU_UCS2( "Привет" ); mpu_printf( MPU_UCS2( "%s\n" ), text );
Макро MPU_UCS2() формирует 16‑битный строковый литерал непосредственно в исходном коде. Это compile‑time механизм, а не runtime converter.
Locale and the Three Text Contracts
Следует различать три контракта над 8‑битными строками. Семейство mpu_str8* работает с байтами, mpu_utf8* интерпретирует строку как UTF‑8, а формат %a рассматривает __mpu_char8_t * как multibyte string текущей LC_CTYPE. Поэтому %a является UTF‑8 преобразованием только тогда, когда активная locale действительно UTF‑8.
%a output: current LC_CTYPE multibyte bytes -> locale decoder -> strict UCS-2 -> LibMPUIO stream -> UTF-8 on a real external text backend
Обычные C conversions %e/%E/%f/%F/%g/%G следуют активной LC_NUMERIC. Форматы LibMPU с z/Z<bits> используют собственную numeric grammar. Начиная с LibMPU 1.0.17 функция __mpu_init() не выполняет скрытый setlocale(), поэтому locale процесса остаётся под контролем приложения.
Unicode 18.0.0 Character Classification
LibMPUIO 1.0.4 добавляет публичную классификацию символов строгого UCS‑2. Она не зависит от locale и использует предварительно сгенерированную таблицу Unicode Character Database 18.0.0. Обычная сборка библиотеки не загружает Unicode data из сети. Surrogate code units всегда имеют нулевую классификацию.
mpu_ucs2_isalpha( c ); mpu_ucs2_isupper( c ); mpu_ucs2_islower( c ); mpu_ucs2_isdigit( c ); mpu_ucs2_is_xid_start( c ); mpu_ucs2_is_xid_continue( c );
Первые три функции используют Unicode properties Alphabetic, Uppercase и Lowercase; mpu_ucs2_isdigit() соответствует General Category Nd. XID_Start/XID_Continue предназначены, в частности, для lexer/preprocessor кода, которому нужна воспроизводимая Unicode‑классификация идентификаторов.
Streams
Публичная stream‑абстракция LibMPUIO – непрозрачный mpu_FILE *. Потоки LibMPUIO не являются объектами FILE * host libc и не должны смешиваться с stdin, stdout и stderr. Библиотека предоставляет собственные стандартные потоки:
mpu_stdin mpu_stdout mpu_stderr
Для обычных файлов доступны mpu_fopen(), mpu_fdopen(), mpu_freopen(), mpu_tmpfile(), mpu_fclose() и mpu_fileno(). Mode grammar следует модели r/w/a с необязательными + и b. Буква b не меняет текстовую кодировку: различие text/raw задаётся самой вызываемой функцией.
Text I/O and Raw Byte I/O
| API | Единицы и преобразование |
|---|---|
| mpu_fgetc/mpu_fputc | UCS‑2 characters; UTF‑8 на внешней границе |
| mpu_fgets/mpu_fputs | UCS‑2 strings |
| mpu_printf/mpu_scanf | форматированный UCS‑2 Text API |
| mpu_fread/mpu_fwrite | raw bytes; без UTF‑8/UCS‑2 conversion |
Raw functions предназначены для descriptor‑backed и cookie byte backends. Нативные UCS‑2 memory streams являются текстовыми потоками; raw mpu_fread()/mpu_fwrite() для них не определены.
String Families and Explicit Conversion
| Семейство | Модель |
|---|---|
| mpu_str8* | обычные byte strings |
| mpu_str16* | UCS‑2 code units |
| mpu_utf8* | UTF‑8 validation and character operations |
__mpu_char8_t utf8[256]; __mpu_char16_t ucs2[256]; mpu_utf8_to_ucs2( ucs2, "Андрей", 256 ); mpu_ucs2_to_utf8( utf8, ucs2, sizeof( utf8 ) );
При dest == NULL функции преобразования валидируют весь input и возвращают точный размер payload без terminating NUL. Это позволяет выполнять двухпроходное выделение буфера. При ошибке кодирования возвращается (__mpu_size_t)-1 и устанавливается EILSEQ.
UCS‑2 Memory Streams
mpu_fmemopen() создаёт фиксированный поток поверх caller‑owned массива __mpu_char16_t. Размер задаётся в UCS‑2 elements, включая место для NUL при записи. mpu_open_memstream() создаёт динамический output stream; библиотека сама увеличивает буфер и публикует актуальные pointer/length после mpu_fflush() или mpu_fclose().
__mpu_char16_t storage[128]; mpu_FILE *fp; fp = mpu_fmemopen( storage, 128, "w+" ); mpu_fprintf( fp, MPU_UCS2( "value=%d" ), 42 ); mpu_fflush( fp ); mpu_fclose( fp );
Позиции native memory streams измеряются в UCS‑2 code units. Для динамического stream seek может уйти за текущий logical end; последующая запись материализует промежуток нулевыми UCS‑2 code units.
Cookie Streams and Command Pipes
mpu_fopencookie() строит mpu_FILE поверх пользовательских byte callbacks. Text API кодирует UCS‑2 в UTF‑8 перед write callback и декодирует UTF‑8 после read callback. Raw I/O передаёт bytes напрямую. Append mode требует seek callback, а mpu_fileno() для cookie stream возвращает EBADF.
mpu_FILE *fp;
fp = mpu_popen( "printf 'hello\\n'", "r" );
if( fp != NULL )
{
__mpu_char16_t line[32];
mpu_fgets( line, 32, fp );
mpu_printf( MPU_UCS2( "%s" ), line );
mpu_pclose( fp );
}
mpu_popen() поддерживает только однонаправленные режимы "r" и "w". Command stream должен завершаться через mpu_pclose(), поскольку именно он ожидает завершения дочернего процесса.
Formatted Output
Семейство форматированного вывода включает mpu_printf(), mpu_fprintf(), mpu_sprintf(), mpu_snprintf(), mpu_asprintf(), mpu_dprintf() и соответствующие v* формы. Format string всегда имеет тип const __mpu_char16_t *. Возвращаемый count измеряется в логических UCS‑2 characters, а не во внешних UTF‑8 bytes.
Практически важнее всего не запомнить синтаксис формата, а понимать, что именно появится в поле вывода. В таблицах ниже результат заключён в |...|, а символ · обозначает обычный пробел. Поэтому ширина, выравнивание и padding видны буквально.
Actual output: | abc| Visible field: |·······abc|
String Conversions: %s and %a
В LibMPUIO %s означает native UCS‑2 string, а %a – 8‑битную multibyte string текущей LC_CTYPE. Это одно из главных отличий от libc и одна из самых частых причин ошибок при первом знакомстве с библиотекой.
| Format | Output argument | Input destination |
|---|---|---|
| %s | const __mpu_char16_t * | __mpu_char16_t * |
| %a | const __mpu_char8_t *, current locale | __mpu_char8_t *, current locale |
__mpu_char16_t s16[] = MPU_UCS2( "abc" ); __mpu_char8_t s8[] = "Андрей";
| Format | Поле (· = пробел) | Что происходит |
|---|---|---|
| %s | |abc| | UCS‑2 string без ограничения ширины |
| %10s | |·······abc| | поле минимум 10 characters, вправо |
| %-10s | |abc·······| | выравнивание влево |
| %.2s | |ab| | не более двух UCS‑2 characters |
| %10.2s | |········ab| | precision 2 внутри поля 10 |
| %a | |Андрей| | current‑locale multibyte string |
| %10a | |····Андрей| | 6 characters + 4 spaces |
| %.3a | |Анд| | precision считается в преобразованных UCS‑2 characters |
| %10.3a | |·······Анд| | три characters в поле 10 |
Width и precision для %a считаются после locale conversion в UCS‑2 characters, а не в исходных multibyte bytes. Поэтому %20a при multibyte locale не означает, что destination buffer при scanf достаточно сделать размером 21 byte. В LibMPUIO строчная %a не является C99 hex‑float conversion.
Ordinary C Integer and Floating Formats
Обычные integer conversions ведут себя привычно, включая width, precision, знак, alternate form и zero padding. Дополнительно LibMPUIO поддерживает двоичные %b/%B.
| Format | Поле для n = 12345 | Смысл |
|---|---|---|
| %d | |12345| | signed decimal |
| %10d | |·····12345| | поле 10, вправо |
| %-10d | |12345·····| | поле 10, влево |
| %+10d | |····+12345| | всегда показывать знак |
| %010d | |0000012345| | zero padding |
| %.8d | |00012345| | минимум 8 digits |
| %10.8d | |··00012345| | precision 8 внутри width 10 |
| Значение | Format | Поле |
|---|---|---|
| 0xabcd | %x | |abcd| |
| 0xabcd | %#x | |0xabcd| |
| 0xabcd | %#010x | |0x0000abcd| |
| 13 | %b | |1101| |
| 13 | %#b | |0b1101| |
| 0755 | %#o | |0755| |
Ordinary floating conversions %e/%E/%f/%F/%g/%G используют host libc и текущий LC_NUMERIC. Это другой путь, чем LibMPU real formats.
| Format | Поле при x = 12.34567 |
|---|---|
| %f | |12.345670| |
| %.2f | |12.35| |
| %10.2f | |·····12.35| |
| %+10.2f | |····+12.35| |
| %.3e | |1.235e+01| |
Width and Precision through *
mpu_printf( MPU_UCS2( "|%*d|\n" ), 10, 12345 ); /* | 12345| */ mpu_printf( MPU_UCS2( "|%*d|\n" ), -10, 12345 ); /* |12345 | */ mpu_printf( MPU_UCS2( "|%*.*f|\n" ), 12, 3, 12.34567 ); /* | 12.346| */
Отрицательная width, полученная через *, трактуется как положительная ширина вместе с флагом -. Precision также может поступать отдельным аргументом через .*.
LibMPU Integer Formats: z/Z<bits>
Размер LibMPU object является частью variadic format contract. Модификаторы z<bits> и Z<bits> эквивалентны; если digits опущены, используется 128 bits. Поэтому %zu в LibMPUIO означает %Z128u, а не стандартный C size_t. Integer formatted I/O поддерживает MPU sizes от 8 до 65536 bits независимо от real/complex I/O limit.
mpu_int128_t n; iatoi( n, (__mpu_char8_t *)"12345", NB_I128 );
| Format | Поле (· = пробел) | Разбор |
|---|---|---|
| %Z128d | |12345| | 128‑bit signed decimal MPU integer |
| %20Z128d | |···············12345| | поле 20, вправо |
| %-20Z128d | |12345···············| | поле 20, влево |
| %+20Z128d | |··············+12345| | знак положительного числа |
| %020Z128d | |00000000000000012345| | zero padding |
| %20.10Z128d | |··········0000012345| | минимум 10 digits внутри поля 20 |
| Format | Пример результата |
|---|---|
| %Z128x | |abcd| |
| %#Z128x | |0xabcd| |
| %#20Z128x | |··············0xabcd| |
| %#Z128b | |0b1010101111001101| |
| %#Z1024b | 0b1010101111001101 |
| %#Z4096x | 0xabcd |
| %Z65536u | 43981 |
LibMPU Real Formats
mpu_real128_t r; ascii_to_real( r, (__mpu_char8_t *)"12345.6789", NB_R128 );
Первая precision задаёт число цифр mantissa. Вторая precision .expdigits – расширение LibMPUIO, задающее минимальную ширину exponent. Native r/R выбирают регистр представления, но printed exponent delimiter для real output остаётся e/E. Исторически MPU f/F являются scientific aliases e/E, а не fixed‑point formats.
| Format | Поле для r = 12345.6789 | Смысл |
|---|---|---|
| %Z128R | |1.234568E+4| | native MPU real |
| %.8Z128R | |1.23456789E+4| | 8 mantissa digits |
| %.4.3Z128e | |1.2346e+004| | precision 4, exponent width 3 |
| %20.4.3Z128e | |·········1.2346e+004| | поле 20, вправо |
| %-20.4.3Z128e | |1.2346e+004·········| | поле 20, влево |
| %20.6Z128g | |·············12345.7| | general format |
| %+20.6Z128g | |············+12345.7| | ведущий плюс |
| %+020.8.4Z128R | |+0001.23456789E+0004| | знак перед zero padding |
Для g/G alternate form # сохраняет decimal point и trailing fractional zeroes. Например для r = 12.5 формат %.8Z128g даёт 12.5, а %#.8Z128g – 12.500000.
%.4.3Z128e -> 1.2346e+004 %.4.3Z128f -> 1.2346e+004
Последние две строки показывают историческую особенность: для LibMPU object f/F являются scientific aliases e/E. Это намеренно не совпадает с ordinary C %f.
LibMPU Complex Formats
mpu_real128_t re, im; mpu_complex128_t c; ascii_to_real( re, (__mpu_char8_t *)"1.25", NB_R128 ); ascii_to_real( im, (__mpu_char8_t *)"-2.5", NB_R128 ); c_gen_complex( c, re, im, NB_C128 );
Complex conversion использует j/J. Для uppercase формы real и imaginary components получают маркеры R и J; lowercase форма использует r/j. Imaginary component всегда имеет собственный знак между компонентами.
| Format | Поле (· = пробел) |
|---|---|
| %+.6.3Z128J | |+1.250000R+000-2.500000J+000| |
| %.6.3Z128J | |1.250000R+000-2.500000J+000| |
| %40.6.3Z128J | |·············1.250000R+000-2.500000J+000| |
| %+40.6.3Z128J | |············+1.250000R+000-2.500000J+000| |
| %-40.6.3Z128J | |1.250000R+000-2.500000J+000·············| |
При обычном space padding всё complex representation рассматривается как одно поле. При zero padding действует специальное правило: свободная ширина делится между real и imaginary components.
padding = width - formatted_length component_zeros = padding / 2 outer_padding = padding % 2
| Format | Поле (· = пробел) |
|---|---|
| %+040.6.3Z128J | |+0000001.250000R+000-0000002.500000J+000| |
| %+039.6.3Z128J | |·+000001.250000R+000-000002.500000J+000| |
| %+038.6.3Z128J | |+000001.250000R+000-000002.500000J+000| |
| %+037.6.3Z128J | |·+00001.250000R+000-00002.500000J+000| |
| %+036.6.3Z128J | |+00001.250000R+000-00002.500000J+000| |
| %+035.6.3Z128J | |·+0001.250000R+000-0002.500000J+000| |
| %+034.6.3Z128J | |+0001.250000R+000-0002.500000J+000| |
| %+033.6.3Z128J | |·+001.250000R+000-002.500000J+000| |
| %+032.6.3Z128J | |+001.250000R+000-002.500000J+000| |
| %+031.6.3Z128J | |·+01.250000R+000-02.500000J+000| |
| %+030.6.3Z128J | |+01.250000R+000-02.500000J+000| |
| %+029.6.3Z128J | |·+1.250000R+000-2.500000J+000| |
| %+028.6.3Z128J | |+1.250000R+000-2.500000J+000| |
Эта последовательность особенно полезна при отладке: уменьшение width на два убирает по одному leading zero у каждой компоненты; нечётная свободная ширина оставляет один внешний пробел. Знак каждой компоненты всегда остаётся перед её собственными нулями.
Flags and Format Anatomy
| Элемент | Значение |
|---|---|
| - | выравнивание влево |
| + | показывать знак для поддерживаемых signed conversions, включая MPU real и complex |
| space | пробел вместо +; явный + имеет приоритет |
| # | alternate form: prefixes и сохранение decimal point/trailing zeroes у MPU g/G |
| 0 | zero padding; знак остаётся перед нулями; у complex padding делится между компонентами |
| width | минимальная ширина поля |
| .precision | precision числа или максимум characters строки |
| .expdigits | дополнительная ширина exponent для MPU real/complex |
| z/Z<bits> | размер LibMPU object и часть variadic type contract |
%+020.8.4Z128R | || | | | | || | | +-- R: uppercase native MPU real conversion | || | +------ Z128: mpu_real128_t | || +-------- .4: minimum exponent width | |+----------- .8: mantissa precision | +------------ 20: minimum field width +-------------- + and 0 flags result: |+0001.23456789E+0004|
Positional arguments glibc вида %2$d и %1$s для перестановки variadic arguments текущим formatter LibMPUIO не реализованы.
Character, Percent, %n and Practical Columns
Несколько небольших conversions часто нужны именно в диагностическом коде. %c выводит один UCS‑2 character, %% выводит literal percent sign, а %n ничего не печатает и записывает число уже произведённых логических UCS‑2 characters.
mpu_printf( MPU_UCS2( "[%c]\n" ), 'A' ); /* [A] */ mpu_printf( MPU_UCS2( "100%% ready\n" ) ); /* 100% ready */ int n; mpu_printf( MPU_UCS2( "abc%nDEF\n" ), &n ); /* output: abcDEF */ /* n == 3 */
Счётчик %n измеряет UCS‑2 characters, а не число внешних UTF‑8 bytes. Это важно для строк с кириллицей и другими многобайтными UTF‑8 символами.
Форматы удобно комбинировать для реальных диагностических таблиц. Например, один и тот же mpu_int128_t можно вывести одновременно как decimal и hexadecimal value:
mpu_printf( MPU_UCS2( "%-16s | %24s | %24s\n" ),
MPU_UCS2( "Variable" ),
MPU_UCS2( "Value" ),
MPU_UCS2( "Hex" ) );
mpu_printf( MPU_UCS2( "%-16s | %24Z128d | %#24Z128x\n" ),
MPU_UCS2( "counter" ), value, value );
Variable | Value | Hex -----------------|--------------------------|-------------------------- counter | 12345 | 0x3039
А несколько real values можно держать на одной строке, не выполняя никаких промежуточных преобразований:
mpu_printf( MPU_UCS2( "x = %20.8Z128g; y = %20.8Z128g;\n" ), x, y );
x = 123.45678; y = -987.65432;
Ready-to-use Diagnostic Formats
Для ручной проверки formatter удобно прогонять не один случай, а соседние варианты width и flags. Ниже набор, который полезно держать под рукой при разработке и диагностике.
/* Strings */ "|%20s|" "|%-20s|" "|%20.10s|" "|%20a|" "|%-20a|" "|%20.10a|" /* Ordinary integer */ "|%20d|" "|%020d|" "|%+20d|" "|%#20x|" "|%#20b|" /* MPU integer */ "|%20Z128d|" "|%020Z128d|" "|%+20Z128d|" "|%#20Z128x|" "|%#40Z256b|" /* MPU real */ "|%24Z128R|" "|%24.16Z128R|" "|%24.16.4Z128R|" "|%-24.16.4Z128R|" "|%+24.16Z128g|" "|%+020.8.4Z128R|" /* MPU complex */ "|%48.16.4Z128J|" "|%-48.16.4Z128J|" "|%+.16.4Z128J|" "|%+040.6.3Z128J|" "|%+039.6.3Z128J|" "|%+038.6.3Z128J|" "|%+029.6.3Z128J|" "|%+028.6.3Z128J|"
Formatted Input
Форматированный ввод представлен mpu_scanf/mpu_vscanf, mpu_fscanf/mpu_vfscanf и mpu_sscanf/mpu_vsscanf. File input сначала декодируется из UTF‑8 в UCS‑2, после чего применяется scanf grammar. Возвращаемое значение – число успешно выполненных assignments; %n и suppressed conversion %*... в этот count не входят.
| Format | Destination | Особенность |
|---|---|---|
| %c | __mpu_char16_t * | не пропускает leading whitespace, не добавляет NUL |
| %s | __mpu_char16_t * | UCS‑2 token с terminating NUL |
| %a | __mpu_char8_t * | current‑locale multibyte token |
| %[ | __mpu_char16_t * | UCS‑2 scanset |
Следует различать три результата до первого successful assignment: input failure возвращает mpu_EOF, matching failure возвращает 0, а ошибка после нескольких assignments возвращает число уже выполненных assignments.
Reading LibMPU Real and Complex Values
mpu_real128_t r1, r2;
mpu_sscanf( MPU_UCS2( "1.23456789E+10" ),
MPU_UCS2( "%Z128R" ), r1 );
mpu_sscanf( MPU_UCS2( "1.23456789E+10" ),
MPU_UCS2( "%Z128E" ), r2 );
mpu_printf( MPU_UCS2( "R: %Z128R\n" ), r1 );
mpu_printf( MPU_UCS2( "E: %Z128R\n" ), r2 );
R: 1.234568E+10 E: 1.234568E+10
%Z128R и %Z128E читают один и тот же тип mpu_real128_t и используют общий real conversion path. %Z128J требует mpu_complex128_t storage.
LibMPU Numeric Grammar and Rollback
Scanner намеренно строже низкоуровневого ascii_to_real(). Для обычного конечного real token mantissa должна содержать хотя бы одну десятичную цифру; decimal point сам по себе не превращает token в число. Если присутствует delimiter e/r/j, после необязательного знака должна присутствовать хотя бы одна exponent digit.
| Валидные примеры | Невалидные примеры |
|---|---|
| 0, +0, -0 | ., +. |
| .0, 3.14, 123. | -.e, .e0 |
| -3.14e+17, 1.25e-003 | r, j, 1e, 1e+ |
Special values inf, infinity, NaN, ind, inf_e, inf_r и inf_j обрабатываются отдельно. Scanner использует rollback: незавершённый exponent, prefix или payload не должен без необходимости поглощать символы, принадлежащие следующей conversion.
input: 123abc %d: consumes 123 remain: abc input: 12e+X real: may accept 12 remain: e+X
Complex Input Grammar
1r0 -1r0 1.25r+3+2.5j-4 1.25r+3 +2.5j-4 -2.5j-4 0r0+0j0 1r0 2j0
После real component положительная imaginary component может следовать через whitespace без явного +. Отсутствующая компонента инициализируется точным нулём. Реальная и мнимая части разбираются отдельно и передаются в ascii_to_real(), поэтому semantic authority над исторической LibMPU grammar остаётся в LibMPU.
Everyday I/O Recipes
Ниже собраны небольшие шаблоны, которые можно почти буквально переносить в прикладной код. Они показывают не отдельную функцию, а полный путь данных: какой тип строки хранится в программе, какая функция вызывается и что оказывается во внешнем потоке.
Write a UTF‑8 Text File
mpu_FILE *fp;
fp = mpu_fopen( "hello.txt", "w" );
if( fp == NULL )
return 1;
if( mpu_fprintf( fp, MPU_UCS2( "Hello, Андрей!\n" ) ) < 0 )
{
mpu_fclose( fp );
return 1;
}
if( mpu_fclose( fp ) != 0 )
return 1;
Format string хранится как UCS‑2, но hello.txt содержит UTF‑8. Отдельный encoder вызывать не требуется: это normal text stream path.
Read a Line as UCS‑2
__mpu_char16_t *line = NULL; size_t capacity = 0; ssize_t length; length = mpu_getline( &line, &capacity, fp ); if( length >= 0 ) mpu_printf( MPU_UCS2( "line = '%s'\n" ), line ); free( line );
capacity измеряется в UCS‑2 elements, а возвращаемая длина не включает final NUL. Внешний UTF‑8 decode выполняется самим file stream.
Use %a or Convert Explicitly
__mpu_char8_t name8[] = "Андрей"; __mpu_char16_t name16[128]; /* Direct formatted bridge through current LC_CTYPE: */ mpu_printf( MPU_UCS2( "name = %a\n" ), name8 ); /* Keep the result as a UCS-2 object for later use: */ mpu_utf8_to_ucs2( name16, name8, 128 ); mpu_printf( MPU_UCS2( "name = %s\n" ), name16 );
Первый вариант зависит от current LC_CTYPE. Второй явно требует UTF‑8 и создаёт UCS‑2 object, который затем можно передавать в mpu_str16* и Text API.
Format into a UCS‑2 Buffer
__mpu_char16_t buffer[128];
int n;
n = mpu_snprintf( buffer, 128,
MPU_UCS2( "value = %d" ), value );
Размер 128 здесь означает число UCS‑2 code units, включая место для NUL. size == 0 допускает sizing call; return value показывает полный логический размер.
Print a Large Real Directly
mpu_real16384_t r, d; int nb = NB_R16384; ascii_to_real( r, "2.0", nb ); ascii_to_real( d, "1.0", nb ); r_atan2( r, r, d, nb ); mpu_printf( MPU_UCS2( "r = %Z16384E;\n" ), r );
Для вывода LibMPU real object не требуется промежуточный real_to_ascii(). Размер и ожидаемый тип storage задаются непосредственно форматом Z16384E.
Buffering, State and Positioning
Поддерживаются full, line и unbuffered modes: MPU_IOFBF, MPU_IOLBF, MPU_IONBF. Основные интерфейсы – mpu_setvbuf(), mpu_setbuf(), mpu_setlinebuf(), mpu_fflush(), mpu_flushlbf() и mpu_fpurge(). mpu_fflush(NULL) синхронизирует все активные writing streams, включая активно записываемый mpu_open_memstream().
| Backend | Единицы позиции |
|---|---|
| real UTF‑8 file | external byte offset |
| native memory stream | UCS‑2 code units |
| seekable cookie stream | external byte position |
mpu_feof() показывает EOF только после операции, реально обнаружившей конец input; mpu_ferror() показывает stream error indicator. На nonblocking descriptor EAGAIN/EWOULDBLOCK является ошибкой, а не EOF.
Locking and Unlocked API
Обычные stream operations используют recursive mutex внутри mpu_FILE. Для серии операций lock можно взять один раз через mpu_flockfile(), использовать _unlocked variants и затем вызвать mpu_funlockfile(). Unlocked меняет только implicit locking; encoding, buffering, errors и return semantics остаются теми же.
mpu_flockfile( fp ); mpu_fputs_unlocked( MPU_UCS2( "first" ), fp ); mpu_fputc_unlocked( '\n', fp ); mpu_fputs_unlocked( MPU_UCS2( "second" ), fp ); mpu_fflush_unlocked( fp ); mpu_funlockfile( fp );
Global stream list, необходимый для mpu_fflush(NULL) и mpu_flushlbf(), использует snapshot/lifetime pinning. Это позволяет cookie callbacks создавать или закрывать другие streams без удержания global list lock во время backend call. Одновременное закрытие и обычное использование одного и того же mpu_FILE * всё равно должно быть синхронизировано приложением.
Build and Installation
LibMPUIO 1.0.4 использует GNU Autotools, GNU C compiler и POSIX threads. В configure.ac проверяется минимальная версия LibMPU 1.0.25. Release tarball уже содержит configure и generated Makefiles; bootstrap требуется только для VCS tree.
$ tar xJvf libmpuio-1.0.4.tar.xz
$ mkdir build
$ cd build
$ ../libmpuio-1.0.4/configure \
--prefix=/usr \
--libdir=/usr/lib64 \
--enable-static=no
$ make
# make install
При пакетной сборке конечный prefix остаётся /usr, а staging выполняется через DESTDIR. Для прикладных программ устанавливается mpuio-config, предоставляющий --cflags, --ldflags и --libs.
$ gcc `mpuio-config --cflags` -c -o main.o main.c $ gcc `mpuio-config --ldflags` -o main main.o `mpuio-config --libs` -lpthread
Practical Example
Следующий пример соединяет LibMPU arithmetic, прямой formatted output большого real object и явное UTF‑8/UCS‑2 преобразование. Он полезен как минимальный шаблон программы, использующей обе библиотеки.
#include <locale.h>
#include <stdio.h>
#include <libmpuio.h>
int
main( void )
{
mpu_real16384_t r, d;
int nb = NB_R16384;
__mpu_char8_t s[6000] = { 0 };
__mpu_char16_t u[6000] = { 0 };
setlocale( LC_ALL, "" );
__mpu_init();
ascii_to_real( r, "2.0", nb );
ascii_to_real( d, "1.0", nb );
r_atan2( r, r, d, nb );
mpu_printf( MPU_UCS2( "r = %Z16384E;\n" ), r );
real_to_ascii( s, r, _real_mant_digs( nb ), 'E', 0, 0, nb );
mpu_utf8_to_ucs2( u, s, 6000 );
mpu_printf( MPU_UCS2( "s = %s\n\nrc = %Z16384E;\n" ), u, r );
mpu_utf8_to_ucs2( u, "Андрей", 6000 );
mpu_printf( MPU_UCS2( "name = %s\n" ), u );
__mpu_free_context();
return 0;
}
Arithmetic остаётся в LibMPU. LibMPUIO печатает mpu_real16384_t непосредственно через Z16384. real_to_ascii() создаёт 8‑битную строку; если она нужна именно как UCS‑2 object для дальнейшей работы, используется mpu_utf8_to_ucs2(). Если же current‑locale multibyte string требуется только как аргумент formatter, часто достаточно %a.
Common Mistakes
| Ошибка | Правильная модель |
|---|---|
| Передать char * в %s | %s – UCS‑2; use %a for current‑locale multibyte text |
| Считать %a всегда UTF‑8 | %a follows LC_CTYPE |
| Измерять memory stream в bytes | native memory stream использует UCS‑2 elements |
| Использовать mpu_fread() как UTF‑8 decoder | raw I/O всегда остаётся byte‑oriented |
| Забыть z/Z<bits> | pointer на LibMPU object не содержит runtime size metadata |
| Считать MPU %f fixed‑point format | для MPU f/F – historical aliases e/E |
| Ожидать UTF‑16 surrogate pairs | LibMPUIO использует strict UCS‑2 |
| Использовать _unlocked без синхронизации | unlocked означает отсутствие implicit mutex |
API Map
| Задача | Основные функции |
|---|---|
| files | mpu_fopen, mpu_fdopen, mpu_freopen, mpu_fclose, mpu_tmpfile |
| command pipe | mpu_popen, mpu_pclose |
| custom transport | mpu_fopencookie |
| UCS‑2 memory | mpu_fmemopen, mpu_open_memstream |
| raw bytes | mpu_fread, mpu_fwrite |
| characters/lines | mpu_fgetc, mpu_fputc, mpu_ungetc, mpu_fgets, mpu_fputs, mpu_getline, mpu_getdelim |
| formatted output | mpu_printf, mpu_fprintf, mpu_snprintf, mpu_asprintf, mpu_dprintf |
| formatted input | mpu_scanf, mpu_fscanf, mpu_sscanf |
| seek/tell | mpu_fseeko, mpu_ftello, mpu_fgetpos, mpu_fsetpos, mpu_rewind |
| buffering/status | mpu_fflush, mpu_setvbuf, mpu_feof, mpu_ferror, mpu_clearerr, mpu_fpurge |
| locking | mpu_flockfile, mpu_ftrylockfile, mpu_funlockfile |
| byte strings | mpu_str8* |
| UCS‑2 strings | mpu_str16* |
| UTF‑8 strings | mpu_utf8* |
| UTF‑8/UCS‑2 bridge | mpu_utf8_to_ucs2, mpu_ucs2_to_utf8 |
| UCS‑2 classification | mpu_ucs2_isalpha, mpu_ucs2_isupper, mpu_ucs2_islower, mpu_ucs2_isdigit, mpu_ucs2_is_xid_start, mpu_ucs2_is_xid_continue |
Sources and Releases
Текущий публичный выпуск: 1.0.4.