Система · поставляется с 0.1.4.6
LibL
Грань libc стандартной библиотеки Loment. Те же имена, тот же порядок аргументов и та же семантика возврата, что у libc, — для тех, кто уже знает C. Это вторая грань одной библиотеки, а не вторая библиотека.
Доступен с 0.1.4.6
С 0.1.4.6 выпускная сборка принимает use libl, а выпускная
сборка может слинковать настоящий libc.so.6 системы и довести его до конца.
Замок, который держал и то, и другое в 0.1.4.5, открыт, и примечания к выпуску это говорят.
«Доступен» — не «полон». Каждое имя здесь проверено против glibc системы, а недостающий набор выписан ниже, а не оставлен читателю на стадию линковки. Страница сохраняет и причины: грань libc без них — обещание, которое никто не может проверить.
Почему вторая грань
Стандартная библиотека уже существовала, и в ней не было ни одного имени
libc — ни strlen, ни printf, ни
malloc. Это было не упущение, а форма. Её функции принимают явную длину, а
не ищут терминатор, адресуют буфер со смещением и никогда не принимают переменное число
аргументов. В языке нет строк, завершающихся NUL, и нет функций с переменным числом
аргументов, поэтому библиотека написана так, как устроен язык.
Эта форма верна для тех, кто пишет на Loment, и она — стена для тех, кто знает C. Вторая
грань отвечает на другой вопрос: как это называлось бы, если бы вы никогда не
слышали о Loment? strcmp, возвращающий то же, что и в C.
qsort. isdigit. Учить нечего.
Обе грани указывают на одни и те же байты. isdigit
вызывает собственный предикат символа библиотеки; memcpy вызывает
собственную процедуру копирования. Так что сбой, которого можно было бы ждать от двух
граней, — один и тот же байт отвечает по-разному в зависимости от того, по какому
имени спросили, — структурно невозможен, а не просто избегнут.
Два входа
LibL — имя библиотеки; std по-прежнему имя пакета. Оба
входа ведут в один и тот же пакет и различаются тем, сколько они тянут за собой.
use std
Весь пакет. То, что уже делает всё написанное до LibL, и продолжает работать. Цена — вход на уровне всего пакета стягивает каждый модуль в одну единицу, а пространство испускаемых символов в этом языке плоское, поэтому все эти имена верхнего уровня должны быть уникальны вместе, и время компиляции растёт быстрее числа модулей.
use libl
Четыре грани libc и то, от чего они зависят, — шесть модулей. Маленькая грань: компилируется быстро, и сталкиваться почти не с чем. Это вход, когда нужны только имена libc.
Что он покрывает
Один заголовок libc отображается на один модуль, и само это отображение — часть следования libc.
<string.h>
strlen, strcpy, strncpy, strcat,
strcmp, strncmp, strchr, strrchr,
strstr, strspn, strcspn, strpbrk,
strtok_r, memchr, memcmp, memcpy,
memmove, memset.
<ctype.h>
isdigit, isalpha, isalnum,
isspace, isupper, islower,
isxdigit, ispunct, isprint, и остальной
набор, с toupper и tolower.
<stdlib.h>
Та половина, которую можно построить без кучи: atoi, atol,
strtol, strtoul, abs, labs,
llabs, qsort, bsearch.
<stdio.h>
Половина форматирования: snprintf, sprintf и остальные форматы
printf. Файловая половина пришла в 0.1.4.6 отдельным модулем
host — fopen, fclose, fread,
fwrite, fseek, ftell, fflush,
feof, fgets, fputs, puts,
putchar, getchar — потому что им нужны системные вызовы, а
основная библиотека не делает ни одного.
Плюс одна функция, которой нет в libc, — и она самая интересная; см. ниже.
Контрольный образец — сама libc
Каждая другая часть стандартной библиотеки судится сравнением того, что вычисляет Loment,
с тем, что вычисляет Python. Это неверный контрольный образец для этого вопроса,
и доказуемо: ровно на гранях, которые определяют «ведёт себя как libc», — обнуляет
ли strncpy, возвращает ли strcmp знак или разность,
находит ли strchr терминатор, безопасен ли memmove при
перекрытии диапазонов, — Python и libc дают не один и тот же ответ. Сравнение с
Python позволило бы двум реализациям ошибиться вместе и пройти.
Поэтому критерий компилирует программу на C против настоящей glibc системы, прогоняет
один и тот же набор вызовов с обеих сторон и сравнивает стандартный вывод
побайтово, сообщая первую отличающуюся строку. Две нормализации нарочны, иначе
тест измерял бы детали реализации, а не семантику: стандарт обещает только знак
strcmp, поэтому проверяются и знак, и разность; а предикаты символов glibc
возвращают битовую маску, а не 1, поэтому обе стороны сводятся к «да или нет»
перед сравнением.
Что навязал язык
Правилом для этой работы было «как libc это реализует, так LibL это и реализует». Язык — не C, поэтому в четырёх местах пришлось отойти и в одном — добавить. Каждое записано, а не спрятано.
Строковый литерал не несёт терминатор
Поэтому strlen на литерале читает за концом. В первом зонде он вернул
17 для пустой строки, пройдя дальше, пока случайно не встретил
нулевой байт. C не нуждается здесь в функции, потому что "abc" в C уже
несёт свой NUL. Это не упущение — два типа str не одно и
то же: str этого языка несёт длину, str в C — нет.
LibL добавляет cstr_of, чтобы преобразовать одно в другое, и каждая
NUL-строковая функция в библиотеке в нём нуждается.
Нет функций с переменным числом аргументов
Поэтому snprintf принимает на один аргумент больше, чем в libc:
snprintf(buf, cap, fmt, args, nargs), где args указывает на
массив 64-битных значений. Спецификатор преобразования по-прежнему решает, как
прочитано каждое, точно как в libc. Это единственная сигнатура в библиотеке,
которая отклоняется, и это не чистая потеря: libc не может посчитать свои
аргументы, поэтому при слишком малом их числе читается что угодно из стека; здесь же
при слишком малом числе преобразование останавливается, а произведённое до сих пор
завершается правильно.
Нет указателей на функцию
Поэтому четвёртый аргумент qsort — перечисление, говорящее, по чему
сравнивать, а не функция сравнения. Это настоящее различие, и оно не понижение в одну
сторону: оно переносит «по чему сравнивает эта сортировка» из адреса времени
выполнения в константу в артефакте, так что на это можно ответить,
не читая исходник. Цена заявлена вместе с этим: порядок, который не
один из встроенных видов, выразить нельзя — цикл пишете сами.
Нет изменяемой глобальной на уровне модуля
Поэтому strtok становится strtok_r, с курсором у
вызывающего, — что POSIX уже предлагает, так что это не понижение.
rand, хранящий скрытое зерно, тоже не может существовать; форма, которую
использует этот язык, берёт состояние у вызывающего, и она нарочно не названа
rand. И нет errno: strtol насыщается до
граничного значения и возвращается нормально, так что вызывающий вместо этого
сравнивает с границей.
Чем оно не является
Не замена libc
LibL — это библиотека исходного кода, а не архив, против которого
можно слинковать объектный файл C. Код C не может вызвать её; для этого нужен путь
«Loment в объектный файл», которого не существует. То, что она заменяет, — уже, и это
стоит сказать точно: программу на Loment, которой приходилось бы писать
strcmp вручную.
Не вся libc
realloc и free отсутствуют намеренно: realloc
нужна старая длина, а старая длина живёт в заголовке кучи, приватном для компилятора;
имя free уже занято встроенным, так что функция библиотеки с этим именем не
разрешилась бы в точке вызова. malloc и calloc есть с 0.1.4.6 —
это написания поверх встроенной кучи без собственного заголовка длины, что и позволяет
встроенному free принимать то, что они возвращают. stdin,
stdout и stderr были бы изменяемыми глобалами уровня модуля, а
языка их нет. Из семейства процессов и окружения, кроме exit и
abort, нет ничего. А %f/%e/%g — это
не «не сделано», а «пока нельзя»: вышестоящий форматтер — программная плавающая точка, и
его обратное направление требует широкого деления, которого этот слой намеренно не имеет.
Не завершено и не притворяется завершённым
Написание её выявило дефекты в окружающей системе — среди них помощник, чья проверка перекрытия сравнивала смещения там, где libc сравнивает адреса, форматтер, испускающий IR, который отвергает системный ассемблер, и проверка зависимостей, которая винит не тот модуль. Они заведены как issues, а не исправлены мимоходом, и собственный критерий библиотеки нарочно узок: он заявляет, что эти модули — допустимые единицы, и не заявляет, что таковы их зависимости.