Перейти к содержанию

T1: владеющий UTF-8 на Liza

examples/text/string.liza — обычный импортируемый модуль, использующий ресурсные структуры. Компилятор не знает имён String, Appended или Info; специальных строковых операций IR, нового runtime и автоматического выделения памяти нет.

python3 -m liza run examples/owned_string.liza

Пример печатает 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.* остаётся минимальным валидатором.