diff --git a/source/coral/io.cpp b/source/coral/io.cpp index bb8d32f..6f076e7 100755 --- a/source/coral/io.cpp +++ b/source/coral/io.cpp @@ -4,34 +4,76 @@ import coral; export namespace coral { /** - * Multiplexing byte-based ring buffer of `capacity` size that may be used for memory-backed I/O operations and - * lightweight data construction. + * Multiplexing byte-based ring buffer that may be used for memory-backed I/O operations and fast data (re)- + * construction. */ - template struct fixed_buffer : public writer, public reader { - fixed_buffer() = default; + struct ring_buffer : public writer, public reader { + /** + * Potential results from attempting to allocate a ring buffer dynamically. + * + * [allocate_result::ok] means nothing went wrong and the allocation was successful. + * + * [allocate_result::out_of_memory] indicates that there is not enough memory available in the provided + * allocator to create a buffer of the requested size. + */ + enum class [[nodiscard]] allocate_result { + ok, + out_of_memory, + }; + + ring_buffer() = default; + + ring_buffer(ring_buffer const &) = delete; + + ~ring_buffer() override { + if (this->data_allocator != nullptr) this->data_allocator->deallocate(this->data.pointer); + } + + /** + * Attempts to allocate memory for the ring buffer `capacity` number of bytes large with `data_allocator` as the + * allocation strategy. + * + * See [allocate_result] for more information on error handling. + * + * *Note*: calling allocate will wipe out the contents of the current buffer. + */ + allocate_result allocate(allocator & data_allocator, usize capacity) { + if (this->data_allocator != nullptr) this->data_allocator->deallocate(this->data.pointer); + + this->clear(); + + u8 * const data {this->data_allocator->reallocate(nullptr, capacity)}; + + if (data == nullptr) { + this->data_allocator = nullptr; + this->data = {}; + + return allocate_result::out_of_memory; + } + + this->data_allocator = &data_allocator; + this->data = {data, capacity}; + + return allocate_result::ok; + } /** * Returns a mutable [slice] ranging from the head to the last-filled element. * * *Note*: The lifetime and validity of the returned slice is only guaranteed for as long as the source - * [fixed_buffer] is not mutated or out-of-scope. + * [ring_buffer] is not mutated or out-of-scope. */ slice as_slice() const { - return {this->data, this->data_filled}; - } - - /** - * Returns the base pointer of the buffer data. - */ - u8 const * begin() const { return this->data; } /** - * Returns the tail pointer of the buffer data. + * Clears all written elements from the buffer. */ - u8 const * end() const { - return this->data + this->cursor; + void clear() { + this->data_filled = 0; + this->read_index = 0; + this->write_index = 0; } /** @@ -45,14 +87,14 @@ export namespace coral { * Returns `true` if the buffer is completely empty of data, otherwise `false`. */ bool is_empty() const { - return this->data_filled == capacity; + return this->data_filled == this->data.length; } /** * Returns `true` if the buffer has been completely filled with data, otherwise `false`. */ bool is_full() const { - return this->data_filled == capacity; + return this->data_filled == this->data.length; } /** @@ -64,7 +106,7 @@ export namespace coral { this->data_filled += 1; this->data[this->write_index] = data; - this->write_index = (this->write_index + 1) % capacity; + this->write_index = (this->write_index + 1) % this->data.length; return true; } @@ -73,13 +115,13 @@ export namespace coral { * Reads whatever data is in the buffer into `data`, returning the number of bytes read from the buffer. */ expected read(slice const & data) override { - slice const readable_data {this->data, min(this->data_filled, data.length)}; + slice const readable_data {this->data.sliced(0, min(this->data_filled, data.length))}; this->data_filled -= readable_data.length; for (usize index = 0; index < readable_data.length; index += 1) { data[index] = this->data[this->read_index]; - this->read_index = (this->read_index + 1) % capacity; + this->read_index = (this->read_index + 1) % this->data.length; } return readable_data.length; @@ -89,23 +131,20 @@ export namespace coral { * Returns the remaining unfilled buffer space in bytes. */ usize remaining() const { - return capacity - this->data_filled; + return this->data.length - this->data_filled; } /** - * Attempts to write `data` to the buffer, returning the number of bytes written or [io_error::unavailable] if - * it has been completely filled and no more bytes can be written. + * Write `data` to the buffer, returning the number of bytes written to the buffer. */ expected write(slice const & data) override { - if (this->is_full()) return io_error::unavailable; - slice const writable_data {data.sliced(0, min(data.length, this->remaining()))}; this->data_filled += writable_data.length; for (usize index = 0; index < writable_data.length; index += 1) { this->data[this->write_index] = data[index]; - this->write_index = (this->write_index + 1) % capacity; + this->write_index = (this->write_index + 1) % this->data.length; } return writable_data.length; @@ -118,7 +157,9 @@ export namespace coral { usize write_index {0}; - u8 data[capacity]{0}; + allocator * data_allocator {nullptr}; + + slice data {}; }; /**