From e94db4695e236b42ae1be44b2605075161d5144f Mon Sep 17 00:00:00 2001 From: Dominick Allen Date: Fri, 18 Oct 2024 21:43:44 -0500 Subject: Add temporary work for formatting. --- include/fud_format.hpp | 293 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 include/fud_format.hpp (limited to 'include/fud_format.hpp') 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 // for std::format_string +#include +#include + +namespace fud { + +template +using CharSpan = Span; + +template +using FormatLiteral = std::format_string; + +template +Result format(CharSpan buffer, FormatLiteral 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 fill; + std::optional formatSign; + uint32_t minWidth; +}; + +namespace detail { + +template +Result formatHelper( + CharSpan buffer, + size_t formattedSize, + StringView formatView, + Arg&& arg, + Args&&... args); + +template +Result formatHelper( + CharSpan buffer, + size_t formattedSize, + StringView formatView, + Arg&& arg); + +template +Result formatHelper( + CharSpan buffer, + size_t formattedSize, + StringView formatView); + +} // namespace detail + +template +Result format(CharSpan buffer, FormatLiteral 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(argCount); + + size_t formattedSize = 0; + + return detail::formatHelper(buffer, formattedSize, formatView, std::forward(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 copyRemaining( + CharSpan 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 +Result handleSpec( + CharSpan 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(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 +Result formatHelper( + CharSpan 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), consumed); + formattedSize = M_TakeOrReturn(specResult); + fudAssert(formattedSize <= Size); + if (formattedSize == Size) { + return formattedSize; + } + + if (consumed) { + return formatHelper(buffer, formattedSize, formatView, std::forward(args)...); + } + } + + return formattedSize; +} + +template +Result formatHelper( + CharSpan 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), consumed); + formattedSize = M_TakeOrReturn(specResult); + if (consumed) { + return formatHelper(buffer, formattedSize, formatView); + } + } + return formattedSize; +} + +template +Result formatHelper( + CharSpan 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 -- cgit v1.2.3