Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
file_io.hpp
Go to the documentation of this file.
1#pragma once
6#include <cstdint>
7#include <cstring>
8#include <fcntl.h>
9#include <filesystem>
10#include <fstream>
11#include <ios>
12#include <sstream>
13#include <string_view>
14#include <sys/stat.h>
15#include <unistd.h>
16#include <vector>
17
18namespace bb {
19inline size_t get_file_size(std::string const& filename)
20{
21 // Open the file in binary mode and move to the end.
22 std::ifstream file(filename, std::ios::binary | std::ios::ate);
23 if (!file) {
24 return 0;
25 }
26
27 file.seekg(0, std::ios::end);
28 return (size_t)file.tellg();
29}
30
31inline std::vector<uint8_t> read_file(const std::string& filename, size_t bytes = 0)
32{
33 // Used for stdin and non-seekable fds (pipes, process substitution) where we don't know the
34 // size in advance. Reads in 64 KiB chunks to avoid the O(n²) reallocation pattern that arises
35 // from the istreambuf_iterator range constructor.
36 constexpr size_t CHUNK = 65536;
37 auto read_chunked = [](int fd, const std::string& name) {
38 std::vector<uint8_t> result;
39 size_t total = 0;
40 ssize_t n = 0;
41 while (true) {
42 // Standard libraries will usually do geometric capacity growth here so that copying is amortized.
43 result.resize(total + CHUNK);
44 n = ::read(fd, result.data() + total, CHUNK);
45 if (n <= 0) {
46 break;
47 }
48 total += static_cast<size_t>(n);
49 }
50 result.resize(total);
51 if (n < 0) {
52 THROW std::runtime_error("Failed to read from " + name + ": " + strerror(errno));
53 }
54 return result;
55 };
56
57 // "-" is the conventional sentinel for stdin.
58 if (filename == "-") {
59 return read_chunked(STDIN_FILENO, "stdin");
60 }
61
62 // std::filesystem::file_size is a single stat() call — no seek-to-end / rewind needed.
63 // The error_code overload returns -1 and sets ec instead of throwing for non-regular
64 // files (pipes, devices), which we treat as "unknown size" and handle with chunked I/O.
65 std::error_code ec;
66 auto raw_size = std::filesystem::file_size(filename, ec);
67 std::optional<size_t> file_size = ec ? std::nullopt : std::optional<size_t>(static_cast<size_t>(raw_size));
68
69 int fd = open(filename.c_str(), O_RDONLY);
70 if (fd == -1) {
71 THROW std::runtime_error("Unable to open file: " + filename + " (" + strerror(errno) + ")");
72 }
73
74 std::vector<uint8_t> fileData;
75 if (!file_size.has_value()) {
76 // Size unknown (pipe, device, etc.): read without pre-allocation.
77 fileData = read_chunked(fd, filename);
78 } else {
79 // Pre-allocate exactly what we need: either the caller's limit or the whole file.
80 // Using POSIX read() directly avoids the extra buffering layer of std::ifstream.
81 size_t to_read = bytes == 0 ? *file_size : bytes;
82 fileData.resize(to_read);
83 size_t total = 0;
84 while (total < to_read) {
85 ssize_t got = ::read(fd, fileData.data() + total, static_cast<unsigned int>(to_read - total));
86 if (got < 0) {
87 close(fd);
88 THROW std::runtime_error("Failed to read file: " + filename + " (" + strerror(errno) + ")");
89 }
90 if (got == 0) {
91 break; // Unexpected EOF (file shrunk between stat and read).
92 }
93 total += static_cast<size_t>(got);
94 }
95 fileData.resize(total); // Shrink if the file was shorter than expected.
96 }
97 close(fd);
98 return fileData;
99}
100
101inline void write_file(const std::string& filename, std::span<const uint8_t> data)
102{
103 // For regular files, truncate and create if missing (O_TRUNC | O_CREAT).
104 // For FIFOs/pipes the file already exists and O_CREAT/O_TRUNC are no-ops, so
105 // the same flags work uniformly for both cases — no need to stat() first.
106 int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
107 if (fd == -1) {
108 THROW std::runtime_error("Failed to open file for writing: " + filename + " (" + strerror(errno) + ")");
109 }
110
111 // Loop to handle short writes (required by POSIX, common on pipes and sockets).
112 size_t total_written = 0;
113 while (total_written < data.size()) {
114 ssize_t written =
115 ::write(fd, data.data() + total_written, static_cast<unsigned int>(data.size() - total_written));
116 if (written < 0) {
117 close(fd);
118 THROW std::runtime_error("Failed to write to file: " + filename + " (" + strerror(errno) + ")");
119 }
120 total_written += static_cast<size_t>(written);
121 }
122 close(fd);
123}
124
125template <typename Fr> inline std::string field_elements_to_json(const std::vector<Fr>& fields)
126{
127 std::stringstream ss;
128 ss << "[";
129 for (size_t i = 0; i < fields.size(); ++i) {
130 ss << '"' << fields[i] << '"';
131 if (i != fields.size() - 1) {
132 ss << ",";
133 }
134 }
135 ss << "]";
136 return ss.str();
137}
138
146inline std::vector<uint8_t> read_vk_file(const std::filesystem::path& vk_path)
147{
148 try {
149 return read_file(vk_path.string());
150 } catch (const std::runtime_error&) {
151 THROW std::runtime_error("Unable to open file: " + vk_path.string() +
152 "\nGenerate a vk during proving by running `bb prove` with an additional `--write_vk` "
153 "flag, or run `bb write_vk` to generate a standalone vk."
154 "\nIf you already have a vk file, specify its path with `--vk_path <path>`.");
155 }
156}
157
158template <typename T>
159inline std::vector<T> many_from_buffer_exact(const std::vector<uint8_t>& buffer, std::string_view object_name)
160{
161 if (buffer.size() % sizeof(T) != 0) {
162 THROW std::runtime_error(std::string(object_name) + " size must be a multiple of " + std::to_string(sizeof(T)) +
163 " bytes, got " + std::to_string(buffer.size()));
164 }
165 return ::many_from_buffer<T>(buffer);
166}
167
168// On Windows, std::filesystem::path uses wide strings (wchar_t) and doesn't implicitly convert
169// to std::string. On Linux/macOS (libstdc++), the conversion is implicit so these aren't needed.
170#ifdef _WIN32
171inline size_t get_file_size(const std::filesystem::path& filename)
172{
173 return get_file_size(filename.string());
174}
175
176inline std::vector<uint8_t> read_file(const std::filesystem::path& filename, size_t bytes = 0)
177{
178 return read_file(filename.string(), bytes);
179}
180
181inline void write_file(const std::filesystem::path& filename, std::span<const uint8_t> data)
182{
183 write_file(filename.string(), data);
184}
185#endif
186
187} // namespace bb
const std::vector< MemoryValue > data
std::unique_ptr< uint8_t[]> buffer
Definition engine.cpp:60
Entry point for Barretenberg command-line interface.
Definition api.hpp:5
void read(B &it, field2< base_field, Params > &value)
std::vector< T > many_from_buffer_exact(const std::vector< uint8_t > &buffer, std::string_view object_name)
Definition file_io.hpp:159
std::vector< uint8_t > read_vk_file(const std::filesystem::path &vk_path)
Read a verification key file with an actionable error message if not found.
Definition file_io.hpp:146
void write(B &buf, field2< base_field, Params > const &value)
std::vector< uint8_t > read_file(const std::string &filename, size_t bytes=0)
Definition file_io.hpp:31
void write_file(const std::string &filename, std::span< const uint8_t > data)
Definition file_io.hpp:101
std::string field_elements_to_json(const std::vector< Fr > &fields)
Definition file_io.hpp:125
size_t get_file_size(std::string const &filename)
Definition file_io.hpp:19
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
#define THROW