Compare commits
	
		
			5 Commits
		
	
	
		
			5d4f40393b
			...
			d58c9e67c4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d58c9e67c4 | |||
| cd3fd2f04a | |||
| 809b45dc3c | |||
| d8b9a7e330 | |||
| e2b1550f62 | 
							
								
								
									
										4
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
								
							@ -4,5 +4,7 @@
 | 
				
			|||||||
        "cassert": "cpp",
 | 
					        "cassert": "cpp",
 | 
				
			||||||
        "cstddef": "cpp",
 | 
					        "cstddef": "cpp",
 | 
				
			||||||
        "string_view": "cpp"
 | 
					        "string_view": "cpp"
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
 | 
					    "editor.detectIndentation": false,
 | 
				
			||||||
 | 
					    "editor.insertSpaces": false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@ export module app;
 | 
				
			|||||||
import coral;
 | 
					import coral;
 | 
				
			||||||
import coral.files;
 | 
					import coral.files;
 | 
				
			||||||
import coral.image;
 | 
					import coral.image;
 | 
				
			||||||
 | 
					import coral.io;
 | 
				
			||||||
import coral.math;
 | 
					import coral.math;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import oar;
 | 
					import oar;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										197
									
								
								source/coral.cpp
									
									
									
									
									
								
							
							
						
						
									
										197
									
								
								source/coral.cpp
									
									
									
									
									
								
							@ -94,17 +94,14 @@ export namespace coral {
 | 
				
			|||||||
		/**
 | 
							/**
 | 
				
			||||||
		 * Number of `type` elements referenced.
 | 
							 * Number of `type` elements referenced.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		usize length;
 | 
							usize length{0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/**
 | 
							/**
 | 
				
			||||||
		 * Base element address referenced.
 | 
							 * Base element address referenced.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		type * pointer;
 | 
							type * pointer{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		constexpr slice() {
 | 
							constexpr slice() = default;
 | 
				
			||||||
			this->length = 0;
 | 
					 | 
				
			||||||
			this->pointer = nullptr;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		constexpr slice(char const *&& zstring) {
 | 
							constexpr slice(char const *&& zstring) {
 | 
				
			||||||
			this->pointer = zstring;
 | 
								this->pointer = zstring;
 | 
				
			||||||
@ -268,6 +265,24 @@ export [[nodiscard]] void * operator new[](coral::usize requested_size, coral::a
 | 
				
			|||||||
	return allocator.reallocate(nullptr, requested_size);
 | 
						return allocator.reallocate(nullptr, requested_size);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * If `pointer` is a non-`nullptr` value, the referenced memory will be deallocated using
 | 
				
			||||||
 | 
					 * `allocator`. Otherwise, the function has no side-effects.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * *Note*: passing a `pointer` value that was not allocated by `allocator` will result in erroneous
 | 
				
			||||||
 | 
					 * behavior defined by the [coral::allocator] implementation.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export void operator delete(void * pointer, coral::allocator & allocator) {
 | 
				
			||||||
 | 
						return allocator.deallocate(pointer);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export void operator delete[](void * pointer, coral::allocator & allocator) {
 | 
				
			||||||
 | 
						return allocator.deallocate(pointer);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Wrapper types.
 | 
					// Wrapper types.
 | 
				
			||||||
export namespace coral {
 | 
					export namespace coral {
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@ -558,174 +573,4 @@ export namespace coral {
 | 
				
			|||||||
		a = b;
 | 
							a = b;
 | 
				
			||||||
		b = temp;
 | 
							b = temp;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Streams the data from `input` to `output`, using `buffer` as temporary transfer space.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * The returned [expected] can be used to introspect if `input` or `output` encountered any
 | 
					 | 
				
			||||||
	 * issues during streaming, otherwise it will contain the number of bytes streamed.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * *Note*: if `buffer` has a length of `0`, no data will be streamed as there is nowhere to
 | 
					 | 
				
			||||||
	 * temporarily place data during streaming.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	expected<usize, io_error> stream(writer & output, reader & input, slice<u8> const & buffer) {
 | 
					 | 
				
			||||||
		usize total_bytes_written = 0;
 | 
					 | 
				
			||||||
		expected bytes_read = input.read(buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (!bytes_read.is_ok()) return bytes_read.error();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		usize read = bytes_read.value();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		while (read != 0) {
 | 
					 | 
				
			||||||
			expected const bytes_written = output.write(buffer.sliced(0, read));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!bytes_written.is_ok()) return bytes_read.error();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			total_bytes_written += bytes_written.value();
 | 
					 | 
				
			||||||
			bytes_read = input.read(buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!bytes_read.is_ok()) return bytes_read.error();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			read = bytes_read.value();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return total_bytes_written;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Attempts to format and print `value` as an unsigned integer out to `output`.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * The returned [expected] can be used to introspect if `output` encountered any issues during
 | 
					 | 
				
			||||||
	 * printing, otherwise it will contain the number of characters used to print `value` as text.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	expected<usize, io_error> print_unsigned(writer & output, u64 value) {
 | 
					 | 
				
			||||||
		if (value == 0) return output.write(slice{"0"}.as_bytes());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		u8 buffer[20]{0};
 | 
					 | 
				
			||||||
		usize buffer_count{0};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		while (value != 0) {
 | 
					 | 
				
			||||||
			constexpr usize radix{10};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			buffer[buffer_count] = static_cast<u8>((value % radix) + '0');
 | 
					 | 
				
			||||||
			value = (value / radix);
 | 
					 | 
				
			||||||
			buffer_count += 1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		usize const half_buffer_count{buffer_count / 2};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for (usize i = 0; i < half_buffer_count; i += 1)
 | 
					 | 
				
			||||||
			swap(buffer[i], buffer[buffer_count - i - 1]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return output.write({buffer, buffer_count});
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Returns a reference to a shared [allocator] which will always return `nullptr` on calls to
 | 
					 | 
				
			||||||
	 * [allocator::reallocate].
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	allocator & null_allocator() {
 | 
					 | 
				
			||||||
		static struct : public allocator {
 | 
					 | 
				
			||||||
			u8 * reallocate(u8 * maybe_allocation, usize requested_size) override {
 | 
					 | 
				
			||||||
				if (maybe_allocation != nullptr) unreachable();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				return nullptr;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			void deallocate(void * allocation) override {
 | 
					 | 
				
			||||||
				if (allocation != nullptr) unreachable();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		} a;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return a;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Multiplexing byte-based ring buffer of `capacity` size that may be used for memory-backed
 | 
					 | 
				
			||||||
	 * I/O operations and lightweight data construction.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	template<usize capacity> struct fixed_buffer : public writer, public reader {
 | 
					 | 
				
			||||||
		fixed_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<usize, io_error> read(slice<u8> 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<usize, io_error> write(slice<u8 const> 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];
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										175
									
								
								source/coral/io.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								source/coral/io.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,175 @@
 | 
				
			|||||||
 | 
					export module coral.io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						template<usize capacity> struct fixed_buffer : public writer, public reader {
 | 
				
			||||||
 | 
							fixed_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<usize, io_error> read(slice<u8> 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<usize, io_error> write(slice<u8 const> 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];
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Streams the data from `input` to `output`, using `buffer` as temporary transfer space.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * The returned [expected] can be used to introspect if `input` or `output` encountered any
 | 
				
			||||||
 | 
						 * issues during streaming, otherwise it will contain the number of bytes streamed.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * *Note*: if `buffer` has a length of `0`, no data will be streamed as there is nowhere to
 | 
				
			||||||
 | 
						 * temporarily place data during streaming.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						expected<usize, io_error> stream(writer & output, reader & input, slice<u8> const & buffer) {
 | 
				
			||||||
 | 
							usize total_bytes_written = 0;
 | 
				
			||||||
 | 
							expected bytes_read = input.read(buffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!bytes_read.is_ok()) return bytes_read.error();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							usize read = bytes_read.value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while (read != 0) {
 | 
				
			||||||
 | 
								expected const bytes_written = output.write(buffer.sliced(0, read));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!bytes_written.is_ok()) return bytes_read.error();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								total_bytes_written += bytes_written.value();
 | 
				
			||||||
 | 
								bytes_read = input.read(buffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!bytes_read.is_ok()) return bytes_read.error();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								read = bytes_read.value();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return total_bytes_written;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Returns a reference to a shared [allocator] which will always return `nullptr` on calls to
 | 
				
			||||||
 | 
						 * [allocator::reallocate].
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						allocator & null_allocator() {
 | 
				
			||||||
 | 
							static struct : public allocator {
 | 
				
			||||||
 | 
								u8 * reallocate(u8 * maybe_allocation, usize requested_size) override {
 | 
				
			||||||
 | 
									if (maybe_allocation != nullptr) unreachable();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									return nullptr;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								void deallocate(void * allocation) override {
 | 
				
			||||||
 | 
									if (allocation != nullptr) unreachable();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} a;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return a;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Attempts to format and print `value` as an unsigned integer out to `output`.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * The returned [expected] can be used to introspect if `output` encountered any issues during
 | 
				
			||||||
 | 
						 * printing, otherwise it will contain the number of characters used to print `value` as text.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						expected<usize, io_error> print_unsigned(writer & output, u64 value) {
 | 
				
			||||||
 | 
							if (value == 0) return output.write(slice{"0"}.as_bytes());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							u8 buffer[20]{0};
 | 
				
			||||||
 | 
							usize buffer_count{0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while (value != 0) {
 | 
				
			||||||
 | 
								constexpr usize radix{10};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								buffer[buffer_count] = static_cast<u8>((value % radix) + '0');
 | 
				
			||||||
 | 
								value = (value / radix);
 | 
				
			||||||
 | 
								buffer_count += 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							usize const half_buffer_count{buffer_count / 2};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (usize i = 0; i < half_buffer_count; i += 1)
 | 
				
			||||||
 | 
								swap(buffer[i], buffer[buffer_count - i - 1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return output.write({buffer, buffer_count});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -44,16 +44,21 @@ export namespace coral {
 | 
				
			|||||||
	 * this, it is recommended to use larger `init_capacity` values for data which has a known or
 | 
						 * this, it is recommended to use larger `init_capacity` values for data which has a known or
 | 
				
			||||||
	 * approximate upper bound at compile-time. Otherwise, the `init_capacity` value may be left at
 | 
						 * approximate upper bound at compile-time. Otherwise, the `init_capacity` value may be left at
 | 
				
			||||||
	 * its default.
 | 
						 * its default.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * *Note*: the [allocator] referenced in the stack must remain valid for the duration of the
 | 
				
			||||||
 | 
						 * stack lifetime.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	template<typename element, usize init_capacity = 1> struct stack : public sequence<element> {
 | 
						template<typename element, usize init_capacity = 1> struct stack : public sequence<element> {
 | 
				
			||||||
		stack(allocator * dynamic_allocator) : local_buffer{0} {
 | 
							stack(allocator * dynamic_allocator) {
 | 
				
			||||||
			this->dynamic_allocator = dynamic_allocator;
 | 
								this->dynamic_allocator = dynamic_allocator;
 | 
				
			||||||
			this->filled = 0;
 | 
					 | 
				
			||||||
			this->elements = this->local_buffer;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		~stack() override {
 | 
							~stack() override {
 | 
				
			||||||
			if (this->is_dynamic()) this->dynamic_allocator->deallocate(this->elements.pointer);
 | 
								if (this->is_dynamic()) {
 | 
				
			||||||
 | 
									for (element & e : this->elements) e.~element();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									this->dynamic_allocator->deallocate(this->elements.pointer);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/**
 | 
							/**
 | 
				
			||||||
@ -115,7 +120,7 @@ export namespace coral {
 | 
				
			|||||||
		 * Returns `true` if the stack is backed by dynamic memory, otherwise `false`.
 | 
							 * Returns `true` if the stack is backed by dynamic memory, otherwise `false`.
 | 
				
			||||||
		 */
 | 
							 */
 | 
				
			||||||
		bool is_dynamic() const {
 | 
							bool is_dynamic() const {
 | 
				
			||||||
			return this->elements.pointer != this->local_buffer;
 | 
								return this->elements.pointer != reinterpret_cast<element const *>(this->local_buffer);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/**
 | 
							/**
 | 
				
			||||||
@ -157,7 +162,6 @@ export namespace coral {
 | 
				
			|||||||
			usize const requested_capacity = this->filled + capacity;
 | 
								usize const requested_capacity = this->filled + capacity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (this->is_dynamic()) {
 | 
								if (this->is_dynamic()) {
 | 
				
			||||||
				// Grow dynamic buffer (bailing out if failed).
 | 
					 | 
				
			||||||
				u8 * const buffer = this->dynamic_allocator->reallocate(
 | 
									u8 * const buffer = this->dynamic_allocator->reallocate(
 | 
				
			||||||
					reinterpret_cast<u8 *>(this->elements.pointer),
 | 
										reinterpret_cast<u8 *>(this->elements.pointer),
 | 
				
			||||||
						sizeof(element) * requested_capacity);
 | 
											sizeof(element) * requested_capacity);
 | 
				
			||||||
@ -188,13 +192,13 @@ export namespace coral {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private:
 | 
							private:
 | 
				
			||||||
		allocator * dynamic_allocator;
 | 
							allocator * dynamic_allocator{nullptr};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		usize filled;
 | 
							usize filled{0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		slice<element> elements;
 | 
							slice<element> elements{reinterpret_cast<element *>(local_buffer), init_capacity};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		element local_buffer[init_capacity];
 | 
							u8 local_buffer[init_capacity]{0};
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ import app;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import coral;
 | 
					import coral;
 | 
				
			||||||
import coral.files;
 | 
					import coral.files;
 | 
				
			||||||
 | 
					import coral.io;
 | 
				
			||||||
import coral.math;
 | 
					import coral.math;
 | 
				
			||||||
import coral.sequence;
 | 
					import coral.sequence;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -29,7 +30,6 @@ extern "C" int main(int argc, char const * const * argv) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			coral::stack<coral::u8> script_source{allocator};
 | 
								coral::stack<coral::u8> script_source{allocator};
 | 
				
			||||||
 | 
					 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				coral::u8 stream_buffer[1024]{0};
 | 
									coral::u8 stream_buffer[1024]{0};
 | 
				
			||||||
				coral::sequence_writer script_writer{&script_source};
 | 
									coral::sequence_writer script_writer{&script_source};
 | 
				
			||||||
@ -38,7 +38,7 @@ extern "C" int main(int argc, char const * const * argv) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			vm.with_object(vm.compile(coral::slice{script_source.begin(), script_source.end()}.as_chars()), [&](kym::bound_object & script) {
 | 
								vm.with_object(vm.compile(coral::slice{script_source.begin(), script_source.end()}.as_chars()), [&](kym::bound_object & script) {
 | 
				
			||||||
				vm.with_object(script.call({}), [&](kym::bound_object config) {
 | 
									vm.with_object(script.call({}), [&](kym::bound_object & config) {
 | 
				
			||||||
					constexpr auto as_u16 = [](coral::i64 value) -> coral::optional<coral::i16> {
 | 
										constexpr auto as_u16 = [](coral::i64 value) -> coral::optional<coral::i16> {
 | 
				
			||||||
						if ((value >= 0) && (value <= coral::u16_max))
 | 
											if ((value >= 0) && (value <= coral::u16_max))
 | 
				
			||||||
							return static_cast<coral::u16>(value);
 | 
												return static_cast<coral::u16>(value);
 | 
				
			||||||
@ -62,16 +62,7 @@ extern "C" int main(int argc, char const * const * argv) {
 | 
				
			|||||||
						return system.log(app::log_level::error, "failed to initialize window");
 | 
											return system.log(app::log_level::error, "failed to initialize window");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					vm.with_object(config.get_field("title"), [&](kym::bound_object & title) {
 | 
										vm.with_object(config.get_field("title"), [&](kym::bound_object & title) {
 | 
				
			||||||
						coral::stack<coral::u8, 128> title_buffer{&system.thread_safe_allocator()};
 | 
											// TODO: implement.
 | 
				
			||||||
						coral::sequence_writer title_writer{&title_buffer};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						if (!title.stringify(title_writer).is_ok()) {
 | 
					 | 
				
			||||||
							system.log(app::log_level::error,
 | 
					 | 
				
			||||||
								"failed to decode `title` property of config");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							return;
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						is_config_loaded = true;
 | 
											is_config_loaded = true;
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
				});
 | 
									});
 | 
				
			||||||
@ -80,7 +71,6 @@ extern "C" int main(int argc, char const * const * argv) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if (!is_config_loaded) {
 | 
							if (!is_config_loaded) {
 | 
				
			||||||
			coral::stack<coral::u8> error_message{&system.thread_safe_allocator()};
 | 
								coral::stack<coral::u8> error_message{&system.thread_safe_allocator()};
 | 
				
			||||||
 | 
					 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				coral::sequence_writer error_writer{&error_message};
 | 
									coral::sequence_writer error_writer{&error_message};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user