bytes: диапазоны, курсоры и ULEB128.
Описывает чтение/запись байтов длиной view и сохранение состояния в случае сбоя.
Wave Foundation
Чем он отличается от строки?
Байтовые данные могут содержать ноль, поэтому передайте указатель вместе с длиной. Bytes и BytesMut являются представлениями, не являющимися владельцами, и действительны только до тех пор, пока базовое хранилище остается действительным. BytesMut требует записываемого хранилища.
Создать cursor
std::bytes::cursor
bytes_reader(data: ptr<u8>, len: i64) -> ByteReader
bytes_writer(data: ptr<u8>, len: i64) -> ByteWriter
bytes_reader_read_be_u16(reader: ptr<ByteReader>, out_value: ptr<u16>) -> i32
bytes_writer_write_be_u16(writer: ptr<ByteWriter>, value: u16) -> i32
Импортируйте ByteReader и ByteWriter из std::bytes::types. Их поле позиции определяет местоположение следующей операции. Их длина равна общему количеству доступных байт. Создание любого курсора не копирует и не выделяет базовую память.
be — это big-endian, le — это little-endian. Если тип файла big-endian, используйте функцию be независимо от порядка байтов хоста CPU. Существуют 16, 32 и 64-битные signed/unsigned функции чтения/записи и однобайтовые функции.
Ошибки и сохранение состояния
BYTES_OK из std::bytes::errors равно 0. INVALID указывает на недопустимый диапазон, EOF недостаточная входная мощность, NO_SPACE недостаточная выходная мощность и OVERFLOW — значение, выходящее за пределы представимого диапазона. Проверенные операции с курсором перемещают позицию только после успешного завершения всей операции. Неудачное чтение также сохраняет выходное значение.
ULEB128
std::bytes::leb128
bytes_reader_read_uleb128_u64(reader: ptr<ByteReader>, output_value: ptr<u64>) -> i32
bytes_writer_write_uleb128_u64(writer: ptr<ByteWriter>, value: u64) -> i32
ULEB128 хранит 64-битное целое число без знака в переменном количестве байтов, используя не более 10 байтов. Если места недостаточно, средство записи сохраняет как свою позицию, так и байты назначения. Считыватель отличает неполный ввод от значения, превышающего u64. Допускаются терминированные, неминимальные кодировки.
Чтобы создать фактическое сообщение и увидеть кратковременную ошибку ввода, перейдите к Практика двоичных сообщений. Не пытайтесь вывести строку байтов, содержащую нули, как str.
Чтение одних и тех же байтов в разном порядке
Порядок байтов — это правило хранения чисел. Если вы прочитаете два байта 1 и 2 как big-endian, это будет 1×256+2, а если вы прочтете их как little-endian, это будет 2×256+1. Выбирайте на основе правил сети или типа файла.
import("std::bytes::types")::{Bytes, bytes_view};
import("std::bytes::read")::{bytes_read_be_u16, bytes_read_le_u16};
fun main() -> i32 {
var data: array<u8, 2> = [1, 2];
var view: Bytes = bytes_view(&data[0], 2);
var big: u16 = 0;
var little: u16 = 0;
if (bytes_read_be_u16(view, 0, &big) < 0) {
return 1;
}
if (bytes_read_le_u16(view, 0, &little) < 0) {
return 2;
}
println("be={} le={}", big, little);
return 0;
}
Результат выполнения:
be=258 le=513
view заимствует массив. Поскольку отдельного выделения или копирования нет, массив можно использовать только до тех пор, пока он действителен. offset измеряется в байтах, и для чтения 16 бит требуется 2 байта из этой позиции.
Выберите offset API и cursor API.
Функция read/write, принимающая аргумент offset, удобна для форматов, которые напрямую считывают указанную позицию поля. Для потоков, где следующая позиция зависит от длины предыдущего поля, удобно использовать cursor с position.
При смешивании этих двух материалов поясните, какой из них является стандартным: cursor.position или отдельный offset. Избегайте ошибки, связанной с добавлением одного и того же местоположения дважды или переходом к следующему местоположению без успешного чтения.
Проверьте статус по короткому входу
import("std::bytes::types")::{ByteReader};
import("std::bytes::cursor")::{bytes_reader, bytes_reader_read_be_u16};
import("std::bytes::errors")::{BYTES_ERROR_EOF};
fun main() {
var data: array<u8, 1> = [1];
var reader: ByteReader = bytes_reader(&data[0], 1);
var value: u16 = 99;
var status: i32 = bytes_reader_read_be_u16(&reader, &value);
if (status == BYTES_ERROR_EOF) {
println("position={} value={}", reader.position, value);
}
}
Результат выполнения:
position=0 value=99
Два байта не нужны, поэтому выходное значение и местоположение сохраняются. Это свойство полезно в проектах, которые повторяют чтение того же поля после получения дополнительных входных данных. Однако если пространство хранения, указанное view, было перераспределено, адрес также необходимо обновить.
Граница ULEB128
0~127 использует один байт, начиная с 128 использует больше байтов. Старший бит каждого байта указывает, следуют ли за ним данные. Значение за пределами u64 или слишком длинный непрерывный ввод — это OVERFLOW, что отличается от EOF, у которого просто меньше входных данных.
Непосредственно проверьте, сохраняет ли состояние «Недостаточная емкость» writer свое состояние.
import("std::bytes::types")::{ByteWriter};
import("std::bytes::cursor")::{bytes_writer};
import("std::bytes::leb128")::{bytes_writer_write_uleb128_u64};
import("std::bytes::errors")::{BYTES_ERROR_NO_SPACE};
fun main() {
var data: array<u8, 1> = [85];
var writer: ByteWriter = bytes_writer(&data[0], 1);
var status: i32 = bytes_writer_write_uleb128_u64(&writer, 128);
if (status == BYTES_ERROR_NO_SPACE) {
println("position={} byte={}", writer.position, data[0]);
}
}
Результат выполнения:
position=0 byte=85
Для 128 требуется два байта, но только один пробел. После сбоя остается и первый байт 85. Совместная проверка типов ошибок и сохранения состояния описывает границу лучше, чем простая проверка успеха roundtrip.
Последовательность создания парсера сообщений
- Прочтите фиксированный заголовок и проверьте тип и версию.
- Прочтите длину и сравните ее с оставшимся входным диапазоном.
- Передавайте только необходимые данные в view или в отдельный буфер.
- Если формат требует всего сообщения, дополнительные байты также проверяются.
- Различает EOF и ошибки недопустимого формата и передает их вызывающей стороне.
Вы можете создать одну программу, соединив поля в Практика двоичных сообщений.