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);