blob: f53d3cba87ff9e683227511c747406a1002d794d [file] [log] [blame] [edit]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_VFS_CPP_NEW_PSEUDO_FILE_H_
#define LIB_VFS_CPP_NEW_PSEUDO_FILE_H_
#include <lib/fit/function.h>
#include <lib/vfs/cpp/new/internal/node.h>
#include <vector>
namespace vfs {
// Buffered pseudo-file.
//
// This variant is optimized for incrementally reading and writing properties
// which are larger than can typically be read or written by the client in
// a single I/O transaction.
//
// In read mode, the pseudo-file invokes its read handler when the file is
// opened and retains the content in a buffer which the client incrementally
// reads from and can seek within.
//
// In write mode, the client incrementally writes into and seeks within the
// buffer which the pseudo-file delivers as a whole to the write handler when
// the file is closed (if there were any writes). Truncation is also supported.
//
// This class is thread-hostile.
//
// # Simple usage
//
// Instances of this class should be owned and managed on the same thread
// that services their connections.
//
// # Advanced usage
//
// You can use a background thread to service connections provided:
// async_dispatcher_t for the background thread is stopped or suspended
// prior to destroying the file.
class PseudoFile final : public internal::Node {
public:
// Handler called to read from the pseudo-file.
using ReadHandler = fit::function<zx_status_t(std::vector<uint8_t>* output, size_t max_bytes)>;
// Handler called to write into the pseudo-file.
using WriteHandler = fit::function<zx_status_t(std::vector<uint8_t> input)>;
// Creates a buffered pseudo-file.
//
// |read_handler| cannot be null. If the |write_handler| is null, then the
// pseudo-file is considered not writable. The |max_file_size|
// determines the maximum number of bytes which can be written to and read from
// the pseudo-file's input buffer when it it opened for writing/reading.
PseudoFile(size_t max_file_size, ReadHandler read_handler = ReadHandler(),
WriteHandler write_handler = WriteHandler())
: Node(MakePseudoFile(max_file_size, std::move(read_handler), std::move(write_handler))) {}
~PseudoFile() override = default;
private:
struct PseudoFileState {
const ReadHandler read_handler;
const WriteHandler write_handler;
const size_t max_size;
std::vector<uint8_t> buffer; // Temporary buffer used to store owned data until it's copied.
};
static inline vfs_internal_node_t* MakePseudoFile(size_t max_file_size, ReadHandler read_handler,
WriteHandler write_handler) {
ZX_ASSERT(read_handler);
vfs_internal_node_t* file;
PseudoFileState* cookie = new PseudoFileState{
.read_handler = std::move(read_handler),
.write_handler = std::move(write_handler),
.max_size = max_file_size,
};
vfs_internal_file_context_t context{
.cookie = cookie,
.read = &ReadCallback,
.release = &ReleaseCallback,
.write = &WriteCallback,
.destroy = &DestroyCookie,
};
ZX_ASSERT(vfs_internal_pseudo_file_create(max_file_size, &context, &file) == ZX_OK);
return file;
}
static void DestroyCookie(void* cookie) { delete static_cast<PseudoFileState*>(cookie); }
static zx_status_t ReadCallback(void* cookie, const char** data_out, size_t* len_out) {
PseudoFileState& state = *static_cast<PseudoFileState*>(cookie);
if (zx_status_t status = state.read_handler(&state.buffer, state.max_size); status != ZX_OK) {
return status;
}
*data_out = reinterpret_cast<const char*>(state.buffer.data());
*len_out = state.buffer.size();
return ZX_OK;
}
static void ReleaseCallback(void* cookie) {
PseudoFileState& state = *static_cast<PseudoFileState*>(cookie);
state.buffer.clear();
}
static zx_status_t WriteCallback(const void* cookie, const char* data, size_t len) {
const PseudoFileState& state = *static_cast<const PseudoFileState*>(cookie);
const uint8_t* begin = reinterpret_cast<const uint8_t*>(data);
std::vector<uint8_t> contents(begin, begin + len);
return state.write_handler(std::move(contents));
}
};
} // namespace vfs
#endif // LIB_VFS_CPP_NEW_PSEUDO_FILE_H_