summaryrefslogtreecommitdiff
path: root/include/fud_format.hpp
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2024-10-18 21:43:44 -0500
committerDominick Allen <djallen@librehumanitas.org>2024-10-18 21:43:44 -0500
commite94db4695e236b42ae1be44b2605075161d5144f (patch)
treeada1a0442abdd08375df7f9a8f9cd50924e0840a /include/fud_format.hpp
parent8249b28bea29e8ce17eac12776a60ec3f9e47176 (diff)
Add temporary work for formatting.
Diffstat (limited to 'include/fud_format.hpp')
-rw-r--r--include/fud_format.hpp293
1 files changed, 293 insertions, 0 deletions
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