summaryrefslogtreecommitdiff
path: root/lib/nes/common
diff options
context:
space:
mode:
Diffstat (limited to 'lib/nes/common')
-rw-r--r--lib/nes/common/CMakeLists.txt3
-rw-r--r--lib/nes/common/box.hh29
-rw-r--r--lib/nes/common/debug.hh18
-rw-r--r--lib/nes/common/list.hh24
-rw-r--r--lib/nes/common/path-builder.cc14
-rw-r--r--lib/nes/common/path-builder.hh21
-rw-r--r--lib/nes/common/path-view.cc4
-rw-r--r--lib/nes/common/span.hh33
-rw-r--r--lib/nes/common/status.hh42
-rw-r--r--lib/nes/common/string-builder.cc116
-rw-r--r--lib/nes/common/string-builder.hh37
11 files changed, 243 insertions, 98 deletions
diff --git a/lib/nes/common/CMakeLists.txt b/lib/nes/common/CMakeLists.txt
index a7c1d76..a5703ff 100644
--- a/lib/nes/common/CMakeLists.txt
+++ b/lib/nes/common/CMakeLists.txt
@@ -13,4 +13,5 @@ target_sources(
path-view.cc
path-builder.hh
path-builder.cc
- span.hh) \ No newline at end of file
+ span.hh
+ status.hh) \ No newline at end of file
diff --git a/lib/nes/common/box.hh b/lib/nes/common/box.hh
index 3a65613..7bf2848 100644
--- a/lib/nes/common/box.hh
+++ b/lib/nes/common/box.hh
@@ -1,6 +1,7 @@
#pragma once
#include "nes/common/types.hh"
+#include "nes/common/debug.hh"
namespace nes
{
@@ -28,10 +29,30 @@ namespace nes
auto operator=(box const&) -> box& = delete;
auto operator=(box&&) -> box& = delete;
- auto operator*() -> T& { return *get(); }
- auto operator*() const -> T const& { return *get(); }
- auto operator->() -> T* { return get(); }
- auto operator->() const -> T const* { return get(); }
+ auto operator*() -> T&
+ {
+ NES_ASSERT(has_value() && "expected box to be non-empty");
+ return *get();
+ }
+
+ auto operator*() const -> T const&
+ {
+ NES_ASSERT(has_value() && "expected box to be non-empty");
+ return *get();
+ }
+
+ auto operator->() -> T*
+ {
+ NES_ASSERT(has_value() && "expected box to be non-empty");
+ return get();
+ }
+
+ auto operator->() const -> T const*
+ {
+ NES_ASSERT(has_value() && "expected box to be non-empty");
+ return get();
+ }
+
operator bool() const { return has_value(); }
auto has_value() const -> bool { return initialized_; }
diff --git a/lib/nes/common/debug.hh b/lib/nes/common/debug.hh
index 409d55a..30370e9 100644
--- a/lib/nes/common/debug.hh
+++ b/lib/nes/common/debug.hh
@@ -5,11 +5,25 @@
#include <spdlog/spdlog.h>
#define NES_DEBUG_LOG(name, ...) \
- spdlog::info("[" #name "] " __VA_ARGS__)
+ spdlog::info("[" #name "] " __VA_ARGS__)
#else
#define NES_DEBUG_LOG(name, fmt, ...) \
- (void)0
+ (void)0
+
+#endif
+
+#ifdef NDEBUG
+
+#define NES_ASSERT(...) \
+ (void)0
+
+#else
+
+#include <cassert>
+
+#define NES_ASSERT(...) \
+ assert(__VA_ARGS__)
#endif \ No newline at end of file
diff --git a/lib/nes/common/list.hh b/lib/nes/common/list.hh
index b1fddf0..46f7027 100644
--- a/lib/nes/common/list.hh
+++ b/lib/nes/common/list.hh
@@ -1,5 +1,7 @@
#pragma once
+#include "nes/common/debug.hh"
+
namespace nes
{
/// An intrusive doubly-linked list.
@@ -39,15 +41,23 @@ namespace nes
auto operator==(iterator const& other) -> bool { return current_ == other.current_; }
auto operator!=(iterator const& other) -> bool { return current_ != other.current_; }
- auto operator*() const -> T* { return current_; }
+ auto operator*() const -> T&
+ {
+ NES_ASSERT(current_ != nullptr && "invalid iterator");
+ return *current_;
+ }
+
+ auto operator->() const -> T*
+ {
+ NES_ASSERT(current_ != nullptr && "invalid iterator");
+ return current_;
+ }
auto operator++() -> iterator&
{
- if (current_)
- {
- node& current_node = *current_;
- current_ = current_node.next_;
- }
+ NES_ASSERT(current_ != nullptr && "invalid iterator");
+ node& current_node = *current_;
+ current_ = current_node.next_;
return *this;
}
@@ -126,7 +136,7 @@ namespace nes
/// Remove an item from the list by its iterator.
auto remove(iterator it) -> bool
{
- if (it != end()) { return remove(**it); }
+ if (it != end()) { return remove(*it); }
return false;
}
};
diff --git a/lib/nes/common/path-builder.cc b/lib/nes/common/path-builder.cc
index 794d565..b66226f 100644
--- a/lib/nes/common/path-builder.cc
+++ b/lib/nes/common/path-builder.cc
@@ -9,28 +9,28 @@ namespace nes
if (buffer_.get_length() > 0) { buffer_[length_++] = '/'; }
}
- auto path_builder::push(std::string_view component) -> bool
+ auto path_builder::push(std::string_view component) -> status
{
// Trim leading and trailing slashes
while (!component.empty() && component[0] == '/') { component = component.substr(1); }
while (!component.empty() && component[component.length() - 1] == '/') { component = component.substr(0, component.length() - 1); }
- if (component.empty() || std::find(component.begin(), component.end(), '/') != component.end()) { return false; }
+ if (component.empty() || std::find(component.begin(), component.end(), '/') != component.end()) { return status::error_invalid_path; }
// Check if there is enough remaining capacity for "/<component>"
auto const add_leading_slash = length_ == 0 || buffer_[length_ - 1] != '/';
auto const length = component.length() + (add_leading_slash ? 1 : 0);
- if (length_ + length > buffer_.get_length()) { return false; }
+ if (length_ + length > buffer_.get_length()) { return status::error_buffer_overflow; }
// Append "/<component>"
if (add_leading_slash) { buffer_[length_++] = '/'; }
for (auto i = u32{ 0 }; i < component.length(); ++i) { buffer_[length_++] = component[i]; }
- return true;
+ return status::success;
}
- auto path_builder::pop() -> bool
+ auto path_builder::pop() -> status
{
- if (length_ == 0 || buffer_[length_ - 1] == '/') { return false; }
+ if (length_ == 0 || buffer_[length_ - 1] == '/') { return status::error_invalid_path; }
// Find the start of the last component.
auto last_start = length_ - 1;
@@ -41,6 +41,6 @@ namespace nes
// Drop everything including the trailing slash.
length_ = last_start;
- return true;
+ return status::success;
}
} // namespace nes \ No newline at end of file
diff --git a/lib/nes/common/path-builder.hh b/lib/nes/common/path-builder.hh
index b64fb28..e7980ea 100644
--- a/lib/nes/common/path-builder.hh
+++ b/lib/nes/common/path-builder.hh
@@ -3,6 +3,7 @@
#include "nes/common/types.hh"
#include "nes/common/span.hh"
#include "nes/common/path-view.hh"
+#include "nes/common/status.hh"
#include <string_view>
namespace nes
@@ -10,6 +11,9 @@ namespace nes
/// A mutable path with a fixed-length buffer.
class path_builder
{
+ template<u32 Capacity>
+ friend class path_buffer;
+
span<char> buffer_;
u32 length_{ 0 };
@@ -19,8 +23,8 @@ namespace nes
auto get_components() const -> path_view { return path_view{ get_path() }; }
auto get_path() const -> std::string_view { return std::string_view{ buffer_.get_data(), length_ }; }
- auto push(std::string_view) -> bool;
- auto pop() -> bool;
+ auto push(std::string_view) -> status;
+ auto pop() -> status;
};
/// A path builder with a buffer embedded into it.
@@ -34,5 +38,18 @@ namespace nes
: path_builder{ buffer_ }
{
}
+
+ path_buffer(path_buffer const& other)
+ : path_builder{ buffer_ }
+ {
+ std::copy_n(other.buffer_, Capacity, buffer_);
+ length_ = other.length_;
+ }
+
+ auto operator=(path_buffer const& other)
+ {
+ std::copy_n(other.buffer_, Capacity, buffer_);
+ length_ = other.length_;
+ }
};
} // namespace nes \ No newline at end of file
diff --git a/lib/nes/common/path-view.cc b/lib/nes/common/path-view.cc
index 47f198b..743c060 100644
--- a/lib/nes/common/path-view.cc
+++ b/lib/nes/common/path-view.cc
@@ -1,10 +1,12 @@
#include "nes/common/path-view.hh"
+#include "nes/common/debug.hh"
namespace nes
{
auto path_view::iterator::operator*() const -> std::string_view
{
- return current_ ? std::string_view{ current_, component_length_ } : "";
+ NES_ASSERT(current_ != nullptr && component_length_ > 0 && "invalid iterator");
+ return std::string_view{ current_, component_length_ };
}
auto path_view::iterator::operator++() -> iterator&
diff --git a/lib/nes/common/span.hh b/lib/nes/common/span.hh
index 16eb129..6779c37 100644
--- a/lib/nes/common/span.hh
+++ b/lib/nes/common/span.hh
@@ -1,6 +1,7 @@
#pragma once
#include "nes/common/types.hh"
+#include "nes/common/debug.hh"
namespace nes
{
@@ -21,6 +22,9 @@ namespace nes
{
using type = T;
};
+
+ template<typename T>
+ using remove_const_type = typename remove_const<T>::type;
} // namespace detail
/// Implementation of a container similar to std::span in C++20.
@@ -54,7 +58,7 @@ namespace nes
static_assert(Length == ActualLength || Length == detail::span_dynamic_length);
}
- span(span<typename detail::remove_const<T>::type, Length> const other)
+ span(span<detail::remove_const_type<T>, Length> const other)
: span{ other.data_, other.length_, {} }
{
}
@@ -65,12 +69,31 @@ namespace nes
auto begin() const -> iterator { return data_; }
auto end() const -> iterator { return data_ + length_; }
- auto subspan(u32 const first, u32 const length) const -> span<T> { return span{ data_ + first, length }; }
- auto subspan(u32 const first) const -> span<T> { return subspan(length_ - first); }
+ auto subspan(u32 const first, u32 const length) const -> span<T>
+ {
+ NES_ASSERT(first + length <= get_length() && "subspan out of bounds");
+ return span{ data_ + first, length };
+ }
+
+ auto subspan(u32 const first) const -> span<T>
+ {
+ NES_ASSERT(first <= get_length() && "subspan out of bounds");
+ return subspan(length_ - first);
+ }
+
template<u32 OtherLength>
- auto subspan(u32 const first) const -> span<T, OtherLength> { return span<T, OtherLength>{ data_ + first, OtherLength, {} }; }
+ auto subspan(u32 const first) const -> span<T, OtherLength>
+ {
+ static_assert(OtherLength < Length);
+ NES_ASSERT(first + OtherLength <= get_length() && "subspan out of bounds");
+ return span<T, OtherLength>{ data_ + first, OtherLength, {} };
+ }
- auto operator[](u32 const index) const -> T& { return data_[index]; }
+ auto operator[](u32 const index) const -> T&
+ {
+ NES_ASSERT(index < get_length() && "index out of bounds");
+ return data_[index];
+ }
private:
explicit span(T* data, u32 const length, detail::span_unchecked)
diff --git a/lib/nes/common/status.hh b/lib/nes/common/status.hh
new file mode 100644
index 0000000..4b23b60
--- /dev/null
+++ b/lib/nes/common/status.hh
@@ -0,0 +1,42 @@
+#pragma once
+
+namespace nes
+{
+ /// A status code.
+ enum class status
+ {
+ success,
+ error_system_error,
+ error_invalid_ines_data,
+ error_unsupported_mapper,
+ error_unsupported_instruction,
+ error_buffer_overflow,
+ error_invalid_path,
+ error_invalid_format_string,
+ };
+
+ constexpr auto to_string(status const status) -> char const*
+ {
+ switch (status)
+ {
+ case status::success:
+ return "Success";
+ case status::error_system_error:
+ return "System error";
+ case status::error_invalid_ines_data:
+ return "Invalid iNES data";
+ case status::error_unsupported_mapper:
+ return "Unsupported mapper";
+ case status::error_unsupported_instruction:
+ return "Unsupported instruction";
+ case status::error_buffer_overflow:
+ return "Buffer overflow";
+ case status::error_invalid_path:
+ return "Invalid path";
+ case status::error_invalid_format_string:
+ return "Invalid format string";
+ }
+
+ return "(invalid)";
+ }
+} // namespace nes \ No newline at end of file
diff --git a/lib/nes/common/string-builder.cc b/lib/nes/common/string-builder.cc
index 087f0d4..7747aaa 100644
--- a/lib/nes/common/string-builder.cc
+++ b/lib/nes/common/string-builder.cc
@@ -7,70 +7,73 @@ namespace nes
{
}
- auto string_builder::append_char(char const value) -> string_builder&
+ auto string_builder::append_char(char const value) -> status
{
- if (length_ == buffer_.get_length())
- {
- is_good_ = false;
- return *this;
- }
+ if (length_ == buffer_.get_length()) { return status::error_buffer_overflow; }
buffer_[length_] = value;
length_ += 1;
- return *this;
+ return status::success;
}
- auto string_builder::append_string(std::string_view const value) -> string_builder&
+ auto string_builder::append_string(std::string_view const value) -> status
{
+ auto copy = *this;
for (auto const c : value)
{
- append_char(c);
+ if (auto const s = copy.append_char(c); s != status::success) { return s; }
}
- return *this;
+ *this = copy;
+ return status::success;
}
- auto string_builder::append_int(i64 value, number_format const format) -> string_builder&
+ auto string_builder::append_int(i64 value, number_format const format) -> status
{
+ auto copy = *this;
if (value < 0)
{
- append_char('-');
+ if (auto const s = copy.append_char('-'); s != status::success) { return s; }
value += 1; // to prevent lossy conversion to unsigned
auto abs_value = static_cast<u64>(-value);
abs_value += 1; // to correct the previous offset
- append_int(abs_value, format);
+ if (auto const s = copy.append_int(abs_value, format); s != status::success) { return s; }
}
else
{
- append_int(static_cast<u64>(value), format);
+ if (auto const s = copy.append_int(static_cast<u64>(value), format); s != status::success) { return s; }
}
- return *this;
+ *this = copy;
+ return status::success;
}
- auto string_builder::append_int(u64 value, number_format const format) -> string_builder&
+ auto string_builder::append_int(u64 value, number_format const format) -> status
{
constexpr auto max_string_length = u32{ 64 }; // worst case is a 64-bit integer in binary representation
constexpr auto digits = "0123456789abcdef";
+ auto copy = *this;
auto base = u64{ 10 };
+ auto prefix = "";
switch (format)
{
case number_format::binary:
- append_string("0b");
+ prefix = "0b";
base = 2;
break;
case number_format::octal:
- append_string("0");
+ prefix = "0";
base = 8;
break;
case number_format::decimal:
break;
case number_format::hexadecimal:
- append_string("0x");
+ prefix = "0x";
base = 16;
break;
}
+ if (auto const s = copy.append_string(prefix); s != status::success) { return s; }
char buf[max_string_length + 1] = {}; // null-terminated
auto pos = max_string_length;
@@ -89,19 +92,21 @@ namespace nes
buf[pos] = '0';
}
- append_string(&buf[pos]);
- return *this;
+ if (const auto s = copy.append_string(&buf[pos]); s != status::success) { return s; }
+
+ *this = copy;
+ return status::success;
}
- auto string_builder::append_bool(bool const value) -> string_builder&
+ auto string_builder::append_bool(bool const value) -> status
{
- append_string(value ? "true" : "false");
- return *this;
+ return append_string(value ? "true" : "false");
}
- auto string_builder::append_format(
- std::string_view const fmt, format_arg const* args, u32 const arg_count) -> string_builder&
+ auto string_builder::append_format(std::string_view const fmt, format_arg const* args, u32 const arg_count) -> status
{
+ auto copy = *this;
+
// Go through the format string, inserting the arguments for the templates as necessary.
auto current_arg = u32{ 0 };
for (auto i = u32{ 0 }; i < fmt.length(); ++i)
@@ -112,7 +117,7 @@ namespace nes
if (fmt[i] == '{') {
// escaped '{'
- append_char('{');
+ if (auto const s = copy.append_char('{'); s != status::success) { return s; }
}
else
{
@@ -128,21 +133,20 @@ namespace nes
if (fmt[i] != '}')
{
// unmatched '{'
- is_good_ = false;
- break;
+ return status::error_invalid_format_string;
}
// end of template
auto const p = std::string_view{ params, params_length };
if (current_arg < arg_count)
{
- append_format_arg(args[current_arg], p);
+ if (auto const s = copy.append_format_arg(args[current_arg], p); s != status::success) { return s; }
current_arg += 1;
}
else
{
// missing argument in parameter pack
- is_good_ = false;
+ return status::error_invalid_format_string;
}
}
}
@@ -153,39 +157,41 @@ namespace nes
if (fmt[i] == '}')
{
// escaped '}'
- append_char('}');
+ if (auto const s = copy.append_char('}'); s != status::success) { return s; }
}
else
{
// unmatched '}'
- is_good_ = false;
- break;
+ return status::error_invalid_format_string;
}
}
else
{
// normal character in the template string
- append_char(fmt[i]);
+ if (auto const s = copy.append_char(fmt[i]); s != status::success) { return s; }
}
}
if (current_arg < arg_count)
{
// not all supplied arguments were used
- is_good_ = false;
+ return status::error_invalid_format_string;
}
- return *this;
+ *this = copy;
+ return status::success;
}
- auto string_builder::append_format_arg(format_arg const arg, std::string_view const params) -> string_builder&
+ auto string_builder::append_format_arg(format_arg const arg, std::string_view const params) -> status
{
auto const parse_params = [&]
{
if (!params.empty())
{
- is_good_ = false;
+ return status::error_invalid_format_string;
}
+
+ return status::success;
};
auto const parse_params_integer = [&](number_format& out_format)
@@ -208,47 +214,43 @@ namespace nes
}
else
{
- out_format = number_format::decimal;
- is_good_ = false;
+ return status::error_invalid_format_string;
}
+
+ return status::success;
};
switch (arg.type)
{
case format_arg_type::string:
{
- parse_params();
- append_string(arg.string);
- break;
+ if (auto const s = parse_params(); s != status::success) { return s; }
+ return append_string(arg.string);
}
case format_arg_type::character:
{
- parse_params();
- append_char(arg.character);
- break;
+ if (auto const s = parse_params(); s != status::success) { return s; }
+ return append_char(arg.character);
}
case format_arg_type::signed_integer:
{
auto format = number_format{};
- parse_params_integer(format);
- append_int(arg.signed_integer, format);
- break;
+ if (auto const s = parse_params_integer(format); s != status::success) { return s; }
+ return append_int(arg.signed_integer, format);
}
case format_arg_type::unsigned_integer:
{
auto format = number_format{};
- parse_params_integer(format);
- append_int(arg.unsigned_integer, format);
- break;
+ if (auto const s = parse_params_integer(format); s != status::success) { return s; }
+ return append_int(arg.unsigned_integer, format);
}
case format_arg_type::boolean:
{
- parse_params();
- append_bool(arg.character);
- break;
+ if (auto const s = parse_params(); s != status::success) { return s; }
+ return append_bool(arg.character);
}
}
- return *this;
+ return status::error_invalid_format_string;
}
} // namespace nes \ No newline at end of file
diff --git a/lib/nes/common/string-builder.hh b/lib/nes/common/string-builder.hh
index 256c267..0298b8e 100644
--- a/lib/nes/common/string-builder.hh
+++ b/lib/nes/common/string-builder.hh
@@ -2,6 +2,7 @@
#include "nes/common/types.hh"
#include "nes/common/span.hh"
+#include "nes/common/status.hh"
#include <string_view>
namespace nes
@@ -17,9 +18,11 @@ namespace nes
/// Builds a string into a fixed-length buffer.
class string_builder
{
+ template<u32 Capacity>
+ friend class string_buffer;
+
span<char> buffer_;
u32 length_{ 0 };
- bool is_good_{ true };
enum class format_arg_type
{
@@ -65,14 +68,11 @@ namespace nes
/// Get the resulting string.
auto get_result() const -> std::string_view { return std::string_view{ buffer_.get_data(), length_ }; }
- /// Returns false after an invalid format string or buffer overflow.
- auto is_good() const -> bool { return is_good_; }
-
- auto append_char(char) -> string_builder&;
- auto append_string(std::string_view) -> string_builder&;
- auto append_int(i64, number_format = number_format::decimal) -> string_builder&;
- auto append_int(u64, number_format = number_format::decimal) -> string_builder&;
- auto append_bool(bool) -> string_builder&;
+ auto append_char(char) -> status;
+ auto append_string(std::string_view) -> status;
+ auto append_int(i64, number_format = number_format::decimal) -> status;
+ auto append_int(u64, number_format = number_format::decimal) -> status;
+ auto append_bool(bool) -> status;
/// Print a templated string.
///
@@ -82,7 +82,7 @@ namespace nes
///
/// Curly braces can be escaped by repeating them twice like {{this}}.
template<typename... Args>
- auto append_format(std::string_view const fmt, Args... args) -> string_builder&
+ auto append_format(std::string_view const fmt, Args... args) -> status
{
constexpr u32 arg_count = sizeof...(Args);
// Add another argument at the end so we don't have an empty array expression.
@@ -92,8 +92,8 @@ namespace nes
}
private:
- auto append_format(std::string_view fmt, format_arg const* args, u32 arg_count) -> string_builder&;
- auto append_format_arg(format_arg, std::string_view params) -> string_builder&;
+ auto append_format(std::string_view fmt, format_arg const* args, u32 arg_count) -> status;
+ auto append_format_arg(format_arg, std::string_view params) -> status;
};
/// A string builder with a buffer embedded into it.
@@ -107,5 +107,18 @@ namespace nes
: string_builder{ buffer_ }
{
}
+
+ string_buffer(string_buffer const& other)
+ : string_builder{ buffer_ }
+ {
+ std::copy_n(other.buffer_, Capacity, buffer_);
+ length_ = other.length_;
+ }
+
+ auto operator=(string_buffer const& other)
+ {
+ std::copy_n(other.buffer_, Capacity, buffer_);
+ length_ = other.length_;
+ }
};
} // namespace nes \ No newline at end of file