buffer: расширяемое хранилище байтов
Buffer Описывает правила инициализации, добавления, запроса, емкости и освобождения.
Wave Foundation
Значение Buffer
Buffer в std::buffer::types имеет data: ptr<u8>, len: i64 и cap: i64. len — количество инициализированных и используемых байтов, а cap — общее количество выделенных байтов. Всегда сохраняйте 0 <= len <= cap. Строка NUL не гарантирует автоматического завершения.
Базовый API
| модуль | декларация | смысл |
|---|---|---|
std::buffer::alloc |
buffer_init(out_buf: ptr<Buffer>, capacity: i64) -> i64 |
Инициализируйте новый репозиторий. Не вызывать в уже принадлежащие буферы |
| тот же модуль | buffer_free(buf: ptr<Buffer>) -> i64 |
Освободить. Пусто в случае успеха |
| тот же модуль | buffer_reserve(buf: ptr<Buffer>, required_cap: i64) -> i64 |
Обеспечьте минимальную полную мощность. len Поддерживается |
| тот же модуль | buffer_clear(buf: ptr<Buffer>) -> i64 |
Поддерживать емкость и len=0 |
| тот же модуль | buffer_resize(buf: ptr<Buffer>, new_len: i64, value: u8) -> i64 |
Измените длину, заполните новые байты с помощью value |
std::buffer::write |
buffer_push(buf: ptr<Buffer>, value: u8) -> i64 |
добавить один байт |
| тот же модуль | buffer_append(buf: ptr<Buffer>, src: ptr<u8>, size: i64) -> i64 |
sizeСкопировать и добавить байт |
| тот же модуль | buffer_append_str(buf: ptr<Buffer>, s: str) -> i64 |
Добавьте строковые байты, исключая NUL. |
std::buffer::read |
buffer_get(buf: ptr<Buffer>, index: i64, out_value: ptr<u8>) -> i64 |
Чтение одного байта в диапазоне |
Возврат статуса API возвращает BUFFER_OK(0) в случае успеха. Отличайте ошибки INVALID, BOUNDS, OVERFLOW и ALLOC от std::buffer::error. Число не интерпретируется как OS errno. buffer_new представляет ошибку выделения в виде пустого Buffer, поэтому, когда вам нужно различать ошибки, используйте buffer_init.
Запуск примера
import("std::buffer::alloc")::{
Buffer, buffer_init, buffer_free
};
import("std::buffer::write")::{
buffer_append_str, buffer_push
};
import("std::buffer::read")::{
buffer_get
};
fun main() -> i32 {
var data: Buffer;
if (buffer_init(&data, 0) < 0) {
return 1;
}
if (buffer_append_str(&data, "Hi") < 0 || buffer_push(&data, 33) < 0) {
buffer_free(&data);
return 2;
}
var value: u8 = 0;
if (buffer_get(&data, 2, &value) < 0) {
buffer_free(&data);
return 3;
}
println("{} {}", data.len, value);
if (buffer_free(&data) < 0) {
return 4;
}
return 0;
}
Результат выполнения:
3 33
Сохраните его как main.wave и запустите как wavec run main.wave. Начальная емкость, равная 0, — это не сбой, а действительный пустой буфер. Освобождает место при дальнейшей обработке.
Продолжительность жизни и неудача
Увеличение буфера может изменить данные. Не используйте ранее заимствованный адрес после операции, которая может привести к перераспределению. Копирование структуры Buffer не дублирует ее выделение, поэтому назначьте этому выделению одного владельца.
buffer_get не меняет выходные аргументы в случае сбоя. С другой стороны, удобная функция buffer_at также представляет ошибки как 0, поэтому используйте buffer_get, чтобы различать фактические 0 байтов и ошибки. Избегайте создания недействительного len/cap путем непосредственного изменения общедоступных полей.
Память API · Потренируйтесь читать файл как Buffer.
Учитывайте длину и мощность отдельно.
reserve освобождает место для хранения, но не увеличивает len. resize изменяет фактическую используемую длину и инициализирует расширенную часть указанным байтом. clear устанавливает только используемую длину в 0, что позволяет повторно использовать выделение.
Сохраните следующую программу как main.wave и запустите ее. Он не зависит от точного коэффициента роста мощности; это просто гарантирует, что у вас есть необходимое пространство.
import("std::buffer::alloc")::{
Buffer,
buffer_init,
buffer_reserve,
buffer_resize,
buffer_clear,
buffer_free
};
fun main() -> i32 {
var data: Buffer;
if (buffer_init(&data, 0) < 0) {
return 1;
}
if (buffer_reserve(&data, 20) < 0) {
buffer_free(&data);
return 2;
}
println("reserved length={}", data.len);
if (buffer_resize(&data, 3, 7) < 0) {
buffer_free(&data);
return 3;
}
println("resized length={} first={}", data.len, deref data.data[0]);
if (buffer_clear(&data) < 0) {
buffer_free(&data);
return 4;
}
println("cleared length={}", data.len);
if (buffer_free(&data) < 0) {
return 5;
}
return 0;
}
Результат выполнения:
reserved length=0
resized length=3 first=7
cleared length=0
При увеличении до resize мы передали value=7, поэтому все три новых байта, которые мы видим, равны 7. Пространство, защищенное только reserve, не считывается как инициализированные данные. cap останется после clear и может быть добавлен снова к тому же Buffer.
Различайте нулевые байты и ошибки поиска.
buffer_get возвращает статус и записывает фактические байты в качестве выходных аргументов. Даже если данные равны 0, это нормальный успех.
import("std::buffer::alloc")::{Buffer, buffer_init, buffer_free};
import("std::buffer::write")::{buffer_push};
import("std::buffer::read")::{buffer_get};
import("std::buffer::error")::{BUFFER_ERR_BOUNDS};
fun main() -> i32 {
var data: Buffer;
if (buffer_init(&data, 0) < 0) {
return 1;
}
if (buffer_push(&data, 0) < 0) {
buffer_free(&data);
return 2;
}
var value: u8 = 99;
if (buffer_get(&data, 0, &value) < 0) {
buffer_free(&data);
return 3;
}
println("stored={}", value);
value = 99;
if (buffer_get(&data, 1, &value) == BUFFER_ERR_BOUNDS) {
println("outside, preserved={}", value);
}
if (buffer_free(&data) < 0) {
return 4;
}
return 0;
}
Результат выполнения:
stored=0
outside, preserved=99
Первое попадание означает успех, равный 0, второе попадание — неудачный выход за пределы поля. Даже если после сбоя остается value=99, это не означает, что это значение, прочитанное из буфера. Обязательно вместе проверьте статус.
Практическое решение: накопление байтов
Чтобы сложить числа от 0 до 9, повторите buffer_push и проверьте каждый результат. Сохраните сумму в i64 и читайте только диапазон 0 <= index < data.len. После обработки буфера мы вызываем buffer_free как для успешного, так и для неудачного пути.
import("std::buffer::alloc")::{Buffer, buffer_init, buffer_free};
import("std::buffer::write")::{buffer_push};
fun main() -> i32 {
var data: Buffer;
if (buffer_init(&data, 0) < 0) {
return 1;
}
for (var value: i32 = 0; value < 10; value += 1) {
if (buffer_push(&data, value as u8) < 0) {
buffer_free(&data);
return 2;
}
}
var total: i64 = 0;
for (var index: i64 = 0; index < data.len; index += 1) {
total += deref data.data[index] as i64;
}
println("sum={}", total);
if (buffer_free(&data) < 0) {
return 3;
}
return 0;
}
Результат выполнения:
sum=45