From e791ac9a46095e0241181da89c35a7c702982c21 Mon Sep 17 00:00:00 2001 From: Hannes Schulze Date: Tue, 4 Feb 2025 19:25:03 +0100 Subject: clean up path logic a bit --- lib/nes/common/CMakeLists.txt | 6 ++++- lib/nes/common/path-builder.cc | 47 ++++++++++++++++++++++++++++++++++++ lib/nes/common/path-builder.hh | 38 +++++++++++++++++++++++++++++ lib/nes/common/path-view.cc | 49 +++++++++++++++++++++++++++++++++++++ lib/nes/common/path-view.hh | 52 +++++++++++++--------------------------- lib/nes/common/string-builder.hh | 13 ++++++++++ 6 files changed, 168 insertions(+), 37 deletions(-) create mode 100644 lib/nes/common/path-builder.cc create mode 100644 lib/nes/common/path-builder.hh create mode 100644 lib/nes/common/path-view.cc (limited to 'lib/nes/common') diff --git a/lib/nes/common/CMakeLists.txt b/lib/nes/common/CMakeLists.txt index 11143d7..f0ee288 100644 --- a/lib/nes/common/CMakeLists.txt +++ b/lib/nes/common/CMakeLists.txt @@ -8,4 +8,8 @@ target_sources( box.hh display.hh string-builder.hh - string-builder.cc) \ No newline at end of file + string-builder.cc + path-view.hh + path-view.cc + path-builder.hh + path-builder.cc) \ No newline at end of file diff --git a/lib/nes/common/path-builder.cc b/lib/nes/common/path-builder.cc new file mode 100644 index 0000000..67190dc --- /dev/null +++ b/lib/nes/common/path-builder.cc @@ -0,0 +1,47 @@ +#include "nes/common/path-builder.hh" + +namespace nes +{ + path_builder::path_builder(char* buffer, u32 const buffer_length) + : buffer_{ buffer } + , buffer_length_{ buffer_length } + { + // Initialize the path to "/" if possible. + if (buffer_length > 0) { buffer_[actual_length_++] = '/'; } + } + + auto path_builder::push(std::string_view component) -> bool + { + // 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; } + + // Check if there is enough remaining capacity for "/" + auto const add_leading_slash = actual_length_ == 0 || buffer_[actual_length_ - 1] != '/'; + auto const length = component.length() + (add_leading_slash ? 1 : 0); + if (actual_length_ + length > buffer_length_) { return false; } + + // Append "/" + if (add_leading_slash) { buffer_[actual_length_++] = '/'; } + for (auto i = u32{ 0 }; i < component.length(); ++i) { buffer_[actual_length_++] = component[i]; } + + return true; + } + + auto path_builder::pop() -> bool + { + if (actual_length_ == 0 || buffer_[actual_length_ - 1] == '/') { return false; } + + // Find the start of the last component. + auto last_start = actual_length_ - 1; + while (last_start > 0 && buffer_[last_start] != '/') { last_start -= 1; } + + // Drop the slash if we are not at the root. + if (last_start == 0) { last_start += 1; } + + // Drop everything including the trailing slash. + actual_length_ = last_start; + return true; + } +} // namespace nes \ No newline at end of file diff --git a/lib/nes/common/path-builder.hh b/lib/nes/common/path-builder.hh new file mode 100644 index 0000000..727d18c --- /dev/null +++ b/lib/nes/common/path-builder.hh @@ -0,0 +1,38 @@ +#pragma once + +#include "nes/common/types.hh" +#include "nes/common/path-view.hh" +#include + +namespace nes +{ + /// A mutable path with a fixed-length buffer. + class path_builder + { + char* buffer_{ nullptr }; + u32 buffer_length_{ 0 }; + u32 actual_length_{ 0 }; + + public: + explicit path_builder(char* buffer, u32 buffer_length); + + auto get_components() const -> path_view { return path_view{ get_path() }; } + auto get_path() const -> std::string_view { return std::string_view{ buffer_, actual_length_ }; } + + auto push(std::string_view) -> bool; + auto pop() -> bool; + }; + + /// A path builder with a buffer embedded into it. + template + class path_buffer : public path_builder + { + char buffer_[Capacity]; + + public: + explicit path_buffer() + : path_builder{ buffer_, Capacity } + { + } + }; +} // namespace nes \ No newline at end of file diff --git a/lib/nes/common/path-view.cc b/lib/nes/common/path-view.cc new file mode 100644 index 0000000..47f198b --- /dev/null +++ b/lib/nes/common/path-view.cc @@ -0,0 +1,49 @@ +#include "nes/common/path-view.hh" + +namespace nes +{ + auto path_view::iterator::operator*() const -> std::string_view + { + return current_ ? std::string_view{ current_, component_length_ } : ""; + } + + auto path_view::iterator::operator++() -> iterator& + { + // Move forward to next component + current_ += component_length_; + remaining_ -= component_length_; + + // Skip over '/'. + while (remaining_ > 0 && *current_ == '/') + { + current_ += 1; + remaining_ -= 1; + } + + // Scan until end of component to find its length. + component_length_ = 0; + while (component_length_ < remaining_ && current_[component_length_] != '/') + { + component_length_ += 1; + } + + return *this; + } + + auto path_view::iterator::operator++(int) -> iterator + { + auto const res = *this; + ++*this; + return res; + } + + auto path_view::begin() const -> iterator + { + return ++iterator{ path_.data(), 0, static_cast(path_.size()) }; + } + + auto path_view::end() const -> iterator + { + return iterator{ path_.data() + path_.size(), 0, 0 }; + } +} // namespace nes \ No newline at end of file diff --git a/lib/nes/common/path-view.hh b/lib/nes/common/path-view.hh index 3d670bf..7741060 100644 --- a/lib/nes/common/path-view.hh +++ b/lib/nes/common/path-view.hh @@ -5,13 +5,10 @@ namespace nes { - /// Non-owning copy of a path, consisting of multiple path components stored as string views. - /// - /// This is similar in spirit to a string_view, but for collections of string_views instead of characters. + /// Non-owning copy of a path, useful for iterating over its components. class path_view { - std::string_view const* components_{ nullptr }; - u32 component_count_{ 0 }; + std::string_view path_; public: /// An iterator for the path components. @@ -19,54 +16,37 @@ namespace nes { friend path_view; - std::string_view const* current_{ nullptr }; - u32 remaining_{ 0 }; + char const* current_{ nullptr }; // Start of the current component + u32 component_length_{ 0 }; // Length of the current component + u32 remaining_{ 0 }; // Number of characters remaining in the buffer, including the current component. public: auto operator==(iterator const& other) -> bool { return current_ == other.current_; } auto operator!=(iterator const& other) -> bool { return current_ != other.current_; } - auto operator*() const -> std::string_view { return current_ ? *current_ : ""; } - - auto operator++() -> iterator& - { - if (remaining_ > 0) - { - current_ += 1; - remaining_ -= 1; - } - return *this; - } - - auto operator++(int) -> iterator - { - auto res = *this; - ++*this; - return res; - } + auto operator*() const -> std::string_view; + auto operator++() -> iterator&; + auto operator++(int) -> iterator; private: - explicit iterator() = default; - explicit iterator(std::string_view const* current, u32 const remaining) + explicit iterator(char const* current, u32 const component_length, u32 const remaining) : current_{ current } + , component_length_{ component_length } , remaining_{ remaining } { } }; explicit path_view() = default; - - explicit path_view(std::string_view const* components, u32 const component_count) - : components_{ components } - , component_count_{ component_count } + explicit path_view(std::string_view const path) + : path_{ path } { } - auto get_components() const -> std::string_view const* { return components_; } - auto get_component_count() const -> u32 { return component_count_; } - auto get_component(u32 const index) const -> std::string_view { return components_[index]; } + auto get_path() const -> std::string_view { return path_; } - auto begin() const -> iterator { return iterator{ components_, component_count_ }; } - auto end() const -> iterator { return iterator{ components_ + component_count_, 0 }; } + auto begin() const -> iterator; + auto end() const -> iterator; + auto is_empty() const -> bool { return begin() == end(); } }; } // namespace nes::app \ No newline at end of file diff --git a/lib/nes/common/string-builder.hh b/lib/nes/common/string-builder.hh index fda85a4..e0bc673 100644 --- a/lib/nes/common/string-builder.hh +++ b/lib/nes/common/string-builder.hh @@ -95,4 +95,17 @@ namespace nes 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&; }; + + /// A string builder with a buffer embedded into it. + template + class string_buffer : public string_builder + { + char buffer_[Capacity]; + + public: + explicit string_buffer() + : string_builder{ buffer_, Capacity } + { + } + }; } // namespace nes \ No newline at end of file -- cgit v1.3.1