fs и io: передача файлов и байтов

Описывает время жизни файла, полное чтение, частичную передачу и состояния после сбоя.

Wave Foundation

Выберите файл API.

Удобная функция std::fs::file получает путь и выполняет необходимое открытие и закрытие. Функции, возвращающие дескриптор, должны быть закрыты вызывающей стороной.

декларация Результаты успеха и меры предосторожности
open_read(path: str) -> i64 Открыть дескриптор. Отрицательные числа являются ошибками.
create(path: str) -> i64 Создайте или удалите существующее содержимое файла. Возвращает дескриптор владельца
open_append(path: str) -> i64 Открыть или создать для добавления
size(path: str) -> i64 Количество байтов. Отрицательные числа являются ошибками.
read_into(path: str, dst: ptr<u8>, dst_cap: i64) -> i64 Количество байт во всем файле. Недостаток мощности – ошибка
read_to_end(path: str, dst_buffer: ptr<Buffer>) -> i64 Добавьте файл после существующего Buffer и верните дополнительную сумму.
write(path: str, src: ptr<u8>, len: i64) -> i64 Количество записанных байтов, заменяющих существующий контент
append(path: str, src: ptr<u8>, len: i64) -> i64 Количество байт, добавленных в конец
remove(path: str) -> i64 статус удаления. неудача отрицательная

false из exists(path) сам по себе не может отличить отсутствующие файлы от ошибок разрешений. Обязательно проверьте фактический результат открытия, так как состояние может меняться между проверкой существования и открытием.

Низкий уровень I/O

std::io::fd
io_read(fd: i64, buf: ptr<u8>, len: i64) -> i64
io_write(fd: i64, buf: ptr<u8>, len: i64) -> i64
io_read_exact(fd: i64, buf: ptr<u8>, len: i64) -> i64
io_write_all(fd: i64, buf: ptr<u8>, len: i64) -> i64
io_close(fd: i64) -> i64

Положительный результат io_read — это количество прочитанных байтов, а 0 в запросе положительной длины — это EOF. io_write может быть записано меньше, чем требуется. Если требуется полная передача, используйте функцию exact/all. Тем не менее, мы не предполагаем, что сбой возвращает внешнее состояние, поскольку некоторые передачи могли произойти до ошибки.

io_read_exact является ошибкой, если EOF встречается до требуемой длины. read_into возвращает IO_ERR_NO_SPACE, если буфер заполнен и некоторые байты, возможно, уже записаны. Функция чтения не добавляет автоматически NUL в конец строки.

Buffer и обработка ошибок

В случае сбоя read_to_end восстанавливает исходный len, но его емкость и адрес данных могли измениться. Вызывающий объект должен освободить Buffer после успеха или неудачи. API записи файлов не гарантируют атомарную замену файлов.

Из Практика чтения файлов вы можете запустить программу из import для выпуска. Учитывайте различия в путях/разрешениях в Linux/macOS/Windows/FreeBSD и ограничениях доступных каталогов в WASI. Он не интерпретирует значение дескриптора напрямую как необработанный дескриптор другого OS.

Чтение больших файлов в маленькие буферы

Операции, которые не требуют размещения всего файла в памяти, могут обрабатываться с помощью фиксированных буферов и итераций чтения. Следующая программа печатает содержимое input.txt и подсчитывает общее количество прочитанных байтов. Сохраните по одному Wave и LF во входном файле.

import("std::fs::file")::{open_read};
import("std::io::fd")::{io_read, io_write_all, io_close};
import("std::io::consts")::{IO_STDOUT_FD};

fun main() -> i32 {
    var descriptor: i64 = open_read("input.txt");

    if (descriptor < 0) {
        return 1;
    }

    var buffer: array<u8, 4>;
    var total: i64 = 0;

    while (true) {
        var count: i64 = io_read(descriptor, &buffer[0], 4);

        if (count < 0) {
            io_close(descriptor);
            return 2;
        }

        if (count == 0) {
            break;
        }

        if (io_write_all(IO_STDOUT_FD, &buffer[0], count) < 0) {
            io_close(descriptor);
            return 3;
        }

        total += count;
    }

    if (io_close(descriptor) < 0) {
        return 4;
    }

    println("bytes={}", total);
    return 0;
}

Результат выполнения:

Wave
bytes=5

Емкость буфера равна 4, но последнее чтение может составлять 1 байт. Мы всегда передаем на выход фактическое значение count. Если вы запишите весь массив, даже старые, непрочитанные байты могут быть выведены.

Программа открыла descriptor из input.txt, поэтому закройте ее. Стандартный вывод не является новым ресурсом в этой функции, поэтому он не закрывается произвольно в конце примера.

Полное чтение и недостаточная емкость

read_into получает фиксированное пространство для хранения всего файла. Если места недостаточно, он автоматически усекается и безуспешно возвращает NO_SPACE.

import("std::fs::file")::{read_into};
import("std::io::consts")::{IO_ERR_NO_SPACE};

fun main() {
    var data: array<u8, 2>;
    var status: i64 = read_into("input.txt", &data[0], 2);

    if (status == IO_ERR_NO_SPACE) {
        println("destination too small");
    }
}

Результат выполнения:

destination too small

Это не предполагает, что неудачное чтение вообще не изменило байт назначения. Не используйте его в качестве готового содержимого файла, подготовьте репозиторий большего размера или выберите метод streaming. Даже если вы сначала запрашиваете размер, окончательным решением будет фактический результат чтения, поскольку файл может меняться между запросом и чтением.

Разница между записью и добавлением файлов

write заменяет существующий контент, а append добавляется в конец. Количество байтов для хранения можно получить непосредственно из длины строки и передать. NUL в конце строки обычно не включается в содержимое текстового файла.

import("std::fs::file")::{write, append, size, remove};
import("std::string::len")::{len};

fun main() -> i32 {
    var first: str = "Wave";
    var second: str = " study";

    if (write("output.txt", first as ptr<u8>, len(first) as i64) < 0) {
        return 1;
    }

    if (append("output.txt", second as ptr<u8>, len(second) as i64) < 0) {
        return 2;
    }

    println("bytes={}", size("output.txt"));

    if (remove("output.txt") < 0) {
        return 3;
    }

    return 0;
}

Результат выполнения:

bytes=10

В этом примере создается, заменяется и, наконец, удаляется output.txt в рабочем каталоге. Запускайте из каталога практики без существующих файлов. Для фактического редактора или программы хранения могут потребоваться отдельные политики хранения, такие как временные файлы и замена.

API Таблица выбора

ситуация выбрать
Считать небольшой весь файл в фиксированный буфер read_into
Храните все содержимое, не зная размера read_to_end и Buffer
Обрабатывайте контент по порядку, а не сохраняйте его целиком. open_read + io_read повторить
Чтение записей фиксированной длины io_read_exact
Передать всю строку байтов io_write_all
Обработка уже открытых файлов fd функция вместо функции пути

После выбора функции проверьте, как изменятся буфер, расположение файлов и внешние данные в случае сбоя.