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_fputcUCS‑2 characters; UTF‑8 на внешней границе
mpu_fgets/mpu_fputsUCS‑2 strings
mpu_printf/mpu_scanfформатированный UCS‑2 Text API
mpu_fread/mpu_fwriteraw 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
%sconst __mpu_char16_t *__mpu_char16_t *
%aconst __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|
%#Z1024b0b1010101111001101
%#Z4096x0xabcd
%Z65536u43981

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
0zero padding; знак остаётся перед нулями; у complex padding делится между компонентами
widthминимальная ширина поля
.precisionprecision числа или максимум 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 не входят.

FormatDestinationОсобенность
%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-003r, 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 fileexternal byte offset
native memory streamUCS‑2 code units
seekable cookie streamexternal 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 в bytesnative memory stream использует UCS‑2 elements
Использовать mpu_fread() как UTF‑8 decoderraw 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 pairsLibMPUIO использует strict UCS‑2
Использовать _unlocked без синхронизацииunlocked означает отсутствие implicit mutex

API Map

ЗадачаОсновные функции
filesmpu_fopen, mpu_fdopen, mpu_freopen, mpu_fclose, mpu_tmpfile
command pipempu_popen, mpu_pclose
custom transportmpu_fopencookie
UCS‑2 memorympu_fmemopen, mpu_open_memstream
raw bytesmpu_fread, mpu_fwrite
characters/linesmpu_fgetc, mpu_fputc, mpu_ungetc, mpu_fgets, mpu_fputs, mpu_getline, mpu_getdelim
formatted outputmpu_printf, mpu_fprintf, mpu_snprintf, mpu_asprintf, mpu_dprintf
formatted inputmpu_scanf, mpu_fscanf, mpu_sscanf
seek/tellmpu_fseeko, mpu_ftello, mpu_fgetpos, mpu_fsetpos, mpu_rewind
buffering/statusmpu_fflush, mpu_setvbuf, mpu_feof, mpu_ferror, mpu_clearerr, mpu_fpurge
lockingmpu_flockfile, mpu_ftrylockfile, mpu_funlockfile
byte stringsmpu_str8*
UCS‑2 stringsmpu_str16*
UTF‑8 stringsmpu_utf8*
UTF‑8/UCS‑2 bridgempu_utf8_to_ucs2, mpu_ucs2_to_utf8
UCS‑2 classificationmpu_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.