diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/fud_assert.hpp | 13 | ||||
-rw-r--r-- | include/fud_format.hpp | 293 | ||||
-rw-r--r-- | include/fud_span.hpp | 16 | ||||
-rw-r--r-- | include/fud_string_view.hpp | 16 | ||||
-rw-r--r-- | include/fud_utf8.hpp | 2 |
5 files changed, 321 insertions, 19 deletions
diff --git a/include/fud_assert.hpp b/include/fud_assert.hpp index ecfaa2f..6b21fdc 100644 --- a/include/fud_assert.hpp +++ b/include/fud_assert.hpp @@ -22,22 +22,9 @@ // #include <stacktrace> #include <source_location> -#include "fud_span.hpp" namespace fud { -constexpr std::size_t MAX_FILE_CHARS = 256; -constexpr std::size_t MAX_FUNCTION_CHARS = 256; -constexpr std::size_t BITS_PER_OCTAL = 3; -constexpr auto MAX_LINE_CHARS = BITS_PER_OCTAL * sizeof(decltype(std::source_location{}.line())) + 3; -constexpr std::size_t MAX_ASSERT_CHARS = 512 - MAX_LINE_CHARS; -constexpr std::size_t ASSERT_MSG_SIZE = MAX_FILE_CHARS + MAX_LINE_CHARS + MAX_FUNCTION_CHARS + MAX_ASSERT_CHARS; - -void assertFormat( - const char* assertion, - std::source_location sourceLocation, - Span<char, ASSERT_MSG_SIZE> buffer); - [[noreturn]] void assertFail( const char* assertion, std::source_location sourceLocation = std::source_location::current()); diff --git a/include/fud_format.hpp b/include/fud_format.hpp new file mode 100644 index 0000000..8985faf --- /dev/null +++ b/include/fud_format.hpp @@ -0,0 +1,293 @@ +/* + * libfud + * Copyright 2024 Dominick Allen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FUD_FORMAT_HPP +#define FUD_FORMAT_HPP + +#include "fud_assert.hpp" +#include "fud_result.hpp" +#include "fud_span.hpp" +#include "fud_status.hpp" +#include "fud_string_view.hpp" + +#include <format> // for std::format_string +#include <cstdint> +#include <optional> + +namespace fud { + +template <size_t Size> +using CharSpan = Span<char, Size>; + +template <typename... Args> +using FormatLiteral = std::format_string<Args...>; + +template <typename... Args, size_t Size> +Result<size_t, FudStatus> format(CharSpan<Size> buffer, FormatLiteral<Args...> formatLiteral, Args&&... args); + +enum class FormatAlign : uint8_t { + Left, + Right, + Center +}; + +struct FormatFill { + FormatAlign align; + char fill; +}; + +enum class FormatSign : uint8_t { + Plus, + Minus, + Space +}; + +struct FormatSpec { + std::optional<FormatFill> fill; + std::optional<FormatSign> formatSign; + uint32_t minWidth; +}; + +namespace detail { + +template <typename Arg, typename... Args, size_t Size> +Result<size_t, FudStatus> formatHelper( + CharSpan<Size> buffer, + size_t formattedSize, + StringView formatView, + Arg&& arg, + Args&&... args); + +template <typename Arg, typename... Args, size_t Size> +Result<size_t, FudStatus> formatHelper( + CharSpan<Size> buffer, + size_t formattedSize, + StringView formatView, + Arg&& arg); + +template <size_t Size> +Result<size_t, FudStatus> formatHelper( + CharSpan<Size> buffer, + size_t formattedSize, + StringView formatView); + +} // namespace detail + +template <typename... Args, size_t Size> +Result<size_t, FudStatus> format(CharSpan<Size> buffer, FormatLiteral<Args...> formatLiteral, Args&&... args) +{ + static_assert(Size > 0); + + if (buffer.data() == nullptr) { + return FudStatus::NullPointer; + } + + StringView formatView{formatLiteral.get()}; + + if (formatView.length() == 0 || formatView.data()[0] == '\0') { + return 0U; + } + + size_t argCount = sizeof...(args); + static_cast<void>(argCount); + + size_t formattedSize = 0; + + return detail::formatHelper(buffer, formattedSize, formatView, std::forward<Args>(args)...); +} + +namespace detail { + +#define FUDETAIL_ADVANCE_FORMAT(FORMAT_VIEW, ADVANCE_BY) \ + fudAssert(ADVANCE_BY <= FORMAT_VIEW.m_length); \ + FORMAT_VIEW.m_length -= ADVANCE_BY; \ + FORMAT_VIEW.m_data += ADVANCE_BY; \ + +constexpr bool findBracket(size_t& copyLength, StringView formatView) +{ + while (copyLength < formatView.m_length) { + if (formatView.m_data[copyLength] == '{') { + return true; + } + copyLength++; + } + + return false; +} + +template <size_t Size> +size_t copyRemaining( + CharSpan<Size> buffer, + size_t formattedSize, + StringView& formatView, + size_t copyLength) +{ + fudAssert(copyLength <= formatView.length()); + if (copyLength + formattedSize > Size) { + copyLength = Size - formattedSize; + } + auto copyResult = copyMem( + buffer.data() + formattedSize, + Size - formattedSize, + formatView.m_data, + copyLength); + fudAssert(copyResult == FudStatus::Success); + FUDETAIL_ADVANCE_FORMAT(formatView, copyLength); + return formattedSize + copyLength; +} + +template <typename Arg, size_t Size> +Result<size_t, FudStatus> handleSpec( + CharSpan<Size> buffer, + size_t formattedSize, + StringView& formatView, + Arg&& arg, + bool& consumed) +{ + fudAssert(formattedSize < Size); + fudAssert(formatView.length() > 1); + + if (formatView.m_data[1] == '{') { + consumed = false; + buffer[formattedSize] = '{'; + FUDETAIL_ADVANCE_FORMAT(formatView, 2); + return formattedSize + 1; + } + + static_cast<void>(arg); + buffer[formattedSize] = 'X'; + formattedSize += 1; + size_t index = 0; + for (; index < formatView.m_length; ++index) { + if (formatView.m_data[index] == '}') { + break; + } + } + FUDETAIL_ADVANCE_FORMAT(formatView, index + 1); + return formattedSize; +} + +template <typename Arg, typename... Args, size_t Size> +Result<size_t, FudStatus> formatHelper( + CharSpan<Size> buffer, + size_t formattedSize, + StringView formatView, + Arg&& arg, + Args&&... args) +{ + while (formattedSize < Size) { + size_t copyLength = 0; + auto found = findBracket(copyLength, formatView); + formattedSize = copyRemaining(buffer, formattedSize, formatView, copyLength); + fudAssert(formattedSize <= Size); + if (!found || formattedSize == Size) { + return formattedSize; + } + + bool consumed = false; + auto specResult = handleSpec(buffer, formattedSize, formatView, std::forward<Arg>(arg), consumed); + formattedSize = M_TakeOrReturn(specResult); + fudAssert(formattedSize <= Size); + if (formattedSize == Size) { + return formattedSize; + } + + if (consumed) { + return formatHelper(buffer, formattedSize, formatView, std::forward<Args>(args)...); + } + } + + return formattedSize; +} + +template <typename Arg, typename... Args, size_t Size> +Result<size_t, FudStatus> formatHelper( + CharSpan<Size> buffer, + size_t formattedSize, + StringView formatView, + Arg&& arg) +{ + while (formattedSize < Size) { + size_t copyLength = 0; + auto found = findBracket(copyLength, formatView); + formattedSize = copyRemaining(buffer, formattedSize, formatView, copyLength); + fudAssert(formattedSize <= Size); + if (!found || formattedSize == Size) { + return formattedSize; + } + + bool consumed = false; + auto specResult = handleSpec(buffer, formattedSize, formatView, std::forward<Arg>(arg), consumed); + formattedSize = M_TakeOrReturn(specResult); + if (consumed) { + return formatHelper(buffer, formattedSize, formatView); + } + } + return formattedSize; +} + +template <size_t Size> +Result<size_t, FudStatus> formatHelper( + CharSpan<Size> buffer, + size_t formattedSize, + StringView formatView) +{ + size_t index = 0; + while (formattedSize < Size && formatView.m_length > 0) { + while (index < formatView.m_length && formattedSize + index < Size) { + if (formatView.m_data[index] == '{') { + break; + } + index++; + } + bool isBracket{false}; + if (index + 1 < formatView.m_length && formattedSize + index + 1 < Size) { + if (formatView.m_data[index] == '{') { + isBracket = true; + index++; + } + } + auto copyResult = copyMem( + buffer.data() + formattedSize, + Size - formattedSize, + formatView.m_data, + index); + formattedSize += index; + formatView.m_length -= index; + formatView.m_data += index; + if (isBracket) { + index = 0; + if (formatView.m_length > 0) { + formatView.m_length--; + formatView.m_data++; + } + if (formattedSize < Size) { + buffer.data()[formattedSize] = 'X'; + formattedSize++; + } + } + } + return formattedSize; +} + +#undef FUDETAIL_ADVANCE_FORMAT + +} // namespace detail + +} // namespace fud + +#endif diff --git a/include/fud_span.hpp b/include/fud_span.hpp index e8c5704..cc693f8 100644 --- a/include/fud_span.hpp +++ b/include/fud_span.hpp @@ -43,6 +43,22 @@ struct Span { return output; } + template <typename U> + static Span make(Array<U, Size>& array) { + static_assert(std::convertible_to<U, T>); + Span<T, Size> output{}; + output.m_data = array.data(); + return output; + } + + template <size_t ArraySize> + static Span makeCStringBuffer(Array<T, ArraySize>& array) { + static_assert(ArraySize > Size); + Span<T, Size> output{}; + output.m_data = array.data(); + return output; + } + T* m_data; [[nodiscard]] constexpr size_t size() const diff --git a/include/fud_string_view.hpp b/include/fud_string_view.hpp index 4796003..7b4925e 100644 --- a/include/fud_string_view.hpp +++ b/include/fud_string_view.hpp @@ -15,23 +15,27 @@ * limitations under the License. */ - #ifndef FUD_STRING_VIEW_HPP #define FUD_STRING_VIEW_HPP #include "fud_status.hpp" #include "fud_utf8.hpp" +#include <string_view> + namespace fud { class String; -class StringView { - public: +struct StringView { constexpr StringView() noexcept = default; + constexpr StringView(const StringView& rhs) noexcept = default; + constexpr StringView(StringView&& rhs) noexcept = default; + constexpr ~StringView() noexcept = default; + constexpr StringView& operator=(const StringView& rhs) = default; constexpr StringView& operator=(StringView&& rhs) = default; @@ -40,11 +44,13 @@ class StringView { } StringView(size_t strLen, const char* strData) : - m_length(strLen), // line break + m_length(strLen), // line break m_data{reinterpret_cast<const utf8*>(strData)} // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) { } + StringView(std::string_view rhs) noexcept : StringView(rhs.length(), rhs.data()) {} + explicit StringView(const String& fudString) noexcept; [[nodiscard]] constexpr size_t length() const @@ -85,8 +91,8 @@ class StringView { FudStatus toDouble(double& number, size_t& strLen) const; - private: size_t m_length{0}; + const utf8* m_data{nullptr}; }; diff --git a/include/fud_utf8.hpp b/include/fud_utf8.hpp index 058e4f9..539e0f4 100644 --- a/include/fud_utf8.hpp +++ b/include/fud_utf8.hpp @@ -31,7 +31,7 @@ namespace fud { using utf8 = unsigned char; class String; -class StringView; +struct StringView; constexpr utf8 ASCII_MASK = 0x7F; |