diff --git a/.github/workflows/actions_build.yml b/.github/workflows/actions_build.yml index 00cfae4..10c8db5 100644 --- a/.github/workflows/actions_build.yml +++ b/.github/workflows/actions_build.yml @@ -258,7 +258,7 @@ jobs: fail-fast: false matrix: type: [Debug, Release, MinSizeRel] - cc_ver: [12, 13, 14] + cc_ver: [12, 13, 14, 15] cpp: [11, 14, 17, 20] steps: diff --git a/doxygen/doxygen.conf b/doxygen/doxygen.conf index fc49d66..e16da1b 100644 --- a/doxygen/doxygen.conf +++ b/doxygen/doxygen.conf @@ -1,4 +1,4 @@ -# Doxyfile 1.8.17 +# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "COMMS" +PROJECT_NAME = COMMS # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -217,6 +217,14 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. @@ -294,18 +302,21 @@ OPTIMIZE_OUTPUT_SLICE = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the -# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat -# .inc files as Fortran files (default is PHP), and .f files as C (default is -# Fortran), use: inc=Fortran f=C. +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = @@ -439,6 +450,19 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 0 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -502,6 +526,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -539,11 +570,18 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES, upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# (including Cygwin) ands Mac users are advised to set this option to NO. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES @@ -782,7 +820,10 @@ WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. WARN_AS_ERROR = YES @@ -813,13 +854,14 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = doxygen include +INPUT = doxygen \ + include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 @@ -832,13 +874,15 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen -# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, -# *.vhdl, *.ucf, *.qsf and *.ice. +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. FILE_PATTERNS = @@ -855,7 +899,10 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = test doc externals cmake +EXCLUDE = test \ + doc \ + externals \ + cmake # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -871,7 +918,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = */build* +EXCLUDE_PATTERNS = */build* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -1055,16 +1102,22 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories @@ -1074,10 +1127,13 @@ CLANG_ASSISTED_PARSING = NO CLANG_OPTIONS = # If clang assisted parsing is enabled you can provide the clang parser with the -# path to the compilation database (see: -# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files -# were built. This is equivalent to specifying the "-p" option to a clang tool, -# such as clang-check. These options will then be passed to the parser. +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. @@ -1264,10 +1320,11 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/xcode/), introduced with OSX -# 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. @@ -1309,8 +1366,8 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1340,7 +1397,7 @@ CHM_FILE = HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated -# (YES) or that it should be included in the master .chm file (NO). +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1385,7 +1442,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1393,8 +1451,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1402,16 +1460,16 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = @@ -1423,9 +1481,9 @@ QHP_CUST_FILTER_ATTRS = QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1502,6 +1560,17 @@ TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1541,7 +1610,7 @@ USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. @@ -1557,7 +1626,7 @@ MATHJAX_FORMAT = HTML-CSS # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ @@ -1571,7 +1640,8 @@ MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1618,7 +1688,8 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1631,8 +1702,9 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1796,9 +1868,11 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES, to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2315,10 +2389,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2510,9 +2606,11 @@ DOT_MULTI_TARGETS = YES GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc and +# plantuml temporary files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/doxygen/main.dox b/doxygen/main.dox index 0fd01c1..bdc5c93 100644 --- a/doxygen/main.dox +++ b/doxygen/main.dox @@ -33,6 +33,6 @@ /// for custom binary protocol, which can be used in any application in the future, /// including bare-metal one with constrained environment. /// -/// Refer to comms/version.h file for the version +/// Refer to @ref version.h "comms/version.h" file for the version /// information of the @b COMMS library and this documentation. /// diff --git a/include/comms/CompileControl.h b/include/comms/CompileControl.h index a3a7bfa..da0e78c 100644 --- a/include/comms/CompileControl.h +++ b/include/comms/CompileControl.h @@ -73,6 +73,8 @@ #define COMMS_IS_GCC_10 (COMMS_IS_GCC && (__GNUC__ == 10)) #define COMMS_IS_GCC_10_OR_ABOVE (COMMS_IS_GCC && (__GNUC__ >= 10)) #define COMMS_IS_GCC_10_OR_BELOW (COMMS_IS_GCC && (__GNUC__ <= 10)) +#define COMMS_IS_GCC_11 (COMMS_IS_GCC && (__GNUC__ == 11)) +#define COMMS_IS_GCC_11_OR_ABOVE (COMMS_IS_GCC && (__GNUC__ >= 11)) #define COMMS_IS_GCC_12 (COMMS_IS_GCC && (__GNUC__ == 12)) #define COMMS_IS_CLANG_7_OR_ABOVE (COMMS_IS_CLANG && (__clang_major__ >= 7)) #define COMMS_IS_CLANG_8 (COMMS_IS_CLANG && (__clang_major__ == 8)) diff --git a/include/comms/field/basic/ArrayList.h b/include/comms/field/basic/ArrayList.h index ff66a79..d9fcad0 100644 --- a/include/comms/field/basic/ArrayList.h +++ b/include/comms/field/basic/ArrayList.h @@ -17,6 +17,7 @@ #include "comms/ErrorStatus.h" #include "comms/util/access.h" #include "comms/util/assign.h" +#include "comms/util/MaxSizeOf.h" #include "comms/util/StaticVector.h" #include "comms/util/StaticString.h" #include "comms/util/detect.h" @@ -161,6 +162,7 @@ class ArrayList : ElementType& createBack() { + COMMS_ASSERT(value_.size() < value_.max_size()); value_.emplace_back(); updateElemVersion(value_.back(), VersionTag<>()); return value_.back(); @@ -654,10 +656,8 @@ class ArrayList : value_.clear(); auto remLen = len; while (0 < remLen) { - ElementType& elem = createBack(); - auto es = readElement(elem, iter, remLen); + auto es = createAndReadNextElementInternal(iter, remLen); if (es != ErrorStatus::Success) { - value_.pop_back(); return es; } } @@ -668,7 +668,7 @@ class ArrayList : template ErrorStatus readInternal(TIter& iter, std::size_t len, RawDataTag) { - comms::util::assign(value(), iter, iter + len); + comms::util::assign(value(), iter, iter + std::min(len, comms::util::maxSizeOf(value()))); std::advance(iter, len); return ErrorStatus::Success; } @@ -678,10 +678,8 @@ class ArrayList : { clear(); while (0 < count) { - auto& elem = createBack(); - auto es = readElement(elem, iter, len); + auto es = createAndReadNextElementInternal(iter, len); if (es != ErrorStatus::Success) { - value_.pop_back(); return es; } @@ -706,8 +704,14 @@ class ArrayList : { clear(); while (0 < count) { - auto& elem = createBack(); - readElementNoStatus(elem, iter); + if (value_.size() < value_.max_size()) { + auto& elem = createBack(); + readElementNoStatus(elem, iter); + } + else { + ElementType elem; + readElementNoStatus(elem, iter); + } --count; } } @@ -773,6 +777,23 @@ class ArrayList : return true; } + template + comms::ErrorStatus createAndReadNextElementInternal(TIter& iter, std::size_t& len) + { + if (value_.size() < value_.max_size()) { + auto& elem = createBack(); + auto es = readElement(elem, iter, len); + if (es != ErrorStatus::Success) { + value_.pop_back(); + } + + return es; + } + + ElementType elem; + return readElement(elem, iter, len); + } + ValueType value_; }; diff --git a/include/comms/field/basic/String.h b/include/comms/field/basic/String.h index a2742e7..7ef8d2c 100644 --- a/include/comms/field/basic/String.h +++ b/include/comms/field/basic/String.h @@ -17,6 +17,7 @@ #include "comms/ErrorStatus.h" #include "comms/util/access.h" #include "comms/util/assign.h" +#include "comms/util/MaxSizeOf.h" #include "comms/util/StaticVector.h" #include "comms/util/StaticString.h" #include "comms/util/detect.h" @@ -186,9 +187,10 @@ class String : public TFieldBase using ConstPointer = typename ValueType::const_pointer; auto* str = reinterpret_cast(&(*iter)); - std::advance(iter, len); - auto* endStr = reinterpret_cast(&(*iter)); + auto endStr = str; + std::advance(endStr, std::min(len, comms::util::maxSizeOf(value_))); comms::util::assign(value_, str, endStr); + std::advance(iter, len); return ErrorStatus::Success; } diff --git a/include/comms/util/MaxSizeOf.h b/include/comms/util/MaxSizeOf.h new file mode 100644 index 0000000..8db568c --- /dev/null +++ b/include/comms/util/MaxSizeOf.h @@ -0,0 +1,81 @@ +// +// Copyright 2015 - 2023 (C). Alex Robenko. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include +#include + +#include "comms/details/tag.h" +#include "comms/util/type_traits.h" +#include "comms/util/detect.h" + +namespace comms +{ + +namespace util +{ + +namespace details +{ + +class MaxSizeOfHelper +{ +public: + template + static std::size_t maxSizeOf(const T& val) + { + using DecayedType = typename std::decay::type; + using Tag = + typename comms::util::LazyShallowConditional< + comms::util::detect::hasMaxSizeFunc() + >::template Type< + HasMaxSizeTag, + NoMaxSizeTag + >; + + return maxSizeOfInternal(val, Tag()); + } + +private: + template + using HasMaxSizeTag = comms::details::tag::Tag1<>; + + template + using NoMaxSizeTag = comms::details::tag::Tag2<>; + + template + static std::size_t maxSizeOfInternal(const T& val, HasMaxSizeTag<>) + { + return val.max_size(); + } + + template + static std::size_t maxSizeOfInternal(const T& val, NoMaxSizeTag<>) + { + static_cast(val); + return std::numeric_limits::max(); + } +}; + +} // namespace details + +/// @cond SKIP_DOC +template +std::size_t maxSizeOf(const T& val) +{ + return details::MaxSizeOfHelper::maxSizeOf(val); +} + +/// @endcond + +} // namespace util + +} // namespace comms + + diff --git a/include/comms/util/ScopeGuard.h b/include/comms/util/ScopeGuard.h index d9f0203..df8d0c0 100644 --- a/include/comms/util/ScopeGuard.h +++ b/include/comms/util/ScopeGuard.h @@ -5,6 +5,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +/// @file +/// @brief Contains definition of the "Scope Guard" idiom, see @ref comms::util::ScopeGuard. + #pragma once #include @@ -160,8 +163,6 @@ ScopeGuard(func), return ScopeGuard(std::move(bindObj)); } -// Class implementation part - } // namespace util } // namespace comms diff --git a/include/comms/util/StaticQueue.h b/include/comms/util/StaticQueue.h index dfc3c26..231a43d 100644 --- a/include/comms/util/StaticQueue.h +++ b/include/comms/util/StaticQueue.h @@ -6,8 +6,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. /// @file comms/util/StaticQueue.h -/// This file contains the definition and implementation of the static queue, -/// which also can be used as circular buffer. +/// @brief This file contains the definition and implementation of the static queue, +/// which also can be used as circular buffer. #pragma once diff --git a/include/comms/util/StaticString.h b/include/comms/util/StaticString.h index cf728f2..00816a4 100644 --- a/include/comms/util/StaticString.h +++ b/include/comms/util/StaticString.h @@ -5,6 +5,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +/// @file +/// @brief Contains comms::util::StaticString class. + #pragma once #include @@ -12,6 +15,7 @@ #include #include +#include "comms/CompileControl.h" #include "comms/Assert.h" #include "StaticVector.h" @@ -348,12 +352,17 @@ class StaticStringBase auto begIter = begin() + std::distance(cbegin(), first); auto endIter = begin() + std::distance(cbegin(), last); for (auto iter = begIter; iter != endIter; ++iter) { - if (first2 == last2) { + if (last2 <= first2) { vec_.erase(iter, endIter); return; } - *iter = static_cast(*first2); + COMMS_GNU_WARNING_PUSH +#if COMMS_IS_GCC_11_OR_ABOVE + COMMS_GNU_WARNING_DISABLE("-Wstringop-overflow") +#endif // #if COMMS_IS_GCC_12 + *iter = static_cast(*first2); // Wrong warning reported by gcc-12 + COMMS_GNU_WARNING_POP ++first2; } @@ -811,14 +820,14 @@ class StaticString : /// @brief Default constructor /// @see Reference StaticString() - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { } /// @brief Constructor variant /// @see Reference StaticString(size_type count, value_type ch) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(count, ch); } @@ -831,7 +840,7 @@ class StaticString : const StaticString& other, size_type pos, size_type count = npos) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(other, pos, count); } @@ -839,7 +848,7 @@ class StaticString : /// @brief Constructor variant. /// @see Reference StaticString(const_pointer str, size_type count) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(str, count); } @@ -847,7 +856,7 @@ class StaticString : /// @brief Constructor variant. /// @see Reference StaticString(const_pointer str) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(str); } @@ -856,7 +865,7 @@ class StaticString : /// @see Reference template StaticString(TIter first, TIter last) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(first, last); } @@ -864,7 +873,7 @@ class StaticString : /// @brief Copy constructor. /// @see Reference StaticString(const StaticString& other) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(other); } @@ -873,7 +882,7 @@ class StaticString : /// @see Reference template explicit StaticString(const StaticString& other) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(other); } @@ -881,7 +890,7 @@ class StaticString : /// @brief Constructor variant. /// @see Reference StaticString(std::initializer_list init) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(init.begin(), init.end()); } @@ -1865,6 +1874,15 @@ bool operator<(const TChar* str1, const StaticString& str2) return (str2 > str1); } +/// @brief Lexicographical compare between the strings. +/// @see Reference +/// @related StaticString +template +bool operator<(const StaticString& str1, const TChar* str2) +{ + return str1.operator<(str2); +} + /// @brief Lexicographical compare between the strings. /// @see Reference /// @related StaticString @@ -1910,6 +1928,15 @@ bool operator>(const TChar* str1, const StaticString& str2) return (str2 < str1); } +/// @brief Lexicographical compare between the strings. +/// @see Reference +/// @related StaticString +template +bool operator>(const StaticString& str1, const TChar* str2) +{ + return str1.operator<(str2); +} + /// @brief Lexicographical compare between the strings. /// @see Reference /// @related StaticString @@ -1954,7 +1981,25 @@ bool operator==(const StaticString& str1, const StaticString bool operator==(const TChar* str1, const StaticString& str2) { - return str2 == str1; + return str2.operator==(str1); +} + +/// @brief Equality compare between the strings. +/// @see Reference +/// @related StaticString +template +bool operator==(const StaticString& str1, const TChar* str2) +{ + return str1.operator==(str2); +} + +/// @brief Inequality compare between the strings. +/// @see Reference +/// @related StaticString +template +bool operator!=(const StaticString& str1, const StaticString& str2) +{ + return !(str1 == str2); } /// @brief Inequality compare between the strings. @@ -1966,6 +2011,15 @@ bool operator!=(const TChar* str1, const StaticString& str2) return !(str2 == str1); } +/// @brief Inequality compare between the strings. +/// @see Reference +/// @related StaticString +template +bool operator!=(const StaticString& str1, const TChar* str2) +{ + return !(str1 == str2); +} + namespace details { diff --git a/include/comms/util/StaticVector.h b/include/comms/util/StaticVector.h index ba4224e..073afb1 100644 --- a/include/comms/util/StaticVector.h +++ b/include/comms/util/StaticVector.h @@ -5,6 +5,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +/// @file +/// @brief Contains comms::util::StaticVector class. + #pragma once #include @@ -16,6 +19,15 @@ #include "comms/CompileControl.h" #include "comms/Assert.h" +COMMS_GNU_WARNING_PUSH + +#if COMMS_IS_GCC_12 && defined(NDEBUG) +// Release compilation with gcc-12 +// assumes size / capacity of the StaticVectorBase is 0 and generates +// unjustified warnings. +COMMS_GNU_WARNING_DISABLE("-Warray-bounds") +#endif + namespace comms { @@ -147,6 +159,10 @@ class StaticVectorBase T* begin() { + if (size() == 0U) { + return nullptr; + } + return &(elem(0)); } @@ -157,6 +173,10 @@ class StaticVectorBase const T* cbegin() const { + if (size() == 0U) { + return nullptr; + } + return &(elem(0)); } @@ -199,11 +219,19 @@ class StaticVectorBase T* data() { + if (size() == 0U) { + return nullptr; + } + return &(elem(0)); } const T* data() const { + if (size() == 0U) { + return nullptr; + } + return &(elem(0)); } @@ -253,16 +281,14 @@ class StaticVectorBase auto moveEndIter = moveBegIter + (tailCount - count); COMMS_ASSERT(moveEndIter < pushEndIter); + COMMS_GNU_WARNING_PUSH #if COMMS_IS_GCC_12 && defined(NDEBUG) - // For some reason RELEASE compilation with gcc-12 + // Release compilation with gcc-12 // gives a warning here, while any debug build works fine. - COMMS_GNU_WARNING_PUSH COMMS_GNU_WARNING_DISABLE("-Wstringop-overflow") +#endif std::move_backward(moveBegIter, moveEndIter, pushEndIter); - COMMS_GNU_WARNING_POP -#else - std::move_backward(moveBegIter, moveEndIter, pushEndIter); -#endif + COMMS_GNU_WARNING_POP auto* assignBegIter = posIter; auto* assignEndIter = assignBegIter + count; @@ -557,18 +583,18 @@ class StaticVectorGeneric : using const_reverse_iterator = typename Base::const_reverse_iterator; StaticVectorGeneric() - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { } StaticVectorGeneric(size_type count, const T& value) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(count, value); } explicit StaticVectorGeneric(size_type count) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { COMMS_ASSERT(count < Base::capacity()); while (0 < count) { @@ -579,26 +605,26 @@ class StaticVectorGeneric : template StaticVectorGeneric(TIter from, TIter to) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(from, to); } template StaticVectorGeneric(const StaticVectorGeneric& other) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(other.begin(), other.end()); } StaticVectorGeneric(const StaticVectorGeneric& other) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(other.begin(), other.end()); } StaticVectorGeneric(std::initializer_list init) - : Base(&StorageBase::data_[0], StorageBase::data_.size()) + : Base(StorageBase::data_.data(), StorageBase::data_.size()) { assign(init.begin(), init.end()); } @@ -1409,6 +1435,490 @@ class StaticVector : public details::ChooseStaticVectorBase } }; +// Template specialization for zero sized vectors +template +class StaticVector +{ + using StorageType = std::array; + + template + friend class StaticVector; + +public: + using value_type = typename StorageType::value_type; + using size_type = typename StorageType::size_type; + using difference_type = typename StorageType::difference_type; + using reference = typename StorageType::reference; + using const_reference = typename StorageType::const_reference; + using pointer = typename StorageType::pointer; + using const_pointer = typename StorageType::const_pointer; + using iterator = typename StorageType::iterator; + using const_iterator = typename StorageType::const_iterator; + using reverse_iterator = typename StorageType::reverse_iterator; + using const_reverse_iterator = typename StorageType::const_reverse_iterator; + + StaticVector() = default; + + StaticVector(size_type count, const T& value) + { + static_cast(value); + if (count == 0U) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + explicit StaticVector(size_type count) + { + if (count == 0U) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + template + StaticVector(TIter from, TIter to) + { + if (from == to) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + template + StaticVector(const StaticVector& other) + { + static_cast(other); + if (TOtherSize == 0U) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + StaticVector(const StaticVector& other) + { + static_cast(other); + } + + StaticVector(std::initializer_list init) + { + if (std::begin(init) == std::end(init)) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + ~StaticVector() noexcept = default; + + StaticVector& operator=(const StaticVector&) = default; + + template + StaticVector& operator=(const StaticVector& other) + { + static_cast(other); + if (TOtherSize == 0U) { + return *this; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return *this; + } + + StaticVector& operator=(std::initializer_list init) + { + if (std::begin(init) == std::end(init)) { + return *this; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return *this; + } + + void assign(size_type count, const T& value) + { + static_cast(value); + if (count == 0U) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + template + void assign(TIter from, TIter to) + { + if (from == to) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + void assign(std::initializer_list init) + { + if (std::begin(init) == std::end(init)) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + reference at(size_type pos) + { + static_cast(pos); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data[pos]; + } + + const_reference at(size_type pos) const + { + static_cast(pos); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data[pos]; + } + + reference operator[](size_type pos) + { + static_cast(pos); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data[pos]; + } + + const_reference operator[](size_type pos) const + { + static_cast(pos); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data[pos]; + } + + reference front() + { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.front(); + } + + const_reference front() const + { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.front(); + } + + reference back() + { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.back(); + } + + const_reference back() const + { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.back(); + } + + pointer data() + { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.data(); + } + + const_pointer data() const + { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.data(); + } + + iterator begin() + { + return m_data.begin(); + } + + const_iterator begin() const + { + return m_data.begin(); + } + + const_iterator cbegin() const + { + return m_data.cbegin(); + } + + iterator end() + { + return m_data.end(); + } + + const_iterator end() const + { + return m_data.end(); + } + + const_iterator cend() const + { + return m_data.cend(); + } + + reverse_iterator rbegin() + { + return m_data.rbegin(); + } + + const_reverse_iterator rbegin() const + { + return m_data.rbegin(); + } + + const_reverse_iterator crbegin() const + { + return m_data.crbegin(); + } + + reverse_iterator rend() + { + return m_data.rend(); + } + + const_reverse_iterator rend() const + { + return m_data.rend(); + } + + const_reverse_iterator crend() const + { + return m_data.crend(); + } + + bool empty() const + { + return m_data.empty(); + } + + size_type size() const + { + return m_data.size(); + } + + size_type max_size() const + { + return m_data.max_size(); + } + + void reserve(size_type new_cap) + { + static_cast(new_cap); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + size_type capacity() const + { + return max_size(); + } + + void shrink_to_fit() + { + } + + void clear() + { + } + + iterator insert(const_iterator iter, const T& value) + { + static_cast(iter); + static_cast(value); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.end(); + } + + iterator insert(const_iterator iter, T&& value) + { + static_cast(iter); + static_cast(value); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.end(); + } + + iterator insert(const_iterator iter, size_type count, const T& value) + { + static_cast(iter); + static_cast(count); + static_cast(value); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.end(); + } + + template + iterator insert(const_iterator iter, TIter from, TIter to) + { + static_cast(iter); + static_cast(from); + static_cast(to); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.end(); + } + + iterator insert(const_iterator iter, std::initializer_list init) + { + static_cast(iter); + static_cast(init); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.end(); + } + + template + iterator emplace(const_iterator iter, TArgs&&...) + { + static_cast(iter); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.end(); + } + + iterator erase(const_iterator iter) + { + static_cast(iter); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + return m_data.end(); + } + + iterator erase(const_iterator from, const_iterator to) + { + if (from != to) { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + return m_data.end(); + } + + void push_back(const T& value) + { + static_cast(value); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + void push_back(T&& value) + { + static_cast(value); + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + template + void emplace_back(TArgs&&...) + { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + void pop_back() + { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + void resize(size_type count) + { + if (count == 0U) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + void resize(size_type count, const value_type& value) + { + static_cast(value); + if (count == 0U) { + return; + } + + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + + template + void swap(StaticVector& other) + { + static_cast(other); + if (TOtherSize != 0U) { + static constexpr bool Must_not_be_called_for_zero_sized_vector = false; + static_cast(Must_not_be_called_for_zero_sized_vector); + COMMS_ASSERT(Must_not_be_called_for_zero_sized_vector); + } + } + +private: + StorageType m_data; +}; + /// @brief Lexicographically compares the values in the vector. /// @see Reference /// @related StaticVector @@ -1509,3 +2019,5 @@ void swap(comms::util::StaticVector& v1, comms::util::StaticVector diff --git a/include/comms/util/access.h b/include/comms/util/access.h index 31d20c4..69c5b1f 100644 --- a/include/comms/util/access.h +++ b/include/comms/util/access.h @@ -5,6 +5,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +/// @file +/// @brief Contains functions for raw data access / (de)serialization. + #pragma once #include diff --git a/include/comms/util/alloc.h b/include/comms/util/alloc.h index df8ff74..ad0fbc2 100644 --- a/include/comms/util/alloc.h +++ b/include/comms/util/alloc.h @@ -6,8 +6,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. /// @file comms/util/alloc.h -/// This file contains various generic allocator classes that may be used -/// to allocate objects using dynamic memory or "in-place" allocations. +/// @brief This file contains various generic allocator classes that may be used +/// to allocate objects using dynamic memory or "in-place" allocations. #pragma once diff --git a/include/comms/util/details/detect.h b/include/comms/util/details/detect.h index b4cee26..7634cdf 100644 --- a/include/comms/util/details/detect.h +++ b/include/comms/util/details/detect.h @@ -169,6 +169,22 @@ class HasPtrSizeConstructor static const bool Value = !std::is_same(nullptr))>::value; }; +template +class HasMaxSizeFunc +{ + using No = comms::util::EmptyStruct<>; + +protected: + template + static auto test(std::nullptr_t) -> decltype(std::declval().max_size()); + + template + static No test(...); + +public: + static const bool Value = !std::is_same(nullptr))>::value; +}; + } // namespace details } // namespace detect diff --git a/include/comms/util/detect.h b/include/comms/util/detect.h index 0037875..79279c9 100644 --- a/include/comms/util/detect.h +++ b/include/comms/util/detect.h @@ -100,6 +100,17 @@ constexpr bool isStdSpan() return details::IsStdSpan::Value; } +/// @brief Detect whether provided type has @b max_size() member function +/// @details +/// @code +/// static_assert(comms::util::detect::hasMaxSizeFunc(), "std::string is expected to have max_size() member function."); +/// @endcode +template +constexpr bool hasMaxSizeFunc() +{ + return details::HasMaxSizeFunc::Value; +} + } // namespace detect } // namespace util diff --git a/include/comms/version.h b/include/comms/version.h index 6f7031f..11c22b4 100644 --- a/include/comms/version.h +++ b/include/comms/version.h @@ -17,7 +17,7 @@ #define COMMS_MINOR_VERSION 2U /// @brief Patch level of the library -#define COMMS_PATCH_VERSION 1U +#define COMMS_PATCH_VERSION 2U /// @brief Macro to create numeric version as single unsigned number #define COMMS_MAKE_VERSION(major_, minor_, patch_) \ diff --git a/script/env_dev.sh b/script/env_dev.sh index 03fe581..7be8068 100755 --- a/script/env_dev.sh +++ b/script/env_dev.sh @@ -12,4 +12,4 @@ mkdir -p ${BUILD_DIR} cd ${BUILD_DIR} cmake .. -DCMAKE_INSTALL_PREFIX=install -DCMAKE_BUILD_TYPE=Debug \ - -DCC_COMMS_BUILD_UNIT_TESTS=ON "$@" + -DCC_COMMS_BUILD_UNIT_TESTS=ON -DCC_COMMS_UNIT_TESTS_USE_SANITIZERS=ON "$@" diff --git a/script/env_dev_clang15.sh b/script/env_dev_clang15.sh new file mode 100755 index 0000000..6f70c1b --- /dev/null +++ b/script/env_dev_clang15.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +export CC=clang-15 +export CXX=clang++-15 + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source ${SCRIPT_DIR}/env_dev.sh "$@" diff --git a/script/env_dev_rel_gcc11.sh b/script/env_dev_rel_gcc11.sh new file mode 100755 index 0000000..7d027fa --- /dev/null +++ b/script/env_dev_rel_gcc11.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +export CC=gcc-11 +export CXX=g++-11 + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source ${SCRIPT_DIR}/env_dev_rel.sh "$@" diff --git a/test/Fields2.th b/test/Fields2.th index 8cabbd1..645264e 100644 --- a/test/Fields2.th +++ b/test/Fields2.th @@ -56,6 +56,12 @@ public: void test28(); void test29(); void test30(); + void test31(); + void test32(); + void test33(); + void test34(); + void test35(); + void test36(); private: template @@ -2029,6 +2035,190 @@ void FieldsTestSuite2::test30() } while (false); } +void FieldsTestSuite2::test31() +{ + typedef comms::field::ArrayList< + comms::Field, + comms::field::IntValue< + comms::Field, + std::uint16_t + >, + comms::option::SequenceSizeFieldPrefix< + comms::field::IntValue< + comms::Field, + std::uint8_t + > + >, + comms::option::app::FixedSizeStorage<2> + > Field; + + Field field; + + const char Buf[] = { + 0x3, // count + 0x1, 0x2, // elem 0 + 0x3, 0x4, // elem 1 + 0x5, 0x6 // elem 2 + }; + const std::size_t BufSize = std::extent::value; + + auto readIter = &Buf[0]; + auto es = field.read(readIter, BufSize); + TS_ASSERT_EQUALS(es, comms::ErrorStatus::Success); + TS_ASSERT_EQUALS(field.value().size(), field.value().max_size()); + TS_ASSERT_EQUALS(static_cast(std::distance(&Buf[0], readIter)), BufSize) +} + +void FieldsTestSuite2::test32() +{ + typedef comms::field::ArrayList< + comms::Field, + comms::field::IntValue< + comms::Field, + std::uint16_t + >, + comms::option::SequenceSerLengthFieldPrefix< + comms::field::IntValue< + comms::Field, + std::uint8_t + > + >, + comms::option::app::FixedSizeStorage<2> + > Field; + + Field field; + + const char Buf[] = { + 0x6, // length + 0x1, 0x2, // elem 0 + 0x3, 0x4, // elem 1 + 0x5, 0x6 // elem 2 + }; + const std::size_t BufSize = std::extent::value; + + auto readIter = &Buf[0]; + auto es = field.read(readIter, BufSize); + TS_ASSERT_EQUALS(es, comms::ErrorStatus::Success); + TS_ASSERT_EQUALS(field.value().size(), field.value().max_size()); + TS_ASSERT_EQUALS(static_cast(std::distance(&Buf[0], readIter)), BufSize) +} + +void FieldsTestSuite2::test33() +{ + typedef comms::field::ArrayList< + comms::Field, + std::uint8_t, + comms::option::SequenceSizeFieldPrefix< + comms::field::IntValue< + comms::Field, + std::uint8_t + > + >, + comms::option::app::FixedSizeStorage<2> + > Field; + + Field field; + + const char Buf[] = { + 0x3, // count + 0x1, 0x2, 0x3 + }; + const std::size_t BufSize = std::extent::value; + + auto readIter = &Buf[0]; + auto es = field.read(readIter, BufSize); + TS_ASSERT_EQUALS(es, comms::ErrorStatus::Success); + TS_ASSERT_EQUALS(field.value().size(), field.value().max_size()); + TS_ASSERT_EQUALS(static_cast(std::distance(&Buf[0], readIter)), BufSize) +} + +void FieldsTestSuite2::test34() +{ + typedef comms::field::ArrayList< + comms::Field, + std::uint8_t, + comms::option::SequenceSerLengthFieldPrefix< + comms::field::IntValue< + comms::Field, + std::uint8_t + > + >, + comms::option::app::FixedSizeStorage<2> + > Field; + + Field field; + + const char Buf[] = { + 0x3, // length + 0x1, 0x2, 0x3 + }; + const std::size_t BufSize = std::extent::value; + + auto readIter = &Buf[0]; + auto es = field.read(readIter, BufSize); + TS_ASSERT_EQUALS(es, comms::ErrorStatus::Success); + TS_ASSERT_EQUALS(field.value().size(), field.value().max_size()); + TS_ASSERT_EQUALS(static_cast(std::distance(&Buf[0], readIter)), BufSize) +} + +void FieldsTestSuite2::test35() +{ + typedef comms::field::String< + comms::Field, + comms::option::SequenceSerLengthFieldPrefix< + comms::field::IntValue< + comms::Field, + std::uint8_t + > + >, + comms::option::app::FixedSizeStorage<2> + > Field; + + Field field; + + const char Buf[] = { + 0x5, // length + 'h', 'e', 'l', 'l', '0' + }; + const std::size_t BufSize = std::extent::value; + + auto readIter = &Buf[0]; + auto es = field.read(readIter, BufSize); + TS_ASSERT_EQUALS(es, comms::ErrorStatus::Success); + TS_ASSERT_EQUALS(field.value().size(), field.value().max_size()); + TS_ASSERT_EQUALS(field.value(), "he"); + TS_ASSERT_EQUALS(static_cast(std::distance(&Buf[0], readIter)), BufSize) +} + +void FieldsTestSuite2::test36() +{ + typedef comms::field::String< + comms::Field, + comms::option::SequenceSizeFieldPrefix< + comms::field::IntValue< + comms::Field, + std::uint8_t + > + >, + comms::option::app::FixedSizeStorage<2> + > Field; + + Field field; + + const char Buf[] = { + 0x5, // count + 'h', 'e', 'l', 'l', '0' + }; + const std::size_t BufSize = std::extent::value; + + auto readIter = &Buf[0]; + auto es = field.read(readIter, BufSize); + TS_ASSERT_EQUALS(es, comms::ErrorStatus::Success); + TS_ASSERT_EQUALS(field.value().size(), field.value().max_size()); + TS_ASSERT_EQUALS(field.value(), "he"); + TS_ASSERT_EQUALS(static_cast(std::distance(&Buf[0], readIter)), BufSize) +} + template void FieldsTestSuite2::writeField( const TField& field, diff --git a/test/Message.th b/test/Message.th index fc40ec2..e10384b 100644 --- a/test/Message.th +++ b/test/Message.th @@ -812,6 +812,8 @@ void MessageTestSuite::test12() static const std::size_t BufSize = std::extent::value; + static_assert(BufSize == 3U, "Invalid assumption"); + auto readIter = comms::readIteratorFor(interface, Buf); auto es = interface.read(readIter, BufSize); TS_ASSERT_EQUALS(es, comms::ErrorStatus::Success); @@ -823,8 +825,7 @@ void MessageTestSuite::test12() std::begin(Buf), std::end(Buf), msg.field_data().value().begin()); TS_ASSERT(equal); - std::vector outBuf; - outBuf.resize(BufSize); + std::vector outBuf(BufSize, 0U); auto writeIter = comms::writeIteratorFor(&outBuf[0]); es = interface.write(writeIter, outBuf.size()); TS_ASSERT_EQUALS(es, comms::ErrorStatus::Success); diff --git a/test/Util.th b/test/Util.th index 0c77230..51f09fb 100644 --- a/test/Util.th +++ b/test/Util.th @@ -92,7 +92,12 @@ void UtilTestSuite::test1() TS_ASSERT_EQUALS(vec.size(), DataSize + InsData1Size + InsData2Size); TS_ASSERT(std::equal(std::begin(Data), std::end(Data), vec.begin())); TS_ASSERT(std::equal(std::begin(InsData2), std::end(InsData2), vec.begin() + DataSize)); - TS_ASSERT(std::equal(std::begin(InsData1), std::end(InsData1), vec.begin() + DataSize + InsData2Size)); + COMMS_GNU_WARNING_PUSH +#if COMMS_IS_GCC_11 + COMMS_GNU_WARNING_DISABLE("-Wstringop-overread") +#endif // #if COMMS_IS_GCC_11 + TS_ASSERT(std::equal(std::begin(InsData1), std::end(InsData1), vec.begin() + DataSize + InsData2Size)); + COMMS_GNU_WARNING_POP iter = vec.erase(vec.begin() + DataSize, vec.begin() + DataSize + InsData2Size); TS_ASSERT_EQUALS(iter, vec.begin() + DataSize);