T1: владеющий UTF-8 на Liza
examples/text/string.liza — обычный импортируемый модуль, использующий
ресурсные структуры. Компилятор не знает имён
String, Appended или Info; специальных строковых операций IR,
нового runtime и автоматического выделения памяти нет.
Пример печатает 9 UTF-8 bytes после добавления Liza 🚀.
Модуль пока служит эталонной реализацией T1 в дереве примеров, а не новым
предустановленным пространством stdlib. Чтобы использовать его в собственном
проекте, скопируйте text/string.liza внутрь корня импорта.
Владение и память
Приватные поля String — memory.Buffer<u8> и логическая байтовая длина.
Строка владеет буфером; хранилищем владеет отдельный регион. Пока строка или
содержащий её результат живы, закрытие или перемещение региона запрещено.
Память выделяет вызывающий код через существующий memory.alloc<u8>.
from_buffer(move buffer) создаёт пустую строку с вместимостью, равной длине
буфера. Прежние байты буфера не интерпретируются как текст и не становятся
доступны. Это не неявное преобразование Bytes в UTF-8.
Инварианты модуля:
0 <= length <= capacity;- только
[0,length)является содержимым; - этот префикс — корректный UTF-8 после каждой успешной операции;
- ошибка append сохраняет содержимое, длину, вместимость и владельца.
API
import text.string
// storage уже выделен в регионе
initial=text.string.from_buffer(move storage)
(next,status)=text.string.append(move initial,data)
(current,length,capacity)=text.string.inspect(move next)
empty=text.string.clear(move current)
released=text.string.release(move empty)
| Функция | Контракт |
|---|---|
from_buffer(move buffer) |
Пустая строка, принимает владение существующим буфером |
append(move text, data:View<u8>) |
Возвращает String и Result<Done,Error> |
clear(move text) |
Возвращает тот же владелец с нулевой логической длиной |
inspect(move text) |
Возвращает String, длину в байтах и вместимость |
length(text) / capacity(text) |
Непотребляющие длина и вместимость (T2-A) |
byte_at(text, index) |
Непотребляющий Option<u8>; вне длины — None (T2-A) |
release(move text) |
Завершает владение и зависимость от региона |
Имена в примере — новые привязки. В цикле используйте (next,status)=...,
затем current=move next. Потребляющий inspect сохранён для совместимости.
T2-A добавляет непотребляющие length/capacity/byte_at.
Прежний byte_at(move current,index) с результатом Byte заменён на
byte_at(current,index)->Option<u8>; владельца возвращать и разбирать больше не нужно.
append сначала проверяет весь вход через utf8.validate, затем вместимость,
и только после успеха обеих проверок копирует байты. InvalidUtf8(Utf8Error)
имеет приоритет над Full; смещение UTF-8 ошибки относится к добавляемому view.
Каждый append принимает законченную UTF-8 последовательность. Нельзя добавлять
половины многобайтового символа отдельными вызовами; для входных чанков нужен
декодер или отдельный накопитель. Пустой вход допустим, как и U+0000.
byte_at — байтовый доступ, включая continuation bytes, а не O(1) индекс
Unicode-символа. Он не позволяет изменять байты строки. T1 не возвращает
TextView, readonly view или срез: это следующий этап с отдельным контрактом
времени жизни и границ кодовых точек.
Стоимость
Функции модуля не аллоцируют. Операции над содержимым pure; добавленный T2-B
write(borrow text:String)->Result<u64,IoFailure>:io выводит только
[0,length) через локальный subview и возвращает результат console. from_buffer, inspect, byte_at,
clear, release имеют постоянную работу относительно размера содержимого.
Они передают дескрипторы и скаляры, а не копируют всю вместимость.
append(n) проверяет n байтов и при успехе записывает ровно n байтов; уже
существующий префикс не копируется. Рост вместимости и reserve не реализованы.
Вызывающий memory.alloc инициализирует весь буфер — это отдельная явная
O(capacity) стоимость. clear не обнуляет старые байты; их нельзя наблюдать через
API строки. release не возвращает вместимость региону: её освобождает close.
Это не обещание безопасного стирания секретов.
Проверка и следующие этапы
Нативные тесты O0/O2 получают байты во время исполнения, сверяют UTF-8 с Python, проверяют успешное содержимое, сохранение старого префикса при отказе, нулевую/точную/недостаточную вместимость, clear и предельные индексы u32. Отрицательные тесты запрещают конструирование некорректной строки извне, извлечение приватного буфера, копирование и потерю владельца.
Это тестируемый контракт, не доказательство для всех входов. Формальные результаты
V0–V4 и Relay автоматически на String или новое расширение Core не переносятся.
Реализованы T2-A: readonly-доступ и
T2-B: локальные subviews и write. Поверх String реализован первый builder и
потоковый format. Далее — самостоятельный JSON tokenizer;
json.* остаётся минимальным валидатором.