| // 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_ |