update parser (#5)

This commit is contained in:
tompzf
2026-01-16 11:40:02 +01:00
committed by GitHub
parent 5039a37131
commit 234be8917f
115 changed files with 14038 additions and 5380 deletions

View File

@@ -84,7 +84,8 @@ endif()
# Default C++ settings # Default C++ settings
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
message("Use MSVC compiler...") message("Use MSVC compiler...")
add_compile_options(/W4 /WX /wd4996 /wd4127 /permissive- /Zc:rvalueCast) add_compile_options(/W4 /WX /wd4996 /wd4127 /permissive- /Zc:rvalueCast /Zi)
add_link_options(/DEBUG)
if (CMAKE_ENABLE_PREFAST) if (CMAKE_ENABLE_PREFAST)
message("Microsoft PREfast static code analysis enabled...") message("Microsoft PREfast static code analysis enabled...")

202
LICENSE.txt Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -218,7 +218,7 @@ JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If # line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus # set to NO, the Qt-style will behave just like regular Qt-style comments (thus
# requiring an explicit \brief command for a brief description.) # requiring an explicit @brief command for a brief description.)
# The default value is: NO. # The default value is: NO.
QT_AUTOBRIEF = NO QT_AUTOBRIEF = NO

View File

@@ -8,6 +8,7 @@
#include <support/app_control.h> #include <support/app_control.h>
#include <support/component_impl.h> #include <support/component_impl.h>
#include <support/timer.h> #include <support/timer.h>
#include <atomic>
#include "signal_names.h" #include "signal_names.h"
#include <fcntl.h> #include <fcntl.h>
#include "vss_vehiclepositioncurrentlatitude_vd_rx.h" #include "vss_vehiclepositioncurrentlatitude_vd_rx.h"
@@ -192,7 +193,7 @@ private:
mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message
std::thread m_threadReadTxSignals; ///< Simulation datalink thread. std::thread m_threadReadTxSignals; ///< Simulation datalink thread.
bool m_bThreadStarted = false; ///< Set when initialized. bool m_bThreadStarted = false; ///< Set when initialized.
bool m_bRunning = false; ///< When set, the application is running. std::atomic_bool m_bRunning = false; ///< When set, the application is running.
mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message
sdv::core::CSignal m_signalCurrentLatitude; ///< Signal Current latitude sdv::core::CSignal m_signalCurrentLatitude; ///< Signal Current latitude

View File

@@ -11,6 +11,7 @@
#include <support/timer.h> #include <support/timer.h>
#include "signal_names.h" #include "signal_names.h"
#include <fcntl.h> #include <fcntl.h>
#include <atomic>
#include "../interfaces/vss_vehiclechassisdooraxle01left_vd_rx.h" #include "../interfaces/vss_vehiclechassisdooraxle01left_vd_rx.h"
#include "../interfaces/vss_vehiclechassisdooraxle01left_bs_rx.h" #include "../interfaces/vss_vehiclechassisdooraxle01left_bs_rx.h"
#include "../interfaces/vss_vehiclechassisdooraxle01right_bs_rx.h" #include "../interfaces/vss_vehiclechassisdooraxle01right_bs_rx.h"
@@ -205,7 +206,7 @@ private:
mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message
bool m_bThreadStarted = false; ///< Set when initialized. bool m_bThreadStarted = false; ///< Set when initialized.
bool m_bRunning = false; ///< When set, the application is running. std::atomic_bool m_bRunning = false; ///< When set, the application is running.
bool m_isExternalApp = false; ///< True when we have an external application bool m_isExternalApp = false; ///< True when we have an external application
mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message

View File

@@ -2,6 +2,7 @@
#define EXMAPLE_UTILITY_H #define EXMAPLE_UTILITY_H
#include <string> #include <string>
#include <atomic>
#include <support/app_control.h> #include <support/app_control.h>
#include <support/signal_support.h> #include <support/signal_support.h>
@@ -155,7 +156,7 @@ private:
bool m_bInitialized = false; ///< Set when initialized. bool m_bInitialized = false; ///< Set when initialized.
bool m_bCmdLnError = false; ///< Command line error occurred. bool m_bCmdLnError = false; ///< Command line error occurred.
bool m_bCmdLnHelp = false; ///< Command line help provided. bool m_bCmdLnHelp = false; ///< Command line help provided.
bool m_bRunning = false; ///< When set, the application is running. std::atomic_bool m_bRunning = false; ///< When set, the application is running.
ERunAs m_eRunAs = ERunAs::standalone; ///< Application operation. ERunAs m_eRunAs = ERunAs::standalone; ///< Application operation.
EAppControlReporting m_eReporting = EAppControlReporting::silent; ///< Application control reporting. EAppControlReporting m_eReporting = EAppControlReporting::silent; ///< Application control reporting.
uint32_t m_uiInstance = 1000u; ///< Server instance to connect to. uint32_t m_uiInstance = 1000u; ///< Server instance to connect to.

View File

@@ -305,7 +305,7 @@ module sdv
interface IEntityComments interface IEntityComments
{ {
/** /**
* \brief Comment mask * @brief Comment mask
*/ */
enum ECommentMask : uint32 enum ECommentMask : uint32
{ {
@@ -342,7 +342,7 @@ module sdv
interface IDefinitionEntity interface IDefinitionEntity
{ {
/** /**
* \brief Does the entity have an unnamed definition. * @brief Does the entity have an unnamed definition.
* @return Returns 'true' when the entity has an unnamed definition; otherwise returns 'false'. * @return Returns 'true' when the entity has an unnamed definition; otherwise returns 'false'.
*/ */
boolean IsUnnamed() const; boolean IsUnnamed() const;

View File

@@ -87,7 +87,7 @@ module sdv
}; };
/** /**
* \brief Interface for managing IPC connection. * @brief Interface for managing IPC connection.
*/ */
interface IConnect interface IConnect
{ {

View File

@@ -34,28 +34,42 @@ module sdv
/** /**
* @brief Collection of possible data in parse tree node * @brief Collection of possible data in parse tree node
*/ */
enum ENodeType : uint8 enum ENodeType : uint32
{ {
node_table, //!< Table node_table, ///< Table
node_array, //!< Array node_array, ///< Array
node_integer, //!< Integer node_integer, ///< Integer
node_floating_point, //!< Floating point node_floating_point, ///< Floating point
node_boolean, //!< Boolean node_boolean, ///< Boolean
node_string, //!< String node_string, ///< String
node_invalid //!< Invalid content node_invalid ///< Invalid content
}; };
/**
* @brief The indicator of an invalid position used in various functions using indices.
*/
const uint32 npos = 0xFFFFFFFF;
/** /**
* @brief Node information interface * @brief Node information interface
*/ */
interface INodeInfo interface INodeInfo
{ {
/** /**
* @brief Get the node name. * @brief Get the node name (no conversion to a literal or quoted key is made).
* @return String containing the name of the node. * @return String containing the name of the node.
*/ */
u8string GetName() const; u8string GetName() const;
/**
* @brief Get the node path following the key rules for bar, literal and quoted keys.
* @param[in] bResolveArrays When set, include array indices in the path. The path returned without array indices is
* identical to the code in the TOML file. The path returned with array indices is identical to the direct access of
* nodes within the parser.
* @return String containing the path of the node.
*/
u8string GetPath(in boolean bResolveArrays) const;
/** /**
* @brief Get the node type. * @brief Get the node type.
* @return Type of the node. * @return Type of the node.
@@ -70,10 +84,75 @@ module sdv
any GetValue() const; any GetValue() const;
/** /**
* @brief Return the TOML string belonging to this node including all potential child nodes. * @brief Get the index of this node within the parent collection.
* @return The index of the node within the parent collection node or npos when no parent is available.
*/
uint32 GetIndex() const;
/**
* @brief Get the parent collection node.
* @return Returns the parent collection node or NULL when there is no parent collection node.
*/
IInterfaceAccess GetParent() const;
/**
* @brief Return the TOML string belonging to this node including all potential child nodes and comments.
* @return The TOML string. * @return The TOML string.
*/ */
u8string GetTOML() const; u8string GetTOML() const;
/**
* @brief Comment access flags.
* @details The comment access flags contain the comment code snippet index (0..15) as well as flags to receive the
* comments in interpreted or raw form. For comment code snippets are defined for all nodes, the comments before and
* after the node not related to the node and the comments before and after the node related to the node. Comment or
* whitespace in between the node tokens are part of the other 11 indices (each node interprets this differently).
*/
enum ECommentFlags
{
comment_before = 0, ///< The comment before the node. This will insert a newline and the comment
///< text preceded by the '#' character.
comment_behind = 1, ///< The comment behind the node. This will align the comment to te next tab
///< stop and precedes the comment with the '#' character.
out_of_scope_comment_before = 2, ///< An independent comment before the node. This comment is not part of the
///< node and is separated by an extra newline.
out_of_scope_comment_behind = 3, ///< An independent comment behind the node. This comment is not part of the
///< node and is separated by an extra newline.
comment_index_mask = 15, ///< Comment type mask to be used ot filter the comment type.
raw_comment = 8, ///< Store the comment exactly as provided (whitespace and comments).
replace_whitespace = 16, ///< When set, the comment will replace the current whitespace as well.
///< Used with SetComment function. Automatically enabled for raw comments.
};
/**
* @brief Set or replace a comment for the node.
* @remarks This function can also be used to insert whitespace (with or without comments) when used in raw mode.
* Set the comment text for the node. If a comment is proided as text (normal behavior), the comment text will be
* formatted automatically when generating the TOML text. If the comment is provided as raw comment, the text should
* contain all whitespace and the comment '#' character before the comment text.
* Comments inserted before the enode will be inserted on the line before the node uness the comment is provided in raw
* format and is ended with a newline and optionally whitespace. Comment inserted behind the node will be inserted on
* the same line as the node.
* Comments provided as text is automatically wrapped to 80 characters if possible. Newlines in the text will cause a
* new comment line to start.
* @param[in] ssComment String containing the comment text or the raw comment string to set.
* @param[in] uiFlags One or more ECommentFlags flags influencing the behavior of the comment.
*/
void SetComment(in u8string ssComment, in uint32 uiFlags);
/**
* Get the current comment for the node.
* @remarks To receive the whitespace formatting the node, use this function in raw mode.
* @param[in] uiFlags One or more ECommentFlags flags identifying the string format of the comment to return.
* @return String with the comment text or an empty string if no comment is available.
*/
u8string GetComment(in uint32 uiFlags);
/**
* @brief Format the node automatically. This will remove the whitespace between the elements within the node. Comments
* will not be changed.
*/
void AutomaticFormat();
}; };
/** /**
@@ -107,6 +186,144 @@ module sdv
IInterfaceAccess GetNodeDirect(in u8string ssPath) const; IInterfaceAccess GetNodeDirect(in u8string ssPath) const;
}; };
/**
* @brief Extend the collection node with values, arrays and tables.
*/
interface INodeCollectionInsert
{
/**
* @brief Insert a value into the collection at the location before the supplied index.
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
* collection count to insert the node at the end of the collection. Value nodes cannot be inserted behind external
* tables and table arrays. If the index is referencing a position behind an external table or a table array, the index
* is automatically corrected.
* @param[in] ssName Name of the node to insert. Will be ignored for an array collection. The name must adhere to the
* key names defined by the TOML specification. Defining the key multiple times is not allowed. Quotation of key names
* is done automatically; the parser decides itself whether the key is bare-key, a literal key or a quoted key.
* @param[in] anyValue The value of the node, being either an integer, floating point number, boolean value or a string.
* Conversion is automatically done to int64, double float, bool or u8string.
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
*/
IInterfaceAccess InsertValue(in uint32 uiIndex, in u8string ssName, in any anyValue);
/**
* @brief Insert an array into the collection at the location before the supplied index.
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
* collection count to insert the node at the end of the collection. Array nodes cannot be inserted behind external
* tables and table arrays. If the index is referencing a position behind an external table or a table array, the index
* is automatically corrected.
* @param[in] ssName Name of the array node to insert. Will be ignored if the current node is also an array collection.
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
* literal key or a quoted key.
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
*/
IInterfaceAccess InsertArray(in uint32 uiIndex, in u8string ssName);
/**
* @brief Insert a table into the collection at the location before the supplied index.
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
* collection count to insert the node at the end of the collection. Table nodes cannot be inserted before value nodes
* or arrays. If the index is referencing a position before a value node or an array, the index is automatically
* corrected.
* @param[in] ssKeyName Name of the table node to insert. Will be ignored if the current node is an array collection.
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
* literal key or a quoted key.
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
*/
IInterfaceAccess InsertTable(in uint32 uiIndex, in u8string ssKeyName);
/**
* @brief Insert a table array into the collection at the location before the supplied index.
* @param[in] uiIndex The insertion location to insert the node before. Can be npos or any value larger than the
* collection count to insert the node at the end of the collection. Table array nodes cannot be inserted before value
* nodes or arrays. If the index is referencing a position before a value node or an array, the index is automatically
* corrected.
* @param[in] ssName Name of the array node to insert. Will be ignored if the current node is also an array collection.
* The name must adhere to the key names defined by the TOML specification. Defining the key multiple times is not
* allowed. Quotation of key names is done automatically; the parser decides itself whether the key is bare-key, a
* literal key or a quoted key.
* @return On success the interface to the newly inserted node is returned or NULL otherwise.
*/
IInterfaceAccess InsertTableArray(in uint32 uiIndex, in u8string ssName);
/**
* @brief The result of the TOML string to insert.
*/
enum EInsertResult : uint32
{
invalid_TOML, ///< The TOML string was invalid or didn't fit the collection node it was to be inserted.
insert_partly_success, ///< Part, but not all nodes could be inserted (duplicate nodes are not inserted).
insert_success, ///< All nodes could be inserted or the TOML didn't contain any nodes.
};
/**
* @brief Insert a TOML string as a child of the current collection node. If the collection is a table, the TOML string
* should contain values and inline/external/array-table nodes with names. If the collection is an array, the TOML
* string should contain and inline table nodes without names.
* @param[in] ssTOML The TOML string to insert.
* @param[in] bRollbackOnPartly If only part of the nodes could be inserted, no node will be inserted.
* @return The result of the insertion.
*/
EInsertResult InsertTOML(in u8string ssTOML, in boolean bRollbackOnPartly);
};
/**
* @brief Remove the current node.
* @remarks The root node cannot be deleted.
*/
interface INodeDelete
{
/**
* @brief Delete the current node.
* @attention A successful deletion will cause all interfaces to the current node to become inoperable.
* @return Returns whether the deletion was successful.
*/
boolean DeleteNode();
};
/**
* @brief Update the current node.
* @remarks The root node cannot be updated.
*/
interface INodeUpdate
{
/**
* @brief Change the key name of the node (if the node is not a value node of an array).
* @param[in] ssNewName The name to assign to the node. The name must adhere to the key names defined by the TOML
* specification. Defining the key multiple times is not allowed. Quotation of key names is done automatically; the
* parser decides itself whether the key is bare-key, a literal key or a quoted key.
* @return Returns whether the name change was successful.
*/
boolean ChangeName(in u8string ssNewName);
/**
* @brief Change the value of the node.
* @remarks Only valid for value nodes. Changing the value type is not supported.
* @param[in] anyNewValue The value of the node, being either an integer, floating point number, boolean value or a
* string. Conversion is automatically done to int64, double float, bool or u8string.
* @return Returns whether the value change was successful.
*/
boolean ChangeValue(in any anyNewValue);
/**
* @brief Move up the node in the collection.
* @remarks External tables or table arrays cannot be moved before value nodes.
* @remarks Moving if the node is the first node is not possible.
* @return Returns whether the move was successful.
*/
boolean MoveUp();
/**
* @brief Move down the node in the collection.
* @remarks Value nodes cannot be moved behind external tables or table arrays.
* @remarks Moving if the node is the last node is not possible.
* @return Returns whether the move was successful.
*/
boolean MoveDown();
};
/** /**
* @brief TOML parser interface. * @brief TOML parser interface.
*/ */

View File

@@ -308,7 +308,6 @@ namespace sdv
protected: protected:
/** /**
* @brief Build the module manifest. * @brief Build the module manifest.
* @return Returns the pointer to a zero terminated string containing the module manifest or NULL when there is no string.
*/ */
void BuildManifest() void BuildManifest()
{ {
@@ -860,7 +859,7 @@ Version = )code" << SDVFrameworkInterfaceVersion
/** /**
* @brief Declare the object class type. To be placed in the SDV object class derived from CSdvObject. * @brief Declare the object class type. To be placed in the SDV object class derived from CSdvObject.
* \param class_type The type of the object (EObjectType). * @param class_type The type of the object (EObjectType).
*/ */
#define DECLARE_OBJECT_CLASS_TYPE(class_type) \ #define DECLARE_OBJECT_CLASS_TYPE(class_type) \
/** \ /** \

View File

@@ -58,7 +58,8 @@ namespace sdv
} }
/** /**
* Select the section that is supported now. * @brief Select the section that is supported now.
* @param[in] uiSection The section number that should be considered.
*/ */
void Select(int uiSection) void Select(int uiSection)
{ {

View File

@@ -49,6 +49,14 @@ namespace sdv::toml
*/ */
sdv::u8string GetName(); sdv::u8string GetName();
/**
* @brief Retrurn the node qualified path including the parent path.
* @details The qualified path is a path composed through all parent nodes containing quoted names where needed. The path
* to the node can be used to directly access the node.
* @return String containing the qualified path to the node.
*/
sdv::u8string GetQualifiedPath();
/** /**
* @brief Get the node type. * @brief Get the node type.
* @return The node type. * @return The node type.
@@ -247,6 +255,11 @@ namespace sdv::toml
return m_pNodeInfo ? m_pNodeInfo->GetName() : sdv::u8string(); return m_pNodeInfo ? m_pNodeInfo->GetName() : sdv::u8string();
} }
inline sdv::u8string CNode::GetQualifiedPath()
{
return m_pNodeInfo ? m_pNodeInfo->GetPath(true) : sdv::u8string();
}
inline ENodeType CNode::GetType() inline ENodeType CNode::GetType()
{ {
return m_pNodeInfo ? m_pNodeInfo->GetType() : ENodeType::node_invalid; return m_pNodeInfo ? m_pNodeInfo->GetType() : ENodeType::node_invalid;

View File

@@ -40,14 +40,24 @@ namespace asc
enum class EState {header, body, footer} eState = EState::header; enum class EState {header, body, footer} eState = EState::header;
while (std::getline(fstream, ssLine)) while (std::getline(fstream, ssLine))
{ {
// Compare two characters and two strings case insensitive
auto ichar_equals = [](char a, char b)
{
return std::tolower(static_cast<unsigned char>(a)) == std::tolower(static_cast<unsigned char>(b));
};
auto iequals = [&](const std::string& a, const std::string& b)
{
return std::equal(a.begin(), a.end(), b.begin(), b.end(), ichar_equals);
};
switch (eState) switch (eState)
{ {
case EState::header: case EState::header:
if (ssLine.compare(0, 18, "Begin TriggerBlock") == 0) if (iequals(ssLine.substr(0, 18), "Begin TriggerBlock"))
eState = EState::body; eState = EState::body;
break; break;
case EState::body: case EState::body:
if (ssLine.compare(0, 16, "End TriggerBlock") == 0) if (iequals(ssLine.substr(0, 16), "End TriggerBlock"))
eState = EState::footer; eState = EState::footer;
else else
ProcessSample(ssLine); ProcessSample(ssLine);
@@ -74,7 +84,7 @@ namespace asc
uint32_t CAscReader::GetMessageCount() const uint32_t CAscReader::GetMessageCount() const
{ {
return m_lstMessages.size(); return static_cast<uint32_t>(m_lstMessages.size());
} }
uint32_t CAscReader::GetLoopCount() const uint32_t CAscReader::GetLoopCount() const

View File

@@ -5,6 +5,7 @@
#include <functional> #include <functional>
#include <filesystem> #include <filesystem>
#include <thread> #include <thread>
#include <atomic>
namespace asc namespace asc
{ {
@@ -159,8 +160,8 @@ namespace asc
std::list<SCanMessage> m_lstMessages; ///< Vector with messages std::list<SCanMessage> m_lstMessages; ///< Vector with messages
std::list<SCanMessage>::iterator m_itCurrent; ///< Current iterator position std::list<SCanMessage>::iterator m_itCurrent; ///< Current iterator position
std::thread m_threadPlayback; ///< Playback thread. std::thread m_threadPlayback; ///< Playback thread.
bool m_bPlaybackThread = false; ///< Set when running playback thread std::atomic_bool m_bPlaybackThread = false; ///< Set when running playback thread
bool m_bPlayback = false; ///< Set when running playback std::atomic_bool m_bPlayback = false; ///< Set when running playback
std::atomic<uint32_t> m_uiLoopCount{ 0 }; ///< Counter how often the data set was sent std::atomic<uint32_t> m_uiLoopCount{ 0 }; ///< Counter how often the data set was sent
}; };
} }

View File

@@ -58,7 +58,8 @@ bool CArgumentDefBase::CompareNameAndAssign(CArgumentIterator& rargit, const std
while (nPos < rssArgument.size()) while (nPos < rssArgument.size())
{ {
char c = rssArgument[nPos]; char c = rssArgument[nPos];
if (!std::isalnum(c) && c != '_' && c != '?') // Protect against multi-byte characters (UTF-8).
if (static_cast<uint8_t>(c) > 127u || (!std::isalnum(c) && c != '_' && c != '?'))
break; break;
ssArgNameCS += c; ssArgNameCS += c;
ssArgNameCI += static_cast<char>(std::tolower(c)); ssArgNameCI += static_cast<char>(std::tolower(c));

View File

@@ -1389,7 +1389,7 @@ public:
* @param[in] rtArgument Reference to the argument. * @param[in] rtArgument Reference to the argument.
* @param[in] rssValue Reference to the string containing the value to be assigned. * @param[in] rssValue Reference to the string containing the value to be assigned.
*/ */
void Parse(TVar& /*rtArgument*/, const std::string& /*rssValue*/) {} void Parse([[maybe_unused]] TVar& rtArgument, [[maybe_unused]] const std::string& rssValue) {}
/** /**
* @brief Get the markup string for the argument type. Overload in derived class. * @brief Get the markup string for the argument type. Overload in derived class.

View File

@@ -8,12 +8,28 @@
#include <filesystem> #include <filesystem>
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <thread>
#include <queue>
#include <chrono>
#include <atomic>
#include "exec_dir_helper.h" #include "exec_dir_helper.h"
#ifndef ENABLE_DEBUG_LOG
/** /**
* @brief Enable debug log by defining it and assigning it the value 1. * @brief Enable debug log by defining the ENABLE_DEBUG_LOG to a value other than zero.
*/ */
#define ENABLE_DEBUG_LOG 1 #define ENABLE_DEBUG_LOG 1
#endif
#ifndef DECOUPLED_DEBUG_LOG
/**
* @brief When DECOUPLED is set to a value not zero, the logging is decoupled from the writing.
* @attention Decoupling the logging from the writing might improve the accuracy of the logging, and reduce influencing the program
* to an absolute minimum. It does, however, cause messages to be written delayed, which might lead to missing the clue when a
* crash occurs. To log messages synchronously with the program code, set DECOUPLED_DEBUG_LOG to the value zero.
*/
#define DECOUPLED_DEBUG_LOG 1
#endif
/** /**
* @brief Debug namespace * @brief Debug namespace
@@ -33,14 +49,6 @@ namespace debug
CLogger() CLogger()
{ {
m_pathLogFile = GetExecDirectory() / GetExecFilename().replace_extension(".log"); m_pathLogFile = GetExecDirectory() / GetExecFilename().replace_extension(".log");
std::unique_lock<std::mutex> lock(m_mtxLogger);
std::ofstream fstream(m_pathLogFile, std::ios_base::out | std::ios_base::trunc);
if (fstream.is_open())
{
fstream << "Starting log of " << GetExecFilename().generic_u8string() << std::endl;
fstream.close();
}
std::clog << "Starting log of " << GetExecFilename().generic_u8string() << std::flush << std::endl;
} }
/** /**
@@ -48,14 +56,18 @@ namespace debug
*/ */
~CLogger() ~CLogger()
{ {
std::unique_lock<std::mutex> lock(m_mtxLogger); #if DECOUPLED_DEBUG_LOG != 0
std::ofstream fstream(m_pathLogFile, std::ios_base::out | std::ios_base::app); // Shut down the logger thread
if (fstream.is_open()) if (m_threadLogger.joinable())
{ {
fstream << "End of log..." << std::endl; m_bShutdown = true;
fstream.close(); m_threadLogger.join();
} }
std::clog << "End of log..." << std::flush << std::endl;
// Prevent the logger mutex to be still in use.
std::unique_lock<std::mutex> lock(m_mtxLogger);
lock.unlock();
#endif
} }
/** /**
@@ -64,16 +76,28 @@ namespace debug
*/ */
void Log(const std::string& rss) void Log(const std::string& rss)
{ {
// Create the message structure
SLogMsg sMsg{std::this_thread::get_id(), DepthPerThreadOperation(EDepthOperation::report_only), rss};
#if DECOUPLED_DEBUG_LOG != 0
std::unique_lock<std::mutex> lock(m_mtxLogger); std::unique_lock<std::mutex> lock(m_mtxLogger);
std::ofstream fstream(m_pathLogFile, std::ios_base::out | std::ios_base::app);
std::string ssIndent(m_nDepth, '>'); // First log entry starts logging
if (!ssIndent.empty()) ssIndent += ' '; if (!m_threadLogger.joinable())
if (fstream.is_open())
{ {
fstream << ssIndent << rss << std::endl; m_threadLogger = std::thread(&CLogger::LogToFileThreadFunc, this);
fstream.close(); while (!m_threadLogger.joinable())
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
std::clog << ssIndent << rss << std::flush << std::endl; std::cout << rss << std::endl;
// Add message to the log queue
m_queueLogger.push(sMsg);
#else
std::ofstream fstream;
fstream.open(m_pathLogFile, std::ios_base::out | std::ios_base::app);
LogMsg(fstream, sMsg);
#endif
} }
/** /**
@@ -81,7 +105,7 @@ namespace debug
*/ */
void IncrDepth() void IncrDepth()
{ {
m_nDepth++; DepthPerThreadOperation(EDepthOperation::increase);
} }
/** /**
@@ -89,13 +113,141 @@ namespace debug
*/ */
void DecrDepth() void DecrDepth()
{ {
if (m_nDepth) DepthPerThreadOperation(EDepthOperation::decrease);
m_nDepth--;
} }
private: private:
/**
* @brief Start the logging
*/
void StartLog()
{
std::ofstream fstream;
fstream.open(m_pathLogFile, std::ios_base::out | std::ios_base::trunc);
if (fstream.is_open())
{
fstream << "Starting log of " << GetExecFilename().generic_u8string() << std::endl;
fstream.close();
}
std::cout << "Starting log of " << GetExecFilename().generic_u8string() << std::endl << std::flush;
}
/**
* @brief Finish the logging
*/
void FinishLog()
{
// Finish logging
std::ofstream fstream;
fstream.open(m_pathLogFile, std::ios_base::out | std::ios_base::app);
if (fstream.is_open())
{
fstream << "End log of " << GetExecFilename().generic_u8string() << std::endl;
fstream.close();
}
std::cout << "End log of " << GetExecFilename().generic_u8string() << std::endl << std::flush;
}
/**
* @brief Log structure containing thread and log information.
*/
struct SLogMsg
{
std::thread::id id; ///< Thread ID
size_t nDepth; ///< Depth within the calls
std::string ssMsg; ///< Message to log
};
/**
* @brief Log a message.
* @param[in] rfstream Reference to the stream to log to.
* @param[in9 rsMsg Reference to the message to log.
*/
void LogMsg(std::ofstream& rfstream, const SLogMsg& rsMsg)
{
std::string ssIndent(rsMsg.nDepth, '>');
if (!ssIndent.empty())
ssIndent += ' ';
if (rfstream.is_open())
{
rfstream << rsMsg.id << ": " << ssIndent << rsMsg.ssMsg << std::endl;
rfstream.close();
}
std::cout << rsMsg.id << ": " << ssIndent << rsMsg.ssMsg << std::endl << std::flush;
}
#if DECOUPLED_DEBUG_LOG != 0
/**
* @brief Log to file thread function.
*/
void LogToFileThreadFunc()
{
// Start logging
StartLog();
// Log until shutdown
while (!m_bShutdown)
{
std::unique_lock<std::mutex> lock(m_mtxLogger);
// Are there any message. If not, pause for 100 ms
if (m_queueLogger.empty())
{
lock.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
std::queue<SLogMsg> queueLocal = std::move(m_queueLogger);
lock.unlock();
// Open the file and log all messages that are in the queue
std::ofstream fstream;
fstream.open(m_pathLogFile, std::ios_base::out | std::ios_base::app);
while (!queueLocal.empty())
{
auto sMsg = std::move(queueLocal.front());
queueLocal.pop();
LogMsg(fstream, sMsg);
}
}
// Finish logging
FinishLog();
}
#endif
/**
* @brief Depth operation selection.
*/
enum class EDepthOperation
{
increase,
decrease,
report_only,
};
/**
* @brief Do a per thread function depth operation (increase, decrease or report).
* @param[in] eOperation The operation to do.
* @return The current function call depth.
*/
size_t DepthPerThreadOperation(EDepthOperation eOperation)
{
thread_local static size_t nDepth = 0; ///< Depth level for indentation.
if (eOperation == EDepthOperation::increase)
nDepth++;
else if (eOperation == EDepthOperation::decrease && nDepth)
nDepth--;
return nDepth;
}
std::filesystem::path m_pathLogFile; ///< Path to the log file. std::filesystem::path m_pathLogFile; ///< Path to the log file.
#if DECOUPLED_DEBUG_LOG != 0
std::mutex m_mtxLogger; ///< Protect against multiple log entries at the same time. std::mutex m_mtxLogger; ///< Protect against multiple log entries at the same time.
size_t m_nDepth; ///< Depth level for indentation. std::thread m_threadLogger; ///< Logger thread
std::atomic_bool m_bShutdown = false; ///< When set, terminate the logging thread
std::queue<SLogMsg> m_queueLogger; ///< Queue with messages to log.
#endif
}; };
/** /**

View File

@@ -125,7 +125,9 @@ namespace ipc
else else
{ {
std::srand(static_cast<unsigned>(std::time(nullptr))); // Use current time as seed for random generator std::srand(static_cast<unsigned>(std::time(nullptr))); // Use current time as seed for random generator
m_ssName = std::string("NAMED_MUTEX_") + std::to_string(std::rand()); uint32_t uiRand = 0;
while (!uiRand) uiRand = std::rand();
m_ssName = std::string("NAMED_MUTEX_") + std::to_string(uiRand);
} }
m_handle = CreateMutexA(nullptr, FALSE, (std::string("Global\\") + m_ssName).c_str()); m_handle = CreateMutexA(nullptr, FALSE, (std::string("Global\\") + m_ssName).c_str());
} }
@@ -191,7 +193,9 @@ namespace ipc
else else
{ {
std::srand(static_cast<unsigned>(std::time(nullptr))); // Use current time as seed for random generator std::srand(static_cast<unsigned>(std::time(nullptr))); // Use current time as seed for random generator
m_ssName = std::string("NAMED_MUTEX_") + std::to_string(std::rand()); uint32_t uiRand = 0;
while (!uiRand) uiRand = std::rand();
m_ssName = std::string("NAMED_MUTEX_") + std::to_string(uiRand);
} }
m_handle = sem_open(m_ssName.c_str(), O_CREAT, 0777 /*O_RDWR*/, 1); m_handle = sem_open(m_ssName.c_str(), O_CREAT, 0777 /*O_RDWR*/, 1);
} }

View File

@@ -11,7 +11,7 @@
#include <support/interface_ptr.h> #include <support/interface_ptr.h>
/** /**
* \brief Local memory manager class allowing the use of the SDV support classes without having to start the framework. * @brief Local memory manager class allowing the use of the SDV support classes without having to start the framework.
* \attention Do not use the local memory manager together with the framework's memory manager. * \attention Do not use the local memory manager together with the framework's memory manager.
*/ */
class CLocalMemMgr : public sdv::core::IMemoryAlloc, public sdv::IInterfaceAccess, class CLocalMemMgr : public sdv::core::IMemoryAlloc, public sdv::IInterfaceAccess,
@@ -19,13 +19,13 @@ class CLocalMemMgr : public sdv::core::IMemoryAlloc, public sdv::IInterfaceAcces
{ {
public: public:
/** /**
* \brief Constructor assigning this class to the local services. * @brief Constructor assigning this class to the local services.
*/ */
CLocalMemMgr() CLocalMemMgr()
{} {}
/** /**
* \brief Destructor removing this class from the local services. * @brief Destructor removing this class from the local services.
*/ */
~CLocalMemMgr() ~CLocalMemMgr()
{} {}

View File

@@ -32,7 +32,7 @@
* extension ending with "n" match. * extension ending with "n" match.
* - "subdir?_*" - all files from a directory starting with the name "subdir" followed by a single digit or character, followed * - "subdir?_*" - all files from a directory starting with the name "subdir" followed by a single digit or character, followed
* with a "_" and zero or more characters. * with a "_" and zero or more characters.
* - "**\/file*.bin" - all files starting with the name "file" followed by zero or more characters and the extension ".bin" in this * - "**\\/file*.bin" - all files starting with the name "file" followed by zero or more characters and the extension ".bin" in this
* and any subdirectory. * and any subdirectory.
* @param[in] rpathRel Reference to the relative path to check for a match. * @param[in] rpathRel Reference to the relative path to check for a match.
* @param[in] rssPattern Reference to the string containing the pattern to match. * @param[in] rssPattern Reference to the string containing the pattern to match.

View File

@@ -3,7 +3,11 @@
#include <thread> #include <thread>
#include <cstdint> #include <cstdint>
#include <string>
#include <iostream> #include <iostream>
#include <fstream>
#include <sstream>
#include <atomic>
#ifdef _WIN32 #ifdef _WIN32
// Prevent reassignment of "interface" // Prevent reassignment of "interface"
#pragma push_macro("interface") #pragma push_macro("interface")
@@ -65,11 +69,49 @@ public:
} }
private: private:
#ifdef _WIN32
#elif defined __linux__
/**
* @brief Check whether the debugger is present and running the application.
* @return Returns true when the debugger is present or false otherwise.
*/
bool IsDebuggerPresent()
{
std::string ssFilename = "/proc/self/status"; // For the current process
std::ifstream fstreamStatus(ssFilename);
if (!fstreamStatus.is_open()) return false; // Cannot open file, assume not debugged
std::string ssLine;
while (std::getline(fstreamStatus, ssLine))
{
if (ssLine.substr(0, 9) == "TracerPid")
{
std::stringstream sstreamLine(ssLine);
std::string ssKey;
int iTracerPid = 0;
sstreamLine >> ssKey >> iTracerPid;
return iTracerPid != 0;
}
}
return false; // TracerPid not found
}
#else
/**
* @brief Check whether the debugger is present and running the application.
* @return Returns true when the debugger is present or false otherwise.
*/
bool IsDebuggerPresent()
{
// No implementation available to check for a debugger.
return false;
}
#endif
/** /**
* @brief Watch thread function. * @brief Watch thread function.
* @param[in] iWatchdogTimeS The duration the watchdog is monitoring before process termination in seconds (default 120s). * @param[in] iWatchdogTimeS The duration the watchdog is monitoring before process termination in seconds.
*/ */
void WatchdogThreadFunc(int64_t iWatchdogTimeS = 120ll) void WatchdogThreadFunc(int64_t iWatchdogTimeS)
{ {
// Run for the most the set time; then terminate... // Run for the most the set time; then terminate...
auto tpStart = std::chrono::steady_clock::now(); auto tpStart = std::chrono::steady_clock::now();
@@ -79,6 +121,9 @@ private:
auto tpNow = std::chrono::steady_clock::now(); auto tpNow = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(tpNow - tpStart).count() > iWatchdogTimeS) if (std::chrono::duration_cast<std::chrono::seconds>(tpNow - tpStart).count() > iWatchdogTimeS)
{ {
// Do not end the process when a debugger is present.
if (IsDebuggerPresent()) continue;
std::cerr << "WATCHDOG TERMINATION ENFORCED!!!" << std::endl; std::cerr << "WATCHDOG TERMINATION ENFORCED!!!" << std::endl;
std::cerr.flush(); std::cerr.flush();
#ifdef _WIN32 #ifdef _WIN32
@@ -97,7 +142,7 @@ private:
} }
} }
bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate. std::atomic_bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate.
std::thread m_threadWatchdog; ///< The watchdog thread. std::thread m_threadWatchdog; ///< The watchdog thread.
}; };

View File

@@ -10,6 +10,7 @@
#include <condition_variable> #include <condition_variable>
#include <cstdint> #include <cstdint>
#include <list> #include <list>
#include <atomic>
#include "../flags.h" #include "../flags.h"
/** /**
@@ -112,8 +113,8 @@ private:
void ThreadFunc(); void ThreadFunc();
std::thread m_thread; ///< The thread that executes the tasks. std::thread m_thread; ///< The thread that executes the tasks.
bool m_bShutdown = false; ///< Set when the thread should terminate. std::atomic_bool m_bShutdown = false; ///< Set when the thread should terminate.
bool m_bStarted = false; ///< Set when the thread has started. std::atomic_bool m_bStarted = false; ///< Set when the thread has started.
std::function<void()> m_fnTask; ///< The task to execute (will be updated with new tasks before execution). std::function<void()> m_fnTask; ///< The task to execute (will be updated with new tasks before execution).
std::mutex m_mtxSyncStart; ///< The startup synchronization mutex. std::mutex m_mtxSyncStart; ///< The startup synchronization mutex.
std::condition_variable m_cvStarted; ///< Triggered by the thread to indicate that it has started. std::condition_variable m_cvStarted; ///< Triggered by the thread to indicate that it has started.

View File

@@ -5,6 +5,9 @@
#include <ctime> #include <ctime>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#ifdef _MSC_VER
#include <process.h>
#endif
#ifdef __GNUC__ #ifdef __GNUC__
#include <unistd.h> #include <unistd.h>
#endif #endif
@@ -29,7 +32,15 @@ inline std::string GetTimestamp()
const auto current_milliseconds {std::chrono::duration_cast<std::chrono::milliseconds> (current_time_since_epoch).count() % 1000}; const auto current_milliseconds {std::chrono::duration_cast<std::chrono::milliseconds> (current_time_since_epoch).count() % 1000};
std::ostringstream stream; std::ostringstream stream;
stream << "PID#" << std::dec << getpid() << " " << std::put_time(&current_localtime, "%H:%M:%S") << "." << std::setw(3) << std::setfill('0') << current_milliseconds << ": "; #ifdef _MSC_VER
stream << "PID#" << std::dec << _getpid() << " " << std::put_time(&current_localtime, "%H:%M:%S") << "." << std::setw(3) <<
std::setfill('0') << current_milliseconds << ": ";
#elif defined __GNUC__
stream << "PID#" << std::dec << getpid() << " " << std::put_time(&current_localtime, "%H:%M:%S") << "." << std::setw(3) <<
std::setfill('0') << current_milliseconds << ": ";
#else
#error The current OS is not supported!
#endif
return stream.str(); return stream.str();
} }
@@ -66,7 +77,8 @@ inline void Trace(TArgs... tArgs)
} }
#else // ENABLE_TRACE == 0 #else // ENABLE_TRACE == 0
inline void Trace() template <typename... TArgs>
inline void Trace(TArgs...)
{} {}
#ifdef __GNUC__ #ifdef __GNUC__

View File

@@ -6,6 +6,7 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <map> #include <map>
#include <atomic>
/** /**
* @brief trace fifo open flags * @brief trace fifo open flags
@@ -462,7 +463,7 @@ private:
const size_t nReadIndex = 0; ///< The read index of the pipe descriptor. const size_t nReadIndex = 0; ///< The read index of the pipe descriptor.
const size_t nWriteIndex = 1; ///< The write index of the pipe descriptor. const size_t nWriteIndex = 1; ///< The write index of the pipe descriptor.
bool m_bShutdown = false; ///< Shutdown flag for the pipe reader thread. std::atomic_bool m_bShutdown = false; ///< Shutdown flag for the pipe reader thread.
int m_rgPipeStdOut[2] = { -1, -1 }; ///< StdOut pipe with read and write descriptors. int m_rgPipeStdOut[2] = { -1, -1 }; ///< StdOut pipe with read and write descriptors.
int m_rgPipeStdErr[2] = { -1, -1 }; ///< StdErr pipe with read and write descriptors. int m_rgPipeStdErr[2] = { -1, -1 }; ///< StdErr pipe with read and write descriptors.
int m_iOldStdOut = -1; ///< Old descriptor for the StdOut int m_iOldStdOut = -1; ///< Old descriptor for the StdOut

View File

@@ -1,4 +1,5 @@
#include <support/sdv_core.h> #include <support/sdv_core.h>
#include <atomic>
#include "../../global/cmdlnparser/cmdlnparser.cpp" #include "../../global/cmdlnparser/cmdlnparser.cpp"
#include "../../global/exec_dir_helper.h" #include "../../global/exec_dir_helper.h"
#include <support/app_control.h> #include <support/app_control.h>
@@ -22,7 +23,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
// See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev // See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev
// NOTE EVE 27.05.2025: in release builds, starting and ending the thread right after each other causes incorrect behavior and // NOTE EVE 27.05.2025: in release builds, starting and ending the thread right after each other causes incorrect behavior and
// leads in some cases to create a deadlock in the join-function. The solution is to add delays in the thread processing. // leads in some cases to create a deadlock in the join-function. The solution is to add delays in the thread processing.
bool bThreadStarted = false; std::atomic_bool bThreadStarted = false;
std::thread thread = std::thread([&]() std::thread thread = std::thread([&]()
{ {
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));

View File

@@ -957,7 +957,7 @@ namespace sdv
static constexpr ::sdv::interface_id _id = 0xEE1AD4FC2B9217BB; static constexpr ::sdv::interface_id _id = 0xEE1AD4FC2B9217BB;
/** /**
* \brief Comment mask * @brief Comment mask
*/ */
enum class ECommentMask : uint32_t enum class ECommentMask : uint32_t
{ {
@@ -1024,7 +1024,7 @@ namespace sdv
static constexpr ::sdv::interface_id _id = 0xC7BB02340D82D7AE; static constexpr ::sdv::interface_id _id = 0xC7BB02340D82D7AE;
/** /**
* \brief Does the entity have an unnamed definition. * @brief Does the entity have an unnamed definition.
* @return Returns 'true' when the entity has an unnamed definition; otherwise returns 'false'. * @return Returns 'true' when the entity has an unnamed definition; otherwise returns 'false'.
*/ */
virtual bool IsUnnamed() const = 0; virtual bool IsUnnamed() const = 0;

View File

@@ -110,7 +110,7 @@ public:
void CreateInheritanceValueChildNodes(); void CreateInheritanceValueChildNodes();
/** /**
* \brief Does the entity have an Unnamed definition. Overload of IDefinitionEntity::Unnamed. * @brief Does the entity have an Unnamed definition. Overload of IDefinitionEntity::Unnamed.
* @return Returns 'true' when the definition supports unnamed definition; 'false' otherwise. * @return Returns 'true' when the definition supports unnamed definition; 'false' otherwise.
*/ */
virtual bool IsUnnamed() const override { return m_bAnonymousDefinition; }; virtual bool IsUnnamed() const override { return m_bAnonymousDefinition; };

View File

@@ -99,8 +99,10 @@ protected:
std::string ssName; ///< Parameter name std::string ssName; ///< Parameter name
std::string ssDefaultValue; ///< Parameter default value (or empty for void return value) std::string ssDefaultValue; ///< Parameter default value (or empty for void return value)
std::string ssSize; ///< Parameter size std::string ssSize; ///< Parameter size
enum class EDirection { in, out, inout, ret, ignored } eDirection = EDirection::ignored; ///< Parameter direction or return value enum class EDirection ///< Parameter direction or return value
enum class EAllocType { direct, indirect, ifc} eAllocType = EAllocType::direct; ///< Parameter allocation type { in, out, inout, ret, ignored } eDirection = EDirection::ignored; ///< Parameter direction or return value
enum class EAllocType ///< Parameter allocation type
{ direct, indirect, ifc} eAllocType = EAllocType::direct; ///< Parameter allocation type
}; };
/** /**

View File

@@ -107,7 +107,7 @@ private:
void PotentialSwapBuffer(TChar* szBuffer, size_t nSize, bool bIsSourceBigEndian); void PotentialSwapBuffer(TChar* szBuffer, size_t nSize, bool bIsSourceBigEndian);
/** /**
* \brief Convert a UTF16 string to UTF-8. * @brief Convert a UTF16 string to UTF-8.
* @param[in] szBuffer The source string. * @param[in] szBuffer The source string.
* @param[in] nSize The length of the string (or zero terminating string when supplied as 0). * @param[in] nSize The length of the string (or zero terminating string when supplied as 0).
* @return Returns the string as UTF8 std::string object. * @return Returns the string as UTF8 std::string object.
@@ -115,7 +115,7 @@ private:
static std::string ConvertToUTF8(const char16_t* szBuffer, size_t nSize); static std::string ConvertToUTF8(const char16_t* szBuffer, size_t nSize);
/** /**
* \brief Convert a UTF32 string to UTF-8. * @brief Convert a UTF32 string to UTF-8.
* @param[in] szBuffer The source string. * @param[in] szBuffer The source string.
* @param[in] nSize The length of the string (or zero terminating string when supplied as 0). * @param[in] nSize The length of the string (or zero terminating string when supplied as 0).
* @return Returns the string as UTF8 std::string object. * @return Returns the string as UTF8 std::string object.

View File

@@ -1,3 +1,4 @@
#include <atomic>
#include <interfaces/ipc.h> #include <interfaces/ipc.h>
#include <support/sdv_core.h> #include <support/sdv_core.h>
#include "../../global/cmdlnparser/cmdlnparser.cpp" #include "../../global/cmdlnparser/cmdlnparser.cpp"
@@ -69,7 +70,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
// See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev // See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev
// NOTE EVE 27.05.2025: in release builds, starting and ending the thread right after each other causes incorrect behavior and // NOTE EVE 27.05.2025: in release builds, starting and ending the thread right after each other causes incorrect behavior and
// leads in some cases to create a deadlock in the join-function. The solution is to add delays in the thread processing. // leads in some cases to create a deadlock in the join-function. The solution is to add delays in the thread processing.
bool bThreadStarted = false; std::atomic_bool bThreadStarted = false;
std::thread thread = std::thread([&]() std::thread thread = std::thread([&]()
{ {
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -223,12 +224,13 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
return APP_CONTROL_INVALID_ISOLATION_CONFIG; return APP_CONTROL_INVALID_ISOLATION_CONFIG;
} }
SConnectEventCallbackWrapper sConnectEventWrapper; SConnectEventCallbackWrapper sConnectEventWrapper;
pConnect->RegisterStatusEventCallback(&sConnectEventWrapper); uint64_t uiCookie = pConnect->RegisterStatusEventCallback(&sConnectEventWrapper);
// Connect to the core repository // Connect to the core repository
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl"); sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
if (!pConnectionControl) if (!pConnectionControl)
{ {
pConnect->UnregisterStatusEventCallback(uiCookie);
if (!bSilent) if (!bSilent)
std::cerr << "ERROR: " << COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl; std::cerr << "ERROR: " << COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
return COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR; return COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR;
@@ -237,6 +239,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
sdv::com::TConnectionID tConnection = pConnectionControl->AssignClientEndpoint(ptrChannelEndpoint, 3000, pCoreRepo); sdv::com::TConnectionID tConnection = pConnectionControl->AssignClientEndpoint(ptrChannelEndpoint, 3000, pCoreRepo);
if (!tConnection.uiControl || !pCoreRepo) if (!tConnection.uiControl || !pCoreRepo)
{ {
pConnect->UnregisterStatusEventCallback(uiCookie);
if (!bSilent) if (!bSilent)
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << std::endl; std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << std::endl;
return CONNECT_SDV_SERVER_ERROR; return CONNECT_SDV_SERVER_ERROR;
@@ -246,6 +249,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
sdv::core::ILinkCoreRepository* pLinkCoreRepo = sdv::core::GetObject<sdv::core::ILinkCoreRepository>("RepositoryService"); sdv::core::ILinkCoreRepository* pLinkCoreRepo = sdv::core::GetObject<sdv::core::ILinkCoreRepository>("RepositoryService");
if (!pLinkCoreRepo) if (!pLinkCoreRepo)
{ {
pConnect->UnregisterStatusEventCallback(uiCookie);
if (!bSilent) if (!bSilent)
std::cerr << "ERROR: " << LINK_REPO_SERVICE_ERROR_MSG << std::endl; std::cerr << "ERROR: " << LINK_REPO_SERVICE_ERROR_MSG << std::endl;
return LINK_REPO_SERVICE_ERROR; return LINK_REPO_SERVICE_ERROR;
@@ -264,6 +268,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
sdv::core::IRepositoryUtilityCreate* pCreateUtility = sdv::core::GetObject<sdv::core::IRepositoryUtilityCreate>("RepositoryService"); sdv::core::IRepositoryUtilityCreate* pCreateUtility = sdv::core::GetObject<sdv::core::IRepositoryUtilityCreate>("RepositoryService");
if (!pRepositoryInfo || !pRepositoryControl || !pObjectAccess || !pCreateUtility) if (!pRepositoryInfo || !pRepositoryControl || !pObjectAccess || !pCreateUtility)
{ {
pConnect->UnregisterStatusEventCallback(uiCookie);
if (!bSilent) if (!bSilent)
std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl; std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl;
return REPOSITORY_SERVICE_ACCESS_ERROR; return REPOSITORY_SERVICE_ACCESS_ERROR;
@@ -337,6 +342,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
} }
// Shutdwown // Shutdwown
pConnect->UnregisterStatusEventCallback(uiCookie);
parser.Clear(); parser.Clear();
pLinkCoreRepo->UnlinkCoreRepository(); pLinkCoreRepo->UnlinkCoreRepository();
appcontrol.Shutdown(); appcontrol.Shutdown();

View File

@@ -12,10 +12,13 @@ add_executable(sdv_packager
"../../sdv_services/core/toml_parser/parser_toml.cpp" "../../sdv_services/core/toml_parser/parser_toml.cpp"
"../../sdv_services/core/toml_parser/lexer_toml.h" "../../sdv_services/core/toml_parser/lexer_toml.h"
"../../sdv_services/core/toml_parser/lexer_toml.cpp" "../../sdv_services/core/toml_parser/lexer_toml.cpp"
"../../sdv_services/core/toml_parser/lexer_toml_token.cpp"
"../../sdv_services/core/toml_parser/parser_node_toml.h" "../../sdv_services/core/toml_parser/parser_node_toml.h"
"../../sdv_services/core/toml_parser/parser_node_toml.cpp" "../../sdv_services/core/toml_parser/parser_node_toml.cpp"
"../../sdv_services/core/toml_parser/character_reader_utf_8.h" "../../sdv_services/core/toml_parser/character_reader_utf_8.h"
"../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" "../../sdv_services/core/toml_parser/character_reader_utf_8.cpp"
"../../sdv_services/core/toml_parser/miscellaneous.h"
"../../sdv_services/core/toml_parser/miscellaneous.cpp"
"../../sdv_services/core/toml_parser/exception.h" "../../sdv_services/core/toml_parser/exception.h"
"environment.cpp" "environment.cpp"
"environment.h" "environment.h"

View File

@@ -929,7 +929,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
} }
else else
{ {
// Check whether preceeded by at least one command // Check whether preceded by at least one command
if (!(m_uiShowFlags & 0x00ff)) if (!(m_uiShowFlags & 0x00ff))
{ {
m_nError = CMDLN_MISSING_SHOW_COMMAND; m_nError = CMDLN_MISSING_SHOW_COMMAND;

View File

@@ -33,6 +33,9 @@ inline bool iequals(const std::string& rssLeft, const std::string& rssRight)
return std::equal(rssLeft.begin(), rssLeft.end(), rssRight.begin(), rssRight.end(), ichar_equals); return std::equal(rssLeft.begin(), rssLeft.end(), rssRight.begin(), rssRight.end(), ichar_equals);
} }
/**
* @brief Environment access class.
*/
class CSdvPackagerEnvironment class CSdvPackagerEnvironment
{ {
public: public:

View File

@@ -1,3 +1,4 @@
#include <atomic>
#include <support/sdv_core.h> #include <support/sdv_core.h>
#include <support/app_control.h> #include <support/app_control.h>
#include "../../global/cmdlnparser/cmdlnparser.cpp" #include "../../global/cmdlnparser/cmdlnparser.cpp"
@@ -73,7 +74,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
// See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev // See: https://stackoverflow.com/questions/51209268/using-stdthread-in-a-library-loaded-with-dlopen-leads-to-a-sigsev
// NOTE EVE 27.05.2025: in release builds, starting and ending the thread right after each other causes incorrect behavior and // NOTE EVE 27.05.2025: in release builds, starting and ending the thread right after each other causes incorrect behavior and
// leads in some cases to create a deadlock in the join-function. The solution is to add delays in the thread processing. // leads in some cases to create a deadlock in the join-function. The solution is to add delays in the thread processing.
bool bThreadStarted = false; std::atomic_bool bThreadStarted = false;
std::thread thread = std::thread([&]() std::thread thread = std::thread([&]()
{ {
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));

View File

@@ -42,7 +42,7 @@ add_library(core_services SHARED
"installation_composer.h" "installation_composer.h"
"installation_composer.cpp" "installation_composer.cpp"
) "toml_parser/lexer_toml_token.h" "toml_parser/lexer_toml_token.cpp" "toml_parser/miscellaneous.h" "toml_parser/miscellaneous.cpp")
# Link target # Link target
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")

View File

@@ -61,10 +61,10 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
// Reset the current baseline // Reset the current baseline
ResetConfigBaseline(); ResetConfigBaseline();
CParserTOML parser(ssContent); toml_parser::CParser parser(ssContent);
// Check for config file compatibility // Check for config file compatibility
auto ptrConfigVersion = parser.GetRoot().GetDirect("Configuration.Version"); auto ptrConfigVersion = parser.Root().Direct("Configuration.Version");
if (!ptrConfigVersion) if (!ptrConfigVersion)
{ {
SDV_LOG_ERROR("Configuration version must be present in the configuration file."); SDV_LOG_ERROR("Configuration version must be present in the configuration file.");
@@ -84,17 +84,17 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
!GetAppControl().IsMaintenanceApplication()) !GetAppControl().IsMaintenanceApplication())
{ {
// Load all modules in the component section // Load all modules in the component section
auto ptrComponents = parser.GetRoot().GetDirect("Component"); auto ptrComponents = parser.Root().Direct("Component");
if (ptrComponents && ptrComponents->GetArray()) if (ptrComponents && ptrComponents->Cast<toml_parser::CArray>())
{ {
for (uint32_t uiIndex = 0; uiIndex < ptrComponents->GetArray()->GetCount(); uiIndex++) for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{ {
auto ptrComponent = ptrComponents->GetArray()->Get(uiIndex); auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
if (ptrComponent) if (ptrComponent)
{ {
// Get the information from the component. // Get the information from the component.
std::filesystem::path pathModule; std::filesystem::path pathModule;
auto ptrModule = ptrComponent->GetDirect("Path"); auto ptrModule = ptrComponent->Direct("Path");
if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue()); if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());
// If there is no path, this is allowed... skip this module // If there is no path, this is allowed... skip this module
@@ -118,17 +118,17 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
} }
// Load all modules from the module section. // Load all modules from the module section.
auto ptrModules = parser.GetRoot().GetDirect("Module"); auto ptrModules = parser.Root().Direct("Module");
if (ptrModules && ptrModules->GetArray()) if (ptrModules && ptrModules->Cast<toml_parser::CArray>())
{ {
for (uint32_t uiIndex = 0; uiIndex < ptrModules->GetArray()->GetCount(); uiIndex++) for (uint32_t uiIndex = 0; uiIndex < ptrModules->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{ {
auto ptrModule = ptrModules->GetArray()->Get(uiIndex); auto ptrModule = ptrModules->Cast<toml_parser::CArray>()->Get(uiIndex);
if (ptrModule) if (ptrModule)
{ {
// Get the information from the component. // Get the information from the component.
std::filesystem::path pathModule; std::filesystem::path pathModule;
auto ptrModulePath = ptrModule->GetDirect("Path"); auto ptrModulePath = ptrModule->Direct("Path");
if (ptrModulePath) pathModule = static_cast<std::string>(ptrModulePath->GetValue()); if (ptrModulePath) pathModule = static_cast<std::string>(ptrModulePath->GetValue());
if (pathModule.empty()) if (pathModule.empty())
@@ -158,23 +158,23 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
// Start all components from the component section // Start all components from the component section
std::filesystem::path pathLastModule; std::filesystem::path pathLastModule;
auto ptrComponents = parser.GetRoot().GetDirect("Component"); auto ptrComponents = parser.Root().Direct("Component");
if (ptrComponents && ptrComponents->GetArray()) if (ptrComponents && ptrComponents->Cast<toml_parser::CArray>())
{ {
for (uint32_t uiIndex = 0; uiIndex < ptrComponents->GetArray()->GetCount(); uiIndex++) for (uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{ {
auto ptrComponent = ptrComponents->GetArray()->Get(uiIndex); auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
if (ptrComponent) if (ptrComponent)
{ {
// Get the information from the component. // Get the information from the component.
std::filesystem::path pathModule; std::filesystem::path pathModule;
auto ptrModule = ptrComponent->GetDirect("Path"); auto ptrModule = ptrComponent->Direct("Path");
if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue()); if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());
std::string ssClass; std::string ssClass;
auto ptrClass = ptrComponent->GetDirect("Class"); auto ptrClass = ptrComponent->Direct("Class");
if (ptrClass) ssClass = static_cast<std::string>(ptrClass->GetValue()); if (ptrClass) ssClass = static_cast<std::string>(ptrClass->GetValue());
std::string ssName; std::string ssName;
auto ptrName = ptrComponent->GetDirect("Name"); auto ptrName = ptrComponent->Direct("Name");
if (ptrName) ssName = static_cast<std::string>(ptrName->GetValue()); if (ptrName) ssName = static_cast<std::string>(ptrName->GetValue());
// If there is a path, store it. If there is none, take the last stored // If there is a path, store it. If there is none, take the last stored
@@ -204,10 +204,10 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
{ {
auto itModule = mapModules.find(pathModule); auto itModule = mapModules.find(pathModule);
if (itModule == mapModules.end()) continue; // Module was not loaded before... if (itModule == mapModules.end()) continue; // Module was not loaded before...
tObjectID = GetRepository().CreateObjectFromModule(itModule->second, ssClass, ssName, ptrComponent->CreateTOMLText()); tObjectID = GetRepository().CreateObjectFromModule(itModule->second, ssClass, ssName, ptrComponent->GenerateTOML());
} }
else else
tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->CreateTOMLText()); tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->GenerateTOML());
if (!tObjectID) if (!tObjectID)
{ {

View File

@@ -723,7 +723,7 @@ void CAppControl::BroadcastOperationState(sdv::app::EAppOperationState eState)
bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig) bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
{ {
CParserTOML parserConfig; toml_parser::CParser parserConfig;
std::string ssError; std::string ssError;
try try
{ {
@@ -736,7 +736,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
} }
// Get the reporting settings (if this succeeded at all...) // Get the reporting settings (if this succeeded at all...)
auto ptrReport = parserConfig.GetRoot().GetDirect("Console.Report"); auto ptrReport = parserConfig.Root().Direct("Console.Report");
if (ptrReport && ptrReport->GetValue() == "Silent") m_bSilent = true; if (ptrReport && ptrReport->GetValue() == "Silent") m_bSilent = true;
if (ptrReport && ptrReport->GetValue() == "Verbose") m_bVerbose = true; if (ptrReport && ptrReport->GetValue() == "Verbose") m_bVerbose = true;
@@ -751,8 +751,8 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
// Allow a custom logger to be defined // Allow a custom logger to be defined
std::filesystem::path pathLoggerModule; std::filesystem::path pathLoggerModule;
std::string ssLoggerClass; std::string ssLoggerClass;
std::shared_ptr<CNode> ptrLogHandlerPath = parserConfig.GetRoot().GetDirect("LogHandler.Path"); std::shared_ptr<toml_parser::CNode> ptrLogHandlerPath = parserConfig.Root().Direct("LogHandler.Path");
std::shared_ptr<CNode> ptrLogHandlerClass = parserConfig.GetRoot().GetDirect("LogHandler.Class"); std::shared_ptr<toml_parser::CNode> ptrLogHandlerClass = parserConfig.Root().Direct("LogHandler.Class");
if (ptrLogHandlerPath && !ptrLogHandlerClass) if (ptrLogHandlerPath && !ptrLogHandlerClass)
{ {
if (!m_bSilent) if (!m_bSilent)
@@ -778,12 +778,12 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
} }
// Get an optional program tag for the logger // Get an optional program tag for the logger
std::shared_ptr<CNode> ptrLogPogramTag = parserConfig.GetRoot().GetDirect("LogHandler.Tag"); std::shared_ptr<toml_parser::CNode> ptrLogPogramTag = parserConfig.Root().Direct("LogHandler.Tag");
if (ptrLogPogramTag) m_ssProgramTag = static_cast<std::string>(ptrLogPogramTag->GetValue()); if (ptrLogPogramTag) m_ssProgramTag = static_cast<std::string>(ptrLogPogramTag->GetValue());
// Get the application-mode // Get the application-mode
std::string ssApplication = "Standalone"; std::string ssApplication = "Standalone";
std::shared_ptr<CNode> ptrApplication = parserConfig.GetRoot().GetDirect("Application.Mode"); std::shared_ptr<toml_parser::CNode> ptrApplication = parserConfig.Root().Direct("Application.Mode");
if (ptrApplication) ssApplication = static_cast<std::string>(ptrApplication->GetValue()); if (ptrApplication) ssApplication = static_cast<std::string>(ptrApplication->GetValue());
if (ssApplication == "Standalone") m_eContextMode = sdv::app::EAppContext::standalone; if (ssApplication == "Standalone") m_eContextMode = sdv::app::EAppContext::standalone;
else if (ssApplication == "External") m_eContextMode = sdv::app::EAppContext::external; else if (ssApplication == "External") m_eContextMode = sdv::app::EAppContext::external;
@@ -820,21 +820,21 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
sdv::core::ELogSeverity eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::error; sdv::core::ELogSeverity eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::error;
if (IsMainApplication() || IsIsolatedApplication()) if (IsMainApplication() || IsIsolatedApplication())
eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::info; eLogDefaultViewSeverityFilter = sdv::core::ELogSeverity::info;
std::shared_ptr<CNode> ptrLogSeverityFilter = parserConfig.GetRoot().GetDirect("LogHandler.Filter"); std::shared_ptr<toml_parser::CNode> ptrLogSeverityFilter = parserConfig.Root().Direct("LogHandler.Filter");
m_eSeverityFilter = fnTranslateSevFilter(ptrLogSeverityFilter ? ptrLogSeverityFilter->GetValue() : "", m_eSeverityFilter = fnTranslateSevFilter(ptrLogSeverityFilter ? ptrLogSeverityFilter->GetValue() : "",
sdv::core::ELogSeverity::info); sdv::core::ELogSeverity::info);
ptrLogSeverityFilter = parserConfig.GetRoot().GetDirect("LogHandler.ViewFilter"); ptrLogSeverityFilter = parserConfig.Root().Direct("LogHandler.ViewFilter");
m_eSeverityViewFilter = fnTranslateSevFilter(ptrLogSeverityFilter ? ptrLogSeverityFilter->GetValue() : "", m_eSeverityViewFilter = fnTranslateSevFilter(ptrLogSeverityFilter ? ptrLogSeverityFilter->GetValue() : "",
eLogDefaultViewSeverityFilter); eLogDefaultViewSeverityFilter);
// Get the optional instance ID. // Get the optional instance ID.
std::shared_ptr<CNode> ptrInstance = parserConfig.GetRoot().GetDirect("Application.Instance"); std::shared_ptr<toml_parser::CNode> ptrInstance = parserConfig.Root().Direct("Application.Instance");
if (ptrInstance) if (ptrInstance)
m_uiInstanceID = ptrInstance->GetValue(); m_uiInstanceID = ptrInstance->GetValue();
else else
m_uiInstanceID = 1000u; m_uiInstanceID = 1000u;
// Number of attempts to establish a connection to a running instance. // Number of attempts to establish a connection to a running instance.
std::shared_ptr<CNode> ptrRetries = parserConfig.GetRoot().GetDirect("Application.Retries"); std::shared_ptr<toml_parser::CNode> ptrRetries = parserConfig.Root().Direct("Application.Retries");
if (ptrRetries) if (ptrRetries)
{ {
m_uiRetries = ptrRetries->GetValue(); m_uiRetries = ptrRetries->GetValue();
@@ -848,7 +848,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
if (IsMainApplication() || IsIsolatedApplication()) if (IsMainApplication() || IsIsolatedApplication())
{ {
// Get the optional installation directory. // Get the optional installation directory.
std::shared_ptr<CNode> ptrInstallDir = parserConfig.GetRoot().GetDirect("Application.InstallDir"); std::shared_ptr<toml_parser::CNode> ptrInstallDir = parserConfig.Root().Direct("Application.InstallDir");
if (ptrInstallDir) if (ptrInstallDir)
{ {
m_pathRootDir = ptrInstallDir->GetValue(); m_pathRootDir = ptrInstallDir->GetValue();
@@ -877,7 +877,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
// not auto-updateable. // not auto-updateable.
if (!IsMaintenanceApplication() && !IsIsolatedApplication()) if (!IsMaintenanceApplication() && !IsIsolatedApplication())
{ {
auto ptrConfigFile = parserConfig.GetRoot().GetDirect("Application.Config"); auto ptrConfigFile = parserConfig.Root().Direct("Application.Config");
if (ptrConfigFile) if (ptrConfigFile)
m_pathAppConfig = ptrConfigFile->GetValue(); m_pathAppConfig = ptrConfigFile->GetValue();
} }
@@ -905,10 +905,10 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
try try
{ {
// Read the configuration // Read the configuration
CParserTOML parserSettings(ssContent); toml_parser::CParser parserSettings(ssContent);
// Check for the version // Check for the version
auto ptrVersion = parserSettings.GetRoot().GetDirect("Settings.Version"); auto ptrVersion = parserSettings.Root().Direct("Settings.Version");
if (!ptrVersion) if (!ptrVersion)
{ {
if (!m_bSilent) if (!m_bSilent)
@@ -925,12 +925,12 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
} }
// Read the system configurations // Read the system configurations
auto ptrSystemConfigs = parserSettings.GetRoot().GetDirect("Settings.SystemConfig"); auto ptrSystemConfigs = parserSettings.Root().Direct("Settings.SystemConfig");
if (ptrSystemConfigs && ptrSystemConfigs->GetArray()) if (ptrSystemConfigs && ptrSystemConfigs->Cast<toml_parser::CArray>())
{ {
for (uint32_t uiIndex = 0; uiIndex < ptrSystemConfigs->GetArray()->GetCount(); uiIndex++) for (uint32_t uiIndex = 0; uiIndex < ptrSystemConfigs->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{ {
auto ptrSystemConfig = ptrSystemConfigs->GetArray()->Get(uiIndex); auto ptrSystemConfig = ptrSystemConfigs->Cast<toml_parser::CArray>()->Get(uiIndex);
if (!ptrSystemConfig) continue; if (!ptrSystemConfig) continue;
m_vecSysConfigs.push_back(static_cast<std::string>(ptrSystemConfig->GetValue())); m_vecSysConfigs.push_back(static_cast<std::string>(ptrSystemConfig->GetValue()));
} }
@@ -939,7 +939,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
// Get the application config - but only when not specified over the app-control-config. // Get the application config - but only when not specified over the app-control-config.
if (m_pathAppConfig.empty()) if (m_pathAppConfig.empty())
{ {
auto ptrAppConfig = parserSettings.GetRoot().GetDirect("Settings.AppConfig"); auto ptrAppConfig = parserSettings.Root().Direct("Settings.AppConfig");
if (ptrAppConfig) if (ptrAppConfig)
{ {
// Path available. Enable auto-update. // Path available. Enable auto-update.

View File

@@ -164,22 +164,22 @@ bool CInstallManifest::Read(const std::string& rssManifest, bool bBlockSystemObj
m_bBlockSystemObjects = bBlockSystemObjects; m_bBlockSystemObjects = bBlockSystemObjects;
// Parse the manifest // Parse the manifest
CParserTOML parser(rssManifest); toml_parser::CParser parser(rssManifest);
// Get the installation version - must be identical to the interface version // Get the installation version - must be identical to the interface version
auto ptrInstallVersionNode = parser.GetRoot().GetDirect("Installation.Version"); auto ptrInstallVersionNode = parser.Root().Direct("Installation.Version");
if (!ptrInstallVersionNode || ptrInstallVersionNode->GetValue() != SDVFrameworkInterfaceVersion) return false; if (!ptrInstallVersionNode || ptrInstallVersionNode->GetValue() != SDVFrameworkInterfaceVersion) return false;
// Get the installation name // Get the installation name
auto ptrInstallNameNode = parser.GetRoot().GetDirect("Installation.Name"); auto ptrInstallNameNode = parser.Root().Direct("Installation.Name");
if (!ptrInstallNameNode) return false; if (!ptrInstallNameNode) return false;
m_ssInstallName = static_cast<std::string>(ptrInstallNameNode->GetValue()); m_ssInstallName = static_cast<std::string>(ptrInstallNameNode->GetValue());
if (m_ssInstallName.empty()) return false; if (m_ssInstallName.empty()) return false;
// Get installation properties. The properties are optional // Get installation properties. The properties are optional
auto ptrProperties = parser.GetRoot().GetDirect("Properties"); auto ptrProperties = parser.Root().Direct("Properties");
std::shared_ptr<CTable> ptrPropertyTable; std::shared_ptr<toml_parser::CTable> ptrPropertyTable;
if (ptrProperties) ptrPropertyTable = ptrProperties->GetTable(); if (ptrProperties) ptrPropertyTable = ptrProperties->Cast<toml_parser::CTable>();
if (ptrPropertyTable) if (ptrPropertyTable)
{ {
for (uint32_t uiIndex = 0; uiIndex < ptrPropertyTable->GetCount(); uiIndex++) for (uint32_t uiIndex = 0; uiIndex < ptrPropertyTable->GetCount(); uiIndex++)
@@ -191,9 +191,9 @@ bool CInstallManifest::Read(const std::string& rssManifest, bool bBlockSystemObj
} }
// Build the module list // Build the module list
auto ptrModulesNode = parser.GetRoot().GetDirect("Module"); auto ptrModulesNode = parser.Root().Direct("Module");
if (!ptrModulesNode) return true; // No modules in the manifest if (!ptrModulesNode) return true; // No modules in the manifest
auto ptrModuleArrayNode = ptrModulesNode->GetArray(); auto ptrModuleArrayNode = ptrModulesNode->Cast<toml_parser::CArray>();
if (!ptrModuleArrayNode) return false; // Must be array if (!ptrModuleArrayNode) return false; // Must be array
for (uint32_t uiModuleIndex = 0; uiModuleIndex < ptrModuleArrayNode->GetCount(); uiModuleIndex++) for (uint32_t uiModuleIndex = 0; uiModuleIndex < ptrModuleArrayNode->GetCount(); uiModuleIndex++)
{ {
@@ -202,19 +202,19 @@ bool CInstallManifest::Read(const std::string& rssManifest, bool bBlockSystemObj
if (!ptrModule) continue; if (!ptrModule) continue;
// Get the module path // Get the module path
auto ptrModulePath = ptrModule->GetDirect("Path"); auto ptrModulePath = ptrModule->Direct("Path");
if (!ptrModulePath) continue; if (!ptrModulePath) continue;
std::filesystem::path pathModule = static_cast<std::string>(ptrModulePath->GetValue()); std::filesystem::path pathModule = static_cast<std::string>(ptrModulePath->GetValue());
std::string ssModuleManifest; std::string ssModuleManifest;
// Get the component list (if available) // Get the component list (if available)
auto ptrModuleComponents = ptrModule->GetDirect("Component"); auto ptrModuleComponents = ptrModule->Direct("Component");
if (ptrModuleComponents) if (ptrModuleComponents)
{ {
// The module manifest contains the TOML text of the component array // The module manifest contains the TOML text of the component array
auto ptrModuleComponentArray = ptrModuleComponents->GetArray(); auto ptrModuleComponentArray = ptrModuleComponents->Cast<toml_parser::CArray>();
if (ptrModuleComponentArray) if (ptrModuleComponentArray)
ssModuleManifest = ptrModuleComponents->CreateTOMLText(); ssModuleManifest = ptrModuleComponents->GenerateTOML();
} }
// Add the module // Add the module
@@ -256,10 +256,10 @@ std::string CInstallManifest::Write() const
sstream << "[[Module]]" << std::endl << "Path=\"" << rsEntry.pathRelModule.generic_u8string() << "\"" << std::endl; sstream << "[[Module]]" << std::endl << "Path=\"" << rsEntry.pathRelModule.generic_u8string() << "\"" << std::endl;
// Read the module manifest // Read the module manifest
CParserTOML parser(rsEntry.ssManifest); toml_parser::CParser parser(rsEntry.ssManifest);
// Add the module manifest as part of the installation manifest. // Add the module manifest as part of the installation manifest.
sstream << parser.CreateTOMLText("Module") << std::endl; sstream << parser.GenerateTOML("Module") << std::endl;
} }
return sstream.str(); return sstream.str();
@@ -286,14 +286,14 @@ bool CInstallManifest::AddModule(const std::filesystem::path& rpathModulePath,
if (!ssManifest.empty()) if (!ssManifest.empty())
{ {
// Check the interface version for compatibility // Check the interface version for compatibility
CParserTOML parser(ssManifest); toml_parser::CParser parser(ssManifest);
auto ptrInterfaceNode = parser.GetRoot().GetDirect("Interface.Version"); auto ptrInterfaceNode = parser.Root().Direct("Interface.Version");
if (!ptrInterfaceNode) return false; if (!ptrInterfaceNode) return false;
if (ptrInterfaceNode->GetValue() != SDVFrameworkInterfaceVersion) return false; if (ptrInterfaceNode->GetValue() != SDVFrameworkInterfaceVersion) return false;
auto ptrComponentsNode = parser.GetRoot().GetDirect("Component"); auto ptrComponentsNode = parser.Root().Direct("Component");
if (!ptrComponentsNode) return true; // No component available in the manifest if (!ptrComponentsNode) return true; // No component available in the manifest
if (!ptrComponentsNode->GetArray()) return false; if (!ptrComponentsNode->Cast<toml_parser::CArray>()) return false;
ssComponentsManifest = ptrComponentsNode->CreateTOMLText(); ssComponentsManifest = ptrComponentsNode->GenerateTOML();
} }
// Store path and component // Store path and component
@@ -398,7 +398,7 @@ bool CInstallManifest::NeedQuotedName(const std::string& rssName)
{ {
for (char c : rssName) for (char c : rssName)
{ {
if (!std::isalnum(c) && c != '_' && c != '-') if (static_cast<uint8_t>(c) > 127u || (!std::isalnum(c) && c != '_' && c != '-'))
return true; return true;
} }
return false; return false;
@@ -408,10 +408,10 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
bool bBlockSystemObjects) : pathRelModule(rpathRelModule), ssManifest(rssManifest) bool bBlockSystemObjects) : pathRelModule(rpathRelModule), ssManifest(rssManifest)
{ {
// Parse the manifest and extract information from them... // Parse the manifest and extract information from them...
CParserTOML parser(rssManifest); toml_parser::CParser parser(rssManifest);
auto ptrComponents = parser.GetRoot().GetDirect("Component"); auto ptrComponents = parser.Root().Direct("Component");
if (!ptrComponents) return; // No objects... if (!ptrComponents) return; // No objects...
auto ptrComponentArray = ptrComponents->GetArray(); auto ptrComponentArray = ptrComponents->Cast<toml_parser::CArray>();
if (!ptrComponentArray) return; // No objects... if (!ptrComponentArray) return; // No objects...
for (uint32_t uiIndex = 0; uiIndex < ptrComponentArray->GetCount(); uiIndex++) for (uint32_t uiIndex = 0; uiIndex < ptrComponentArray->GetCount(); uiIndex++)
{ {
@@ -422,14 +422,14 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
SComponent sComponent{}; SComponent sComponent{};
//sComponent.pathModule = rpathModule; //sComponent.pathModule = rpathModule;
sComponent.pathRelModule = rpathRelModule; sComponent.pathRelModule = rpathRelModule;
sComponent.ssManifest = ptrComponent->CreateTOMLText("Component"); sComponent.ssManifest = ptrComponent->GenerateTOML(toml_parser::CGenContext("Component"));
auto ptrClassName = ptrComponent->GetDirect("Class"); auto ptrClassName = ptrComponent->Direct("Class");
if (!ptrClassName) continue; if (!ptrClassName) continue;
sComponent.ssClassName = static_cast<std::string>(ptrClassName->GetValue()); sComponent.ssClassName = static_cast<std::string>(ptrClassName->GetValue());
auto ptrAliases = ptrComponent->GetDirect("Aliases"); auto ptrAliases = ptrComponent->Direct("Aliases");
if (ptrAliases) if (ptrAliases)
{ {
auto ptrAliasesArray = ptrAliases->GetArray(); auto ptrAliasesArray = ptrAliases->Cast<toml_parser::CArray>();
for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++) for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++)
{ {
auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex); auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex);
@@ -437,10 +437,10 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
sComponent.seqAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue())); sComponent.seqAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue()));
} }
} }
auto ptrDefaultName = ptrComponent->GetDirect("DefaultName"); auto ptrDefaultName = ptrComponent->Direct("DefaultName");
if (ptrDefaultName) sComponent.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue()); if (ptrDefaultName) sComponent.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue());
else sComponent.ssDefaultObjectName = sComponent.ssClassName; else sComponent.ssDefaultObjectName = sComponent.ssClassName;
auto ptrType = ptrComponent->GetDirect("Type"); auto ptrType = ptrComponent->Direct("Type");
if (!ptrType) continue; if (!ptrType) continue;
std::string ssType = static_cast<std::string>(ptrType->GetValue()); std::string ssType = static_cast<std::string>(ptrType->GetValue());
if (ssType == "System") sComponent.eType = sdv::EObjectType::SystemObject; if (ssType == "System") sComponent.eType = sdv::EObjectType::SystemObject;
@@ -453,13 +453,13 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
else if (ssType == "Utility") sComponent.eType = sdv::EObjectType::Utility; else if (ssType == "Utility") sComponent.eType = sdv::EObjectType::Utility;
else continue; else continue;
if (bBlockSystemObjects && sComponent.eType == sdv::EObjectType::SystemObject) continue; if (bBlockSystemObjects && sComponent.eType == sdv::EObjectType::SystemObject) continue;
auto ptrSingleton = ptrComponent->GetDirect("Singleton"); auto ptrSingleton = ptrComponent->Direct("Singleton");
if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue())) if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue()))
sComponent.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton); sComponent.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
auto ptrDependencies = ptrComponent->GetDirect("Dependencies"); auto ptrDependencies = ptrComponent->Direct("Dependencies");
if (ptrDependencies) if (ptrDependencies)
{ {
auto ptrDependencyArray = ptrDependencies->GetArray(); auto ptrDependencyArray = ptrDependencies->Cast<toml_parser::CArray>();
for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount(); for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount();
uiDependencyIndex++) uiDependencyIndex++)
{ {

View File

@@ -299,7 +299,7 @@ private:
/** /**
* @brief Interpret a version number as string. * @brief Interpret a version number as string.
* @details A version string is composed of: major.minor.patch (numbers only; characters and whitespace are ignored). * @details A version string is composed of: major.minor.patch (numbers only; characters and whitespace are ignored).
* @param rssVersion Reference to the version string. * @param[in] rssVersion Reference to the version string.
* @return The interpreted version. * @return The interpreted version.
*/ */
inline sdv::installation::SPackageVersion InterpretVersionString(const std::string& rssVersion) inline sdv::installation::SPackageVersion InterpretVersionString(const std::string& rssVersion)

View File

@@ -206,10 +206,10 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
try try
{ {
CParserTOML parser(ssManifest); toml_parser::CParser parser(ssManifest);
// Check for the interface version - currently must be equal. // Check for the interface version - currently must be equal.
auto ptrVersion = parser.GetRoot().GetDirect("Interface.Version"); auto ptrVersion = parser.Root().Direct("Interface.Version");
if (ptrVersion) m_uiIfcVersion = ptrVersion->GetValue(); if (ptrVersion) m_uiIfcVersion = ptrVersion->GetValue();
if (!ptrVersion || m_uiIfcVersion != SDVFrameworkInterfaceVersion) if (!ptrVersion || m_uiIfcVersion != SDVFrameworkInterfaceVersion)
{ {
@@ -230,26 +230,26 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
} }
// Get available classes // Get available classes
auto ptrComponents = parser.GetRoot().GetDirect("Component"); auto ptrComponents = parser.Root().Direct("Component");
if (!ptrComponents || !ptrComponents->GetArray()) if (!ptrComponents || !ptrComponents->Cast<toml_parser::CArray>())
{ {
SDV_LOG_ERROR("Error opening SDV module: ", rpathModule.generic_u8string(), " error: no components available"); SDV_LOG_ERROR("Error opening SDV module: ", rpathModule.generic_u8string(), " error: no components available");
Unload(true); Unload(true);
return false; return false;
} }
for (std::uint32_t uiIndex = 0; uiIndex < ptrComponents->GetArray()->GetCount(); uiIndex++) for (std::uint32_t uiIndex = 0; uiIndex < ptrComponents->Cast<toml_parser::CArray>()->GetCount(); uiIndex++)
{ {
// Fill in the class info. // Fill in the class info.
sdv::SClassInfo sInfo{}; sdv::SClassInfo sInfo{};
auto ptrComponent = ptrComponents->GetArray()->Get(uiIndex); auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
if (!ptrComponent) continue; if (!ptrComponent) continue;
auto ptrClassName = ptrComponent->GetDirect("Class"); auto ptrClassName = ptrComponent->Direct("Class");
if (!ptrClassName) continue; if (!ptrClassName) continue;
sInfo.ssClassName = static_cast<std::string>(ptrClassName->GetValue()); sInfo.ssClassName = static_cast<std::string>(ptrClassName->GetValue());
auto ptrAliases = ptrComponent->GetDirect("Aliases"); auto ptrAliases = ptrComponent->Direct("Aliases");
if (ptrAliases) if (ptrAliases)
{ {
auto ptrAliasesArray = ptrAliases->GetArray(); auto ptrAliasesArray = ptrAliases->Cast<toml_parser::CArray>();
for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++) for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); uiAliasIndex++)
{ {
auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex); auto ptrClassAlias = ptrAliasesArray->Get(uiAliasIndex);
@@ -257,10 +257,10 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
sInfo.seqClassAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue())); sInfo.seqClassAliases.push_back(static_cast<sdv::u8string>(ptrClassAlias->GetValue()));
} }
} }
auto ptrDefaultName = ptrComponent->GetDirect("DefaultName"); auto ptrDefaultName = ptrComponent->Direct("DefaultName");
if (ptrDefaultName) sInfo.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue()); if (ptrDefaultName) sInfo.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue());
else sInfo.ssDefaultObjectName = sInfo.ssClassName; else sInfo.ssDefaultObjectName = sInfo.ssClassName;
auto ptrType = ptrComponent->GetDirect("Type"); auto ptrType = ptrComponent->Direct("Type");
if (!ptrType) continue; if (!ptrType) continue;
std::string ssType = static_cast<std::string>(ptrType->GetValue()); std::string ssType = static_cast<std::string>(ptrType->GetValue());
if (ssType == "System") sInfo.eType = sdv::EObjectType::SystemObject; if (ssType == "System") sInfo.eType = sdv::EObjectType::SystemObject;
@@ -272,13 +272,13 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
else if (ssType == "Stub") sInfo.eType = sdv::EObjectType::Stub; else if (ssType == "Stub") sInfo.eType = sdv::EObjectType::Stub;
else if (ssType == "Utility") sInfo.eType = sdv::EObjectType::Utility; else if (ssType == "Utility") sInfo.eType = sdv::EObjectType::Utility;
else continue; else continue;
auto ptrSingleton = ptrComponent->GetDirect("Singleton"); auto ptrSingleton = ptrComponent->Direct("Singleton");
if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue())) if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue()))
sInfo.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton); sInfo.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
auto ptrDependencies = ptrComponent->GetDirect("Dependencies"); auto ptrDependencies = ptrComponent->Direct("Dependencies");
if (ptrDependencies) if (ptrDependencies)
{ {
auto ptrDependencyArray = ptrDependencies->GetArray(); auto ptrDependencyArray = ptrDependencies->Cast<toml_parser::CArray>();
for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount(); for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount();
uiDependencyIndex++) uiDependencyIndex++)
{ {

View File

@@ -312,11 +312,11 @@ sdv::core::TModuleID CModuleControl::ContextLoad(const std::filesystem::path& rp
{ {
size_t nIndex = 0; size_t nIndex = 0;
size_t nUnsupportedObjectCount = 0; size_t nUnsupportedObjectCount = 0;
CParserTOML parser(rssManifest); toml_parser::CParser parser(rssManifest);
do do
{ {
std::shared_ptr<CNode> ptrComponentType = std::shared_ptr<toml_parser::CNode> ptrComponentType =
parser.GetRoot().GetDirect(std::string("Component[") + std::to_string(nIndex++) + "].Type"); parser.Root().Direct(std::string("Component[") + std::to_string(nIndex++) + "].Type");
if (!ptrComponentType) break; if (!ptrComponentType) break;
std::string ssType = static_cast<std::string>(ptrComponentType->GetValue()); std::string ssType = static_cast<std::string>(ptrComponentType->GetValue());
if (ssType == "Device") continue; // Okay if (ssType == "Device") continue; // Okay

View File

@@ -983,11 +983,11 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
sstreamConfig << "Object = \"" << rssObjectName << "\"" << std::endl << std::endl; sstreamConfig << "Object = \"" << rssObjectName << "\"" << std::endl << std::endl;
if (!rssObjectConfig.empty()) if (!rssObjectConfig.empty())
{ {
CParserTOML parserConfig(rssObjectConfig); toml_parser::CParser parserConfig(rssObjectConfig);
sstreamConfig << parserConfig.CreateTOMLText("Isolation.Config"); sstreamConfig << parserConfig.GenerateTOML("Isolation.Config");
} }
CParserTOML parserConnection(ssConnectionString); toml_parser::CParser parserConnection(ssConnectionString);
sstreamConfig << parserConnection.CreateTOMLText("Isolation.Connection"); sstreamConfig << parserConnection.GenerateTOML("Isolation.Connection");
// Create command line arguments // Create command line arguments
sdv::sequence<sdv::u8string> seqArguments; sdv::sequence<sdv::u8string> seqArguments;
@@ -1177,8 +1177,9 @@ sdv::core::TObjectID CRepository::CreateObjectID()
static sdv::core::TObjectID tCurrent = 0; static sdv::core::TObjectID tCurrent = 0;
if (!tCurrent) if (!tCurrent)
{ {
srand(static_cast<unsigned int>(time(0))); std::srand(static_cast<unsigned int>(time(0)));
tCurrent = rand(); tCurrent = 0;
while (!tCurrent) tCurrent = std::rand();
} }
return ++tCurrent; return ++tCurrent;
} }

View File

@@ -22,7 +22,7 @@ public:
CSDVCore(); CSDVCore();
/** /**
* \brief Destructor * @brief Destructor
*/ */
~CSDVCore(); ~CSDVCore();

View File

@@ -1,11 +1,10 @@
#include "character_reader_utf_8.h" #include "character_reader_utf_8.h"
#include "exception.h" #include "exception.h"
CCharacterReaderUTF8::CCharacterReaderUTF8() : m_nDataLength{0}, m_nCursor{0} /// The TOML parser namespace
{} namespace toml_parser
{
CCharacterReaderUTF8::CCharacterReaderUTF8(const std::string& rssString) : CCharacterReaderUTF8::CCharacterReaderUTF8(const std::string& rssString) : m_ssString{rssString}
m_ssString{rssString}, m_nDataLength{rssString.size()}, m_nCursor{0}
{ {
CheckForInvalidUTF8Bytes(); CheckForInvalidUTF8Bytes();
CheckForInvalidUTF8Sequences(); CheckForInvalidUTF8Sequences();
@@ -14,7 +13,6 @@ CCharacterReaderUTF8::CCharacterReaderUTF8(const std::string& rssString) :
void CCharacterReaderUTF8::Feed(const std::string& rssString) void CCharacterReaderUTF8::Feed(const std::string& rssString)
{ {
m_ssString = rssString; m_ssString = rssString;
m_nDataLength = rssString.size();
m_nCursor = 0; m_nCursor = 0;
CheckForInvalidUTF8Bytes(); CheckForInvalidUTF8Bytes();
@@ -24,120 +22,111 @@ void CCharacterReaderUTF8::Feed(const std::string& rssString)
void CCharacterReaderUTF8::Reset() void CCharacterReaderUTF8::Reset()
{ {
m_ssString.clear(); m_ssString.clear();
m_nDataLength = 0;
m_nCursor = 0; m_nCursor = 0;
} }
std::string CCharacterReaderUTF8::Peek() std::string CCharacterReaderUTF8::Peek(size_t nSkip /*= 0*/, size_t nAmount /*= 1*/) const
{ {
return GetNextCharacter(); if (IsEOF())
return {};
size_t nOffset = 0;
for (size_t n = 0; n < nSkip; ++n)
{
nOffset += GetLengthOfNextCharacter(nOffset);
if (m_ssString.size() <= m_nCursor + nOffset)
return {};
}
std::string ssCharacters;
for (size_t n = 0; n < nAmount; n++)
{
ssCharacters += GetNextCharacter(nOffset);
nOffset += GetLengthOfNextCharacter(nOffset);
}
return ssCharacters;
} }
std::string CCharacterReaderUTF8::Peek(std::size_t n) std::string CCharacterReaderUTF8::PeekUntil(const std::vector<std::string>& lstCollection) const
{ {
if (n < 1) size_t nOffset = 0;
{ bool bFound = false;
return "";
}
size_t offset{0};
for (std::size_t i = 0; i < n - 1; ++i)
{
offset += GetLengthOfNextCharacter(offset);
if (m_nDataLength <= m_nCursor + offset)
{
return "";
}
}
return GetNextCharacter(offset);
}
std::string CCharacterReaderUTF8::PeekUntil(const std::vector<std::string>& lstCollection)
{
size_t offset{0};
bool found{false};
std::string accumulation; std::string accumulation;
while (!found && m_nDataLength > m_nCursor + offset) while (!bFound && m_ssString.size() > m_nCursor + nOffset)
{ {
std::string character = GetNextCharacter(offset); std::string ssCharacter = GetNextCharacter(nOffset);
offset += GetLengthOfNextCharacter(offset); nOffset += GetLengthOfNextCharacter(nOffset);
for (const auto& delimiter : lstCollection) for (const auto& delimiter : lstCollection)
{ {
if (delimiter == character) if (delimiter == ssCharacter)
{ bFound = true;
found = true;
}
}
if (!found)
{
accumulation += character;
} }
if (!bFound)
accumulation += ssCharacter;
} }
return accumulation; return accumulation;
} }
std::string CCharacterReaderUTF8::Consume() std::string CCharacterReaderUTF8::Consume(size_t nSkip /*= 0*/, size_t nAmount /*= 1*/)
{ {
if (IsEOF()) if (IsEOF())
return {};
for (size_t n = 0; n < nSkip; ++n)
{ {
return ""; size_t nLength = GetLengthOfNextCharacter();
m_nCursor += nLength;
if (!nLength || m_ssString.size() < m_nCursor)
return {};
} }
std::string character{GetNextCharacter()}; std::string ssCharacters;
for (size_t n = 0; n < nAmount; n++)
{
ssCharacters += GetNextCharacter();
m_nCursor += GetLengthOfNextCharacter(); m_nCursor += GetLengthOfNextCharacter();
return character;
} }
return ssCharacters;
std::string CCharacterReaderUTF8::Consume(std::size_t n)
{
if (n < 1)
{
return "";
}
size_t offset{0};
for (uint32_t i = 0; i < n - 1; ++i)
{
offset += GetLengthOfNextCharacter(offset);
if (m_nDataLength < m_nCursor + offset)
{
return "";
}
}
std::string character{GetNextCharacter(offset)};
m_nCursor += offset + GetLengthOfNextCharacter(offset);
return character;
} }
std::string CCharacterReaderUTF8::ConsumeUntil(const std::vector<std::string>& lstCollection) std::string CCharacterReaderUTF8::ConsumeUntil(const std::vector<std::string>& lstCollection)
{ {
std::size_t offset{0}; size_t nOffset = 0;
bool found{false}; bool bFound = false;
std::string accumulation; std::string accumulation;
while (!found && m_nDataLength > m_nCursor + offset) while (!bFound && m_ssString.size() > m_nCursor + nOffset)
{ {
std::string character = GetNextCharacter(offset); std::string ssCharacter = GetNextCharacter(nOffset);
offset += GetLengthOfNextCharacter(offset); nOffset += GetLengthOfNextCharacter(nOffset);
for (const auto& delimiter : lstCollection) for (const auto& delimiter : lstCollection)
{ {
if (delimiter == character) if (delimiter == ssCharacter)
{ {
found = true; bFound = true;
} }
} }
if (!found) if (!bFound)
{ {
accumulation += character; accumulation += ssCharacter;
} }
else else
{ {
offset -= character.size(); nOffset -= ssCharacter.size();
} }
} }
m_nCursor += offset; m_nCursor += nOffset;
return accumulation; return accumulation;
} }
bool CCharacterReaderUTF8::IsEOF() const bool CCharacterReaderUTF8::IsEOF() const
{ {
return m_nDataLength < m_nCursor + 1; return m_ssString.size() < m_nCursor + 1;
}
void CCharacterReaderUTF8::SetBookmark()
{
m_nBookmark = m_nCursor;
}
std::string CCharacterReaderUTF8::StringFromBookmark() const
{
return m_ssString.substr(m_nBookmark, (m_nCursor > m_nBookmark) ? m_nCursor - m_nBookmark : 0);
} }
void CCharacterReaderUTF8::CheckForInvalidUTF8Bytes() const void CCharacterReaderUTF8::CheckForInvalidUTF8Bytes() const
@@ -145,7 +134,7 @@ void CCharacterReaderUTF8::CheckForInvalidUTF8Bytes() const
const unsigned char invalidByteC0{0xC0}; const unsigned char invalidByteC0{0xC0};
const unsigned char invalidByteC1{0xC1}; const unsigned char invalidByteC1{0xC1};
const unsigned char lowerBoundInvalidRegion{0xF5}; const unsigned char lowerBoundInvalidRegion{0xF5};
for (std::size_t i = 0; i < m_ssString.size(); ++i) for (size_t i = 0; i < m_ssString.size(); ++i)
{ {
unsigned char uc = m_ssString[i]; unsigned char uc = m_ssString[i];
if (uc == invalidByteC0 || uc == invalidByteC1 || uc >= lowerBoundInvalidRegion) if (uc == invalidByteC0 || uc == invalidByteC1 || uc >= lowerBoundInvalidRegion)
@@ -167,8 +156,9 @@ void CCharacterReaderUTF8::CheckForInvalidUTF8Sequences() const
state_four_byte, state_four_byte,
state_error state_error
}; };
EState eCurrentState{EState::state_neutral}; EState eCurrentState = EState::state_neutral;
uint8_t uiIndex{0}; uint8_t uiIndex = 0;
auto fnCheckByteInNeutralState = [&uiIndex, &eCurrentState](unsigned char uc) auto fnCheckByteInNeutralState = [&uiIndex, &eCurrentState](unsigned char uc)
{ {
if ((uc & m_uiOneByteCheckMask) == m_OneByteCheckValue) if ((uc & m_uiOneByteCheckMask) == m_OneByteCheckValue)
@@ -240,7 +230,7 @@ void CCharacterReaderUTF8::CheckForInvalidUTF8Sequences() const
eCurrentState = EState::state_error; eCurrentState = EState::state_error;
} }
}; };
for (std::size_t i = 0; i < m_ssString.size(); ++i) for (size_t i = 0; i < m_ssString.size(); ++i)
{ {
uint8_t uiCurrentByte = m_ssString[i]; uint8_t uiCurrentByte = m_ssString[i];
switch (eCurrentState) switch (eCurrentState)
@@ -262,7 +252,6 @@ void CCharacterReaderUTF8::CheckForInvalidUTF8Sequences() const
sstreamMessage << "Invalid character with byte " << std::hex << m_ssString[i - 1] << std::dec << "(" sstreamMessage << "Invalid character with byte " << std::hex << m_ssString[i - 1] << std::dec << "("
<< static_cast<int32_t>(m_ssString[i - 1]) << ") at index " << i - 1 << "\n"; << static_cast<int32_t>(m_ssString[i - 1]) << ") at index " << i - 1 << "\n";
throw XTOMLParseException(sstreamMessage.str()); throw XTOMLParseException(sstreamMessage.str());
break;
} }
} }
if (eCurrentState != EState::state_neutral) if (eCurrentState != EState::state_neutral)
@@ -273,51 +262,41 @@ void CCharacterReaderUTF8::CheckForInvalidUTF8Sequences() const
} }
} }
std::string CCharacterReaderUTF8::GetNextCharacter() std::string CCharacterReaderUTF8::GetNextCharacter(size_t nOffset /*= 0*/) const
{ {
if (IsEOF()) if (IsEOF())
{ return {};
return ""; return {m_ssString, m_nCursor + nOffset, GetLengthOfNextCharacter(nOffset)};
}
return {m_ssString, m_nCursor, GetLengthOfNextCharacter()};
} }
std::string CCharacterReaderUTF8::GetNextCharacter(std::size_t offset) size_t CCharacterReaderUTF8::GetLengthOfNextCharacter(size_t nOffset) const
{ {
return {m_ssString, m_nCursor + offset, GetLengthOfNextCharacter(offset)}; if (IsEOF())
} return 0;
uint8_t ui = static_cast<uint8_t>(m_ssString[m_nCursor + nOffset]);
size_t CCharacterReaderUTF8::GetLengthOfNextCharacter() const int32_t uiRet = 0;
{
return GetLengthOfNextCharacter(0);
}
size_t CCharacterReaderUTF8::GetLengthOfNextCharacter(std::size_t offset) const
{
uint8_t ui = m_ssString[m_nCursor + offset];
int32_t ret;
if ((ui & m_uiOneByteCheckMask) == m_OneByteCheckValue) if ((ui & m_uiOneByteCheckMask) == m_OneByteCheckValue)
{ {
ret = 1; uiRet = 1;
} }
else if ((ui & m_uiFourByteCheckMask) == m_uiFourByteCheckValue) else if ((ui & m_uiFourByteCheckMask) == m_uiFourByteCheckValue)
{ {
ret = 4; uiRet = 4;
} }
else if ((ui & m_uiThreeByteCheckMask) == m_uiThreeByteCheckValue) else if ((ui & m_uiThreeByteCheckMask) == m_uiThreeByteCheckValue)
{ {
ret = 3; uiRet = 3;
} }
else if ((ui & m_uiTwoByteCheckMask) == m_uiTwoByteCheckValue) else if ((ui & m_uiTwoByteCheckMask) == m_uiTwoByteCheckValue)
{ {
ret = 2; uiRet = 2;
} }
else else
{ {
std::stringstream sstreamMessage; std::stringstream sstreamMessage;
sstreamMessage << "Invalid character sequence with byte " << std::hex << ui << std::dec sstreamMessage << "Invalid character sequence with byte " << std::hex << ui << std::dec << " as start byte\n";
<< " as start byte\n";
throw XTOMLParseException(sstreamMessage.str()); throw XTOMLParseException(sstreamMessage.str());
} }
return ret; return uiRet;
} }
} // namespace toml_parser

View File

@@ -6,23 +6,25 @@
#include <vector> #include <vector>
#include <cstdint> #include <cstdint>
/// The TOML parser namespace
namespace toml_parser
{
/** /**
* @brief Reads a given input string, interprets bytes as UTF-8 and returns UTF-8 characters or strings in order on * @brief Reads a given input string, interprets bytes as UTF-8 and returns UTF-8 characters or strings in order of demand.
* demand
*/ */
class CCharacterReaderUTF8 class CCharacterReaderUTF8
{ {
public: public:
/** /**
* @brief Standard constructor for empty character reader * @brief Standard constructor for empty character reader.
*/ */
CCharacterReaderUTF8(); CCharacterReaderUTF8() = default;
/** /**
* @brief Constructs a character reader from a given string * @brief Constructs a character reader from a given string.
* @param[in] rssString UTF-8 input string. * @param[in] rssString UTF-8 input string.
* @throw InvalidCharacterException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters * @throw XTOMLParseException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters.
* @throw InvalidByteException Throws an InvalidByteException if the input contains for UTF-8 invalid bytes * @throw XTOMLParseException Throws an InvalidByteException if the input contains for UTF-8 invalid bytes.
*/ */
CCharacterReaderUTF8(const std::string& rssString); CCharacterReaderUTF8(const std::string& rssString);
@@ -38,90 +40,107 @@ public:
void Reset(); void Reset();
/** /**
* @brief Gets the next UTF-8 character without advancing the cursor * @brief Get the next n-th UTF-8 character without advancing the cursor.
* @return Returns the next UTF-8 character after the current cursor or an empty string if the cursor is at the * @param[in] nSkip Amount of characters to skip.
* end * @param[in] nAmount The amount of characters to read.
* @return Returns one or more characters starting at the requested position after the current cursor or an empty string
* if the requested position is behind the last character.
*/ */
std::string Peek(); std::string Peek(size_t nSkip = 0, size_t nAmount = 1) const;
/** /**
* @brief Gets the next n-th UTF-8 character without advancing the cursor * @brief Get all upcoming UTF-8 characters until a terminating character without advancing the cursor.
* @param[in] n Step size * @param[in] lstCollection A collection of terminating characters.
* @return Returns the n-th UTF-8 character after the current cursor or an empty string if n<1 or it would read * @return Returns a string of UTF-8 characters until (excluding) the first occurrence of one of the given characters.
* after the last character
*/ */
std::string Peek(std::size_t n); std::string PeekUntil(const std::vector<std::string>& lstCollection) const;
/** /**
* @brief Gets all upcoming UTF-8 characters until a terminating character without advancing the cursor * @brief Get the next n-th UTF-8 character and advancing the cursor by n.
* @param[in] lstCollection A collection of terminating characters * @param[in] nSkip Amount of characters to skip.
* @return Returns a string of UTF-8 characters until (excluding) the first occurrence of one of the given * @param[in] nAmount The amount of characters to read.
* characters * @return Returns one or more characters starting at the requested position after the current cursor or an empty string
* if the requested position is behind the last character.
*/ */
std::string PeekUntil(const std::vector<std::string>& lstCollection); std::string Consume(size_t nSkip = 0, size_t nAmount = 1);
/** /**
* @brief Gets the next UTF-8 character and advancing the cursor by one * @brief Get all upcoming UTF-8 characters until a terminating character and advancing the cursor by the number of
* @return Returns the next UTF-8 character after the current cursor or an empty string if the cursor is at the * characters in the returned string.
* end * @param[in] lstCollection A collection of terminating characters.
*/ * @return Returns a string of UTF-8 characters until (excluding) the first occurrence of one of the given characters.
std::string Consume();
/**
* @brief Gets the next n-th UTF-8 character and advancing the cursor by n
* @param[in] n Step size
* @return Returns the n-th UTF-8 character after the current cursor or an empty string if n<1 or it would read
* after the last character
*/
std::string Consume(std::size_t n);
/**
* @brief Gets all upcoming UTF-8 characters until a terminating character and advancing the cursor by the number
* of characters in the returned string
* @param[in] lstCollection A collection of terminating characters
* @return Returns a string of UTF-8 characters until excludingg) the first occurrence of one of the given
* characters
*/ */
std::string ConsumeUntil(const std::vector<std::string>& lstCollection); std::string ConsumeUntil(const std::vector<std::string>& lstCollection);
/** /**
* @brief Checks if the cursor is at the end of the data to read * @brief Checks if the cursor is at the end of the data to read.
* @return Returns true if the cursor is at the end of the readable data, false otherwise * @return Returns true if the cursor is at the end of the readable data, false otherwise.
*/ */
bool IsEOF() const; bool IsEOF() const;
/**
* @brief Set the bookmark at the current sursor position. This can be used to read the raw string.
*/
void SetBookmark();
/**
* @brief Get string from bookmark.
* @return Returns the raw string from the last bookmark position (if set), or from the beginning of the string (if not
* set).
*/
std::string StringFromBookmark() const;
private: private:
/**
* @brief Check for an out-of-bound UTF8 byte.
* @throw XTOMLParseException Throws an InvalidByteException if the input contains for UTF-8 invalid bytes.
*/
void CheckForInvalidUTF8Bytes() const; void CheckForInvalidUTF8Bytes() const;
/**
* @brief Check for an invalid UTF8 sequence.
* @throw XTOMLParseException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters.
*/
void CheckForInvalidUTF8Sequences() const; void CheckForInvalidUTF8Sequences() const;
std::string GetNextCharacter(); /**
* @brief Get the next character using an nOffset of nOffset bytes.
* @attention This function doesn't protect for an invalid nOffset when reading multi-byte characters.
* @remarks In UTF8 one character could contain up to 4 bytes.
* @param[in] nOffset The nOffset in bytes to the next character to read.
* @return The character from the string or an empty string if there are no more characters in the string.
*/
std::string GetNextCharacter(size_t nOffset = 0) const;
std::string GetNextCharacter(std::size_t offset); /**
* @brief Get the length of the next character in bytes.
* @remarks In UTF8 one character could contain up to 4 bytes.
* @param[in] nOffset The nOffset in bytes to the next character to read.
* @return the length of the character in bytes or zero when there are no more characters in the string.
* @throw XTOMLParseException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters.
*/
size_t GetLengthOfNextCharacter(size_t nOffset = 0) const;
size_t GetLengthOfNextCharacter() const; static const uint8_t m_uiOneByteCheckMask{0b10000000}; ///< Checkmask for 1-Byte UTF-8 characters
static const uint8_t m_OneByteCheckValue{0b00000000}; ///< Value of a 1-Byte UTF-8 character
///< after being or-ed with the checkmask
static const uint8_t m_uiFollowByteCheckMask{0b11000000}; ///< Checkmask for followbyte of a multi-Byte UTF-8 character
static const uint8_t m_uiFollowByteValue{0b10000000}; ///< Value of a followbyte of a multi-Byte UTF-8 character
///< after being or-ed with the checkmask
static const uint8_t m_uiTwoByteCheckMask{0b11100000}; ///< Checkmask for startbyte of 2-Byte UTF-8 characters
static const uint8_t m_uiTwoByteCheckValue{0b11000000}; ///< Value of a startbyte of a 2-Byte UTF-8 character
///< after being or-ed with the checkmask
static const uint8_t m_uiThreeByteCheckMask{0b11110000}; ///< Checkmask for startbyte of 3-Byte UTF-8 characters
static const uint8_t m_uiThreeByteCheckValue{0b11100000}; ///< Value of a startbyte of a 3-Byte UTF-8 character
///< after being or-ed with the checkmask
static const uint8_t m_uiFourByteCheckMask{0b11111000}; ///< Checkmask for startbyte of 4-Byte UTF-8 characters
static const uint8_t m_uiFourByteCheckValue{0b11110000}; ///< Value of a startbyte of a 4-Byte UTF-8 character
///< after being or-ed with the checkmask
size_t GetLengthOfNextCharacter(std::size_t offset) const; std::string m_ssString; ///< String containing the characters to acquire.
size_t m_nCursor = 0; ///< Current position pointing to the next character.
static const uint8_t m_uiOneByteCheckMask{0b10000000}; //!< Checkmask for 1-Byte UTF-8 characters size_t m_nBookmark = 0; ///< Bookmark cursor position to use to get raw string chunks.
static const uint8_t m_OneByteCheckValue{0b00000000}; //!< Value of a 1-Byte UTF-8 character
//!< after being or-ed with the checkmask
static const uint8_t m_uiFollowByteCheckMask{0b11000000}; //!< Checkmask for followbyte of a multi-Byte UTF-8 character
static const uint8_t m_uiFollowByteValue{0b10000000}; //!< Value of a followbyte of a multi-Byte UTF-8 character
//!< after being or-ed with the checkmask
static const uint8_t m_uiTwoByteCheckMask{0b11100000}; //!< Checkmask for startbyte of 2-Byte UTF-8 characters
static const uint8_t m_uiTwoByteCheckValue{0b11000000}; //!< Value of a startbyte of a 2-Byte UTF-8 character
//!< after being or-ed with the checkmask
static const uint8_t m_uiThreeByteCheckMask{0b11110000}; //!< Checkmask for startbyte of 3-Byte UTF-8 characters
static const uint8_t m_uiThreeByteCheckValue{0b11100000}; //!< Value of a startbyte of a 3-Byte UTF-8 character
//!< after being or-ed with the checkmask
static const uint8_t m_uiFourByteCheckMask{0b11111000}; //!< Checkmask for startbyte of 4-Byte UTF-8 characters
static const uint8_t m_uiFourByteCheckValue{0b11110000}; //!< Value of a startbyte of a 4-Byte UTF-8 character
//!< after being or-ed with the checkmask
std::string m_ssString;
std::size_t m_nDataLength;
std::size_t m_nCursor;
}; };
} // namespace toml_parser
#endif // CHARACTER_READER_UTF_8_H #endif // CHARACTER_READER_UTF_8_H

View File

@@ -3,18 +3,31 @@
#include <interfaces/toml.h> #include <interfaces/toml.h>
/// The TOML parser namespace
namespace toml_parser
{
/**
* @brief Extended exception for the TOML parser.
*/
except XTOMLParseException : public sdv::toml::XTOMLParseException except XTOMLParseException : public sdv::toml::XTOMLParseException
{ {
/** /**
* @brief Constructor * @brief Constructor
*/ */
XTOMLParseException(const std::string& rss) { ssMessage = rss; }; XTOMLParseException(const std::string& rss)
{
ssMessage = rss;
};
/** /**
* @brief Return the explanatory string. * @brief Return the explanatory string.
* @return The descriptive string. * @return The descriptive string.
*/ */
virtual const char* what() const noexcept override { return ssMessage.c_str(); } virtual const char* what() const noexcept override
{
return ssMessage.c_str();
}
}; };
} // namespace toml_parser
#endif // !defined CONFIG_EXCEPTION_H #endif // !defined CONFIG_EXCEPTION_H

File diff suppressed because it is too large Load Diff

View File

@@ -3,122 +3,210 @@
#include <algorithm> #include <algorithm>
#include <stack> #include <stack>
#include <list>
#include <functional>
#include "character_reader_utf_8.h" #include "character_reader_utf_8.h"
#include "lexer_toml_token.h"
/// The TOML parser namespace
namespace toml_parser
{
/** /**
* @brief Tokenizes the output of a character reader in regard of the TOML format and returns tokens in order on demand * @brief Node token range used to regenerate the source from the node entries.
* @details A node can have several token ranges identifying code that belongs to the node or precedes or succeeds the node. The
* following ranges can be identified:
* - code before the node, not belonging to another node
* - code before the node with comments belonging to the node
* - the first part of the node
* - the in between part, which might contain other nodes (in case of an inline node collection)
* - the second part of the node finishing the node (in case of an inline node collection)
* - code behind the node with comments belonging to the node
* - code behind the node, not belonging to another node (this is the code until the end of the node list)
*/ */
class CLexerTOML class CNodeTokenRange
{ {
public: public:
/** /**
* @brief Enum for all possible token categories * @brief Initialize the range with the first initial token. Any token before that belong to the token range of the previous
* node.
* @param[in] rInitialToken The initial token that defines/starts the range.
*/ */
enum class ETokenCategory : uint8_t CNodeTokenRange(const CToken& rInitialToken);
{
token_none, ///< Default /**
token_syntax_assignment, ///< '=' * @brief Construct a node range with the main node token range.
token_syntax_array_open, ///< '[' after '=' * @param[in] rrangeNodeMain Reference to the token range holding the main node tokens.
token_syntax_array_close, ///< ']' after an array open */
token_syntax_table_open, ///< '[' CNodeTokenRange(const CTokenRange& rrangeNodeMain);
token_syntax_table_close, ///< ']'
token_syntax_table_array_open, ///< '[[' /**
token_syntax_table_array_close, ///< ']]' * @brief Set the token identifying the area before the extended node. Will start before or at and end at the extended node
token_syntax_inline_table_open, ///< '{' * range.
token_syntax_inline_table_close, ///< '}' * @param[in] rTokenBegin Reference to the token identifying the begin of the lines before the extended node range.
token_syntax_comma, ///< ',' * @remarks Setting the begin will not change the extended node range. If starting within the extended node range, the begin
token_syntax_dot, ///< '.' * of the extended node range determines the begin of the lines before.
token_syntax_new_line, ///< Line break */
token_key, ///< Key of a Key-Value-Pair void LinesBeforeNode(const CToken& rTokenBegin);
token_string, ///< A string for a Value of a Key-Value-Pair or Array
token_integer, ///< An integer for a Value of a Key-Value-Pair or Array /**
token_float, ///< A floating point number for a Value of a Key-Value-Pair or Array * @brief Get the lines before range, being located before the extended node range.
token_boolean, ///< A bool for a Value of a Key-Value-Pair or Array * @return The token range with begin and end token of the lines before. If identical, there are no lines before.
token_time_local, ///< Unused for now */
token_date_time_offset, ///< Unused for now CTokenRange LinesBeforeNode() const;
token_date_time_local, ///< Unused for now
token_date_local, ///< Unused for now /**
token_eof, ///< End of File Token; may only be at the end of the token array * @brief Set the token range identifying the extended node range. Will start before or at and end behind or at the main
token_error, ///< Error token containing an error message; further lexing is not affected * node ranges (incl. the finish range).
token_empty, ///< Empty token for trying to read out of bounds * @param[in] rRange Token range holding the begin and end of the extended node range.
token_terminated, ///< Terminated token containing an error message; further lexing is terminated * @remarks If the range starts or ends within the main node range, the extended node range is adapted to the main node
* range. If the lines before or behind the node fall within the extended node range, the lines will be adapted to the
* extended node range.
*/
void ExtendedNode(const CTokenRange& rRange);
/**
* @brief Get the extended node range. The range includes the main node ranges (including the node finish range).
* @return The token range with begin and end token of the extended node range.
*/
CTokenRange ExtendedNode() const;
/**
* @brief Get the node comments part before the node definition, which is the difference between the beginning of the
* extended node and the main node.
* @return The token range with begin and end token of the node comments range.
*/
CTokenRange NodeCommentsBefore() const;
/**
* @brief Set the main node range.
* @param[in] rRange Token range holding the begin and end of the main node range.
* @remarks If the main node finish range starts within or before the main node range, the main node finish range will be
* updated. If the extended node range does not include the main range completely (incl. finish node range), the extended
* node range will be updated.
*/
void NodeMain(const CTokenRange& rRange);
/**
* @brief Get the main node range.
* @return The token range with begin and end token of the main node range.
*/
CTokenRange NodeMain() const;
/**
* @brief Set the main node finish range (for inline tables and arrays).
* @param[in] rRange Token range holding the begin and end of the main node finish range.
* @remarks If the main node finish range starts within or before the main node range, the main node finish range will be
* updated. If the extended node range does not include the main range completely (incl. finish node range), the extended
* node range will be updated.
*/
void NodeMainFinish(const CTokenRange& rRange);
/**
* @brief Get the main node finish range.
* @return The token range with begin and end token of the main node finish range.
*/
CTokenRange NodeMainFinish() const;
/**
* @brief Get the node comments part behind the node definition, which is the difference between the end of the
* main node and the end of the extended node.
* @return The token range with begin and end token of the node comments range.
*/
CTokenRange NodeCommentsBehind() const;
/**
* @brief Set the token identifying the area behind the extended node. Will start at and end behind or at the extended node
* range.
* @param[in] rTokenEnd Reference to the token identifying the end of the lines behind the extended node range.
* @remarks Setting the end will not change the extended node range. If ending within the extended node range, the end
* of the extended node range determines the end of the lines before.
*/
void LinesBehindNode(const CToken& rTokenEnd);
/**
* @brief Get the lines behind range, being located behind the extended node range.
* @return The token range with begin and end token of the lines behind. If identical, there are no lines behind.
*/
CTokenRange LinesBehindNode() const;
private:
std::reference_wrapper<const CToken> m_refBeforeNodeBegin; ///< The start of the lines before the extended node (not
///< belonging to any node).
CTokenRange m_rangeExtendedNode; ///< The extended node including comments before and behind
///< the node.
CTokenRange m_rangeNodeMain; ///< The node tokens or for inline tables and arrays the
///< opening part of the node.
CTokenRange m_rangeNodeFinish; ///< For inline tables and arrays, the node tokens for closing the node.
std::reference_wrapper<const CToken> m_refBehindNodeEnd; ///< The end of the lines behind the extended node (not
///< belonging to any node).
}; };
/** /**
* @brief Contains lexed information for the parser * @brief Tokenizes the output of a character reader using TOML v1.0: https://toml.io/en/v1.0.0
*/ */
struct SToken class CLexer
{ {
public:
/** /**
* @brief Default constructor * @brief Default constructor
*/ */
SToken() = default; CLexer() = default;
/**
* @brief Constructs a new Token object with a given category
* @param[in] category The initial token category value for the token to be constructed
*/
explicit SToken(ETokenCategory category) : eCategory(category)
{}
std::string ssContentString; ///< Token string content
int64_t iContentInteger{}; ///< Token integer content
double dContentFloatingpoint{}; ///< Token floatingpoint content
bool bContentBoolean{}; ///< Token boolean content
ETokenCategory eCategory{ETokenCategory::token_none}; ///< Token category
};
/**
* @brief Default constructor
*/
CLexerTOML() = default;
/** /**
* @brief Constructs a new LexerTOML object with given input data that will be lexed * @brief Constructs a new LexerTOML object with given input data that will be lexed
* @param[in] rssString The UTF-8 encoded content of a TOML source * @param[in] rssString The UTF-8 encoded content of a TOML source
* @param[in] bValueOnly When set, the lexer should treat the string as a value assignment.
*/ */
CLexerTOML(const std::string& rssString); CLexer(const std::string& rssString, bool bValueOnly = false);
/** /**
* @brief Feed the lexer with the given string. * @brief Feed the lexer with the given string. This will replace a previous lexing result.
* @param[in] rssString UTF-8 input string. * @param[in] rssString UTF-8 input string.
* @param[in] bValueOnly When set, the lexer should treat the string as a value assignment.
*/ */
void Feed(const std::string& rssString); void Feed(const std::string& rssString, bool bValueOnly = false);
/** /**
* @brief Reset the lexer content. * @brief Reset the lexer cursor position.
*/ */
void Reset(); void Reset();
/** /**
* @brief Gets the next token after the current cursor position without advancing the cursor * @brief Navigation modes supported by the lexer.
* @return Returns the next token after the current cursor position or a End-of-File-Token if there are no
* tokens
*/ */
SToken Peek() const; enum class ENavigationMode
{
skip_comments_and_whitespace = 1, ///< Skip comments and whitespace during navigation (default)
do_not_skip_anything = 2, ///< Do not skip anything during navigation.
};
/** /**
* @brief Gets the next token after the current cursor position and advancing the cursor by one * @brief Get the current navigation mode.
* @return Returns the next token after the current cursor position or a End-of-File-Token if there are no * @return The current naviation mode.
* tokens
*/ */
SToken Consume(); ENavigationMode NavigationMode() const;
/**
* @brief Set the navigation mode.
* @param[in] eMode The mode to be used for navigation.
*/
void NavigationMode(ENavigationMode eMode);
/** /**
* @brief Gets the n-th token after the current cursor without advancing the cursor * @brief Gets the n-th token after the current cursor without advancing the cursor
* @param[in] n Step size * @remarks Whitespace and comments are skipped.
* @return Returns the n-th token after the current cursor position or and empty token if n<1 or the end-token * @param[in] nSkip Skip the amount of tokens.
* if a step of n would read a position after the end-token * @return Returns smart pointer to the token in the token list or an empty pointer.
*/ */
SToken Peek(int32_t n); const CToken& Peek(size_t nSkip = 0) const;
/** /**
* @brief Gets the n-th token after the current cursor and advancing the cursor by n * @brief Gets the n-th token after the current cursor and advancing the cursor by n
* @param[in] n Step size * @remarks Whitespace and comments are skipped.
* @return Returns the n-th token after the current cursor position or and empty token if n<1 or the end-token * @param[in] nSkip Skip the amount of tokens.
* if a step of n would read a position after the end-token * @return Returns smart pointer to the token in the token list or an empty pointer.
*/ */
SToken Consume(int32_t n); const CToken& Consume(size_t nSkip = 0);
/** /**
* @brief Checks if the end-token was consumed * @brief Checks if the end-token was consumed
@@ -127,82 +215,146 @@ public:
*/ */
bool IsEnd() const; bool IsEnd() const;
/**
* @brief Check and extend the provided boundaries of a given token range to include white space and comments which
* obviously belong to the token range and check for additional tokens when reaching the end of the token list.
* @param[in, out] rTokenRange Reference to the node token range being updated with extended boundaries.
*/
void SmartExtendNodeRange(CNodeTokenRange& rTokenRange) const;
private: private:
void GenerateTokens(); /**
* @brief Run through the string and generate tokens.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
*/
void GenerateTokens(CCharacterReaderUTF8& rReader);
bool IsBasicQuotedKey(); /**
* @brief Read a quoted key. A quoted key accepts basic strings as keys surrounded by double quotes.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a basic quoted key or an empty token if the token hasn't been found.
*/
CToken ReadBasicQuotedKey(CCharacterReaderUTF8& rReader) const;
void ReadBasicQuotedKey(); /**
* @brief Read a literal quoted key. A literal quoted key accepts basic strings as keys surrounded by single quotes.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a literal quoted key or an empty token if the token hasn't been found.
*/
CToken ReadLiteralQuotedKey(CCharacterReaderUTF8& rReader) const;
bool IsLiteralQuotedKey(); /**
* @brief Read a bare key. A bare key may only contain ASCII letters, ASCII digits, underscores and dashes (A-Za-z0-9_-).
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a bare key or an empty token if the token hasn't been found.
*/
CToken ReadBareKey(CCharacterReaderUTF8& rReader) const;
void ReadLiteralQuotedKey(); /**
* @brief Read a basic string. A basic string may contain any unicode character. Some characters need to be escaped. The
* basis string is surrounded by double quotes.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a basic string or an empty token if the token hasn't been found.
*/
CToken ReadBasicString(CCharacterReaderUTF8& rReader);
bool IsBareKey(); /**
* @brief Read a multi-line basic string. A basic string may contain any unicode character. Some characters need to be
* escaped. The multi-line basis string is surrounded by three double quotes before and behind the string.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a multi-line basic string or an empty token if the token hasn't been found.
*/
CToken ReadBasicMultilineString(CCharacterReaderUTF8& rReader);
void ReadBareKey(); /**
* @brief Read a literal string. A literal string may contain any unicode character, but does not support escaped
* characters. The literal string is surrounded by single quotes.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a literal string or an empty token if the token hasn't been found.
*/
CToken ReadLiteralString(CCharacterReaderUTF8& rReader);
bool IsBasicString(); /**
* @brief Read a multi-line literal string. A literal string may contain any unicode character, but does not support escaped
* characters. The multi-line literal string is surrounded by three single quotes before and behind the string.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a multi-line literal string or an empty token if the token hasn't been found.
*/
CToken ReadLiteralMultilineString(CCharacterReaderUTF8& rReader);
void ReadBasicString(); /**
* @brief Read the integer. An integer is number optionally preceded by a sign and possible defined as hexadecimal
* number (preceded by 0x), octal number (preceded by 0o) or binary number (preceeeded by 0b). Numbers can be split with
* underscores.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for an integer number or an empty token if the token hasn't been found.
*/
CToken ReadInteger(CCharacterReaderUTF8& rReader);
bool IsBasicMultilineString(); /**
* @brief Read the floating point number. A floating point number follows the rules defined in IEEE 754 binary64 and consist
* of a integer part followed by a fractional part (separated by a dot). An sign can be preceded and an exponential part
* can be succeeded (separated from the fractional part with the 'e'). The number can be split with underscores. Infinite
* and not-a-number are also supported (as defined in the IEEE 754).
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a floating point number or an empty token if the token hasn't been found.
*/
CToken ReadFloat(CCharacterReaderUTF8& rReader);
void ReadBasicMultilineString(); /**
* @brief Read the boolean. A boolean has the value 'true' or 'false'.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a boolean or an empty token if the token hasn't been found.
*/
CToken ReadBool(CCharacterReaderUTF8& rReader);
bool IsLiteralString(); /**
* @brief Get the whitespace. Whitespace is a tab or space.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for whitespace or an empty token if the token hasn't been found.
*/
CToken ReadWhitespace(CCharacterReaderUTF8& rReader) const;
void ReadLiteralString(); /**
* @brief Get the syntax element. A syntax element identifies tables, arrays, new-lines, separators and
* assignments: '[]{},.=\\r\\n'.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for a syntax element or an empty token if the token hasn't been found.
*/
CToken ReadSyntaxElement(CCharacterReaderUTF8& rReader);
bool IsLiteralMultilineString(); /**
* @brief Read the comment. A comment is identified by a hash '#' and includes the rest of the line.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for comment or an empty token if the token hasn't been found.
*/
static CToken ReadComment(CCharacterReaderUTF8& rReader);
void ReadLiteralMultilineString(); /**
* @brief Read the unknown sequence (not represented by defined tokens).
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return Returns the token for an unknown sequence or an empty token if the reader has reached EOF.
*/
CToken ReadUnknownSequence(CCharacterReaderUTF8& rReader);
bool IsInteger(); /**
* @brief In case of an escape, return the interpretation of the character following.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @return The unescaped character.
*/
static std::string Unescape(CCharacterReaderUTF8& rReader);
void ReadInteger(); /**
* @brief Generic implementation of escaped unicode character interpretation.
* @param[in] rReader Reference to the reader providing the UTF8 characters.
* @param[in] nDigits The amount of digits the unicode character consist of.
* @return The interpreted unicode character.
*/
static std::string EscapedUnicodeCharacterToUTF8(CCharacterReaderUTF8& rReader, size_t nDigits);
bool IsFloat(); ENavigationMode m_eNavMode = ENavigationMode::skip_comments_and_whitespace; ///< The current navigation mode.
TTokenList m_lstTokens; ///< List of tokens.
void ReadFloat(); TTokenListIterator m_itCursor{m_lstTokens.end()}; ///< Current position within token list.
bool IsBool();
void ReadBool();
bool IsWhitespace();
void ReadWhitespace();
bool IsSyntaxElement();
void ReadSyntaxElement();
bool IsComment();
void ReadComment();
void ReadUnknownSequence();
std::string Unescape();
std::string Unicode4DigitToUTF8();
std::string Unicode8DigitToUTF8();
std::string UnicodeToUTF8(uint8_t numCharacters);
static uint32_t HexToDecimal(const char character);
static uint32_t DecimalToDecimal(const char character);
static uint32_t OctalToDecimal(const char character);
static uint32_t BinaryToDecimal(const char character);
CCharacterReaderUTF8 m_reader;
std::vector<SToken> m_vecTokens;
std::size_t m_nCursor{0};
/** /**
* @brief Enum for differentiating between keys and values that are potentially indifferent like '"value" = * @brief Enum for differentiating between keys and values that are potentially indifferent like '"value" =
@@ -215,12 +367,11 @@ private:
expect_value_once, ///< A value is expected over a key once expect_value_once, ///< A value is expected over a key once
}; };
std::stack<EExpectation> m_stackExpectations; ///< Tracking of key or value expectations in nested structures std::stack<EExpectation> m_stackExpectations; ///< Tracking of key or value expectations in nested structures
// int32_t m_LineCount{0};
const std::vector<std::string> m_vecKeyDelimiters{ const std::vector<std::string>
"\n", "\t", "\r", " ", "", ".", "=", "]"}; ///< Characters that delimit a key m_vecKeyDelimiters{"\n", "\t", "\r", " ", "", ".", "=", "]"}; ///< Characters that delimit a key
const std::vector<std::string> m_vecValueDelimiters{ const std::vector<std::string>
"\n", "\t", "\r", " ", ",", "", "]", "}"}; ///< Characters that delimit a value m_vecValueDelimiters{"\n", "\t", "\r", " ", ",", "", "]", "}", "#"}; ///< Characters that delimit a value
}; };
} // namespace toml_parser
#endif // LEXER_TOML_H #endif // LEXER_TOML_H

View File

@@ -0,0 +1,552 @@
#include "lexer_toml_token.h"
#include <sstream>
/// The TOML parser namespace
namespace toml_parser
{
CToken::CToken()
{}
CToken::CToken(const TTokenList& rTokenList, EBoundary eBoundary)
{
switch (eBoundary)
{
case EBoundary::lower_boundary:
m_optTokenList = rTokenList;
// NOTE: No iterator assignment.
break;
case EBoundary::upper_boundary:
m_optTokenList = rTokenList;
m_optLocation = rTokenList.end();
break;
case EBoundary::no_boundary:
default:
// NOTE: No tokenlist assignment.
break;
}
}
CToken::CToken(ETokenCategory eCategory) : m_eCategory(eCategory)
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
case ETokenCategory::token_integer:
case ETokenCategory::token_float:
case ETokenCategory::token_boolean:
case ETokenCategory::token_time_local:
case ETokenCategory::token_date_time_offset:
case ETokenCategory::token_date_time_local:
case ETokenCategory::token_date_local:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token category for token without ssContent: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
default:
break;
}
}
CToken::CToken(ETokenCategory eCategory, const std::string& rssContent,
ETokenStringType eStringType /*= ETokenStringType::not_specified*/) :
m_eCategory(eCategory), m_eStringType(eStringType)
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(rssContent);
break;
default:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token category for string token: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
break;
}
}
CToken::CToken(ETokenCategory eCategory, int64_t iContent) : m_eCategory(eCategory)
{
switch (m_eCategory)
{
case ETokenCategory::token_integer:
m_iContentInteger = iContent;
break;
default:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token category for integer token: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
break;
}
}
CToken::CToken(ETokenCategory eCategory, double dContent) : m_eCategory(eCategory)
{
switch (m_eCategory)
{
case ETokenCategory::token_float:
m_dContentFloatingpoint = dContent;
break;
default:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token categoryfor floating point token: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
break;
}
}
CToken::CToken(ETokenCategory eCategory, bool bContent) : m_eCategory(eCategory)
{
switch (m_eCategory)
{
case ETokenCategory::token_boolean:
m_bContentBoolean = bContent;
break;
default:
{
std::stringstream sstreamMessage;
sstreamMessage << "Invalid token category for boolean token: " << static_cast<uint32_t>(eCategory);
m_eCategory = ETokenCategory::token_error;
new (&m_ssContentString) std::string(sstreamMessage.str());
}
break;
}
}
CToken::CToken(ETokenCategory eCategory, const sdv::toml::XTOMLParseException& rexcept) :
CToken(eCategory, std::string(rexcept.what()))
{}
CToken::CToken(const CToken& rToken) :
m_ssRawString(rToken.m_ssRawString), m_optTokenList(rToken.m_optTokenList), m_optLocation(rToken.m_optLocation),
m_eCategory(rToken.m_eCategory), m_eStringType(rToken.m_eStringType)
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(rToken.m_ssContentString);
break;
case ETokenCategory::token_integer:
m_iContentInteger = rToken.m_iContentInteger;
break;
case ETokenCategory::token_float:
m_dContentFloatingpoint = rToken.m_dContentFloatingpoint;
break;
case ETokenCategory::token_boolean:
m_bContentBoolean = rToken.m_bContentBoolean;
break;
default:
break;
}
}
CToken::CToken(CToken&& rToken) :
m_ssRawString(std::move(rToken.m_ssRawString)), m_optTokenList(std::move(rToken.m_optTokenList)),
m_optLocation(rToken.m_optLocation), m_eCategory(rToken.m_eCategory), m_eStringType(rToken.m_eStringType)
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(std::move(rToken.m_ssContentString));
break;
case ETokenCategory::token_integer:
m_iContentInteger = rToken.m_iContentInteger;
break;
case ETokenCategory::token_float:
m_dContentFloatingpoint = rToken.m_dContentFloatingpoint;
break;
case ETokenCategory::token_boolean:
m_bContentBoolean = rToken.m_bContentBoolean;
break;
default:
break;
}
// Clear the ssContent
rToken.~CToken();
rToken.m_eCategory = ETokenCategory::token_none;
rToken.m_eStringType = ETokenStringType::not_specified;
}
CToken::~CToken()
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
m_ssContentString.~basic_string<char>();
break;
default:
break;
}
}
CToken& CToken::operator=(const CToken& rToken)
{
// Clear current ssContent.
this->~CToken();
// Copy new ssContent
m_ssRawString = rToken.m_ssRawString;
m_optTokenList = rToken.m_optTokenList;
m_optLocation = rToken.m_optLocation;
m_eCategory = rToken.m_eCategory;
m_eStringType = rToken.m_eStringType;
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(rToken.m_ssContentString);
break;
case ETokenCategory::token_integer:
m_iContentInteger = rToken.m_iContentInteger;
break;
case ETokenCategory::token_float:
m_dContentFloatingpoint = rToken.m_dContentFloatingpoint;
break;
case ETokenCategory::token_boolean:
m_bContentBoolean = rToken.m_bContentBoolean;
break;
default:
break;
}
return *this;
}
CToken& CToken::operator=(CToken&& rToken)
{
// Clear current ssContent.
this->~CToken();
// Copy new ssContent
switch (rToken.m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
new (&m_ssContentString) std::string(std::move(rToken.m_ssContentString));
break;
case ETokenCategory::token_integer:
m_iContentInteger = rToken.m_iContentInteger;
break;
case ETokenCategory::token_float:
m_dContentFloatingpoint = rToken.m_dContentFloatingpoint;
break;
case ETokenCategory::token_boolean:
m_bContentBoolean = rToken.m_bContentBoolean;
break;
default:
break;
}
m_ssRawString = std::move(rToken.m_ssRawString);
m_optTokenList = std::move(rToken.m_optTokenList);
m_optLocation = std::move(rToken.m_optLocation);
m_eCategory = rToken.m_eCategory;
m_eStringType = rToken.m_eStringType;
// Clear origional ssContent
rToken.~CToken();
rToken.m_eCategory = ETokenCategory::token_none;
rToken.m_eStringType = ETokenStringType::not_specified;
return *this;
}
bool CToken::operator==(const CToken& rToken) const
{
if (m_optLocation && rToken.m_optLocation)
return *m_optLocation == *rToken.m_optLocation;
if (m_eCategory != rToken.m_eCategory)
return false;
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
return m_ssContentString == rToken.m_ssContentString;
case ETokenCategory::token_integer:
return m_iContentInteger == rToken.m_iContentInteger;
case ETokenCategory::token_float:
return m_dContentFloatingpoint == rToken.m_dContentFloatingpoint;
case ETokenCategory::token_boolean:
return m_bContentBoolean == rToken.m_bContentBoolean;
default:
return true; // No value to compare
}
}
bool CToken::operator!=(const CToken& rToken) const
{
if (m_optLocation && rToken.m_optLocation)
return *m_optLocation != *rToken.m_optLocation;
if (m_eCategory != rToken.m_eCategory)
return true;
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
return m_ssContentString != rToken.m_ssContentString;
case ETokenCategory::token_integer:
return m_iContentInteger != rToken.m_iContentInteger;
case ETokenCategory::token_float:
return m_dContentFloatingpoint != rToken.m_dContentFloatingpoint;
case ETokenCategory::token_boolean:
return m_bContentBoolean != rToken.m_bContentBoolean;
default:
return false; // No value to compare
}
}
CToken::operator bool() const
{
return m_eCategory != ETokenCategory::token_none;
}
const CToken& CToken::Next(size_t nSkip /*= 0*/) const
{
static CToken none;
if (!m_optLocation || !m_optTokenList) return none;
const TTokenListIterator& ritToken = *m_optLocation;
TTokenList::const_iterator ritToken2 = ritToken;
const TTokenList& rTokenList = *m_optTokenList;
if (ritToken2 == rTokenList.end()) return m_optTokenList->get().tokenEnd;
size_t nLocalSkip = nSkip + 1;
do
{
++ritToken2;
if (ritToken2 == rTokenList.end()) return m_optTokenList->get().tokenEnd;
} while (--nLocalSkip);
return *ritToken2;
}
const CToken& CToken::Prev(size_t nSkip /*= 0*/) const
{
static CToken none;
if (!m_optLocation || !m_optTokenList) return none;
const TTokenListIterator& ritToken = *m_optLocation;
TTokenList::const_iterator ritToken2 = ritToken;
const TTokenList& rTokenList = *m_optTokenList;
size_t nLocalSkip = nSkip + 1;
do
{
if (ritToken2 == rTokenList.begin()) return m_optTokenList->get().tokenReverseEnd;
--ritToken2;
} while (--nLocalSkip);
return *ritToken2;
}
const CToken& CToken::JumpToBegin() const
{
static CToken none;
if (!m_optLocation || !m_optTokenList) return none;
return *(m_optTokenList->get().begin());
}
const CToken& CToken::JumpToEnd() const
{
static CToken none;
if (!m_optLocation || !m_optTokenList) return none;
return m_optTokenList->get().tokenEnd;
}
ETokenCategory CToken::Category() const
{
return m_eCategory;
}
ETokenStringType CToken::StringType() const
{
return m_eStringType;
}
std::string CToken::StringValue() const
{
switch (m_eCategory)
{
case ETokenCategory::token_key:
case ETokenCategory::token_string:
case ETokenCategory::token_error:
case ETokenCategory::token_terminated:
return m_ssContentString;
default:
return {};
}
}
int64_t CToken::IntegerValue() const
{
switch (m_eCategory)
{
case ETokenCategory::token_integer:
return m_iContentInteger;
default:
return 0;
}
}
double CToken::FloatValue() const
{
switch (m_eCategory)
{
case ETokenCategory::token_float:
return m_dContentFloatingpoint;
default:
return 0.0;
}
}
bool CToken::BooleanValue() const
{
switch (m_eCategory)
{
case ETokenCategory::token_boolean:
return m_bContentBoolean;
default:
return false;
}
}
const std::string& CToken::RawString() const
{
return m_ssRawString;
}
uint32_t CToken::TokenIndex() const
{
if (m_eCategory == ETokenCategory::token_none)
return 0xffffffff;
return m_uiIndex;
}
void CToken::RawDataInfo(const std::string& rssString, const TTokenList& rTokenList, const TTokenListIterator& ritLocation)
{
m_ssRawString = rssString;
m_optTokenList = rTokenList;
m_optLocation = std::cref(ritLocation);
}
const std::optional<TTokenListIterator>& CToken::Location() const
{
return m_optLocation;
}
const std::optional<std::reference_wrapper<const TTokenList>>& CToken::TokenList() const
{
return m_optTokenList;
}
uint32_t CToken::CreateIndex()
{
static uint32_t uiIndexCounter = 0;
return uiIndexCounter++;
}
CTokenList::CTokenList() :
tokenReverseEnd(*this, CToken::EBoundary::lower_boundary), tokenEnd(*this, CToken::EBoundary::upper_boundary)
{}
CTokenRange::CTokenRange(const CToken& rTokenBegin) : m_rTokenBegin(rTokenBegin), m_rTokenEnd(rTokenBegin.JumpToEnd())
{
if (!rTokenBegin.TokenList())
throw XTOMLParseException("The begin token provided to the token range doesn't have a token list assigned.");
}
CTokenRange::CTokenRange(const CToken& rTokenBegin, const CToken& rTokenEnd) :
m_rTokenBegin(rTokenBegin), m_rTokenEnd(rTokenEnd)
{
if (!rTokenBegin.TokenList())
throw XTOMLParseException("The begin token provided to the token range doesn't have a token list assigned.");
if (!rTokenEnd.TokenList())
throw XTOMLParseException("The end token provided to the token range doesn't have a token list assigned.");
if (&(*rTokenBegin.TokenList()).get() != &(*rTokenEnd.TokenList()).get())
throw XTOMLParseException("The end token of the range is not from the same token list as the begin token.");
}
void CTokenRange::ReassignBeginToken(const CToken& rTokenBegin)
{
if (!rTokenBegin.TokenList())
throw XTOMLParseException("The begin token provided to the token range doesn't have a token list assigned.");
m_rTokenBegin = rTokenBegin;
m_rTokenEnd = rTokenBegin.JumpToEnd();
}
void CTokenRange::ReassignTokenRange(const CToken& rTokenBegin, const CToken& rTokenEnd)
{
if (!rTokenBegin.TokenList())
throw XTOMLParseException("The begin token provided to the token range doesn't have a token list assigned.");
if (!rTokenEnd.TokenList())
throw XTOMLParseException("The end token provided to the token range doesn't have a token list assigned.");
if (&(*rTokenBegin.TokenList()).get() != &(*rTokenEnd.TokenList()).get())
throw XTOMLParseException("The end token of the range is not from the same token list as the begin token.");
m_rTokenBegin = rTokenBegin;
m_rTokenEnd = rTokenEnd;
}
void CTokenRange::AssignEndToken(const CToken& rTokenEnd, bool bIncludeEnd /*= true*/)
{
// Check whether the end token corresponds to the same list of the begin token.
if (!rTokenEnd.TokenList())
throw XTOMLParseException("The end token provided to the token range doesn't have a token list assigned.");
if (&(*m_rTokenBegin.get().TokenList()).get() != &(*rTokenEnd.TokenList()).get())
throw XTOMLParseException("The end token of the range is not from the same token list as the begin token.");
const CToken& rTokenEndCorrected = bIncludeEnd ? rTokenEnd.Next() : rTokenEnd;
m_rTokenEnd = rTokenEndCorrected;
}
const CToken& CTokenRange::Begin() const
{
return m_rTokenBegin.get();
}
const CToken& CTokenRange::End() const
{
return m_rTokenEnd.get();
}
std::list<CToken> CTokenRange::TokenListSLice() const
{
if (!m_rTokenBegin.get().Location() || !m_rTokenEnd.get().Location())
return {};
return std::list<CToken>(*m_rTokenBegin.get().Location(), *m_rTokenEnd.get().Location());
}
CTokenRange::operator bool() const
{
return m_rTokenEnd.get() != m_rTokenBegin.get() && m_rTokenBegin.get().TokenIndex() < m_rTokenEnd.get().TokenIndex();
}
} // namespace toml_parser

View File

@@ -0,0 +1,421 @@
#ifndef LEXER_TOML_TOKEN_H
#define LEXER_TOML_TOKEN_H
#include <string>
#include <cstdint>
#include <list>
#include <optional>
#include <memory>
#include "exception.h"
/// The TOML parser namespace
namespace toml_parser
{
/**
* @brief Enum for all possible token categories
*/
enum class ETokenCategory : uint32_t
{
token_none, ///< Default - not having read anything
token_syntax_assignment, ///< '='
token_syntax_array_open, ///< '[' after '='
token_syntax_array_close, ///< ']' after an array open
token_syntax_table_open, ///< '['
token_syntax_table_close, ///< ']'
token_syntax_table_array_open, ///< '[['
token_syntax_table_array_close, ///< ']]'
token_syntax_inline_table_open, ///< '{'
token_syntax_inline_table_close, ///< '}'
token_syntax_comma, ///< ','
token_syntax_dot, ///< '.'
token_syntax_new_line, ///< Line break
token_key, ///< Key of a Key-Value-Pair
token_string, ///< A for a Value of a Key-Value-Pair or Array
token_integer, ///< An integer for a Value of a Key-Value-Pair or Array
token_float, ///< A floating point number for a Value of a Key-Value-Pair or Array
token_boolean, ///< A bool for a Value of a Key-Value-Pair or Array
token_time_local, ///< Unused for now
token_date_time_offset, ///< Unused for now
token_date_time_local, ///< Unused for now
token_date_local, ///< Unused for now
token_whitespace, ///< Whitespace token
token_comment, ///< Comment token
token_error, ///< Error token containing an error message; further lexing is not affected
token_empty, ///< Empty token for trying to read out of bounds
token_terminated, ///< Terminated token containing an error message; further lexing is terminated
};
/**
* @brief The string type for key and string tokens.
*/
enum ETokenStringType
{
not_specified, ///< Not specified or not applicable
quoted_string, ///< Quoted string
literal_string, ///< Literal string
multi_line_quoted, ///< Multiple lines as a quoted string (only for string tokens)
multi_line_literal, ///< Multiple lines as a literal string (only for string tokens)
};
// Forward declaration
class CLexer;
class CToken;
class CTokenList;
class CTokenRange;
/// Token list type definition
using TTokenList = CTokenList;
/// Token list iterator definition
using TTokenListIterator = std::list<CToken>::const_iterator;
/**
* @brief Contains lexed information for the parser
*/
class CToken
{
// Access to private functions is allowed for the following classes:
friend class CLexer;
friend class CTokenRange;
public:
/**
* @brief Default constructor
*/
CToken();
/**
* @brief Boundary identification.
*/
enum EBoundary
{
no_boundary, ///< The token is not defined a boundary token.
lower_boundary, ///< One token before the first token in the token list.
upper_boundary, ///< One token behind the last token in the token list.
};
/**
* @brief Constructor to construct a boundary token (beginning or end of the token list).
* @param[in] rTokenList Reference to the token list this token resides in.
* @param[in] eBoundary The boundary type to define.
*/
explicit CToken(const TTokenList& rTokenList, EBoundary eBoundary);
/**
* @brief Constructs a new Token object with a given category (can be anything, except key, string, integer, boolean, time
* and error).
* @param[in] eCategory The initial token category value for the token to be constructed.
*/
explicit CToken(ETokenCategory eCategory);
/**
* @brief Constructs a new Token object with a given category (can only be a key, a string or an error).
* @param[in] eCategory The token category value for the token to be constructed.
* @param[in] rssContent Reference to the string value.
* @param[in] eStringType The type of string that should be used.
*/
CToken(ETokenCategory eCategory, const std::string& rssContent,
ETokenStringType eStringType = ETokenStringType::not_specified);
/**
* @brief Constructs a new Token object with a given category (can only be an integer value).
* @param[in] eCategory The token category value for the token to be constructed.
* @param[in] iContent The integer value.
*/
explicit CToken(ETokenCategory eCategory, int64_t iContent);
/**
* @brief Constructs a new Token object with a given category (can only be a floating point value).
* @param[in] eCategory The token category value for the token to be constructed.
* @param[in] dContent The double precision floating point value.
*/
explicit CToken(ETokenCategory eCategory, double dContent);
/**
* @brief Constructs a new Token object with a given category (can only be a boolean value).
* @param[in] eCategory The token category value for the token to be constructed
* @param[in] bContent The boolean value.
*/
explicit CToken(ETokenCategory eCategory, bool bContent);
/**
* @brief Error token.
* @param[in] eCategory The token category value for the token to be constructed (category error or terminated).
* @param[in] rexcept Reference to the exception that was triggered.
*/
CToken(ETokenCategory eCategory, const sdv::toml::XTOMLParseException& rexcept);
/**
* @brief Copy constructor.
* @param[in] rToken Reference to the token to copy from.
*/
CToken(const CToken& rToken);
/**
* @brief Move constructor.
* @param[in] rToken Reference to the token to move from.
*/
CToken(CToken&& rToken);
/**
* @brief Destructor
*/
~CToken();
/**
* @brief Copy assignment operator.
* @param[in] rToken Reference to the token to copy from.
* @return Reference to this token.
*/
CToken& operator=(const CToken& rToken);
/**
* @brief Move constructor.
* @param[in] rToken Reference to the token to move from.
* @return Reference to this token.
*/
CToken& operator=(CToken&& rToken);
/**
* @brief Check whether the supplied token is identical to this token. The tokens are identical if the token iterators of
* the token list are identical, or if they don't have a token iterator, have identical category and value.
* @param[in] rToken Reference to the token to use for the comparison.
* @return Returns whether the supplied token is identical.
*/
bool operator==(const CToken& rToken) const;
/**
* @brief Check whether the supplied token is not identical to this token. The tokens are identical if the token iterators
* of the token list are identical, or if they don't have a token iterator, have identical category and value.
* @param[in] rToken Reference to the token to use for the comparison.
* @return Returns whether the supplied token is not identical.
*/
bool operator!=(const CToken& rToken) const;
/**
* @brief Checks whether the token is initialized with any token other then ETokenCategory::token_none.
* @return Returns whether the token is initialized.
*/
operator bool() const;
/**
* @brief Quick navigation through the token list. Get the next token in the token list.
* @param[in] nSkip The amount of tokens to skip while getting the next token.
* @return Returns the next token or an none-token when the end of the token list has been reached or no tokenlist
* assignment has been made for this token.
*/
const CToken& Next(size_t nSkip = 0) const;
/**
* @brief Quick navigation through the token list. Get the previous token in the token list.
* @param[in] nSkip The amount of tokens to skip while getting the previous token.
* @return Returns the previous token or an none-token when the begin of the token list has been reached or no tokenlist
* assignment has been made for this token.
*/
const CToken& Prev(size_t nSkip = 0) const;
/**
* @brief Quick navigation through the token list to the begin of the token list.
* @return Returns the first token of the token list.
*/
const CToken& JumpToBegin() const;
/**
* @brief Quick navigation through the token list to the end of the token list.
* @remarks The last token of the token list will return "false" when tested. It can be used to iterate backwards through
* the token list.
* @return Returns the one beyond the last token of the token list.
*/
const CToken& JumpToEnd() const;
/**
* @brief Return the current category.
* @return The token category.
*/
ETokenCategory Category() const;
/**
* @brief Get the token string type.
* @return Returns the string type for the key or string token. Returns ETokenStringType::not_specified when there is no
* string type defined or the token isn't string based.
*/
ETokenStringType StringType() const;
/**
* @brief Get the string value. Will be filled for keys, strings and errors.
* @return The string value or empty string when the category type is invalid.
*/
std::string StringValue() const;
/**
* @brief Get the integer value. Will be filled for a integer number.
* @return The integer value or zero when the category type is invalid.
*/
int64_t IntegerValue() const;
/**
* @brief Get the floating point number value. Will be filled for a floating point number.
* @return The floating point number value value or 0.0 when the category type is invalid.
*/
double FloatValue() const;
/**
* @brief Get the boolean value. Will be filled for a boolean.
* @return The boolean value or false when the category type is invalid.
*/
bool BooleanValue() const;
/**
* @brief Get the associated raw string.
* @return Reference to the raw string.
*/
const std::string& RawString() const;
/**
* @brief The token index identifies the token order.
* @return The unique index of this token.
*/
uint32_t TokenIndex() const;
private:
/**
* @brief Set the string chunk from the original TOML belonging to this token and the location within the lexer token list.
* @param[in] rssString Reference to the string containing the "raw" value of the token.
* @param[in] rTokenList Reference to the token list the iterator is referring to.
* @param[in] ritLocation The location in the lexer list.
*/
void RawDataInfo(const std::string& rssString, const TTokenList& rTokenList, const TTokenListIterator& ritLocation);
/**
* @brief Return the location in the lexer token list.
* @return Reference to the iterator location option.
*/
const std::optional<TTokenListIterator>& Location() const;
/**
* @brief Return a reference to the token list this token is located in.
* @return Reference to the token list reference option.
*/
const std::optional<std::reference_wrapper<const TTokenList>>& TokenList() const;
/**
* @brief Create a new index. Indices are used to guarantee correct token order.
* @return The next index number.
*/
static uint32_t CreateIndex();
std::string m_ssRawString; ///< The raw string without interpretation.
std::optional<std::reference_wrapper<const TTokenList>> m_optTokenList; ///< The token list this token is referring to.
std::optional<TTokenListIterator> m_optLocation; ///< Location in the lexer token list.
union
{
std::string m_ssContentString; ///< Token string content (used with keys, strings and errors).
int64_t m_iContentInteger; ///< Token integer content
double m_dContentFloatingpoint; ///< Token floatingpoint content
bool m_bContentBoolean; ///< Token boolean content
};
ETokenCategory m_eCategory = ETokenCategory::token_none; ///< Token category
ETokenStringType m_eStringType = ETokenStringType::not_specified; ///< Token string type for key and string tokens.
uint32_t m_uiIndex = CreateIndex(); ///< Current token index.
};
/**
* @brief Token list with boundary tokens used in navigation.
*/
class CTokenList : public std::list<CToken>
{
public:
/**
* @brief Default constructor.
*/
CTokenList();
CToken tokenReverseEnd; ///< One token before first token in token list. Used to detect backwards iteration boundary.
CToken tokenEnd; ///< One token behind last token in token list. Used to detect forwards iteration boundary.
};
/**
* @brief The token range is used to identify the initial and the final token belonging to a collection. All intermediate
* tokens are then part of the collection.
*/
class CTokenRange
{
public:
/**
* @brief The default constructor is deleted.
*/
CTokenRange() = delete;
/**
* @brief Start a token range using the begin token.
* @remarks The end token is automatically set to one token beyond the last token in the list. This can be changed by
* assigning the end token.
* @param[in] rTokenBegin Reference to the first token in this range.
*/
CTokenRange(const CToken& rTokenBegin);
/**
* @brief Directly assign a token range.
* @remarks If the begin token and the end token are identical, the range doesn't contain any tokens.
* @param[in] rTokenBegin Reference to the first token in this range.
* @param[in] rTokenEnd Reference to one token beyond the last token in this range.
*/
CTokenRange(const CToken& rTokenBegin, const CToken& rTokenEnd);
/**
* @brief Reassign the first token in the range. This will set the end token to the one beyond the last token in the list.
* @param[in] rTokenBegin Reference to the first token in this range.
*/
void ReassignBeginToken(const CToken& rTokenBegin);
/**
* @brief Reassign the token range.
* @remarks If the begin token and the end token are identical, the range doesn't contain any tokens.
* @param[in] rTokenBegin Reference to the first token in the range.
* @param[in] rTokenEnd Reference to one token beyond the last token in the range.
*/
void ReassignTokenRange(const CToken& rTokenBegin, const CToken& rTokenEnd);
/**
* @brief Assign or overwrite an end token.
* @param[in] rTokenEnd Reference to the last token in this range.
* @param[in] bIncludeEnd When set, the token belongs to the range; if not, the token points past the last range token.
*/
void AssignEndToken(const CToken& rTokenEnd, bool bIncludeEnd = true);
/**
* @brief Get the initial token in the range.
* @return Reference to the smart pointer containing the first token in this range.
*/
const CToken& Begin() const;
/**
* @brief Get one token beyond the final token in the range.
* @remarks The end token can be one token beyond the last token in the list. This token will return 'false' when tested and
* can be used for navigation.
* @return Reference to the smart pointer containing one token beyond the final token in this range.
*/
const CToken& End() const;
/**
* @brief Create a copy of the token list part identified by the start and end token (not including the end token).
* @return The token list containing the slice.
*/
std::list<CToken> TokenListSLice() const;
/**
* @brief Does the token range contain a valid range of at least one token.
* @return Returns whether the token range is valid.
*/
operator bool() const;
private:
std::reference_wrapper<const CToken> m_rTokenBegin; ///< Smart pointer to the first token in the range.
std::reference_wrapper<const CToken> m_rTokenEnd; ///< Smart pointer to one token beyond the last token in the range.
};
} // namespace toml_parser
#endif // !defined LEXER_TOML_TOKEN_H

View File

@@ -0,0 +1,549 @@
#include "miscellaneous.h"
#include "exception.h"
#include <sstream>
#include <limits>
namespace toml_parser
{
std::string EscapedUnicodeCharacterToUTF8(const std::string& rss)
{
// Read the characters
uint32_t uiUTFVal = HexadecimalToDecimal(rss);
// One byte UTF-8 character
if (uiUTFVal < 0x80)
return std::string() + static_cast<char>(uiUTFVal);
// Two byte UTF-8 character
if (uiUTFVal < 0x800)
return std::string() + static_cast<char>(uiUTFVal >> 6 | 0xc0) + static_cast<char>((uiUTFVal & 0b111111) | 0x80);
// Three byte UTF-8 character
if (uiUTFVal < 0x10000)
return std::string() + static_cast<char>(uiUTFVal >> 12 | 0xe0) + static_cast<char>(((uiUTFVal >> 6) & 0b111111) | 0x80)
+ static_cast<char>((uiUTFVal & 0b111111) | 0x80);
// Four byte UTF-8 character
if (uiUTFVal < 0x110000)
{
return std::string() + static_cast<char>(uiUTFVal >> 18 | 0xf0)
+ static_cast<char>(((uiUTFVal >> 12) & 0b111111) | 0x80)
+ static_cast<char>(((uiUTFVal >> 6) & 0b111111) | 0x80) + static_cast<char>((uiUTFVal & 0b111111) | 0x80);
}
std::stringstream sstream;
sstream << "Invalid unicode conversion for hexadecimal number: " << rss;
throw XTOMLParseException(sstream.str());
}
uint32_t HexadecimalToDecimal(const std::string& rss)
{
if (rss.empty()) throw XTOMLParseException("Invalid hexadecimal number interpretation for string");
uint32_t uiVal = 0;
bool bInitial = true;
for (char c : rss)
{
if (c >= '0' && c <= '9')
uiVal = uiVal * 16u + static_cast<uint32_t>(c - '0');
else if (c >= 'A' && c <= 'F')
uiVal = uiVal * 16u + static_cast<uint32_t>(c - 'A' + 10u);
else if (c >= 'a' && c <= 'f')
uiVal = uiVal * 16u + static_cast<uint32_t>(c - 'a' + 10u);
else
{
// Not a correct value... only an error if it is the first digit
if (bInitial)
throw XTOMLParseException(std::string("Invalid hexadecimal number interpretation for string: ") + rss);
break;
}
bInitial = false;
if (static_cast<uint64_t>(uiVal) * 16u > std::numeric_limits<uint32_t>().max()) break;
}
return uiVal;
}
uint32_t DecimalToDecimal(const std::string& rss)
{
if (rss.empty()) throw XTOMLParseException("Invalid decimal number interpretation for string");
uint32_t uiVal = 0;
bool bInitial = true;
for (char c : rss)
{
if (c >= '0' && c <= '9')
uiVal = uiVal * 10u + static_cast<uint32_t>(c - '0');
else
{
// Not a correct value... only an error if it is the first digit
if (bInitial)
throw XTOMLParseException(std::string("Invalid decimal number interpretation for string: ") + rss);
break;
}
bInitial = false;
if (static_cast<uint64_t>(uiVal) * 10u > std::numeric_limits<uint32_t>().max()) break;
}
return uiVal;
}
uint32_t OctalToDecimal(const std::string& rss)
{
if (rss.empty()) throw XTOMLParseException("Invalid octal number interpretation for string");
uint32_t uiVal = 0;
bool bInitial = true;
for (char c : rss)
{
if (c >= '0' && c <= '7')
uiVal = uiVal * 8u + static_cast<uint32_t>(c - '0');
else
{
// Not a correct value... only an error if it is the first digit
if (bInitial)
throw XTOMLParseException(std::string("Invalid octal number interpretation for string: ") + rss);
break;
}
bInitial = false;
if (static_cast<uint64_t>(uiVal) * 8u > std::numeric_limits<uint32_t>().max()) break;
}
return uiVal;
}
uint32_t BinaryToDecimal(const std::string& rss)
{
if (rss.empty()) throw XTOMLParseException("Invalid binary number interpretation for string");
uint32_t uiVal = 0;
bool bInitial = true;
for (char c : rss)
{
if (c == '0')
uiVal = uiVal * 2u;
else if (c == '1')
uiVal = uiVal * 2u + static_cast<uint32_t>(c - '0');
else
{
// Not a correct value... only an error if it is the first digit
if (bInitial)
throw XTOMLParseException(std::string("Invalid binary number interpretation for string: ") + rss);
break;
}
bInitial = false;
if (static_cast<uint64_t>(uiVal) * 2u > std::numeric_limits<uint32_t>().max()) break;
}
return uiVal;
}
std::pair<std::string, std::string> SplitNodeKey(const std::string& rssKeyPath)
{
if (rssKeyPath.empty())
return {};
enum class EType
{
normal,
expect_separator,
single_quoted_string,
double_quoted_string,
expect_index,
expect_index_or_end_bracket
} eType = EType::normal;
size_t nPos = 0;
std::string ssFirst;
while (nPos < rssKeyPath.size())
{
char c = rssKeyPath[nPos];
switch (c)
{
case ' ':
case '\t':
// When inside a string, add the whitespace to the key.
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
{
ssFirst += c;
break;
}
if (eType == EType::normal && !ssFirst.empty()) // This is only allowed at the end of a key
eType = EType::expect_separator;
break;
case '\r':
case '\n':
if (eType == EType::normal && !ssFirst.empty()) // This is only allowed at the end of a key
eType = EType::expect_separator;
break;
case '\'':
if (eType == EType::normal)
{
if (!ssFirst.empty())
return {}; // Cannot be placed in the middle of the key
eType = EType::single_quoted_string;
}
else if (eType == EType::single_quoted_string)
eType = EType::expect_separator;
else if (eType == EType::double_quoted_string)
ssFirst += c;
else
return {}; // Invalid usage
break;
case '\"':
if (eType == EType::normal)
{
if (!ssFirst.empty())
return {}; // Cannot be placed in the middle of the key
eType = EType::double_quoted_string;
}
else if (eType == EType::double_quoted_string)
eType = EType::expect_separator;
else if (eType == EType::single_quoted_string)
ssFirst += c;
else
return {}; // Invalid usage
break;
case '\\':
if (eType == EType::single_quoted_string)
{
ssFirst += c;
break;
}
if (eType != EType::double_quoted_string) return {}; // Only allowed for double quoted strings.
nPos++;
if (nPos >= rssKeyPath.size()) return {}; // Ended with escape...
try
{
switch (rssKeyPath[nPos])
{
case 'b': ssFirst += '\b'; break;
case 't': ssFirst += '\t'; break;
case 'n': ssFirst += '\n'; break;
case 'f': ssFirst += '\f'; break;
case 'r': ssFirst += '\r'; break;
case '"': ssFirst += '\"'; break;
case '\\': ssFirst += '\\'; break;
case 'u': ssFirst += EscapedUnicodeCharacterToUTF8(rssKeyPath.substr(nPos + 1, 4)); nPos += 4; break;
case 'U': ssFirst += EscapedUnicodeCharacterToUTF8(rssKeyPath.substr(nPos + 1, 8)); nPos += 8; break;
default:
return {}; // Invalid escape sequence
}
}
catch (const sdv::toml::XTOMLParseException&)
{
return {};
}
break;
case '.':
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
{
ssFirst += c;
break;
}
if (ssFirst.empty()) break; // Ignore an initial dot.
if (eType != EType::normal && eType != EType::expect_separator)
return {}; // Unexpected separator
return std::make_pair(ssFirst, rssKeyPath.substr(nPos + 1));
case '[':
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
{
ssFirst += c;
break;
}
if (eType != EType::normal && eType != EType::expect_separator)
return {}; // Unexpected separator
if (!ssFirst.empty()) // Belongs to second part?
return std::make_pair(ssFirst, rssKeyPath.substr(nPos));
eType = EType::expect_index;
break;
case ']':
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
{
ssFirst += c;
break;
}
if (eType != EType::expect_index_or_end_bracket) // Allowed?
return {};
eType = EType::expect_separator; // Expected a separator to follow
break;
default:
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
ssFirst += c;
else if ((eType == EType::expect_index || eType == EType::expect_index_or_end_bracket) && std::isdigit(c))
{
ssFirst += c;
eType = EType::expect_index_or_end_bracket;
}
// Protect against multi-byte characters (UTF-8)
else if (eType == EType::normal && static_cast<uint8_t>(c) < 127u && (std::isalnum(c) || c == '-' || c == '_'))
ssFirst += c;
else
return {}; // Invalid key character
break;
}
nPos++;
}
// When within quotes, this is an error
if (eType == EType::single_quoted_string || eType == EType::double_quoted_string)
return {};
// When expecting index and/or end bracket, this is an error
if (eType == EType::expect_index || eType == EType::expect_index_or_end_bracket)
return {};
// When coming here, there is no more character in the string.
return std::make_pair(ssFirst, std::string());
}
std::pair<std::reference_wrapper<const CToken>, CTokenRange> SplitNodeKey(const CTokenRange& rrangeKeyPath)
{
std::reference_wrapper<const CToken> refToken = rrangeKeyPath.Begin();
std::reference_wrapper<const CToken> refTokenFirst = refToken;
enum class EState
{
initial_key_or_index_marker,
dot_or_index_marker_or_end,
key,
index,
index_marker_close,
} eState = EState::initial_key_or_index_marker;
while (refToken.get() != rrangeKeyPath.End())
{
switch (refToken.get().Category())
{
case ETokenCategory::token_whitespace:
case ETokenCategory::token_comment:
case ETokenCategory::token_syntax_new_line:
break;
case ETokenCategory::token_key:
if (eState == EState::initial_key_or_index_marker)
refTokenFirst = refToken;
else if (eState == EState::key)
return std::make_pair(refTokenFirst, CTokenRange(refToken.get(), rrangeKeyPath.End()));
else
throw XTOMLParseException("Internal error: invalid token range, expecting key.");
eState = EState::dot_or_index_marker_or_end;
break;
case ETokenCategory::token_syntax_dot:
if (eState != EState::dot_or_index_marker_or_end)
throw XTOMLParseException("Internal error: invalid token range, expecting dot.");
eState = EState::key;
break;
case ETokenCategory::token_syntax_array_open:
if (eState == EState::initial_key_or_index_marker)
eState = EState::index;
else if (eState == EState::dot_or_index_marker_or_end)
return std::make_pair(refTokenFirst, CTokenRange(refToken, rrangeKeyPath.End()));
else
throw XTOMLParseException("Internal error: invalid token range, unexpected array open.");
break;
case ETokenCategory::token_integer:
if (eState != EState::index)
throw XTOMLParseException("Internal error: invalid token range, unexpected index.");
refTokenFirst = refToken;
eState = EState::index_marker_close;
break;
case ETokenCategory::token_syntax_array_close:
if (eState != EState::index_marker_close)
throw XTOMLParseException("Internal error: invalid token range, unexpected array close.");
eState = EState::dot_or_index_marker_or_end;
break;
default:
throw XTOMLParseException("Internal error: invalid token range, unexpected token.");
}
refToken = refToken.get().Next();
}
// Coming here would mean that there was one key, but nothing more
if (eState != EState::dot_or_index_marker_or_end)
throw XTOMLParseException("Internal error: invalid token range, unexpected end.");
// Return a pair with the first token and an empty token range.
return std::make_pair(refTokenFirst, CTokenRange(refToken.get(), refToken.get()));
}
std::string ExtractKeyName(const std::string& rssKeyPath)
{
// Split the key parh until there is no second part any more.
auto prSplittedKey = SplitNodeKey(rssKeyPath);
while (!prSplittedKey.second.empty())
prSplittedKey = SplitNodeKey(prSplittedKey.second);
return prSplittedKey.first;
}
std::string QuoteText(const std::string& rssText, EQuoteRequest eQuoteRequest /*= EQuoteRequest::smart_key*/)
{
EQuoteRequest eQuoteNeeded = eQuoteRequest;
if (rssText.empty() && eQuoteNeeded == EQuoteRequest::smart_key)
eQuoteNeeded = EQuoteRequest::quoted_text;
std::stringstream sstreamQuotedText;
sstreamQuotedText << "\"";
for (size_t nPos = 0; nPos < rssText.size(); nPos++)
{
uint8_t uiChar = static_cast<uint8_t>(rssText[nPos]);
uint32_t uiUTFChar = 0;
switch (uiChar)
{
case '\'':
if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text &&
eQuoteRequest != EQuoteRequest::multi_line_literal_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << '\'';
break; // Single quote character
case '\b':
if (eQuoteRequest == EQuoteRequest::multi_line_literal_text)
eQuoteNeeded = EQuoteRequest::multi_line_quoted_text;
else if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\b";
break; // Escape backspace
case '\t':
if (eQuoteRequest == EQuoteRequest::multi_line_literal_text)
eQuoteNeeded = EQuoteRequest::multi_line_quoted_text;
else if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\t";
break; // Escape tab
case '\n':
if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text &&
eQuoteRequest != EQuoteRequest::multi_line_literal_text)
{
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\n";
}
else
sstreamQuotedText << "\n";
break; // Escape linefeed
case '\f':
if (eQuoteRequest == EQuoteRequest::multi_line_literal_text)
eQuoteNeeded = EQuoteRequest::multi_line_quoted_text;
else if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\f";
break; // Escape form feed
case '\r':
if (eQuoteRequest != EQuoteRequest::multi_line_quoted_text &&
eQuoteRequest != EQuoteRequest::multi_line_literal_text)
{
eQuoteNeeded = EQuoteRequest::quoted_text;
sstreamQuotedText << "\\r";
} else
sstreamQuotedText << "\r";
break; // Escape carriage return
case '\"':
if (eQuoteNeeded == EQuoteRequest::smart_key)
eQuoteNeeded = EQuoteRequest::quoted_text;
else if (eQuoteNeeded == EQuoteRequest::smart_text)
eQuoteNeeded = EQuoteRequest::literal_text;
sstreamQuotedText << "\\\"";
break; // Escape quote
case '\\':
if (eQuoteNeeded == EQuoteRequest::smart_key)
eQuoteNeeded = EQuoteRequest::quoted_text;
else if (eQuoteNeeded == EQuoteRequest::smart_text)
eQuoteNeeded = EQuoteRequest::literal_text;
sstreamQuotedText << "\\\\";
break; // Escape backslash
default:
// Check for ASCII character
if (uiChar >= 0x20 && uiChar < 0x7f)
{
// Standard ASCII
sstreamQuotedText << static_cast<char>(uiChar);
if (!std::isalnum(static_cast<char>(uiChar)) && uiChar != '-' && uiChar != '_')
{
if (eQuoteNeeded == EQuoteRequest::smart_key || eQuoteNeeded == EQuoteRequest::smart_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
}
break;
}
// Not a standard ASCII character
if (eQuoteNeeded == EQuoteRequest::smart_key || eQuoteNeeded == EQuoteRequest::smart_text)
eQuoteNeeded = EQuoteRequest::quoted_text;
else if (eQuoteNeeded == EQuoteRequest::literal_text && (uiChar < 0x20 || uiChar == 0x7f))
{
// If the character is one of the control characters (< 0x20 || == 0x7f) then must be quoted
eQuoteNeeded = EQuoteRequest::quoted_text;
}
else if (eQuoteNeeded == EQuoteRequest::multi_line_literal_text && (uiChar < 0x20 || uiChar == 0x7f))
{
// If the character is one of the control characters (< 0x20 || == 0x7f) then must be quoted
eQuoteNeeded = EQuoteRequest::multi_line_quoted_text;
}
// Use UNICODE escape character for the quoted text
if (uiChar <= 0x80) // One byte UTF-8
uiUTFChar = static_cast<uint32_t>(uiChar);
else if (uiChar <= 0xDF) // Two bytes UTF-8
{
uiUTFChar = static_cast<size_t>(uiChar & 0b00011111) << 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
}
else if (uiChar <= 0xEF) // Three bytes UTF-8
{
uiUTFChar = static_cast<size_t>(uiChar & 0b00001111) << 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
uiUTFChar <<= 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
}
else if (uiChar <= 0xF7) // Four bytes UTF-8
{
uiUTFChar = static_cast<size_t>(uiChar & 0b00000111) << 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
uiUTFChar <<= 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
uiUTFChar <<= 6;
// Expecting the next character to be between 0x80 and 0xBF
nPos++;
if (nPos >= rssText.size())
break;
uiUTFChar |= static_cast<size_t>(rssText[nPos] & 0b00111111);
}
// Stream the UTF character
if (uiUTFChar <= 0xFFFF)
sstreamQuotedText << "\\u" << std::uppercase << std::hex << std::setfill('0') << std::setw(4) << uiUTFChar;
else
sstreamQuotedText << "\\U" << std::uppercase << std::hex << std::setfill('0') << std::setw(8) << uiUTFChar;
break;
}
}
sstreamQuotedText << "\"";
// Return depedent of the needed quotation.
switch (eQuoteNeeded)
{
case EQuoteRequest::smart_key:
return rssText; // No need to change
case EQuoteRequest::literal_text:
return "'" + rssText + "'";
case EQuoteRequest::multi_line_literal_text:
return "'''" + rssText + "'''";
case EQuoteRequest::multi_line_quoted_text:
return "\"\"" + sstreamQuotedText.str() + "\"\"";
case EQuoteRequest::smart_text:
case EQuoteRequest::quoted_text:
default:
return sstreamQuotedText.str();
}
}
} // namespace toml_parser

View File

@@ -0,0 +1,99 @@
#ifndef MISCELLANEOUS_H
#define MISCELLANEOUS_H
#include <string>
#include <cstdint>
#include <cctype>
#include <utility>
#include "lexer_toml_token.h"
/// The TOML parser namespace
namespace toml_parser
{
/**
* @brief Implementation of hexadecimal number to UTF8 unicode character.
* @param[in] rss Reference to the string providing the hexadecimal unicode number.
* @return The interpreted unicode character.
*/
std::string EscapedUnicodeCharacterToUTF8(const std::string& rss);
/**
* @brief Calculate the interpretation value of a hexadecimal number in a string (characters 0-9a-fA-F).
* @param[in] rss Reference to the string containing the number.
* @return The calculated value.
*/
uint32_t HexadecimalToDecimal(const std::string& rss);
/**
* @brief Calculate the interpretation value of a decimal number in a string (characters 0-9).
* @param[in] rss Reference to the string containing the number.
* @return The calculated value.
*/
uint32_t DecimalToDecimal(const std::string& rss);
/**
* @brief Calculate the interpretation value of an octal number in a string (characters 0-7).
* @param[in] rss Reference to the string containing the number.
* @return The calculated value.
*/
uint32_t OctalToDecimal(const std::string& rss);
/**
* @brief Calculate the interpretation value of a binary number in a string (characters 0-1).
* @param[in] rss Reference to the string containing the number.
* @return The calculated value.
*/
uint32_t BinaryToDecimal(const std::string& rss);
/**
* @brief Split the node key in a first node key and the rest of the node key (if available). Separators are the dot, for table
* separation and [] index for array separation.
* @param[in] rssKeyPath Reference to the string containing the key path.
* @return Pair of string containing the first key (unquoted) and the rest of the key string (still quoted) if applicable. If
* no key is available, will be a reference to two empty strings. The dot and the indexing markers will not be part of the
* returned first string.
*/
std::pair<std::string, std::string> SplitNodeKey(const std::string& rssKeyPath);
/**
* @brief Split the node key contained in the token range in a first node key and the rest of the node key (if available).
* Separators are the dot, for table separation and [] index for array separation.
* @param[in] rrangeKeyPath Reference to the token range ccontaining the tokens representing the key path.
* @return Pair of string containing the first key (unquoted) and the rest of the key string (still quoted) if applicable. If
* no key is available, will be a reference to two empty strings. The dot and the indexing markers will not be part of the
* returned token.
*/
std::pair<std::reference_wrapper<const CToken>, CTokenRange> SplitNodeKey(const CTokenRange& rrangeKeyPath);
/**
* @brief Extract the unquoted key name from a key path. The key name is the last node on the key path.
* @remarks Removes quotes, escape characters and index markers from the key name before returning.
* @param[in] rssKeyPath Reference to the string containing the key path.
* @return The unquoted key name.
*/
std::string ExtractKeyName(const std::string& rssKeyPath);
/**
* @brief Quotation request enum for quoting text (or keys).
*/
enum class EQuoteRequest
{
smart_key, ///< Uses quotation markers and escape characters if needed.
smart_text, ///< Use literal markers if possible, use quotation markers if needed.
quoted_text, ///< Will always use quotation markers.
literal_text, ///< Will use literal markers if possible; otherwise uses quotation markers.
multi_line_quoted_text, ///< Multiple lines quoted text (carriage return and newline are not escaped).
multi_line_literal_text, ///< Multiple lines literal text (carriage return and newline are not escaped).
};
/**
* @brief If the provided key contains characters which are not allowed to be used in a bare-key, add quotes to the key and
* use escape characters if applicable.
* @param[in] rssText The text to (optionally) quote.
* @param[in] eQuoteRequest The quotation method requested.
* @return The quoted key (if applicable); otherwise the key without quotes.
*/
std::string QuoteText(const std::string& rssText, EQuoteRequest eQuoteRequest = EQuoteRequest::smart_text);
} // namespace toml_parser
#endif // !defined MISCELLANEOUS_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,51 +1,82 @@
#include "parser_toml.h" #include "parser_toml.h"
#include <iostream> #include <iostream>
#include "miscellaneous.h"
#include "exception.h" #include "exception.h"
CParserTOML::CParserTOML(const std::string& rssString) : m_lexer(rssString) /// The TOML parser namespace
namespace toml_parser
{
CParser::CParser(const std::string& rssString)
{ {
Process(rssString); Process(rssString);
} }
void CParserTOML::Clear() void CParser::Clear()
{ {
m_ptrRoot = std::make_shared<CRootTable>(); m_ptrRoot.reset();
m_ssCurrentTable.clear(); m_ptrCurrentCollection.reset();
m_lexer.Reset(); m_lexer.Reset();
while (!m_stackEnvironment.empty()) m_stackEnvironment.pop(); while (!m_stackEnvironment.empty())
m_stackEnvironment.pop();
} }
bool CParserTOML::Process(/*in*/ const sdv::u8string& ssContent) bool CParser::Process(/*in*/ const sdv::u8string& ssContent)
{ {
// Process the TOML string
Clear(); Clear();
m_lexer.Feed(ssContent); m_lexer.Feed(ssContent);
// Create the root node.
m_ptrRoot = std::make_shared<CRootTable>(*this);
m_ptrCurrentCollection = m_ptrRoot;
// Nodes available at all?
if (m_lexer.IsEnd()) return true;
// The initial start position for the node token is the begin of the token list.
std::reference_wrapper<const CToken> refStartNodeToken = m_lexer.Peek().JumpToBegin();
CNodeTokenRange rangeRootTokens(refStartNodeToken);
try try
{ {
// Run through all tokens of the lexer and process the tokens. // Run through all tokens of the lexer and process the tokens.
while (!m_lexer.IsEnd()) bool bEOF = false; // Explicit test, since the peek could return EOF, but the cursor might not be at the end yet.
while (!bEOF && !m_lexer.IsEnd())
{ {
CLexerTOML::SToken current = m_lexer.Peek(); // The node has its own token range
switch (current.eCategory) CNodeTokenRange rangeNode(refStartNodeToken.get());
const CToken& rCurrent = m_lexer.Peek();
if (!rCurrent)
{ {
case CLexerTOML::ETokenCategory::token_syntax_table_open: bEOF = true;
ProcessTable();
break; break;
case CLexerTOML::ETokenCategory::token_syntax_table_array_open: }
ProcessTableArray(); switch (rCurrent.Category())
{
case ETokenCategory::token_syntax_table_open:
ProcessTable(rangeNode);
break; break;
case CLexerTOML::ETokenCategory::token_key: case ETokenCategory::token_syntax_table_array_open:
ProcessValueKey(); ProcessTableArray(rangeNode);
break; break;
case CLexerTOML::ETokenCategory::token_syntax_new_line: case ETokenCategory::token_key:
ProcessValueKey(rangeNode);
break;
case ETokenCategory::token_syntax_new_line:
m_lexer.Consume(); m_lexer.Consume();
break; break;
case CLexerTOML::ETokenCategory::token_terminated: case ETokenCategory::token_terminated:
case CLexerTOML::ETokenCategory::token_error: case ETokenCategory::token_error:
throw XTOMLParseException(current.ssContentString); throw XTOMLParseException(rCurrent.StringValue());
break; break;
default: default:
throw XTOMLParseException("Invalid Syntax; not a Key, Table or Tablearray"); throw XTOMLParseException("Invalid Syntax; not a Key, Table or Tablearray");
} }
// Update the start of the next value range
refStartNodeToken = rangeNode.LinesBehindNode().End();
} }
} }
catch (const sdv::toml::XTOMLParseException& e) catch (const sdv::toml::XTOMLParseException& e)
@@ -53,217 +84,245 @@ bool CParserTOML::Process(/*in*/ const sdv::u8string& ssContent)
std::cout << e.what() << '\n'; std::cout << e.what() << '\n';
throw; throw;
} }
// In case there are no nodes any more, but still comments and whitespace, attach this to the root node.
if (refStartNodeToken.get() != m_lexer.Peek())
{
rangeRootTokens.NodeMain(CTokenRange(refStartNodeToken, refStartNodeToken));
rangeRootTokens.LinesBehindNode(m_lexer.Peek());
m_ptrRoot->UpdateNodeCode(rangeRootTokens);
}
return true; return true;
} }
const CNodeCollection& CParserTOML::GetRoot() const CLexer& CParser::Lexer()
{ {
auto ptrCollection = m_ptrRoot->GetTable(); return m_lexer;
}
const CNodeCollection& CParser::Root() const
{
auto ptrCollection = m_ptrRoot->Cast<CTable>();
return *ptrCollection.get(); return *ptrCollection.get();
} }
CNodeCollection& CParserTOML::GetRoot() CNodeCollection& CParser::Root()
{ {
auto ptrCollection = m_ptrRoot->GetTable(); auto ptrCollection = m_ptrRoot->Cast<CTable>();
return *ptrCollection.get(); return *ptrCollection.get();
} }
std::string CParserTOML::CreateTOMLText(const std::string& rssParent) const std::string CParser::GenerateTOML(const std::string& rssPrefixKey) const
{ {
std::string ssLastPrintedTable; return m_ptrRoot->GenerateTOML(rssPrefixKey);
return m_ptrRoot->CreateTOMLText(rssParent, ssLastPrintedTable);
} }
bool CParserTOML::Add(const std::string& rssPath, bool bVal) void CParser::ProcessTable(CNodeTokenRange& rNodeRange)
{
size_t nOffset = FindLast(rssPath);
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<CBooleanNode>(ssName, bVal));
return true;
}
bool CParserTOML::Add(const std::string& rssPath, int64_t iVal)
{
size_t nOffset = FindLast(rssPath);
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<CIntegerNode>(ssName, iVal));
return true;
}
bool CParserTOML::Add(const std::string& rssPath, double dVal)
{
size_t nOffset = FindLast(rssPath);
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<CFloatingPointNode>(ssName, dVal));
return true;
}
bool CParserTOML::Add(const std::string& rssPath, const std::string& rssVal)
{
size_t nOffset = FindLast(rssPath);
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<CStringNode>(ssName, rssVal));
return true;
}
void CParserTOML::ProcessTable()
{ {
// Get the table path (table name preceded by parent tables separated with dots). // Get the table path (table name preceded by parent tables separated with dots).
m_lexer.Consume(); CTokenRange rangeMain(m_lexer.Consume());
std::string ssPath = ComposePath(); CTokenRange rangeKeyPath = ProcessKeyPath();
CLexerTOML::SToken sToken = m_lexer.Consume(); const CToken& rToken = m_lexer.Consume();
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_table_close) if (!rToken || rToken.Category() != ETokenCategory::token_syntax_table_close)
{ throw XTOMLParseException("Invalid Table construct");
throw XTOMLParseException("invalid Table construct"); rangeMain.AssignEndToken(rToken); // NOTE: This includes only the name and the brackets, not the values.
}
// Find the last dot - the name follows // Assign the main token range to the node token range and let the lexer determine the extended token range
size_t nOffset = FindLast(ssPath); rNodeRange.NodeMain(rangeMain);
if (nOffset == std::string::npos) m_lexer.SmartExtendNodeRange(rNodeRange);
nOffset = 0; // No dot found, the whole path is one table name
else
nOffset++; // Skip the dot
std::string ssName = ssPath.substr(nOffset);
// Add the table to the root // Add the table to the root
m_ptrRoot->Add(ssPath, std::make_shared<CNormalTable>(ssName), false); auto ptrTable = m_ptrRoot->Insert<CTable>(sdv::toml::npos, rangeKeyPath, false);
if (ptrTable)
m_ssCurrentTable = ssPath;
}
void CParserTOML::ProcessTableArray()
{ {
m_lexer.Consume(); m_ptrCurrentCollection = ptrTable->Cast<CTable>();
std::string rssKeyPath = ComposePath(); ptrTable->UpdateNodeCode(rNodeRange);
auto ptrNode = m_ptrRoot->Find(rssKeyPath);
if (!ptrNode)
{
Add<CTableArray>(rssKeyPath);
ptrNode = m_ptrRoot->Find(rssKeyPath);
}
if (!ptrNode) return;
if (dynamic_cast<CTableArray*>(ptrNode.get()))
ptrNode->GetArray()->AddElement(std::make_shared<CNormalTable>(""));
else
throw XTOMLParseException(("'" + rssKeyPath + "' already defined as a non-table-array").c_str());
m_ssCurrentTable = rssKeyPath;
CLexerTOML::SToken sToken = m_lexer.Consume();
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_table_array_close)
{
throw XTOMLParseException("invalid Table Array construct");
} }
} }
void CParserTOML::ProcessValueKey() void CParser::ProcessTableArray(CNodeTokenRange& rNodeRange)
{ {
std::string rssKeyPath = (m_ssCurrentTable.empty() ? "" : (m_ssCurrentTable + ".")) + ComposePath(); CTokenRange rangeMain(m_lexer.Consume());
CTokenRange rangeKeyPath = ProcessKeyPath();
const CToken& rToken = m_lexer.Consume();
if (!rToken || rToken.Category() != ETokenCategory::token_syntax_table_array_close)
throw XTOMLParseException("Invalid Table Array construct");
rangeMain.AssignEndToken(rToken); // NOTE: This includes only the name and the brackets, not the values.
CLexerTOML::SToken sToken = m_lexer.Consume(); // Assign the main token range to the node token range and let the lexer determine the extended token range
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_assignment) rNodeRange.NodeMain(rangeMain);
m_lexer.SmartExtendNodeRange(rNodeRange);
// Add the table array to the root
auto ptrTableArray = m_ptrRoot->Insert<CTableArray>(sdv::toml::npos, rangeKeyPath);
if (ptrTableArray)
{ {
m_ptrCurrentCollection = ptrTableArray->Cast<CNodeCollection>();
ptrTableArray->UpdateNodeCode(rNodeRange);
}
}
void CParser::ProcessValueKey(CNodeTokenRange& rNodeRange)
{
// Initial part of the main token range containing the key and the assignment
CTokenRange rangeMain(m_lexer.Peek());
CTokenRange rangeKeyPath = ProcessKeyPath();
const CToken& rToken = m_lexer.Consume();
if (rToken.Category() != ETokenCategory::token_syntax_assignment)
throw XTOMLParseException("Assignment expected"); throw XTOMLParseException("Assignment expected");
rangeMain.AssignEndToken(rToken);
// Store this initial range
rNodeRange.NodeMain(rangeMain);
// Process the value assignment
ProcessValue(rangeKeyPath, rNodeRange);
} }
ProcessValue(rssKeyPath); void CParser::ProcessValue(const CTokenRange& rrangeKeyPath, CNodeTokenRange& rNodeRange)
} {
// Extend the main range
CTokenRange rangeMain = rNodeRange.NodeMain();
void CParserTOML::ProcessValue(const std::string& rssKeyPath) // Skip newlines (other whitespace and comments are already skipped)
while (m_lexer.Peek().Category() == ETokenCategory::token_syntax_new_line)
m_lexer.Consume();
// Get the value
const CToken& rAssignmentValue = m_lexer.Consume();
if (!rAssignmentValue)
throw XTOMLParseException("Missing value");
// Assign the end token for the main part
rangeMain.AssignEndToken(rAssignmentValue);
rNodeRange.NodeMain(rangeMain);
// Process the value
std::shared_ptr<CNode> ptrNode;
switch (rAssignmentValue.Category())
{ {
CLexerTOML::SToken assignmentValue = m_lexer.Consume(); case ETokenCategory::token_boolean:
switch (assignmentValue.eCategory) ptrNode = m_ptrCurrentCollection->Insert<CBooleanNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.BooleanValue(),
rAssignmentValue.RawString());
break;
case ETokenCategory::token_integer:
ptrNode = m_ptrCurrentCollection->Insert<CIntegerNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.IntegerValue(),
rAssignmentValue.RawString());
break;
case ETokenCategory::token_float:
ptrNode = m_ptrCurrentCollection->Insert<CFloatingPointNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.FloatValue(),
rAssignmentValue.RawString());
break;
case ETokenCategory::token_string:
switch (rAssignmentValue.StringType())
{ {
case CLexerTOML::ETokenCategory::token_boolean: case ETokenStringType::literal_string:
Add(rssKeyPath, assignmentValue.bContentBoolean); ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(),
CStringNode::EQuotationType::literal_string, rAssignmentValue.RawString());
break; break;
case CLexerTOML::ETokenCategory::token_integer: case ETokenStringType::multi_line_literal:
Add(rssKeyPath, assignmentValue.iContentInteger); ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_literal,
rAssignmentValue.RawString());
break; break;
case CLexerTOML::ETokenCategory::token_float: case ETokenStringType::multi_line_quoted:
Add(rssKeyPath, assignmentValue.dContentFloatingpoint); ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_quoted,
rAssignmentValue.RawString());
break; break;
case CLexerTOML::ETokenCategory::token_string: case ETokenStringType::quoted_string:
Add(rssKeyPath, assignmentValue.ssContentString); default:
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::quoted_string,
rAssignmentValue.RawString());
break; break;
case CLexerTOML::ETokenCategory::token_syntax_array_open: }
Add<CNormalArray>(rssKeyPath); break;
case ETokenCategory::token_syntax_array_open:
{
auto ptrCurrentCollectionStored = m_ptrCurrentCollection;
ptrNode = m_ptrCurrentCollection->Insert<CArray>(sdv::toml::npos, rrangeKeyPath);
m_ptrCurrentCollection = ptrNode->Cast<CNodeCollection>();
m_stackEnvironment.push(EEnvironment::env_array); m_stackEnvironment.push(EEnvironment::env_array);
ProcessArray(rssKeyPath); ProcessArray(rNodeRange);
m_stackEnvironment.pop(); m_stackEnvironment.pop();
m_ptrCurrentCollection = ptrCurrentCollectionStored;
}
break; break;
case CLexerTOML::ETokenCategory::token_syntax_inline_table_open: case ETokenCategory::token_syntax_inline_table_open:
Add<CInlineTable>(rssKeyPath); {
auto ptrCurrentCollectionStored = m_ptrCurrentCollection;
ptrNode = m_ptrCurrentCollection->Insert<CTable>(sdv::toml::npos, rrangeKeyPath, true);
m_ptrCurrentCollection = ptrNode->Cast<CNodeCollection>();
m_stackEnvironment.push(EEnvironment::env_inline_table); m_stackEnvironment.push(EEnvironment::env_inline_table);
ProcessInlineTable(rssKeyPath); ProcessInlineTable(rNodeRange);
m_stackEnvironment.pop(); m_stackEnvironment.pop();
m_ptrCurrentCollection = ptrCurrentCollectionStored;
}
break; break;
default: default:
throw XTOMLParseException("Missing value"); throw XTOMLParseException("Missing value");
break; break;
} }
CLexerTOML::SToken sToken = m_lexer.Peek(); // let the lexer determine the extended token range and update the node
m_lexer.SmartExtendNodeRange(rNodeRange);
// Deal with the next value if expecting
std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
if (!m_stackEnvironment.empty()) if (!m_stackEnvironment.empty())
{ {
// Skip newlines
while (refToken.get().Category() == ETokenCategory::token_syntax_new_line)
{
m_lexer.Consume();
refToken = m_lexer.Peek();
}
switch (m_stackEnvironment.top()) switch (m_stackEnvironment.top())
{ {
case EEnvironment::env_array: case EEnvironment::env_array:
{ {
int32_t index = 2; int32_t index = 1;
while (sToken.eCategory == CLexerTOML::ETokenCategory::token_syntax_new_line) while (refToken.get() && refToken.get().Category() == ETokenCategory::token_syntax_new_line)
{ {
sToken = m_lexer.Peek(index++); refToken = m_lexer.Peek(index++);
} }
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_comma if (!refToken.get()
&& sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_array_close) || (refToken.get().Category() != ETokenCategory::token_syntax_comma
&& refToken.get().Category() != ETokenCategory::token_syntax_array_close))
{ {
throw XTOMLParseException( throw XTOMLParseException("Invalid Token after value assignment in array; ',' or ']' needed");
"Invalid Token after value assignment in array; ',' or ']' needed");
} }
} }
break; break;
case EEnvironment::env_inline_table: case EEnvironment::env_inline_table:
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_comma if (!refToken.get()
&& sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_inline_table_close) || (refToken.get().Category() != ETokenCategory::token_syntax_comma
&& refToken.get().Category() != ETokenCategory::token_syntax_inline_table_close))
{ {
throw XTOMLParseException( throw XTOMLParseException("Invalid Token after value assignment in inline table; ',' or '}' needed ");
"Invalid Token after value assignment in inline table; ',' or '}' needed ");
} }
break; break;
default: default:
break; break;
} }
rNodeRange.LinesBehindNode(refToken);
} }
else else
{ {
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_new_line && sToken.eCategory != CLexerTOML::ETokenCategory::token_eof) if (refToken.get() && refToken.get().Category() != ETokenCategory::token_syntax_new_line)
{ {
throw XTOMLParseException("Invalid Token after value assignment; newline needed"); throw XTOMLParseException("Invalid Token after value assignment; newline needed");
} }
} }
if (ptrNode) ptrNode->UpdateNodeCode(rNodeRange);
} }
void CParserTOML::ProcessArray(const std::string& rssKeyPath) void CParser::ProcessArray(CNodeTokenRange& rNodeRange)
{ {
/* /*
Arrays are defined as follow: array_name = [value, value, ...] Arrays are defined as follow: array_name = [value, value, ...]
@@ -278,40 +337,70 @@ void CParserTOML::ProcessArray(const std::string& rssKeyPath)
string_array = [ "all", 'strings', """are the same""", '''type''' ] string_array = [ "all", 'strings', """are the same""", '''type''' ]
*/ */
CLexerTOML::SToken sToken = m_lexer.Peek(); // The initial start position for the value token is the end of the first main part.
std::reference_wrapper<const CToken> refStartValueToken = rNodeRange.LinesBehindNode().End();
// Iterator token
std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
// State machine
enum class EExpect
{
value_or_end,
comma_or_end // Trailing comma is allowed
} eExpect = EExpect::value_or_end;
// Iterate through the values
size_t nIndex = 0; size_t nIndex = 0;
enum class EExpect {value_comma_end, comma_end} eExpect = EExpect::value_comma_end; bool bAdditionalCommaBeforeEnd = false;
while (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_array_close) std::reference_wrapper<const CToken> refCommaToken = m_lexer.Peek();
while (refToken.get() && refToken.get().Category() != ETokenCategory::token_syntax_array_close)
{ {
switch (sToken.eCategory) // The value has its own token range
CNodeTokenRange rangeNode(refStartValueToken.get());
switch (refToken.get().Category())
{ {
//case CLexerTOML::ETokenCategory::token_syntax_array_open: // Embedded array case ETokenCategory::token_syntax_new_line:
// if (eExpect == comma_end) throw XTOMLParseException("Expecting comma or table end.");
// m_lexer.Consume();
// ProcessArray(rssKeyPath + "." + std::to_string(nIndex++));
// eExpect = comma_end;
// break;
case CLexerTOML::ETokenCategory::token_syntax_new_line:
m_lexer.Consume(); m_lexer.Consume();
break; break;
case CLexerTOML::ETokenCategory::token_syntax_comma: case ETokenCategory::token_syntax_comma:
if (eExpect == EExpect::value_or_end) throw XTOMLParseException("Expecting value or array end.");
m_lexer.Consume(); m_lexer.Consume();
eExpect = EExpect::value_comma_end; eExpect = EExpect::value_or_end;
refCommaToken = refToken;
bAdditionalCommaBeforeEnd = true;
break; break;
default: default:
if (eExpect == EExpect::comma_end) {
throw XTOMLParseException("Expecting comma or table end."); bAdditionalCommaBeforeEnd = false;
ProcessValue(rssKeyPath + "." + std::to_string(nIndex++)); if (eExpect == EExpect::comma_or_end) throw XTOMLParseException("Expecting comma or array end.");
eExpect = EExpect::comma_end; CLexer lexerLocal("[" + std::to_string(nIndex++) + "]", true);
CTokenRange rangeIndexKey(lexerLocal.Peek(), lexerLocal.Peek().JumpToEnd());
rangeNode.NodeMain(CTokenRange(refToken, refToken.get().Next()));
ProcessValue(rangeIndexKey, rangeNode);
eExpect = EExpect::comma_or_end;
// Update the start of the next value range
refStartValueToken = rangeNode.LinesBehindNode().End();
break; break;
} }
sToken = m_lexer.Peek();
} }
// Get the next value
refToken = m_lexer.Peek();
}
// This is the node token range finishing main part.
rNodeRange.NodeMainFinish(
CTokenRange(bAdditionalCommaBeforeEnd ? refStartValueToken.get() : refToken.get(), refToken.get().Next()));
// Consume the token
m_lexer.Consume(); m_lexer.Consume();
} }
void CParserTOML::ProcessInlineTable(const std::string& rssKeyPath) void CParser::ProcessInlineTable(CNodeTokenRange& rNodeRange)
{ {
/* /*
Inline tables are defined as follow: table_name = {value, value, ...} Inline tables are defined as follow: table_name = {value, value, ...}
@@ -321,60 +410,98 @@ void CParserTOML::ProcessInlineTable(const std::string& rssKeyPath)
animal = { type.name = "pug" } animal = { type.name = "pug" }
*/ */
CLexerTOML::SToken sToken = m_lexer.Peek(); // The initial start position for the value token is the end of the first main part.
std::reference_wrapper<const CToken> refStartValueToken = rNodeRange.LinesBehindNode().End();
std::string ssCurrentTableTemp = m_ssCurrentTable; // Iterator token
m_ssCurrentTable = rssKeyPath; std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
enum class EExpect { value_comma_end, value, comma_end } eExpect = EExpect::value_comma_end;
while (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_inline_table_close) // State machine
enum class EExpect
{ {
switch (sToken.eCategory) value_or_end,
value,
comma_or_end
} eExpect = EExpect::value_or_end;
// Iterate through the value
while (refToken.get() && refToken.get().Category() != ETokenCategory::token_syntax_inline_table_close)
{ {
case CLexerTOML::ETokenCategory::token_syntax_new_line: // The value has its own token range
throw XTOMLParseException("No newlines allowed in inline table"); CNodeTokenRange rangeNode(refStartValueToken.get());
switch (refToken.get().Category())
{
case ETokenCategory::token_syntax_new_line:
m_lexer.Consume();
break; break;
case CLexerTOML::ETokenCategory::token_syntax_comma: case ETokenCategory::token_syntax_comma:
if (eExpect == EExpect::value) if (eExpect != EExpect::comma_or_end) throw XTOMLParseException("Expecting value or table end.");
throw XTOMLParseException("Unexpected comma.");
m_lexer.Consume(); m_lexer.Consume();
eExpect = EExpect::value; eExpect = EExpect::value;
break; break;
default: default:
if (eExpect == EExpect::comma_end) if (eExpect == EExpect::comma_or_end) throw XTOMLParseException("Expecting comma or table end.");
throw XTOMLParseException("Expecting comma or table end."); ProcessValueKey(rangeNode);
ProcessValueKey(); eExpect = EExpect::comma_or_end;
eExpect = EExpect::comma_end;
break; break;
} }
sToken = m_lexer.Peek();
// Update the start of the next value range
refStartValueToken = rangeNode.LinesBehindNode().End();
// Get the next value
refToken = m_lexer.Peek();
} }
if (eExpect == EExpect::value) if (eExpect == EExpect::value)
throw XTOMLParseException("Expecting a value before inline table end."); throw XTOMLParseException("Expecting a value before inline table end.");
m_ptrRoot->Find(rssKeyPath)->GetTable()->m_bOpenToAddChildren = false; // This is the node token range finishing main part.
rNodeRange.NodeMainFinish(CTokenRange(refToken.get(), refToken.get().Next()));
// Consume the token
m_lexer.Consume(); m_lexer.Consume();
m_ssCurrentTable = ssCurrentTableTemp;
} }
std::string CParserTOML::ComposePath() CTokenRange CParser::ProcessKeyPath()
{ {
std::string ssPath; //std::string ssPath;
CLexerTOML::SToken sToken = m_lexer.Peek(); std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_dot std::reference_wrapper<const CToken> refKeyStart = refToken;
&& sToken.eCategory != CLexerTOML::ETokenCategory::token_key) if (!refToken.get()
|| (refToken.get().Category() != ETokenCategory::token_syntax_dot
&& refToken.get().Category() != ETokenCategory::token_key))
throw XTOMLParseException("Invalid Token to assemble path from keys"); throw XTOMLParseException("Invalid Token to assemble path from keys");
while (sToken.eCategory == CLexerTOML::ETokenCategory::token_syntax_dot while (refToken.get()
|| sToken.eCategory == CLexerTOML::ETokenCategory::token_key) && (refToken.get().Category() == ETokenCategory::token_syntax_dot
|| refToken.get().Category() == ETokenCategory::token_key))
{ {
m_lexer.Consume(); m_lexer.Consume();
if (sToken.eCategory == CLexerTOML::ETokenCategory::token_key) //if (refToken.get().Category() == ETokenCategory::token_key)
ssPath += sToken.ssContentString; //{
else // EQuoteRequest eQuoteRequest = EQuoteRequest::smart_key;
ssPath += "."; // switch (refToken.get().StringType())
sToken = m_lexer.Peek(); // {
} // case ETokenStringType::literal_string:
return ssPath; // eQuoteRequest = EQuoteRequest::literal_text;
// break;
// case ETokenStringType::quoted_string:
// eQuoteRequest = EQuoteRequest::quoted_text;
// break;
// case ETokenStringType::multi_line_literal:
// eQuoteRequest = EQuoteRequest::multi_line_literal_text;
// break;
// case ETokenStringType::multi_line_quoted:
// eQuoteRequest = EQuoteRequest::multi_line_quoted_text;
// break;
// default:
// break;
// }
//}
refToken = m_lexer.Peek();
} }
return CTokenRange(refKeyStart.get(), refToken);
}
} // namespace toml_parser

View File

@@ -3,23 +3,32 @@
#include "lexer_toml.h" #include "lexer_toml.h"
#include "parser_node_toml.h" #include "parser_node_toml.h"
#include "miscellaneous.h"
#include <stack>
#include <memory>
#include <string>
/// The TOML parser namespace
namespace toml_parser
{
class CNode;
/** /**
* @brief Creates a tree structure from input of UTF-8 encoded TOML source data * @brief Creates a tree structure from input of UTF-8 encoded TOML source data
*/ */
class CParserTOML : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser class CParser : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser
{ {
public: public:
/** /**
* @brief Default constructor * @brief Default constructor
*/ */
CParserTOML() = default; CParser() = default;
/** /**
* @brief Construct a new Parser object * @brief Construct a new Parser object
* @param[in] rssString UTF-8 encoded data of a TOML source * @param[in] rssString UTF-8 encoded data of a TOML source
*/ */
CParserTOML(const std::string& rssString); CParser(const std::string& rssString);
// Interface map // Interface map
BEGIN_SDV_INTERFACE_MAP() BEGIN_SDV_INTERFACE_MAP()
@@ -42,132 +51,89 @@ public:
*/ */
virtual bool Process(/*in*/ const sdv::u8string& ssContent) override; virtual bool Process(/*in*/ const sdv::u8string& ssContent) override;
/**
* @brief Get the lexer containing the token list.
* @return A reference to the lexer containing the token list.
*/
CLexer& Lexer();
/** /**
* @{ * @{
* @brief Return the root node. * @brief Return the root node.
* @return Reference to the root node collection. * @return Reference to the root node collection.
*/ */
const CNodeCollection& GetRoot() const; const CNodeCollection& Root() const;
CNodeCollection& GetRoot(); CNodeCollection& Root();
/** /**
* @} * @}
*/ */
/** /**
* @brief Get the TOML text based on the content. * @brief Get the TOML text based on the content.
* @param[in] rssParent When present, uses the parent node into the TOML text generation. * @param[in] rssPrefixKey When present, uses the prefix node into the TOML text generation. The string must follow the key
* rules for separation with bare, literal and quoted keys.
* @return The string containing the TOML text. * @return The string containing the TOML text.
*/ */
std::string CreateTOMLText(const std::string& rssParent = std::string()) const; std::string GenerateTOML(const std::string& rssPrefixKey = std::string()) const;
private: private:
/**
* @brief Add a collection node (table or array).
* @tparam TCollectionNode The collection node class to add (to create).
* @param[in] rssPath Reference to the node path.
* @return Returns whether the node could be added.
*/
template <class TCollectionNode>
bool Add(const std::string& rssPath);
/**
* @brief Add a boolean value node.
* @param[in] rssPath Reference to the node path.
* @param[in] bVal The boolean value.
* @return Returns whether the node could be added.
*/
bool Add(const std::string& rssPath, bool bVal);
/**
* @brief Add a integer value node.
* @param[in] rssPath Reference to the node path.
* @param[in] iVal The integer value.
* @return Returns whether the node could be added.
*/
bool Add(const std::string& rssPath, int64_t iVal);
/**
* @brief Add a floating point value node.
* @param[in] rssPath Reference to the node path.
* @param[in] dVal The floating point value.
* @return Returns whether the node could be added.
*/
bool Add(const std::string& rssPath, double dVal);
/**
* @brief Add a string value node.
* @param[in] rssPath Reference to the node path.
* @param[in] rssVal Reference to the string value.
* @return Returns whether the node could be added.
*/
bool Add(const std::string& rssPath, const std::string& rssVal);
/** /**
* @brief Process a table declaration. * @brief Process a table declaration.
* @param[in, out] rNodeRange Reference to the extended token range of the node.
*/ */
void ProcessTable(); void ProcessTable(CNodeTokenRange& rNodeRange);
/** /**
* @brief Process a table array declaration. * @brief Process a table array declaration.
* @param[in, out] rNodeRange Reference to the extended token range of the node.
*/ */
void ProcessTableArray(); void ProcessTableArray(CNodeTokenRange& rNodeRange);
/** /**
* @brief Process the value key. * @brief Process the value key.
* @param[in, out] rNodeRange Reference to the extended token range of the node.
*/ */
void ProcessValueKey(); void ProcessValueKey(CNodeTokenRange& rNodeRange);
/** /**
* @brief Process the value with the supplied key. * @brief Process the value with the supplied key.
* @param[in] rssKeyPath Reference to the key path string. * @param[in] rrangeKeyPath Reference to the key path token range.
* @param[in, out] rNodeRange Reference to the extended token range of the node.
*/ */
void ProcessValue(const std::string& rssKeyPath); void ProcessValue(const CTokenRange& rrangeKeyPath, CNodeTokenRange& rNodeRange);
/** /**
* @brief Process the array value with the supplied key. * @brief Process the array value with the supplied key.
* @param[in] rssKeyPath Reference to the key path string. * @param[in, out] rNodeRange Reference to the extended token range of the node. The second main range will be added.
*/ */
void ProcessArray(const std::string& rssKeyPath); void ProcessArray(CNodeTokenRange& rNodeRange);
/** /**
* @brief Process the inline table value with the supplied key. * @brief Process the inline table value with the supplied key.
* @param[in] rssKeyPath Reference to the key path string. * @param[in, out] rNodeRange Reference to the extended token range of the node. The second main range will be added.
*/ */
void ProcessInlineTable(const std::string& rssKeyPath); void ProcessInlineTable(CNodeTokenRange& rNodeRange);
/** /**
* @brief Compose a path from lexer tokens. A path is composed of table and array elements separated with a dot. * @brief Compose a path from lexer tokens. A path is composed of table and array elements separated with a dot.
* @return The composed path. * @return The token range of the key path.
*/ */
std::string ComposePath(); CTokenRange ProcessKeyPath();
/** /**
* @brief Enum for differentiating between an array environment and an inline table environment for syntax checks. * @brief Enum for differentiating between an array environment and an inline table environment for syntax checks.
*/ */
enum class EEnvironment enum class EEnvironment
{ {
env_array, //!< Environment for an array env_array, ///< Environment for an array
env_inline_table //!< Environment for a table env_inline_table ///< Environment for a table
}; };
std::stack<EEnvironment> m_stackEnvironment; ///< Tracking of environments in nested structures. std::stack<EEnvironment> m_stackEnvironment; ///< Tracking of environments in nested structures.
std::shared_ptr<CNode> m_ptrRoot = std::make_shared<CRootTable>(); ///< The one root node. std::shared_ptr<CRootTable> m_ptrRoot; ///< The one root node.
std::string m_ssCurrentTable; ///< Path to the current table. std::shared_ptr<CNodeCollection> m_ptrCurrentCollection; ///< The current collection node.
CLexerTOML m_lexer; ///< Lexer. CLexer m_lexer; ///< Lexer.
}; };
} // namespace toml_parser
template <class TCollectionNode>
inline bool CParserTOML::Add(const std::string& rssPath)
{
size_t nOffset = rssPath.rfind('.');
if (nOffset == std::string::npos)
nOffset = 0;
else
nOffset++;
std::string ssName = rssPath.substr(nOffset);
m_ptrRoot->Add(rssPath, std::make_shared<TCollectionNode>(ssName), true);
return true;
}
#endif // PARSER_TOML_H #endif // PARSER_TOML_H

View File

@@ -25,7 +25,7 @@ public:
DECLARE_OBJECT_CLASS_NAME("TOMLParserUtility") DECLARE_OBJECT_CLASS_NAME("TOMLParserUtility")
private: private:
CParserTOML m_parser; ///< Configuration parser toml_parser::CParser m_parser; ///< Configuration parser
}; };
DEFINE_SDV_OBJECT_NO_EXPORT(CTOMLParserUtility) DEFINE_SDV_OBJECT_NO_EXPORT(CTOMLParserUtility)

View File

@@ -12,6 +12,7 @@ CChannelConnector::CChannelConnector(CCommunicationControl& rcontrol, uint32_t u
m_pDataSend(m_ptrChannelEndpoint.GetInterface<sdv::ipc::IDataSend>()) m_pDataSend(m_ptrChannelEndpoint.GetInterface<sdv::ipc::IDataSend>())
{ {
m_tConnectionID.uiIdent = uiIndex; m_tConnectionID.uiIdent = uiIndex;
while (!m_tConnectionID.uiControl)
m_tConnectionID.uiControl = static_cast<uint32_t>(rand()); m_tConnectionID.uiControl = static_cast<uint32_t>(rand());
} }
@@ -29,7 +30,7 @@ CChannelConnector::~CChannelConnector()
lock.unlock(); lock.unlock();
// Cancel the processing // Cancel the processing
rsEntry.bCancel = true; rsEntry.eState = SCallEntry::EState::canceled;
rsEntry.cvWaitForResult.notify_all(); rsEntry.cvWaitForResult.notify_all();
// Handle next call. // Handle next call.
@@ -44,6 +45,10 @@ CChannelConnector::~CChannelConnector()
if (m_uiConnectionStatusCookie) pConnection->UnregisterStatusEventCallback(m_uiConnectionStatusCookie); if (m_uiConnectionStatusCookie) pConnection->UnregisterStatusEventCallback(m_uiConnectionStatusCookie);
pConnection->Disconnect(); pConnection->Disconnect();
} }
// There are several dependencies on this channel connector, which should be availble during the processing of the asynchronous
// disconnect function. Wait for a quarter second to allow the processing to complete.
std::this_thread::sleep_for(std::chrono::milliseconds(250));
} }
bool CChannelConnector::ServerConnect(sdv::IInterfaceAccess* pObject, bool bAllowReconnect) bool CChannelConnector::ServerConnect(sdv::IInterfaceAccess* pObject, bool bAllowReconnect)
@@ -230,20 +235,25 @@ void CChannelConnector::DecoupledReceiveData(/*inout*/ sdv::sequence<sdv::pointe
// Send the result back // Send the result back
m_pDataSend->SendData(seqResult); m_pDataSend->SendData(seqResult);
} else }
else
{ {
// Look for the call entry // Look for the call entry
std::unique_lock<std::mutex> lockCallMap(m_mtxCalls); std::unique_lock<std::mutex> lockCallMap(m_mtxCalls);
auto itCall = m_mapCalls.find(sAddress.uiCallIndex); auto itCall = m_mapCalls.find(sAddress.uiCallIndex);
if (itCall == m_mapCalls.end()) return; if (itCall == m_mapCalls.end())
return;
SCallEntry& rsCallEntry = itCall->second; SCallEntry& rsCallEntry = itCall->second;
m_mapCalls.erase(itCall); m_mapCalls.erase(itCall);
lockCallMap.unlock(); lockCallMap.unlock();
if (rsCallEntry.eState != SCallEntry::EState::processing)
return;
// Update the result // Update the result
std::unique_lock<std::mutex> lockCall(rsCallEntry.mtxWaitForResult); std::unique_lock<std::mutex> lockCall(rsCallEntry.mtxWaitForResult);
rsCallEntry.seqResult = seqData; rsCallEntry.seqResult = seqData;
lockCall.unlock(); lockCall.unlock();
rsCallEntry.eState = SCallEntry::EState::processed;
rsCallEntry.cvWaitForResult.notify_all(); rsCallEntry.cvWaitForResult.notify_all();
} }
} }
@@ -271,12 +281,14 @@ sdv::sequence<sdv::pointer<uint8_t>> CChannelConnector::MakeCall(sdv::ps::TMarsh
std::unique_lock<std::mutex> lock(m_mtxCalls); std::unique_lock<std::mutex> lock(m_mtxCalls);
SCallEntry sResult; SCallEntry sResult;
m_mapCalls.try_emplace(sAddress.uiCallIndex, sResult); m_mapCalls.try_emplace(sAddress.uiCallIndex, sResult);
sResult.eState = SCallEntry::EState::processing;
lock.unlock(); lock.unlock();
// Store the channel context (used to marshall interfaces over the same connector) // Store the channel context (used to marshall interfaces over the same connector)
m_rcontrol.SetConnectorContext(this); m_rcontrol.SetConnectorContext(this);
// Send the data // Send the data
std::unique_lock<std::mutex> lockResult(sResult.mtxWaitForResult);
try try
{ {
if (!m_pDataSend->SendData(rseqInputData)) throw sdv::ps::XMarshallExcept(); if (!m_pDataSend->SendData(rseqInputData)) throw sdv::ps::XMarshallExcept();
@@ -291,10 +303,13 @@ sdv::sequence<sdv::pointer<uint8_t>> CChannelConnector::MakeCall(sdv::ps::TMarsh
} }
// Wait for the result // Wait for the result
if (sResult.bCancel) throw sdv::ps::XMarshallTimeout(); // NOTE: Sinde the conditional variable doesn't keep its state, it might happen, that the variable is set before the wait
std::unique_lock<std::mutex> lockResult(sResult.mtxWaitForResult); // function has been entered (race condition). This would cause the function to wait forever.
sResult.cvWaitForResult.wait(lockResult); while (sResult.eState == SCallEntry::EState::processing)
if (sResult.bCancel) throw sdv::ps::XMarshallTimeout(); sResult.cvWaitForResult.wait_for(lockResult, std::chrono::milliseconds(1));
if (sResult.eState == SCallEntry::EState::canceled)
throw sdv::ps::XMarshallTimeout();
return sResult.seqResult; return sResult.seqResult;
} }

View File

@@ -114,10 +114,19 @@ private:
*/ */
struct SCallEntry struct SCallEntry
{ {
/**
* @brief Call entry state.
*/
enum EState
{
initialized,
processing,
processed,
canceled,
} eState = EState::initialized; ///< Data processing state.
sdv::sequence<sdv::pointer<uint8_t>> seqResult; ///< The result data. sdv::sequence<sdv::pointer<uint8_t>> seqResult; ///< The result data.
std::mutex mtxWaitForResult; ///< Mutex to protect result processing. std::mutex mtxWaitForResult; ///< Mutex to protect result processing.
std::condition_variable cvWaitForResult; ///< Condition variable to trigger result processing. std::condition_variable cvWaitForResult; ///< Condition variable to trigger result processing.
bool bCancel = false; ///< Cancel processing when set.
}; };
CCommunicationControl& m_rcontrol; ///< Reference to the communication control class. CCommunicationControl& m_rcontrol; ///< Reference to the communication control class.

View File

@@ -32,7 +32,9 @@ sdv::interface_t CMarshallObject::InitializeAsProxy(uint32_t uiProxyIndex, sdv::
m_eType = EType::proxy; m_eType = EType::proxy;
// Create marshall ID from index and a random number. // Create marshall ID from index and a random number.
sdv::ps::TMarshallID tMarshallID = { 0, GetProcessID(), uiProxyIndex, static_cast<uint32_t>(rand()) }; sdv::ps::TMarshallID tMarshallID = { 0, GetProcessID(), uiProxyIndex, 0 };
while (!tMarshallID.uiControl)
tMarshallID.uiControl = static_cast<uint32_t>(rand());
// Get the stub creation interface from the repository // Get the stub creation interface from the repository
sdv::core::IRepositoryMarshallCreate* pMarshallCreate = sdv::core::IRepositoryMarshallCreate* pMarshallCreate =
@@ -99,6 +101,8 @@ bool CMarshallObject::InitializeAsStub(uint32_t uiStubIndex, sdv::interface_t if
// Create marshall ID from index and a random number. // Create marshall ID from index and a random number.
sdv::ps::TMarshallID tMarshallID = { 0, GetProcessID(), uiStubIndex, static_cast<uint32_t>(rand()) }; sdv::ps::TMarshallID tMarshallID = { 0, GetProcessID(), uiStubIndex, static_cast<uint32_t>(rand()) };
while (!tMarshallID.uiControl)
tMarshallID.uiControl = static_cast<uint32_t>(rand());
// Get the stub creation interface from the repository // Get the stub creation interface from the repository
sdv::core::IRepositoryMarshallCreate* pMarshallCreate = sdv::core::IRepositoryMarshallCreate* pMarshallCreate =

View File

@@ -342,7 +342,8 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
if (m_eStatus != sdv::ipc::EConnectStatus::uninitialized) if (m_eStatus != sdv::ipc::EConnectStatus::uninitialized)
{ {
for (auto& rprEventCallback : m_lstEventCallbacks) for (auto& rprEventCallback : m_lstEventCallbacks)
if (rprEventCallback.pCallback) rprEventCallback.pCallback->SetStatus(sdv::ipc::EConnectStatus::connection_error); if (rprEventCallback.pCallback && rprEventCallback.uiCookie)
rprEventCallback.pCallback->SetStatus(sdv::ipc::EConnectStatus::connection_error);
return false; return false;
} }
@@ -456,6 +457,8 @@ uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess*
sdv::ipc::IConnectEventCallback* pCallback = pEventCallback->GetInterface<sdv::ipc::IConnectEventCallback>(); sdv::ipc::IConnectEventCallback* pCallback = pEventCallback->GetInterface<sdv::ipc::IConnectEventCallback>();
if (!pCallback) return 0; if (!pCallback) return 0;
uint64_t uiCookie = rand(); uint64_t uiCookie = rand();
while (!uiCookie)
uiCookie = rand();
std::unique_lock<std::shared_mutex> lock(m_mtxEventCallbacks); std::unique_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
m_lstEventCallbacks.emplace(m_lstEventCallbacks.begin(), std::move(SEventCallback{ uiCookie, pCallback })); m_lstEventCallbacks.emplace(m_lstEventCallbacks.begin(), std::move(SEventCallback{ uiCookie, pCallback }));
return uiCookie; return uiCookie;
@@ -490,7 +493,10 @@ void CConnection::DestroyObject()
// Clear all events callbacks (if not done so already) // Clear all events callbacks (if not done so already)
std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks); std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
for (auto& rprEventCallback : m_lstEventCallbacks) for (auto& rprEventCallback : m_lstEventCallbacks)
{
rprEventCallback.uiCookie = 0;
rprEventCallback.pCallback = nullptr; rprEventCallback.pCallback = nullptr;
}
lock.unlock(); lock.unlock();
// Just in case... so no calls are made into the destructed class any more. // Just in case... so no calls are made into the destructed class any more.
@@ -519,7 +525,10 @@ void CConnection::SetStatus(sdv::ipc::EConnectStatus eStatus)
m_eStatus = eStatus; m_eStatus = eStatus;
std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks); std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
for (auto& rprEventCallback : m_lstEventCallbacks) for (auto& rprEventCallback : m_lstEventCallbacks)
if (rprEventCallback.pCallback) rprEventCallback.pCallback->SetStatus(eStatus); {
if (rprEventCallback.pCallback && rprEventCallback.uiCookie)
rprEventCallback.pCallback->SetStatus(eStatus);
}
// If disconnected by force update the disconnect status. // If disconnected by force update the disconnect status.
if (m_eStatus == sdv::ipc::EConnectStatus::disconnected_forced) if (m_eStatus == sdv::ipc::EConnectStatus::disconnected_forced)
@@ -554,12 +563,13 @@ void CConnection::ReceiveMessages()
{ {
SetStatus(sdv::ipc::EConnectStatus::communication_error); SetStatus(sdv::ipc::EConnectStatus::communication_error);
SDV_LOG_ERROR("No valid shared memory for receiving."); SDV_LOG_ERROR("No valid shared memory for receiving.");
lock.unlock();
m_cvStartConnect.notify_all(); m_cvStartConnect.notify_all();
return; return;
} }
m_cvStartConnect.notify_all();
lock.unlock(); lock.unlock();
m_cvStartConnect.notify_all();
// Read processing // Read processing
auto tpStart = std::chrono::high_resolution_clock::time_point(); auto tpStart = std::chrono::high_resolution_clock::time_point();
@@ -623,8 +633,24 @@ void CConnection::ReceiveMessages()
case EMsgType::data_fragment: case EMsgType::data_fragment:
case EMsgType::data: case EMsgType::data:
break; break;
case EMsgType::sync_request:
TRACE("Receive sync-request message of of ", message.GetSize(), " bytes");
break;
case EMsgType::sync_answer:
TRACE("Receive sync-answer message of of ", message.GetSize(), " bytes");
break;
case EMsgType::connect_request:
TRACE("Receive connect-request message of of ", message.GetSize(), " bytes");
break;
case EMsgType::connect_answer:
TRACE("Receive connect-answer message of of ", message.GetSize(), " bytes");
break;
case EMsgType::connect_term:
TRACE("Receive connect-termination message of of ", message.GetSize(), " bytes");
break;
default: default:
TRACE("Receive raw data 0x", (void*) &message.GetMsgHdr(), " of ", message.GetSize(), " bytes"); TRACE("Received unknown message of type ", (uint32_t) message.GetMsgHdr().eType, " of ", message.GetSize(), " bytes");
break;
} }
#endif #endif
@@ -1011,7 +1037,7 @@ void CConnection::ReceiveDataFragementMessage(CMessage& rMessage, SDataContext&
if (!rMessage.GetFragmentedHdr().uiOffset) if (!rMessage.GetFragmentedHdr().uiOffset)
{ {
#if ENABLE_REPORTING >= 1 #if ENABLE_REPORTING >= 1
TRACE("Start receive fragmented data message of ", rsDataCtxt.uiTotalSize, " bytes"); TRACE("Start receive fragmented data message of ", rsDataCtxt.uiTotalSize, " bytes (following the header)");
#endif #endif
// Read the data directory table... // Read the data directory table...
// Note: it is assumed that the table fits in the first message completely. // Note: it is assumed that the table fits in the first message completely.
@@ -1029,7 +1055,7 @@ void CConnection::ReceiveDataFragementMessage(CMessage& rMessage, SDataContext&
if (!sstream.str().empty()) sstream << ", "; if (!sstream.str().empty()) sstream << ", ";
sstream << rptrData.size(); sstream << rptrData.size();
} }
TRACE("Fragmented message has ", rsDataCtxt.seqDataChunks.size(), " of {", sstream.str(), "} bytes"); TRACE("Fragmented message has ", rsDataCtxt.seqDataChunks.size(), " chunk of ", sstream.str(), " bytes");
#endif #endif
} }

View File

@@ -22,7 +22,7 @@
/// When put to 1, decoupling of receive data is activated (default is not activated). /// When put to 1, decoupling of receive data is activated (default is not activated).
#define ENABLE_DECOUPLING 0 #define ENABLE_DECOUPLING 0
#if ENABLE_REPORTING > 0 #if ENABLE_REPORTING > 0 && !defined(ENABLE_TRACE)
/// Enable tracing /// Enable tracing
#define ENABLE_TRACE 1 #define ENABLE_TRACE 1
#endif #endif

View File

@@ -29,7 +29,7 @@ public:
CInProcMemBuffer(const std::string& rssConnectionString); CInProcMemBuffer(const std::string& rssConnectionString);
/** /**
* \brief Default destructor * @brief Default destructor
*/ */
~CInProcMemBuffer() = default; ~CInProcMemBuffer() = default;

View File

@@ -43,7 +43,7 @@ public:
CSharedMemBuffer(CSharedMemBuffer&&) = delete; CSharedMemBuffer(CSharedMemBuffer&&) = delete;
/** /**
* \brief Default destructor * @brief Default destructor
*/ */
~CSharedMemBuffer(); ~CSharedMemBuffer();
@@ -364,10 +364,15 @@ CSharedMemBuffer<TAccessor>::~CSharedMemBuffer()
munmap(m_pBuffer, m_uiSize); munmap(m_pBuffer, m_uiSize);
if (m_bServer && !m_ssName.empty()) if (m_bServer && !m_ssName.empty())
shm_unlink((std::string("/") + m_ssName).c_str()); shm_unlink((std::string("/") + m_ssName).c_str());
if (m_iFileDescr >= 0) close(m_iFileDescr);
if (m_bServer && m_pSemaphoreTx) if (m_bServer && m_pSemaphoreTx)
sem_unlink(m_ssSyncTx.c_str()); sem_unlink(m_ssSyncTx.c_str());
if (m_pSemaphoreTx)
sem_close(m_pSemaphoreTx);
if (m_bServer && m_pSemaphoreRx) if (m_bServer && m_pSemaphoreRx)
sem_unlink(m_ssSyncRx.c_str()); sem_unlink(m_ssSyncRx.c_str());
if (m_pSemaphoreRx)
sem_close(m_pSemaphoreRx);
} }
template <class TAccessor> template <class TAccessor>

View File

@@ -53,7 +53,7 @@ public:
CSharedMemBuffer(CSharedMemBuffer&&) = delete; CSharedMemBuffer(CSharedMemBuffer&&) = delete;
/** /**
* \brief Default destructor * @brief Default destructor
*/ */
~CSharedMemBuffer(); ~CSharedMemBuffer();

View File

@@ -28,6 +28,7 @@
#include <thread> #include <thread>
#include <queue> #include <queue>
#include <condition_variable> #include <condition_variable>
#include <atomic>
// Forward declaration // Forward declaration
class CConnection; class CConnection;
@@ -114,7 +115,7 @@ private:
///< connection is scheduled for destruction. ///< connection is scheduled for destruction.
std::queue<std::shared_ptr<CConnection>> m_queueScheduledConnectionDestructions; ///< Scheduled connection for destruction. std::queue<std::shared_ptr<CConnection>> m_queueScheduledConnectionDestructions; ///< Scheduled connection for destruction.
std::thread m_threadScheduledConnectionDestructions; ///< Thread processing the scheduled destructions. std::thread m_threadScheduledConnectionDestructions; ///< Thread processing the scheduled destructions.
bool m_bShutdown = false; ///< Set when shutting down the watchdog std::atomic_bool m_bShutdown = false; ///< Set when shutting down the watchdog
}; };
#endif // !defined WATCH_DOG_H #endif // !defined WATCH_DOG_H

View File

@@ -17,6 +17,7 @@
#include <map> #include <map>
#include <set> #include <set>
#include <condition_variable> #include <condition_variable>
#include <atomic>
/** /**
* @brief Process control service class * @brief Process control service class
@@ -135,7 +136,6 @@ public:
void MonitorThread(); void MonitorThread();
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status. sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
std::mutex m_mtxProcessThreadShutdown; ///< Synchronize access std::mutex m_mtxProcessThreadShutdown; ///< Synchronize access
#ifdef _WIN32 #ifdef _WIN32
std::map<sdv::process::TProcessID, std::pair<HANDLE,HANDLE>> m_mapProcessThreadShutdown; ///< Map with process IDs and event handles std::map<sdv::process::TProcessID, std::pair<HANDLE,HANDLE>> m_mapProcessThreadShutdown; ///< Map with process IDs and event handles
@@ -158,7 +158,7 @@ public:
#else #else
#error OS is not supported! #error OS is not supported!
#endif #endif
bool bRunning = true; ///< Set when the process is running and not terminated yet. std::atomic_bool bRunning = true; ///< Set when the process is running and not terminated yet.
int64_t iRetVal = 0; ///< Process return value. int64_t iRetVal = 0; ///< Process return value.
std::map<uint32_t, sdv::process::IProcessLifetimeCallback*> mapAssociatedMonitors; ///< Map with associated monitors. std::map<uint32_t, sdv::process::IProcessLifetimeCallback*> mapAssociatedMonitors; ///< Map with associated monitors.
std::mutex mtxProcess; ///< Mutex for process access. std::mutex mtxProcess; ///< Mutex for process access.
@@ -168,7 +168,7 @@ public:
std::map<sdv::process::TProcessID, std::shared_ptr<SProcessHelper>> m_mapProcesses; ///< Monitor map std::map<sdv::process::TProcessID, std::shared_ptr<SProcessHelper>> m_mapProcesses; ///< Monitor map
uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie
std::map<uint32_t, std::shared_ptr<SProcessHelper>> m_mapMonitors; ///< Map with monitors. std::map<uint32_t, std::shared_ptr<SProcessHelper>> m_mapMonitors; ///< Map with monitors.
bool m_bShutdown = false; ///< Set to shutdown the monitor thread. std::atomic_bool m_bShutdown = false; ///< Set to shutdown the monitor thread.
std::thread m_threadMonitor; ///< Monitor thread. std::thread m_threadMonitor; ///< Monitor thread.
}; };
DEFINE_SDV_OBJECT(CProcessControl) DEFINE_SDV_OBJECT(CProcessControl)

View File

@@ -58,7 +58,7 @@ add_subdirectory(unit_tests/smart_ifc)
add_subdirectory(unit_tests/toml_parser) add_subdirectory(unit_tests/toml_parser)
add_subdirectory(unit_tests/module_control) add_subdirectory(unit_tests/module_control)
add_subdirectory(unit_tests/repository) add_subdirectory(unit_tests/repository)
# add_subdirectory(unit_tests/shared_mem) add_subdirectory(unit_tests/shared_mem)
add_subdirectory(unit_tests/memory_manager) add_subdirectory(unit_tests/memory_manager)
add_subdirectory(unit_tests/core_loader) add_subdirectory(unit_tests/core_loader)
add_subdirectory(unit_tests/named_mutex) add_subdirectory(unit_tests/named_mutex)

View File

@@ -11,7 +11,7 @@ or
# SDV TEST MACRO # SDV TEST MACRO
## Overview ## Overview
There is set of macros in "export/support/sdv_test_macro.h" which are designed to extend the Google Test (GTEST) framework by incorporating a warning level mechanism. These macros allow developers to specify a warning level for each test assertion, providing more control over test outcomes and enabling better handling of test result conditions based on their warning level. There is set of macros in "tests/include/sdv_test_macro.h" which are designed to extend the Google Test (GTEST) framework by incorporating a warning level mechanism. These macros allow developers to specify a warning level for each test assertion, providing more control over test outcomes and enabling better handling of test result conditions based on their warning level.
## Purpose ## Purpose
The primary purpose of these macros is to enhance the flexibility and robustness of test assertions in the GTEST framework. By integrating a warning level, developers can decide whether to report failed tests as warning or error and handle them accordingly. This is particularly useful in where different test failures may have varying impacts on the overall build system. The primary purpose of these macros is to enhance the flexibility and robustness of test assertions in the GTEST framework. By integrating a warning level, developers can decide whether to report failed tests as warning or error and handle them accordingly. This is particularly useful in where different test failures may have varying impacts on the overall build system.
@@ -62,19 +62,19 @@ The WarningLevel enum defines three levels of warnings:
Additionally SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD is implemented to check if tests are running with CMake build or any other way. It returns true if they are running with CMake build. Additionally SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD is implemented to check if tests are running with CMake build or any other way. It returns true if they are running with CMake build.
## Usage ## Usage
To use these macros, include the header file '#include <support/sdv_test_macro.h>" in your test files. When writing test cases, use the SDV macros instead of the standard GTEST macros, and specify the appropriate warning level for each assertion. To use these macros, include the header file '#include "tests/include/sdv_test_macro.h"' in your test files. When writing test cases, use the SDV macros instead of the standard GTEST macros, and specify the appropriate warning level for each assertion.
```cpp ```cpp
#include <support/sdv_test_macro.h> #include "tests/include/sdv_test_macro.h"
TEST(SDVTestMacros, TestExpectEq) TEST(SDVTestMacros, TestExpectEq)
{ {
int val1 = 5; int val1 = 5;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_EQ(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_EQ(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_EQ(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_EQ(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectStreq) TEST(SDVTestMacros, TestExpectStreq)
@@ -82,9 +82,9 @@ TEST(SDVTestMacros, TestExpectStreq)
std::string str1 = "test"; std::string str1 = "test";
std::string str2 = "test"; std::string str2 = "test";
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_STREQ(str1, str2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_STREQ(str1, str2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_STREQ(str1, str2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_STREQ(str1, str2, sdv_test::WARNING_ENABLED);
} }
``` ```
In this example, the SDV_EXPECT_EQ macro checks if val1 and val2 are equal, while the SDV_EXPECT_STREQ macro checks if str1 and str2 are equal. In both test cases, it is checked whether it is running In this example, the SDV_EXPECT_EQ macro checks if val1 and val2 are equal, while the SDV_EXPECT_STREQ macro checks if str1 and str2 are equal. In both test cases, it is checked whether it is running

View File

@@ -163,11 +163,11 @@ ViewFilter = "Fatal"
auto table1 = config.GetDirect("newTableArray[0]"); auto table1 = config.GetDirect("newTableArray[0]");
EXPECT_EQ(table1.GetType(), sdv::toml::ENodeType::node_table); EXPECT_EQ(table1.GetType(), sdv::toml::ENodeType::node_table);
EXPECT_TRUE(table1.GetName().empty()); EXPECT_EQ(table1.GetName(), "newTableArray");
auto table2 = config.GetDirect("newTableArray[1]"); auto table2 = config.GetDirect("newTableArray[1]");
EXPECT_EQ(table2.GetType(), sdv::toml::ENodeType::node_table); EXPECT_EQ(table2.GetType(), sdv::toml::ENodeType::node_table);
EXPECT_TRUE(table2.GetName().empty()); EXPECT_EQ(table2.GetName(), "newTableArray");
config.Clear(); config.Clear();
appcontrol.Shutdown(); appcontrol.Shutdown();
@@ -770,6 +770,11 @@ ViewFilter = "Fatal"
[j."ʞ".'l'] [j."ʞ".'l']
)")); )"));
EXPECT_FALSE(sdv::toml::CTOMLParser(u8R"(
[ j . "ʞ" . 'l' ]
["j".'ʞ'."l"]
)"));
EXPECT_FALSE(sdv::toml::CTOMLParser(R"( EXPECT_FALSE(sdv::toml::CTOMLParser(R"(
[fruit] [fruit]
apple = "red" apple = "red"

View File

@@ -929,13 +929,13 @@ TEST(DataDispatchServiceTest, DirectRxTxSignalConcurrency)
while (!bShutdown) while (!bShutdown)
{ {
signalPubA.Write(std::rand()); signalPubA.Write(std::rand());
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
signalPubB.Write(std::rand()); signalPubB.Write(std::rand());
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
signalPubC.Write(std::rand()); signalPubC.Write(std::rand());
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
signalPubD.Write(std::rand()); signalPubD.Write(std::rand());
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
} }
} }
catch (...) catch (...)

View File

@@ -1,4 +1,5 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <cstring>
#include "../../../global/process_watchdog.h" #include "../../../global/process_watchdog.h"
#if defined(_WIN32) && defined(_UNICODE) #if defined(_WIN32) && defined(_UNICODE)
@@ -7,8 +8,30 @@ extern "C" int wmain(int argc, wchar_t* argv[])
extern "C" int main(int argc, char* argv[]) extern "C" int main(int argc, char* argv[])
#endif #endif
{ {
CProcessWatchdog watchdog; // Check for the --gtest_repeat option.
bool bRepeatEnabled = false;
for (int iIndex = 0; iIndex < argc; iIndex++)
{
if (!argv[iIndex])
continue;
#if defined(_WIN32) && defined(_UNICODE)
bRepeatEnabled |= std::wcsncmp(argv[iIndex], L"--gtest_repeat", 14) == 0;
#else
bRepeatEnabled |= std::strncmp(argv[iIndex], "--gtest_repeat", 14) == 0;
#endif
}
testing::InitGoogleTest(&argc, argv); // When repeat is enabled, do not enable the watchdog.
if (bRepeatEnabled)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }
else
{
CProcessWatchdog watchdog;
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
}

View File

@@ -1446,7 +1446,7 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency)
std::atomic_uint64_t uiValueCnt = 1000; std::atomic_uint64_t uiValueCnt = 1000;
bool bShutdown = false; bool bShutdownPublisher = false, bShutdownConsumer = false;
std::srand(static_cast<unsigned>(std::time(0))); std::srand(static_cast<unsigned>(std::time(0)));
// Thread sync // Thread sync
@@ -1471,18 +1471,18 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency)
uiInitCnt++; uiInitCnt++;
std::shared_lock<std::shared_mutex> lock(mtxStart); std::shared_lock<std::shared_mutex> lock(mtxStart);
while (!bShutdown) while (!bShutdownPublisher)
{ {
sdv::core::CTransaction transaction = dispatch.CreateTransaction(); sdv::core::CTransaction transaction = dispatch.CreateTransaction();
uint64_t uiValue = std::rand(); uint64_t uiValue = std::rand();
signalPubA.Write(uiValue, transaction); signalPubA.Write(uiValue, transaction);
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
signalPubB.Write(uiValue + 10, transaction); signalPubB.Write(uiValue + 10, transaction);
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
signalPubC.Write(uiValue + 20, transaction); signalPubC.Write(uiValue + 20, transaction);
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
signalPubD.Write(uiValue + 30, transaction); signalPubD.Write(uiValue + 30, transaction);
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
transaction.Finish(); transaction.Finish();
} }
} }
@@ -1513,7 +1513,7 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency)
uiInitCnt++; uiInitCnt++;
std::shared_lock<std::shared_mutex> lock(mtxStart); std::shared_lock<std::shared_mutex> lock(mtxStart);
while (!bShutdown) while (!bShutdownConsumer)
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
@@ -1616,9 +1616,11 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency)
// Wait for all threads to finalize // Wait for all threads to finalize
appcontrol.SetConfigMode(); appcontrol.SetConfigMode();
bShutdown = true; bShutdownPublisher = true;
for (std::thread& rThread : rgPublishThreads) for (std::thread& rThread : rgPublishThreads)
if (rThread.joinable()) rThread.join(); if (rThread.joinable()) rThread.join();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
bShutdownConsumer = true;
for (std::thread& rThread : rgConsumeThreads) for (std::thread& rThread : rgConsumeThreads)
if (rThread.joinable()) rThread.join(); if (rThread.joinable()) rThread.join();

View File

@@ -12,7 +12,7 @@
#include <interfaces/dispatch.h> #include <interfaces/dispatch.h>
#include <support/app_control.h> #include <support/app_control.h>
#include <support/timer.h> #include <support/timer.h>
#include <support/sdv_test_macro.h> #include "../../include/sdv_test_macro.h"
#include "../../../global/process_watchdog.h" #include "../../../global/process_watchdog.h"
#include "../global/ascformat/ascreader.cpp" #include "../global/ascformat/ascreader.cpp"
#include "../global/ascformat/ascwriter.cpp" #include "../global/ascformat/ascwriter.cpp"
@@ -2713,38 +2713,38 @@ TEST(DbcUtilCanDLTest, CyclicTransmit)
if (iCnt < 4) if (iCnt < 4)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
} }
for (n = 0; n < 5; n++) for (n = 0; n < 5; n++)
{ {
if (n != 0) if (n != 0)
{ {
if (vecStat[n] > 6) // In the unlucky case, 6 triggers might have occurred (during startup, there might be many more...). if (vecStat[n] > 6u) // In the unlucky case, 6 triggers might have occurred (during startup, there might be many more...).
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(vecStat[n], 6, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(vecStat[n], 6, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_ENABLED);
} }
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0; double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms if (dPeriod > 0.051) // Max 51ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
} }
if (n == 4) if (n == 4)
{ {
if (dPeriod < 0.019) // Min 19ms if (dPeriod < 0.019) // Min 19ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
} }
} }
else else
@@ -2752,18 +2752,18 @@ TEST(DbcUtilCanDLTest, CyclicTransmit)
if (dPeriod < 0.039) // Min 39ms if (dPeriod < 0.039) // Min 39ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
} }
} }
} }
if (vecStat[n] < 4u) // At least 4 triggers. if (vecStat[n] < 4u) // At least 4 triggers.
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(vecStat[n], 4, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(vecStat[n], 4u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(vecStat[n], 4, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(vecStat[n], 4u, sdv_test::WARNING_ENABLED);
} }
} }
} }
@@ -2857,9 +2857,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit)
if (iCnt != 0 && iCnt != 2) if (iCnt != 0 && iCnt != 2)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_EQ(iCnt, 0, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_EQ(iCnt, 0, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_ENABLED);
} }
bInit = true; bInit = true;
@@ -2874,9 +2874,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit)
if (iCnt < 4) if (iCnt < 4)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
} }
for (n = 0; n < 5; n++) for (n = 0; n < 5; n++)
@@ -2885,40 +2885,40 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit)
{ {
if (n == 2) if (n == 2)
{ {
if (vecStat[n] != 1) // One trigger should have occurred due to default value if (vecStat[n] != 1u) // One trigger should have occurred due to default value
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_EQ(vecStat[n], 1, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_EQ(vecStat[n], 1, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_ENABLED);
} }
} }
else else
{ {
if (vecStat[n] > 6) // In the unlucky case, 6 triggers might have occurred (during startup, there might be many more...). if (vecStat[n] > 6u) // In the unlucky case, 6 triggers might have occurred (during startup, there might be many more...).
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(vecStat[n], 6, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(vecStat[n], 6, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_ENABLED);
} }
} }
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0; double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms if (dPeriod > 0.051) // Max 51ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
} }
if (n == 4) if (n == 4)
{ {
if (dPeriod < 0.019) // Min 19ms if (dPeriod < 0.019) // Min 19ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
} }
} }
else else
@@ -2926,9 +2926,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit)
if (dPeriod < 0.039) // Min 39ms if (dPeriod < 0.039) // Min 39ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
} }
} }
} }
@@ -3028,38 +3028,38 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousTransmit)
if (iCnt < 4) if (iCnt < 4)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
} }
for (n = 0; n < 5; n++) for (n = 0; n < 5; n++)
{ {
if (n != 0) if (n != 0)
{ {
if (vecStat[n] != 2) // One trigger and one cycle. if (vecStat[n] != 2u) // One trigger and one cycle.
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_EQ(vecStat[n], 2, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_EQ(vecStat[n], 2, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_ENABLED);
} }
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0; double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms if (dPeriod > 0.051) // Max 51ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
} }
if (n == 4) if (n == 4)
{ {
if (dPeriod < 0.019) // Min 19ms if (dPeriod < 0.019) // Min 19ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
} }
} }
else else
@@ -3067,9 +3067,9 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousTransmit)
if (dPeriod < 0.039) // Min 39ms if (dPeriod < 0.039) // Min 39ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
} }
} }
} }
@@ -3172,43 +3172,43 @@ TEST(DbcUtilCanDLTest, SpontaneousDelayTransmit)
if (iCnt < 4) if (iCnt < 4)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
} }
for (n = 0; n < 5; n++) for (n = 0; n < 5; n++)
{ {
if (n != 0) if (n != 0)
{ {
if (vecStat[n] > 4) // Max 4 times if (vecStat[n] > 4u) // Max 4 times
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(vecStat[n], 4, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(vecStat[n], 4u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(vecStat[n], 4, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(vecStat[n], 4u, sdv_test::WARNING_ENABLED);
} }
if (vecStat[n] < 3) // Min 3 times if (vecStat[n] < 3u) // Min 3 times
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(vecStat[n], 3, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(vecStat[n], 3u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(vecStat[n], 3, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(vecStat[n], 3u, sdv_test::WARNING_ENABLED);
} }
double dPeriod = std::round((vecLastTime[n] - vecFirstTime[n]) * 1000.0) / 1000.0; double dPeriod = std::round((vecLastTime[n] - vecFirstTime[n]) * 1000.0) / 1000.0;
if (dPeriod > 0.061) // Max 61ms if (dPeriod > 0.061) // Max 61ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(dPeriod, 0.061, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(dPeriod, 0.061, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(dPeriod, 0.061, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(dPeriod, 0.061, sdv_test::WARNING_ENABLED);
} }
if (dPeriod < 0.039) // Min 39ms if (dPeriod < 0.039) // Min 39ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
} }
} }
} }
@@ -3310,38 +3310,38 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousDelayTransmit)
if (iCnt < 4) if (iCnt < 4)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
} }
for (n = 0; n < 5; n++) for (n = 0; n < 5; n++)
{ {
if (n != 0) if (n != 0)
{ {
if (vecStat[n] != 2) // Two trigger and/or cycle. if (vecStat[n] != 2u) // Two trigger and/or cycle.
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_EQ(vecStat[n], 2, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_EQ(vecStat[n], 2, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_ENABLED);
} }
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0; double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms if (dPeriod > 0.051) // Max 51ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
} }
if (n == 4) if (n == 4)
{ {
if (dPeriod < 0.019) // Min 19ms if (dPeriod < 0.019) // Min 19ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
} }
} }
else else
@@ -3349,9 +3349,9 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousDelayTransmit)
if (dPeriod < 0.049) // Min 49ms if (dPeriod < 0.049) // Min 49ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.049, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.049, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.049, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.049, sdv_test::WARNING_ENABLED);
} }
} }
} }
@@ -3446,9 +3446,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit)
if (iCnt != 0 && iCnt != 2) if (iCnt != 0 && iCnt != 2)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_EQ(iCnt, 0, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_EQ(iCnt, 0, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_ENABLED);
} }
bInit = true; bInit = true;
@@ -3463,9 +3463,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit)
if (iCnt < 4) if (iCnt < 4)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(iCnt, 4, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(iCnt, 4, sdv_test::WARNING_ENABLED);
} }
for (n = 0; n < 5; n++) for (n = 0; n < 5; n++)
{ {
@@ -3473,40 +3473,40 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit)
{ {
if (n == 2) if (n == 2)
{ {
if (vecStat[n] != 1) // One trigger no cycle. if (vecStat[n] != 1u) // One trigger no cycle.
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_EQ(vecStat[n], 1, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_EQ(vecStat[n], 1, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_ENABLED);
} }
} }
else else
{ {
if (vecStat[n] != 2) // One trigger and one cycle. if (vecStat[n] != 2u) // One trigger and one cycle.
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_EQ(vecStat[n], 2, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_EQ(vecStat[n], 2, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_ENABLED);
} }
} }
double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0; double dPeriod = std::round((vecTime[n] - vecTime[n - 1]) * 1000.0) / 1000.0;
if (dPeriod > 0.051) // Max 51ms if (dPeriod > 0.051) // Max 51ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(dPeriod, 0.051, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(dPeriod, 0.051, sdv_test::WARNING_ENABLED);
} }
if (n == 4) if (n == 4)
{ {
if (dPeriod < 0.019) // Min 19ms if (dPeriod < 0.019) // Min 19ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
} }
} }
else else
@@ -3514,9 +3514,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit)
if (dPeriod < 0.039) // Min 39ms if (dPeriod < 0.039) // Min 39ms
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
} }
} }
} }

View File

@@ -0,0 +1,273 @@
#ifndef SDV_TEST_MACRO_H
#define SDV_TEST_MACRO_H
#include <sstream>
#include <iostream>
#include <gtest/gtest.h>
/**
* @brief Namespace sdv_test
*/
namespace sdv_test
{
/**
* @brief Enum for warning levels.
*/
enum EWarningLevel
{
WARNING_ENABLED, //<! With this level Failed tests are reported as error as it is in GTEST normally.
WARNING_REDUCED, //<! With this level Failed tests are reported as warning.
WARNING_DISABLED //<! With this level no action is implemented at this moment.
};
/**
* @brief Function to report a warning when the expected condition is not met.
* @param[in] condition The condition that is being checked (should be true if valid).
* @param[in] warningLevel The level of warning to display.
* @param[in] message The warning message to display.
* @param[in] file The name of the file where the warning occurred.
* @param[in] line The line number where the warning occurred.
*/
inline void ReportWarning(bool condition, EWarningLevel warningLevel, const std::string& message, const char* file, int line)
{
if (!condition)
{
switch (warningLevel)
{
case WARNING_ENABLED:
FAIL() << "[ FAILED ]: " << message << " in file " << file << " on line " << line << std::endl;
break;
case WARNING_DISABLED:
// No action
break;
case WARNING_REDUCED:
std::clog << "[[ WARNING ]] TEST FAILURE NOT EVALUATED: " << message << " in file " << file <<
" on line " << line << std::endl;
break;
default:
break;
}
}
}
/**
* @brief Function to detect whether tests are running with CMAKE build or locally manually.
* @return Returns true if it is running with CMAKE build, otherwise false.
*/
inline bool IsRunningTestsWithCmakeBuild()
{
auto envVar = std::getenv("TEST_EXECUTION_MODE");
if (envVar && std::string(envVar) == "CMake") return true;
else return false;
}
} // namespace sdv_test
/**
* @brief Macro for checking whether tests are running with CMAKE build or locally manually.
* Returns true if it is running with CMAKE build, otherwise false.
*/
#define SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD sdv_test::IsRunningTestsWithCmakeBuild()
/**
* @brief Helper macro to handle warning levels.
* @param[in] level The warning level.
* @param[in] statement The statement to execute.
* @param[in] val1 The first value for comparison.
* @param[in] val2 The second value for comparison.
* @param[in] condition The condition to check.
*/
#define HANDLE_WARNING_LEVEL(level, statement, val1, val2, condition) \
do \
{ \
switch (level) \
{ \
case sdv_test::EWarningLevel::WARNING_ENABLED: \
statement; \
break; \
case sdv_test::EWarningLevel::WARNING_REDUCED: \
if (val1 condition val2) statement; \
else \
{ \
std::ostringstream oss; \
oss << "[[ WARNING ]] TEST FAILURE NOT EVALUATED: Condition did not match for [" \
<< #val1 "] and [" #val2 << "] in file " << __FILE__ << " on line " << __LINE__; \
std::clog << oss.str() << std::endl; \
} \
break; \
case sdv_test::EWarningLevel::WARNING_DISABLED: /* No action */ \
break; \
default: \
break; \
} \
} while (0)
/**
* @brief Redefine GTEST macros with warning level.
* @param[in] val1 The first value.
* @param[in] val2 The second value.
* @param[in] level The warning level.
*/
#define SDV_EXPECT_EQ(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_EQ(val1, val2), val1, val2, ==)
#define SDV_ASSERT_EQ(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_EQ(val1, val2), val1, val2, ==)
#define SDV_EXPECT_NE(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_NE(val1, val2), val1, val2, !=)
#define SDV_ASSERT_NE(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_NE(val1, val2), val1, val2, !=)
#define SDV_EXPECT_TRUE(condition, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_TRUE(condition), condition, true, ==)
#define SDV_ASSERT_TRUE(condition, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_TRUE(condition), condition, true, ==)
#define SDV_EXPECT_FALSE(condition, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_FALSE(condition), condition, false, ==)
#define SDV_ASSERT_FALSE(condition, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_FALSE(condition), condition, false, ==)
#define SDV_EXPECT_LT(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_LT(val1, val2), val1, val2, <)
#define SDV_ASSERT_LT(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_LT(val1, val2), val1, val2, <)
#define SDV_EXPECT_LE(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_LE(val1, val2), val1, val2, <=)
#define SDV_ASSERT_LE(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_LE(val1, val2), val1, val2, <=)
#define SDV_EXPECT_GT(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_GT(val1, val2), val1, val2, >)
#define SDV_ASSERT_GT(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_GT(val1, val2), val1, val2, >)
#define SDV_EXPECT_GE(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_GE(val1, val2), val1, val2, >=)
#define SDV_ASSERT_GE(val1, val2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_GE(val1, val2), val1, val2, >=)
#define SDV_EXPECT_STREQ(str1, str2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_STREQ((str1).c_str(), (str2).c_str()), str1, str2, ==)
#define SDV_ASSERT_STREQ(str1, str2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_STREQ((str1).c_str(), (str2).c_str()), str1, str2, ==)
#define SDV_EXPECT_STRNE(str1, str2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_STRNE(str1.c_str(), str2.c_str()), str1, str2, !=)
#define SDV_ASSERT_STRNE(str1, str2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_STRNE(str1.c_str(), str2.c_str()), str1, str2, !=)
#define SDV_EXPECT_STRCASEEQ(str1, str2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_STRCASEEQ((str1).c_str(), (str2).c_str()), str1, str2, ==)
#define SDV_ASSERT_STRCASEEQ(str1, str2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_STRCASEEQ((str1).c_str(), (str2).c_str()), str1, str2, ==)
#define SDV_EXPECT_STRCASENE(str1, str2, level) \
HANDLE_WARNING_LEVEL(level, EXPECT_STRCASENE((str1).c_str(), (str2).c_str()), str1, str2, !=)
#define SDV_ASSERT_STRCASENE(str1, str2, level) \
HANDLE_WARNING_LEVEL(level, ASSERT_STRCASENE((str1).c_str(), (str2).c_str()), str1, str2, !=)
/**
* @brief Macro for equality check (==) with warning reporting for time critical tests.
* @param[in] val1 The first value.
* @param[in] val2 The second value.
* @param[in] warningLevel The level of warning to display.
*/
#define SDV_EXPECT_EQ_WARN(val1, val2, warningLevel) \
do { \
auto expr1 = val1; \
auto expr2 = val2; \
sdv_test::ReportWarning((expr1) == (expr2), warningLevel, \
"Expected " #val1 " == " #val2 " (" #val1 "=" + std::to_string(expr1) + ", " #val2 "=" + std::to_string(expr2) + ")", \
__FILE__, __LINE__); \
} while (0)
/**
* @brief Macro for inequality check (!=) with warning reporting for time critical tests.
* @param[in] val1 The first value.
* @param[in] val2 The second value.
* @param[in] warningLevel The level of warning to display.
*/
#define SDV_EXPECT_NE_WARN(val1, val2, warningLevel) \
do { \
auto expr1 = val1; \
auto expr2 = val2; \
sdv_test::ReportWarning((expr1) != (expr2), warningLevel, \
"Expected " #val1 " != " #val2 " (" #val1 "=" + std::to_string(expr1) + ", " #val2 "=" + std::to_string(expr2) + ")", \
__FILE__, __LINE__); \
} while (0)
/**
* @brief Macro for greater-than check (>) with warning reporting for time critical tests.
* @param[in] val1 The first value.
* @param[in] val2 The second value.
* @param[in] warningLevel The level of warning to display.
*/
#define SDV_EXPECT_GT_WARN(val1, val2, warningLevel) \
do { \
auto expr1 = val1; \
auto expr2 = val2; \
sdv_test::ReportWarning((expr1) > (expr2), warningLevel, \
"Expected " #val1 " > " #val2 " (" #val1 "=" + std::to_string(expr1) + ", " #val2 "=" + std::to_string(expr2) + ")", \
__FILE__, __LINE__); \
} while (0)
/**
* @brief Macro for less-than check (<) with warning reporting for time critical tests.
* @param[in] val1 The first value.
* @param[in] val2 The second value.
* @param[in] warningLevel The level of warning to display.
*/
#define SDV_EXPECT_LT_WARN(val1, val2, warningLevel) \
do { \
auto expr1 = val1; \
auto expr2 = val2; \
sdv_test::ReportWarning((expr1) < (expr2), warningLevel, \
"Expected " #val1 " < " #val2 " (" #val1 "=" + std::to_string(expr1) + ", " #val2 "=" + std::to_string(expr2) + ")", \
__FILE__, __LINE__); \
} while (0)
/**
* @brief Macro for greater-than-or-equal-to check (>=) with warning reporting for time critical tests.
* @param[in] val1 The first value.
* @param[in] val2 The second value.
* @param[in] warningLevel The level of warning to display.
*/
#define SDV_EXPECT_GE_WARN(val1, val2, warningLevel) \
do { \
auto expr1 = val1; \
auto expr2 = val2; \
sdv_test::ReportWarning((expr1) >= (expr2), warningLevel, \
"Expected " #val1 " >= " #val2 " (" #val1 "=" + std::to_string(expr1) + ", " #val2 "=" + std::to_string(expr2) + ")", \
__FILE__, __LINE__); \
} while (0)
/**
* @brief Macro for less-than-or-equal-to check (<=) with warning reporting for time critical tests.
* @param[in] val1 The first value.
* @param[in] val2 The second value.
* @param[in] warningLevel The level of warning to display.
*/
#define SDV_EXPECT_LE_WARN(val1, val2, warningLevel) \
do { \
auto expr1 = val1; \
auto expr2 = val2; \
sdv_test::ReportWarning((expr1) <= (expr2), warningLevel, \
"Expected " #val1 " <= " #val2 " (" #val1 "=" + std::to_string(expr1) + ", " #val2 "=" + std::to_string(expr2) + ")", \
__FILE__, __LINE__); \
} while (0)
#endif // SDV_TEST_MACRO_H

View File

@@ -4,6 +4,7 @@
#include <thread> #include <thread>
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include <atomic>
#ifdef _WIN32 #ifdef _WIN32
// Prevent reassignment of "interface" // Prevent reassignment of "interface"
#pragma push_macro("interface") #pragma push_macro("interface")
@@ -96,7 +97,7 @@ private:
} }
} }
bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate. std::atomic_bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate.
std::thread m_threadWatchdog; ///< The watchdog thread. std::thread m_threadWatchdog; ///< The watchdog thread.
}; };

View File

@@ -4,6 +4,7 @@
#include <thread> #include <thread>
#include <atomic> #include <atomic>
#include <vector> #include <vector>
#include <atomic>
#include <condition_variable> #include <condition_variable>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <interfaces/can.h> #include <interfaces/can.h>
@@ -121,7 +122,7 @@ public:
bool m_messageSent = false; bool m_messageSent = false;
private: private:
bool m_StopThread = false; std::atomic_bool m_StopThread = false;
std::thread m_thSend2DatalinkThread; std::thread m_thSend2DatalinkThread;
mutable std::mutex m_mtxReceivers; mutable std::mutex m_mtxReceivers;
std::set<sdv::can::IReceive*> m_setReceivers; std::set<sdv::can::IReceive*> m_setReceivers;

View File

@@ -3,7 +3,7 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include "../../../global/exec_dir_helper.h" #include "../../../global/exec_dir_helper.h"
#include <support/sdv_test_macro.h> #include "../../include/sdv_test_macro.h"
TEST(CAscWriterTest, AddSamplesDirect) TEST(CAscWriterTest, AddSamplesDirect)
{ {
@@ -108,16 +108,16 @@ TEST(CAscWriterTest, AddTimedSamples)
if (dDeltaTSGenerated < dDeltaTSGroundThruth - 0.002) if (dDeltaTSGenerated < dDeltaTSGroundThruth - 0.002)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(dDeltaTSGenerated, dDeltaTSGroundThruth - 0.002, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_GE_WARN(dDeltaTSGenerated, dDeltaTSGroundThruth - 0.002, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(dDeltaTSGenerated, dDeltaTSGroundThruth - 0.002, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_GE_WARN(dDeltaTSGenerated, dDeltaTSGroundThruth - 0.002, sdv_test::WARNING_ENABLED);
} }
if (dDeltaTSGenerated > dDeltaTSGroundThruth + 0.002) if (dDeltaTSGenerated > dDeltaTSGroundThruth + 0.002)
{ {
if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if(SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(dDeltaTSGenerated, dDeltaTSGroundThruth + 0.002, sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_LE_WARN(dDeltaTSGenerated, dDeltaTSGroundThruth + 0.002, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(dDeltaTSGenerated, dDeltaTSGroundThruth + 0.002, sdv::TEST::WarningLevel::WARNING_ENABLED); SDV_EXPECT_LE_WARN(dDeltaTSGenerated, dDeltaTSGroundThruth + 0.002, sdv_test::WARNING_ENABLED);
} }
std::cout << "TIMING: Expected generated timestamp <= ground truth + 2ms (generated=" << dDeltaTSGenerated << "ms, ground truth=" << dDeltaTSGroundThruth << ")..." << std::endl; std::cout << "TIMING: Expected generated timestamp <= ground truth + 2ms (generated=" << dDeltaTSGenerated << "ms, ground truth=" << dDeltaTSGroundThruth << ")..." << std::endl;
EXPECT_EQ(prSampleGroundTruth.first.uiChannel, prSampleGenerated.first.uiChannel); EXPECT_EQ(prSampleGroundTruth.first.uiChannel, prSampleGenerated.first.uiChannel);
@@ -136,7 +136,7 @@ TEST(CAscWriterTest, AddTimedSamples)
TEST(CAscWriterTest, ExtendedID) TEST(CAscWriterTest, ExtendedID)
{ {
asc::CAscReader readerGroundThruth; asc::CAscReader readerGroundThruth;
SDV_EXPECT_TRUE(readerGroundThruth.Read(GetExecDirectory() / "asc_reader_ext_id_test.asc"), sdv::TEST::WarningLevel::WARNING_REDUCED); SDV_EXPECT_TRUE(readerGroundThruth.Read(GetExecDirectory() / "asc_reader_ext_id_test.asc"), sdv_test::WARNING_REDUCED);
// Add all samples // Add all samples
asc::CAscWriter writer; asc::CAscWriter writer;

View File

@@ -80,10 +80,10 @@ TEST_F(CSerdesTest, DeserializeSimpleEndianSwap)
TEST_F(CSerdesTest, SerializeArray) TEST_F(CSerdesTest, SerializeArray)
{ {
srand((unsigned int)time(0)); std::srand((unsigned int)time(0));
int16_t rguiBuffer[4096]; int16_t rguiBuffer[4096];
for (size_t nIndex = 0; nIndex < 4096; nIndex++) for (size_t nIndex = 0; nIndex < 4096; nIndex++)
rguiBuffer[nIndex] = static_cast<int16_t>(rand()); rguiBuffer[nIndex] = static_cast<int16_t>(std::rand());
sdv::serializer serializer; sdv::serializer serializer;
serializer << rguiBuffer; serializer << rguiBuffer;
@@ -95,10 +95,10 @@ TEST_F(CSerdesTest, SerializeArray)
TEST_F(CSerdesTest, DeserializeArray) TEST_F(CSerdesTest, DeserializeArray)
{ {
srand((unsigned int)time(0)); std::srand((unsigned int)time(0));
int16_t rguiBuffer[4096]; int16_t rguiBuffer[4096];
for (size_t nIndex = 0; nIndex < 4096; nIndex++) for (size_t nIndex = 0; nIndex < 4096; nIndex++)
rguiBuffer[nIndex] = static_cast<int16_t>(rand()); rguiBuffer[nIndex] = static_cast<int16_t>(std::rand());
sdv::serializer serializer; sdv::serializer serializer;
serializer << rguiBuffer; serializer << rguiBuffer;
@@ -113,10 +113,10 @@ TEST_F(CSerdesTest, DeserializeArray)
TEST_F(CSerdesTest, SerializeArrayEndianSwap) TEST_F(CSerdesTest, SerializeArrayEndianSwap)
{ {
srand((unsigned int)time(0)); std::srand((unsigned int)time(0));
int16_t rguiBuffer[4096]; int16_t rguiBuffer[4096];
for (size_t nIndex = 0; nIndex < 4096; nIndex++) for (size_t nIndex = 0; nIndex < 4096; nIndex++)
rguiBuffer[nIndex] = static_cast<int16_t>(rand()); rguiBuffer[nIndex] = static_cast<int16_t>(std::rand());
static constexpr sdv::EEndian eEndian = static constexpr sdv::EEndian eEndian =
sdv::GetPlatformEndianess() == sdv::EEndian::little_endian ? sdv::EEndian::big_endian : sdv::EEndian::little_endian; sdv::GetPlatformEndianess() == sdv::EEndian::little_endian ? sdv::EEndian::big_endian : sdv::EEndian::little_endian;
@@ -139,10 +139,10 @@ TEST_F(CSerdesTest, SerializeArrayEndianSwap)
TEST_F(CSerdesTest, DeserializeArrayEndianSwap) TEST_F(CSerdesTest, DeserializeArrayEndianSwap)
{ {
srand((unsigned int)time(0)); std::srand((unsigned int)time(0));
int16_t rguiBuffer[4096]; int16_t rguiBuffer[4096];
for (size_t nIndex = 0; nIndex < 4096; nIndex++) for (size_t nIndex = 0; nIndex < 4096; nIndex++)
rguiBuffer[nIndex] = static_cast<int16_t>(rand()); rguiBuffer[nIndex] = static_cast<int16_t>(std::rand());
static constexpr sdv::EEndian eEndian = static constexpr sdv::EEndian eEndian =
sdv::GetPlatformEndianess() == sdv::EEndian::little_endian ? sdv::EEndian::big_endian : sdv::EEndian::little_endian; sdv::GetPlatformEndianess() == sdv::EEndian::little_endian ? sdv::EEndian::big_endian : sdv::EEndian::little_endian;

View File

@@ -5,33 +5,33 @@
#include <support/interface_ptr.h> #include <support/interface_ptr.h>
/** /**
* \brief Test class for instantiation tests. * @brief Test class for instantiation tests.
*/ */
class CCommandLineParserTest : public testing::Test class CCommandLineParserTest : public testing::Test
{ {
public: public:
/** /**
* \brief Constructor * @brief Constructor
*/ */
CCommandLineParserTest() = default; CCommandLineParserTest() = default;
/** /**
* \brief Set up the test suite. * @brief Set up the test suite.
*/ */
static void SetUpTestCase(); static void SetUpTestCase();
/** /**
* \brief Tear down the test suite. * @brief Tear down the test suite.
*/ */
static void TearDownTestCase(); static void TearDownTestCase();
/** /**
* \brief Test setup. * @brief Test setup.
*/ */
void SetUp() override; void SetUp() override;
/** /**
* \brief Test teardown. * @brief Test teardown.
*/ */
void TearDown() override; void TearDown() override;
}; };

View File

@@ -98,7 +98,7 @@ struct SMakeNoice
rthread.join(); rthread.join();
} }
bool bShutdown = false; ///< Run threads until shutdown is set. std::atomic_bool bShutdown = false; ///< Run threads until shutdown is set.
std::atomic_size_t nStarted = 0; ///< Amount of threads that were started. This is to wait for all threads to start. std::atomic_size_t nStarted = 0; ///< Amount of threads that were started. This is to wait for all threads to start.
std::thread rgThreads[nAmount]; ///< The noise generating threads. std::thread rgThreads[nAmount]; ///< The noise generating threads.
}; };
@@ -383,7 +383,7 @@ TEST(ConcurrencyTest, ConditionVarWaitForPrediction_SeparateMutexForEachThread_I
std::atomic_size_t nCnt = 0; std::atomic_size_t nCnt = 0;
std::atomic_size_t nThreadCnt = 0; std::atomic_size_t nThreadCnt = 0;
std::atomic_size_t nViolationCnt = 0; std::atomic_size_t nViolationCnt = 0;
bool b1 = false, b2 = false, b3 = false, b4 = false; std::atomic_bool b1 = false, b2 = false, b3 = false, b4 = false;
SMakeNoice noice; SMakeNoice noice;
std::thread thread1([&]() std::thread thread1([&]()
{ {

View File

@@ -5,33 +5,33 @@
#include <support/interface_ptr.h> #include <support/interface_ptr.h>
/** /**
* \brief Test class for instantiation tests. * @brief Test class for instantiation tests.
*/ */
class CDbcParserTest : public testing::Test class CDbcParserTest : public testing::Test
{ {
public: public:
/** /**
* \brief Constructor * @brief Constructor
*/ */
CDbcParserTest() = default; CDbcParserTest() = default;
/** /**
* \brief Set up the test suite. * @brief Set up the test suite.
*/ */
static void SetUpTestCase(); static void SetUpTestCase();
/** /**
* \brief Tear down the test suite. * @brief Tear down the test suite.
*/ */
static void TearDownTestCase(); static void TearDownTestCase();
/** /**
* \brief Test setup. * @brief Test setup.
*/ */
void SetUp() override; void SetUp() override;
/** /**
* \brief Test teardown. * @brief Test teardown.
*/ */
void TearDown() override; void TearDown() override;
}; };

View File

@@ -2,33 +2,33 @@
#define LEXER_TEST_H #define LEXER_TEST_H
/** /**
* \brief Test class for instantiation tests. * @brief Test class for instantiation tests.
*/ */
class CLexerTest : public testing::Test class CLexerTest : public testing::Test
{ {
public: public:
/** /**
* \brief Constructor * @brief Constructor
*/ */
CLexerTest() = default; CLexerTest() = default;
/** /**
* \brief Set up the test suite. * @brief Set up the test suite.
*/ */
static void SetUpTestCase(); static void SetUpTestCase();
/** /**
* \brief Tear down the test suite. * @brief Tear down the test suite.
*/ */
static void TearDownTestCase(); static void TearDownTestCase();
/** /**
* \brief Test setup. * @brief Test setup.
*/ */
void SetUp() override; void SetUp() override;
/** /**
* \brief Test teardown. * @brief Test teardown.
*/ */
void TearDown() override; void TearDown() override;

View File

@@ -2,33 +2,33 @@
#define PARSER_TEST_H #define PARSER_TEST_H
/** /**
* \brief Test class for instantiation tests. * @brief Test class for instantiation tests.
*/ */
class CParserTest : public testing::Test class CParserTest : public testing::Test
{ {
public: public:
/** /**
* \brief Constructor * @brief Constructor
*/ */
CParserTest() = default; CParserTest() = default;
/** /**
* \brief Set up the test suite. * @brief Set up the test suite.
*/ */
static void SetUpTestCase(); static void SetUpTestCase();
/** /**
* \brief Tear down the test suite. * @brief Tear down the test suite.
*/ */
static void TearDownTestCase(); static void TearDownTestCase();
/** /**
* \brief Test setup. * @brief Test setup.
*/ */
void SetUp() override; void SetUp() override;
/** /**
* \brief Test teardown. * @brief Test teardown.
*/ */
void TearDown() override; void TearDown() override;

View File

@@ -9,8 +9,10 @@
#include "../../../sdv_services/core/installation_composer.cpp" #include "../../../sdv_services/core/installation_composer.cpp"
#include "../../../sdv_services/core/toml_parser/parser_toml.cpp" #include "../../../sdv_services/core/toml_parser/parser_toml.cpp"
#include "../../../sdv_services/core/toml_parser/lexer_toml.cpp" #include "../../../sdv_services/core/toml_parser/lexer_toml.cpp"
#include "../../../sdv_services/core/toml_parser/lexer_toml_token.cpp"
#include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp" #include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp"
#include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp"
#include "../../../sdv_services/core/toml_parser/miscellaneous.cpp"
#include <support/app_control.h> #include <support/app_control.h>
#if defined(_WIN32) && defined(_UNICODE) #if defined(_WIN32) && defined(_UNICODE)

View File

@@ -2108,7 +2108,7 @@ TEST_F(CInstallPackageComposerTest, DetectPackageCorruptionContent)
for (size_t n = 0; n < 25; n++) for (size_t n = 0; n < 25; n++)
{ {
// Corrupt the package at the content (from 100 until the size minus 32) // Corrupt the package at the content (from 100 until the size minus 32)
size_t nPos = static_cast<size_t>(rand()) * (ptrPackage.size() - 132) / RAND_MAX + 100; size_t nPos = static_cast<size_t>(std::rand()) * (ptrPackage.size() - 132) / RAND_MAX + 100;
uint8_t uiStoredByte = ptrPackage.get()[nPos]; uint8_t uiStoredByte = ptrPackage.get()[nPos];
ptrPackage.get()[nPos] = ~uiStoredByte; ptrPackage.get()[nPos] = ~uiStoredByte;
@@ -2337,7 +2337,7 @@ TEST_F(CInstallPackageComposerTest, VerifyIntegrityCorruptionContent)
for (size_t n = 0; n < 25; n++) for (size_t n = 0; n < 25; n++)
{ {
// Corrupt the package at the content (from 100 until the size minus 32) // Corrupt the package at the content (from 100 until the size minus 32)
size_t nPos = static_cast<size_t>(rand()) * (ptrPackage.size() - 132) / RAND_MAX + 100; size_t nPos = static_cast<size_t>(std::rand()) * (ptrPackage.size() - 132) / RAND_MAX + 100;
uint8_t uiStoredByte = ptrPackage.get()[nPos]; uint8_t uiStoredByte = ptrPackage.get()[nPos];
ptrPackage.get()[nPos] = ~uiStoredByte; ptrPackage.get()[nPos] = ~uiStoredByte;

View File

@@ -3,6 +3,7 @@
#include "../../../sdv_services/ipc_com/com_ctrl.cpp" #include "../../../sdv_services/ipc_com/com_ctrl.cpp"
#include "../../../sdv_services/ipc_com/com_channel.cpp" #include "../../../sdv_services/ipc_com/com_channel.cpp"
#include "../../../sdv_services/ipc_com/marshall_object.cpp" #include "../../../sdv_services/ipc_com/marshall_object.cpp"
#include <cstring>
/** /**
* @brief Main function * @brief Main function
@@ -12,9 +13,30 @@ extern "C" int wmain(int argc, wchar_t* argv[])
#else #else
extern "C" int main(int argc, char* argv[]) extern "C" int main(int argc, char* argv[])
#endif #endif
{
// Check for the --gtest_repeat option.
bool bRepeatEnabled = false;
for (int iIndex = 0; iIndex < argc; iIndex++)
{
if (!argv[iIndex]) continue;
#if defined(_WIN32) && defined(_UNICODE)
bRepeatEnabled |= std::wcsncmp(argv[iIndex], L"--gtest_repeat", 14) == 0;
#else
bRepeatEnabled |= std::strncmp(argv[iIndex], "--gtest_repeat", 14) == 0;
#endif
}
// When repeat is enabled, do not enable the watchdog.
if (bRepeatEnabled)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
else
{ {
CProcessWatchdog watchdog; CProcessWatchdog watchdog;
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }
}

View File

@@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <list> #include <list>
#include <deque> #include <deque>
#include <atomic>
TEST(TraceFifoTest, Connect_Disconnect) TEST(TraceFifoTest, Connect_Disconnect)
{ {
@@ -190,7 +191,7 @@ TEST(TraceFifoTest, Simple_Publish_Monitor)
EXPECT_TRUE(fifoReader.IsOpened()); EXPECT_TRUE(fifoReader.IsOpened());
std::vector<std::string> vecSent, vecReceived; std::vector<std::string> vecSent, vecReceived;
bool bShutdown = false; std::atomic_bool bShutdown = false;
// Start receiving thread until shutdown flag is set. // Start receiving thread until shutdown flag is set.
std::thread thread([&]() std::thread thread([&]()
@@ -238,7 +239,7 @@ TEST(TraceFifoTest, Simple_Publish_Monitor_Multi)
EXPECT_TRUE(fifoReader2.IsOpened()); EXPECT_TRUE(fifoReader2.IsOpened());
std::vector<std::string> vecSent, vecReceived1, vecReceived2; std::vector<std::string> vecSent, vecReceived1, vecReceived2;
bool bShutdown = false; std::atomic_bool bShutdown = false;
// Start receiving thread until shutdown flag is set. // Start receiving thread until shutdown flag is set.
std::thread thread1([&]() std::thread thread1([&]()
@@ -297,7 +298,7 @@ TEST(TraceFifoTest, Simple_Publish_Beyond_Buffer_With_Reading)
EXPECT_TRUE(fifoReader.IsOpened()); EXPECT_TRUE(fifoReader.IsOpened());
std::vector<std::string> vecSent, vecReceived; std::vector<std::string> vecSent, vecReceived;
bool bShutdown = false; std::atomic_bool bShutdown = false;
// Start receiving thread until shutdown flag is set. // Start receiving thread until shutdown flag is set.
std::thread thread([&]() std::thread thread([&]()

View File

@@ -3,8 +3,10 @@
#include "mock.h" #include "mock.h"
#include "../../../sdv_services/core/toml_parser/parser_toml.cpp" #include "../../../sdv_services/core/toml_parser/parser_toml.cpp"
#include "../../../sdv_services/core/toml_parser/lexer_toml.cpp" #include "../../../sdv_services/core/toml_parser/lexer_toml.cpp"
#include "../../../sdv_services/core/toml_parser/lexer_toml_token.cpp"
#include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp" #include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp"
#include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp"
#include "../../../sdv_services/core/toml_parser/miscellaneous.cpp"
#include "../../../sdv_services/core/module_control.cpp" #include "../../../sdv_services/core/module_control.cpp"
#include "../../../sdv_services/core/module.cpp" #include "../../../sdv_services/core/module.cpp"
#include "../../../sdv_services/core/app_config.cpp" #include "../../../sdv_services/core/app_config.cpp"

View File

@@ -11,13 +11,7 @@
class CMock class CMock
{ {
public: public:
CMock() : m_root("root") CMock() {}
{
auto ptrElement = std::make_shared<CNormalTable>("Install");
m_root.AddElement(ptrElement);
auto ptrValue = std::make_shared<CStringNode>("Directory", "install/test");
ptrElement->AddElement(ptrValue);
}
void DestroyModuleObjects(sdv::core::TModuleID) {} void DestroyModuleObjects(sdv::core::TModuleID) {}
bool IsStandaloneApplication() { return true; } bool IsStandaloneApplication() { return true; }
bool IsEssentialApplication() { return false; } bool IsEssentialApplication() { return false; }
@@ -40,7 +34,6 @@ public:
sdv::core::TModuleID ContextLoad(const std::filesystem::path&, const sdv::u8string&) { return 0; } sdv::core::TModuleID ContextLoad(const std::filesystem::path&, const sdv::u8string&) { return 0; }
bool ContextUnload(sdv::core::TModuleID, bool) { return false; } bool ContextUnload(sdv::core::TModuleID, bool) { return false; }
CNormalTable m_root;
bool m_bIsMain = false; bool m_bIsMain = false;
bool m_bIsIsolated = false; bool m_bIsIsolated = false;
}; };

View File

@@ -3,6 +3,7 @@
#include "../../../global/ipc_named_mutex.h" #include "../../../global/ipc_named_mutex.h"
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <atomic>
#if defined(_WIN32) && defined(_UNICODE) #if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int argc, wchar_t* argv[]) extern "C" int wmain(int argc, wchar_t* argv[])
@@ -28,19 +29,19 @@ TEST(NamedMutexTest, CritSectSyncManualLock)
// The checking is manipulated by the bEnable flag. When disabled, no sync will be done and the check will fail. When enabled, // The checking is manipulated by the bEnable flag. When disabled, no sync will be done and the check will fail. When enabled,
// sync will be done and the check will succeed. // sync will be done and the check will succeed.
int32_t iCnt = 0; int32_t iCnt = 0;
bool bSuccess = true; std::atomic_bool bSuccess = true;
bool bEnable = false; std::atomic_bool bEnable = false;
auto fn = [&]() auto fn = [&]()
{ {
ipc::named_mutex mtx("HELLO"); ipc::named_mutex mtx("HELLO");
if (bEnable) if (bEnable)
mtx.lock(); mtx.lock();
bSuccess &= (iCnt == 0); bSuccess = bSuccess && (iCnt == 0);
iCnt++; iCnt++;
std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 100)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 100));
iCnt--; iCnt--;
bSuccess &= (iCnt == 0); bSuccess = bSuccess && (iCnt == 0);
if (bEnable) if (bEnable)
mtx.unlock(); mtx.unlock();
}; };
@@ -69,17 +70,17 @@ TEST(NamedMutexTest, CritSectSyncAutoLock)
{ {
// Counter function check for correct counter value. // Counter function check for correct counter value.
int32_t iCnt = 0; int32_t iCnt = 0;
bool bSuccess = true; std::atomic_bool bSuccess = true;
auto fn = [&]() auto fn = [&]()
{ {
ipc::named_mutex mtx("HELLO"); ipc::named_mutex mtx("HELLO");
std::unique_lock<ipc::named_mutex> lock(mtx); std::unique_lock<ipc::named_mutex> lock(mtx);
bSuccess &= (iCnt == 0); bSuccess = bSuccess && (iCnt == 0);
iCnt++; iCnt++;
std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 100)); std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 100));
iCnt--; iCnt--;
bSuccess &= (iCnt == 0); bSuccess = bSuccess && (iCnt == 0);
}; };
// Test sync // Test sync
@@ -94,7 +95,7 @@ TEST(NamedMutexTest, CritSectSyncAutoLock)
TEST(NamedMutexTest, TryLock) TEST(NamedMutexTest, TryLock)
{ {
bool bRunning = false; std::atomic_bool bRunning = false;
auto fn = [&]() auto fn = [&]()
{ {
ipc::named_mutex mtx("HELLO"); ipc::named_mutex mtx("HELLO");

View File

@@ -3,8 +3,10 @@
#include "mock.h" #include "mock.h"
#include "../../../sdv_services/core/toml_parser/parser_toml.cpp" #include "../../../sdv_services/core/toml_parser/parser_toml.cpp"
#include "../../../sdv_services/core/toml_parser/lexer_toml.cpp" #include "../../../sdv_services/core/toml_parser/lexer_toml.cpp"
#include "../../../sdv_services/core/toml_parser/lexer_toml_token.cpp"
#include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp" #include "../../../sdv_services/core/toml_parser/parser_node_toml.cpp"
#include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp" #include "../../../sdv_services/core/toml_parser/character_reader_utf_8.cpp"
#include "../../../sdv_services/core/toml_parser/miscellaneous.cpp"
#include "../../../sdv_services/core/module_control.cpp" #include "../../../sdv_services/core/module_control.cpp"
#include "../../../sdv_services/core/module.cpp" #include "../../../sdv_services/core/module.cpp"
#include "../../../sdv_services/core/repository.cpp" #include "../../../sdv_services/core/repository.cpp"

View File

@@ -1,6 +1,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "../../../global/process_watchdog.h" #include "../../../global/process_watchdog.h"
#include "support/sdv_test_macro.h" #include "../../include/sdv_test_macro.h"
#if defined(_WIN32) && defined(_UNICODE) #if defined(_WIN32) && defined(_UNICODE)
extern "C" int wmain(int argc, wchar_t* argv[]) extern "C" int wmain(int argc, wchar_t* argv[])
@@ -19,9 +19,9 @@ TEST(SDVTestMacros, TestExpectEq)
int val1 = 5; int val1 = 5;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_EQ(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_EQ(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_EQ(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_EQ(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertEq) TEST(SDVTestMacros, TestAssertEq)
@@ -29,9 +29,9 @@ TEST(SDVTestMacros, TestAssertEq)
int val1 = 5; int val1 = 5;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_EQ(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_EQ(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_EQ(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_EQ(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectNe) TEST(SDVTestMacros, TestExpectNe)
@@ -39,9 +39,9 @@ TEST(SDVTestMacros, TestExpectNe)
int val1 = 5; int val1 = 5;
int val2 = 6; int val2 = 6;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_NE(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_NE(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_NE(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_NE(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertNe) TEST(SDVTestMacros, TestAssertNe)
@@ -49,45 +49,45 @@ TEST(SDVTestMacros, TestAssertNe)
int val1 = 5; int val1 = 5;
int val2 = 6; int val2 = 6;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_NE(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_NE(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_NE(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_NE(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectTrue) TEST(SDVTestMacros, TestExpectTrue)
{ {
bool condition = true; bool condition = true;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_TRUE(condition, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_TRUE(condition, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_TRUE(condition, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_TRUE(condition, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertTrue) TEST(SDVTestMacros, TestAssertTrue)
{ {
bool condition = true; bool condition = true;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_TRUE(condition, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_TRUE(condition, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_TRUE(condition, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_TRUE(condition, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectFalse) TEST(SDVTestMacros, TestExpectFalse)
{ {
bool condition = false; bool condition = false;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_FALSE(condition, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_FALSE(condition, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_FALSE(condition, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_FALSE(condition, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertFalse) TEST(SDVTestMacros, TestAssertFalse)
{ {
bool condition = false; bool condition = false;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_FALSE(condition, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_FALSE(condition, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_FALSE(condition, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_FALSE(condition, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectLt) TEST(SDVTestMacros, TestExpectLt)
@@ -95,9 +95,9 @@ TEST(SDVTestMacros, TestExpectLt)
int val1 = 5; int val1 = 5;
int val2 = 6; int val2 = 6;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_LT(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_LT(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_LT(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_LT(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertLt) TEST(SDVTestMacros, TestAssertLt)
@@ -105,9 +105,9 @@ TEST(SDVTestMacros, TestAssertLt)
int val1 = 5; int val1 = 5;
int val2 = 6; int val2 = 6;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_LT(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_LT(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_LT(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_LT(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectLe) TEST(SDVTestMacros, TestExpectLe)
@@ -115,9 +115,9 @@ TEST(SDVTestMacros, TestExpectLe)
int val1 = 5; int val1 = 5;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_LE(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_LE(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_LE(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_LE(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertLe) TEST(SDVTestMacros, TestAssertLe)
@@ -125,9 +125,9 @@ TEST(SDVTestMacros, TestAssertLe)
int val1 = 5; int val1 = 5;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_LE(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_LE(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_LE(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_LE(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectGt) TEST(SDVTestMacros, TestExpectGt)
@@ -135,9 +135,9 @@ TEST(SDVTestMacros, TestExpectGt)
int val1 = 6; int val1 = 6;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_GT(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_GT(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_GT(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_GT(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertGt) TEST(SDVTestMacros, TestAssertGt)
@@ -145,9 +145,9 @@ TEST(SDVTestMacros, TestAssertGt)
int val1 = 6; int val1 = 6;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_GT(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_GT(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_GT(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_GT(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectGe) TEST(SDVTestMacros, TestExpectGe)
@@ -155,9 +155,9 @@ TEST(SDVTestMacros, TestExpectGe)
int val1 = 6; int val1 = 6;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_GE(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_GE(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_GE(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_GE(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertGe) TEST(SDVTestMacros, TestAssertGe)
@@ -165,9 +165,9 @@ TEST(SDVTestMacros, TestAssertGe)
int val1 = 6; int val1 = 6;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_GE(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_GE(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_GE(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_GE(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectStreq) TEST(SDVTestMacros, TestExpectStreq)
@@ -175,9 +175,9 @@ TEST(SDVTestMacros, TestExpectStreq)
std::string str1 = "test"; std::string str1 = "test";
std::string str2 = "test"; std::string str2 = "test";
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_STREQ(str1, str2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_STREQ(str1, str2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_STREQ(str1, str2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_STREQ(str1, str2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertStreq) TEST(SDVTestMacros, TestAssertStreq)
@@ -185,9 +185,9 @@ TEST(SDVTestMacros, TestAssertStreq)
std::string str1 = "test"; std::string str1 = "test";
std::string str2 = "test"; std::string str2 = "test";
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_STREQ(str1, str2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_STREQ(str1, str2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_STREQ(str1, str2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_STREQ(str1, str2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectStrne) TEST(SDVTestMacros, TestExpectStrne)
@@ -195,9 +195,9 @@ TEST(SDVTestMacros, TestExpectStrne)
std::string str1 = "test"; std::string str1 = "test";
std::string str2 = "test1"; std::string str2 = "test1";
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_STRNE(str1, str2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_STRNE(str1, str2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_STRNE(str1, str2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_STRNE(str1, str2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertStrne) TEST(SDVTestMacros, TestAssertStrne)
@@ -205,9 +205,9 @@ TEST(SDVTestMacros, TestAssertStrne)
std::string str1 = "test"; std::string str1 = "test";
std::string str2 = "test1"; std::string str2 = "test1";
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_STRNE(str1, str2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_STRNE(str1, str2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_STRNE(str1, str2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_STRNE(str1, str2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectStrcaseeq) TEST(SDVTestMacros, TestExpectStrcaseeq)
@@ -215,9 +215,9 @@ TEST(SDVTestMacros, TestExpectStrcaseeq)
std::string str1 = "test"; std::string str1 = "test";
std::string str2 = "TEST"; std::string str2 = "TEST";
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_STRCASEEQ(str1, str2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_STRCASEEQ(str1, str2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_STRCASEEQ(str1, str2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_STRCASEEQ(str1, str2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertStrcaseeq) TEST(SDVTestMacros, TestAssertStrcaseeq)
@@ -225,9 +225,9 @@ TEST(SDVTestMacros, TestAssertStrcaseeq)
std::string str1 = "test"; std::string str1 = "test";
std::string str2 = "TEST"; std::string str2 = "TEST";
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_STRCASEEQ(str1, str2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_STRCASEEQ(str1, str2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_STRCASEEQ(str1, str2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_STRCASEEQ(str1, str2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestExpectStrcasene) TEST(SDVTestMacros, TestExpectStrcasene)
@@ -235,9 +235,9 @@ TEST(SDVTestMacros, TestExpectStrcasene)
std::string str1 = "test"; std::string str1 = "test";
std::string str2 = "TEST1"; std::string str2 = "TEST1";
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_EXPECT_STRCASENE(str1, str2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_STRCASENE(str1, str2, sdv_test::WARNING_REDUCED);
else else
SDV_EXPECT_STRCASENE(str1, str2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_STRCASENE(str1, str2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestAssertStrcasene) TEST(SDVTestMacros, TestAssertStrcasene)
@@ -245,9 +245,9 @@ TEST(SDVTestMacros, TestAssertStrcasene)
std::string str1 = "test"; std::string str1 = "test";
std::string str2 = "TEST1"; std::string str2 = "TEST1";
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_ASSERT_STRCASENE(str1, str2, sdv::TEST::WARNING_REDUCED); SDV_ASSERT_STRCASENE(str1, str2, sdv_test::WARNING_REDUCED);
else else
SDV_ASSERT_STRCASENE(str1, str2, sdv::TEST::WARNING_ENABLED); SDV_ASSERT_STRCASENE(str1, str2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestTimingExpectEq) TEST(SDVTestMacros, TestTimingExpectEq)
@@ -255,9 +255,9 @@ TEST(SDVTestMacros, TestTimingExpectEq)
int val1 = 5; int val1 = 5;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_EQ(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_EQ_WARN(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_EQ(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_EQ_WARN(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestTimingExpectNe) TEST(SDVTestMacros, TestTimingExpectNe)
@@ -265,9 +265,9 @@ TEST(SDVTestMacros, TestTimingExpectNe)
int val1 = 5; int val1 = 5;
int val2 = 6; int val2 = 6;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_NE(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_NE_WARN(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_NE(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_NE_WARN(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestTimingExpectGt) TEST(SDVTestMacros, TestTimingExpectGt)
@@ -275,9 +275,9 @@ TEST(SDVTestMacros, TestTimingExpectGt)
int val1 = 6; int val1 = 6;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GT(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_GT_WARN(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GT(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_GT_WARN(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestTimingExpectLt) TEST(SDVTestMacros, TestTimingExpectLt)
@@ -285,9 +285,9 @@ TEST(SDVTestMacros, TestTimingExpectLt)
int val1 = 5; int val1 = 5;
int val2 = 6; int val2 = 6;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LT(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_LT_WARN(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LT(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_LT_WARN(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestTimingExpectGe) TEST(SDVTestMacros, TestTimingExpectGe)
@@ -295,9 +295,9 @@ TEST(SDVTestMacros, TestTimingExpectGe)
int val1 = 6; int val1 = 6;
int val2 = 5; int val2 = 5;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_GE(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_GE_WARN(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_GE(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_GE_WARN(val1, val2, sdv_test::WARNING_ENABLED);
} }
TEST(SDVTestMacros, TestTimingExpectLe) TEST(SDVTestMacros, TestTimingExpectLe)
@@ -305,7 +305,7 @@ TEST(SDVTestMacros, TestTimingExpectLe)
int val1 = 5; int val1 = 5;
int val2 = 6; int val2 = 6;
if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD) if (SDV_IS_RUNNING_TESTS_WITH_CMAKE_BUILD)
SDV_TIMING_EXPECT_LE(val1, val2, sdv::TEST::WARNING_REDUCED); SDV_EXPECT_LE_WARN(val1, val2, sdv_test::WARNING_REDUCED);
else else
SDV_TIMING_EXPECT_LE(val1, val2, sdv::TEST::WARNING_ENABLED); SDV_EXPECT_LE_WARN(val1, val2, sdv_test::WARNING_ENABLED);
} }

View File

@@ -53,16 +53,11 @@ endif()
# Add the communication unittest # Add the communication unittest
add_test(NAME UnitTest_InprocMemTests COMMAND UnitTest_InprocMemTests) add_test(NAME UnitTest_InprocMemTests COMMAND UnitTest_InprocMemTests)
#TODO Shared memory tests during complete rebuild fail on Windows when compiling with MINGW. This is due to race conditions occuring
# only when the system is under heavy load (like during a complete rebuild). The tests have been disabled for the moment and a
# bug report is filed here: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608134
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
# Execute the test # Execute the test
add_custom_command(TARGET UnitTest_InprocMemTests POST_BUILD add_custom_command(TARGET UnitTest_InprocMemTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_InprocMemTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_InprocMemTests.xml COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_InprocMemTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_InprocMemTests.xml
VERBATIM VERBATIM
) )
endif()
# Shared mem buffer test # Shared mem buffer test
add_executable(UnitTest_SharedMemBufferTests add_executable(UnitTest_SharedMemBufferTests
@@ -85,16 +80,11 @@ endif()
# Add the communication unittest # Add the communication unittest
add_test(NAME UnitTest_SharedMemBufferTests COMMAND UnitTest_SharedMemBufferTests) add_test(NAME UnitTest_SharedMemBufferTests COMMAND UnitTest_SharedMemBufferTests)
#TODO Shared memory tests during complete rebuild fail on Windows when compiling with MINGW. This is due to race conditions occuring
# only when the system is under heavy load (like during a complete rebuild). The tests have been disabled for the moment and a
# bug report is filed here: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608134
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
# Execute the test # Execute the test
add_custom_command(TARGET UnitTest_SharedMemBufferTests POST_BUILD add_custom_command(TARGET UnitTest_SharedMemBufferTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_SharedMemBufferTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SharedMemBufferTests.xml COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_SharedMemBufferTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SharedMemBufferTests.xml
VERBATIM VERBATIM
) )
endif()
# Shared mem connection test # Shared mem connection test
add_executable(UnitTest_SharedMemConnectTests add_executable(UnitTest_SharedMemConnectTests
@@ -115,16 +105,11 @@ endif()
# Add the communication unittest # Add the communication unittest
add_test(NAME UnitTest_SharedMemConnectTests COMMAND UnitTest_SharedMemConnectTests) add_test(NAME UnitTest_SharedMemConnectTests COMMAND UnitTest_SharedMemConnectTests)
#TODO Shared memory tests during complete rebuild fail on Windows when compiling with MINGW. This is due to race conditions occuring
# only when the system is under heavy load (like during a complete rebuild). The tests have been disabled for the moment and a
# bug report is filed here: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608134
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
# Execute the test # Execute the test
add_custom_command(TARGET UnitTest_SharedMemConnectTests POST_BUILD add_custom_command(TARGET UnitTest_SharedMemConnectTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_SharedMemConnectTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SharedMemConnectTests.xml COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_SharedMemConnectTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SharedMemConnectTests.xml
VERBATIM VERBATIM
) )
endif()
# Shared mem large data test # Shared mem large data test
@@ -146,16 +131,11 @@ endif()
# Add the communication unittest # Add the communication unittest
add_test(NAME UnitTest_SharedMemLargeDataTests COMMAND UnitTest_SharedMemLargeDataTests) add_test(NAME UnitTest_SharedMemLargeDataTests COMMAND UnitTest_SharedMemLargeDataTests)
#TODO Shared memory tests during complete rebuild fail on Windows when compiling with MINGW. This is due to race conditions occuring
# only when the system is under heavy load (like during a complete rebuild). The tests have been disabled for the moment and a
# bug report is filed here: https://dev.azure.com/SW4ZF/AZP-074_DivDI_SofDCarResearch/_workitems/edit/608134
if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32))
# Execute the test # Execute the test
add_custom_command(TARGET UnitTest_SharedMemLargeDataTests POST_BUILD add_custom_command(TARGET UnitTest_SharedMemLargeDataTests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_SharedMemLargeDataTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SharedMemLargeDataTests.xml COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake "$<TARGET_FILE:UnitTest_SharedMemLargeDataTests>" --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_SharedMemLargeDataTests.xml
VERBATIM VERBATIM
) )
endif()
# Build dependencies # Build dependencies
add_dependencies(UnitTest_SharedMemTests_App_Repeater core_services) add_dependencies(UnitTest_SharedMemTests_App_Repeater core_services)

View File

@@ -4,7 +4,7 @@
#define TIME_TRACKING #define TIME_TRACKING
#include "../../../sdv_services/ipc_shared_mem/channel_mgnt.cpp" #include "../../../sdv_services/ipc_shared_mem/channel_mgnt.cpp"
#include "../../../sdv_services/ipc_shared_mem/connection.cpp" #include "../../../sdv_services/ipc_shared_mem/connection.cpp" // Tracing is enabled/disabled in the connection.h file
#include "../../../sdv_services/ipc_shared_mem/watchdog.cpp" #include "../../../sdv_services/ipc_shared_mem/watchdog.cpp"
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp" #include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp"
#include <sstream> #include <sstream>
@@ -12,6 +12,7 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <queue> #include <queue>
#include <atomic>
#ifdef __GNUC__ #ifdef __GNUC__
#include <unistd.h> #include <unistd.h>
@@ -67,6 +68,8 @@ public:
{ {
// Send the same data back again (if needed). // Send the same data back again (if needed).
std::unique_lock<std::mutex> lock(m_mtxData); std::unique_lock<std::mutex> lock(m_mtxData);
m_nReceiveCallCnt++;
m_nPackageReceiveCnt += seqData.size();
m_queueSendData.push(seqData); m_queueSendData.push(seqData);
lock.unlock(); lock.unlock();
@@ -93,7 +96,10 @@ public:
// Send the data back to the sender // Send the data back to the sender
if (m_pSend) if (m_pSend)
{
m_pSend->SendData(seqData); m_pSend->SendData(seqData);
m_nSendCallCnt++;
}
} }
} }
@@ -184,6 +190,33 @@ public:
m_threadSender.join(); m_threadSender.join();
} }
/**
* @brief Get the receive call count.
* @return The amount of receive calls that has been made.
*/
size_t GetReceiveCallCount() const
{
return m_nReceiveCallCnt;
}
/**
* @brief Get the package call count.
* @return The amount of packages that have been received.
*/
size_t GetPackageReceiveCount() const
{
return m_nPackageReceiveCnt;
}
/**
* @brief Get the send call count.
* @return The amount of send calls that has been made.
*/
size_t GetSendCallCount() const
{
return m_nSendCallCnt;
}
private: private:
sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function. sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function.
mutable std::mutex m_mtxData; ///< Protect data access. mutable std::mutex m_mtxData; ///< Protect data access.
@@ -191,9 +224,12 @@ private:
std::condition_variable m_cvDisconnect; ///< Disconnect event. std::condition_variable m_cvDisconnect; ///< Disconnect event.
std::condition_variable m_cvReceived; ///< Receive event. std::condition_variable m_cvReceived; ///< Receive event.
std::thread m_threadSender; ///< Thread to send data. std::thread m_threadSender; ///< Thread to send data.
bool m_bConnected = false; ///< Set when connected was triggered. std::atomic_bool m_bConnected = false; ///< Set when connected was triggered.
bool m_bDisconnect = false; ///< Set when shutdown was triggered. std::atomic_bool m_bDisconnect = false; ///< Set when shutdown was triggered.
bool m_bShutdown = false; ///< Set when shutdown is processed. std::atomic_bool m_bShutdown = false; ///< Set when shutdown is processed.
std::atomic_size_t m_nReceiveCallCnt = 0; ///< Receive call counter.
std::atomic_size_t m_nPackageReceiveCnt = 0; ///< Package receive counter.
std::atomic_size_t m_nSendCallCnt = 0; ///< Send call counter.
}; };
@@ -280,15 +316,18 @@ extern "C" int main(int argc, char* argv[])
// Open the control channel endpoint // Open the control channel endpoint
sdv::TObjectPtr ptrControlConnection; sdv::TObjectPtr ptrControlConnection;
sdv::ipc::IConnect* pControlConnect = nullptr;
CReceiver receiverControl; CReceiver receiverControl;
uint64_t uiControlEventCookie = 0;
if (!ssControlConnectString.empty()) if (!ssControlConnectString.empty())
{ {
TRACE(bServer ? "Server" : "Client", ": Start of control channel connection..."); TRACE(bServer ? "Server" : "Client", ": Start of control channel connection...");
ptrControlConnection = mgntControlMgntChannel.Access(ssControlConnectString); ptrControlConnection = mgntControlMgntChannel.Access(ssControlConnectString);
if (!ptrControlConnection) return -12; if (!ptrControlConnection) return -12;
sdv::ipc::IConnect* pControlConnect = ptrControlConnection.GetInterface<sdv::ipc::IConnect>(); pControlConnect = ptrControlConnection.GetInterface<sdv::ipc::IConnect>();
if (!pControlConnect) return -13; if (!pControlConnect) return -13;
if (!pControlConnect->RegisterStatusEventCallback(&receiverControl)) return -20; uiControlEventCookie = pControlConnect->RegisterStatusEventCallback(&receiverControl);
if (!uiControlEventCookie) return -20;
if (!pControlConnect->AsyncConnect(&receiverControl)) return -14; if (!pControlConnect->AsyncConnect(&receiverControl)) return -14;
if (!pControlConnect->WaitForConnection(250)) return -5; // Note: Connection should be possible within 250ms. if (!pControlConnect->WaitForConnection(250)) return -5; // Note: Connection should be possible within 250ms.
if (pControlConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -15; if (pControlConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -15;
@@ -339,8 +378,10 @@ Size = 1024000
// Establish the connection // Establish the connection
sdv::ipc::IConnect* pDataConnect = ptrDataConnection.GetInterface<sdv::ipc::IConnect>(); sdv::ipc::IConnect* pDataConnect = ptrDataConnection.GetInterface<sdv::ipc::IConnect>();
uint64_t uiDataEventCookie = 0;
if (!pDataConnect) return -3; if (!pDataConnect) return -3;
if (!pDataConnect->RegisterStatusEventCallback(&receiverData)) return -21; uiDataEventCookie = pDataConnect->RegisterStatusEventCallback(&receiverData);
if (!uiDataEventCookie) return -21;
if (!pDataConnect->AsyncConnect(&receiverData)) return -4; if (!pDataConnect->AsyncConnect(&receiverData)) return -4;
if (!pDataConnect->WaitForConnection(10000)) return -5; // Note: Connection should be possible within 10000ms. if (!pDataConnect->WaitForConnection(10000)) return -5; // Note: Connection should be possible within 10000ms.
if (pDataConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -5; if (pDataConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -5;
@@ -358,12 +399,17 @@ Size = 1024000
// Repeat data until disconnect occurrs (differentiate between forced and not forced to allow two apps to start at the // Repeat data until disconnect occurrs (differentiate between forced and not forced to allow two apps to start at the
// same time). // same time).
receiverData.WaitUntilDisconnect(bForceTerminate ? 800 : 1600); receiverData.WaitUntilDisconnect(bForceTerminate ? 800 : 1600);
std::cout << "App " << (bServer ? "server" : "client") << " connect process disconnecting..." << std::endl; TRACE("App ", bServer ? "server" : "client", " connect process disconnecting...");
} }
// Statistics
TRACE("Receive was called ", receiverData.GetReceiveCallCount(), " times (", receiverData.GetPackageReceiveCount(),
" packages).");
TRACE("Send was called ", receiverData.GetSendCallCount(), " times.");
if (bForceTerminate) if (bForceTerminate)
{ {
std::cout << "Forced termination of app " << (bServer ? "server" : "client") << " connect process..." << std::endl; TRACE("Forced termination of app ", bServer ? "server" : "client", " connect process...");
#ifdef _MSC_VER #ifdef _MSC_VER
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#endif #endif
@@ -371,9 +417,11 @@ Size = 1024000
} }
// Initiate shutdown // Initiate shutdown
if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStatusEventCallback(uiDataEventCookie);
ptrDataConnection.Clear(); ptrDataConnection.Clear();
mgntDataMgntChannel.Shutdown(); mgntDataMgntChannel.Shutdown();
if (mgntDataMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -6; if (mgntDataMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -6;
if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStatusEventCallback(uiControlEventCookie);
ptrControlConnection.Clear(); ptrControlConnection.Clear();
mgntControlMgntChannel.Shutdown(); mgntControlMgntChannel.Shutdown();
if (mgntControlMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -16; if (mgntControlMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -16;

View File

@@ -2,6 +2,7 @@
#include "../../../sdv_services/ipc_shared_mem/in_process_mem_buffer.h" #include "../../../sdv_services/ipc_shared_mem/in_process_mem_buffer.h"
#include <support/app_control.h> #include <support/app_control.h>
#include "pattern_gen.h" #include "pattern_gen.h"
#include <atomic>
TEST(InProcessMemoryBufferTest, Instantiate) TEST(InProcessMemoryBufferTest, Instantiate)
{ {
@@ -26,7 +27,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestRx)
CInProcMemBufferRx receiver(sender.GetConnectionString()); CInProcMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); EXPECT_TRUE(receiver.IsValid());
bool bShutdown = false; std::atomic_bool bShutdown = false;
size_t nCorrectCnt = 0; size_t nCorrectCnt = 0;
std::condition_variable cvStart; std::condition_variable cvStart;
std::mutex mtxStart; std::mutex mtxStart;
@@ -75,7 +76,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestTx)
CInProcMemBufferRx receiver(sender.GetConnectionString()); CInProcMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); EXPECT_TRUE(receiver.IsValid());
bool bShutdown = false; std::atomic_bool bShutdown = false;
size_t nCorrectCnt = 0; size_t nCorrectCnt = 0;
std::condition_variable cvStart; std::condition_variable cvStart;
std::mutex mtxStart; std::mutex mtxStart;
@@ -124,7 +125,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestRxTx)
CInProcMemBufferRx receiver(sender.GetConnectionString()); CInProcMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); EXPECT_TRUE(receiver.IsValid());
bool bShutdown = false; std::atomic_bool bShutdown = false;
size_t nCorrectCnt = 0; size_t nCorrectCnt = 0;
std::condition_variable cvSenderStart, cvReceiverStart; std::condition_variable cvSenderStart, cvReceiverStart;
std::mutex mtxReceiverStart; std::mutex mtxReceiverStart;

View File

@@ -5,6 +5,7 @@
#include "../../../sdv_services/ipc_shared_mem/channel_mgnt.cpp" #include "../../../sdv_services/ipc_shared_mem/channel_mgnt.cpp"
#include "../../../sdv_services/ipc_shared_mem/watchdog.cpp" #include "../../../sdv_services/ipc_shared_mem/watchdog.cpp"
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp" #include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp"
#include <cstring>
/** /**
* @brief Load support modules to publish the needed services. * @brief Load support modules to publish the needed services.
@@ -27,9 +28,31 @@ extern "C" int wmain(int argc, wchar_t* argv[])
#else #else
extern "C" int main(int argc, char* argv[]) extern "C" int main(int argc, char* argv[])
#endif #endif
{
// Check for the --gtest_repeat option.
bool bRepeatEnabled = false;
for (int iIndex = 0; iIndex < argc; iIndex++)
{
if (!argv[iIndex])
continue;
#if defined(_WIN32) && defined(_UNICODE)
bRepeatEnabled |= std::wcsncmp(argv[iIndex], L"--gtest_repeat", 14) == 0;
#else
bRepeatEnabled |= std::strncmp(argv[iIndex], "--gtest_repeat", 14) == 0;
#endif
}
// When repeat is enabled, do not enable the watchdog.
if (bRepeatEnabled)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
else
{ {
CProcessWatchdog watchdog; CProcessWatchdog watchdog;
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }
}

View File

@@ -3,6 +3,7 @@
#include <iostream> #include <iostream>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <atomic>
#include "pattern_gen.h" #include "pattern_gen.h"
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.h" #include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.h"

View File

@@ -2,6 +2,7 @@
#define PATTERN_GEN_H #define PATTERN_GEN_H
#include <thread> #include <thread>
#include <atomic>
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.h" #include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.h"
/** /**
@@ -43,8 +44,8 @@ public:
private: private:
CMemBufferAccessorTx& m_raccessorOut; //!< Reference to the output accessor CMemBufferAccessorTx& m_raccessorOut; //!< Reference to the output accessor
std::thread m_thread; //!< Processing thread std::thread m_thread; //!< Processing thread
bool m_bStarted = false; //!< Set by the thread when started. std::atomic_bool m_bStarted = false; //!< Set by the thread when started.
bool m_bShutdown = false; //!< When set, shutdown the thread. std::atomic_bool m_bShutdown = false; //!< When set, shutdown the thread.
uint32_t m_uiDelayMs = 0u; //!< Delay (in ms) to insert while processing. uint32_t m_uiDelayMs = 0u; //!< Delay (in ms) to insert while processing.
uint32_t m_uiCycleCnt = 0u; //!< Amount of packets uint32_t m_uiCycleCnt = 0u; //!< Amount of packets
uint32_t m_uiPacketCnt = 0u; //!< Amount of packets uint32_t m_uiPacketCnt = 0u; //!< Amount of packets

View File

@@ -1,5 +1,6 @@
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <atomic>
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "pattern_gen.h" #include "pattern_gen.h"
#include "../../../sdv_services/ipc_shared_mem/shared_mem_buffer_posix.h" #include "../../../sdv_services/ipc_shared_mem/shared_mem_buffer_posix.h"
@@ -28,10 +29,14 @@ TEST(SharedMemoryBufferTest, CreateBuffer)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
appcontrol.Shutdown(); appcontrol.Shutdown();
} }
@@ -42,12 +47,16 @@ TEST(SharedMemoryBufferTest, TriggerTestRx)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
bool bShutdown = false; std::atomic_bool bShutdown = false;
size_t nCorrectCnt = 0; size_t nCorrectCnt = 0;
std::condition_variable cvStart; std::condition_variable cvStart;
std::mutex mtxStart; std::mutex mtxStart;
@@ -91,12 +100,16 @@ TEST(SharedMemoryBufferTest, TriggerTestTx)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
bool bShutdown = false; std::atomic_bool bShutdown = false;
size_t nCorrectCnt = 0; size_t nCorrectCnt = 0;
std::condition_variable cvStart; std::condition_variable cvStart;
std::mutex mtxStart; std::mutex mtxStart;
@@ -140,16 +153,21 @@ TEST(SharedMemoryBufferTest, TriggerTestRxTx)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
bool bShutdown = false; std::atomic_bool bShutdown = false;
size_t nCorrectCnt = 0; size_t nCorrectCnt = 0;
std::condition_variable cvSenderStart, cvReceiverStart; std::condition_variable cvSenderStart, cvReceiverStart;
std::mutex mtxReceiverStart; std::mutex mtxReceiverStart;
std::mutex mtxSenderStart; std::mutex mtxSenderStart;
auto fnWaitForTriggerReceiver = [&]() auto fnWaitForTriggerReceiver = [&]()
{ {
std::unique_lock<std::mutex> lockReceiver(mtxReceiverStart); std::unique_lock<std::mutex> lockReceiver(mtxReceiverStart);
@@ -166,9 +184,6 @@ TEST(SharedMemoryBufferTest, TriggerTestRxTx)
std::unique_lock<std::mutex> lockSender(mtxSenderStart); std::unique_lock<std::mutex> lockSender(mtxSenderStart);
lockSender.unlock(); lockSender.unlock();
cvSenderStart.notify_all(); cvSenderStart.notify_all();
std::unique_lock<std::mutex> lockReceiver(mtxReceiverStart);
cvReceiverStart.wait(lockReceiver);
lockReceiver.unlock();
while (!bShutdown) while (!bShutdown)
{ {
bool bResult = sender.WaitForFreeSpace(200); bool bResult = sender.WaitForFreeSpace(200);
@@ -179,11 +194,12 @@ TEST(SharedMemoryBufferTest, TriggerTestRxTx)
}; };
std::unique_lock<std::mutex> lockStartSender(mtxSenderStart); std::unique_lock<std::mutex> lockStartSender(mtxSenderStart);
std::unique_lock<std::mutex> lockStartReceiver(mtxReceiverStart);
std::thread threadSender(fnWaitForTriggerSender); std::thread threadSender(fnWaitForTriggerSender);
std::thread threadReceiver(fnWaitForTriggerReceiver);
cvSenderStart.wait(lockStartSender); cvSenderStart.wait(lockStartSender);
lockStartSender.unlock(); lockStartSender.unlock();
std::unique_lock<std::mutex> lockStartReceiver(mtxReceiverStart); //CHECKPOINT();
std::thread threadReceiver(fnWaitForTriggerReceiver);
cvReceiverStart.wait(lockStartReceiver); cvReceiverStart.wait(lockStartReceiver);
lockStartReceiver.unlock(); lockStartReceiver.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Needed for the threads to enter their loop. std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Needed for the threads to enter their loop.
@@ -210,10 +226,14 @@ TEST(SharedMemoryBufferTest, SimpleSynchronousWriteRead)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
EXPECT_TRUE(sender.TryWrite("HELLO", 6)); EXPECT_TRUE(sender.TryWrite("HELLO", 6));
auto optPacket = receiver.TryRead(); auto optPacket = receiver.TryRead();
@@ -229,10 +249,14 @@ TEST(SharedMemoryBufferTest, ReadWithoutSending)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
auto optPacket = receiver.TryRead(); auto optPacket = receiver.TryRead();
EXPECT_FALSE(optPacket); EXPECT_FALSE(optPacket);
@@ -246,10 +270,14 @@ TEST(SharedMemoryBufferTest, RequestReadPacketSize)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
EXPECT_TRUE(sender.TryWrite("HELLO", 5)); EXPECT_TRUE(sender.TryWrite("HELLO", 5));
@@ -266,10 +294,14 @@ TEST(SharedMemoryBufferTest, FragmentRead)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
EXPECT_TRUE(sender.TryWrite("HELLO", 6)); EXPECT_TRUE(sender.TryWrite("HELLO", 6));
EXPECT_TRUE(sender.TryWrite("HELLO2", 7)); EXPECT_TRUE(sender.TryWrite("HELLO2", 7));
@@ -296,10 +328,14 @@ TEST(SharedMemoryBufferTest, BufferBoundary)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender(256); CSharedMemBufferTx sender(256);
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
// The buffer header has 16 bytes // The buffer header has 16 bytes
// Each allocation is 8 bytes header, 6 bytes data and 2 bytes alignment // Each allocation is 8 bytes header, 6 bytes data and 2 bytes alignment
@@ -353,10 +389,14 @@ TEST(SharedMemoryBufferTest, ReserveCommitAccessReleaseNonChronologicalOrder)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender(256); CSharedMemBufferTx sender(256);
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
// Reserve buffers for strings // Reserve buffers for strings
// The buffer header has 16 bytes // The buffer header has 16 bytes
@@ -464,10 +504,14 @@ TEST(SharedMemoryBufferTest, SendReceivePattern)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
CPatternReceiver pattern_inspector(receiver); CPatternReceiver pattern_inspector(receiver);
CPatternSender pattern_generator(sender); CPatternSender pattern_generator(sender);
@@ -501,10 +545,14 @@ TEST(SharedMemoryBufferTest, DelayedSendReceivePattern)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
CPatternReceiver pattern_inspector(receiver); CPatternReceiver pattern_inspector(receiver);
CPatternSender pattern_generator(sender, 10); CPatternSender pattern_generator(sender, 10);
@@ -538,10 +586,14 @@ TEST(SharedMemoryBufferTest, SendDelayedReceivePattern)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx sender; CSharedMemBufferTx sender;
EXPECT_TRUE(sender.IsValid()); if (!sender.IsValid())
std::cout << "ERROR sender: " << sender.GetError() << std::endl;
ASSERT_TRUE(sender.IsValid());
CSharedMemBufferRx receiver(sender.GetConnectionString()); CSharedMemBufferRx receiver(sender.GetConnectionString());
EXPECT_TRUE(receiver.IsValid()); if (!receiver.IsValid())
std::cout << "ERROR receiver: " << receiver.GetError() << std::endl;
ASSERT_TRUE(receiver.IsValid());
CPatternReceiver pattern_inspector(receiver, 10); CPatternReceiver pattern_inspector(receiver, 10);
CPatternSender pattern_generator(sender); CPatternSender pattern_generator(sender);
@@ -575,9 +627,13 @@ TEST(SharedMemoryBufferTest, SendRepeatReceivePattern)
ASSERT_TRUE(appcontrol.Startup("")); ASSERT_TRUE(appcontrol.Startup(""));
CSharedMemBufferTx bufferTX; CSharedMemBufferTx bufferTX;
EXPECT_TRUE(bufferTX.IsValid()); if (!bufferTX.IsValid())
std::cout << "ERROR TX: " << bufferTX.GetError() << std::endl;
ASSERT_TRUE(bufferTX.IsValid());
CSharedMemBufferRx bufferRX; CSharedMemBufferRx bufferRX;
EXPECT_TRUE(bufferRX.IsValid()); if (!bufferTX.IsValid())
std::cout << "ERROR RX: " << bufferTX.GetError() << std::endl;
ASSERT_TRUE(bufferRX.IsValid());
// The connection string containing the RX and TX strings for the repeater // The connection string containing the RX and TX strings for the repeater
std::string ssConnectionString = bufferTX.GetConnectionString() + "\n" + bufferRX.GetConnectionString(); std::string ssConnectionString = bufferTX.GetConnectionString() + "\n" + bufferRX.GetConnectionString();
@@ -630,9 +686,13 @@ Mode = "Essential")code"));
LoadSupportServices(); LoadSupportServices();
CSharedMemBufferTx bufferTX; CSharedMemBufferTx bufferTX;
EXPECT_TRUE(bufferTX.IsValid()); if (!bufferTX.IsValid())
std::cout << "ERROR TX: " << bufferTX.GetError() << std::endl;
ASSERT_TRUE(bufferTX.IsValid());
CSharedMemBufferRx bufferRX; CSharedMemBufferRx bufferRX;
EXPECT_TRUE(bufferRX.IsValid()); if (!bufferTX.IsValid())
std::cout << "ERROR RX: " << bufferTX.GetError() << std::endl;
ASSERT_TRUE(bufferRX.IsValid());
// Start process // Start process
sdv::process::IProcessControl* pProcessControl = sdv::core::GetObject<sdv::process::IProcessControl>("ProcessControlService"); sdv::process::IProcessControl* pProcessControl = sdv::core::GetObject<sdv::process::IProcessControl>("ProcessControlService");

Some files were not shown because too many files have changed in this diff Show More