diff --git a/source/coral.cpp b/source/coral.cpp index 3a30715..3737e5d 100644 --- a/source/coral.cpp +++ b/source/coral.cpp @@ -452,12 +452,34 @@ export namespace coral { unavailable, }; + /** + * Readable resource interface. + */ struct reader { - virtual expected read(slice const & buffer) = 0; + virtual ~reader() {} + + /** + * Attempts to fill `data` with whatever data the reader has to offer, returning the number + * of bytes actually read. + * + * Should the read operation fail for any reason, a [io_error] is returned instead. + */ + virtual expected read(slice const & data) = 0; }; + /** + * Writable resource interface. + */ struct writer { - virtual expected write(slice const & buffer) = 0; + virtual ~writer() {} + + /** + * Attempts to write `data` out to the writer, returning the number of bytes actually + * written. + * + * Should the write operation fail for any reason, a [io_error] is returned instead. + */ + virtual expected write(slice const & data) = 0; }; } @@ -617,4 +639,93 @@ export namespace coral { return a; } + + /** + * Multiplexing byte buffer of `capacity` size that may be used for memory-backed I/O + * operations and lightweight data construction. + */ + template struct ring_buffer : public writer, public reader { + ring_buffer(coral::u8 fill_value) : data{fill_value} {} + + /** + * Returns the base pointer of the buffer data. + */ + u8 * begin() { + return this->data; + } + + /** + * Returns the base pointer of the buffer data. + */ + u8 const * begin() const { + return this->data; + } + + /** + * Returns the tail pointer of the buffer data. + */ + u8 * end() { + return this->data + this->cursor; + } + + /** + * Returns the tail pointer of the buffer data. + */ + u8 const * end() const { + return this->data + this->cursor; + } + + /** + * Returns `true` if the buffer has been completely filled with data. + */ + bool is_full() const { + return this->filled == capacity; + } + + /** + * 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->filled, data.length)}; + + this->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; + } + + return readable_data.length; + } + + /** + * 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. + */ + 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->filled))}; + + this->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; + } + + return writable_data.length; + } + + private: + usize filled = 0; + + usize read_index = 0; + + usize write_index = 0; + + u8 data[capacity]; + }; }