summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2024-09-22 10:19:15 -0500
committerDominick Allen <djallen@librehumanitas.org>2024-09-22 10:19:15 -0500
commitbf81e34921e3e30b05313efbcf5c9fa839cb7c05 (patch)
treeb56a343e59164bc347232669e8bb808cf3c4f4ef
Initial commit.
-rw-r--r--.clang-format201
-rw-r--r--.clang-tidy33
-rw-r--r--.gitignore11
-rw-r--r--CMakeLists.txt50
-rw-r--r--LICENSE196
-rw-r--r--README.org1
-rw-r--r--cmake/warnings.cmake66
-rw-r--r--include/array.hpp113
-rw-r--r--include/c_file.hpp103
-rw-r--r--include/fud_type_traits.hpp80
-rw-r--r--include/libfud.hpp30
-rw-r--r--include/memory.hpp140
-rw-r--r--include/result.hpp83
-rw-r--r--include/status.hpp106
-rw-r--r--include/string.hpp153
-rw-r--r--include/unique_array.hpp68
-rw-r--r--include/utf8.hpp557
-rw-r--r--include/utf8_iterator.hpp39
-rw-r--r--source/c_file.cpp60
-rw-r--r--source/libfud.cpp24
-rw-r--r--source/memory.cpp127
-rw-r--r--source/string.cpp19
-rw-r--r--source/utf8.cpp343
-rw-r--r--source/utf8_iterator.cpp38
24 files changed, 2641 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..689c8e6
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,201 @@
+---
+Language: Cpp
+# BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: AlwaysBreak
+AlignArrayOfStructures: None
+AlignConsecutiveAssignments:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: true
+AlignConsecutiveBitFields:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveDeclarations:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveMacros:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignEscapedNewlines: Left
+AlignOperands: false
+AllowAllArgumentsOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortEnumsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLambdasOnASingleLine: All
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: Yes
+AttributeMacros:
+ - __capability
+BinPackArguments: false
+BinPackParameters: false
+BitFieldColonSpacing: Both
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: false
+ AfterControlStatement: Never
+ AfterEnum: true
+ AfterExternBlock: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakAfterJavaFieldAnnotations: false
+BreakBeforeBinaryOperators: None
+BreakBeforeConceptDeclarations: Always
+BreakBeforeBraces: Custom
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: AfterColon
+BreakInheritanceList: BeforeColon
+BreakStringLiterals: true
+ColumnLimit: 120
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IfMacros:
+ - KJ_IF_MAYBE
+IncludeBlocks: Regroup
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '^(<|"(gtest|gmock|isl|json)/)'
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: '.*'
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: '(Test)?$'
+IncludeIsMainSourceRegex: ''
+IndentAccessModifiers: false
+IndentCaseBlocks: false
+IndentCaseLabels: false
+IndentExternBlock: AfterExternBlock
+IndentGotoLabels: true
+IndentPPDirectives: None
+IndentRequiresClause: true
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+InsertBraces: false
+InsertTrailingCommas: None
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+LambdaBodyIndentation: Signature
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PackConstructorInitializers: BinPack
+PenaltyBreakAssignment: 1000000
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakOpenParenthesis: 0
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyIndentedWhitespace: 1
+PenaltyReturnTypeOnItsOwnLine: 1000000
+PointerAlignment: Left
+ReferenceAlignment: Left
+PPIndentWidth: -1
+QualifierAlignment: Leave
+ReflowComments: true
+RemoveBracesLLVM: false
+RequiresClausePosition: OwnLine
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
+SortIncludes: CaseSensitive
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: true
+SpaceAroundPointerQualifiers: Default
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeParensOptions:
+ AfterControlStatements: true
+ AfterForeachMacros: true
+ AfterFunctionDefinitionName: false
+ AfterFunctionDeclarationName: false
+ AfterIfMacros: true
+ AfterOverloadedOperator: false
+ AfterRequiresInClause: false
+ AfterRequiresInExpression: false
+ BeforeNonEmptyParentheses: false
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: Never
+SpacesInContainerLiterals: true
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
+SpacesInSquareBrackets: false
+Standard: Latest
+StatementAttributeLikeMacros:
+ - Q_EMIT
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
+TabWidth: 4
+UseTab: Never
+WhitespaceSensitiveMacros:
+ - BOOST_PP_STRINGIZE
+ - CF_SWIFT_NAME
+ - NS_SWIFT_NAME
+ - PP_STRINGIZE
+ - STRINGIZE
+...
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 0000000..2a7bc1b
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,33 @@
+---
+Checks: 'clang-diagnostic-*,clang-analyzer-*,readability*,-readability-use-anyofallof,bugprone*,-bugprone-easily-swappable-parameters,deadcode,cppcoreguidelines*,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-avoid-do-while,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-owning-memory,-cppcoreguidelines-non-private-member-variables-in-classes,modernize-*,-modernize-pass-by-value,-modernize-use-trailing-return-type,-modernize-avoid-c-arrays,performance*,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-type-union-access,-clang-diagnostic-unknown-warning-option,-clang-analyzer-valist*'
+WarningsAsErrors: ''
+HeaderFileExtensions:
+ - ''
+ - h
+ - hh
+ - hpp
+ - hxx
+ImplementationFileExtensions:
+ - c
+ - cc
+ - cpp
+ - cxx
+HeaderFilterRegex: ''
+FormatStyle: none
+User: djallen9
+CheckOptions:
+ cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU'
+ google-readability-namespace-comments.ShortNamespaceLines: '10'
+ cert-err33-c.CheckedFunctions: '::aligned_alloc;::asctime_s;::at_quick_exit;::atexit;::bsearch;::bsearch_s;::btowc;::c16rtomb;::c32rtomb;::calloc;::clock;::cnd_broadcast;::cnd_init;::cnd_signal;::cnd_timedwait;::cnd_wait;::ctime_s;::fclose;::fflush;::fgetc;::fgetpos;::fgets;::fgetwc;::fopen;::fopen_s;::fprintf;::fprintf_s;::fputc;::fputs;::fputwc;::fputws;::fread;::freopen;::freopen_s;::fscanf;::fscanf_s;::fseek;::fsetpos;::ftell;::fwprintf;::fwprintf_s;::fwrite;::fwscanf;::fwscanf_s;::getc;::getchar;::getenv;::getenv_s;::gets_s;::getwc;::getwchar;::gmtime;::gmtime_s;::localtime;::localtime_s;::malloc;::mbrtoc16;::mbrtoc32;::mbsrtowcs;::mbsrtowcs_s;::mbstowcs;::mbstowcs_s;::memchr;::mktime;::mtx_init;::mtx_lock;::mtx_timedlock;::mtx_trylock;::mtx_unlock;::printf_s;::putc;::putwc;::raise;::realloc;::remove;::rename;::scanf;::scanf_s;::setlocale;::setvbuf;::signal;::snprintf;::snprintf_s;::sprintf;::sprintf_s;::sscanf;::sscanf_s;::strchr;::strerror_s;::strftime;::strpbrk;::strrchr;::strstr;::strtod;::strtof;::strtoimax;::strtok;::strtok_s;::strtol;::strtold;::strtoll;::strtoul;::strtoull;::strtoumax;::strxfrm;::swprintf;::swprintf_s;::swscanf;::swscanf_s;::thrd_create;::thrd_detach;::thrd_join;::thrd_sleep;::time;::timespec_get;::tmpfile;::tmpfile_s;::tmpnam;::tmpnam_s;::tss_create;::tss_get;::tss_set;::ungetc;::ungetwc;::vfprintf;::vfprintf_s;::vfscanf;::vfscanf_s;::vfwprintf;::vfwprintf_s;::vfwscanf;::vfwscanf_s;::vprintf_s;::vscanf;::vscanf_s;::vsnprintf;::vsnprintf_s;::vsprintf;::vsprintf_s;::vsscanf;::vsscanf_s;::vswprintf;::vswprintf_s;::vswscanf;::vswscanf_s;::vwprintf_s;::vwscanf;::vwscanf_s;::wcrtomb;::wcschr;::wcsftime;::wcspbrk;::wcsrchr;::wcsrtombs;::wcsrtombs_s;::wcsstr;::wcstod;::wcstof;::wcstoimax;::wcstok;::wcstok_s;::wcstol;::wcstold;::wcstoll;::wcstombs;::wcstombs_s;::wcstoul;::wcstoull;::wcstoumax;::wcsxfrm;::wctob;::wctrans;::wctype;::wmemchr;::wprintf_s;::wscanf;::wscanf_s;'
+ llvm-else-after-return.WarnOnUnfixable: 'false'
+ cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false'
+ google-readability-namespace-comments.SpacesBeforeComments: '2'
+ cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true'
+ google-readability-braces-around-statements.ShortStatementLines: '1'
+ google-readability-function-size.StatementThreshold: '800'
+ llvm-qualified-auto.AddConstToQualified: 'false'
+ llvm-else-after-return.WarnOnConditionVariables: 'false'
+ cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false'
+SystemHeaders: false
+...
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ec79c81
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+.cache
+*build/
+*release/
+.ccls-cache
+compile_commands.json
+err.xml
+cppcheck.json
+*.log
+test/fuzztest
+dist/
+.semgrepignore
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..b135103
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,50 @@
+cmake_minimum_required(VERSION 3.19...3.27)
+
+if(${CMAKE_VERSION} VERSION_LESS 3.12)
+ cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
+endif()
+
+project(libfud
+ VERSION 1.0.0
+ DESCRIPTION "The Standard Library Extended and Exception Free"
+ LANGUAGES CXX C)
+
+set(CXX_CPPCHECK "project=build/compile_commands.json;enable=information;force")
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS true)
+
+add_library(libfud SHARED
+ source/libfud.cpp
+ source/c_file.cpp
+ source/string.cpp
+ source/utf8.cpp
+ source/utf8_iterator.cpp
+ )
+
+target_compile_options(libfud PRIVATE ${FUD_WARNINGS})
+
+target_include_directories(libfud PUBLIC include)
+
+set_target_properties(
+ libfud PROPERTIES
+ CXX_STANDARD 20
+ C_STANDARD 23
+ CXX_EXTENSIONS OFF
+ C_EXTENSIONS OFF
+ CXX_STANDARD_REQUIRED ON)
+
+if (FUD_TEST)
+add_subdirectory(test)
+add_subdirectory(examples)
+endif ()
+
+if (FUD_DOC)
+ find_package(Doxygen
+ REQUIRED dot
+ OPTIONAL_COMPONENTS mscgen dia)
+
+ doxygen_add_docs(docs
+ include
+ # CONFIG_FILE "Doxyfile"
+ )
+endif()
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2dc3022
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,196 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction,
+and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the
+copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other
+entities that control, are controlled by, or are under common control
+with that entity. For the purposes of this definition, "control" means
+(i) the power, direct or indirect, to cause the direction or
+management of such entity, whether by contract or otherwise, or (ii)
+ownership of fifty percent (50%) or more of the outstanding shares, or
+(iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications,
+including but not limited to software source code, documentation
+source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical
+transformation or translation of a Source form, including but not
+limited to compiled object code, generated documentation, and
+conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object
+form, made available under the License, as indicated by a copyright
+notice that is included in or attached to the work (an example is
+provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object
+form, that is based on (or derived from) the Work and for which the
+editorial revisions, annotations, elaborations, or other modifications
+represent, as a whole, an original work of authorship. For the
+purposes of this License, Derivative Works shall not include works
+that remain separable from, or merely link (or bind by name) to the
+interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the
+original version of the Work and any modifications or additions to
+that Work or Derivative Works thereof, that is intentionally submitted
+to Licensor for inclusion in the Work by the copyright owner or by an
+individual or Legal Entity authorized to submit on behalf of the
+copyright owner. For the purposes of this definition, "submitted"
+means any form of electronic, verbal, or written communication sent to
+the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control
+systems, and issue tracking systems that are managed by, or on behalf
+of, the Licensor for the purpose of discussing and improving the Work,
+but excluding communication that is conspicuously marked or otherwise
+designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity
+on behalf of whom a Contribution has been received by Licensor and
+subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+copyright license to reproduce, prepare Derivative Works of, publicly
+display, publicly perform, sublicense, and distribute the Work and
+such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+this License, each Contributor hereby grants to You a perpetual,
+worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except
+as stated in this section) patent license to make, have made, use,
+offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such
+Contributor that are necessarily infringed by their Contribution(s)
+alone or by combination of their Contribution(s) with the Work to
+which such Contribution(s) was submitted. If You institute patent
+litigation against any entity (including a cross-claim or counterclaim
+in a lawsuit) alleging that the Work or a Contribution incorporated
+within the Work constitutes direct or contributory patent
+infringement, then any patent licenses granted to You under this
+License for that Work shall terminate as of the date such litigation
+is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work
+or Derivative Works thereof in any medium, with or without
+modifications, and in Source or Object form, provided that You meet
+the following conditions:
+
+ You must give any other recipients of the Work or Derivative Works
+ a copy of this License; and You must cause any modified files to
+ carry prominent notices stating that You changed the files; and
+ You must retain, in the Source form of any Derivative Works that
+ You distribute, all copyright, patent, trademark, and attribution
+ notices from the Source form of the Work, excluding those notices
+ that do not pertain to any part of the Derivative Works; and If
+ the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one of
+ the following places: within a NOTICE text file distributed as
+ part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents of
+ the NOTICE file are for informational purposes only and do not
+ modify the License. You may add Your own attribution notices
+ within Derivative Works that You distribute, alongside or as an
+ addendum to the NOTICE text from the Work, provided that such
+ additional attribution notices cannot be construed as modifying
+ the License.
+
+You may add Your own copyright statement to Your modifications and may
+provide additional or different license terms and conditions for use,
+reproduction, or distribution of Your modifications, or for any such
+Derivative Works as a whole, provided Your use, reproduction, and
+distribution of the Work otherwise complies with the conditions stated
+in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+any Contribution intentionally submitted for inclusion in the Work by
+You to the Licensor shall be under the terms and conditions of this
+License, without any additional terms or conditions. Notwithstanding
+the above, nothing herein shall supersede or modify the terms of any
+separate license agreement you may have executed with Licensor
+regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+names, trademarks, service marks, or product names of the Licensor,
+except as required for reasonable and customary use in describing the
+origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed
+to in writing, Licensor provides the Work (and each Contributor
+provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied, including, without
+limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely
+responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your
+exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+whether in tort (including negligence), contract, or otherwise, unless
+required by applicable law (such as deliberate and grossly negligent
+acts) or agreed to in writing, shall any Contributor be liable to You
+for damages, including any direct, indirect, special, incidental, or
+consequential damages of any character arising as a result of this
+License or out of the use or inability to use the Work (including but
+not limited to damages for loss of goodwill, work stoppage, computer
+failure or malfunction, or any and all other commercial damages or
+losses), even if such Contributor has been advised of the possibility
+of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+the Work or Derivative Works thereof, You may choose to offer, and
+charge a fee for, acceptance of support, warranty, indemnity, or other
+liability obligations and/or rights consistent with this
+License. However, in accepting such obligations, You may act only on
+Your own behalf and on Your sole responsibility, not on behalf of any
+other Contributor, and only if You agree to indemnify, defend, and
+hold each Contributor harmless for any liability incurred by, or
+claims asserted against, such Contributor by reason of your accepting
+any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ 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. \ No newline at end of file
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..61795d7
--- /dev/null
+++ b/README.org
@@ -0,0 +1 @@
+* libfud
diff --git a/cmake/warnings.cmake b/cmake/warnings.cmake
new file mode 100644
index 0000000..1bddeb8
--- /dev/null
+++ b/cmake/warnings.cmake
@@ -0,0 +1,66 @@
+set(FUD_WARNINGS
+ -Werror
+ -Wstack-usage=2048
+ -Wno-long-long
+ -Wno-error=long-long
+ -Wno-error=inline
+ -Wno-error=mismatched-tags
+ -Wall
+ -Weffc++
+ -pedantic
+ -Wsizeof-pointer-memaccess
+ -pedantic-errors
+ -Wextra
+ -Wpacked
+ -Wshadow
+ -Wvla
+ -Wnull-dereference
+ -Wuninitialized
+ -Wstack-protector
+ -Warray-bounds
+ -Woverlength-strings
+ -Wwrite-strings
+ -Wcast-qual
+ -Wcast-align
+ -Wdisabled-optimization
+ -Wmissing-field-initializers
+ -Wimport
+ -Winit-self
+ -Winline
+ -Wchar-subscripts
+ -Wcomment
+ -Wconversion
+ -Wfloat-equal
+ -Wformat
+ -Wformat=2
+ -Wformat-nonliteral
+ -Wformat-security
+ -Wformat-y2k
+ -Wmissing-format-attribute
+ -Wmissing-braces
+ -Winvalid-pch
+ -Wmissing-include-dirs
+ -Wmissing-noreturn
+ -Wparentheses
+ -Wpointer-arith
+ -Wredundant-decls
+ -Wreturn-type
+ -Wsequence-point
+ -Wsign-compare
+ -Wstrict-aliasing
+ -Wstrict-aliasing=2
+ -Wswitch
+ -Wswitch-default
+ -Wswitch-enum
+ -Wtrigraphs
+ -Wunknown-pragmas
+ -Wunreachable-code
+ -Wunused
+ -Wunused-function
+ -Wunused-label
+ -Wunused-parameter
+ -Wunused-value
+ -Wunused-variable
+ -Wvariadic-macros
+ -Wvolatile-register-var
+)
diff --git a/include/array.hpp b/include/array.hpp
new file mode 100644
index 0000000..9de6c0a
--- /dev/null
+++ b/include/array.hpp
@@ -0,0 +1,113 @@
+/*
+ * 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 EXT_ARRAY_HPP
+#define EXT_ARRAY_HPP
+
+#include "memory.hpp"
+
+#include <cstdlib>
+
+namespace fud {
+
+template <typename T, size_t Size>
+struct Array {
+ static_assert(Size > 0);
+ using ValueType = T;
+
+ T m_data[Size]; // NOLINT(cppcoreguidelines-avoid-c-arrays)
+
+ constexpr static Array constFill(T value)
+ {
+ Array arr{};
+ setMemory(arr, value);
+ return arr;
+ }
+
+ [[nodiscard]] constexpr size_t size() const
+ {
+ return Size;
+ }
+
+ constexpr T& front()
+ {
+ return m_data[0];
+ }
+
+ constexpr const T& front() const
+ {
+ return m_data[0];
+ }
+
+ constexpr T& back()
+ {
+ return m_data[Size - 1];
+ }
+
+ constexpr const T& back() const
+ {
+ return m_data[Size - 1];
+ }
+
+ constexpr T* data() noexcept
+ {
+ return m_data;
+ }
+
+ constexpr const T* data() const noexcept
+ {
+ return m_data;
+ }
+
+ constexpr T* begin() noexcept
+ {
+ return m_data;
+ }
+
+ constexpr const T* begin() const noexcept
+ {
+ return m_data;
+ }
+
+ constexpr T* end() noexcept
+ {
+ return m_data + Size;
+ }
+
+ constexpr const T* end() const noexcept
+ {
+ return m_data + Size;
+ }
+
+ constexpr T& operator[](size_t index)
+ {
+ return m_data[index];
+ }
+
+ constexpr const T& operator[](size_t index) const
+ {
+ return m_data[index];
+ }
+
+ constexpr bool operator==(const Array<T, Size>&) const noexcept = default;
+
+ constexpr auto operator<=>(const Array<T, Size>& other) const noexcept = default;
+};
+
+} // namespace ext_lib
+
+#endif
diff --git a/include/c_file.hpp b/include/c_file.hpp
new file mode 100644
index 0000000..0f43e08
--- /dev/null
+++ b/include/c_file.hpp
@@ -0,0 +1,103 @@
+/*
+ * 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_C_FILE_HPP
+#define FUD_C_FILE_HPP
+
+#include "result.hpp"
+
+#include <cstdint>
+#include <string>
+
+namespace fud {
+
+enum class CFileMode : uint8_t
+{
+ ReadOnly,
+ ReadWrite,
+ WriteTruncate,
+ ReadWriteTruncate,
+ WriteAppend,
+ ReadWriteAppend,
+};
+
+constexpr const char* CBinaryFileModeFromFlags(CFileMode mode)
+{
+ switch (mode) {
+ case CFileMode::ReadOnly:
+ return "rb";
+ case CFileMode::ReadWrite:
+ return "r+b";
+ case CFileMode::WriteTruncate:
+ return "wb";
+ case CFileMode::ReadWriteTruncate:
+ return "w+b";
+ case CFileMode::WriteAppend:
+ return "ab";
+ case CFileMode::ReadWriteAppend:
+ return "a+b";
+ default:
+ return "";
+ }
+}
+
+constexpr const char* CTextFileModeFromFlags(CFileMode mode)
+{
+ switch (mode) {
+ case CFileMode::ReadOnly:
+ return "r";
+ case CFileMode::ReadWrite:
+ return "r+";
+ case CFileMode::WriteTruncate:
+ return "w";
+ case CFileMode::ReadWriteTruncate:
+ return "w+";
+ case CFileMode::WriteAppend:
+ return "a";
+ case CFileMode::ReadWriteAppend:
+ return "a+";
+ default:
+ return "";
+ }
+}
+
+enum class FileResult
+{
+ Success,
+ Error,
+};
+
+class CBinaryFile {
+ public:
+ CBinaryFile(const std::string& filename, CFileMode mode);
+ CBinaryFile(const std::string& filename, CFileMode mode, const std::string& extraFlags);
+ ~CBinaryFile();
+ FileResult open();
+ void close();
+ const FILE* file() const;
+
+ private:
+ std::string m_filename;
+ std::string m_extraFlags{};
+ std::string m_mode;
+ CFileMode m_modeFlags;
+ FILE* m_file{nullptr};
+};
+
+} // namespace fud
+
+
+#endif
diff --git a/include/fud_type_traits.hpp b/include/fud_type_traits.hpp
new file mode 100644
index 0000000..3fdff79
--- /dev/null
+++ b/include/fud_type_traits.hpp
@@ -0,0 +1,80 @@
+/*
+ * 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_TYPE_TRAITS_HPP
+#define FUD_TYPE_TRAITS_HPP
+
+#include <cstddef>
+#include <type_traits>
+
+namespace fud {
+
+template <template <class, size_t> class Container, typename T, size_t Size>
+constexpr bool hasDuplicates(Container<T, Size> const& arr)
+{
+ for (size_t iIdx = 1; iIdx < Size; iIdx++) {
+ for (size_t jIdx = 0; jIdx < iIdx; jIdx++) {
+ if (arr[iIdx] == arr[jIdx]) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+template <typename T, size_t Size>
+struct Array;
+
+template <typename T, T... Vs>
+constexpr bool hasDuplicates()
+{
+ static_assert(sizeof...(Vs) > 0);
+ constexpr Array<T, sizeof...(Vs)> arr{{Vs...}};
+ return hasDuplicates(arr);
+}
+
+template <template <class, size_t> class Container, typename T, T... Vs>
+constexpr bool hasDuplicates()
+{
+ static_assert(sizeof...(Vs) > 0);
+ constexpr Container<T, sizeof...(Vs)> arr{{Vs...}};
+ return hasDuplicates(arr);
+}
+
+template <typename...>
+inline constexpr auto isUnique = std::true_type{};
+
+template <typename T, typename... Rest>
+inline constexpr auto
+ isUnique<T, Rest...> = std::bool_constant<(!std::is_same_v<T, Rest> && ...) && isUnique<Rest...>>{};
+
+
+template <template <class, size_t> class Container, class T, size_t N>
+constexpr bool allDifferentFrom(Container<T, N>& data, const T& value)
+{
+ for (size_t i = 0; i < data.size(); ++i) {
+ if (data[i] == value) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace fud
+
+#endif
diff --git a/include/libfud.hpp b/include/libfud.hpp
new file mode 100644
index 0000000..ffea195
--- /dev/null
+++ b/include/libfud.hpp
@@ -0,0 +1,30 @@
+/*
+ * 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 LIBFUD_HPP
+#define LIBFUD_HPP
+
+#include "result.hpp" // IWYU pragma: export
+
+namespace fud {
+
+void fud();
+
+}
+
+
+#endif
diff --git a/include/memory.hpp b/include/memory.hpp
new file mode 100644
index 0000000..1ca6029
--- /dev/null
+++ b/include/memory.hpp
@@ -0,0 +1,140 @@
+/*
+ * 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 MEMORY_HPP
+#define MEMORY_HPP
+
+#include "result.hpp"
+#include "status.hpp"
+
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+namespace fud {
+
+// An allocating function which returns null on failure.
+using FudAllocOne = void(*)(size_t);
+
+// An allocating function which returns null on failure.
+using FudAllocMany = void(*)(size_t, size_t);
+
+FudStatus copyMem(void* destination, size_t destination_size, const void* source, size_t count);
+
+FudStatus compareMem(const void* lhs, size_t destination_size, const void* rhs, size_t count, int* difference);
+
+Result<int, FudStatus> compareMem(const void* lhs, size_t destination_size, const void* rhs, size_t count);
+
+FudStatus setMemory(void* data, size_t dataSize, uint8_t pattern, size_t count);
+
+FudStatus setMemory(
+ void* data,
+ size_t collectionCount,
+ size_t eltOffset,
+ size_t eltSize,
+ uint8_t pattern,
+ size_t eltCount);
+
+template <template <class, size_t> class Container, typename T, size_t Size>
+constexpr void setMemory(Container<T, Size>& container, const T& value)
+{
+ for (auto& elt : container) {
+ elt = value;
+ }
+}
+
+template <size_t Count, typename T, typename U>
+void copyMem(T& destination, const U& source)
+{
+ static_assert(Count <= sizeof(U));
+ static_assert(Count <= sizeof(T));
+ static_assert(std::is_standard_layout_v<T>);
+ static_assert(std::is_standard_layout_v<U>);
+
+ for (size_t idx = 0; idx < Count; ++idx) {
+ // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
+ reinterpret_cast<uint8_t*>(&destination)[idx] = reinterpret_cast<const uint8_t*>(&source)[idx];
+ // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
+ }
+}
+
+template <typename T, typename U>
+void copyMem(T& destination, const U& source)
+{
+ static_assert(sizeof(U) <= sizeof(T));
+
+ copyMem<sizeof(U)>(destination, source);
+}
+
+template <size_t Count, typename T, typename U>
+int compareMem(const T& lhs, const U& rhs)
+{
+ static_assert(Count <= sizeof(T));
+ static_assert(Count <= sizeof(U));
+
+ int difference = 0;
+ for (size_t idx = 0; idx < Count; ++idx) {
+ // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
+ difference = reinterpret_cast<const uint8_t*>(&lhs)[idx] - reinterpret_cast<const uint8_t*>(&rhs)[idx];
+ // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
+ if (difference != 0) {
+ break;
+ }
+ }
+
+ return difference;
+}
+
+template <size_t Count, typename T, typename U>
+int compareMem(const T& lhs, U&& rhs)
+{
+ static_assert(Count <= sizeof(T));
+ static_assert(Count <= sizeof(U));
+
+ int difference = 0;
+ for (size_t idx = 0; idx < Count; ++idx) {
+ // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
+ difference = reinterpret_cast<const uint8_t*>(&lhs)[idx] -
+ reinterpret_cast<const uint8_t*>(&std::forward<U>(rhs))[idx];
+ // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
+ if (difference != 0) {
+ break;
+ }
+ }
+
+ return difference;
+}
+
+template <typename T, typename U>
+int compareMem(const T& lhs, const U& rhs)
+{
+ static_assert(sizeof(U) == sizeof(T));
+
+ return compareMem<sizeof(U)>(lhs, rhs);
+}
+
+template <typename T, typename U>
+int compareMem(const T& lhs, U&& rhs)
+{
+ static_assert(sizeof(U) == sizeof(T));
+
+ return compareMem<sizeof(U)>(lhs, std::forward<U>(rhs));
+}
+
+} // namespace fud
+
+#endif
diff --git a/include/result.hpp b/include/result.hpp
new file mode 100644
index 0000000..158afd1
--- /dev/null
+++ b/include/result.hpp
@@ -0,0 +1,83 @@
+/*
+ * 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 BOOKMOUSE_RESULT_HPP
+#define BOOKMOUSE_RESULT_HPP
+
+#include <utility>
+#include <variant>
+
+namespace fud {
+
+template<typename T, typename E>
+class Result {
+public:
+ using ResultType = Result<T, E>;
+ static ResultType okay(const T& okay)
+ {
+ return ResultType{okay};
+ }
+
+ static ResultType okay(T&& okay)
+ {
+ return ResultType{std::move(okay)};
+ }
+
+ static ResultType error(const E& error)
+ {
+ return ResultType{error};
+ }
+
+ static ResultType error(E&& error)
+ {
+ return ResultType{std::move(error)};
+ }
+
+ [[nodiscard]] constexpr bool isOkay() const
+ {
+ return(m_value.index() == 0);
+ }
+
+ [[nodiscard]] constexpr bool isError() const
+ {
+ return(m_value.index() == 1);
+ }
+
+ T getOkay()
+ {
+ return std::get<T>(m_value);
+ }
+
+ E getError()
+ {
+ return std::get<E>(m_value);
+ }
+
+private:
+ explicit Result() : m_value() {}
+ explicit Result(const T& value) : m_value(value) {}
+ explicit Result(const E& value) : m_value(value) {}
+
+ explicit Result(T&& value) : m_value(std::move(value)) {}
+ explicit Result(E&& value) : m_value(std::move(value)) {}
+
+ std::variant<T, E> m_value;
+};
+
+} // namespace bookmouse
+
+#endif
diff --git a/include/status.hpp b/include/status.hpp
new file mode 100644
index 0000000..2bba4b3
--- /dev/null
+++ b/include/status.hpp
@@ -0,0 +1,106 @@
+/*
+ * 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 STATUS_HPP
+#define STATUS_HPP
+
+namespace fud {
+
+enum class [[nodiscard]] FudStatus
+{
+ Success = 0,
+ NullPointer,
+ StringInvalid,
+ OperationInvalid,
+ AllocFailure,
+ InvalidInput,
+ Utf8Invalid,
+ Failure,
+ NotFound,
+ Aliased,
+ Empty,
+ Partial,
+ Full,
+ RangeError,
+ VariantInvalid,
+ NotImplemented,
+ NotSupported
+};
+
+static inline const char* ExtStatusToString(FudStatus status)
+{
+ switch (status) {
+ case FudStatus::Success:
+ return "ExtSuccess";
+ case FudStatus::NullPointer:
+ return "ExtNullPointer";
+ case FudStatus::StringInvalid:
+ return "ExtStringInvalid";
+ case FudStatus::OperationInvalid:
+ return "ExtOperationInvalid";
+ case FudStatus::AllocFailure:
+ return "ExtAllocFailure";
+ case FudStatus::InvalidInput:
+ return "ExtInvalidInput";
+ case FudStatus::Utf8Invalid:
+ return "ExtUtf8Invalid";
+ case FudStatus::Failure:
+ return "ExtFailure";
+ case FudStatus::NotFound:
+ return "ExtNotFound";
+ case FudStatus::Aliased:
+ return "ExtAliased";
+ case FudStatus::Empty:
+ return "ExtEmpty";
+ case FudStatus::Partial:
+ return "ExtPartial";
+ case FudStatus::Full:
+ return "ExtFull";
+ case FudStatus::RangeError:
+ return "ExtRangeError";
+ case FudStatus::VariantInvalid:
+ return "ExtVariantInvalid";
+ case FudStatus::NotImplemented:
+ return "ExtNotImplemented";
+ case FudStatus::NotSupported:
+ return "ExtNotSupported";
+ default:
+ return "Unknown";
+ }
+}
+
+static inline bool anyAreNull() { return false; }
+
+template <typename T>
+bool anyAreNull(const T* pointer)
+{
+ return pointer == nullptr;
+}
+
+template <typename T, typename... Ts>
+bool anyAreNull(T pointer, Ts... pointers)
+{
+ if (pointer == nullptr)
+ {
+ return true;
+ }
+ return anyAreNull(pointers...);
+}
+
+} // namespace ext_lib
+
+#endif
diff --git a/include/string.hpp b/include/string.hpp
new file mode 100644
index 0000000..89aa94e
--- /dev/null
+++ b/include/string.hpp
@@ -0,0 +1,153 @@
+/*
+ * 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_STRING_HPP
+#define FUD_STRING_HPP
+
+#include "utf8.hpp"
+
+#include <climits>
+#include <cstddef>
+
+static_assert(CHAR_BIT == 8);
+
+namespace fud {
+
+class String {
+ public:
+ [[nodiscard]] constexpr size_t length() const
+ {
+ return m_length;
+ }
+
+ [[nodiscard]] constexpr size_t size() const
+ {
+ return m_length + 1;
+ }
+
+ [[nodiscard]] constexpr size_t capacity() const
+ {
+ return m_capacity;
+ }
+
+ [[nodiscard]] constexpr utf8* data() const
+ {
+ return m_data;
+ }
+
+ [[nodiscard]] bool nullTerminated() const;
+
+ [[nodiscard]] bool valid() const;
+
+ [[nodiscard]] bool utf8Valid() const;
+
+ [[nodiscard]] FudStatus nullTerminate() const;
+
+ [[nodiscard]] constexpr size_t remainingLength() const
+ {
+ if (m_length >= m_capacity) {
+ return 0;
+ }
+
+ return m_capacity - 1U - m_length;
+ }
+
+ [[nodiscard]] FudStatus pushBack(char letter);
+
+ [[nodiscard]] FudStatus pushBack(utf8 letter);
+
+ [[nodiscard]] FudStatus pushBack(const ExtUtf8& letter);
+
+ std::optional<utf8> pop();
+
+ [[nodiscard]] FudStatus catenate(StringView source);
+
+ private:
+ utf8* m_data;
+ size_t m_length;
+ size_t m_capacity;
+};
+
+class StringView {
+ public:
+ constexpr StringView() noexcept : m_length(0), m_data{nullptr}
+ {
+ }
+
+ constexpr StringView(size_t strLen, const utf8* strData) : m_length(0), m_data{strData}
+ {
+ }
+
+ StringView(size_t strLen, const char* strData) :
+ m_length(0), // line break
+ m_data{reinterpret_cast<const utf8*>(strData)} // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
+ {
+ }
+
+ explicit constexpr StringView(const StringView& view) noexcept = default;
+
+ explicit constexpr StringView(const String& fudString) noexcept : StringView(fudString.length(), fudString.data())
+ {
+ }
+
+ [[nodiscard]] constexpr size_t length() const
+ {
+ return m_length;
+ }
+
+ [[nodiscard]] constexpr const utf8* data() const
+ {
+ return m_data;
+ }
+
+ [[nodiscard]] bool nullTerminated() const;
+
+ [[nodiscard]] bool utf8Valid() const;
+
+ Result<size_t, FudStatus> skipWhitespace();
+
+ Result<size_t, FudStatus> trimWhitespace();
+
+ FudStatus toUint8(uint8_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toUint16(uint16_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toUint32(uint32_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toUint64(uint64_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toInt8(int8_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toInt16(int16_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toInt32(int32_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toInt64(int64_t& number, uint8_t specifiedRadix, size_t& strLen) const;
+
+ FudStatus toFloat(float& number, size_t& strLen) const;
+
+ FudStatus toDouble(double& number, size_t& strLen) const;
+
+ private:
+ size_t m_length;
+ const utf8* m_data;
+};
+
+FudStatus skipWhitespace(StringView& view, size_t& skipIndex);
+
+} // namespace fud
+
+#endif
diff --git a/include/unique_array.hpp b/include/unique_array.hpp
new file mode 100644
index 0000000..a7e0731
--- /dev/null
+++ b/include/unique_array.hpp
@@ -0,0 +1,68 @@
+/*
+ * 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_UNIQUE_ARRAY_HPP
+#define FUD_UNIQUE_ARRAY_HPP
+
+#include "array.hpp"
+#include "fud_type_traits.hpp"
+
+#include <cstdlib>
+#include <utility>
+
+namespace fud {
+
+template <class T, T... Vs>
+class UniqueArray { // NOLINT(cppcoreguidelines-special-member-functions)
+ public:
+ // NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members)
+ const Array<T, sizeof...(Vs)> m_values{};
+ // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members)
+
+ ~UniqueArray() = default;
+
+ constexpr UniqueArray() : m_values{{Vs...}}
+ {
+ static_assert(sizeof...(Vs) < 2 || !hasDuplicates<T, Vs...>());
+ }
+
+ constexpr UniqueArray(const UniqueArray& rhs) : m_values(rhs.m_values)
+ {
+ static_assert(sizeof...(Vs) < 2 || !hasDuplicates<T, Vs...>());
+ }
+
+ constexpr UniqueArray(UniqueArray&& rhs) noexcept : m_values(std::move(rhs).m_values)
+ {
+ static_assert(sizeof...(Vs) < 2 || !hasDuplicates<T, Vs...>());
+ }
+
+ [[nodiscard]] constexpr size_t size() const
+ {
+ return sizeof...(Vs);
+ }
+};
+
+template <typename T=size_t, T... Is>
+constexpr UniqueArray<T, Is...> makeStaticIndexSet(std::integer_sequence<T, Is...> /*unused*/)
+{
+ static_assert(sizeof...(Is) > 0);
+ return UniqueArray<T, Is...>{};
+}
+
+} // namespace fud
+
+#endif
diff --git a/include/utf8.hpp b/include/utf8.hpp
new file mode 100644
index 0000000..c66d93c
--- /dev/null
+++ b/include/utf8.hpp
@@ -0,0 +1,557 @@
+/*
+ * 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_UTF8_HPP
+#define FUD_UTF8_HPP
+
+#include "array.hpp"
+#include "memory.hpp"
+#include "status.hpp"
+#include "unique_array.hpp"
+
+/*
+#include "ext_hash.hpp"
+#include "ext_set.hpp"
+*/
+
+#include <cstdint>
+#include <optional>
+#include <type_traits>
+
+namespace fud {
+
+using utf8 = unsigned char;
+
+struct StringView;
+
+constexpr uint8_t ASCII_MASK = 0x7F;
+
+constexpr uint8_t UTF8_MB_PATTERN_MASK = 0xC0;
+constexpr uint8_t UTF8_MB_PATTERN = 0x80;
+constexpr uint8_t UTF8_MB_MASK = static_cast<uint8_t>(~UTF8_MB_PATTERN_MASK);
+
+constexpr uint8_t UTF8_2B_PATTERN_MASK = 0xE0;
+constexpr uint8_t UTF8_2B_PATTERN = 0xC0;
+constexpr uint8_t UTF8_2B_MASK = static_cast<uint8_t>(~UTF8_2B_PATTERN_MASK);
+
+constexpr uint8_t UTF8_3B_PATTERN_MASK = 0xF0;
+constexpr uint8_t UTF8_3B_PATTERN = 0xE0;
+constexpr uint8_t UTF8_3B_MASK = static_cast<uint8_t>(~UTF8_3B_PATTERN_MASK);
+
+constexpr uint8_t UTF8_4B_PATTERN_MASK = 0xF8;
+constexpr uint8_t UTF8_4B_PATTERN = 0xF0;
+constexpr uint8_t UTF8_4B_MASK = static_cast<uint8_t>(~UTF8_4B_PATTERN_MASK);
+
+namespace privateImpl {
+constexpr bool validUtf8MB(uint8_t code) noexcept
+{
+ return (code & UTF8_MB_PATTERN_MASK) == UTF8_MB_PATTERN;
+}
+} // namespace privateImpl
+
+struct Ascii {
+ Array<uint8_t, 1> characters;
+
+ constexpr Ascii() noexcept = default;
+
+ constexpr explicit Ascii(uint8_t chr) noexcept : characters{{chr}}
+ {
+ }
+
+ [[nodiscard]] constexpr uint8_t character() const noexcept
+ {
+ return characters[0];
+ }
+
+ [[nodiscard]] constexpr char asChar() const noexcept
+ {
+ return static_cast<char>(characters[0]);
+ }
+
+ static constexpr size_t size() noexcept
+ {
+ return 1;
+ }
+
+ [[nodiscard]] constexpr bool valid() const noexcept
+ {
+ return valid(characters[0]);
+ }
+
+ static constexpr bool valid(uint8_t character) noexcept
+ {
+ return static_cast<uint8_t>(character & ~ASCII_MASK) == 0;
+ }
+
+ auto operator<=>(const Ascii& other) const noexcept = default;
+};
+
+static_assert(std::is_trivial_v<Ascii>);
+static_assert(std::is_standard_layout_v<Ascii>);
+
+/*
+| B | E | Byte 1 | Byte 2 | Byte 3 | Byte 4
+| U+0000 | U+007F | 0xxxxxxx | | |
+| U+0080 | U+07FF | 110xxxxx | 10xxxxxx | |
+| U+0800 | U+FFFF | 1110xxxx | 10xxxxxx | 10xxxxxx |
+| U+10000 | U+10FFFF | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx
+*/
+
+struct Utf82Byte {
+ constexpr Utf82Byte(uint8_t first, uint8_t second) noexcept : characters{{first, second}}
+ {
+ }
+ Array<uint8_t, 2> characters;
+ static constexpr size_t size() noexcept
+ {
+ return 2;
+ }
+
+ [[nodiscard]] constexpr bool valid() const noexcept
+ {
+ return valid(first(), second());
+ }
+
+ static constexpr bool valid(uint8_t first, uint8_t second) noexcept
+ {
+ using privateImpl::validUtf8MB;
+ return ((first & UTF8_2B_PATTERN_MASK) == UTF8_2B_PATTERN) && validUtf8MB(second);
+ }
+
+ [[nodiscard]] constexpr uint8_t first() const noexcept
+ {
+ return characters[0];
+ }
+
+ [[nodiscard]] constexpr uint8_t second() const noexcept
+ {
+ return characters[1];
+ }
+
+ auto operator<=>(const Utf82Byte& other) const noexcept = default;
+};
+
+struct Utf83Byte {
+ constexpr Utf83Byte(uint8_t first, uint8_t second, uint8_t third) noexcept : characters{{first, second, third}}
+ {
+ }
+
+ Array<uint8_t, 3> characters;
+
+ static constexpr size_t size() noexcept
+ {
+ return 3;
+ }
+
+ [[nodiscard]] constexpr bool valid() const noexcept
+ {
+ return valid(first(), second(), third());
+ }
+
+ static constexpr bool valid(uint8_t first, uint8_t second, uint8_t third) noexcept
+ {
+ using privateImpl::validUtf8MB;
+ return ((first & UTF8_3B_PATTERN_MASK) == UTF8_3B_PATTERN) && validUtf8MB(second) && validUtf8MB(third);
+ }
+
+ [[nodiscard]] constexpr uint8_t first() const noexcept
+ {
+ return characters[0];
+ }
+
+ [[nodiscard]] constexpr uint8_t second() const noexcept
+ {
+ return characters[1];
+ }
+
+ [[nodiscard]] constexpr uint8_t third() const noexcept
+ {
+ return characters[2];
+ }
+
+ auto operator<=>(const Utf83Byte& other) const noexcept = default;
+};
+
+struct Utf84Byte {
+ constexpr Utf84Byte(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) noexcept :
+ characters{{first, second, third, fourth}}
+ {
+ }
+
+ Array<uint8_t, 4> characters;
+
+ static constexpr size_t size() noexcept
+ {
+ return 4;
+ }
+
+ [[nodiscard]] constexpr bool valid() const noexcept
+ {
+ return valid(first(), second(), third(), fourth());
+ }
+
+ static constexpr bool valid(uint8_t first, uint8_t second, uint8_t third, uint8_t fourth) noexcept
+ {
+ using privateImpl::validUtf8MB;
+ if ((first & UTF8_4B_PATTERN_MASK) != UTF8_4B_PATTERN) {
+ return false;
+ }
+ return validUtf8MB(second) && validUtf8MB(third) && validUtf8MB(fourth);
+ }
+
+ [[nodiscard]] constexpr uint8_t first() const noexcept
+ {
+ return characters[0];
+ }
+
+ [[nodiscard]] constexpr uint8_t second() const noexcept
+ {
+ return characters[1];
+ }
+
+ [[nodiscard]] constexpr uint8_t third() const noexcept
+ {
+ return characters[2];
+ }
+
+ [[nodiscard]] constexpr uint8_t fourth() const noexcept
+ {
+ return characters[3];
+ }
+
+ auto operator<=>(const Utf84Byte& other) const noexcept = default;
+};
+
+using Utf8Variant = std::variant<Ascii, Utf82Byte, Utf83Byte, Utf84Byte>;
+
+constexpr auto ExtUtf8TypeSet{UniqueArray<size_t, 0, 1, 2, 3>{}};
+enum class ExtUtf8Type : uint8_t
+{
+ Ascii,
+ Utf82Byte,
+ Utf83Byte,
+ Utf84Byte,
+};
+static_assert(ExtUtf8TypeSet.m_values[0] == static_cast<uint8_t>(ExtUtf8Type::Ascii));
+static_assert(ExtUtf8TypeSet.m_values[1] == static_cast<uint8_t>(ExtUtf8Type::Utf82Byte));
+static_assert(ExtUtf8TypeSet.m_values[2] == static_cast<uint8_t>(ExtUtf8Type::Utf83Byte));
+static_assert(ExtUtf8TypeSet.m_values[3] == static_cast<uint8_t>(ExtUtf8Type::Utf84Byte));
+
+class String;
+class StringView;
+
+struct ExtUtf8 {
+ Utf8Variant m_variant{Utf8Variant{Ascii{}}};
+
+ static constexpr Ascii invalidAsciiCode{Ascii{0xFF}};
+ static ExtUtf8 fromString(const String& fudString, size_t index) noexcept;
+ static ExtUtf8 fromStringView(StringView&& fudView, size_t index) noexcept;
+ static ExtUtf8 fromStringView(const StringView& fudView, size_t index) noexcept;
+
+ static constexpr ExtUtf8 makeUtf8(Array<utf8, 4>& data)
+ {
+ ExtUtf8 unicode{};
+ if (Ascii::valid(data[0])) {
+ unicode.m_variant = Ascii{data[0]};
+ } else if (Utf82Byte::valid(data[0], data[1])) {
+ unicode.m_variant = Utf82Byte{data[0], data[1]};
+ } else if (Utf83Byte::valid(data[0], data[1], data[2])) {
+ unicode.m_variant = Utf83Byte{data[0], data[1], data[2]};
+ } else if (Utf84Byte::valid(data[0], data[1], data[2], data[3])) {
+ unicode.m_variant = Utf84Byte{data[0], data[1], data[2], data[3]};
+ } else {
+ unicode.m_variant = invalidAsciiCode;
+ }
+ return unicode;
+ }
+
+ static constexpr ExtUtf8 makeUtf8(const Ascii& utf8Char)
+ {
+ ExtUtf8 unicode{{Utf8Variant{Ascii{}}}};
+ if (utf8Char.valid()) {
+ unicode.m_variant = utf8Char;
+ } else {
+ unicode.m_variant = invalidAsciiCode;
+ }
+ return unicode;
+ }
+
+ static constexpr ExtUtf8 invalidAscii()
+ {
+ ExtUtf8 utf8{};
+ utf8.m_variant = Ascii{invalidAsciiCode};
+ return utf8;
+ }
+
+ [[nodiscard]] constexpr ExtUtf8Type getType() const
+ {
+ return static_cast<ExtUtf8Type>(m_variant.index());
+ }
+
+ [[nodiscard]] constexpr bool isAscii() const
+ {
+ return getType() == ExtUtf8Type::Ascii;
+ }
+
+ [[nodiscard]] constexpr bool valid() const noexcept
+ {
+ switch (m_variant.index()) {
+ case static_cast<size_t>(ExtUtf8Type::Ascii):
+ return std::get<Ascii>(m_variant).valid();
+ case static_cast<size_t>(ExtUtf8Type::Utf82Byte):
+ return std::get<Utf82Byte>(m_variant).valid();
+ case static_cast<size_t>(ExtUtf8Type::Utf83Byte):
+ return std::get<Utf83Byte>(m_variant).valid();
+ case static_cast<size_t>(ExtUtf8Type::Utf84Byte):
+ return std::get<Utf84Byte>(m_variant).valid();
+ default: // unlikely
+ return false;
+ }
+ }
+
+ [[nodiscard]] constexpr size_t size() const noexcept
+ {
+ if (!valid()) {
+ return 0;
+ }
+ switch (m_variant.index()) {
+ case static_cast<size_t>(ExtUtf8Type::Ascii):
+ return Ascii::size();
+ case static_cast<size_t>(ExtUtf8Type::Utf82Byte):
+ return Utf82Byte::size();
+ case static_cast<size_t>(ExtUtf8Type::Utf83Byte):
+ return Utf83Byte::size();
+ case static_cast<size_t>(ExtUtf8Type::Utf84Byte):
+ return Utf84Byte::size();
+ default: // unlikely
+ return 0;
+ }
+ }
+
+ [[nodiscard]] constexpr const uint8_t* data() const noexcept
+ {
+ if (!valid()) {
+ return nullptr;
+ }
+
+ switch (m_variant.index()) {
+ case static_cast<size_t>(ExtUtf8Type::Ascii):
+ return std::get<Ascii>(m_variant).characters.data();
+ case static_cast<size_t>(ExtUtf8Type::Utf82Byte):
+ return std::get<Utf82Byte>(m_variant).characters.data();
+ case static_cast<size_t>(ExtUtf8Type::Utf83Byte):
+ return std::get<Utf83Byte>(m_variant).characters.data();
+ case static_cast<size_t>(ExtUtf8Type::Utf84Byte):
+ return std::get<Utf84Byte>(m_variant).characters.data();
+ default: // unlikely
+ return nullptr;
+ }
+ }
+
+ template <typename Func>
+ [[nodiscard]] bool transformAscii(Func&& transform)
+ {
+ if (isAscii()) {
+ std::forward<Func>(transform)(std::get<Ascii>(m_variant));
+ return true;
+ }
+ return false;
+ }
+
+ [[nodiscard]] constexpr int64_t hash() const noexcept
+ {
+ using fud::ExtUtf8Type;
+ using fud::Utf82Byte;
+ using fud::Utf83Byte;
+ using fud::Utf84Byte;
+
+ if (!valid()) {
+ return -1;
+ }
+
+ constexpr uint8_t OneByteShift = 8;
+ constexpr uint8_t TwoByteShift = 2 * OneByteShift;
+ constexpr uint8_t ThreeByteShift = 3 * OneByteShift;
+
+ switch (static_cast<ExtUtf8Type>(m_variant.index())) {
+ case ExtUtf8Type::Ascii:
+ return std::get<Ascii>(m_variant).characters[0];
+ case ExtUtf8Type::Utf82Byte:
+ return static_cast<int64_t>(std::get<Utf82Byte>(m_variant).characters[0]) << OneByteShift |
+ static_cast<int64_t>(std::get<Utf82Byte>(m_variant).characters[1]);
+ case ExtUtf8Type::Utf83Byte:
+ return static_cast<int64_t>(std::get<Utf83Byte>(m_variant).characters[0]) << TwoByteShift |
+ static_cast<int64_t>(std::get<Utf83Byte>(m_variant).characters[1]) << OneByteShift |
+ static_cast<int64_t>(std::get<Utf83Byte>(m_variant).characters[2]);
+ case ExtUtf8Type::Utf84Byte:
+ return static_cast<int64_t>(std::get<Utf84Byte>(m_variant).characters[0]) << ThreeByteShift |
+ static_cast<int64_t>(std::get<Utf84Byte>(m_variant).characters[1]) << TwoByteShift |
+ static_cast<int64_t>(std::get<Utf84Byte>(m_variant).characters[2]) << OneByteShift |
+ static_cast<int64_t>(std::get<Utf84Byte>(m_variant).characters[3]);
+ default: // unlikely
+ return -1;
+ }
+ }
+
+ constexpr bool operator==(const ExtUtf8& other) const noexcept = default;
+
+ constexpr auto operator<=>(const ExtUtf8& other) const noexcept
+ {
+ auto hasSameAlternative = []<typename T>(const ExtUtf8& lhs, const ExtUtf8& rhs) noexcept {
+ return std::holds_alternative<T>(lhs.m_variant) && std::holds_alternative<T>(rhs.m_variant);
+ };
+
+ auto getSameAlternative = []<typename T>(const ExtUtf8& lhs, const ExtUtf8& rhs) noexcept {
+ return std::get<T>(lhs.m_variant).operator<=>(std::get<T>(rhs.m_variant));
+ };
+
+ if (hasSameAlternative.template operator()<Ascii>(*this, other)) {
+ return getSameAlternative.template operator()<Ascii>(*this, other);
+ }
+
+ if (hasSameAlternative.template operator()<Utf82Byte>(*this, other)) {
+ return getSameAlternative.template operator()<Utf82Byte>(*this, other);
+ }
+
+ if (hasSameAlternative.template operator()<Utf83Byte>(*this, other)) {
+ return getSameAlternative.template operator()<Utf83Byte>(*this, other);
+ }
+
+ if (hasSameAlternative.template operator()<Utf84Byte>(*this, other)) {
+ return getSameAlternative.template operator()<Utf84Byte>(*this, other);
+ }
+
+ if (std::holds_alternative<Ascii>(m_variant)) {
+ return std::strong_ordering::less;
+ }
+
+ if (std::holds_alternative<Ascii>(other.m_variant)) {
+ return std::strong_ordering::greater;
+ }
+
+ if (std::holds_alternative<Utf82Byte>(m_variant)) {
+ return std::strong_ordering::less;
+ }
+
+ if (std::holds_alternative<Utf82Byte>(other.m_variant)) {
+ return std::strong_ordering::greater;
+ }
+
+ if (std::holds_alternative<Utf83Byte>(m_variant)) {
+ return std::strong_ordering::less;
+ }
+
+ return std::strong_ordering::greater;
+ }
+
+ std::optional<Ascii> getAscii() const
+ {
+ if (m_variant.index() == static_cast<size_t>(ExtUtf8Type::Ascii)) {
+ return std::get<Ascii>(m_variant);
+ }
+ return std::nullopt;
+ }
+};
+
+/** \brief Checks if a character is ascii. */
+bool ext_lib_char_is_ascii(char character);
+
+FudStatus ext_lib_utf8_is_ascii(ExtUtf8& character, bool& isAscii);
+
+/** \brief Checks if a character is alphanumeric. */
+bool ext_lib_char_is_alphanumeric(char character);
+
+/** \brief Checks if a character is alphanumeric. */
+FudStatus ext_lib_utf8_is_alphanumeric(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is alphabetic. */
+bool ext_lib_char_is_alpha(char character);
+
+/** \brief Checks if a character is alphabetic. */
+FudStatus ext_lib_utf8_is_alpha(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is lowercase. */
+bool ext_lib_char_is_lowercase(char character);
+
+/** \brief Checks if a character is lowercase. */
+FudStatus ext_lib_utf8_is_lowercase(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is an uppercase character. */
+bool ext_lib_char_is_uppercase(char character);
+
+/** \brief Checks if a character is uppercase. */
+FudStatus ext_lib_utf8_is_uppercase(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is a digit. */
+bool ext_lib_char_is_digit(char character);
+
+/** \brief Checks if a character is a digit. */
+FudStatus ext_lib_utf8_is_digit(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is a hexadecimal character. */
+bool ext_lib_char_is_hex_digit(char character);
+
+/** \brief Checks if a character is a hexadecimal digit. */
+FudStatus ext_lib_utf8_is_hex_digit(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is a control character. */
+bool ext_lib_char_is_control(char character);
+
+/** \brief Checks if a character is a control character. */
+FudStatus ext_lib_utf8_is_control(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is a graphical character. */
+bool ext_lib_char_is_graphical(char character);
+
+/** \brief Checks if a character is a graphical character. */
+FudStatus ext_lib_utf8_is_graphical(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is a space character. */
+bool ext_lib_char_is_space(char character);
+
+/** \brief Checks if a character is a space character. */
+FudStatus ext_lib_utf8_is_space(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is a blank character. */
+bool ext_lib_char_is_blank(char character);
+
+/** \brief Checks if a character is a blank character. */
+FudStatus ext_lib_utf8_is_blank(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is a printable character. */
+bool ext_lib_char_is_printable(char character);
+
+/** \brief Checks if a character is a printable character. */
+FudStatus ext_lib_utf8_is_printable(ExtUtf8* character, bool* pred);
+
+/** \brief Checks if a character is a punctuation character. */
+bool ext_lib_char_is_punctuation(char character);
+
+/** \brief Checks if a character is a punctuation character. */
+FudStatus ext_lib_utf8_is_punctuation(ExtUtf8* character, bool* pred);
+
+uint8_t ext_lib_char_to_lower(uint8_t character);
+
+ExtUtf8* ext_lib_utf8_to_lower(ExtUtf8* character);
+
+uint8_t ext_lib_char_to_upper(uint8_t character);
+
+ExtUtf8* ext_lib_utf8_to_upper(ExtUtf8* character);
+
+} // namespace fud
+
+#endif
diff --git a/include/utf8_iterator.hpp b/include/utf8_iterator.hpp
new file mode 100644
index 0000000..1f9674b
--- /dev/null
+++ b/include/utf8_iterator.hpp
@@ -0,0 +1,39 @@
+#ifndef FUD_UTF8_ITERATOR_HPP
+#define FUD_UTF8_ITERATOR_HPP
+
+#include "string.hpp"
+#include "utf8.hpp"
+
+#include <cstddef>
+#include <optional>
+
+namespace fud {
+
+class Utf8Iterator {
+ private:
+ size_t m_index{0};
+ // NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members)
+ const StringView m_view;
+ // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members)
+
+ public:
+ explicit constexpr Utf8Iterator(const String& extString) : m_view{extString}
+ {
+ }
+
+ explicit constexpr Utf8Iterator(const StringView& view) : m_view{view}
+ {
+ }
+
+ constexpr void reset()
+ {
+ m_index = 0;
+ }
+
+ [[nodiscard]] std::optional<ExtUtf8> peek() const;
+ std::optional<ExtUtf8> next();
+};
+
+} // namespace fud
+
+#endif
diff --git a/source/c_file.cpp b/source/c_file.cpp
new file mode 100644
index 0000000..f64e024
--- /dev/null
+++ b/source/c_file.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include "c_file.hpp"
+
+namespace fud {
+
+CBinaryFile::CBinaryFile(const std::string& filename, CFileMode mode)
+ : m_filename{filename},
+ m_mode{CBinaryFileModeFromFlags(mode)},
+ m_modeFlags{mode}
+{
+}
+
+CBinaryFile::CBinaryFile(const std::string& filename, CFileMode mode, const std::string& extraFlags)
+ : m_filename{filename},
+ m_extraFlags{extraFlags},
+ m_mode{std::string(CBinaryFileModeFromFlags(mode) + extraFlags)},
+ m_modeFlags{mode}
+{
+}
+
+CBinaryFile::~CBinaryFile() {
+ close();
+}
+
+FileResult CBinaryFile::open()
+{
+ m_file = fopen(m_filename.c_str(), m_mode.c_str());
+ return m_file != nullptr ? FileResult::Success : FileResult::Error;
+}
+
+void CBinaryFile::close()
+{
+ if (m_file != nullptr) {
+ fclose(m_file);
+ m_file = nullptr;
+ }
+}
+
+const FILE* CBinaryFile::file() const
+{
+ return m_file;
+}
+
+} // namespace fud
diff --git a/source/libfud.cpp b/source/libfud.cpp
new file mode 100644
index 0000000..fa0e3a0
--- /dev/null
+++ b/source/libfud.cpp
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#include "libfud.hpp"
+
+namespace fud {
+
+void fud() {
+}
+
+} // namespace fud
diff --git a/source/memory.cpp b/source/memory.cpp
new file mode 100644
index 0000000..9f5d358
--- /dev/null
+++ b/source/memory.cpp
@@ -0,0 +1,127 @@
+/*
+ * ExtLib
+ * 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.
+ */
+
+#include "memory.hpp"
+
+#include <cstdint>
+
+namespace fud {
+
+FudStatus copyMem(void* destination, size_t destination_size, const void* source, size_t count)
+{
+ if (anyAreNull(destination, source)) {
+ return FudStatus::NullPointer;
+ }
+
+ if (destination_size < count) {
+ return FudStatus::InvalidInput;
+ }
+
+ auto* destPtr = static_cast<uint8_t*>(destination);
+ const auto* sourcePtr = static_cast<const uint8_t*>(source);
+ for (decltype(destination_size) idx = 0; idx < count; ++idx) {
+ destPtr[idx] = sourcePtr[idx];
+ }
+
+ return FudStatus::Success;
+}
+
+FudStatus compareMem(const void* lhs, size_t destination_size, const void* rhs, size_t count, int* difference)
+{
+ if (anyAreNull(lhs, rhs, difference)) {
+ return FudStatus::NullPointer;
+ }
+
+ if (destination_size < count) {
+ return FudStatus::InvalidInput;
+ }
+
+ int localDifference = 0;
+ // NOLINTBEGIN(readability-magic-numbers)
+ for (decltype(destination_size) idx = 0; idx < count; idx++) {
+ localDifference = static_cast<const uint8_t*>(lhs)[idx] - static_cast<const uint8_t*>(rhs)[idx];
+ if (localDifference != 0) {
+ *difference = localDifference;
+ return FudStatus::Success;
+ }
+ }
+ *difference = localDifference;
+
+ return FudStatus::Success;
+}
+
+Result<int, FudStatus> compareMem(const void* lhs, size_t destination_size, const void* rhs, size_t count)
+{
+ int difference = 0;
+ auto status = compareMem(lhs, destination_size, rhs, count, &difference);
+ if (status != FudStatus::Success)
+ {
+ return Result<int, FudStatus>::error(status);
+ }
+
+ return Result<int, FudStatus>::okay(difference);
+}
+
+FudStatus setMemory(void* data, size_t dataSize, uint8_t pattern, size_t count)
+{
+ if (data == nullptr)
+ {
+ return FudStatus::NullPointer;
+ }
+
+ if (count > dataSize)
+ {
+ return FudStatus::InvalidInput;
+ }
+
+ for (size_t idx = 0; idx < count; ++idx)
+ {
+ static_cast<uint8_t*>(data)[idx] = pattern;
+ }
+
+ return FudStatus::Success;
+}
+
+FudStatus setMemory(
+ void* data,
+ size_t collectionCount,
+ size_t eltOffset,
+ size_t eltSize,
+ uint8_t pattern,
+ size_t eltCount)
+{
+ if (eltOffset >= collectionCount)
+ {
+ return FudStatus::InvalidInput;
+ }
+
+ if (eltOffset + eltCount > collectionCount)
+ {
+ return FudStatus::InvalidInput;
+ }
+
+ auto dataSize = collectionCount * eltSize;
+ auto byteOffset = eltOffset * eltSize;
+ auto byteCount = eltCount * eltSize;
+
+ auto remainingSize = dataSize - byteOffset;
+
+ auto* offsetData = static_cast<uint8_t*>(data) + byteOffset;
+ return setMemory(offsetData, remainingSize, pattern, byteCount);
+}
+
+} // namespace fud
diff --git a/source/string.cpp b/source/string.cpp
new file mode 100644
index 0000000..a121418
--- /dev/null
+++ b/source/string.cpp
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+
+#include "string.hpp"
diff --git a/source/utf8.cpp b/source/utf8.cpp
new file mode 100644
index 0000000..c94ac1f
--- /dev/null
+++ b/source/utf8.cpp
@@ -0,0 +1,343 @@
+/*
+ * 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.
+ */
+
+#include "utf8.hpp"
+
+#include "string.hpp"
+
+#include <new> // IWYU pragma: keep - this is for placement new overloads.
+
+namespace fud {
+
+ExtUtf8 ExtUtf8::fromString(const String& fudString, size_t index) noexcept
+{
+ if (!fudString.valid()) {
+ return invalidAscii();
+ }
+
+
+ return fromStringView(StringView{fudString}, index);
+}
+
+ExtUtf8 ExtUtf8::fromStringView(const StringView& view, size_t index) noexcept
+{
+ return fromStringView(StringView{view}, index);
+}
+
+ExtUtf8 ExtUtf8::fromStringView(StringView&& view, size_t index) noexcept
+{
+ auto len = view.length();
+ const auto* data = view.data();
+ if (data == nullptr) {
+ return invalidAscii();
+ }
+
+ ExtUtf8 localChar{Ascii{data[index]}};
+ if (localChar.valid()) {
+ return localChar;
+ }
+
+ if (index + 1 < len) {
+ localChar.m_variant = Utf82Byte{data[index], data[index + 1]};
+ }
+ if (localChar.valid()) {
+ return localChar;
+ }
+
+ if (index + 2 < len) {
+ localChar.m_variant = Utf83Byte{data[index], data[index + 1], data[index + 2]};
+ }
+ if (localChar.valid()) {
+ return localChar;
+ }
+
+ if (index + 3 < len) {
+ localChar.m_variant = Utf84Byte{data[index], data[index + 1], data[index + 2], data[index + 3]};
+ }
+ if (localChar.valid()) {
+ return localChar;
+ }
+
+ return invalidAscii();
+}
+
+bool ext_lib_char_is_ascii(char character)
+{
+ return static_cast<uint8_t>(character & ~ASCII_MASK) == 0;
+}
+
+FudStatus ext_lib_utf8_is_ascii(ExtUtf8* character, bool* isAscii)
+{
+ if (anyAreNull(character, isAscii)) {
+ return FudStatus::NullPointer;
+ }
+
+ *isAscii = character->getType() == ExtUtf8Type::Ascii && character->valid();
+
+ return FudStatus::Success;
+}
+
+namespace impl {
+
+/* Assumes that predicate is not a null pointer! */
+template <typename Predicate>
+inline FudStatus isAsciiPredicate(ExtUtf8* character, bool* pred, Predicate&& predicate)
+{
+ if (anyAreNull(character, pred)) {
+ return FudStatus::NullPointer;
+ }
+
+ auto maybeAscii = character->getAscii();
+ if (!maybeAscii.has_value()) {
+ return FudStatus::InvalidInput;
+ }
+
+ auto asciiChar = *maybeAscii;
+ *pred = std::forward<Predicate>(predicate)(asciiChar.asChar());
+
+ return FudStatus::Success;
+}
+
+} // namespace impl
+
+bool ext_lib_char_is_alphanumeric(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ if (ext_lib_char_is_alpha(character)) {
+ return true;
+ }
+
+ return ext_lib_char_is_digit(character);
+}
+
+FudStatus ext_lib_utf8_is_alphanumeric(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_alphanumeric);
+}
+
+bool ext_lib_char_is_alpha(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ if (ext_lib_char_is_uppercase(character)) {
+ return true;
+ }
+
+ return ext_lib_char_is_lowercase(character);
+}
+
+FudStatus ext_lib_utf8_is_alpha(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_alpha);
+}
+
+bool ext_lib_char_is_lowercase(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ return 'a' <= character && character <= 'z';
+}
+
+FudStatus ext_lib_utf8_is_lowercase(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_lowercase);
+}
+
+bool ext_lib_char_is_uppercase(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ return 'A' <= character && character <= 'Z';
+}
+
+FudStatus ext_lib_utf8_is_uppercase(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_uppercase);
+}
+
+bool ext_lib_char_is_digit(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ return '0' <= character && character <= '9';
+}
+
+FudStatus ext_lib_utf8_is_digit(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_digit);
+}
+
+bool ext_lib_char_is_hex_digit(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ return ('0' <= character && character <= '9') || ('a' <= character && character <= 'f') ||
+ ('A' <= character && character <= 'F');
+}
+
+FudStatus ext_lib_utf8_is_hex_digit(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_hex_digit);
+}
+
+bool ext_lib_char_is_control(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ constexpr char maxControlChar = 0x1F;
+ constexpr const char deleteChar = 0x7F;
+ return ((static_cast<uint8_t>(character) <= maxControlChar)) || character == deleteChar;
+}
+
+FudStatus ext_lib_utf8_is_control(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_control);
+}
+
+bool ext_lib_char_is_graphical(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ return ext_lib_char_is_alphanumeric(character) || ext_lib_char_is_punctuation(character);
+}
+
+FudStatus ext_lib_utf8_is_graphical(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_graphical);
+}
+
+bool ext_lib_char_is_space(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ return character == ' ' || character == '\t' || character == '\n' || character == '\r' || character == '\v';
+}
+
+FudStatus ext_lib_utf8_is_space(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_space);
+}
+
+bool ext_lib_char_is_blank(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ return character == ' ' || character == '\t';
+}
+
+FudStatus ext_lib_utf8_is_blank(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_blank);
+}
+
+bool ext_lib_char_is_printable(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ return (character >= ' ' && character <= '~');
+}
+
+FudStatus ext_lib_utf8_is_printable(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_printable);
+}
+
+bool ext_lib_char_is_punctuation(char character)
+{
+ if (!ext_lib_char_is_ascii(character)) {
+ return false;
+ }
+
+ return (character >= '!' && character <= '/') || (character >= ':' && character <= '@') ||
+ (character >= '[' && character <= '`') || (character >= '{' && character <= '~');
+}
+
+FudStatus ext_lib_utf8_is_punctuation(ExtUtf8* character, bool* pred)
+{
+ return impl::isAsciiPredicate(character, pred, ext_lib_char_is_punctuation);
+}
+
+uint8_t ext_lib_char_to_lower(uint8_t character)
+{
+ if (ext_lib_char_is_uppercase(static_cast<char>(character))) {
+ constexpr uint8_t lowerA = 'a';
+ constexpr uint8_t upperA = 'A';
+ return static_cast<uint8_t>(character - upperA) + lowerA;
+ }
+ return character;
+}
+
+ExtUtf8* ext_lib_utf8_to_lower(ExtUtf8* character)
+{
+ if (character == nullptr) {
+ return character;
+ }
+
+ static_cast<void>(character->transformAscii([](Ascii& ascii) {
+ ascii = Ascii{ext_lib_char_to_lower(static_cast<uint8_t>(ascii.asChar()))};
+ }));
+
+ return character;
+}
+
+uint8_t ext_lib_char_to_upper(uint8_t character)
+{
+ if (ext_lib_char_is_lowercase(static_cast<char>(character))) {
+ constexpr uint8_t lowerA = 'a';
+ constexpr uint8_t upperA = 'A';
+ return static_cast<uint8_t>(character - lowerA) + upperA;
+ }
+ return character;
+}
+
+ExtUtf8* ext_lib_utf8_to_upper(ExtUtf8* character)
+{
+ if (character == nullptr) {
+ return character;
+ }
+
+ static_cast<void>(character->transformAscii([](Ascii& ascii) {
+ ascii = Ascii{ext_lib_char_to_upper(static_cast<uint8_t>(ascii.asChar()))};
+ }));
+
+ return character;
+}
+
+} // namespace ext_lib
diff --git a/source/utf8_iterator.cpp b/source/utf8_iterator.cpp
new file mode 100644
index 0000000..e439687
--- /dev/null
+++ b/source/utf8_iterator.cpp
@@ -0,0 +1,38 @@
+#include "utf8_iterator.hpp"
+
+namespace fud {
+
+std::optional<ExtUtf8> Utf8Iterator::peek() const
+{
+ if (m_index >= m_view.length()) {
+ return std::nullopt;
+ }
+
+ auto utf8 = ExtUtf8::fromStringView(m_view, m_index);
+
+ if (!utf8.valid()) {
+ return std::nullopt;
+ }
+
+ return utf8;
+}
+
+std::optional<ExtUtf8> Utf8Iterator::next()
+{
+ if (m_index >= m_view.length()) {
+ m_index = m_view.length();
+ return std::nullopt;
+ }
+
+ auto utf8 = ExtUtf8::fromStringView(m_view, m_index);
+
+ if (!utf8.valid()) {
+ m_index = m_view.length();
+ return std::nullopt;
+ }
+
+ m_index += utf8.size();
+ return utf8;
+}
+
+} // namespace fud