diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b444d8e --- /dev/null +++ b/.clang-format @@ -0,0 +1,270 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +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 +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 3 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +PPIndentWidth: -1 +QualifierAlignment: Leave +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: pb + BasedOnStyle: google +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +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: 2 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +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..4457ad3 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,190 @@ +Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,bugprone-*,clang-analyzer-*,misc-*,performance-*,portability-*,readability-*,-readability-magic-numbers,-readability-static-accessed-through-instance,-readability-misplaced-array-index,google-*' +WarningsAsErrors: '' +HeaderFilterRegex: '' +AnalyzeTemporaryDtors: false +FormatStyle: none +CheckOptions: + bugprone-argument-comment.CommentBoolLiterals: '0' + bugprone-argument-comment.CommentCharacterLiterals: '0' + bugprone-argument-comment.CommentFloatLiterals: '0' + bugprone-argument-comment.CommentIntegerLiterals: '0' + bugprone-argument-comment.CommentNullPtrs: '0' + bugprone-argument-comment.CommentStringLiterals: '0' + bugprone-argument-comment.CommentUserDefinedLiterals: '0' + bugprone-argument-comment.IgnoreSingleArgument: '0' + bugprone-argument-comment.StrictMode: '0' + bugprone-assert-side-effect.AssertMacros: assert,NSAssert,NSCAssert + bugprone-assert-side-effect.CheckFunctionCalls: 'false' + bugprone-assert-side-effect.IgnoredFunctions: __builtin_expect + bugprone-dangling-handle.HandleClasses: 'std::basic_string_view;std::experimental::basic_string_view' + bugprone-dynamic-static-initializers.HeaderFileExtensions: ';h;hh;hpp;hxx' + bugprone-easily-swappable-parameters.IgnoredParameterNames: '"";iterator;Iterator;begin;Begin;end;End;first;First;last;Last;lhs;LHS;rhs;RHS' + bugprone-easily-swappable-parameters.IgnoredParameterTypeSuffixes: 'bool;Bool;_Bool;it;It;iterator;Iterator;inputit;InputIt;forwardit;ForwardIt;bidirit;BidirIt;constiterator;const_iterator;Const_Iterator;Constiterator;ConstIterator;RandomIt;randomit;random_iterator;ReverseIt;reverse_iterator;reverse_const_iterator;ConstReverseIterator;Const_Reverse_Iterator;const_reverse_iterator;Constreverseiterator;constreverseiterator' + bugprone-easily-swappable-parameters.MinimumLength: '2' + bugprone-easily-swappable-parameters.ModelImplicitConversions: 'true' + bugprone-easily-swappable-parameters.NamePrefixSuffixSilenceDissimilarityTreshold: '1' + bugprone-easily-swappable-parameters.QualifiersMix: 'false' + bugprone-easily-swappable-parameters.SuppressParametersUsedTogether: 'true' + bugprone-exception-escape.FunctionsThatShouldNotThrow: '' + bugprone-exception-escape.IgnoredExceptions: '' + bugprone-implicit-widening-of-multiplication-result.IncludeStyle: llvm + bugprone-implicit-widening-of-multiplication-result.UseCXXHeadersInCppSources: 'true' + bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources: 'true' + bugprone-misplaced-widening-cast.CheckImplicitCasts: 'false' + bugprone-narrowing-conversions.IgnoreConversionFromTypes: '' + bugprone-narrowing-conversions.PedanticMode: 'false' + bugprone-narrowing-conversions.WarnOnEquivalentBitWidth: 'true' + bugprone-narrowing-conversions.WarnOnFloatingPointNarrowingConversion: 'true' + bugprone-narrowing-conversions.WarnOnIntegerNarrowingConversion: 'true' + bugprone-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion: 'true' + bugprone-narrowing-conversions.WarnWithinTemplateInstantiation: 'false' + bugprone-not-null-terminated-result.WantToUseSafeFunctions: 'true' + bugprone-reserved-identifier.AggressiveDependentMemberLookup: 'false' + bugprone-reserved-identifier.AllowedIdentifiers: '' + bugprone-reserved-identifier.Invert: 'false' + bugprone-signal-handler.AsyncSafeFunctionSet: POSIX + bugprone-signed-char-misuse.CharTypdefsToIgnore: '' + bugprone-signed-char-misuse.DiagnoseSignedUnsignedCharComparisons: 'true' + bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant: 'true' + bugprone-sizeof-expression.WarnOnSizeOfConstant: 'true' + bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression: 'false' + bugprone-sizeof-expression.WarnOnSizeOfThis: 'true' + bugprone-string-constructor.LargeLengthThreshold: '8388608' + bugprone-string-constructor.StringNames: '::std::basic_string;::std::basic_string_view' + bugprone-string-constructor.WarnOnLargeLength: 'true' + bugprone-stringview-nullptr.IncludeStyle: llvm + bugprone-suspicious-enum-usage.StrictMode: 'false' + bugprone-suspicious-include.HeaderFileExtensions: ';h;hh;hpp;hxx' + bugprone-suspicious-include.ImplementationFileExtensions: 'c;cc;cpp;cxx' + bugprone-suspicious-missing-comma.MaxConcatenatedTokens: '5' + bugprone-suspicious-missing-comma.RatioThreshold: '0.200000' + bugprone-suspicious-missing-comma.SizeThreshold: '5' + bugprone-suspicious-string-compare.StringCompareLikeFunctions: '' + bugprone-suspicious-string-compare.WarnOnImplicitComparison: 'true' + bugprone-suspicious-string-compare.WarnOnLogicalNotComparison: 'false' + bugprone-too-small-loop-variable.MagnitudeBitsUpperLimit: '16' + bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField: 'true' + bugprone-unused-return-value.CheckedFunctions: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty;::std::back_inserter;::std::distance;::std::find;::std::find_if;::std::inserter;::std::lower_bound;::std::make_pair;::std::map::count;::std::map::find;::std::map::lower_bound;::std::multimap::equal_range;::std::multimap::upper_bound;::std::set::count;::std::set::find;::std::setfill;::std::setprecision;::std::setw;::std::upper_bound;::std::vector::at;::bsearch;::ferror;::feof;::isalnum;::isalpha;::isblank;::iscntrl;::isdigit;::isgraph;::islower;::isprint;::ispunct;::isspace;::isupper;::iswalnum;::iswprint;::iswspace;::isxdigit;::memchr;::memcmp;::strcmp;::strcoll;::strncmp;::strpbrk;::strrchr;::strspn;::strstr;::wcscmp;::access;::bind;::connect;::difftime;::dlsym;::fnmatch;::getaddrinfo;::getopt;::htonl;::htons;::iconv_open;::inet_addr;::isascii;::isatty;::mmap;::newlocale;::openat;::pathconf;::pthread_equal;::pthread_getspecific;::pthread_mutex_trylock;::readdir;::readlink;::recvmsg;::regexec;::scandir;::semget;::setjmp;::shm_open;::shmget;::sigismember;::strcasecmp;::strsignal;::ttyname' + cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU' + 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;' + cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false' + cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false' + cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true' + google-build-namespaces.HeaderFileExtensions: ';h;hh;hpp;hxx' + google-global-names-in-headers.HeaderFileExtensions: ';h;hh;hpp;hxx' + google-readability-braces-around-statements.ShortStatementLines: '1' + google-readability-function-size.BranchThreshold: '4294967295' + google-readability-function-size.LineThreshold: '4294967295' + google-readability-function-size.NestingThreshold: '4294967295' + google-readability-function-size.ParameterThreshold: '4294967295' + google-readability-function-size.StatementThreshold: '800' + google-readability-function-size.VariableThreshold: '4294967295' + google-readability-namespace-comments.ShortNamespaceLines: '10' + google-readability-namespace-comments.SpacesBeforeComments: '2' + google-runtime-int.SignedTypePrefix: int + google-runtime-int.TypeSuffix: '' + google-runtime-int.UnsignedTypePrefix: uint + llvm-else-after-return.WarnOnConditionVariables: 'false' + llvm-else-after-return.WarnOnUnfixable: 'false' + llvm-qualified-auto.AddConstToQualified: 'false' + misc-const-correctness.AnalyzeReferences: 'false' + misc-const-correctness.AnalyzeValues: 'false' + misc-const-correctness.TransformPointersAsValues: 'false' + misc-const-correctness.TransformReferences: 'false' + misc-const-correctness.TransformValues: 'false' + misc-const-correctness.WarnPointersAsValues: 'false' + misc-definitions-in-headers.HeaderFileExtensions: ';h;hh;hpp;hxx' + misc-definitions-in-headers.UseHeaderFileExtension: 'true' + misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true' + misc-non-private-member-variables-in-classes.IgnorePublicMemberVariables: 'false' + misc-throw-by-value-catch-by-reference.CheckThrowTemporaries: 'true' + misc-throw-by-value-catch-by-reference.MaxSize: '-1' + misc-throw-by-value-catch-by-reference.WarnOnLargeObjects: 'false' + misc-uniqueptr-reset-release.IncludeStyle: llvm + misc-unused-parameters.StrictMode: 'false' + modernize-loop-convert.MaxCopySize: '16' + modernize-loop-convert.MinConfidence: reasonable + modernize-loop-convert.NamingStyle: CamelCase + modernize-pass-by-value.IncludeStyle: llvm + modernize-replace-auto-ptr.IncludeStyle: llvm + modernize-use-nullptr.NullMacros: 'NULL' + performance-faster-string-find.StringLikeClasses: '::std::basic_string;::std::basic_string_view' + performance-for-range-copy.AllowedTypes: '' + performance-for-range-copy.WarnOnAllAutoCopies: 'false' + performance-inefficient-string-concatenation.StrictMode: 'false' + performance-inefficient-vector-operation.EnableProto: 'false' + performance-inefficient-vector-operation.VectorLikeClasses: '::std::vector' + performance-move-const-arg.CheckMoveToConstRef: 'true' + performance-move-const-arg.CheckTriviallyCopyableMove: 'true' + performance-no-automatic-move.AllowedTypes: '' + performance-type-promotion-in-math-fn.IncludeStyle: llvm + performance-unnecessary-copy-initialization.AllowedTypes: '' + performance-unnecessary-copy-initialization.ExcludedContainerTypes: '' + performance-unnecessary-value-param.AllowedTypes: '' + performance-unnecessary-value-param.IncludeStyle: llvm + portability-restrict-system-includes.Includes: '*' + portability-simd-intrinsics.Std: '' + portability-simd-intrinsics.Suggest: 'false' + readability-braces-around-statements.ShortStatementLines: '0' + readability-else-after-return.WarnOnConditionVariables: 'true' + readability-else-after-return.WarnOnUnfixable: 'true' + readability-function-cognitive-complexity.DescribeBasicIncrements: 'true' + readability-function-cognitive-complexity.IgnoreMacros: 'false' + readability-function-cognitive-complexity.Threshold: '25' + readability-function-size.BranchThreshold: '4294967295' + readability-function-size.LineThreshold: '4294967295' + readability-function-size.NestingThreshold: '4294967295' + readability-function-size.ParameterThreshold: '4294967295' + readability-function-size.StatementThreshold: '800' + readability-function-size.VariableThreshold: '4294967295' + readability-identifier-length.IgnoredExceptionVariableNames: '^[e]$' + readability-identifier-length.IgnoredLoopCounterNames: '^[ijk_]$' + readability-identifier-length.IgnoredParameterNames: '^[n]$' + readability-identifier-length.IgnoredVariableNames: '' + readability-identifier-length.MinimumExceptionNameLength: '2' + readability-identifier-length.MinimumLoopCounterNameLength: '2' + readability-identifier-length.MinimumParameterNameLength: '3' + readability-identifier-length.MinimumVariableNameLength: '3' + readability-identifier-naming.AggressiveDependentMemberLookup: 'false' + readability-identifier-naming.GetConfigPerFile: 'true' + readability-identifier-naming.IgnoreFailedSplit: 'false' + readability-identifier-naming.IgnoreMainLikeFunctions: 'false' + readability-implicit-bool-conversion.AllowIntegerConditions: 'false' + readability-implicit-bool-conversion.AllowPointerConditions: 'false' + readability-inconsistent-declaration-parameter-name.IgnoreMacros: 'true' + readability-inconsistent-declaration-parameter-name.Strict: 'false' + readability-qualified-auto.AddConstToQualified: 'true' + readability-redundant-declaration.IgnoreMacros: 'true' + readability-redundant-member-init.IgnoreBaseInCopyConstructors: 'false' + readability-redundant-smartptr-get.IgnoreMacros: 'true' + readability-redundant-string-init.StringNames: '::std::basic_string_view;::std::basic_string' + readability-simplify-boolean-expr.ChainedConditionalAssignment: 'false' + readability-simplify-boolean-expr.ChainedConditionalReturn: 'false' + readability-simplify-boolean-expr.SimplifyDeMorgan: 'true' + readability-simplify-boolean-expr.SimplifyDeMorganRelaxed: 'false' + readability-simplify-subscript-expr.Types: '::std::basic_string;::std::basic_string_view;::std::vector;::std::array' + readability-suspicious-call-argument.Abbreviation: 'true' + readability-suspicious-call-argument.Abbreviations: 'arr=array;cnt=count;idx=index;src=source;stmt=statement;cpy=copy;dest=destination;dist=distancedst=distance;ptr=pointer;wdth=width;str=string;ln=line;srv=server;attr=attribute;ref=reference;buf=buffer;col=column;nr=number;vec=vector;len=length;elem=element;val=value;i=index;var=variable;hght=height;cl=client;num=number;pos=position;lst=list;addr=address' + readability-suspicious-call-argument.Dice: 'true' + readability-suspicious-call-argument.DiceDissimilarBelow: '60' + readability-suspicious-call-argument.DiceSimilarAbove: '70' + readability-suspicious-call-argument.Equality: 'true' + readability-suspicious-call-argument.JaroWinkler: 'true' + readability-suspicious-call-argument.JaroWinklerDissimilarBelow: '75' + readability-suspicious-call-argument.JaroWinklerSimilarAbove: '85' + readability-suspicious-call-argument.Levenshtein: 'true' + readability-suspicious-call-argument.LevenshteinDissimilarBelow: '50' + readability-suspicious-call-argument.LevenshteinSimilarAbove: '66' + readability-suspicious-call-argument.MinimumIdentifierNameLength: '3' + readability-suspicious-call-argument.Prefix: 'true' + readability-suspicious-call-argument.PrefixDissimilarBelow: '25' + readability-suspicious-call-argument.PrefixSimilarAbove: '30' + readability-suspicious-call-argument.Substring: 'true' + readability-suspicious-call-argument.SubstringDissimilarBelow: '40' + readability-suspicious-call-argument.SubstringSimilarAbove: '50' + readability-suspicious-call-argument.Suffix: 'true' + readability-suspicious-call-argument.SuffixDissimilarBelow: '25' + readability-suspicious-call-argument.SuffixSimilarAbove: '30' + readability-uniqueptr-delete-release.PreferResetCall: 'false' + readability-uppercase-literal-suffix.IgnoreMacros: 'true' + readability-uppercase-literal-suffix.NewSuffixes: 'L;UL;ULL' \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..4c6a7ed --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,43 @@ +name: Build + +on: + push: + branches: + - main + pull_request: + +# The concurrency spec means that we'll only run one set of jobs per pull request or push to main. +# If a new push or pull request comes in while a job is running, all jobs in the concurrency group will be cancelled. +concurrency: + group: build-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build: + strategy: + matrix: + platform: [test-native] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + .pio/libdeps + key: ${{ runner.os }}-pio + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + - name: Build ${{ matrix.platform }} + id: build + run: | + pio run -e ${{ matrix.platform }} diff --git a/.github/workflows/clangformat.yml b/.github/workflows/clangformat.yml new file mode 100644 index 0000000..80faa40 --- /dev/null +++ b/.github/workflows/clangformat.yml @@ -0,0 +1,27 @@ +name: ClangFormat + +# The concurrency spec means that we'll only run one set of jobs per pull request or push to main. +# If a new push or pull request comes in while a job is running, all jobs in the concurrency group will be cancelled. +concurrency: + group: clang-format-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +on: + push: + branches: + - main + pull_request: + +jobs: + clang-format: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: clang-format style check + uses: jidicula/clang-format-action@v4.11.0 + with: + clang-format-version: '17' + check-path: 'src' + fallback-style: 'llvm' # optional diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml new file mode 100644 index 0000000..d6ac187 --- /dev/null +++ b/.github/workflows/static_analysis.yml @@ -0,0 +1,43 @@ +name: Static Analysis + +# The concurrency spec means that we'll only run one set of jobs per pull request or push to main. +# If a new push or pull request comes in while a job is running, all jobs in the concurrency group will be cancelled. +concurrency: + group: static-analysis-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +on: + push: + branches: + - main + pull_request: + +jobs: + analyze: + strategy: + matrix: + platform: [test-native] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + .pio/libdeps + key: ${{ runner.os }}-pio + + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + - name: Check ${{ matrix.platform }} + run: | + pio check -e ${{ matrix.platform }} --fail-on-defect low diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..51dd0a5 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,39 @@ +name: Test + +# The concurrency spec means that we'll only run one set of jobs per pull request or push to main. +# If a new push or pull request comes in while a job is running, all jobs in the concurrency group will be cancelled. +concurrency: + group: test-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +on: + push: + branches: + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/cache@v3 + with: + path: | + ~/.cache/pip + ~/.platformio/.cache + .pio/libdeps + key: ${{ runner.os }}-pio + + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install PlatformIO Core + run: pip install --upgrade platformio + + - name: Run tests + run: | + pio test -e test-native diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..533df33 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,38 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +src_dir = src +test_dir = test +default_envs = test-native + +[env] +lib_deps = + bblanchon/ArduinoJson @ ^7.0.2 +check_tool = clangtidy, cppcheck +check_flags = + clangtidy: --checks '-*,bugprone-*,clang-analyzer-*,misc-*,performance-*,portability-*,readability-*,-readability-magic-numbers,-readability-static-accessed-through-instance,-readability-misplaced-array-index,google-*' + cppcheck: --enable=all --disable=unusedFunction --inline-suppr --std=c++20 --suppress=*:*/.pio/* --suppress=unmatchedSuppression:*/.pio/* --suppress=missingIncludeSystem:* --suppress=*:*/test/* +check_skip_packages = no +check_src_filters = + +build_flags = -Wall -DENABLE_LOGGING +platform_packages = + platformio/tool-clangtidy@^1.150005.0 + platformio/tool-cppcheck@^1.21100.230717 + +[env:test-native] +platform = native +build_type = test +test_framework = doctest +test_build_src = true +lib_deps = + bblanchon/ArduinoJson @ ^7.0.2 +build_flags = -std=gnu++17 -I ${platformio.test_dir}/helpers +debug_test = test_template diff --git a/src/ministache.cpp b/src/ministache.cpp index e4589f8..caae55b 100644 --- a/src/ministache.cpp +++ b/src/ministache.cpp @@ -34,14 +34,16 @@ struct DelimiterPair { String close; }; +using ministache::PartialList; + static std::pair renderWithContextStack( const String& templateContents, size_t position, std::vector& contextStack, - const std::vector>& partials, bool renderingEnabled, + const PartialList& partials, bool renderingEnabled, const DelimiterPair& initialDelimiters = {.open = "{{", .close = "}}"}); String ministache::render(const String& templateContents, const ArduinoJson::JsonDocument& data, - const std::vector>& partials) { + const PartialList& partials) { // set up context stack // root of stack: object/array/null/string // each recursive call to renderWithContextStack does the following: @@ -60,16 +62,20 @@ String ministache::render(const String& templateContents, const ArduinoJson::Jso // Tokens that don't output content and are standalone (i.e. not surrounded by non-whitespace) // should not leave blank lines in the content. This function returns the range of the template // that should be excluded from the output when the token is standalone. +struct Range { + size_t start; + size_t end; +}; struct ExclusionRange { size_t start; size_t end; size_t indentation; }; -static ExclusionRange getExclusionRangeForToken(const String& templateContents, size_t tokenStart, - size_t tokenEnd, TokenType tokenType) { +static ExclusionRange getExclusionRangeForToken(const String& templateContents, const Range& range, + TokenType tokenType) { ExclusionRange defaultResult{ - .start = tokenStart, - .end = tokenEnd, + .start = range.start, + .end = range.end, .indentation = 0, }; @@ -78,20 +84,20 @@ static ExclusionRange getExclusionRangeForToken(const String& templateContents, return defaultResult; } - auto lineStart = templateContents.lastIndexOf('\n', static_cast(tokenStart)) + - 1; // 0 if this is the first line - auto lineEnd = templateContents.indexOf('\n', tokenEnd); // -1 if this is the last line + auto lineStart = templateContents.lastIndexOf('\n', static_cast(range.start)) + + 1; // 0 if this is the first line + auto lineEnd = templateContents.indexOf('\n', range.end); // -1 if this is the last line if (lineEnd == -1) { lineEnd = static_cast(templateContents.length()); } bool standalone = true; - for (int i = lineStart; i < static_cast(tokenStart); i++) { + for (int i = lineStart; i < static_cast(range.start); i++) { if (isspace(templateContents[i]) == 0) { standalone = false; break; } } - for (int i = static_cast(tokenEnd); i < lineEnd; i++) { + for (int i = static_cast(range.end); i < lineEnd; i++) { if (isspace(templateContents[i]) == 0) { standalone = false; break; @@ -103,9 +109,9 @@ static ExclusionRange getExclusionRangeForToken(const String& templateContents, // If the token is on the very last line of the template, then remove the preceding newline, // but only if there's no leading whitespace before the token - size_t indentation = tokenStart - lineStart; + size_t indentation = range.start - lineStart; if (lineEnd == static_cast(templateContents.length()) && lineStart > 0 && - lineStart == static_cast(tokenStart)) { + lineStart == static_cast(range.start)) { lineStart--; // Also remove any preceding carriage return if (lineStart > 0 && templateContents[lineStart] == '\r') { @@ -149,7 +155,6 @@ static bool isValidContextForPath(const JsonVariantConst& context, static JsonVariantConst lookupTokenInContext(const std::vector& path, const JsonVariantConst& context) { - String result; auto node = context; for (size_t i = 0; i < path.size(); i++) { node = node[path[i]]; @@ -163,10 +168,12 @@ static JsonVariantConst lookupTokenInContextStack( return contextStack.back(); } std::vector path = splitPath(name); - for (auto context = contextStack.rbegin(); context != contextStack.rend(); context++) { - if (isValidContextForPath(*context, path)) { - return lookupTokenInContext(path, *context); - } + auto context = std::find_if( + contextStack.rbegin(), contextStack.rend(), + [&](const JsonVariantConst& context) { return isValidContextForPath(context, path); }); + + if (context != contextStack.rend()) { + return lookupTokenInContext(path, *context); } return JsonVariantConst(); } @@ -287,8 +294,7 @@ std::pair parseTokenAtPoint(const String& templateContents, size_ static std::pair renderWithContextStack( const String& templateContents, size_t position, std::vector& contextStack, - const std::vector>& partials, bool renderingEnabled, - const DelimiterPair& initialDelimiters) { + const PartialList& partials, bool renderingEnabled, const DelimiterPair& initialDelimiters) { String result; DelimiterPair delimiters = initialDelimiters; while (position < templateContents.length()) { @@ -305,7 +311,8 @@ static std::pair renderWithContextStack( const auto parsedToken = parseTokenAtPoint(templateContents, nextToken, delimiters); const auto tokenRenderExtents = getExclusionRangeForToken( - templateContents, nextToken, parsedToken.second, parsedToken.first.type); + templateContents, {.start = static_cast(nextToken), .end = parsedToken.second}, + parsedToken.first.type); if (renderingEnabled) { result.concat(templateContents.substring(position, tokenRenderExtents.start)); } diff --git a/src/ministache.hpp b/src/ministache.hpp index f45e790..78e608b 100644 --- a/src/ministache.hpp +++ b/src/ministache.hpp @@ -12,8 +12,10 @@ namespace ministache { +typedef std::vector> PartialList; + String render(const String& templateContents, const ArduinoJson::JsonDocument& data, - const std::vector>& partials = {}); + const PartialList& partials = {}); }; // namespace ministache diff --git a/test/helpers/Allocators.hpp b/test/helpers/Allocators.hpp new file mode 100644 index 0000000..5df98ed --- /dev/null +++ b/test/helpers/Allocators.hpp @@ -0,0 +1,283 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +#include +#include +#include + +#include + +struct FailingAllocator : ArduinoJson::Allocator { + static FailingAllocator* instance() { + static FailingAllocator allocator; + return &allocator; + } + + private: + FailingAllocator() = default; + ~FailingAllocator() = default; + + void* allocate(size_t) override { + return nullptr; + } + + void deallocate(void*) override {} + + void* reallocate(void*, size_t) override { + return nullptr; + } +}; + +class AllocatorLogEntry { + public: + AllocatorLogEntry(std::string s, size_t n = 1) : str_(s), count_(n) {} + + const std::string& str() const { + return str_; + } + + size_t count() const { + return count_; + } + + AllocatorLogEntry operator*(size_t n) const { + return AllocatorLogEntry(str_, n); + } + + private: + std::string str_; + size_t count_; +}; + +inline AllocatorLogEntry Allocate(size_t s) { + char buffer[32]; + sprintf(buffer, "allocate(%zu)", s); + return AllocatorLogEntry(buffer); +} + +inline AllocatorLogEntry AllocateFail(size_t s) { + char buffer[32]; + sprintf(buffer, "allocate(%zu) -> nullptr", s); + return AllocatorLogEntry(buffer); +} + +inline AllocatorLogEntry Reallocate(size_t s1, size_t s2) { + char buffer[32]; + sprintf(buffer, "reallocate(%zu, %zu)", s1, s2); + return AllocatorLogEntry(buffer); +} + +inline AllocatorLogEntry ReallocateFail(size_t s1, size_t s2) { + char buffer[32]; + sprintf(buffer, "reallocate(%zu, %zu) -> nullptr", s1, s2); + return AllocatorLogEntry(buffer); +} + +inline AllocatorLogEntry Deallocate(size_t s) { + char buffer[32]; + sprintf(buffer, "deallocate(%zu)", s); + return AllocatorLogEntry(buffer); +} + +class AllocatorLog { + public: + AllocatorLog() = default; + AllocatorLog(std::initializer_list list) { + for (auto& entry : list) + append(entry); + } + + void clear() { + log_.str(""); + } + + void append(const AllocatorLogEntry& entry) { + for (size_t i = 0; i < entry.count(); i++) + log_ << entry.str() << "\n"; + } + + std::string str() const { + auto s = log_.str(); + if (s.empty()) + return "(empty)"; + s.pop_back(); // remove the trailing '\n' + return s; + } + + bool operator==(const AllocatorLog& other) const { + return str() == other.str(); + } + + friend std::ostream& operator<<(std::ostream& os, const AllocatorLog& log) { + os << log.str(); + return os; + } + + private: + std::ostringstream log_; +}; + +class SpyingAllocator : public ArduinoJson::Allocator { + public: + SpyingAllocator( + Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance()) + : upstream_(upstream) {} + virtual ~SpyingAllocator() {} + + size_t allocatedBytes() const { + return allocatedBytes_; + } + + void* allocate(size_t n) override { + auto block = reinterpret_cast( + upstream_->allocate(sizeof(AllocatedBlock) + n - 1)); + if (block) { + log_.append(Allocate(n)); + allocatedBytes_ += n; + block->size = n; + return block->payload; + } else { + log_.append(AllocateFail(n)); + return nullptr; + } + } + + void deallocate(void* p) override { + auto block = AllocatedBlock::fromPayload(p); + allocatedBytes_ -= block->size; + log_.append(Deallocate(block ? block->size : 0)); + upstream_->deallocate(block); + } + + void* reallocate(void* p, size_t n) override { + auto block = AllocatedBlock::fromPayload(p); + auto oldSize = block ? block->size : 0; + block = reinterpret_cast( + upstream_->reallocate(block, sizeof(AllocatedBlock) + n - 1)); + if (block) { + log_.append(Reallocate(oldSize, n)); + block->size = n; + allocatedBytes_ += n - oldSize; + return block->payload; + } else { + log_.append(ReallocateFail(oldSize, n)); + return nullptr; + } + } + + void clearLog() { + log_.clear(); + } + + const AllocatorLog& log() const { + return log_; + } + + private: + struct AllocatedBlock { + size_t size; + char payload[1]; + + static AllocatedBlock* fromPayload(void* p) { + if (!p) + return nullptr; + return reinterpret_cast( + // Cast to void* to silence "cast increases required alignment of + // target type [-Werror=cast-align]" + reinterpret_cast(reinterpret_cast(p) - + offsetof(AllocatedBlock, payload))); + } + }; + + AllocatorLog log_; + Allocator* upstream_; + size_t allocatedBytes_ = 0; +}; + +class KillswitchAllocator : public ArduinoJson::Allocator { + public: + KillswitchAllocator( + Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance()) + : working_(true), upstream_(upstream) {} + virtual ~KillswitchAllocator() {} + + void* allocate(size_t n) override { + return working_ ? upstream_->allocate(n) : 0; + } + + void deallocate(void* p) override { + upstream_->deallocate(p); + } + + void* reallocate(void* ptr, size_t n) override { + return working_ ? upstream_->reallocate(ptr, n) : 0; + } + + // Turn the killswitch on, so all allocation fail + void on() { + working_ = false; + } + + private: + bool working_; + Allocator* upstream_; +}; + +class TimebombAllocator : public ArduinoJson::Allocator { + public: + TimebombAllocator( + size_t initialCountdown, + Allocator* upstream = ArduinoJson::detail::DefaultAllocator::instance()) + : countdown_(initialCountdown), upstream_(upstream) {} + virtual ~TimebombAllocator() {} + + void* allocate(size_t n) override { + if (!countdown_) + return nullptr; + countdown_--; + return upstream_->allocate(n); + } + + void deallocate(void* p) override { + upstream_->deallocate(p); + } + + void* reallocate(void* ptr, size_t n) override { + if (!countdown_) + return nullptr; + countdown_--; + return upstream_->reallocate(ptr, n); + } + + void setCountdown(size_t value) { + countdown_ = value; + } + + private: + size_t countdown_ = 0; + Allocator* upstream_; +}; + +inline size_t sizeofPoolList(size_t n = ARDUINOJSON_INITIAL_POOL_COUNT) { + return sizeof(ArduinoJson::detail::VariantPool) * n; +} + +inline size_t sizeofPool( + ArduinoJson::detail::SlotCount n = ARDUINOJSON_POOL_CAPACITY) { + return ArduinoJson::detail::VariantPool::slotsToBytes(n); +} + +inline size_t sizeofStringBuffer(size_t iteration = 1) { + // returns 31, 63, 127, 255, etc. + auto capacity = ArduinoJson::detail::StringBuilder::initialCapacity; + for (size_t i = 1; i < iteration; i++) + capacity = capacity * 2 + 1; + return ArduinoJson::detail::sizeofString(capacity); +} + +inline size_t sizeofString(const char* s) { + return ArduinoJson::detail::sizeofString(strlen(s)); +} diff --git a/test/helpers/Arduino.h b/test/helpers/Arduino.h new file mode 100644 index 0000000..4b8c27c --- /dev/null +++ b/test/helpers/Arduino.h @@ -0,0 +1,13 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +#include "api/Print.h" +#include "api/Stream.h" +#include "api/String.h" +#include "avr/pgmspace.h" + +#define ARDUINO +#define ARDUINO_H_INCLUDED 1 diff --git a/test/helpers/CustomReader.hpp b/test/helpers/CustomReader.hpp new file mode 100644 index 0000000..66350a7 --- /dev/null +++ b/test/helpers/CustomReader.hpp @@ -0,0 +1,24 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +#include + +class CustomReader { + std::stringstream stream_; + + public: + CustomReader(const char* input) : stream_(input) {} + CustomReader(const CustomReader&) = delete; + + int read() { + return stream_.get(); + } + + size_t readBytes(char* buffer, size_t length) { + stream_.read(buffer, static_cast(length)); + return static_cast(stream_.gcount()); + } +}; diff --git a/test/helpers/api/Print.h b/test/helpers/api/Print.h new file mode 100644 index 0000000..c9ec191 --- /dev/null +++ b/test/helpers/api/Print.h @@ -0,0 +1,33 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +#include +#include +#include + +class Print { + public: + virtual ~Print() {} + + virtual size_t write(uint8_t) = 0; + virtual size_t write(const uint8_t *buffer, size_t size) = 0; + + size_t write(const char *str) { + if (!str) + return 0; + return write(reinterpret_cast(str), strlen(str)); + } + + size_t write(const char *buffer, size_t size) { + return write(reinterpret_cast(buffer), size); + } +}; + +class Printable { + public: + virtual ~Printable() {} + virtual size_t printTo(Print &p) const = 0; +}; diff --git a/test/helpers/api/Stream.h b/test/helpers/api/Stream.h new file mode 100644 index 0000000..2cd4651 --- /dev/null +++ b/test/helpers/api/Stream.h @@ -0,0 +1,14 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +// Reproduces Arduino's Stream class +class Stream // : public Print +{ + public: + virtual ~Stream() {} + virtual int read() = 0; + virtual size_t readBytes(char *buffer, size_t length) = 0; +}; diff --git a/test/helpers/api/String.h b/test/helpers/api/String.h new file mode 100644 index 0000000..e5dd367 --- /dev/null +++ b/test/helpers/api/String.h @@ -0,0 +1,140 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +#include +#include + +class __FlashStringHelper; + +// Reproduces Arduino's String class +class String { + public: + String() = default; + String(const char* s) { + if (s) { + str_.assign(s); + } + } + + void limitCapacityTo(size_t maxCapacity) { + maxCapacity_ = maxCapacity; + } + + unsigned char concat(const String& s) { + return concat(s.c_str(), s.length()); + } + + unsigned char concat(const String& s, size_t n) { + return concat(s.c_str(), n); + } + + unsigned char concat(const char* s) { + return concat(s, strlen(s)); + } + + unsigned char concat(const char* s, size_t n) { + if (str_.size() + n > maxCapacity_) { + return 0; + } + str_.append(s, n); + return 1; + } + + size_t length() const { + return str_.size(); + } + + const char* c_str() const { + return str_.c_str(); + } + + bool operator==(const char* s) const { + return str_ == s; + } + + bool operator==(const String& s) const { + return str_ == s.str_; + } + + String& operator=(const char* s) { + if (s) { + str_.assign(s); + } else { + str_.clear(); + } + return *this; + } + + char operator[](unsigned int index) const { + if (index >= str_.size()) { + return 0; + } + return str_[index]; + } + + void replace(const char* from, const char* to) { + size_t start = 0; + while (true) { + const auto pos = str_.find(from, start); + if (pos == std::string::npos) { + return; + } + str_.replace(pos, strlen(from), to); + start = pos + strlen(to); + } + } + + void replace(const __FlashStringHelper* from, const __FlashStringHelper* to) { + replace(reinterpret_cast(from), reinterpret_cast(to)); + } + + int indexOf(char ch, unsigned int fromIndex = 0) const { + const auto pos = str_.find(ch, fromIndex); + if (pos == std::string::npos) { + return -1; + } + return pos; + } + + int lastIndexOf(char ch, unsigned int fromIndex = 0) const { + const auto pos = str_.rfind(ch, fromIndex); + if (pos == std::string::npos) { + return -1; + } + return pos; + } + + int indexOf(const char* str, unsigned int fromIndex = 0) const { + const auto pos = str_.find(str, fromIndex); + if (pos == std::string::npos) { + return -1; + } + return pos; + } + + int indexOf(const String& str, unsigned int fromIndex = 0) const { + return indexOf(str.c_str(), fromIndex); + } + + String substring(unsigned int beginIndex, unsigned int endIndex) const { + return String(str_.substr(beginIndex, endIndex - beginIndex).c_str()); + } + + friend std::ostream& operator<<(std::ostream& lhs, const ::String& rhs) { + lhs << rhs.str_; + return lhs; + } + + protected: + std::string str_; + size_t maxCapacity_ = 1024; +}; + +class StringSumHelper : public ::String {}; + +inline bool operator==(const std::string& lhs, const ::String& rhs) { + return lhs == rhs.c_str(); +} diff --git a/test/helpers/avr/pgmspace.h b/test/helpers/avr/pgmspace.h new file mode 100644 index 0000000..2cdd182 --- /dev/null +++ b/test/helpers/avr/pgmspace.h @@ -0,0 +1,31 @@ +// ArduinoJson - https://arduinojson.org +// Copyright © 2014-2024, Benoit BLANCHON +// MIT License + +#pragma once + +#include // uint8_t + +#define PROGMEM + +class __FlashStringHelper; + +inline const void* convertPtrToFlash(const void* s) { + return reinterpret_cast(s) + 42; +} + +inline const void* convertFlashToPtr(const void* s) { + return reinterpret_cast(s) - 42; +} + +#define PSTR(X) reinterpret_cast(convertPtrToFlash(X)) +#define F(X) reinterpret_cast(PSTR(X)) + +inline uint8_t pgm_read_byte(const void* p) { + return *reinterpret_cast(convertFlashToPtr(p)); +} + +#define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \ + static type const ARDUINOJSON_CONCAT2(name, _progmem)[] = __VA_ARGS__; \ + static type const* name = reinterpret_cast( \ + convertPtrToFlash(ARDUINOJSON_CONCAT2(name, _progmem)));