33#include <system_error>
36#include "iceberg/file_io.h"
37#include "iceberg/result.h"
38#include "iceberg/util/macros.h"
40namespace iceberg::test {
44inline Result<std::streamsize> ToStreamSize(
size_t size) {
45 if (size >
static_cast<size_t>(std::numeric_limits<std::streamsize>::max())) {
46 return InvalidArgument(
"Buffer size {} exceeds streamsize max", size);
48 return static_cast<std::streamsize
>(size);
51inline Result<int64_t> ToInt64FileSize(uintmax_t size, std::string_view location) {
52 if (size >
static_cast<uintmax_t
>(std::numeric_limits<int64_t>::max())) {
53 return Invalid(
"File size for {} exceeds int64_t max", location);
55 return static_cast<int64_t
>(size);
63 : location_(std::move(location)), file_(location_, std::ios::binary) {}
65 bool is_open()
const {
return file_.is_open(); }
68 auto position = file_.tellg();
70 return IOError(
"Failed to get read position for: {}", location_);
72 return static_cast<int64_t
>(position);
75 Status
Seek(int64_t position)
override {
77 return InvalidArgument(
"Cannot seek to negative position {}", position);
80 file_.seekg(position);
82 return IOError(
"Failed to seek to {} in file: {}", position, location_);
87 Result<int64_t>
Read(std::span<std::byte> out)
override {
91 ICEBERG_ASSIGN_OR_RAISE(
auto read_size, detail::ToStreamSize(out.size()));
92 file_.read(
reinterpret_cast<char*
>(out.data()), read_size);
93 auto bytes_read = file_.gcount();
95 if (file_.bad() || !file_.eof()) {
96 return IOError(
"Failed to read from file: {}", location_);
100 if (bytes_read < 0) {
101 return IOError(
"Failed to read from file: {}", location_);
103 return static_cast<int64_t
>(bytes_read);
106 Status
ReadFully(int64_t position, std::span<std::byte> out)
override {
108 return InvalidArgument(
"Cannot read from negative position {}", position);
113 ICEBERG_ASSIGN_OR_RAISE(
auto original_position,
Position());
114 auto seek_status =
Seek(position);
115 if (!seek_status.has_value()) {
116 static_cast<void>(
Seek(original_position));
120 Status read_status = {};
121 size_t total_read = 0;
122 while (total_read < out.size()) {
123 auto read_result =
Read(out.subspan(total_read));
124 if (!read_result.has_value()) {
125 read_status = std::unexpected<Error>(read_result.error());
128 if (read_result.value() == 0) {
130 IOError(
"Failed to read {} bytes from file: {}", out.size(), location_);
133 total_read +=
static_cast<size_t>(read_result.value());
136 auto restore_status =
Seek(original_position);
137 ICEBERG_RETURN_UNEXPECTED(read_status);
138 return restore_status;
142 if (!file_.is_open()) {
147 return IOError(
"Failed to close file: {}", location_);
153 std::string location_;
154 mutable std::ifstream file_;
160 : location_(std::move(location)),
161 file_(location_, std::ios::binary | std::ios::out | std::ios::trunc) {}
163 bool is_open()
const {
return file_.is_open(); }
166 auto position = file_.tellp();
168 return IOError(
"Failed to get write position for: {}", location_);
170 return static_cast<int64_t
>(position);
174 if (file_.is_open()) {
178 auto size = std::filesystem::file_size(location_, ec);
180 return IOError(
"Failed to get file size for {}: {}", location_, ec.message());
182 return detail::ToInt64FileSize(size, location_);
185 Status
Write(std::span<const std::byte> data)
override {
189 ICEBERG_ASSIGN_OR_RAISE(
auto write_size, detail::ToStreamSize(data.size()));
190 file_.write(
reinterpret_cast<const char*
>(data.data()), write_size);
192 return IOError(
"Failed to write to file: {}", location_);
200 return IOError(
"Failed to flush file: {}", location_);
206 if (!file_.is_open()) {
211 return IOError(
"Failed to close file: {}", location_);
217 std::string location_;
218 mutable std::ofstream file_;
224 std::optional<int64_t> file_size = std::nullopt)
225 : location_(std::move(
location)), file_size_(file_size) {}
227 std::string_view
location()
const override {
return location_; }
229 Result<int64_t>
Size()
const override {
230 if (file_size_.has_value()) {
234 auto size = std::filesystem::file_size(location_, ec);
236 return IOError(
"Failed to get file size for {}: {}", location_, ec.message());
238 return detail::ToInt64FileSize(size, location_);
241 Result<std::unique_ptr<SeekableInputStream>>
Open()
override {
242 auto stream = std::make_unique<StdSeekableInputStream>(location_);
243 if (!stream->is_open()) {
244 return IOError(
"Failed to open file for reading: {}", location_);
250 std::string location_;
251 std::optional<int64_t> file_size_;
258 std::string_view
location()
const override {
return location_; }
260 Result<std::unique_ptr<PositionOutputStream>>
Create()
override {
269 Result<std::unique_ptr<PositionOutputStream>>
Create(
bool overwrite) {
270 std::filesystem::path path(location_);
272 auto exists = std::filesystem::exists(path, ec);
274 return IOError(
"Failed to check file existence for {}: {}", location_,
277 if (!overwrite && exists) {
278 return AlreadyExists(
"File already exists: {}", location_);
280 if (path.has_parent_path()) {
281 std::filesystem::create_directories(path.parent_path(), ec);
283 return IOError(
"Failed to create parent directories for {}: {}", location_,
287 auto stream = std::make_unique<StdPositionOutputStream>(location_);
288 if (!stream->is_open()) {
289 return IOError(
"Failed to open file for writing: {}", location_);
294 std::string location_;
303 Result<std::unique_ptr<InputFile>>
NewInputFile(std::string file_location)
override {
304 return std::make_unique<StdInputFile>(std::move(file_location));
307 Result<std::unique_ptr<InputFile>>
NewInputFile(std::string file_location,
308 size_t length)
override {
309 if (length >
static_cast<size_t>(std::numeric_limits<int64_t>::max())) {
310 return InvalidArgument(
"File length {} exceeds int64_t max", length);
312 return std::make_unique<StdInputFile>(std::move(file_location),
313 static_cast<int64_t
>(length));
316 Result<std::unique_ptr<OutputFile>>
NewOutputFile(std::string file_location)
override {
317 return std::make_unique<StdOutputFile>(std::move(file_location));
320 Status
DeleteFile(
const std::string& file_location)
override {
322 if (!std::filesystem::remove(file_location, ec)) {
324 return IOError(
"Failed to delete file {}: {}", file_location, ec.message());
326 return IOError(
"File does not exist: {}", file_location);
Pluggable module for reading, writing, and deleting files.
Definition file_io.h:125
Handle for creating a writable file.
Definition file_io.h:104
Positioned byte stream for writing file contents.
Definition file_io.h:65
Simple local filesystem FileIO implementation for testing.
Definition std_io.h:301
Result< std::unique_ptr< InputFile > > NewInputFile(std::string file_location) override
Create an input file handle for the given location.
Definition std_io.h:303
Status DeleteFile(const std::string &file_location) override
Delete a file at the given location.
Definition std_io.h:320
Result< std::unique_ptr< OutputFile > > NewOutputFile(std::string file_location) override
Create an output file handle for the given location.
Definition std_io.h:316
Result< std::unique_ptr< InputFile > > NewInputFile(std::string file_location, size_t length) override
Create an input file handle for the given location with a known length.
Definition std_io.h:307
std::string_view location() const override
File location represented by this handle.
Definition std_io.h:258
Result< std::unique_ptr< PositionOutputStream > > CreateOrOverwrite() override
Create a new output stream, replacing any existing file.
Definition std_io.h:264
Result< std::unique_ptr< PositionOutputStream > > Create() override
Create a new output stream and fail if the file already exists.
Definition std_io.h:260
Result< int64_t > StoredLength() const override
Return the current stored length of the output.
Definition std_io.h:173
Status Write(std::span< const std::byte > data) override
Write all bytes in data at the current position.
Definition std_io.h:185
Result< int64_t > Position() const override
Return the current write position.
Definition std_io.h:165
Status Flush() override
Flush buffered data to the underlying store.
Definition std_io.h:197
Status Close() override
Close the stream. Implementations should allow repeated Close calls.
Definition std_io.h:205