Регионы и хранилище — 0.0.19
Логическое владение значением и владение его физическим хранилищем различны.
memory.Region владеет ограниченной областью памяти. memory.Buffer<T> —
move-only дескриптор размещённой последовательности копируемых значений.
Его зависимость от конкретного региона проверяется статически.
Lifetime не является runtime-объектом. Происхождение хранилища не зависит
от имени привязки: move, generic-функции и результаты сохраняют его.
Два вызова создания региона дают разные экземпляры, даже с одинаковой вместимостью.
Минимальный пример
fn main() -> u32 : alloc {
match memory.create(4096) {
Err(error) => { return 1 }
Ok(region) => {
allocation = memory.alloc<u32>(region, 64, 7)
match move allocation {
Err(error) => {}
Ok(initial) => {
buffer = memory.set(move initial, 3, 42)
{
data = memory.view(buffer)
value = data[3]
}
released = memory.release(move buffer)
}
}
closed = memory.close(move region)
return 0
}
}
}
Отдельный блок завершает лексическое readonly-заимствование. Он не освобождает
память автоматически. Полный пример: examples/region_buffer.liza.
Оператор ? и неявное закрытие ресурсов пока не поддерживаются.
API
| Вызов | Результат | Эффект / владение |
|---|---|---|
memory.create(capacity: u64) |
Result<memory.Region, MemoryError> |
alloc; новый регион |
memory.alloc<T>(region, count: u32, initial: T) |
Result<memory.Buffer<T>, MemoryError> |
alloc; временный доступ к региону, новая зависимость |
memory.set(move buffer, index: u32, value: T) |
memory.Buffer<T> |
Возвращает владение; сохраняет регион и размещение |
memory.view(buffer) |
View<T> |
Лексический readonly-доступ без копирования содержимого |
memory.len(buffer) |
u32 |
Чтение длины без передачи владения |
memory.release(move buffer) |
u32, всегда 0 |
Завершает владение буфером и зависимость |
memory.close(move region) |
u32, всегда 0 |
alloc; освобождает всё хранилище региона |
memory.alloc требует явный аргумент типа. Инициализатор вычисляется один раз
по обычным правилам вызова; затем каждый элемент получает независимую
логическую копию. Вычисление инициализатора происходит также при нулевой длине
и при последующем отказе аллокации.
Вместимость задаётся в байтах полезного хранилища. Внутренние служебные данные региона находятся вне этой вместимости. Выравнивание между выделениями расходует вместимость; размер и выравнивание элементов определяются целевой реализацией. Нулевой размер выделения не расходует байты или padding.
MemoryError имеет варианты OutOfMemory, Exhausted, SizeOverflow.
Отказ создания, нехватка вместимости и непредставимый размер — ожидаемые ошибки,
возвращаемые через Result. Проверяются умножение размера на количество,
выравнивание и оставшаяся вместимость. При отказе состояние региона не меняется.
release не возвращает вместимость. Регион не растёт и не перемещает ранее
выделенные данные. Можно выделять новые буферы при существующих буферах и views.
Весь ресурс освобождается только через close.
Эффекты и порядок вычисления
Создание, выделение и закрытие региона требуют эффекта alloc.
Он независим от io; совместная запись — : io, alloc.
pure не включает ни один из них. Эффекты сохраняются через обычные
и generic-вызовы. Чтение, обновление и release не выполняют аллокаций.
memory.set соблюдает обычный порядок вызова: аргументы вычисляются слева
направо, затем проверяется индекс. Поэтому в
memory.set(move buffer, index, expensive()) эффекты expensive() происходят
до возможного bounds trap. Для пропуска дорогого вычисления индекс нужно
проверить явно через memory.len.
Это отличается от синтаксиса обновления фиксированного массива a[i] = value,
где границы проверяются до правой части. Исключения в правила вызова не вводятся.
Зависимости, Result и функции
Неразобранный Result<memory.Buffer<T>, MemoryError> потенциально владеет
буфером и удерживает зависимость. match move allocation передаёт её в Ok;
в Err зависимости этого выделения нет. После объединения ветвей закрытие
допустимо, если на всех продолжающихся путях зависимость завершена.
Запрещены:
copy buffer, неявная передача или потеря ресурса на нормальном выходе;move regionиcloseпри живом буфере или потенциально владеющем результате;set,releaseи перемещение буфера при действующем readonly view;- повторное закрытие, использование перемещённого дескриптора и escape view.
Проверка учитывает также временные аргументы вызова. Передача буфера первым аргументом не позволяет переместить его регион вторым аргументом того же вызова.
Обычные функции могут потреблять, возвращать и обновлять буферы; сводка результата
сохраняет происхождение параметра, в том числе внутри вариантов. Функции-фабрики
могут возвращать новый регион. memory.alloc заимствует регион только на время
вызова и изменяет состояние его вместимости. Заимствуемый аргумент должен
быть именем доступной привязки, без move; временное значение не принимается.
Заимствование сохраняется до завершения вызова: последующий аргумент
не может закрыть или переместить уже заимствованный регион.
Пользовательские заимствуемые параметры региона пока не вводятся.
Анализ консервативен: неоднозначное происхождение после слияния CFG и рекурсивные сводки с регионами/буферами отвергаются. На обратном ребре цикла происхождение внешних владельцев должно сохраняться. Локальные регионы можно создавать на каждой итерации, если все обязательства завершены до следующей итерации. Одно статическое место создания не объявляет runtime-экземпляры одинаковыми.
Ошибки доступа и завершение
Чтение или обновление за границами вызывает обязательный trap. Нулевой буфер допустим; любой доступ к элементу вызывает trap. Оптимизация не может превращать обязательный trap в успешное выполнение.
На нормальном выходе компилятор требует завершить или передать владение всеми
ресурсами. При trap язык не обещает cleanup, вызов деструкторов или освобождение
регионов. Это тот же принцип, что у File.
Объём первой реализации
Поддерживаются копируемые элементы, включая структуры и фиксированные массивы.
Элемент с расчётным размером больше 2^63−1 байт отвергается при типизации,
до формирования машинных констант размера. Динамическое умножение размера
представимого элемента на количество проверяется при выделении.
Дескрипторы регионов и буферов могут передаваться через варианты, но не храниться
в пользовательских структурах или массивах ресурсов. Нет индивидуального
возврата памяти, reset, mutable views, автоматического роста, Vec, GC,
пользовательских аллокаторов и неявного клонирования размещённых буферов.
Проверки O0/O2 охватывают обязанности по закрытию, условные зависимости, циклы и вызовы, инъекцию отказа выделения, выравнивание и атомарность отказа. В выбранном тракте чтения/обновления нет аллокаций или полного копирования буфера. Стоимость создания и заполнения остаётся явной: заполнение требует работы пропорционально количеству и представлению элементов.
Чтение view[index] возвращает независимое значение элемента. Для крупного T
это может требовать копирования одного элемента, но не всего буфера. Значение
остаётся доступным после обновления буфера или закрытия региона. Само создание
view содержимое не копирует; заимствуемое чтение крупных элементов без снимка
потребует отдельного будущего контракта.