diff options
author | Dominick Allen <djallen@librehumanitas.org> | 2024-09-22 10:19:15 -0500 |
---|---|---|
committer | Dominick Allen <djallen@librehumanitas.org> | 2024-09-22 10:19:15 -0500 |
commit | bf81e34921e3e30b05313efbcf5c9fa839cb7c05 (patch) | |
tree | b56a343e59164bc347232669e8bb808cf3c4f4ef |
Initial commit.
-rw-r--r-- | .clang-format | 201 | ||||
-rw-r--r-- | .clang-tidy | 33 | ||||
-rw-r--r-- | .gitignore | 11 | ||||
-rw-r--r-- | CMakeLists.txt | 50 | ||||
-rw-r--r-- | LICENSE | 196 | ||||
-rw-r--r-- | README.org | 1 | ||||
-rw-r--r-- | cmake/warnings.cmake | 66 | ||||
-rw-r--r-- | include/array.hpp | 113 | ||||
-rw-r--r-- | include/c_file.hpp | 103 | ||||
-rw-r--r-- | include/fud_type_traits.hpp | 80 | ||||
-rw-r--r-- | include/libfud.hpp | 30 | ||||
-rw-r--r-- | include/memory.hpp | 140 | ||||
-rw-r--r-- | include/result.hpp | 83 | ||||
-rw-r--r-- | include/status.hpp | 106 | ||||
-rw-r--r-- | include/string.hpp | 153 | ||||
-rw-r--r-- | include/unique_array.hpp | 68 | ||||
-rw-r--r-- | include/utf8.hpp | 557 | ||||
-rw-r--r-- | include/utf8_iterator.hpp | 39 | ||||
-rw-r--r-- | source/c_file.cpp | 60 | ||||
-rw-r--r-- | source/libfud.cpp | 24 | ||||
-rw-r--r-- | source/memory.cpp | 127 | ||||
-rw-r--r-- | source/string.cpp | 19 | ||||
-rw-r--r-- | source/utf8.cpp | 343 | ||||
-rw-r--r-- | source/utf8_iterator.cpp | 38 |
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() @@ -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 |