diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-10-29 21:02:25 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-10-29 21:02:25 -0500 |
commit | 8ce397e8c0a83e49e390de9deb73d588e4931ecf (patch) | |
tree | 31f4f4facf0cb75535aaec130d606c54fe97b2d8 | |
parent | f281050ddb3b9d658cff67a254eedc3b79de5c5d (diff) |
Reworking of Result.
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | include/fud_drain.hpp | 35 | ||||
-rw-r--r-- | include/fud_format.hpp | 43 | ||||
-rw-r--r-- | include/fud_result.hpp | 214 | ||||
-rw-r--r-- | include/fud_string.hpp | 64 | ||||
-rw-r--r-- | include/fud_string_convert.hpp | 51 | ||||
-rw-r--r-- | include/fud_utf8.hpp | 1 | ||||
-rw-r--r-- | include/fud_vector.hpp | 66 | ||||
-rw-r--r-- | source/fud_directory.cpp | 2 | ||||
-rw-r--r-- | source/fud_file.cpp | 59 | ||||
-rw-r--r-- | source/fud_format.cpp | 59 | ||||
-rw-r--r-- | source/fud_string.cpp | 54 | ||||
-rw-r--r-- | source/fud_string_convert.cpp | 25 | ||||
-rw-r--r-- | test/CMakeLists.txt | 2 | ||||
-rw-r--r-- | test/test_file.cpp | 2 | ||||
-rw-r--r-- | test/test_fud.cpp | 2 | ||||
-rw-r--r-- | test/test_result.cpp | 7 | ||||
-rw-r--r-- | test/test_utf8.cpp | 5 | ||||
-rw-r--r-- | test/test_vector.cpp | 3 |
19 files changed, 459 insertions, 242 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b2d05a..e40d7b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,10 +52,14 @@ else() set(FUD_BOUNDS_CHECKING true) endif() +if(FUD_SAN) +set(CVG_FLAGS ${CVG_FLAGS} -fsanitize=address -fsanitize=undefined -fno-sanitize=vptr) +endif() + if (FUD_TEST) add_subdirectory(test) # set(CVG_FLAGS -fsanitize=address -fsanitize=undefined --coverage) - set(CVG_FLAGS --coverage) + set(CVG_FLAGS ${CVG_FLAGS} --coverage) target_compile_options(fud PUBLIC ${CVG_FLAGS}) target_link_options(fud PUBLIC ${CVG_FLAGS}) endif () @@ -102,6 +106,7 @@ set(FUD_HEADERS "include/fud_c_file.hpp" "include/fud_c_string.hpp" "include/fud_directory.hpp" + "include/fud_drain.hpp" "include/fud_file.hpp" "include/fud_fixed_vector.hpp" "include/fud_fud_type_traits.hpp" diff --git a/include/fud_drain.hpp b/include/fud_drain.hpp new file mode 100644 index 0000000..5b78b10 --- /dev/null +++ b/include/fud_drain.hpp @@ -0,0 +1,35 @@ +/* + * 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_DRAIN_HPP +#define FUD_DRAIN_HPP + +#include "fud_status.hpp" + +#include <concepts> +#include <cstddef> + +namespace fud { + +struct DrainResult { + size_t bytesWritten; + FudStatus status; +}; + +} // namespace fud + +#endif diff --git a/include/fud_format.hpp b/include/fud_format.hpp index e156f43..c06643d 100644 --- a/include/fud_format.hpp +++ b/include/fud_format.hpp @@ -125,10 +125,10 @@ struct FormatFill { length = 2; auto fill = data[0]; if (not Ascii::valid(fill)) { - return FudStatus::Utf8Invalid; + return RetType::error(FudStatus::Utf8Invalid); } if (fill == '{' || fill == '}') { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } return RetType::okay(FormatFill{std::move(align2).value(), data[0]}); } @@ -465,45 +465,46 @@ FormatResult vFormat(Sink& sink, FormatCharMode formatMode, FormatString fmt, co template <size_t Size> Result<uint32_t, FudStatus> getSpecField(FormatSpec& formatSpec, uint32_t index, const FormatArguments<Size>& args) { + using RetType = Result<uint32_t, FudStatus>; static_assert(not std::is_signed_v<utf8>); if (not formatSpec.takesPosition() || not formatSpec.takesWidth) { - return FudStatus::ArgumentInvalid; + return RetType::error(FudStatus::ArgumentInvalid); } if (index >= Size) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } if (std::holds_alternative<utf8>(args[index])) { - return std::get<utf8>(args[index]); + return RetType::okay(std::get<utf8>(args[index])); } if (std::holds_alternative<int32_t>(args[index])) { auto value = std::get<int32_t>(args[index]); if (value < 0) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } - return static_cast<uint32_t>(value); + return RetType::okay(static_cast<uint32_t>(value)); } if (std::holds_alternative<int64_t>(args[index])) { auto value = std::get<int64_t>(args[index]); if (value < 0 || value > std::numeric_limits<uint32_t>::max()) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } - return static_cast<uint32_t>(value); + return RetType::okay(static_cast<uint32_t>(value)); } if (std::holds_alternative<uint32_t>(args[index])) { - return std::get<uint32_t>(args[index]); + return RetType::okay(std::get<uint32_t>(args[index])); } if (std::holds_alternative<uint32_t>(args[index])) { auto value = std::get<uint64_t>(args[index]); if (value > std::numeric_limits<uint32_t>::max()) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } - return static_cast<uint32_t>(value); + return RetType::okay(static_cast<uint32_t>(value)); } - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } template <size_t Size> @@ -514,6 +515,7 @@ Result<FormatSpec, FudStatus> vFormatPrepareSpec( bool& firstSpec, bool& takesPosition) { + using RetType = Result<FormatSpec, FudStatus>; if (firstSpec) { firstSpec = false; return vFormatPrepareFirstSpec(scanView, args, argIndex, takesPosition); @@ -528,24 +530,24 @@ Result<FormatSpec, FudStatus> vFormatPrepareSpec( auto formatSpec{formatSpecResult.getOkay()}; if (takesPosition != formatSpec.takesPosition() || (takesPosition && formatSpec.position >= Size)) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } if (takesPosition) { auto status = vFormatPreparePositionalSpec(formatSpec, args); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } } else { auto status = vFormatPrepareNonPositionalSpec(formatSpec, args, argIndex); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } } scanView.advanceUnsafe(specLength); - return formatSpec; + return RetType::okay(formatSpec); } template <size_t Size> @@ -555,6 +557,7 @@ Result<FormatSpec, FudStatus> vFormatPrepareFirstSpec( const uint32_t& argIndex, bool& takesPosition) { + using RetType = Result<FormatSpec, FudStatus>; size_t specLength{0}; auto formatSpecResult{FormatSpec::parse(scanView, specLength)}; if (formatSpecResult.isError()) { @@ -567,18 +570,18 @@ Result<FormatSpec, FudStatus> vFormatPrepareFirstSpec( if (takesPosition) { auto status = vFormatPreparePositionalSpec(formatSpec, args); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } } else { auto status = vFormatPrepareNonPositionalSpec(formatSpec, args, argIndex); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } } scanView.advanceUnsafe(specLength); - return formatSpec; + return RetType::okay(formatSpec); } template <size_t Size> diff --git a/include/fud_result.hpp b/include/fud_result.hpp index 497b007..b91a31a 100644 --- a/include/fud_result.hpp +++ b/include/fud_result.hpp @@ -18,31 +18,127 @@ #ifndef FUD_RESULT_HPP #define FUD_RESULT_HPP +#include "fud_assert.hpp" +#include "fud_option.hpp" +#include "fud_status.hpp" + +#include <cstdint> +#include <new> // IWYU pragma: keep (placement new) +#include <type_traits> #include <utility> -#include <variant> namespace fud { +template <typename T> +struct Okay { + T value; +}; + +template <typename E> +struct Error { + E value; +}; + +using FudError = Error<FudStatus>; + /** \brief A result type which contains either a T on success or an E on error. */ template <typename T, typename E> class [[nodiscard]] Result { public: using ResultType = Result<T, E>; - constexpr Result(const T& value) : m_value{value} + constexpr ~Result() noexcept { + destroy(); } - constexpr Result(const E& value) : m_value{value} + constexpr Result(const Okay<T>& value) : m_data{}, m_discriminant{Discriminant::Okay} { + auto ptrValue = new (m_data.data()) T(value.value); + fudAssert(ptrValue != nullptr); } - constexpr Result(T&& value) : m_value{std::move(value)} + constexpr Result(Okay<T>&& value) : m_data{}, m_discriminant{Discriminant::Okay} { + auto ptrValue = new (m_data.data()) T(std::move(value.value)); + fudAssert(ptrValue != nullptr); } - constexpr Result(E&& value) : m_value{std::move(value)} + constexpr Result(const Error<E>& value) : m_data{}, m_discriminant{Discriminant::Error} { + auto ptrValue = new (m_data.data()) E(value.value); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(E&& value) : m_data{}, m_discriminant{Discriminant::Error} + { + auto ptrValue = new (m_data.data()) E(std::move(value.value)); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(const Result<T, E>& rhs) : m_data{}, m_discriminant{rhs.m_discriminant} + { + fudAssert(m_discriminant != Discriminant::Invalid); + if (isOkay()) { + auto ptrValue = new (m_data.data()) T(rhs.getOkay()); + fudAssert(ptrValue != nullptr); + } else { + auto ptrValue = new (m_data.data()) E(rhs.getError()); + fudAssert(ptrValue != nullptr); + } + } + + constexpr Result(Result<T, E>&& rhs) : m_data{}, m_discriminant{rhs.m_discriminant} + { + fudAssert(m_discriminant != Discriminant::Invalid); + if (isOkay()) { + auto ptrValue = new (m_data.data()) T(rhs.takeOkay()); + fudAssert(ptrValue != nullptr); + } else { + auto ptrValue = new (m_data.data()) E(rhs.takeError()); + fudAssert(ptrValue != nullptr); + } + rhs.m_discriminant = Discriminant::Invalid; + } + + constexpr Result& operator=(const Result<T, E>& rhs) + { + if (&rhs == this) { + return *this; + } + destroy(); + + m_discriminant = rhs.m_discriminant; + fudAssert(m_discriminant != Discriminant::Invalid); + if (isOkay()) { + auto ptrValue = new (m_data.data()) T(rhs.getOkay()); + fudAssert(ptrValue != nullptr); + } else { + auto ptrValue = new (m_data.data()) E(rhs.getError()); + fudAssert(ptrValue != nullptr); + } + + return *this; + } + + constexpr Result& operator=(Result<T, E>&& rhs) + { + if (&rhs == this) { + return *this; + } + destroy(); + + m_discriminant = rhs.m_discriminant; + fudAssert(m_discriminant != Discriminant::Invalid); + if (isOkay()) { + auto ptrValue = new (m_data.data()) T(rhs.takeOkay()); + fudAssert(ptrValue != nullptr); + } else { + auto ptrValue = new (m_data.data()) E(rhs.takeError()); + fudAssert(ptrValue != nullptr); + } + + return *this; } static constexpr ResultType okay(const T& okay) @@ -68,40 +164,43 @@ class [[nodiscard]] Result { template <typename F> static constexpr ResultType okay(const Result<T, F>& okayRes) { - return ResultType{okayRes.getOkay()}; + return ResultType::okay(okayRes.getOkay()); } template <typename F> static constexpr ResultType okay(Result<T, F>&& okayRes) { - return ResultType{okayRes.takeOkay()}; + return ResultType::okay(okayRes.takeOkay()); } template <typename U> static constexpr ResultType error(const Result<U, E>& errorRes) { - return ResultType{errorRes.getError()}; + return ResultType::error(errorRes.getError()); } template <typename U> static constexpr ResultType error(Result<U, E>&& errorRes) { - return ResultType{errorRes.takeError()}; + return ResultType::error(errorRes.takeError()); } [[nodiscard]] constexpr bool isOkay() const { - return (m_value.index() == 0); + fudAssert(m_discriminant != Discriminant::Invalid); + return (m_discriminant == Discriminant::Okay); } [[nodiscard]] constexpr bool isError() const { - return (m_value.index() == 1); + fudAssert(m_discriminant != Discriminant::Invalid); + return (m_discriminant == Discriminant::Error); } [[nodiscard]] constexpr const T& getOkay() const& { - return std::get<T>(m_value); + fudAssert(isOkay()); + return *reinterpret_cast<const T*>(m_data.data()); } [[nodiscard]] constexpr const T& getOkayOr(const T& alternative) const& @@ -109,12 +208,13 @@ class [[nodiscard]] Result { if (!isOkay()) { return alternative; } - return std::get<T>(m_value); + return *reinterpret_cast<const T*>(m_data.data()); } [[nodiscard]] constexpr const E& getError() const& { - return std::get<E>(m_value); + fudAssert(isError()); + return *reinterpret_cast<const E*>(m_data.data()); } [[nodiscard]] constexpr const E& getErrorOr(const E& alternative) const& @@ -122,12 +222,13 @@ class [[nodiscard]] Result { if (!isError()) { return alternative; } - return std::get<E>(m_value); + return *reinterpret_cast<const E*>(m_data.data()); } [[nodiscard]] constexpr T&& takeOkay() { - return std::move(std::get<T>(m_value)); + fudAssert(isOkay()); + return std::move(*reinterpret_cast<T*>(m_data.data())); } [[nodiscard]] constexpr T&& takeOkayOr(T&& alternative) @@ -135,12 +236,13 @@ class [[nodiscard]] Result { if (!isOkay()) { return std::move(alternative); } - return std::move(std::get<T>(m_value)); + return std::move(*reinterpret_cast<T*>(m_data.data())); } [[nodiscard]] constexpr E&& takeError() { - return std::move(std::get<E>(m_value)); + fudAssert(isError()); + return std::move(*reinterpret_cast<E*>(m_data.data())); } [[nodiscard]] constexpr E&& takeErrorOr(E&& alternative) @@ -148,24 +250,80 @@ class [[nodiscard]] Result { if (!isError()) { return std::move(alternative); } - return std::move(std::get<E>(m_value)); + return std::move(*reinterpret_cast<E*>(m_data.data())); } private: - constexpr Result() : m_value() + constexpr Result() + requires(std::is_default_constructible_v<T>) + : m_data{}, m_discriminant{Discriminant::Okay} + { + auto ptrValue = new (m_data.data()) T(); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(const T& value) + requires(not std::is_convertible_v<T, E> and not std::is_convertible_v<E, T>) + : m_data{}, m_discriminant{Discriminant::Okay} + { + auto ptrValue = new (m_data.data()) T(value); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(T&& value) + requires(not std::is_convertible_v<T, E> and not std::is_convertible_v<E, T>) + : m_data{}, m_discriminant{Discriminant::Okay} { + auto ptrValue = new (m_data.data()) T(std::move(value)); + fudAssert(ptrValue != nullptr); } - std::variant<T, E> m_value; + constexpr Result(const E& value) + requires(not std::is_convertible_v<T, E> and not std::is_convertible_v<E, T>) + : m_data{}, m_discriminant{Discriminant::Error} + { + auto ptrValue = new (m_data.data()) E(value); + fudAssert(ptrValue != nullptr); + } + + constexpr Result(E&& value) + requires(not std::is_convertible_v<T, E> and not std::is_convertible_v<E, T>) + : m_data{}, m_discriminant{Discriminant::Error} + { + auto ptrValue = new (m_data.data()) E(std::move(value)); + fudAssert(ptrValue != nullptr); + } + + constexpr void destroy() noexcept + { + if (m_discriminant == Discriminant::Okay) { + reinterpret_cast<T*>(m_data.data())->~T(); + m_discriminant = Discriminant::Invalid; + } else if (m_discriminant == Discriminant::Error) { + reinterpret_cast<E*>(m_data.data())->~E(); + m_discriminant = Discriminant::Invalid; + } + } + + static constexpr auto Size = std::max(sizeof(T), sizeof(E)); + static constexpr auto Align = std::max(alignof(T), alignof(E)); + option_detail::DataArray<Size> m_data{}; + + enum class Discriminant : uint8_t + { + Invalid, + Okay, + Error, + } m_discriminant{Discriminant::Invalid}; }; -#define M_TakeOrReturn(HYGIENE_EXPRESSION) \ - ({ \ - auto HYGIENE_RESULT{(HYGIENE_EXPRESSION)}; \ - if (HYGIENE_RESULT.isError()) { \ - return HYGIENE_RESULT.takeError(); \ - } \ - HYGIENE_RESULT.takeOkay(); \ +#define M_TakeOrReturn(HYGIENE_RESULT_TYPE, HYGIENE_EXPRESSION) \ + ({ \ + auto HYGIENE_RESULT{(HYGIENE_EXPRESSION)}; \ + if (HYGIENE_RESULT.isError()) { \ + return HYGIENE_RESULT_TYPE::error(HYGIENE_RESULT.takeError()); \ + } \ + HYGIENE_RESULT.takeOkay(); \ }) } // namespace fud diff --git a/include/fud_string.hpp b/include/fud_string.hpp index 0020c67..cdc6b91 100644 --- a/include/fud_string.hpp +++ b/include/fud_string.hpp @@ -21,6 +21,7 @@ #include "fud_allocator.hpp" #include "fud_assert.hpp" #include "fud_c_string.hpp" +#include "fud_drain.hpp" #include "fud_option.hpp" #include "fud_result.hpp" #include "fud_status.hpp" @@ -36,22 +37,7 @@ static_assert(CHAR_BIT == 8); namespace fud { -struct DrainResult { - size_t bytesWritten; - FudStatus status; -}; - -/* TODO: make SSO_BUF_LENGTH user configurable. */ - -/** \brief The maximum length of a string using the small string optimization - * buffer. */ -constexpr size_t SSO_BUF_LENGTH = 15; - -/** \brief The size of the small string optimization buffer, to include space - * for the null terminator. */ -constexpr size_t SSO_BUF_SIZE = SSO_BUF_LENGTH + 1; - -static constexpr size_t SsoBufSize = 23; +constexpr size_t SsoBufSize = 23; class String; @@ -163,26 +149,26 @@ class String { fudAssert(totalLength < maxStringLength); String output{}; - output.m_allocator = allocator; + output.m_allocator = reinterpret_cast<uintptr_t>(allocator); utf8* data{nullptr}; size_t capacity = totalLength + 1; bool isLarge = capacity > SsoBufSize; if (isLarge) { - output.m_repr.large.capacity = capacity & largeStringCapacitymask; + output.m_repr.large.capacity = capacity; output.m_repr.large.length = totalLength; auto dataResult = output.allocator()->allocate(output.m_repr.large.capacity); if (dataResult.isError()) { return StringResult::error(dataResult.getError()); } output.m_repr.large.data = static_cast<utf8*>(dataResult.getOkay()); - output.m_repr.large.isLarge = 1; data = output.m_repr.large.data; + output.setLarge(); } else { capacity = SsoBufSize; static_assert(SsoBufSize < std::numeric_limits<int8_t>::max()); - output.m_repr.small.isLarge = 0; output.m_repr.small.length = static_cast<uint8_t>(totalLength) & smallStringLengthMask; data = output.m_repr.small.buffer.data(); + output.setSmall(); } fudAssert(data != nullptr); @@ -317,18 +303,19 @@ class String { private: static constexpr size_t maxStringLength = (static_cast<size_t>(1) << 63) - 1; - static constexpr size_t largeStringCapacitymask = (static_cast<size_t>(1) << 63) - 1; static constexpr uint8_t maxSmallStringLength = SsoBufSize; - static constexpr uint8_t smallStringLengthMask = 0x7F; + static constexpr uint8_t smallStringLengthMask = 0xFF; + static constexpr auto isLargeMask = static_cast<uintptr_t>(0x01); + static constexpr auto allocatorMask = ~isLargeMask; [[nodiscard]] static bool allocatorValid(Allocator* allocator) { - return allocator != nullptr; + return (reinterpret_cast<uintptr_t>(allocator) & isLargeMask) == 0; } Allocator* allocator() const { - return m_allocator; + return reinterpret_cast<Allocator*>(m_allocator & allocatorMask); } [[nodiscard]] bool nullTerminated() const; @@ -350,19 +337,17 @@ class String { /** \brief The allocator used to get storage for characters when the string * is large. */ - Allocator* m_allocator{&globalFudAllocator}; + uintptr_t m_allocator{reinterpret_cast<uintptr_t>(&globalFudAllocator)}; using BufType = Array<utf8, SsoBufSize>; union { struct { - uint8_t isLarge : 1; - size_t capacity : 63; + size_t capacity; size_t length; utf8* data; } large; struct { - uint8_t isLarge : 1 = 0; - uint8_t length : 7 = 0; + uint8_t length = 0; BufType buffer{}; } small{}; } m_repr{}; @@ -370,22 +355,17 @@ class String { /** \brief Whether or not the string must use its allocator for storage. */ [[nodiscard]] bool isLarge() const { - struct { - uint8_t isLarge : 1; - uint8_t length : 7; - } determinant; - copyMem<1>(determinant, m_repr); - return determinant.isLarge; + return (m_allocator & isLargeMask) != 0; } - [[nodiscard]] size_t smallLength() const + void setLarge() { - struct { - uint8_t isLarge : 1; - uint8_t length : 7; - } determinant; - copyMem<1>(determinant, m_repr); - return determinant.isLarge; + m_allocator |= isLargeMask; + } + + void setSmall() + { + m_allocator &= allocatorMask; } void addToLength(size_t augend) diff --git a/include/fud_string_convert.hpp b/include/fud_string_convert.hpp index 597c6a9..d7a62f0 100644 --- a/include/fud_string_convert.hpp +++ b/include/fud_string_convert.hpp @@ -111,12 +111,12 @@ StringConvertResult<T> unsignedFromString(StringView nextView, size_t skipIndex, static_assert(std::is_unsigned_v<T> && std::is_integral_v<T>); auto status = checkPlusSigned(nextView, skipIndex); if (status != FudStatus::Success) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } auto radixResult = impl::getRadix(nextView, skipIndex, specifiedRadixOption); if (radixResult.isError()) { - return radixResult.takeError(); + return FudError{radixResult.takeError()}; } auto radix = radixResult.takeOkay(); @@ -131,20 +131,20 @@ StringConvertResult<T> unsignedFromString(StringView nextView, size_t skipIndex, auto digit = static_cast<uint8_t>(digitResult); if (std::numeric_limits<T>::max() / radix < num) { - return FudStatus::RangeError; + return FudError{FudStatus::RangeError}; } num *= radix; if (std::numeric_limits<T>::max() - digit < num) { - return FudStatus::RangeError; + return FudError{FudStatus::RangeError}; } num += digit; digitIndex++; } if (digitIndex < 1) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } - return ConvertValue{skipIndex + digitIndex, num}; + return Okay{ConvertValue{skipIndex + digitIndex, num}}; } template <typename T> @@ -203,12 +203,12 @@ StringConvertResult<T> signedFromString(StringView nextView, size_t skipIndex, O static_assert(std::is_signed_v<T> && std::is_integral_v<T>); auto status = impl::checkPlusSigned(nextView, skipIndex); if (status != FudStatus::Success) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } auto radixResult = impl::getRadix(nextView, skipIndex, specifiedRadixOption); if (radixResult.isError()) { - return radixResult.takeError(); + return FudError{radixResult.takeError()}; } auto radix = radixResult.takeOkay(); @@ -217,7 +217,7 @@ StringConvertResult<T> signedFromString(StringView nextView, size_t skipIndex, O auto isNegativeResult = checkNegative(nextView, skipIndex); if (isNegativeResult.isError()) { - return isNegativeResult.takeError(); + return FudError{isNegativeResult.takeError()}; } const auto isNegative = isNegativeResult.takeOkay(); @@ -228,14 +228,14 @@ StringConvertResult<T> signedFromString(StringView nextView, size_t skipIndex, O } if (status != FudStatus::Success) { - return status; + return FudError{status}; } if (digitIndex < 1) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } - return ConvertValue{skipIndex + digitIndex, num}; + return Okay{ConvertValue{skipIndex + digitIndex, num}}; } template <typename T> @@ -343,21 +343,22 @@ FudStatus getFraction(const StringView view, size_t& digitIndex, T& num, T sign, template <typename T> StringConvertResult<T> floatFromString(StringView nextView, size_t skipIndex, Option<uint8_t> specifiedRadixOption) { + using RetType = StringConvertResult<T>; static_assert(std::is_floating_point_v<T>); if (nextView.length() < 1) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } auto isNegativeResult = checkNegative(nextView, skipIndex); if (isNegativeResult.isError()) { - return isNegativeResult.takeError(); + return FudError{isNegativeResult.takeError()}; } const auto isNegative = isNegativeResult.takeOkay(); if (!isNegative) { auto status = checkPlusSigned(nextView, skipIndex); if (status != FudStatus::Success) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } } T sign = isNegative ? -1.0 : 1.0; @@ -365,7 +366,7 @@ StringConvertResult<T> floatFromString(StringView nextView, size_t skipIndex, Op T num = 0; size_t digitIndex = 0; - auto retSuccess = [&]() { return ConvertValue{skipIndex + digitIndex, num}; }; + auto retSuccess = [&]() { return RetType::okay(ConvertValue{skipIndex + digitIndex, num}); }; if (impl::isNanOrInf(num, nextView, sign, digitIndex)) { return retSuccess(); @@ -373,7 +374,7 @@ StringConvertResult<T> floatFromString(StringView nextView, size_t skipIndex, Op auto radixResult = impl::getRadix(nextView, skipIndex, specifiedRadixOption); if (radixResult.isError()) { - return radixResult.takeError(); + return FudError{radixResult.takeError()}; } auto radix = radixResult.takeOkay(); @@ -386,12 +387,12 @@ StringConvertResult<T> floatFromString(StringView nextView, size_t skipIndex, Op } if (status != FudStatus::Success) { - return status; + return FudError{status}; } if (!foundDecimal) { if (digitIndex < 1) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } return retSuccess(); @@ -402,17 +403,17 @@ StringConvertResult<T> floatFromString(StringView nextView, size_t skipIndex, Op if (foundExponent) { status = getExponent(nextView, digitIndex, num, radix); if (status != FudStatus::Success) { - return status; + return FudError{status}; } } if (digitIndex < 1) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } if (std::isinf(num) || std::isnan(num)) // isnan is dubious here - likely unreachable { - return FudStatus::RangeError; + return FudError{FudStatus::RangeError}; } return retSuccess(); @@ -430,13 +431,13 @@ template <typename T> StringConvertResult<T> fromString(StringView inputView, Option<uint8_t> specifiedRadixOption) { if (inputView.data() == nullptr) { - return FudStatus::NullPointer; + return FudError{FudStatus::NullPointer}; } StringView nextView{inputView}; auto skipResult = nextView.skipWhitespace(); if (skipResult.isError()) { - return skipResult.takeError(); + return FudError{skipResult.takeError()}; } size_t skipIndex = skipResult.takeOkay(); @@ -447,7 +448,7 @@ StringConvertResult<T> fromString(StringView inputView, Option<uint8_t> specifie } else if constexpr (std::is_floating_point_v<T>) { return impl::floatFromString<T>(nextView, skipIndex, specifiedRadixOption); } else { - return FudStatus::NotImplemented; + return FudError{FudStatus::NotImplemented}; } } diff --git a/include/fud_utf8.hpp b/include/fud_utf8.hpp index 31c215a..3d53feb 100644 --- a/include/fud_utf8.hpp +++ b/include/fud_utf8.hpp @@ -25,6 +25,7 @@ #include <cstdint> #include <type_traits> +#include <variant> namespace fud { diff --git a/include/fud_vector.hpp b/include/fud_vector.hpp index f90819a..2b5de9a 100644 --- a/include/fud_vector.hpp +++ b/include/fud_vector.hpp @@ -257,62 +257,68 @@ class Vector { Result<Span<const T>, FudStatus> span() const { + using RetType = Result<Span<const T>, FudStatus>; if (m_data == nullptr) { - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); } - return Span{m_data, m_length}; + return RetType::okay(Span{m_data, m_length}); } Result<Span<T>, FudStatus> span() { + using RetType = Result<Span<T>, FudStatus>; if (m_data == nullptr) { - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); } - return Span{m_data, m_length}; + return RetType::okay(Span{m_data, m_length}); } Result<Span<const T>, FudStatus> span(size_t count) const { + using RetType = Result<Span<const T>, FudStatus>; if (m_data == nullptr) { - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); } if (count > m_length) { - return FudStatus::ArgumentInvalid; + return RetType::error(FudStatus::ArgumentInvalid); } - return Span{m_data, count}; + return RetType::okay(Span{m_data, count}); } Result<Span<T>, FudStatus> span(size_t count) { + using RetType = Result<Span<T>, FudStatus>; if (m_data == nullptr) { - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); } if (count > m_length) { - return FudStatus::ArgumentInvalid; + return RetType::error(FudStatus::ArgumentInvalid); } - return Span{m_data, count}; + return RetType::okay(Span{m_data, count}); } Result<Span<const T>, FudStatus> span(size_t start, size_t count) const { + using RetType = Result<Span<const T>, FudStatus>; if (m_data == nullptr) { - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); } if (SIZE_MAX - start < m_length || start + count > m_length) { - return FudStatus::ArgumentInvalid; + return RetType::error(FudStatus::ArgumentInvalid); } - return Span{m_data + start, count}; + return RetType::okay(Span{m_data + start, count}); } Result<Span<T>, FudStatus> span(size_t start, size_t count) { + using RetType = Result<Span<T>, FudStatus>; if (m_data == nullptr) { - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); } if (SIZE_MAX - start < m_length || start + count > m_length) { - return FudStatus::ArgumentInvalid; + return RetType::error(FudStatus::ArgumentInvalid); } - return Span{m_data + start, count}; + return RetType::okay(Span{m_data + start, count}); } FudStatus reserve(size_t count) @@ -342,10 +348,15 @@ class Vector { m_data[index].~T(); } + auto status = FudStatus::Success; + if (m_capacity > 0) { + status = m_allocator->deallocate(m_data, m_capacity); + } + m_data = dataPtr; m_capacity = count; - return FudStatus::Success; + return status; } FudStatus resize(size_t count) @@ -394,24 +405,26 @@ class Vector { Result<std::reference_wrapper<T>, FudStatus> get(size_t index) { + using RetType = Result<std::reference_wrapper<T>, FudStatus>; if (m_data == nullptr) { - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); } if (index >= m_length) { - return FudStatus::IndexInvalid; + return RetType::error(FudStatus::IndexInvalid); } - return std::ref(m_data[index]); + return RetType::okay(std::ref(m_data[index])); } Result<const std::reference_wrapper<const T>, FudStatus> ref(size_t index) const { + using RetType = Result<const std::reference_wrapper<const T>, FudStatus>; if (m_data == nullptr) { - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); } if (index >= m_length) { - return FudStatus::IndexInvalid; + return RetType::error(FudStatus::IndexInvalid); } - return std::cref(m_data[index]); + return RetType::okay(std::cref(m_data[index])); } constexpr Option<T&> front() @@ -527,13 +540,14 @@ class Vector { Result<T, FudStatus> popBack() { + using RetType = Result<T, FudStatus>; if (m_data == nullptr) { - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); } if (m_length == 0) { - return FudStatus::Empty; + return RetType::error(FudStatus::Empty); } - auto result{std::move(m_data[m_length - 1])}; + auto result{RetType::okay({std::move(m_data[m_length - 1])})}; m_length--; m_data[m_length].~T(); return result; diff --git a/source/fud_directory.cpp b/source/fud_directory.cpp index 1dc1aba..7dcf911 100644 --- a/source/fud_directory.cpp +++ b/source/fud_directory.cpp @@ -146,7 +146,7 @@ Result<DirectoryEntry, FudStatus> Directory::info() m_errorCode = 0; } - return retValue; + return RetType::okay(std::move(retValue)); } Result<Option<DirectoryEntry>, FudStatus> Directory::getNextEntry() diff --git a/source/fud_file.cpp b/source/fud_file.cpp index 8dab031..8f84648 100644 --- a/source/fud_file.cpp +++ b/source/fud_file.cpp @@ -34,11 +34,11 @@ FileResult RegularFile::open( Allocator* allocator) { if (allocator == nullptr) { - return FudStatus::NullPointer; + return FileResult::error(FudStatus::NullPointer); } if (!filename.nullTerminated()) { - return FudStatus::ArgumentInvalid; + return FileResult::error(FudStatus::ArgumentInvalid); } int dirFd = dirFdOption.valueOr(AT_FDCWD); @@ -57,11 +57,11 @@ FileResult RegularFile::open( openFlags = O_RDWR; break; default: - return FudStatus::ArgumentInvalid; + return FileResult::error(FudStatus::ArgumentInvalid); } if (flags.hasFlag(OpenFlagEnum::Append) && flags.hasFlag(OpenFlagEnum::Truncate)) { - return FudStatus::OperationInvalid; + return FileResult::error(FudStatus::OperationInvalid); } openFlags |= flags.flags(); @@ -74,31 +74,31 @@ FileResult RegularFile::open( auto status = syscall(SYS_openat2, dirFd, filename.data(), &openHow, sizeof(openHow)); if (status == -1) { if constexpr (EAGAIN != EWOULDBLOCK && status == EWOULDBLOCK) { - return FudStatus::Partial; + return FileResult::error(FudStatus::Partial); } switch (errno) { case ETXTBSY: case EAGAIN: - return FudStatus::Partial; + return FileResult::error(FudStatus::Partial); case ENOENT: - return FudStatus::NotFound; + return FileResult::error(FudStatus::NotFound); case EBADF: case EFBIG: case EOVERFLOW: case EINVAL: case EISDIR: case ENAMETOOLONG: - return FudStatus::ArgumentInvalid; + return FileResult::error(FudStatus::ArgumentInvalid); case EROFS: case EACCES: case EPERM: - return FudStatus::PermissionDenied; + return FileResult::error(FudStatus::PermissionDenied); case ELOOP: case EXDEV: case ENFILE: case E2BIG: default: - return FudStatus::Failure; + return FileResult::error(FudStatus::Failure); } } fudAssert(status <= std::numeric_limits<decltype(file.m_fd)>::max()); @@ -108,14 +108,14 @@ FileResult RegularFile::open( Stat sBuffer{}; auto fStatus = fstat(file.m_fd, &sBuffer); if (fStatus == -1) { - return FudStatus::Failure; + return FileResult::error(FudStatus::Failure); } if ((sBuffer.st_mode & S_IFMT) != S_IFREG) { - return FudStatus::ObjectInvalid; + return FileResult::error(FudStatus::ObjectInvalid); } - return file; + return FileResult::okay(std::move(file)); } FileResult RegularFile::create( @@ -128,11 +128,11 @@ FileResult RegularFile::create( Allocator* allocator) { if (allocator == nullptr) { - return FudStatus::NullPointer; + return FileResult::error(FudStatus::NullPointer); } if (!filename.nullTerminated()) { - return FudStatus::ArgumentInvalid; + return FileResult::error(FudStatus::ArgumentInvalid); } int dirFd = dirFdOption.valueOr(AT_FDCWD); @@ -151,11 +151,11 @@ FileResult RegularFile::create( openFlags = O_RDWR; break; default: - return FudStatus::ArgumentInvalid; + return FileResult::error(FudStatus::ArgumentInvalid); } if (flags.hasFlag(OpenFlagEnum::Append) && flags.hasFlag(OpenFlagEnum::Truncate)) { - return FudStatus::OperationInvalid; + return FileResult::error(FudStatus::OperationInvalid); } openFlags |= flags.flags() | O_CREAT | (O_EXCL * static_cast<uint8_t>(exclusive)); @@ -169,23 +169,23 @@ FileResult RegularFile::create( auto status = syscall(SYS_openat2, dirFd, filename.data(), &openHow, sizeof(openHow)); if (status == -1) { if constexpr (EAGAIN != EWOULDBLOCK && status == EWOULDBLOCK) { - return FudStatus::Partial; + return FileResult::error(FudStatus::Partial); } switch (errno) { case ETXTBSY: case EAGAIN: - return FudStatus::Partial; + return FileResult::error(FudStatus::Partial); case EBADF: case EFBIG: case EOVERFLOW: case EINVAL: case EISDIR: case ENAMETOOLONG: - return FudStatus::ArgumentInvalid; + return FileResult::error(FudStatus::ArgumentInvalid); case EROFS: case EACCES: case EPERM: - return FudStatus::PermissionDenied; + return FileResult::error(FudStatus::PermissionDenied); case EDQUOT: case ENOENT: case ELOOP: @@ -193,7 +193,7 @@ FileResult RegularFile::create( case ENFILE: case E2BIG: default: - return FudStatus::Failure; + return FileResult::error(FudStatus::Failure); } } fudAssert(status <= std::numeric_limits<decltype(file.m_fd)>::max()); @@ -203,14 +203,14 @@ FileResult RegularFile::create( Stat sBuffer{}; auto fStatus = fstat(file.m_fd, &sBuffer); if (fStatus == -1) { - return FudStatus::Failure; + return FileResult::error(FudStatus::Failure); } if ((sBuffer.st_mode & S_IFMT) != S_IFREG) { - return FudStatus::ObjectInvalid; + return FileResult::error(FudStatus::ObjectInvalid); } - return file; + return FileResult::okay(std::move(file)); } RegularFile::~RegularFile() @@ -284,23 +284,24 @@ FudStatus RegularFile::close() Result<size_t, FudStatus> RegularFile::size() const { + using RetType = Result<size_t, FudStatus>; auto fileSize = lseek(m_fd, 0, SEEK_END); if (fileSize == -1) { switch (errno) { case EBADF: case ESPIPE: - return FudStatus::ObjectInvalid; + return RetType::error(FudStatus::ObjectInvalid); default: - return FudStatus::Failure; + return RetType::error(FudStatus::Failure); } } auto seekBegin = lseek(m_fd, 0, SEEK_SET); if (seekBegin == -1) { - return FudStatus::Failure; + return RetType::error(FudStatus::Failure); } - return static_cast<size_t>(fileSize); + return RetType::okay(static_cast<size_t>(fileSize)); } } // namespace fud diff --git a/source/fud_format.cpp b/source/fud_format.cpp index 960a146..f1fc3cf 100644 --- a/source/fud_format.cpp +++ b/source/fud_format.cpp @@ -25,8 +25,9 @@ FudStatus getFormatSign(StringView& formatView, FormatSpec& spec); Result<FormatSpec, FudStatus> FormatSpec::parse(StringView formatView, size_t& specLength) { + using RetType = Result<FormatSpec, FudStatus>; if (formatView.length() < 2 || formatView[0] != FormatSpec::openBracket) { - return FudStatus::ArgumentInvalid; + return RetType::error(FudStatus::ArgumentInvalid); } auto hasEnded = [](StringView& view) { @@ -43,82 +44,82 @@ Result<FormatSpec, FudStatus> FormatSpec::parse(StringView formatView, size_t& s bool hasColon{false}; auto lengthResult = impl::preScanSpec(formatView, spec, hasPosition, hasColon); if (lengthResult.isError()) { - return lengthResult.takeError(); + return RetType::error(lengthResult.takeError()); } length += lengthResult.takeOkay(); if (length < 1) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } specLength = length; auto status = impl::getPosition(formatView, spec, hasPosition); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } // check early ending if (!hasColon) { if (hasEnded(formatView)) { - return spec; + return RetType::okay(spec); } - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } // check validity for being non-default spec and advance if (formatView[0] != ':') { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } formatView.advanceUnsafe(); if (hasEnded(formatView)) { - return spec; + return RetType::okay(spec); } // Spec status = impl::getFill(formatView, spec); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } if (hasEnded(formatView)) { - return spec; + return RetType::okay(spec); } // sign, alternate, leading zero status = impl::getSpecialOpts(formatView, spec); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } if (hasEnded(formatView)) { - return spec; + return RetType::okay(spec); } // Width status = impl::getWidth(formatView, spec); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } if (hasEnded(formatView)) { - return spec; + return RetType::okay(spec); } // Precision status = impl::getPrecision(formatView, spec); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } if (hasEnded(formatView)) { - return spec; + return RetType::okay(spec); } // Locale @@ -129,26 +130,27 @@ Result<FormatSpec, FudStatus> FormatSpec::parse(StringView formatView, size_t& s } if (hasEnded(formatView)) { - return spec; + return RetType::okay(spec); } // Format Sign status = impl::getFormatSign(formatView, spec); if (status != FudStatus::Success) { - return status; + return RetType::error(status); } if (!hasEnded(formatView)) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } - return spec; + return RetType::okay(spec); } namespace impl { Result<size_t, FudStatus> preScanSpec(StringView formatView, FormatSpec& spec, bool& hasPosition, bool& hasColon) { + using RetType = Result<size_t, FudStatus>; int nesting = 0; uint32_t captureGroups = 0; size_t index = 0; @@ -162,14 +164,14 @@ Result<size_t, FudStatus> preScanSpec(StringView formatView, FormatSpec& spec, b switch (letter) { case FormatSpec::openBracket: if (not hasColon) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } if (takesWidth) { spec.takesWidth = true; } else if (takesPrecision) { spec.takesPrecision = true; } else { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } nesting++; captureGroups++; @@ -193,7 +195,7 @@ Result<size_t, FudStatus> preScanSpec(StringView formatView, FormatSpec& spec, b } if (nesting > 1) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } index++; @@ -201,10 +203,10 @@ Result<size_t, FudStatus> preScanSpec(StringView formatView, FormatSpec& spec, b if (nesting != 0 || captureGroups > 2 || index >= formatView.length() || formatView[index] != FormatSpec::closeBracket) { - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); } - return index + 1U; + return RetType::okay(index + 1U); } FudStatus getPosition(StringView& formatView, FormatSpec& spec, bool& hasPosition) @@ -570,6 +572,7 @@ FudStatus fillUnsignedBuffer( [[nodiscard]] Result<bool, FudStatus> validateFloatFormatType(FormatType formatType) { + using RetType = Result<bool, FudStatus>; auto uppercase = false; switch (formatType) { @@ -604,13 +607,13 @@ FudStatus fillUnsignedBuffer( case FormatType::BinaryUpper: case FormatType::String: case FormatType::Escaped: - return FudStatus::FormatInvalid; + return RetType::error(FudStatus::FormatInvalid); break; default: - return FudStatus::Failure; + return RetType::error(FudStatus::Failure); } - return uppercase; + return RetType::okay(uppercase); } ExponentBuffer getScientificExponent(int exponent, uint8_t& exponentLength, bool uppercase) diff --git a/source/fud_string.cpp b/source/fud_string.cpp index 4943dac..edb25da 100644 --- a/source/fud_string.cpp +++ b/source/fud_string.cpp @@ -51,27 +51,27 @@ StringResult String::makeFromCString(const char* cString, Allocator* allocator) } String output{}; - output.m_allocator = allocator; + output.m_allocator = reinterpret_cast<uintptr_t>(allocator); utf8* data{nullptr}; size_t capacity = length + 1; bool isLarge = capacity > SsoBufSize; if (isLarge) { - output.m_repr.large.capacity = capacity & largeStringCapacitymask; + output.m_repr.large.capacity = capacity; output.m_repr.large.length = length; auto dataResult = output.allocator()->allocate(output.m_repr.large.capacity); if (dataResult.isError()) { return StringResult::error(dataResult.getError()); } output.m_repr.large.data = static_cast<utf8*>(dataResult.getOkay()); - output.m_repr.large.isLarge = 1; data = output.m_repr.large.data; + output.setLarge(); } else { capacity = SsoBufSize; static_assert(SsoBufSize < std::numeric_limits<int8_t>::max()); - output.m_repr.small.isLarge = 0; output.m_repr.small.length = static_cast<uint8_t>(length) & smallStringLengthMask; data = output.m_repr.small.buffer.data(); + output.setSmall(); } fudAssert(data != nullptr); @@ -99,7 +99,7 @@ StringResult String::from(const String& rhs) if (rhs.isLarge()) { output.m_repr.large = rhs.m_repr.large; output.m_repr.large.data = static_cast<utf8*>( - M_TakeOrReturn(output.allocator()->allocate(output.m_repr.large.capacity))); + M_TakeOrReturn(StringResult, output.allocator()->allocate(output.m_repr.large.capacity))); data = output.m_repr.large.data; capacity = output.m_repr.large.capacity; length = output.m_repr.large.length; @@ -135,22 +135,22 @@ StringResult String::from(StringView view, Allocator* allocator) } String output{}; - output.m_allocator = allocator; + output.m_allocator = reinterpret_cast<uintptr_t>(allocator); size_t capacity = view.length() + 1U; bool isLarge = capacity > SsoBufSize; utf8* data{nullptr}; if (isLarge) { - output.m_repr.large.capacity = capacity & largeStringCapacitymask; + output.m_repr.large.capacity = capacity; output.m_repr.large.length = view.length(); - output.m_repr.large.data = static_cast<utf8*>(M_TakeOrReturn(output.allocator()->allocate(capacity))); - output.m_repr.large.isLarge = 1; + output.m_repr.large.data = static_cast<utf8*>(M_TakeOrReturn(StringResult, output.allocator()->allocate(capacity))); data = output.m_repr.large.data; + output.setLarge(); } else { capacity = SsoBufSize; static_assert(SsoBufSize < std::numeric_limits<int8_t>::max()); - output.m_repr.small.isLarge = 0; output.m_repr.small.length = static_cast<uint8_t>(view.length()) & smallStringLengthMask; data = output.m_repr.small.buffer.data(); + output.setSmall(); } fudAssert(data != nullptr); auto copyStatus = copyMem(data, capacity, view.m_data, view.length()); @@ -194,7 +194,11 @@ FudStatus String::copy(const String& rhs) size_t length{}; if (isLarge()) { - m_repr.large.data = static_cast<utf8*>(M_TakeOrReturn(allocator()->allocate(m_repr.large.capacity))); + auto allocResult = allocator()->allocate(m_repr.large.capacity); + if (allocResult.isError()) { + return allocResult.takeError(); + } + m_repr.large.data = static_cast<utf8*>(allocResult.takeOkay()); capacity = m_repr.large.capacity; length = m_repr.large.length; data = m_repr.large.data; @@ -251,11 +255,11 @@ FudStatus String::resize(size_t newCapacity) return FudStatus::OperationInvalid; } - if (!isLarge() && newCapacity <= SSO_BUF_SIZE) { + if (!isLarge() && newCapacity <= SsoBufSize) { return FudStatus::Success; } - if (newCapacity <= SSO_BUF_SIZE) { + if (newCapacity <= SsoBufSize) { fudAssert(isLarge()); auto len = static_cast<uint8_t>(length()); @@ -264,7 +268,7 @@ FudStatus String::resize(size_t newCapacity) fudAssert(copyResult == FudStatus::Success); auto deallocStatus = allocator()->deallocate(m_repr.large.data, m_repr.large.capacity); - m_repr.small.isLarge = 0; + setSmall(); m_repr.small.length = len & smallStringLengthMask; copyMem(m_repr.small.buffer, temp); m_repr.small.buffer[len] = '\0'; @@ -272,7 +276,11 @@ FudStatus String::resize(size_t newCapacity) return deallocStatus != FudStatus::Success ? FudStatus::DeallocFailure : FudStatus::Success; } - auto* newData = static_cast<utf8*>(M_TakeOrReturn(allocator()->allocate(newCapacity))); + auto allocResult = allocator()->allocate(newCapacity); + if (allocResult.isError()) { + return allocResult.takeError(); + } + auto* newData = static_cast<utf8*>(allocResult.takeOkay()); fudAssert(newData != nullptr); auto copyResult = copyMem(newData, newCapacity, data(), length()); @@ -285,8 +293,8 @@ FudStatus String::resize(size_t newCapacity) size_t len = length(); - m_repr.large.isLarge = 1; - m_repr.large.capacity = newCapacity & largeStringCapacitymask; + setLarge(); + m_repr.large.capacity = newCapacity; m_repr.large.data = newData; m_repr.large.length = len; m_repr.large.data[m_repr.large.length] = '\0'; @@ -622,19 +630,19 @@ StringResult String::catenate(const char* rhs) const size_t outputCapacity = outputLength + 1; utf8* outputData{nullptr}; if (outputCapacity > SsoBufSize) { - output.m_repr.large.capacity = outputCapacity & largeStringCapacitymask; + output.m_repr.large.capacity = outputCapacity; output.m_repr.large.length = outputLength; auto dataResult = output.allocator()->allocate(output.m_repr.large.capacity); if (dataResult.isError()) { return StringResult::error(dataResult.getError()); } output.m_repr.large.data = static_cast<utf8*>(dataResult.getOkay()); - output.m_repr.large.isLarge = 1; + output.setLarge(); outputData = output.m_repr.large.data; } else { outputCapacity = SsoBufSize; static_assert(SsoBufSize < std::numeric_limits<int8_t>::max()); - output.m_repr.small.isLarge = 0; + output.setSmall(); output.m_repr.small.length = static_cast<uint8_t>(outputLength) & smallStringLengthMask; outputData = output.m_repr.small.buffer.data(); } @@ -669,19 +677,19 @@ StringResult String::catenate(const String& rhs) const size_t outputCapacity = outputLength + 1; utf8* outputData{nullptr}; if (outputCapacity > SsoBufSize) { - output.m_repr.large.capacity = outputCapacity & largeStringCapacitymask; + output.m_repr.large.capacity = outputCapacity; output.m_repr.large.length = outputLength; auto dataResult = output.allocator()->allocate(output.m_repr.large.capacity); if (dataResult.isError()) { return StringResult::error(dataResult.getError()); } output.m_repr.large.data = static_cast<utf8*>(dataResult.getOkay()); - output.m_repr.large.isLarge = 1; + output.setLarge(); outputData = output.m_repr.large.data; } else { outputCapacity = SsoBufSize; static_assert(SsoBufSize < std::numeric_limits<int8_t>::max()); - output.m_repr.small.isLarge = 0; + output.setSmall(); output.m_repr.small.length = static_cast<uint8_t>(outputLength) & smallStringLengthMask; outputData = output.m_repr.small.buffer.data(); } diff --git a/source/fud_string_convert.cpp b/source/fud_string_convert.cpp index 428ab36..fdf3436 100644 --- a/source/fud_string_convert.cpp +++ b/source/fud_string_convert.cpp @@ -36,59 +36,60 @@ Result<bool, FudStatus> checkNegative(StringView& view, size_t& skipIndex) { bool isNegative = view.data()[0] == '-'; if (isNegative && view.length() == 1) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } if (isNegative) { skipIndex += 1; view.advanceUnsafe(); } - return isNegative; + return Okay<bool>{isNegative}; } Result<Radix, FudStatus> determineRadix(StringView input, size_t& index) { if (input.length() < 1) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } if (input.length() == 1 && input.data()[0] == '0') { - return Radix::Octal; + return Okay<Radix>{Radix::Octal}; } if (input.length() == 1) { - return Radix::Decimal; + return Okay<Radix>{Radix::Decimal}; } if (input.data()[0] == '0' && (input.data()[1] == 'x' || input.data()[1] == 'X')) { index += 2; - return Radix::Hexadecimal; + return Okay<Radix>{Radix::Hexadecimal}; } if (input.data()[0] == '0') { auto nextChar = input.data()[1]; auto nextVal = AsciiLookup[nextChar]; if (nextVal >= 0 && nextVal < static_cast<uint8_t>(Radix::Octal)) { - return Radix::Octal; + return Okay<Radix>{Radix::Octal}; } if (nextVal >= static_cast<uint8_t>(Radix::Octal)) { - return FudStatus::ArgumentInvalid; + return FudError{FudStatus::ArgumentInvalid}; } } - return Radix::Decimal; + return Okay<Radix>{Radix::Decimal}; } Result<uint8_t, FudStatus> getRadix(StringView& view, size_t& skipIndex, Option<uint8_t> specifiedRadixOption) { + using RetType = Result<uint8_t, FudStatus>; if (specifiedRadixOption.isNone()) { size_t radixIndex = 0; auto status = determineRadix(view, radixIndex); if (status.isOkay()) { skipIndex += radixIndex; view.advanceUnsafe(radixIndex); - return static_cast<uint8_t>(status.takeOkay()); + return RetType::okay(static_cast<uint8_t>(status.takeOkay())); } - return status.takeError(); + return RetType::error(status); } auto radix = specifiedRadixOption.value(); @@ -104,7 +105,7 @@ Result<uint8_t, FudStatus> getRadix(StringView& view, size_t& skipIndex, Option< view.advanceUnsafe(2); } - return radix; + return RetType::okay(radix); } } // namespace fud::impl diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index aef8052..1ceca71 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,7 +7,7 @@ else() endif() if(FUD_SAN) -set(CVG_FLAGS ${CVG_FLAGS} -fsanitize=address -fsanitize=undefined) +set(CVG_FLAGS ${CVG_FLAGS} -fsanitize=address -fsanitize=undefined -fno-sanitize=vptr) endif() set(gtest_URL https://github.com/google/googletest.git) diff --git a/test/test_file.cpp b/test/test_file.cpp index 06e6bcc..27844d8 100644 --- a/test/test_file.cpp +++ b/test/test_file.cpp @@ -75,7 +75,7 @@ TEST(FudFile, Basic) }; ASSERT_GE(createFile(testName1), 0); - fileResult = RegularFile::open(testName1.asView(), FileAccessMode::Read, OpenFlags{}, NullOpt); + fileResult = std::move(RegularFile::open(testName1.asView(), FileAccessMode::Read, OpenFlags{}, NullOpt)); ASSERT_TRUE(fileResult.isOkay()); auto file{fileResult.takeOkay()}; ASSERT_EQ(file.close(), FudStatus::Success); diff --git a/test/test_fud.cpp b/test/test_fud.cpp index 72d569a..f84ad20 100644 --- a/test/test_fud.cpp +++ b/test/test_fud.cpp @@ -51,7 +51,7 @@ TEST(FudTest, GetEnv) ASSERT_TRUE(fudVarResult.isError()); ASSERT_EQ(fudVarResult.getError(), FudStatus::NullPointer); - fudVarResult = getEnv(testVarName); + fudVarResult = std::move(getEnv(testVarName)); ASSERT_TRUE(fudVarResult.isError()); ASSERT_EQ(fudVarResult.getError(), FudStatus::NotFound); diff --git a/test/test_result.cpp b/test/test_result.cpp index 5eadd8c..414941a 100644 --- a/test/test_result.cpp +++ b/test/test_result.cpp @@ -15,8 +15,8 @@ * limitations under the License. */ -#include "fud_status.hpp" #include "fud_result.hpp" +#include "fud_status.hpp" #include "gtest/gtest.h" @@ -49,4 +49,9 @@ TEST(ResultTest, ErrResult) ASSERT_EQ(err2.getError(), FudStatus::ArgumentInvalid); } +TEST(ResultTest, ImplicitConversion) +{ + // Result<int, int16_t> foo = Result<int, int16_t>::okay(-1); +} + } // namespace fud diff --git a/test/test_utf8.cpp b/test/test_utf8.cpp index 5447954..8c3cad2 100644 --- a/test/test_utf8.cpp +++ b/test/test_utf8.cpp @@ -137,13 +137,14 @@ TEST(Utf8Test, Utf8MultiByte) virtual Result<void*, FudStatus> allocate(size_t bytes, size_t alignment) override final { + using RetType = Result<void*, FudStatus>; static_cast<void>(alignment); if (bytes > m_memory.size() - m_allocated) { - return FudStatus::AllocFailure; + return RetType::error(FudStatus::AllocFailure); } auto* data = m_memory.data() + m_allocated; m_allocated += bytes; - return data; + return RetType::okay(data); } virtual FudStatus deallocate(void* pointer, size_t bytes) override final diff --git a/test/test_vector.cpp b/test/test_vector.cpp index b4fd83c..cadeaa6 100644 --- a/test/test_vector.cpp +++ b/test/test_vector.cpp @@ -98,7 +98,8 @@ TEST(VectorTest, NonTrivialVector) ASSERT_GE(nonTrivialVector.capacity(), 11); ASSERT_EQ(counter, 11); auto capacity = nonTrivialVector.capacity(); - ASSERT_EQ(nonTrivialVector.reserve(SIZE_MAX / sizeof(NonTrivial)), FudStatus::AllocFailure); + constexpr auto FAIL_RESERVE_SIZE = 0x10000000000 / sizeof(NonTrivial); + ASSERT_EQ(nonTrivialVector.reserve(FAIL_RESERVE_SIZE), FudStatus::AllocFailure); ASSERT_EQ(nonTrivialVector.capacity(), capacity); { |