mirror of
https://github.com/eclipse-openvehicle-api/openvehicle-api.git
synced 2026-02-05 07:18:44 +00:00
update parser (#5)
This commit is contained in:
@@ -84,7 +84,8 @@ endif()
|
||||
# Default C++ settings
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
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)
|
||||
message("Microsoft PREfast static code analysis enabled...")
|
||||
|
||||
202
LICENSE.txt
Normal file
202
LICENSE.txt
Normal 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.
|
||||
@@ -218,7 +218,7 @@ JAVADOC_BANNER = NO
|
||||
# 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
|
||||
# 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.
|
||||
|
||||
QT_AUTOBRIEF = NO
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <support/app_control.h>
|
||||
#include <support/component_impl.h>
|
||||
#include <support/timer.h>
|
||||
#include <atomic>
|
||||
#include "signal_names.h"
|
||||
#include <fcntl.h>
|
||||
#include "vss_vehiclepositioncurrentlatitude_vd_rx.h"
|
||||
@@ -192,7 +193,7 @@ private:
|
||||
mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message
|
||||
std::thread m_threadReadTxSignals; ///< Simulation datalink thread.
|
||||
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
|
||||
|
||||
sdv::core::CSignal m_signalCurrentLatitude; ///< Signal Current latitude
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <support/timer.h>
|
||||
#include "signal_names.h"
|
||||
#include <fcntl.h>
|
||||
#include <atomic>
|
||||
#include "../interfaces/vss_vehiclechassisdooraxle01left_vd_rx.h"
|
||||
#include "../interfaces/vss_vehiclechassisdooraxle01left_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
|
||||
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
|
||||
mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define EXMAPLE_UTILITY_H
|
||||
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include <support/app_control.h>
|
||||
#include <support/signal_support.h>
|
||||
|
||||
@@ -155,7 +156,7 @@ private:
|
||||
bool m_bInitialized = false; ///< Set when initialized.
|
||||
bool m_bCmdLnError = false; ///< Command line error occurred.
|
||||
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.
|
||||
EAppControlReporting m_eReporting = EAppControlReporting::silent; ///< Application control reporting.
|
||||
uint32_t m_uiInstance = 1000u; ///< Server instance to connect to.
|
||||
|
||||
@@ -305,7 +305,7 @@ module sdv
|
||||
interface IEntityComments
|
||||
{
|
||||
/**
|
||||
* \brief Comment mask
|
||||
* @brief Comment mask
|
||||
*/
|
||||
enum ECommentMask : uint32
|
||||
{
|
||||
@@ -342,7 +342,7 @@ module sdv
|
||||
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'.
|
||||
*/
|
||||
boolean IsUnnamed() const;
|
||||
|
||||
@@ -87,7 +87,7 @@ module sdv
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Interface for managing IPC connection.
|
||||
* @brief Interface for managing IPC connection.
|
||||
*/
|
||||
interface IConnect
|
||||
{
|
||||
|
||||
@@ -34,28 +34,42 @@ module sdv
|
||||
/**
|
||||
* @brief Collection of possible data in parse tree node
|
||||
*/
|
||||
enum ENodeType : uint8
|
||||
enum ENodeType : uint32
|
||||
{
|
||||
node_table, //!< Table
|
||||
node_array, //!< Array
|
||||
node_integer, //!< Integer
|
||||
node_floating_point, //!< Floating point
|
||||
node_boolean, //!< Boolean
|
||||
node_string, //!< String
|
||||
node_invalid //!< Invalid content
|
||||
node_table, ///< Table
|
||||
node_array, ///< Array
|
||||
node_integer, ///< Integer
|
||||
node_floating_point, ///< Floating point
|
||||
node_boolean, ///< Boolean
|
||||
node_string, ///< String
|
||||
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
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
*/
|
||||
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.
|
||||
* @return Type of the node.
|
||||
@@ -70,10 +84,75 @@ module sdv
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
|
||||
@@ -308,7 +308,6 @@ namespace sdv
|
||||
protected:
|
||||
/**
|
||||
* @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()
|
||||
{
|
||||
@@ -860,7 +859,7 @@ Version = )code" << SDVFrameworkInterfaceVersion
|
||||
|
||||
/**
|
||||
* @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) \
|
||||
/** \
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -49,6 +49,14 @@ namespace sdv::toml
|
||||
*/
|
||||
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.
|
||||
* @return The node type.
|
||||
@@ -247,6 +255,11 @@ namespace sdv::toml
|
||||
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()
|
||||
{
|
||||
return m_pNodeInfo ? m_pNodeInfo->GetType() : ENodeType::node_invalid;
|
||||
|
||||
@@ -40,14 +40,24 @@ namespace asc
|
||||
enum class EState {header, body, footer} eState = EState::header;
|
||||
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)
|
||||
{
|
||||
case EState::header:
|
||||
if (ssLine.compare(0, 18, "Begin TriggerBlock") == 0)
|
||||
if (iequals(ssLine.substr(0, 18), "Begin TriggerBlock"))
|
||||
eState = EState::body;
|
||||
break;
|
||||
case EState::body:
|
||||
if (ssLine.compare(0, 16, "End TriggerBlock") == 0)
|
||||
if (iequals(ssLine.substr(0, 16), "End TriggerBlock"))
|
||||
eState = EState::footer;
|
||||
else
|
||||
ProcessSample(ssLine);
|
||||
@@ -74,7 +84,7 @@ namespace asc
|
||||
|
||||
uint32_t CAscReader::GetMessageCount() const
|
||||
{
|
||||
return m_lstMessages.size();
|
||||
return static_cast<uint32_t>(m_lstMessages.size());
|
||||
}
|
||||
|
||||
uint32_t CAscReader::GetLoopCount() const
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
namespace asc
|
||||
{
|
||||
@@ -159,8 +160,8 @@ namespace asc
|
||||
std::list<SCanMessage> m_lstMessages; ///< Vector with messages
|
||||
std::list<SCanMessage>::iterator m_itCurrent; ///< Current iterator position
|
||||
std::thread m_threadPlayback; ///< Playback thread.
|
||||
bool m_bPlaybackThread = false; ///< Set when running playback thread
|
||||
bool m_bPlayback = false; ///< Set when running playback
|
||||
std::atomic_bool m_bPlaybackThread = false; ///< Set when running playback thread
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -58,7 +58,8 @@ bool CArgumentDefBase::CompareNameAndAssign(CArgumentIterator& rargit, const std
|
||||
while (nPos < rssArgument.size())
|
||||
{
|
||||
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;
|
||||
ssArgNameCS += c;
|
||||
ssArgNameCI += static_cast<char>(std::tolower(c));
|
||||
|
||||
@@ -1389,7 +1389,7 @@ public:
|
||||
* @param[in] rtArgument Reference to the argument.
|
||||
* @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.
|
||||
|
||||
@@ -8,12 +8,28 @@
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#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
|
||||
#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
|
||||
@@ -33,14 +49,6 @@ namespace debug
|
||||
CLogger()
|
||||
{
|
||||
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()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mtxLogger);
|
||||
std::ofstream fstream(m_pathLogFile, std::ios_base::out | std::ios_base::app);
|
||||
if (fstream.is_open())
|
||||
#if DECOUPLED_DEBUG_LOG != 0
|
||||
// Shut down the logger thread
|
||||
if (m_threadLogger.joinable())
|
||||
{
|
||||
fstream << "End of log..." << std::endl;
|
||||
fstream.close();
|
||||
m_bShutdown = true;
|
||||
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)
|
||||
{
|
||||
// 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::ofstream fstream(m_pathLogFile, std::ios_base::out | std::ios_base::app);
|
||||
std::string ssIndent(m_nDepth, '>');
|
||||
if (!ssIndent.empty()) ssIndent += ' ';
|
||||
if (fstream.is_open())
|
||||
|
||||
// First log entry starts logging
|
||||
if (!m_threadLogger.joinable())
|
||||
{
|
||||
fstream << ssIndent << rss << std::endl;
|
||||
fstream.close();
|
||||
m_threadLogger = std::thread(&CLogger::LogToFileThreadFunc, this);
|
||||
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()
|
||||
{
|
||||
m_nDepth++;
|
||||
DepthPerThreadOperation(EDepthOperation::increase);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,13 +113,141 @@ namespace debug
|
||||
*/
|
||||
void DecrDepth()
|
||||
{
|
||||
if (m_nDepth)
|
||||
m_nDepth--;
|
||||
DepthPerThreadOperation(EDepthOperation::decrease);
|
||||
}
|
||||
private:
|
||||
std::filesystem::path m_pathLogFile; ///< Path to the log file.
|
||||
std::mutex m_mtxLogger; ///< Protect against multiple log entries at the same time.
|
||||
size_t m_nDepth; ///< Depth level for indentation.
|
||||
/**
|
||||
* @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.
|
||||
#if DECOUPLED_DEBUG_LOG != 0
|
||||
std::mutex m_mtxLogger; ///< Protect against multiple log entries at the same time.
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -125,7 +125,9 @@ namespace ipc
|
||||
else
|
||||
{
|
||||
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());
|
||||
}
|
||||
@@ -191,7 +193,9 @@ namespace ipc
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#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.
|
||||
*/
|
||||
class CLocalMemMgr : public sdv::core::IMemoryAlloc, public sdv::IInterfaceAccess,
|
||||
@@ -19,13 +19,13 @@ class CLocalMemMgr : public sdv::core::IMemoryAlloc, public sdv::IInterfaceAcces
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor assigning this class to the local services.
|
||||
* @brief Constructor assigning this class to the local services.
|
||||
*/
|
||||
CLocalMemMgr()
|
||||
{}
|
||||
|
||||
/**
|
||||
* \brief Destructor removing this class from the local services.
|
||||
* @brief Destructor removing this class from the local services.
|
||||
*/
|
||||
~CLocalMemMgr()
|
||||
{}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
* extension ending with "n" match.
|
||||
* - "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.
|
||||
* - "**\/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.
|
||||
* @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.
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
|
||||
#include <thread>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <atomic>
|
||||
#ifdef _WIN32
|
||||
// Prevent reassignment of "interface"
|
||||
#pragma push_macro("interface")
|
||||
@@ -65,11 +69,49 @@ public:
|
||||
}
|
||||
|
||||
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.
|
||||
* @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...
|
||||
auto tpStart = std::chrono::steady_clock::now();
|
||||
@@ -79,6 +121,9 @@ private:
|
||||
auto tpNow = std::chrono::steady_clock::now();
|
||||
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.flush();
|
||||
#ifdef _WIN32
|
||||
@@ -97,8 +142,8 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate.
|
||||
std::thread m_threadWatchdog; ///< The watchdog thread.
|
||||
std::atomic_bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate.
|
||||
std::thread m_threadWatchdog; ///< The watchdog thread.
|
||||
};
|
||||
|
||||
#endif // !defined PROCESS_WATCHDOG_H
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <atomic>
|
||||
#include "../flags.h"
|
||||
|
||||
/**
|
||||
@@ -112,8 +113,8 @@ private:
|
||||
void ThreadFunc();
|
||||
|
||||
std::thread m_thread; ///< The thread that executes the tasks.
|
||||
bool m_bShutdown = false; ///< Set when the thread should terminate.
|
||||
bool m_bStarted = false; ///< Set when the thread has started.
|
||||
std::atomic_bool m_bShutdown = false; ///< Set when the thread should terminate.
|
||||
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::mutex m_mtxSyncStart; ///< The startup synchronization mutex.
|
||||
std::condition_variable m_cvStarted; ///< Triggered by the thread to indicate that it has started.
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#ifdef _MSC_VER
|
||||
#include <process.h>
|
||||
#endif
|
||||
#ifdef __GNUC__
|
||||
#include <unistd.h>
|
||||
#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};
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "PID#" << std::dec << getpid() << " " << std::put_time(¤t_localtime, "%H:%M:%S") << "." << std::setw(3) << std::setfill('0') << current_milliseconds << ": ";
|
||||
#ifdef _MSC_VER
|
||||
stream << "PID#" << std::dec << _getpid() << " " << std::put_time(¤t_localtime, "%H:%M:%S") << "." << std::setw(3) <<
|
||||
std::setfill('0') << current_milliseconds << ": ";
|
||||
#elif defined __GNUC__
|
||||
stream << "PID#" << std::dec << getpid() << " " << std::put_time(¤t_localtime, "%H:%M:%S") << "." << std::setw(3) <<
|
||||
std::setfill('0') << current_milliseconds << ": ";
|
||||
#else
|
||||
#error The current OS is not supported!
|
||||
#endif
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
@@ -66,7 +77,8 @@ inline void Trace(TArgs... tArgs)
|
||||
}
|
||||
|
||||
#else // ENABLE_TRACE == 0
|
||||
inline void Trace()
|
||||
template <typename... TArgs>
|
||||
inline void Trace(TArgs...)
|
||||
{}
|
||||
|
||||
#ifdef __GNUC__
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
|
||||
/**
|
||||
* @brief trace fifo open flags
|
||||
@@ -460,14 +461,14 @@ private:
|
||||
*/
|
||||
void DispatchThreadFunc();
|
||||
|
||||
const size_t nReadIndex = 0; ///< The read 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.
|
||||
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_iOldStdOut = -1; ///< Old descriptor for the StdOut
|
||||
int m_iOldStdErr = -1; ///< Old descriptor for the StdErr
|
||||
std::thread m_threadDispatch; ///< Dispatch thread
|
||||
const size_t nReadIndex = 0; ///< The read index of the pipe descriptor.
|
||||
const size_t nWriteIndex = 1; ///< The write index of the pipe descriptor.
|
||||
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_rgPipeStdErr[2] = { -1, -1 }; ///< StdErr pipe with read and write descriptors.
|
||||
int m_iOldStdOut = -1; ///< Old descriptor for the StdOut
|
||||
int m_iOldStdErr = -1; ///< Old descriptor for the StdErr
|
||||
std::thread m_threadDispatch; ///< Dispatch thread
|
||||
};
|
||||
|
||||
#endif // !defined TRACE_FIFO_H
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <support/sdv_core.h>
|
||||
#include <atomic>
|
||||
#include "../../global/cmdlnparser/cmdlnparser.cpp"
|
||||
#include "../../global/exec_dir_helper.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
|
||||
// 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.
|
||||
bool bThreadStarted = false;
|
||||
std::atomic_bool bThreadStarted = false;
|
||||
std::thread thread = std::thread([&]()
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
@@ -957,7 +957,7 @@ namespace sdv
|
||||
static constexpr ::sdv::interface_id _id = 0xEE1AD4FC2B9217BB;
|
||||
|
||||
/**
|
||||
* \brief Comment mask
|
||||
* @brief Comment mask
|
||||
*/
|
||||
enum class ECommentMask : uint32_t
|
||||
{
|
||||
@@ -1024,7 +1024,7 @@ namespace sdv
|
||||
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'.
|
||||
*/
|
||||
virtual bool IsUnnamed() const = 0;
|
||||
|
||||
@@ -110,7 +110,7 @@ public:
|
||||
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.
|
||||
*/
|
||||
virtual bool IsUnnamed() const override { return m_bAnonymousDefinition; };
|
||||
|
||||
@@ -99,8 +99,10 @@ protected:
|
||||
std::string ssName; ///< Parameter name
|
||||
std::string ssDefaultValue; ///< Parameter default value (or empty for void return value)
|
||||
std::string ssSize; ///< Parameter size
|
||||
enum class EDirection { in, out, inout, ret, ignored } eDirection = EDirection::ignored; ///< Parameter direction or return value
|
||||
enum class EAllocType { direct, indirect, ifc} eAllocType = EAllocType::direct; ///< Parameter allocation type
|
||||
enum class EDirection ///< Parameter direction or return value
|
||||
{ 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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -107,7 +107,7 @@ private:
|
||||
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] nSize The length of the string (or zero terminating string when supplied as 0).
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* \brief Convert a UTF32 string to UTF-8.
|
||||
* @brief Convert a UTF32 string to UTF-8.
|
||||
* @param[in] szBuffer The source string.
|
||||
* @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.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <atomic>
|
||||
#include <interfaces/ipc.h>
|
||||
#include <support/sdv_core.h>
|
||||
#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
|
||||
// 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.
|
||||
bool bThreadStarted = false;
|
||||
std::atomic_bool bThreadStarted = false;
|
||||
std::thread thread = std::thread([&]()
|
||||
{
|
||||
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;
|
||||
}
|
||||
SConnectEventCallbackWrapper sConnectEventWrapper;
|
||||
pConnect->RegisterStatusEventCallback(&sConnectEventWrapper);
|
||||
uint64_t uiCookie = pConnect->RegisterStatusEventCallback(&sConnectEventWrapper);
|
||||
|
||||
// Connect to the core repository
|
||||
sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject<sdv::com::IConnectionControl>("CommunicationControl");
|
||||
if (!pConnectionControl)
|
||||
{
|
||||
pConnect->UnregisterStatusEventCallback(uiCookie);
|
||||
if (!bSilent)
|
||||
std::cerr << "ERROR: " << COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl;
|
||||
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);
|
||||
if (!tConnection.uiControl || !pCoreRepo)
|
||||
{
|
||||
pConnect->UnregisterStatusEventCallback(uiCookie);
|
||||
if (!bSilent)
|
||||
std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << std::endl;
|
||||
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");
|
||||
if (!pLinkCoreRepo)
|
||||
{
|
||||
pConnect->UnregisterStatusEventCallback(uiCookie);
|
||||
if (!bSilent)
|
||||
std::cerr << "ERROR: " << LINK_REPO_SERVICE_ERROR_MSG << std::endl;
|
||||
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");
|
||||
if (!pRepositoryInfo || !pRepositoryControl || !pObjectAccess || !pCreateUtility)
|
||||
{
|
||||
pConnect->UnregisterStatusEventCallback(uiCookie);
|
||||
if (!bSilent)
|
||||
std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl;
|
||||
return REPOSITORY_SERVICE_ACCESS_ERROR;
|
||||
@@ -337,6 +342,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[])
|
||||
}
|
||||
|
||||
// Shutdwown
|
||||
pConnect->UnregisterStatusEventCallback(uiCookie);
|
||||
parser.Clear();
|
||||
pLinkCoreRepo->UnlinkCoreRepository();
|
||||
appcontrol.Shutdown();
|
||||
|
||||
@@ -12,10 +12,13 @@ add_executable(sdv_packager
|
||||
"../../sdv_services/core/toml_parser/parser_toml.cpp"
|
||||
"../../sdv_services/core/toml_parser/lexer_toml.h"
|
||||
"../../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.cpp"
|
||||
"../../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/miscellaneous.h"
|
||||
"../../sdv_services/core/toml_parser/miscellaneous.cpp"
|
||||
"../../sdv_services/core/toml_parser/exception.h"
|
||||
"environment.cpp"
|
||||
"environment.h"
|
||||
|
||||
@@ -929,7 +929,7 @@ bool CSdvPackagerEnvironment::ProcessCommandLine(const std::vector<std::string>&
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check whether preceeded by at least one command
|
||||
// Check whether preceded by at least one command
|
||||
if (!(m_uiShowFlags & 0x00ff))
|
||||
{
|
||||
m_nError = CMDLN_MISSING_SHOW_COMMAND;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Environment access class.
|
||||
*/
|
||||
class CSdvPackagerEnvironment
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <atomic>
|
||||
#include <support/sdv_core.h>
|
||||
#include <support/app_control.h>
|
||||
#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
|
||||
// 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.
|
||||
bool bThreadStarted = false;
|
||||
std::atomic_bool bThreadStarted = false;
|
||||
std::thread thread = std::thread([&]()
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
@@ -344,7 +344,7 @@ protected:
|
||||
|
||||
/**
|
||||
* @brief add to the type a cast, for example for 'std::string' it adds "const std::string&'
|
||||
* @param[in]ssSignalType the string representing the type
|
||||
* @param[in] ssSignalType the string representing the type
|
||||
* @return Returns either original string or 'const' and '&' added.
|
||||
*/
|
||||
std::string CastValueType(const std::string& ssSignalType) const;
|
||||
|
||||
@@ -42,7 +42,7 @@ add_library(core_services SHARED
|
||||
"installation_composer.h"
|
||||
"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
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
|
||||
@@ -61,10 +61,10 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
|
||||
// Reset the current baseline
|
||||
ResetConfigBaseline();
|
||||
|
||||
CParserTOML parser(ssContent);
|
||||
toml_parser::CParser parser(ssContent);
|
||||
|
||||
// Check for config file compatibility
|
||||
auto ptrConfigVersion = parser.GetRoot().GetDirect("Configuration.Version");
|
||||
auto ptrConfigVersion = parser.Root().Direct("Configuration.Version");
|
||||
if (!ptrConfigVersion)
|
||||
{
|
||||
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())
|
||||
{
|
||||
// Load all modules in the component section
|
||||
auto ptrComponents = parser.GetRoot().GetDirect("Component");
|
||||
if (ptrComponents && ptrComponents->GetArray())
|
||||
auto ptrComponents = parser.Root().Direct("Component");
|
||||
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)
|
||||
{
|
||||
// Get the information from the component.
|
||||
std::filesystem::path pathModule;
|
||||
auto ptrModule = ptrComponent->GetDirect("Path");
|
||||
auto ptrModule = ptrComponent->Direct("Path");
|
||||
if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());
|
||||
|
||||
// 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.
|
||||
auto ptrModules = parser.GetRoot().GetDirect("Module");
|
||||
if (ptrModules && ptrModules->GetArray())
|
||||
auto ptrModules = parser.Root().Direct("Module");
|
||||
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)
|
||||
{
|
||||
// Get the information from the component.
|
||||
std::filesystem::path pathModule;
|
||||
auto ptrModulePath = ptrModule->GetDirect("Path");
|
||||
auto ptrModulePath = ptrModule->Direct("Path");
|
||||
if (ptrModulePath) pathModule = static_cast<std::string>(ptrModulePath->GetValue());
|
||||
|
||||
if (pathModule.empty())
|
||||
@@ -158,23 +158,23 @@ sdv::core::EConfigProcessResult CAppConfig::ProcessConfig(/*in*/ const sdv::u8st
|
||||
|
||||
// Start all components from the component section
|
||||
std::filesystem::path pathLastModule;
|
||||
auto ptrComponents = parser.GetRoot().GetDirect("Component");
|
||||
if (ptrComponents && ptrComponents->GetArray())
|
||||
auto ptrComponents = parser.Root().Direct("Component");
|
||||
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)
|
||||
{
|
||||
// Get the information from the component.
|
||||
std::filesystem::path pathModule;
|
||||
auto ptrModule = ptrComponent->GetDirect("Path");
|
||||
auto ptrModule = ptrComponent->Direct("Path");
|
||||
if (ptrModule) pathModule = static_cast<std::string>(ptrModule->GetValue());
|
||||
std::string ssClass;
|
||||
auto ptrClass = ptrComponent->GetDirect("Class");
|
||||
auto ptrClass = ptrComponent->Direct("Class");
|
||||
if (ptrClass) ssClass = static_cast<std::string>(ptrClass->GetValue());
|
||||
std::string ssName;
|
||||
auto ptrName = ptrComponent->GetDirect("Name");
|
||||
auto ptrName = ptrComponent->Direct("Name");
|
||||
if (ptrName) ssName = static_cast<std::string>(ptrName->GetValue());
|
||||
|
||||
// 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);
|
||||
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
|
||||
tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->CreateTOMLText());
|
||||
tObjectID = GetRepository().CreateObject2(ssClass, ssName, ptrComponent->GenerateTOML());
|
||||
|
||||
if (!tObjectID)
|
||||
{
|
||||
|
||||
@@ -723,7 +723,7 @@ void CAppControl::BroadcastOperationState(sdv::app::EAppOperationState eState)
|
||||
|
||||
bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
|
||||
{
|
||||
CParserTOML parserConfig;
|
||||
toml_parser::CParser parserConfig;
|
||||
std::string ssError;
|
||||
try
|
||||
{
|
||||
@@ -736,7 +736,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
|
||||
}
|
||||
|
||||
// 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() == "Verbose") m_bVerbose = true;
|
||||
|
||||
@@ -751,8 +751,8 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
|
||||
// Allow a custom logger to be defined
|
||||
std::filesystem::path pathLoggerModule;
|
||||
std::string ssLoggerClass;
|
||||
std::shared_ptr<CNode> ptrLogHandlerPath = parserConfig.GetRoot().GetDirect("LogHandler.Path");
|
||||
std::shared_ptr<CNode> ptrLogHandlerClass = parserConfig.GetRoot().GetDirect("LogHandler.Class");
|
||||
std::shared_ptr<toml_parser::CNode> ptrLogHandlerPath = parserConfig.Root().Direct("LogHandler.Path");
|
||||
std::shared_ptr<toml_parser::CNode> ptrLogHandlerClass = parserConfig.Root().Direct("LogHandler.Class");
|
||||
if (ptrLogHandlerPath && !ptrLogHandlerClass)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
@@ -778,12 +778,12 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
|
||||
}
|
||||
|
||||
// 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());
|
||||
|
||||
// Get the application-mode
|
||||
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 (ssApplication == "Standalone") m_eContextMode = sdv::app::EAppContext::standalone;
|
||||
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;
|
||||
if (IsMainApplication() || IsIsolatedApplication())
|
||||
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() : "",
|
||||
sdv::core::ELogSeverity::info);
|
||||
ptrLogSeverityFilter = parserConfig.GetRoot().GetDirect("LogHandler.ViewFilter");
|
||||
ptrLogSeverityFilter = parserConfig.Root().Direct("LogHandler.ViewFilter");
|
||||
m_eSeverityViewFilter = fnTranslateSevFilter(ptrLogSeverityFilter ? ptrLogSeverityFilter->GetValue() : "",
|
||||
eLogDefaultViewSeverityFilter);
|
||||
|
||||
// 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)
|
||||
m_uiInstanceID = ptrInstance->GetValue();
|
||||
else
|
||||
m_uiInstanceID = 1000u;
|
||||
// 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)
|
||||
{
|
||||
m_uiRetries = ptrRetries->GetValue();
|
||||
@@ -848,7 +848,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
|
||||
if (IsMainApplication() || IsIsolatedApplication())
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
m_pathRootDir = ptrInstallDir->GetValue();
|
||||
@@ -877,7 +877,7 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
|
||||
// not auto-updateable.
|
||||
if (!IsMaintenanceApplication() && !IsIsolatedApplication())
|
||||
{
|
||||
auto ptrConfigFile = parserConfig.GetRoot().GetDirect("Application.Config");
|
||||
auto ptrConfigFile = parserConfig.Root().Direct("Application.Config");
|
||||
if (ptrConfigFile)
|
||||
m_pathAppConfig = ptrConfigFile->GetValue();
|
||||
}
|
||||
@@ -905,10 +905,10 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
|
||||
try
|
||||
{
|
||||
// Read the configuration
|
||||
CParserTOML parserSettings(ssContent);
|
||||
toml_parser::CParser parserSettings(ssContent);
|
||||
|
||||
// Check for the version
|
||||
auto ptrVersion = parserSettings.GetRoot().GetDirect("Settings.Version");
|
||||
auto ptrVersion = parserSettings.Root().Direct("Settings.Version");
|
||||
if (!ptrVersion)
|
||||
{
|
||||
if (!m_bSilent)
|
||||
@@ -925,12 +925,12 @@ bool CAppControl::ProcessAppConfig(const sdv::u8string& rssConfig)
|
||||
}
|
||||
|
||||
// Read the system configurations
|
||||
auto ptrSystemConfigs = parserSettings.GetRoot().GetDirect("Settings.SystemConfig");
|
||||
if (ptrSystemConfigs && ptrSystemConfigs->GetArray())
|
||||
auto ptrSystemConfigs = parserSettings.Root().Direct("Settings.SystemConfig");
|
||||
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;
|
||||
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.
|
||||
if (m_pathAppConfig.empty())
|
||||
{
|
||||
auto ptrAppConfig = parserSettings.GetRoot().GetDirect("Settings.AppConfig");
|
||||
auto ptrAppConfig = parserSettings.Root().Direct("Settings.AppConfig");
|
||||
if (ptrAppConfig)
|
||||
{
|
||||
// Path available. Enable auto-update.
|
||||
|
||||
@@ -164,22 +164,22 @@ bool CInstallManifest::Read(const std::string& rssManifest, bool bBlockSystemObj
|
||||
m_bBlockSystemObjects = bBlockSystemObjects;
|
||||
|
||||
// Parse the manifest
|
||||
CParserTOML parser(rssManifest);
|
||||
toml_parser::CParser parser(rssManifest);
|
||||
|
||||
// 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;
|
||||
|
||||
// Get the installation name
|
||||
auto ptrInstallNameNode = parser.GetRoot().GetDirect("Installation.Name");
|
||||
auto ptrInstallNameNode = parser.Root().Direct("Installation.Name");
|
||||
if (!ptrInstallNameNode) return false;
|
||||
m_ssInstallName = static_cast<std::string>(ptrInstallNameNode->GetValue());
|
||||
if (m_ssInstallName.empty()) return false;
|
||||
|
||||
// Get installation properties. The properties are optional
|
||||
auto ptrProperties = parser.GetRoot().GetDirect("Properties");
|
||||
std::shared_ptr<CTable> ptrPropertyTable;
|
||||
if (ptrProperties) ptrPropertyTable = ptrProperties->GetTable();
|
||||
auto ptrProperties = parser.Root().Direct("Properties");
|
||||
std::shared_ptr<toml_parser::CTable> ptrPropertyTable;
|
||||
if (ptrProperties) ptrPropertyTable = ptrProperties->Cast<toml_parser::CTable>();
|
||||
if (ptrPropertyTable)
|
||||
{
|
||||
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
|
||||
auto ptrModulesNode = parser.GetRoot().GetDirect("Module");
|
||||
auto ptrModulesNode = parser.Root().Direct("Module");
|
||||
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
|
||||
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;
|
||||
|
||||
// Get the module path
|
||||
auto ptrModulePath = ptrModule->GetDirect("Path");
|
||||
auto ptrModulePath = ptrModule->Direct("Path");
|
||||
if (!ptrModulePath) continue;
|
||||
std::filesystem::path pathModule = static_cast<std::string>(ptrModulePath->GetValue());
|
||||
std::string ssModuleManifest;
|
||||
|
||||
// Get the component list (if available)
|
||||
auto ptrModuleComponents = ptrModule->GetDirect("Component");
|
||||
auto ptrModuleComponents = ptrModule->Direct("Component");
|
||||
if (ptrModuleComponents)
|
||||
{
|
||||
// The module manifest contains the TOML text of the component array
|
||||
auto ptrModuleComponentArray = ptrModuleComponents->GetArray();
|
||||
auto ptrModuleComponentArray = ptrModuleComponents->Cast<toml_parser::CArray>();
|
||||
if (ptrModuleComponentArray)
|
||||
ssModuleManifest = ptrModuleComponents->CreateTOMLText();
|
||||
ssModuleManifest = ptrModuleComponents->GenerateTOML();
|
||||
}
|
||||
|
||||
// Add the module
|
||||
@@ -256,10 +256,10 @@ std::string CInstallManifest::Write() const
|
||||
sstream << "[[Module]]" << std::endl << "Path=\"" << rsEntry.pathRelModule.generic_u8string() << "\"" << std::endl;
|
||||
|
||||
// Read the module manifest
|
||||
CParserTOML parser(rsEntry.ssManifest);
|
||||
toml_parser::CParser parser(rsEntry.ssManifest);
|
||||
|
||||
// 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();
|
||||
@@ -286,14 +286,14 @@ bool CInstallManifest::AddModule(const std::filesystem::path& rpathModulePath,
|
||||
if (!ssManifest.empty())
|
||||
{
|
||||
// Check the interface version for compatibility
|
||||
CParserTOML parser(ssManifest);
|
||||
auto ptrInterfaceNode = parser.GetRoot().GetDirect("Interface.Version");
|
||||
toml_parser::CParser parser(ssManifest);
|
||||
auto ptrInterfaceNode = parser.Root().Direct("Interface.Version");
|
||||
if (!ptrInterfaceNode) 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->GetArray()) return false;
|
||||
ssComponentsManifest = ptrComponentsNode->CreateTOMLText();
|
||||
if (!ptrComponentsNode->Cast<toml_parser::CArray>()) return false;
|
||||
ssComponentsManifest = ptrComponentsNode->GenerateTOML();
|
||||
}
|
||||
|
||||
// Store path and component
|
||||
@@ -398,7 +398,7 @@ bool CInstallManifest::NeedQuotedName(const std::string& 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 false;
|
||||
@@ -408,10 +408,10 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
|
||||
bool bBlockSystemObjects) : pathRelModule(rpathRelModule), ssManifest(rssManifest)
|
||||
{
|
||||
// Parse the manifest and extract information from them...
|
||||
CParserTOML parser(rssManifest);
|
||||
auto ptrComponents = parser.GetRoot().GetDirect("Component");
|
||||
toml_parser::CParser parser(rssManifest);
|
||||
auto ptrComponents = parser.Root().Direct("Component");
|
||||
if (!ptrComponents) return; // No objects...
|
||||
auto ptrComponentArray = ptrComponents->GetArray();
|
||||
auto ptrComponentArray = ptrComponents->Cast<toml_parser::CArray>();
|
||||
if (!ptrComponentArray) return; // No objects...
|
||||
for (uint32_t uiIndex = 0; uiIndex < ptrComponentArray->GetCount(); uiIndex++)
|
||||
{
|
||||
@@ -422,14 +422,14 @@ CInstallManifest::SModule::SModule(const std::filesystem::path& rpathRelModule,
|
||||
SComponent sComponent{};
|
||||
//sComponent.pathModule = rpathModule;
|
||||
sComponent.pathRelModule = rpathRelModule;
|
||||
sComponent.ssManifest = ptrComponent->CreateTOMLText("Component");
|
||||
auto ptrClassName = ptrComponent->GetDirect("Class");
|
||||
sComponent.ssManifest = ptrComponent->GenerateTOML(toml_parser::CGenContext("Component"));
|
||||
auto ptrClassName = ptrComponent->Direct("Class");
|
||||
if (!ptrClassName) continue;
|
||||
sComponent.ssClassName = static_cast<std::string>(ptrClassName->GetValue());
|
||||
auto ptrAliases = ptrComponent->GetDirect("Aliases");
|
||||
auto ptrAliases = ptrComponent->Direct("Aliases");
|
||||
if (ptrAliases)
|
||||
{
|
||||
auto ptrAliasesArray = ptrAliases->GetArray();
|
||||
auto ptrAliasesArray = ptrAliases->Cast<toml_parser::CArray>();
|
||||
for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); 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()));
|
||||
}
|
||||
}
|
||||
auto ptrDefaultName = ptrComponent->GetDirect("DefaultName");
|
||||
auto ptrDefaultName = ptrComponent->Direct("DefaultName");
|
||||
if (ptrDefaultName) sComponent.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue());
|
||||
else sComponent.ssDefaultObjectName = sComponent.ssClassName;
|
||||
auto ptrType = ptrComponent->GetDirect("Type");
|
||||
auto ptrType = ptrComponent->Direct("Type");
|
||||
if (!ptrType) continue;
|
||||
std::string ssType = static_cast<std::string>(ptrType->GetValue());
|
||||
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 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()))
|
||||
sComponent.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
|
||||
auto ptrDependencies = ptrComponent->GetDirect("Dependencies");
|
||||
auto ptrDependencies = ptrComponent->Direct("Dependencies");
|
||||
if (ptrDependencies)
|
||||
{
|
||||
auto ptrDependencyArray = ptrDependencies->GetArray();
|
||||
auto ptrDependencyArray = ptrDependencies->Cast<toml_parser::CArray>();
|
||||
for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount();
|
||||
uiDependencyIndex++)
|
||||
{
|
||||
|
||||
@@ -299,7 +299,7 @@ private:
|
||||
/**
|
||||
* @brief Interpret a version number as string.
|
||||
* @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.
|
||||
*/
|
||||
inline sdv::installation::SPackageVersion InterpretVersionString(const std::string& rssVersion)
|
||||
|
||||
@@ -206,10 +206,10 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
|
||||
|
||||
try
|
||||
{
|
||||
CParserTOML parser(ssManifest);
|
||||
toml_parser::CParser parser(ssManifest);
|
||||
|
||||
// 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 != SDVFrameworkInterfaceVersion)
|
||||
{
|
||||
@@ -230,26 +230,26 @@ bool CModuleInst::Load(const std::filesystem::path& rpathModule) noexcept
|
||||
}
|
||||
|
||||
// Get available classes
|
||||
auto ptrComponents = parser.GetRoot().GetDirect("Component");
|
||||
if (!ptrComponents || !ptrComponents->GetArray())
|
||||
auto ptrComponents = parser.Root().Direct("Component");
|
||||
if (!ptrComponents || !ptrComponents->Cast<toml_parser::CArray>())
|
||||
{
|
||||
SDV_LOG_ERROR("Error opening SDV module: ", rpathModule.generic_u8string(), " error: no components available");
|
||||
Unload(true);
|
||||
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.
|
||||
sdv::SClassInfo sInfo{};
|
||||
auto ptrComponent = ptrComponents->GetArray()->Get(uiIndex);
|
||||
auto ptrComponent = ptrComponents->Cast<toml_parser::CArray>()->Get(uiIndex);
|
||||
if (!ptrComponent) continue;
|
||||
auto ptrClassName = ptrComponent->GetDirect("Class");
|
||||
auto ptrClassName = ptrComponent->Direct("Class");
|
||||
if (!ptrClassName) continue;
|
||||
sInfo.ssClassName = static_cast<std::string>(ptrClassName->GetValue());
|
||||
auto ptrAliases = ptrComponent->GetDirect("Aliases");
|
||||
auto ptrAliases = ptrComponent->Direct("Aliases");
|
||||
if (ptrAliases)
|
||||
{
|
||||
auto ptrAliasesArray = ptrAliases->GetArray();
|
||||
auto ptrAliasesArray = ptrAliases->Cast<toml_parser::CArray>();
|
||||
for (uint32_t uiAliasIndex = 0; ptrAliasesArray && uiAliasIndex < ptrAliasesArray->GetCount(); 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()));
|
||||
}
|
||||
}
|
||||
auto ptrDefaultName = ptrComponent->GetDirect("DefaultName");
|
||||
auto ptrDefaultName = ptrComponent->Direct("DefaultName");
|
||||
if (ptrDefaultName) sInfo.ssDefaultObjectName = static_cast<std::string>(ptrDefaultName->GetValue());
|
||||
else sInfo.ssDefaultObjectName = sInfo.ssClassName;
|
||||
auto ptrType = ptrComponent->GetDirect("Type");
|
||||
auto ptrType = ptrComponent->Direct("Type");
|
||||
if (!ptrType) continue;
|
||||
std::string ssType = static_cast<std::string>(ptrType->GetValue());
|
||||
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 == "Utility") sInfo.eType = sdv::EObjectType::Utility;
|
||||
else continue;
|
||||
auto ptrSingleton = ptrComponent->GetDirect("Singleton");
|
||||
auto ptrSingleton = ptrComponent->Direct("Singleton");
|
||||
if (ptrSingleton && static_cast<bool>(ptrSingleton->GetValue()))
|
||||
sInfo.uiFlags = static_cast<uint32_t>(sdv::EObjectFlags::singleton);
|
||||
auto ptrDependencies = ptrComponent->GetDirect("Dependencies");
|
||||
auto ptrDependencies = ptrComponent->Direct("Dependencies");
|
||||
if (ptrDependencies)
|
||||
{
|
||||
auto ptrDependencyArray = ptrDependencies->GetArray();
|
||||
auto ptrDependencyArray = ptrDependencies->Cast<toml_parser::CArray>();
|
||||
for (uint32_t uiDependencyIndex = 0; ptrDependencyArray && uiDependencyIndex < ptrDependencyArray->GetCount();
|
||||
uiDependencyIndex++)
|
||||
{
|
||||
|
||||
@@ -312,11 +312,11 @@ sdv::core::TModuleID CModuleControl::ContextLoad(const std::filesystem::path& rp
|
||||
{
|
||||
size_t nIndex = 0;
|
||||
size_t nUnsupportedObjectCount = 0;
|
||||
CParserTOML parser(rssManifest);
|
||||
toml_parser::CParser parser(rssManifest);
|
||||
do
|
||||
{
|
||||
std::shared_ptr<CNode> ptrComponentType =
|
||||
parser.GetRoot().GetDirect(std::string("Component[") + std::to_string(nIndex++) + "].Type");
|
||||
std::shared_ptr<toml_parser::CNode> ptrComponentType =
|
||||
parser.Root().Direct(std::string("Component[") + std::to_string(nIndex++) + "].Type");
|
||||
if (!ptrComponentType) break;
|
||||
std::string ssType = static_cast<std::string>(ptrComponentType->GetValue());
|
||||
if (ssType == "Device") continue; // Okay
|
||||
|
||||
@@ -983,11 +983,11 @@ sdv::core::TObjectID CRepository::CreateIsolatedObject(const sdv::SClassInfo& rs
|
||||
sstreamConfig << "Object = \"" << rssObjectName << "\"" << std::endl << std::endl;
|
||||
if (!rssObjectConfig.empty())
|
||||
{
|
||||
CParserTOML parserConfig(rssObjectConfig);
|
||||
sstreamConfig << parserConfig.CreateTOMLText("Isolation.Config");
|
||||
toml_parser::CParser parserConfig(rssObjectConfig);
|
||||
sstreamConfig << parserConfig.GenerateTOML("Isolation.Config");
|
||||
}
|
||||
CParserTOML parserConnection(ssConnectionString);
|
||||
sstreamConfig << parserConnection.CreateTOMLText("Isolation.Connection");
|
||||
toml_parser::CParser parserConnection(ssConnectionString);
|
||||
sstreamConfig << parserConnection.GenerateTOML("Isolation.Connection");
|
||||
|
||||
// Create command line arguments
|
||||
sdv::sequence<sdv::u8string> seqArguments;
|
||||
@@ -1177,8 +1177,9 @@ sdv::core::TObjectID CRepository::CreateObjectID()
|
||||
static sdv::core::TObjectID tCurrent = 0;
|
||||
if (!tCurrent)
|
||||
{
|
||||
srand(static_cast<unsigned int>(time(0)));
|
||||
tCurrent = rand();
|
||||
std::srand(static_cast<unsigned int>(time(0)));
|
||||
tCurrent = 0;
|
||||
while (!tCurrent) tCurrent = std::rand();
|
||||
}
|
||||
return ++tCurrent;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
CSDVCore();
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
* @brief Destructor
|
||||
*/
|
||||
~CSDVCore();
|
||||
|
||||
|
||||
@@ -1,323 +1,302 @@
|
||||
#include "character_reader_utf_8.h"
|
||||
#include "exception.h"
|
||||
|
||||
CCharacterReaderUTF8::CCharacterReaderUTF8() : m_nDataLength{0}, m_nCursor{0}
|
||||
{}
|
||||
|
||||
CCharacterReaderUTF8::CCharacterReaderUTF8(const std::string& rssString) :
|
||||
m_ssString{rssString}, m_nDataLength{rssString.size()}, m_nCursor{0}
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
CheckForInvalidUTF8Bytes();
|
||||
CheckForInvalidUTF8Sequences();
|
||||
}
|
||||
|
||||
void CCharacterReaderUTF8::Feed(const std::string& rssString)
|
||||
{
|
||||
m_ssString = rssString;
|
||||
m_nDataLength = rssString.size();
|
||||
m_nCursor = 0;
|
||||
|
||||
CheckForInvalidUTF8Bytes();
|
||||
CheckForInvalidUTF8Sequences();
|
||||
}
|
||||
|
||||
void CCharacterReaderUTF8::Reset()
|
||||
{
|
||||
m_ssString.clear();
|
||||
m_nDataLength = 0;
|
||||
m_nCursor = 0;
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::Peek()
|
||||
{
|
||||
return GetNextCharacter();
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::Peek(std::size_t n)
|
||||
{
|
||||
if (n < 1)
|
||||
CCharacterReaderUTF8::CCharacterReaderUTF8(const std::string& rssString) : m_ssString{rssString}
|
||||
{
|
||||
return "";
|
||||
CheckForInvalidUTF8Bytes();
|
||||
CheckForInvalidUTF8Sequences();
|
||||
}
|
||||
size_t offset{0};
|
||||
for (std::size_t i = 0; i < n - 1; ++i)
|
||||
|
||||
void CCharacterReaderUTF8::Feed(const std::string& rssString)
|
||||
{
|
||||
offset += GetLengthOfNextCharacter(offset);
|
||||
if (m_nDataLength <= m_nCursor + offset)
|
||||
m_ssString = rssString;
|
||||
m_nCursor = 0;
|
||||
|
||||
CheckForInvalidUTF8Bytes();
|
||||
CheckForInvalidUTF8Sequences();
|
||||
}
|
||||
|
||||
void CCharacterReaderUTF8::Reset()
|
||||
{
|
||||
m_ssString.clear();
|
||||
m_nCursor = 0;
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::Peek(size_t nSkip /*= 0*/, size_t nAmount /*= 1*/) const
|
||||
{
|
||||
if (IsEOF())
|
||||
return {};
|
||||
size_t nOffset = 0;
|
||||
for (size_t n = 0; n < nSkip; ++n)
|
||||
{
|
||||
return "";
|
||||
nOffset += GetLengthOfNextCharacter(nOffset);
|
||||
if (m_ssString.size() <= m_nCursor + nOffset)
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return GetNextCharacter(offset);
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::PeekUntil(const std::vector<std::string>& lstCollection)
|
||||
{
|
||||
size_t offset{0};
|
||||
bool found{false};
|
||||
std::string accumulation;
|
||||
while (!found && m_nDataLength > m_nCursor + offset)
|
||||
{
|
||||
std::string character = GetNextCharacter(offset);
|
||||
offset += GetLengthOfNextCharacter(offset);
|
||||
for (const auto& delimiter : lstCollection)
|
||||
std::string ssCharacters;
|
||||
for (size_t n = 0; n < nAmount; n++)
|
||||
{
|
||||
if (delimiter == character)
|
||||
ssCharacters += GetNextCharacter(nOffset);
|
||||
nOffset += GetLengthOfNextCharacter(nOffset);
|
||||
}
|
||||
return ssCharacters;
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::PeekUntil(const std::vector<std::string>& lstCollection) const
|
||||
{
|
||||
size_t nOffset = 0;
|
||||
bool bFound = false;
|
||||
std::string accumulation;
|
||||
while (!bFound && m_ssString.size() > m_nCursor + nOffset)
|
||||
{
|
||||
std::string ssCharacter = GetNextCharacter(nOffset);
|
||||
nOffset += GetLengthOfNextCharacter(nOffset);
|
||||
for (const auto& delimiter : lstCollection)
|
||||
{
|
||||
found = true;
|
||||
if (delimiter == ssCharacter)
|
||||
bFound = true;
|
||||
}
|
||||
if (!bFound)
|
||||
accumulation += ssCharacter;
|
||||
}
|
||||
return accumulation;
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::Consume(size_t nSkip /*= 0*/, size_t nAmount /*= 1*/)
|
||||
{
|
||||
if (IsEOF())
|
||||
return {};
|
||||
for (size_t n = 0; n < nSkip; ++n)
|
||||
{
|
||||
size_t nLength = GetLengthOfNextCharacter();
|
||||
m_nCursor += nLength;
|
||||
if (!nLength || m_ssString.size() < m_nCursor)
|
||||
return {};
|
||||
}
|
||||
std::string ssCharacters;
|
||||
for (size_t n = 0; n < nAmount; n++)
|
||||
{
|
||||
ssCharacters += GetNextCharacter();
|
||||
m_nCursor += GetLengthOfNextCharacter();
|
||||
}
|
||||
return ssCharacters;
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::ConsumeUntil(const std::vector<std::string>& lstCollection)
|
||||
{
|
||||
size_t nOffset = 0;
|
||||
bool bFound = false;
|
||||
std::string accumulation;
|
||||
while (!bFound && m_ssString.size() > m_nCursor + nOffset)
|
||||
{
|
||||
std::string ssCharacter = GetNextCharacter(nOffset);
|
||||
nOffset += GetLengthOfNextCharacter(nOffset);
|
||||
for (const auto& delimiter : lstCollection)
|
||||
{
|
||||
if (delimiter == ssCharacter)
|
||||
{
|
||||
bFound = true;
|
||||
}
|
||||
}
|
||||
if (!bFound)
|
||||
{
|
||||
accumulation += ssCharacter;
|
||||
}
|
||||
else
|
||||
{
|
||||
nOffset -= ssCharacter.size();
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
accumulation += character;
|
||||
}
|
||||
m_nCursor += nOffset;
|
||||
return accumulation;
|
||||
}
|
||||
return accumulation;
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::Consume()
|
||||
{
|
||||
if (IsEOF())
|
||||
bool CCharacterReaderUTF8::IsEOF() const
|
||||
{
|
||||
return "";
|
||||
return m_ssString.size() < m_nCursor + 1;
|
||||
}
|
||||
std::string character{GetNextCharacter()};
|
||||
m_nCursor += GetLengthOfNextCharacter();
|
||||
return character;
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::Consume(std::size_t n)
|
||||
{
|
||||
if (n < 1)
|
||||
void CCharacterReaderUTF8::SetBookmark()
|
||||
{
|
||||
return "";
|
||||
m_nBookmark = m_nCursor;
|
||||
}
|
||||
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::size_t offset{0};
|
||||
bool found{false};
|
||||
std::string accumulation;
|
||||
while (!found && m_nDataLength > m_nCursor + offset)
|
||||
std::string CCharacterReaderUTF8::StringFromBookmark() const
|
||||
{
|
||||
std::string character = GetNextCharacter(offset);
|
||||
offset += GetLengthOfNextCharacter(offset);
|
||||
for (const auto& delimiter : lstCollection)
|
||||
return m_ssString.substr(m_nBookmark, (m_nCursor > m_nBookmark) ? m_nCursor - m_nBookmark : 0);
|
||||
}
|
||||
|
||||
void CCharacterReaderUTF8::CheckForInvalidUTF8Bytes() const
|
||||
{
|
||||
const unsigned char invalidByteC0{0xC0};
|
||||
const unsigned char invalidByteC1{0xC1};
|
||||
const unsigned char lowerBoundInvalidRegion{0xF5};
|
||||
for (size_t i = 0; i < m_ssString.size(); ++i)
|
||||
{
|
||||
if (delimiter == character)
|
||||
unsigned char uc = m_ssString[i];
|
||||
if (uc == invalidByteC0 || uc == invalidByteC1 || uc >= lowerBoundInvalidRegion)
|
||||
{
|
||||
found = true;
|
||||
std::stringstream message;
|
||||
message << "Invalid byte " << std::hex << uc << std::dec << " at position " << i << "\n";
|
||||
throw XTOMLParseException(message.str());
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
accumulation += character;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset -= character.size();
|
||||
}
|
||||
}
|
||||
m_nCursor += offset;
|
||||
return accumulation;
|
||||
}
|
||||
|
||||
bool CCharacterReaderUTF8::IsEOF() const
|
||||
{
|
||||
return m_nDataLength < m_nCursor + 1;
|
||||
}
|
||||
void CCharacterReaderUTF8::CheckForInvalidUTF8Sequences() const
|
||||
{
|
||||
enum class EState
|
||||
{
|
||||
state_neutral,
|
||||
state_two_byte,
|
||||
state_three_byte,
|
||||
state_four_byte,
|
||||
state_error
|
||||
};
|
||||
EState eCurrentState = EState::state_neutral;
|
||||
uint8_t uiIndex = 0;
|
||||
|
||||
void CCharacterReaderUTF8::CheckForInvalidUTF8Bytes() const
|
||||
{
|
||||
const unsigned char invalidByteC0{0xC0};
|
||||
const unsigned char invalidByteC1{0xC1};
|
||||
const unsigned char lowerBoundInvalidRegion{0xF5};
|
||||
for (std::size_t i = 0; i < m_ssString.size(); ++i)
|
||||
{
|
||||
unsigned char uc = m_ssString[i];
|
||||
if (uc == invalidByteC0 || uc == invalidByteC1 || uc >= lowerBoundInvalidRegion)
|
||||
auto fnCheckByteInNeutralState = [&uiIndex, &eCurrentState](unsigned char uc)
|
||||
{
|
||||
std::stringstream message;
|
||||
message << "Invalid byte " << std::hex << uc << std::dec << " at position " << i << "\n";
|
||||
throw XTOMLParseException(message.str());
|
||||
if ((uc & m_uiOneByteCheckMask) == m_OneByteCheckValue)
|
||||
{
|
||||
eCurrentState = EState::state_neutral;
|
||||
uiIndex = 0;
|
||||
}
|
||||
else if ((uc & m_uiFourByteCheckMask) == m_uiFourByteCheckValue)
|
||||
{
|
||||
eCurrentState = EState::state_four_byte;
|
||||
uiIndex = 1;
|
||||
}
|
||||
else if ((uc & m_uiThreeByteCheckMask) == m_uiThreeByteCheckValue)
|
||||
{
|
||||
eCurrentState = EState::state_three_byte;
|
||||
uiIndex = 1;
|
||||
}
|
||||
else if ((uc & m_uiTwoByteCheckMask) == m_uiTwoByteCheckValue)
|
||||
{
|
||||
eCurrentState = EState::state_two_byte;
|
||||
uiIndex = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
eCurrentState = EState::state_error;
|
||||
}
|
||||
};
|
||||
auto fnCheckByteInTwoByteState = [&uiIndex, &eCurrentState](unsigned char uc)
|
||||
{
|
||||
if ((uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
uiIndex = 0;
|
||||
eCurrentState = EState::state_neutral;
|
||||
}
|
||||
else
|
||||
{
|
||||
eCurrentState = EState::state_error;
|
||||
}
|
||||
};
|
||||
auto fnCheckByteInThreeByteState = [&uiIndex, &eCurrentState](unsigned char uc)
|
||||
{
|
||||
if (uiIndex == 1 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
uiIndex = 2;
|
||||
}
|
||||
else if (uiIndex == 2 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
uiIndex = 0;
|
||||
eCurrentState = EState::state_neutral;
|
||||
}
|
||||
else
|
||||
{
|
||||
eCurrentState = EState::state_error;
|
||||
}
|
||||
};
|
||||
auto fnCheckByteInFourByteState = [&uiIndex, &eCurrentState](unsigned char uc)
|
||||
{
|
||||
if (uiIndex <= 2 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
++uiIndex;
|
||||
}
|
||||
else if (uiIndex == 3 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
uiIndex = 0;
|
||||
eCurrentState = EState::state_neutral;
|
||||
}
|
||||
else
|
||||
{
|
||||
eCurrentState = EState::state_error;
|
||||
}
|
||||
};
|
||||
for (size_t i = 0; i < m_ssString.size(); ++i)
|
||||
{
|
||||
uint8_t uiCurrentByte = m_ssString[i];
|
||||
switch (eCurrentState)
|
||||
{
|
||||
case EState::state_neutral:
|
||||
fnCheckByteInNeutralState(uiCurrentByte);
|
||||
break;
|
||||
case EState::state_two_byte:
|
||||
fnCheckByteInTwoByteState(uiCurrentByte);
|
||||
break;
|
||||
case EState::state_three_byte:
|
||||
fnCheckByteInThreeByteState(uiCurrentByte);
|
||||
break;
|
||||
case EState::state_four_byte:
|
||||
fnCheckByteInFourByteState(uiCurrentByte);
|
||||
break;
|
||||
default:
|
||||
std::stringstream sstreamMessage;
|
||||
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";
|
||||
throw XTOMLParseException(sstreamMessage.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCharacterReaderUTF8::CheckForInvalidUTF8Sequences() const
|
||||
{
|
||||
enum class EState
|
||||
{
|
||||
state_neutral,
|
||||
state_two_byte,
|
||||
state_three_byte,
|
||||
state_four_byte,
|
||||
state_error
|
||||
};
|
||||
EState eCurrentState{EState::state_neutral};
|
||||
uint8_t uiIndex{0};
|
||||
auto fnCheckByteInNeutralState = [&uiIndex, &eCurrentState](unsigned char uc)
|
||||
{
|
||||
if ((uc & m_uiOneByteCheckMask) == m_OneByteCheckValue)
|
||||
if (eCurrentState != EState::state_neutral)
|
||||
{
|
||||
eCurrentState = EState::state_neutral;
|
||||
uiIndex = 0;
|
||||
}
|
||||
else if ((uc & m_uiFourByteCheckMask) == m_uiFourByteCheckValue)
|
||||
{
|
||||
eCurrentState = EState::state_four_byte;
|
||||
uiIndex = 1;
|
||||
}
|
||||
else if ((uc & m_uiThreeByteCheckMask) == m_uiThreeByteCheckValue)
|
||||
{
|
||||
eCurrentState = EState::state_three_byte;
|
||||
uiIndex = 1;
|
||||
}
|
||||
else if ((uc & m_uiTwoByteCheckMask) == m_uiTwoByteCheckValue)
|
||||
{
|
||||
eCurrentState = EState::state_two_byte;
|
||||
uiIndex = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
eCurrentState = EState::state_error;
|
||||
}
|
||||
};
|
||||
auto fnCheckByteInTwoByteState = [&uiIndex, &eCurrentState](unsigned char uc)
|
||||
{
|
||||
if ((uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
uiIndex = 0;
|
||||
eCurrentState = EState::state_neutral;
|
||||
}
|
||||
else
|
||||
{
|
||||
eCurrentState = EState::state_error;
|
||||
}
|
||||
};
|
||||
auto fnCheckByteInThreeByteState = [&uiIndex, &eCurrentState](unsigned char uc)
|
||||
{
|
||||
if (uiIndex == 1 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
uiIndex = 2;
|
||||
}
|
||||
else if (uiIndex == 2 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
uiIndex = 0;
|
||||
eCurrentState = EState::state_neutral;
|
||||
}
|
||||
else
|
||||
{
|
||||
eCurrentState = EState::state_error;
|
||||
}
|
||||
};
|
||||
auto fnCheckByteInFourByteState = [&uiIndex, &eCurrentState](unsigned char uc)
|
||||
{
|
||||
if (uiIndex <= 2 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
++uiIndex;
|
||||
}
|
||||
else if (uiIndex == 3 && (uc & m_uiFollowByteCheckMask) == m_uiFollowByteValue)
|
||||
{
|
||||
uiIndex = 0;
|
||||
eCurrentState = EState::state_neutral;
|
||||
}
|
||||
else
|
||||
{
|
||||
eCurrentState = EState::state_error;
|
||||
}
|
||||
};
|
||||
for (std::size_t i = 0; i < m_ssString.size(); ++i)
|
||||
{
|
||||
uint8_t uiCurrentByte = m_ssString[i];
|
||||
switch (eCurrentState)
|
||||
{
|
||||
case EState::state_neutral:
|
||||
fnCheckByteInNeutralState(uiCurrentByte);
|
||||
break;
|
||||
case EState::state_two_byte:
|
||||
fnCheckByteInTwoByteState(uiCurrentByte);
|
||||
break;
|
||||
case EState::state_three_byte:
|
||||
fnCheckByteInThreeByteState(uiCurrentByte);
|
||||
break;
|
||||
case EState::state_four_byte:
|
||||
fnCheckByteInFourByteState(uiCurrentByte);
|
||||
break;
|
||||
default:
|
||||
std::stringstream sstreamMessage;
|
||||
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";
|
||||
sstreamMessage << "Unfinished character at the end of file\n";
|
||||
throw XTOMLParseException(sstreamMessage.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (eCurrentState != EState::state_neutral)
|
||||
{
|
||||
std::stringstream sstreamMessage;
|
||||
sstreamMessage << "Unfinished character at the end of file\n";
|
||||
throw XTOMLParseException(sstreamMessage.str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::GetNextCharacter()
|
||||
{
|
||||
if (IsEOF())
|
||||
std::string CCharacterReaderUTF8::GetNextCharacter(size_t nOffset /*= 0*/) const
|
||||
{
|
||||
return "";
|
||||
if (IsEOF())
|
||||
return {};
|
||||
return {m_ssString, m_nCursor + nOffset, GetLengthOfNextCharacter(nOffset)};
|
||||
}
|
||||
return {m_ssString, m_nCursor, GetLengthOfNextCharacter()};
|
||||
}
|
||||
|
||||
std::string CCharacterReaderUTF8::GetNextCharacter(std::size_t offset)
|
||||
{
|
||||
return {m_ssString, m_nCursor + offset, GetLengthOfNextCharacter(offset)};
|
||||
}
|
||||
|
||||
size_t CCharacterReaderUTF8::GetLengthOfNextCharacter() const
|
||||
{
|
||||
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)
|
||||
size_t CCharacterReaderUTF8::GetLengthOfNextCharacter(size_t nOffset) const
|
||||
{
|
||||
ret = 1;
|
||||
if (IsEOF())
|
||||
return 0;
|
||||
uint8_t ui = static_cast<uint8_t>(m_ssString[m_nCursor + nOffset]);
|
||||
int32_t uiRet = 0;
|
||||
if ((ui & m_uiOneByteCheckMask) == m_OneByteCheckValue)
|
||||
{
|
||||
uiRet = 1;
|
||||
}
|
||||
else if ((ui & m_uiFourByteCheckMask) == m_uiFourByteCheckValue)
|
||||
{
|
||||
uiRet = 4;
|
||||
}
|
||||
else if ((ui & m_uiThreeByteCheckMask) == m_uiThreeByteCheckValue)
|
||||
{
|
||||
uiRet = 3;
|
||||
}
|
||||
else if ((ui & m_uiTwoByteCheckMask) == m_uiTwoByteCheckValue)
|
||||
{
|
||||
uiRet = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream sstreamMessage;
|
||||
sstreamMessage << "Invalid character sequence with byte " << std::hex << ui << std::dec << " as start byte\n";
|
||||
throw XTOMLParseException(sstreamMessage.str());
|
||||
}
|
||||
return uiRet;
|
||||
}
|
||||
else if ((ui & m_uiFourByteCheckMask) == m_uiFourByteCheckValue)
|
||||
{
|
||||
ret = 4;
|
||||
}
|
||||
else if ((ui & m_uiThreeByteCheckMask) == m_uiThreeByteCheckValue)
|
||||
{
|
||||
ret = 3;
|
||||
}
|
||||
else if ((ui & m_uiTwoByteCheckMask) == m_uiTwoByteCheckValue)
|
||||
{
|
||||
ret = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream sstreamMessage;
|
||||
sstreamMessage << "Invalid character sequence with byte " << std::hex << ui << std::dec
|
||||
<< " as start byte\n";
|
||||
throw XTOMLParseException(sstreamMessage.str());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} // namespace toml_parser
|
||||
@@ -6,122 +6,141 @@
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief Reads a given input string, interprets bytes as UTF-8 and returns UTF-8 characters or strings in order on
|
||||
* demand
|
||||
*/
|
||||
class CCharacterReaderUTF8
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Standard constructor for empty character reader
|
||||
* @brief Reads a given input string, interprets bytes as UTF-8 and returns UTF-8 characters or strings in order of demand.
|
||||
*/
|
||||
CCharacterReaderUTF8();
|
||||
class CCharacterReaderUTF8
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Standard constructor for empty character reader.
|
||||
*/
|
||||
CCharacterReaderUTF8() = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a character reader from a given string
|
||||
* @param[in] rssString UTF-8 input string.
|
||||
* @throw InvalidCharacterException 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
|
||||
*/
|
||||
CCharacterReaderUTF8(const std::string& rssString);
|
||||
/**
|
||||
* @brief Constructs a character reader from a given string.
|
||||
* @param[in] rssString UTF-8 input string.
|
||||
* @throw XTOMLParseException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters.
|
||||
* @throw XTOMLParseException Throws an InvalidByteException if the input contains for UTF-8 invalid bytes.
|
||||
*/
|
||||
CCharacterReaderUTF8(const std::string& rssString);
|
||||
|
||||
/**
|
||||
* @brief Feed the character reader from the given string.
|
||||
* @param[in] rssString UTF-8 input string.
|
||||
*/
|
||||
void Feed(const std::string& rssString);
|
||||
/**
|
||||
* @brief Feed the character reader from the given string.
|
||||
* @param[in] rssString UTF-8 input string.
|
||||
*/
|
||||
void Feed(const std::string& rssString);
|
||||
|
||||
/**
|
||||
* @brief Reset the character reader content.
|
||||
*/
|
||||
void Reset();
|
||||
/**
|
||||
* @brief Reset the character reader content.
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* @brief Gets the next 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
|
||||
* end
|
||||
*/
|
||||
std::string Peek();
|
||||
/**
|
||||
* @brief Get the next n-th UTF-8 character without advancing the cursor.
|
||||
* @param[in] nSkip Amount of characters to skip.
|
||||
* @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(size_t nSkip = 0, size_t nAmount = 1) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the next n-th UTF-8 character without advancing the cursor
|
||||
* @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 Peek(std::size_t n);
|
||||
/**
|
||||
* @brief Get all upcoming UTF-8 characters until a terminating character without advancing the cursor.
|
||||
* @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 PeekUntil(const std::vector<std::string>& lstCollection) const;
|
||||
|
||||
/**
|
||||
* @brief Gets all upcoming UTF-8 characters until a terminating character without advancing the cursor
|
||||
* @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 PeekUntil(const std::vector<std::string>& lstCollection);
|
||||
/**
|
||||
* @brief Get the next n-th UTF-8 character and advancing the cursor by n.
|
||||
* @param[in] nSkip Amount of characters to skip.
|
||||
* @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 Consume(size_t nSkip = 0, size_t nAmount = 1);
|
||||
|
||||
/**
|
||||
* @brief Gets the next UTF-8 character and advancing the cursor by one
|
||||
* @return Returns the next UTF-8 character after the current cursor or an empty string if the cursor is at the
|
||||
* end
|
||||
*/
|
||||
std::string Consume();
|
||||
/**
|
||||
* @brief Get 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 (excluding) the first occurrence of one of the given characters.
|
||||
*/
|
||||
std::string ConsumeUntil(const std::vector<std::string>& lstCollection);
|
||||
|
||||
/**
|
||||
* @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 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.
|
||||
*/
|
||||
bool IsEOF() const;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
/**
|
||||
* @brief Set the bookmark at the current sursor position. This can be used to read the raw string.
|
||||
*/
|
||||
void SetBookmark();
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
bool IsEOF() const;
|
||||
/**
|
||||
* @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:
|
||||
void CheckForInvalidUTF8Bytes() const;
|
||||
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 CheckForInvalidUTF8Sequences() const;
|
||||
/**
|
||||
* @brief Check for an invalid UTF8 sequence.
|
||||
* @throw XTOMLParseException Throws an InvalidCharacterException if the input contains invalid UTF-8 characters.
|
||||
*/
|
||||
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;
|
||||
|
||||
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
|
||||
|
||||
std::string m_ssString;
|
||||
std::size_t m_nDataLength;
|
||||
std::size_t m_nCursor;
|
||||
};
|
||||
std::string m_ssString; ///< String containing the characters to acquire.
|
||||
size_t m_nCursor = 0; ///< Current position pointing to the next character.
|
||||
size_t m_nBookmark = 0; ///< Bookmark cursor position to use to get raw string chunks.
|
||||
};
|
||||
} // namespace toml_parser
|
||||
|
||||
#endif // CHARACTER_READER_UTF_8_H
|
||||
|
||||
@@ -3,18 +3,31 @@
|
||||
|
||||
#include <interfaces/toml.h>
|
||||
|
||||
except XTOMLParseException : public sdv::toml::XTOMLParseException
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @brief Extended exception for the TOML parser.
|
||||
*/
|
||||
XTOMLParseException(const std::string& rss) { ssMessage = rss; };
|
||||
except XTOMLParseException : public sdv::toml::XTOMLParseException
|
||||
{
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
XTOMLParseException(const std::string& rss)
|
||||
{
|
||||
ssMessage = rss;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return the explanatory string.
|
||||
* @return The descriptive string.
|
||||
*/
|
||||
virtual const char* what() const noexcept override { return ssMessage.c_str(); }
|
||||
};
|
||||
/**
|
||||
* @brief Return the explanatory string.
|
||||
* @return The descriptive string.
|
||||
*/
|
||||
virtual const char* what() const noexcept override
|
||||
{
|
||||
return ssMessage.c_str();
|
||||
}
|
||||
};
|
||||
} // namespace toml_parser
|
||||
|
||||
#endif // !defined CONFIG_EXCEPTION_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,224 +3,375 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <stack>
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include "character_reader_utf_8.h"
|
||||
#include "lexer_toml_token.h"
|
||||
|
||||
/**
|
||||
* @brief Tokenizes the output of a character reader in regard of the TOML format and returns tokens in order on demand
|
||||
*/
|
||||
class CLexerTOML
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Enum for all possible token categories
|
||||
* @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)
|
||||
*/
|
||||
enum class ETokenCategory : uint8_t
|
||||
class CNodeTokenRange
|
||||
{
|
||||
token_none, ///< Default
|
||||
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 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
|
||||
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_eof, ///< End of File Token; may only be at the end of the token array
|
||||
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
|
||||
public:
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
CNodeTokenRange(const CToken& rInitialToken);
|
||||
|
||||
/**
|
||||
* @brief Construct a node range with the main node token range.
|
||||
* @param[in] rrangeNodeMain Reference to the token range holding the main node tokens.
|
||||
*/
|
||||
CNodeTokenRange(const CTokenRange& rrangeNodeMain);
|
||||
|
||||
/**
|
||||
* @brief Set the token identifying the area before the extended node. Will start before or at and end at the extended node
|
||||
* range.
|
||||
* @param[in] rTokenBegin Reference to the token identifying the begin of the lines before the extended node range.
|
||||
* @remarks Setting the begin will not change the extended node range. If starting within the extended node range, the begin
|
||||
* of the extended node range determines the begin of the lines before.
|
||||
*/
|
||||
void LinesBeforeNode(const CToken& rTokenBegin);
|
||||
|
||||
/**
|
||||
* @brief Get the lines before range, being located before the extended node range.
|
||||
* @return The token range with begin and end token of the lines before. If identical, there are no lines before.
|
||||
*/
|
||||
CTokenRange LinesBeforeNode() const;
|
||||
|
||||
/**
|
||||
* @brief Set the token range identifying the extended node range. Will start before or at and end behind or at the main
|
||||
* node ranges (incl. the finish range).
|
||||
* @param[in] rRange Token range holding the begin and end of the extended node range.
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
* @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] bValueOnly When set, the lexer should treat the string as a value assignment.
|
||||
*/
|
||||
explicit SToken(ETokenCategory category) : eCategory(category)
|
||||
{}
|
||||
CLexer(const std::string& rssString, bool bValueOnly = false);
|
||||
|
||||
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 Feed the lexer with the given string. This will replace a previous lexing result.
|
||||
* @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, bool bValueOnly = false);
|
||||
|
||||
/**
|
||||
* @brief Reset the lexer cursor position.
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* @brief Navigation modes supported by the lexer.
|
||||
*/
|
||||
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 Get the current navigation mode.
|
||||
* @return The current naviation mode.
|
||||
*/
|
||||
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
|
||||
* @remarks Whitespace and comments are skipped.
|
||||
* @param[in] nSkip Skip the amount of tokens.
|
||||
* @return Returns smart pointer to the token in the token list or an empty pointer.
|
||||
*/
|
||||
const CToken& Peek(size_t nSkip = 0) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the n-th token after the current cursor and advancing the cursor by n
|
||||
* @remarks Whitespace and comments are skipped.
|
||||
* @param[in] nSkip Skip the amount of tokens.
|
||||
* @return Returns smart pointer to the token in the token list or an empty pointer.
|
||||
*/
|
||||
const CToken& Consume(size_t nSkip = 0);
|
||||
|
||||
/**
|
||||
* @brief Checks if the end-token was consumed
|
||||
* @return Returns true if the end-token was consumed by Consume() or Consume(n) or if there are no tokens;
|
||||
* false otherwise
|
||||
*/
|
||||
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:
|
||||
/**
|
||||
* @brief Run through the string and generate tokens.
|
||||
* @param[in] rReader Reference to the reader providing the UTF8 characters.
|
||||
*/
|
||||
void GenerateTokens(CCharacterReaderUTF8& rReader);
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
ENavigationMode m_eNavMode = ENavigationMode::skip_comments_and_whitespace; ///< The current navigation mode.
|
||||
TTokenList m_lstTokens; ///< List of tokens.
|
||||
TTokenListIterator m_itCursor{m_lstTokens.end()}; ///< Current position within token list.
|
||||
|
||||
/**
|
||||
* @brief Enum for differentiating between keys and values that are potentially indifferent like '"value" =
|
||||
* "value"'
|
||||
*/
|
||||
enum class EExpectation
|
||||
{
|
||||
expect_key, ///< A key is expected over a value
|
||||
expect_value, ///< A value is expected over a key
|
||||
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
|
||||
|
||||
const std::vector<std::string>
|
||||
m_vecKeyDelimiters{"\n", "\t", "\r", " ", "", ".", "=", "]"}; ///< Characters that delimit a key
|
||||
const std::vector<std::string>
|
||||
m_vecValueDelimiters{"\n", "\t", "\r", " ", ",", "", "]", "}", "#"}; ///< Characters that delimit a value
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
CLexerTOML() = default;
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
CLexerTOML(const std::string& rssString);
|
||||
|
||||
/**
|
||||
* @brief Feed the lexer with the given string.
|
||||
* @param[in] rssString UTF-8 input string.
|
||||
*/
|
||||
void Feed(const std::string& rssString);
|
||||
|
||||
/**
|
||||
* @brief Reset the lexer content.
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
/**
|
||||
* @brief Gets the next token after the current cursor position without advancing the cursor
|
||||
* @return Returns the next token after the current cursor position or a End-of-File-Token if there are no
|
||||
* tokens
|
||||
*/
|
||||
SToken Peek() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the next token after the current cursor position and advancing the cursor by one
|
||||
* @return Returns the next token after the current cursor position or a End-of-File-Token if there are no
|
||||
* tokens
|
||||
*/
|
||||
SToken Consume();
|
||||
|
||||
/**
|
||||
* @brief Gets the n-th token after the current cursor without advancing the cursor
|
||||
* @param[in] n Step size
|
||||
* @return Returns the n-th token after the current cursor position or and empty token if n<1 or the end-token
|
||||
* if a step of n would read a position after the end-token
|
||||
*/
|
||||
SToken Peek(int32_t n);
|
||||
|
||||
/**
|
||||
* @brief Gets the n-th token after the current cursor and advancing the cursor by n
|
||||
* @param[in] n Step size
|
||||
* @return Returns the n-th token after the current cursor position or and empty token if n<1 or the end-token
|
||||
* if a step of n would read a position after the end-token
|
||||
*/
|
||||
SToken Consume(int32_t n);
|
||||
|
||||
/**
|
||||
* @brief Checks if the end-token was consumed
|
||||
* @return Returns true if the end-token was consumed by Consume() or Consume(n) or if there are no tokens;
|
||||
* false otherwise
|
||||
*/
|
||||
bool IsEnd() const;
|
||||
|
||||
private:
|
||||
void GenerateTokens();
|
||||
|
||||
bool IsBasicQuotedKey();
|
||||
|
||||
void ReadBasicQuotedKey();
|
||||
|
||||
bool IsLiteralQuotedKey();
|
||||
|
||||
void ReadLiteralQuotedKey();
|
||||
|
||||
bool IsBareKey();
|
||||
|
||||
void ReadBareKey();
|
||||
|
||||
bool IsBasicString();
|
||||
|
||||
void ReadBasicString();
|
||||
|
||||
bool IsBasicMultilineString();
|
||||
|
||||
void ReadBasicMultilineString();
|
||||
|
||||
bool IsLiteralString();
|
||||
|
||||
void ReadLiteralString();
|
||||
|
||||
bool IsLiteralMultilineString();
|
||||
|
||||
void ReadLiteralMultilineString();
|
||||
|
||||
bool IsInteger();
|
||||
|
||||
void ReadInteger();
|
||||
|
||||
bool IsFloat();
|
||||
|
||||
void ReadFloat();
|
||||
|
||||
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" =
|
||||
* "value"'
|
||||
*/
|
||||
enum class EExpectation
|
||||
{
|
||||
expect_key, ///< A key is expected over a value
|
||||
expect_value, ///< A value is expected over a key
|
||||
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
|
||||
// int32_t m_LineCount{0};
|
||||
|
||||
const std::vector<std::string> m_vecKeyDelimiters{
|
||||
"\n", "\t", "\r", " ", "", ".", "=", "]"}; ///< Characters that delimit a key
|
||||
const std::vector<std::string> m_vecValueDelimiters{
|
||||
"\n", "\t", "\r", " ", ",", "", "]", "}"}; ///< Characters that delimit a value
|
||||
};
|
||||
|
||||
} // namespace toml_parser
|
||||
#endif // LEXER_TOML_H
|
||||
|
||||
552
sdv_services/core/toml_parser/lexer_toml_token.cpp
Normal file
552
sdv_services/core/toml_parser/lexer_toml_token.cpp
Normal 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
|
||||
421
sdv_services/core/toml_parser/lexer_toml_token.h
Normal file
421
sdv_services/core/toml_parser/lexer_toml_token.h
Normal 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
|
||||
549
sdv_services/core/toml_parser/miscellaneous.cpp
Normal file
549
sdv_services/core/toml_parser/miscellaneous.cpp
Normal 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
|
||||
99
sdv_services/core/toml_parser/miscellaneous.h
Normal file
99
sdv_services/core/toml_parser/miscellaneous.h
Normal 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
@@ -1,380 +1,507 @@
|
||||
#include "parser_toml.h"
|
||||
#include <iostream>
|
||||
#include "miscellaneous.h"
|
||||
#include "exception.h"
|
||||
|
||||
CParserTOML::CParserTOML(const std::string& rssString) : m_lexer(rssString)
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
Process(rssString);
|
||||
}
|
||||
|
||||
void CParserTOML::Clear()
|
||||
{
|
||||
m_ptrRoot = std::make_shared<CRootTable>();
|
||||
m_ssCurrentTable.clear();
|
||||
m_lexer.Reset();
|
||||
while (!m_stackEnvironment.empty()) m_stackEnvironment.pop();
|
||||
}
|
||||
|
||||
bool CParserTOML::Process(/*in*/ const sdv::u8string& ssContent)
|
||||
{
|
||||
Clear();
|
||||
m_lexer.Feed(ssContent);
|
||||
try
|
||||
CParser::CParser(const std::string& rssString)
|
||||
{
|
||||
// Run through all tokens of the lexer and process the tokens.
|
||||
while (!m_lexer.IsEnd())
|
||||
Process(rssString);
|
||||
}
|
||||
|
||||
void CParser::Clear()
|
||||
{
|
||||
m_ptrRoot.reset();
|
||||
m_ptrCurrentCollection.reset();
|
||||
m_lexer.Reset();
|
||||
while (!m_stackEnvironment.empty())
|
||||
m_stackEnvironment.pop();
|
||||
}
|
||||
|
||||
bool CParser::Process(/*in*/ const sdv::u8string& ssContent)
|
||||
{
|
||||
// Process the TOML string
|
||||
Clear();
|
||||
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
|
||||
{
|
||||
CLexerTOML::SToken current = m_lexer.Peek();
|
||||
switch (current.eCategory)
|
||||
// Run through all tokens of the lexer and process the tokens.
|
||||
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())
|
||||
{
|
||||
case CLexerTOML::ETokenCategory::token_syntax_table_open:
|
||||
ProcessTable();
|
||||
// The node has its own token range
|
||||
CNodeTokenRange rangeNode(refStartNodeToken.get());
|
||||
|
||||
const CToken& rCurrent = m_lexer.Peek();
|
||||
if (!rCurrent)
|
||||
{
|
||||
bEOF = true;
|
||||
break;
|
||||
}
|
||||
switch (rCurrent.Category())
|
||||
{
|
||||
case ETokenCategory::token_syntax_table_open:
|
||||
ProcessTable(rangeNode);
|
||||
break;
|
||||
case ETokenCategory::token_syntax_table_array_open:
|
||||
ProcessTableArray(rangeNode);
|
||||
break;
|
||||
case ETokenCategory::token_key:
|
||||
ProcessValueKey(rangeNode);
|
||||
break;
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
m_lexer.Consume();
|
||||
break;
|
||||
case ETokenCategory::token_terminated:
|
||||
case ETokenCategory::token_error:
|
||||
throw XTOMLParseException(rCurrent.StringValue());
|
||||
break;
|
||||
default:
|
||||
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)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
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;
|
||||
}
|
||||
|
||||
CLexer& CParser::Lexer()
|
||||
{
|
||||
return m_lexer;
|
||||
}
|
||||
|
||||
const CNodeCollection& CParser::Root() const
|
||||
{
|
||||
auto ptrCollection = m_ptrRoot->Cast<CTable>();
|
||||
return *ptrCollection.get();
|
||||
}
|
||||
|
||||
CNodeCollection& CParser::Root()
|
||||
{
|
||||
auto ptrCollection = m_ptrRoot->Cast<CTable>();
|
||||
return *ptrCollection.get();
|
||||
}
|
||||
|
||||
std::string CParser::GenerateTOML(const std::string& rssPrefixKey) const
|
||||
{
|
||||
return m_ptrRoot->GenerateTOML(rssPrefixKey);
|
||||
}
|
||||
|
||||
void CParser::ProcessTable(CNodeTokenRange& rNodeRange)
|
||||
{
|
||||
// Get the table path (table name preceded by parent tables separated with dots).
|
||||
CTokenRange rangeMain(m_lexer.Consume());
|
||||
CTokenRange rangeKeyPath = ProcessKeyPath();
|
||||
const CToken& rToken = m_lexer.Consume();
|
||||
if (!rToken || rToken.Category() != ETokenCategory::token_syntax_table_close)
|
||||
throw XTOMLParseException("Invalid Table construct");
|
||||
rangeMain.AssignEndToken(rToken); // NOTE: This includes only the name and the brackets, not the values.
|
||||
|
||||
// Assign the main token range to the node token range and let the lexer determine the extended token range
|
||||
rNodeRange.NodeMain(rangeMain);
|
||||
m_lexer.SmartExtendNodeRange(rNodeRange);
|
||||
|
||||
// Add the table to the root
|
||||
auto ptrTable = m_ptrRoot->Insert<CTable>(sdv::toml::npos, rangeKeyPath, false);
|
||||
if (ptrTable)
|
||||
{
|
||||
m_ptrCurrentCollection = ptrTable->Cast<CTable>();
|
||||
ptrTable->UpdateNodeCode(rNodeRange);
|
||||
}
|
||||
}
|
||||
|
||||
void CParser::ProcessTableArray(CNodeTokenRange& rNodeRange)
|
||||
{
|
||||
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.
|
||||
|
||||
// Assign the main token range to the node token range and let the lexer determine the extended token range
|
||||
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");
|
||||
rangeMain.AssignEndToken(rToken);
|
||||
|
||||
// Store this initial range
|
||||
rNodeRange.NodeMain(rangeMain);
|
||||
|
||||
// Process the value assignment
|
||||
ProcessValue(rangeKeyPath, rNodeRange);
|
||||
}
|
||||
|
||||
void CParser::ProcessValue(const CTokenRange& rrangeKeyPath, CNodeTokenRange& rNodeRange)
|
||||
{
|
||||
// Extend the main range
|
||||
CTokenRange rangeMain = rNodeRange.NodeMain();
|
||||
|
||||
// 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())
|
||||
{
|
||||
case ETokenCategory::token_boolean:
|
||||
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 ETokenStringType::literal_string:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(),
|
||||
CStringNode::EQuotationType::literal_string, rAssignmentValue.RawString());
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_syntax_table_array_open:
|
||||
ProcessTableArray();
|
||||
case ETokenStringType::multi_line_literal:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
|
||||
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_literal,
|
||||
rAssignmentValue.RawString());
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_key:
|
||||
ProcessValueKey();
|
||||
case ETokenStringType::multi_line_quoted:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
|
||||
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::multi_line_quoted,
|
||||
rAssignmentValue.RawString());
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_syntax_new_line:
|
||||
case ETokenStringType::quoted_string:
|
||||
default:
|
||||
ptrNode = m_ptrCurrentCollection->Insert<CStringNode>(
|
||||
sdv::toml::npos, rrangeKeyPath, rAssignmentValue.StringValue(), CStringNode::EQuotationType::quoted_string,
|
||||
rAssignmentValue.RawString());
|
||||
break;
|
||||
}
|
||||
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);
|
||||
ProcessArray(rNodeRange);
|
||||
m_stackEnvironment.pop();
|
||||
m_ptrCurrentCollection = ptrCurrentCollectionStored;
|
||||
}
|
||||
break;
|
||||
case ETokenCategory::token_syntax_inline_table_open:
|
||||
{
|
||||
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);
|
||||
ProcessInlineTable(rNodeRange);
|
||||
m_stackEnvironment.pop();
|
||||
m_ptrCurrentCollection = ptrCurrentCollectionStored;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw XTOMLParseException("Missing value");
|
||||
break;
|
||||
}
|
||||
|
||||
// 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())
|
||||
{
|
||||
// Skip newlines
|
||||
while (refToken.get().Category() == ETokenCategory::token_syntax_new_line)
|
||||
{
|
||||
m_lexer.Consume();
|
||||
refToken = m_lexer.Peek();
|
||||
}
|
||||
|
||||
switch (m_stackEnvironment.top())
|
||||
{
|
||||
case EEnvironment::env_array:
|
||||
{
|
||||
int32_t index = 1;
|
||||
while (refToken.get() && refToken.get().Category() == ETokenCategory::token_syntax_new_line)
|
||||
{
|
||||
refToken = m_lexer.Peek(index++);
|
||||
}
|
||||
if (!refToken.get()
|
||||
|| (refToken.get().Category() != ETokenCategory::token_syntax_comma
|
||||
&& refToken.get().Category() != ETokenCategory::token_syntax_array_close))
|
||||
{
|
||||
throw XTOMLParseException("Invalid Token after value assignment in array; ',' or ']' needed");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_terminated:
|
||||
case CLexerTOML::ETokenCategory::token_error:
|
||||
throw XTOMLParseException(current.ssContentString);
|
||||
case EEnvironment::env_inline_table:
|
||||
if (!refToken.get()
|
||||
|| (refToken.get().Category() != ETokenCategory::token_syntax_comma
|
||||
&& refToken.get().Category() != ETokenCategory::token_syntax_inline_table_close))
|
||||
{
|
||||
throw XTOMLParseException("Invalid Token after value assignment in inline table; ',' or '}' needed ");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw XTOMLParseException("Invalid Syntax; not a Key, Table or Tablearray");
|
||||
break;
|
||||
}
|
||||
rNodeRange.LinesBehindNode(refToken);
|
||||
}
|
||||
}
|
||||
catch (const sdv::toml::XTOMLParseException& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const CNodeCollection& CParserTOML::GetRoot() const
|
||||
{
|
||||
auto ptrCollection = m_ptrRoot->GetTable();
|
||||
return *ptrCollection.get();
|
||||
}
|
||||
|
||||
CNodeCollection& CParserTOML::GetRoot()
|
||||
{
|
||||
auto ptrCollection = m_ptrRoot->GetTable();
|
||||
return *ptrCollection.get();
|
||||
}
|
||||
|
||||
std::string CParserTOML::CreateTOMLText(const std::string& rssParent) const
|
||||
{
|
||||
std::string ssLastPrintedTable;
|
||||
return m_ptrRoot->CreateTOMLText(rssParent, ssLastPrintedTable);
|
||||
}
|
||||
|
||||
bool CParserTOML::Add(const std::string& rssPath, bool bVal)
|
||||
{
|
||||
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).
|
||||
m_lexer.Consume();
|
||||
std::string ssPath = ComposePath();
|
||||
CLexerTOML::SToken sToken = m_lexer.Consume();
|
||||
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_table_close)
|
||||
{
|
||||
throw XTOMLParseException("invalid Table construct");
|
||||
}
|
||||
|
||||
// Find the last dot - the name follows
|
||||
size_t nOffset = FindLast(ssPath);
|
||||
if (nOffset == std::string::npos)
|
||||
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
|
||||
m_ptrRoot->Add(ssPath, std::make_shared<CNormalTable>(ssName), false);
|
||||
|
||||
m_ssCurrentTable = ssPath;
|
||||
}
|
||||
|
||||
void CParserTOML::ProcessTableArray()
|
||||
{
|
||||
m_lexer.Consume();
|
||||
std::string rssKeyPath = ComposePath();
|
||||
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()
|
||||
{
|
||||
std::string rssKeyPath = (m_ssCurrentTable.empty() ? "" : (m_ssCurrentTable + ".")) + ComposePath();
|
||||
|
||||
CLexerTOML::SToken sToken = m_lexer.Consume();
|
||||
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_assignment)
|
||||
{
|
||||
throw XTOMLParseException("Assignment expected");
|
||||
}
|
||||
|
||||
ProcessValue(rssKeyPath);
|
||||
}
|
||||
|
||||
void CParserTOML::ProcessValue(const std::string& rssKeyPath)
|
||||
{
|
||||
CLexerTOML::SToken assignmentValue = m_lexer.Consume();
|
||||
switch (assignmentValue.eCategory)
|
||||
{
|
||||
case CLexerTOML::ETokenCategory::token_boolean:
|
||||
Add(rssKeyPath, assignmentValue.bContentBoolean);
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_integer:
|
||||
Add(rssKeyPath, assignmentValue.iContentInteger);
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_float:
|
||||
Add(rssKeyPath, assignmentValue.dContentFloatingpoint);
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_string:
|
||||
Add(rssKeyPath, assignmentValue.ssContentString);
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_syntax_array_open:
|
||||
Add<CNormalArray>(rssKeyPath);
|
||||
m_stackEnvironment.push(EEnvironment::env_array);
|
||||
ProcessArray(rssKeyPath);
|
||||
m_stackEnvironment.pop();
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_syntax_inline_table_open:
|
||||
Add<CInlineTable>(rssKeyPath);
|
||||
m_stackEnvironment.push(EEnvironment::env_inline_table);
|
||||
ProcessInlineTable(rssKeyPath);
|
||||
m_stackEnvironment.pop();
|
||||
break;
|
||||
default:
|
||||
throw XTOMLParseException("Missing value");
|
||||
break;
|
||||
}
|
||||
|
||||
CLexerTOML::SToken sToken = m_lexer.Peek();
|
||||
if (!m_stackEnvironment.empty())
|
||||
{
|
||||
switch (m_stackEnvironment.top())
|
||||
{
|
||||
case EEnvironment::env_array:
|
||||
{
|
||||
int32_t index = 2;
|
||||
while (sToken.eCategory == CLexerTOML::ETokenCategory::token_syntax_new_line)
|
||||
{
|
||||
sToken = m_lexer.Peek(index++);
|
||||
}
|
||||
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_comma
|
||||
&& sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_array_close)
|
||||
{
|
||||
throw XTOMLParseException(
|
||||
"Invalid Token after value assignment in array; ',' or ']' needed");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EEnvironment::env_inline_table:
|
||||
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_comma
|
||||
&& sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_inline_table_close)
|
||||
{
|
||||
throw XTOMLParseException(
|
||||
"Invalid Token after value assignment in inline table; ',' or '}' needed ");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_new_line && sToken.eCategory != CLexerTOML::ETokenCategory::token_eof)
|
||||
{
|
||||
throw XTOMLParseException("Invalid Token after value assignment; newline needed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CParserTOML::ProcessArray(const std::string& rssKeyPath)
|
||||
{
|
||||
/*
|
||||
Arrays are defined as follow: array_name = [value, value, ...]
|
||||
Arrays can have new-lines between their values.
|
||||
And can end with a comma.
|
||||
For example:
|
||||
integers = [ 1, 2, 3 ]
|
||||
colors = [ "red", "yellow", "green", ]
|
||||
nested_arrays_of_ints = [ [ 1, 2 ],
|
||||
[3, 4, 5] ]
|
||||
nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ]
|
||||
string_array = [ "all", 'strings', """are the same""", '''type''' ]
|
||||
*/
|
||||
|
||||
CLexerTOML::SToken sToken = m_lexer.Peek();
|
||||
|
||||
size_t nIndex = 0;
|
||||
enum class EExpect {value_comma_end, comma_end} eExpect = EExpect::value_comma_end;
|
||||
while (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_array_close)
|
||||
{
|
||||
switch (sToken.eCategory)
|
||||
{
|
||||
//case CLexerTOML::ETokenCategory::token_syntax_array_open: // Embedded array
|
||||
// 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();
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_syntax_comma:
|
||||
m_lexer.Consume();
|
||||
eExpect = EExpect::value_comma_end;
|
||||
break;
|
||||
default:
|
||||
if (eExpect == EExpect::comma_end)
|
||||
throw XTOMLParseException("Expecting comma or table end.");
|
||||
ProcessValue(rssKeyPath + "." + std::to_string(nIndex++));
|
||||
eExpect = EExpect::comma_end;
|
||||
break;
|
||||
}
|
||||
sToken = m_lexer.Peek();
|
||||
}
|
||||
m_lexer.Consume();
|
||||
}
|
||||
|
||||
void CParserTOML::ProcessInlineTable(const std::string& rssKeyPath)
|
||||
{
|
||||
/*
|
||||
Inline tables are defined as follow: table_name = {value, value, ...}
|
||||
For example:
|
||||
name = { first = "Tom", last = "Preston-Werner" }
|
||||
point = { x = 1, y = 2 }
|
||||
animal = { type.name = "pug" }
|
||||
*/
|
||||
|
||||
CLexerTOML::SToken sToken = m_lexer.Peek();
|
||||
|
||||
std::string ssCurrentTableTemp = m_ssCurrentTable;
|
||||
m_ssCurrentTable = rssKeyPath;
|
||||
enum class EExpect { value_comma_end, value, comma_end } eExpect = EExpect::value_comma_end;
|
||||
while (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_inline_table_close)
|
||||
{
|
||||
switch (sToken.eCategory)
|
||||
{
|
||||
case CLexerTOML::ETokenCategory::token_syntax_new_line:
|
||||
throw XTOMLParseException("No newlines allowed in inline table");
|
||||
break;
|
||||
case CLexerTOML::ETokenCategory::token_syntax_comma:
|
||||
if (eExpect == EExpect::value)
|
||||
throw XTOMLParseException("Unexpected comma.");
|
||||
m_lexer.Consume();
|
||||
eExpect = EExpect::value;
|
||||
break;
|
||||
default:
|
||||
if (eExpect == EExpect::comma_end)
|
||||
throw XTOMLParseException("Expecting comma or table end.");
|
||||
ProcessValueKey();
|
||||
eExpect = EExpect::comma_end;
|
||||
break;
|
||||
}
|
||||
sToken = m_lexer.Peek();
|
||||
}
|
||||
if (eExpect == EExpect::value)
|
||||
throw XTOMLParseException("Expecting a value before inline table end.");
|
||||
|
||||
m_ptrRoot->Find(rssKeyPath)->GetTable()->m_bOpenToAddChildren = false;
|
||||
|
||||
m_lexer.Consume();
|
||||
m_ssCurrentTable = ssCurrentTableTemp;
|
||||
}
|
||||
|
||||
std::string CParserTOML::ComposePath()
|
||||
{
|
||||
std::string ssPath;
|
||||
CLexerTOML::SToken sToken = m_lexer.Peek();
|
||||
if (sToken.eCategory != CLexerTOML::ETokenCategory::token_syntax_dot
|
||||
&& sToken.eCategory != CLexerTOML::ETokenCategory::token_key)
|
||||
throw XTOMLParseException("Invalid Token to assemble path from keys");
|
||||
|
||||
while (sToken.eCategory == CLexerTOML::ETokenCategory::token_syntax_dot
|
||||
|| sToken.eCategory == CLexerTOML::ETokenCategory::token_key)
|
||||
{
|
||||
m_lexer.Consume();
|
||||
if (sToken.eCategory == CLexerTOML::ETokenCategory::token_key)
|
||||
ssPath += sToken.ssContentString;
|
||||
else
|
||||
ssPath += ".";
|
||||
sToken = m_lexer.Peek();
|
||||
{
|
||||
if (refToken.get() && refToken.get().Category() != ETokenCategory::token_syntax_new_line)
|
||||
{
|
||||
throw XTOMLParseException("Invalid Token after value assignment; newline needed");
|
||||
}
|
||||
}
|
||||
if (ptrNode) ptrNode->UpdateNodeCode(rNodeRange);
|
||||
}
|
||||
return ssPath;
|
||||
}
|
||||
|
||||
void CParser::ProcessArray(CNodeTokenRange& rNodeRange)
|
||||
{
|
||||
/*
|
||||
Arrays are defined as follow: array_name = [value, value, ...]
|
||||
Arrays can have new-lines between their values.
|
||||
And can end with a comma.
|
||||
For example:
|
||||
integers = [ 1, 2, 3 ]
|
||||
colors = [ "red", "yellow", "green", ]
|
||||
nested_arrays_of_ints = [ [ 1, 2 ],
|
||||
[3, 4, 5] ]
|
||||
nested_mixed_array = [ [ 1, 2 ], ["a", "b", "c"] ]
|
||||
string_array = [ "all", 'strings', """are the same""", '''type''' ]
|
||||
*/
|
||||
|
||||
// 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;
|
||||
bool bAdditionalCommaBeforeEnd = false;
|
||||
std::reference_wrapper<const CToken> refCommaToken = m_lexer.Peek();
|
||||
while (refToken.get() && refToken.get().Category() != ETokenCategory::token_syntax_array_close)
|
||||
{
|
||||
// The value has its own token range
|
||||
CNodeTokenRange rangeNode(refStartValueToken.get());
|
||||
|
||||
switch (refToken.get().Category())
|
||||
{
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
m_lexer.Consume();
|
||||
break;
|
||||
case ETokenCategory::token_syntax_comma:
|
||||
if (eExpect == EExpect::value_or_end) throw XTOMLParseException("Expecting value or array end.");
|
||||
m_lexer.Consume();
|
||||
eExpect = EExpect::value_or_end;
|
||||
refCommaToken = refToken;
|
||||
bAdditionalCommaBeforeEnd = true;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
bAdditionalCommaBeforeEnd = false;
|
||||
if (eExpect == EExpect::comma_or_end) throw XTOMLParseException("Expecting comma or array 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
void CParser::ProcessInlineTable(CNodeTokenRange& rNodeRange)
|
||||
{
|
||||
/*
|
||||
Inline tables are defined as follow: table_name = {value, value, ...}
|
||||
For example:
|
||||
name = { first = "Tom", last = "Preston-Werner" }
|
||||
point = { x = 1, y = 2 }
|
||||
animal = { type.name = "pug" }
|
||||
*/
|
||||
|
||||
// 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,
|
||||
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)
|
||||
{
|
||||
// The value has its own token range
|
||||
CNodeTokenRange rangeNode(refStartValueToken.get());
|
||||
|
||||
switch (refToken.get().Category())
|
||||
{
|
||||
case ETokenCategory::token_syntax_new_line:
|
||||
m_lexer.Consume();
|
||||
break;
|
||||
case ETokenCategory::token_syntax_comma:
|
||||
if (eExpect != EExpect::comma_or_end) throw XTOMLParseException("Expecting value or table end.");
|
||||
m_lexer.Consume();
|
||||
eExpect = EExpect::value;
|
||||
break;
|
||||
default:
|
||||
if (eExpect == EExpect::comma_or_end) throw XTOMLParseException("Expecting comma or table end.");
|
||||
ProcessValueKey(rangeNode);
|
||||
eExpect = EExpect::comma_or_end;
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the start of the next value range
|
||||
refStartValueToken = rangeNode.LinesBehindNode().End();
|
||||
|
||||
// Get the next value
|
||||
refToken = m_lexer.Peek();
|
||||
}
|
||||
if (eExpect == EExpect::value)
|
||||
throw XTOMLParseException("Expecting a value before inline table end.");
|
||||
|
||||
// This is the node token range finishing main part.
|
||||
rNodeRange.NodeMainFinish(CTokenRange(refToken.get(), refToken.get().Next()));
|
||||
|
||||
// Consume the token
|
||||
m_lexer.Consume();
|
||||
}
|
||||
|
||||
CTokenRange CParser::ProcessKeyPath()
|
||||
{
|
||||
//std::string ssPath;
|
||||
std::reference_wrapper<const CToken> refToken = m_lexer.Peek();
|
||||
std::reference_wrapper<const CToken> refKeyStart = refToken;
|
||||
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");
|
||||
|
||||
while (refToken.get()
|
||||
&& (refToken.get().Category() == ETokenCategory::token_syntax_dot
|
||||
|| refToken.get().Category() == ETokenCategory::token_key))
|
||||
{
|
||||
m_lexer.Consume();
|
||||
//if (refToken.get().Category() == ETokenCategory::token_key)
|
||||
//{
|
||||
// EQuoteRequest eQuoteRequest = EQuoteRequest::smart_key;
|
||||
// switch (refToken.get().StringType())
|
||||
// {
|
||||
// case ETokenStringType::literal_string:
|
||||
// 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
|
||||
@@ -3,171 +3,137 @@
|
||||
|
||||
#include "lexer_toml.h"
|
||||
#include "parser_node_toml.h"
|
||||
#include "miscellaneous.h"
|
||||
#include <stack>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief Creates a tree structure from input of UTF-8 encoded TOML source data
|
||||
*/
|
||||
class CParserTOML : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser
|
||||
/// The TOML parser namespace
|
||||
namespace toml_parser
|
||||
{
|
||||
public:
|
||||
class CNode;
|
||||
|
||||
/**
|
||||
* @brief Default constructor
|
||||
* @brief Creates a tree structure from input of UTF-8 encoded TOML source data
|
||||
*/
|
||||
CParserTOML() = default;
|
||||
|
||||
/**
|
||||
* @brief Construct a new Parser object
|
||||
* @param[in] rssString UTF-8 encoded data of a TOML source
|
||||
*/
|
||||
CParserTOML(const std::string& rssString);
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::toml::ITOMLParser)
|
||||
SDV_INTERFACE_CHAIN_MEMBER(m_ptrRoot)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Clears the current parse result.
|
||||
* @attention This will render any pointer invalid!
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
// Ignore cppcheck warning for not using dynamic binding when being called through the constructor.
|
||||
// cppcheck-suppress virtualCallInConstructor
|
||||
/**
|
||||
* @brief Process the configuration from the supplied content string. Overload of sdv::toml::ITOMLParser.
|
||||
* @param[in] ssContent Configuration string.
|
||||
* @return Returns 'true' when the configuration could be read successfully, false when not.
|
||||
*/
|
||||
virtual bool Process(/*in*/ const sdv::u8string& ssContent) override;
|
||||
|
||||
/**
|
||||
* @{
|
||||
* @brief Return the root node.
|
||||
* @return Reference to the root node collection.
|
||||
*/
|
||||
const CNodeCollection& GetRoot() const;
|
||||
CNodeCollection& GetRoot();
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Get the TOML text based on the content.
|
||||
* @param[in] rssParent When present, uses the parent node into the TOML text generation.
|
||||
* @return The string containing the TOML text.
|
||||
*/
|
||||
std::string CreateTOMLText(const std::string& rssParent = std::string()) const;
|
||||
|
||||
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.
|
||||
*/
|
||||
void ProcessTable();
|
||||
|
||||
/**
|
||||
* @brief Process a table array declaration.
|
||||
*/
|
||||
void ProcessTableArray();
|
||||
|
||||
/**
|
||||
* @brief Process the value key.
|
||||
*/
|
||||
void ProcessValueKey();
|
||||
|
||||
/**
|
||||
* @brief Process the value with the supplied key.
|
||||
* @param[in] rssKeyPath Reference to the key path string.
|
||||
*/
|
||||
void ProcessValue(const std::string& rssKeyPath);
|
||||
|
||||
/**
|
||||
* @brief Process the array value with the supplied key.
|
||||
* @param[in] rssKeyPath Reference to the key path string.
|
||||
*/
|
||||
void ProcessArray(const std::string& rssKeyPath);
|
||||
|
||||
/**
|
||||
* @brief Process the inline table value with the supplied key.
|
||||
* @param[in] rssKeyPath Reference to the key path string.
|
||||
*/
|
||||
void ProcessInlineTable(const std::string& rssKeyPath);
|
||||
|
||||
/**
|
||||
* @brief Compose a path from lexer tokens. A path is composed of table and array elements separated with a dot.
|
||||
* @return The composed path.
|
||||
*/
|
||||
std::string ComposePath();
|
||||
|
||||
/**
|
||||
* @brief Enum for differentiating between an array environment and an inline table environment for syntax checks.
|
||||
*/
|
||||
enum class EEnvironment
|
||||
class CParser : public sdv::IInterfaceAccess, public sdv::toml::ITOMLParser
|
||||
{
|
||||
env_array, //!< Environment for an array
|
||||
env_inline_table //!< Environment for a table
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor
|
||||
*/
|
||||
CParser() = default;
|
||||
|
||||
/**
|
||||
* @brief Construct a new Parser object
|
||||
* @param[in] rssString UTF-8 encoded data of a TOML source
|
||||
*/
|
||||
CParser(const std::string& rssString);
|
||||
|
||||
// Interface map
|
||||
BEGIN_SDV_INTERFACE_MAP()
|
||||
SDV_INTERFACE_ENTRY(sdv::toml::ITOMLParser)
|
||||
SDV_INTERFACE_CHAIN_MEMBER(m_ptrRoot)
|
||||
END_SDV_INTERFACE_MAP()
|
||||
|
||||
/**
|
||||
* @brief Clears the current parse result.
|
||||
* @attention This will render any pointer invalid!
|
||||
*/
|
||||
void Clear();
|
||||
|
||||
// Ignore cppcheck warning for not using dynamic binding when being called through the constructor.
|
||||
// cppcheck-suppress virtualCallInConstructor
|
||||
/**
|
||||
* @brief Process the configuration from the supplied content string. Overload of sdv::toml::ITOMLParser.
|
||||
* @param[in] ssContent Configuration string.
|
||||
* @return Returns 'true' when the configuration could be read successfully, false when not.
|
||||
*/
|
||||
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.
|
||||
* @return Reference to the root node collection.
|
||||
*/
|
||||
const CNodeCollection& Root() const;
|
||||
CNodeCollection& Root();
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Get the TOML text based on the content.
|
||||
* @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.
|
||||
*/
|
||||
std::string GenerateTOML(const std::string& rssPrefixKey = std::string()) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Process a table declaration.
|
||||
* @param[in, out] rNodeRange Reference to the extended token range of the node.
|
||||
*/
|
||||
void ProcessTable(CNodeTokenRange& rNodeRange);
|
||||
|
||||
/**
|
||||
* @brief Process a table array declaration.
|
||||
* @param[in, out] rNodeRange Reference to the extended token range of the node.
|
||||
*/
|
||||
void ProcessTableArray(CNodeTokenRange& rNodeRange);
|
||||
|
||||
/**
|
||||
* @brief Process the value key.
|
||||
* @param[in, out] rNodeRange Reference to the extended token range of the node.
|
||||
*/
|
||||
void ProcessValueKey(CNodeTokenRange& rNodeRange);
|
||||
|
||||
/**
|
||||
* @brief Process the value with the supplied key.
|
||||
* @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 CTokenRange& rrangeKeyPath, CNodeTokenRange& rNodeRange);
|
||||
|
||||
/**
|
||||
* @brief Process the array value with the supplied key.
|
||||
* @param[in, out] rNodeRange Reference to the extended token range of the node. The second main range will be added.
|
||||
*/
|
||||
void ProcessArray(CNodeTokenRange& rNodeRange);
|
||||
|
||||
/**
|
||||
* @brief Process the inline table value with the supplied key.
|
||||
* @param[in, out] rNodeRange Reference to the extended token range of the node. The second main range will be added.
|
||||
*/
|
||||
void ProcessInlineTable(CNodeTokenRange& rNodeRange);
|
||||
|
||||
/**
|
||||
* @brief Compose a path from lexer tokens. A path is composed of table and array elements separated with a dot.
|
||||
* @return The token range of the key path.
|
||||
*/
|
||||
CTokenRange ProcessKeyPath();
|
||||
|
||||
/**
|
||||
* @brief Enum for differentiating between an array environment and an inline table environment for syntax checks.
|
||||
*/
|
||||
enum class EEnvironment
|
||||
{
|
||||
env_array, ///< Environment for an array
|
||||
env_inline_table ///< Environment for a table
|
||||
};
|
||||
|
||||
std::stack<EEnvironment> m_stackEnvironment; ///< Tracking of environments in nested structures.
|
||||
std::shared_ptr<CRootTable> m_ptrRoot; ///< The one root node.
|
||||
std::shared_ptr<CNodeCollection> m_ptrCurrentCollection; ///< The current collection node.
|
||||
CLexer m_lexer; ///< Lexer.
|
||||
};
|
||||
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::string m_ssCurrentTable; ///< Path to the current table.
|
||||
CLexerTOML m_lexer; ///< Lexer.
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
} // namespace toml_parser
|
||||
|
||||
#endif // PARSER_TOML_H
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
DECLARE_OBJECT_CLASS_NAME("TOMLParserUtility")
|
||||
|
||||
private:
|
||||
CParserTOML m_parser; ///< Configuration parser
|
||||
toml_parser::CParser m_parser; ///< Configuration parser
|
||||
};
|
||||
|
||||
DEFINE_SDV_OBJECT_NO_EXPORT(CTOMLParserUtility)
|
||||
|
||||
@@ -12,7 +12,8 @@ CChannelConnector::CChannelConnector(CCommunicationControl& rcontrol, uint32_t u
|
||||
m_pDataSend(m_ptrChannelEndpoint.GetInterface<sdv::ipc::IDataSend>())
|
||||
{
|
||||
m_tConnectionID.uiIdent = uiIndex;
|
||||
m_tConnectionID.uiControl = static_cast<uint32_t>(rand());
|
||||
while (!m_tConnectionID.uiControl)
|
||||
m_tConnectionID.uiControl = static_cast<uint32_t>(rand());
|
||||
}
|
||||
|
||||
CChannelConnector::~CChannelConnector()
|
||||
@@ -29,7 +30,7 @@ CChannelConnector::~CChannelConnector()
|
||||
lock.unlock();
|
||||
|
||||
// Cancel the processing
|
||||
rsEntry.bCancel = true;
|
||||
rsEntry.eState = SCallEntry::EState::canceled;
|
||||
rsEntry.cvWaitForResult.notify_all();
|
||||
|
||||
// Handle next call.
|
||||
@@ -44,6 +45,10 @@ CChannelConnector::~CChannelConnector()
|
||||
if (m_uiConnectionStatusCookie) pConnection->UnregisterStatusEventCallback(m_uiConnectionStatusCookie);
|
||||
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)
|
||||
@@ -230,20 +235,25 @@ void CChannelConnector::DecoupledReceiveData(/*inout*/ sdv::sequence<sdv::pointe
|
||||
|
||||
// Send the result back
|
||||
m_pDataSend->SendData(seqResult);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
// Look for the call entry
|
||||
std::unique_lock<std::mutex> lockCallMap(m_mtxCalls);
|
||||
auto itCall = m_mapCalls.find(sAddress.uiCallIndex);
|
||||
if (itCall == m_mapCalls.end()) return;
|
||||
if (itCall == m_mapCalls.end())
|
||||
return;
|
||||
SCallEntry& rsCallEntry = itCall->second;
|
||||
m_mapCalls.erase(itCall);
|
||||
lockCallMap.unlock();
|
||||
if (rsCallEntry.eState != SCallEntry::EState::processing)
|
||||
return;
|
||||
|
||||
// Update the result
|
||||
std::unique_lock<std::mutex> lockCall(rsCallEntry.mtxWaitForResult);
|
||||
rsCallEntry.seqResult = seqData;
|
||||
lockCall.unlock();
|
||||
rsCallEntry.eState = SCallEntry::EState::processed;
|
||||
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);
|
||||
SCallEntry sResult;
|
||||
m_mapCalls.try_emplace(sAddress.uiCallIndex, sResult);
|
||||
sResult.eState = SCallEntry::EState::processing;
|
||||
lock.unlock();
|
||||
|
||||
// Store the channel context (used to marshall interfaces over the same connector)
|
||||
m_rcontrol.SetConnectorContext(this);
|
||||
|
||||
// Send the data
|
||||
std::unique_lock<std::mutex> lockResult(sResult.mtxWaitForResult);
|
||||
try
|
||||
{
|
||||
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
|
||||
if (sResult.bCancel) throw sdv::ps::XMarshallTimeout();
|
||||
std::unique_lock<std::mutex> lockResult(sResult.mtxWaitForResult);
|
||||
sResult.cvWaitForResult.wait(lockResult);
|
||||
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
|
||||
// function has been entered (race condition). This would cause the function to wait forever.
|
||||
while (sResult.eState == SCallEntry::EState::processing)
|
||||
sResult.cvWaitForResult.wait_for(lockResult, std::chrono::milliseconds(1));
|
||||
|
||||
if (sResult.eState == SCallEntry::EState::canceled)
|
||||
throw sdv::ps::XMarshallTimeout();
|
||||
|
||||
return sResult.seqResult;
|
||||
}
|
||||
|
||||
@@ -114,10 +114,19 @@ private:
|
||||
*/
|
||||
struct SCallEntry
|
||||
{
|
||||
sdv::sequence<sdv::pointer<uint8_t>> seqResult; ///< The result data.
|
||||
std::mutex mtxWaitForResult; ///< Mutex to protect result processing.
|
||||
std::condition_variable cvWaitForResult; ///< Condition variable to trigger result processing.
|
||||
bool bCancel = false; ///< Cancel processing when set.
|
||||
/**
|
||||
* @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.
|
||||
std::mutex mtxWaitForResult; ///< Mutex to protect result processing.
|
||||
std::condition_variable cvWaitForResult; ///< Condition variable to trigger result processing.
|
||||
};
|
||||
|
||||
CCommunicationControl& m_rcontrol; ///< Reference to the communication control class.
|
||||
|
||||
@@ -32,7 +32,9 @@ sdv::interface_t CMarshallObject::InitializeAsProxy(uint32_t uiProxyIndex, sdv::
|
||||
m_eType = EType::proxy;
|
||||
|
||||
// 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
|
||||
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.
|
||||
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
|
||||
sdv::core::IRepositoryMarshallCreate* pMarshallCreate =
|
||||
|
||||
@@ -342,7 +342,8 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver)
|
||||
if (m_eStatus != sdv::ipc::EConnectStatus::uninitialized)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -456,6 +457,8 @@ uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess*
|
||||
sdv::ipc::IConnectEventCallback* pCallback = pEventCallback->GetInterface<sdv::ipc::IConnectEventCallback>();
|
||||
if (!pCallback) return 0;
|
||||
uint64_t uiCookie = rand();
|
||||
while (!uiCookie)
|
||||
uiCookie = rand();
|
||||
std::unique_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
|
||||
m_lstEventCallbacks.emplace(m_lstEventCallbacks.begin(), std::move(SEventCallback{ uiCookie, pCallback }));
|
||||
return uiCookie;
|
||||
@@ -490,7 +493,10 @@ void CConnection::DestroyObject()
|
||||
// Clear all events callbacks (if not done so already)
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
|
||||
for (auto& rprEventCallback : m_lstEventCallbacks)
|
||||
{
|
||||
rprEventCallback.uiCookie = 0;
|
||||
rprEventCallback.pCallback = nullptr;
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
// 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;
|
||||
std::shared_lock<std::shared_mutex> lock(m_mtxEventCallbacks);
|
||||
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 (m_eStatus == sdv::ipc::EConnectStatus::disconnected_forced)
|
||||
@@ -554,12 +563,13 @@ void CConnection::ReceiveMessages()
|
||||
{
|
||||
SetStatus(sdv::ipc::EConnectStatus::communication_error);
|
||||
SDV_LOG_ERROR("No valid shared memory for receiving.");
|
||||
lock.unlock();
|
||||
m_cvStartConnect.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
m_cvStartConnect.notify_all();
|
||||
lock.unlock();
|
||||
m_cvStartConnect.notify_all();
|
||||
|
||||
// Read processing
|
||||
auto tpStart = std::chrono::high_resolution_clock::time_point();
|
||||
@@ -623,8 +633,24 @@ void CConnection::ReceiveMessages()
|
||||
case EMsgType::data_fragment:
|
||||
case EMsgType::data:
|
||||
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:
|
||||
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
|
||||
|
||||
@@ -1011,7 +1037,7 @@ void CConnection::ReceiveDataFragementMessage(CMessage& rMessage, SDataContext&
|
||||
if (!rMessage.GetFragmentedHdr().uiOffset)
|
||||
{
|
||||
#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
|
||||
// Read the data directory table...
|
||||
// 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 << ", ";
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
/// When put to 1, decoupling of receive data is activated (default is not activated).
|
||||
#define ENABLE_DECOUPLING 0
|
||||
|
||||
#if ENABLE_REPORTING > 0
|
||||
#if ENABLE_REPORTING > 0 && !defined(ENABLE_TRACE)
|
||||
/// Enable tracing
|
||||
#define ENABLE_TRACE 1
|
||||
#endif
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
CInProcMemBuffer(const std::string& rssConnectionString);
|
||||
|
||||
/**
|
||||
* \brief Default destructor
|
||||
* @brief Default destructor
|
||||
*/
|
||||
~CInProcMemBuffer() = default;
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
CSharedMemBuffer(CSharedMemBuffer&&) = delete;
|
||||
|
||||
/**
|
||||
* \brief Default destructor
|
||||
* @brief Default destructor
|
||||
*/
|
||||
~CSharedMemBuffer();
|
||||
|
||||
@@ -364,10 +364,15 @@ CSharedMemBuffer<TAccessor>::~CSharedMemBuffer()
|
||||
munmap(m_pBuffer, m_uiSize);
|
||||
if (m_bServer && !m_ssName.empty())
|
||||
shm_unlink((std::string("/") + m_ssName).c_str());
|
||||
if (m_iFileDescr >= 0) close(m_iFileDescr);
|
||||
if (m_bServer && m_pSemaphoreTx)
|
||||
sem_unlink(m_ssSyncTx.c_str());
|
||||
if (m_pSemaphoreTx)
|
||||
sem_close(m_pSemaphoreTx);
|
||||
if (m_bServer && m_pSemaphoreRx)
|
||||
sem_unlink(m_ssSyncRx.c_str());
|
||||
if (m_pSemaphoreRx)
|
||||
sem_close(m_pSemaphoreRx);
|
||||
}
|
||||
|
||||
template <class TAccessor>
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
CSharedMemBuffer(CSharedMemBuffer&&) = delete;
|
||||
|
||||
/**
|
||||
* \brief Default destructor
|
||||
* @brief Default destructor
|
||||
*/
|
||||
~CSharedMemBuffer();
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
|
||||
// Forward declaration
|
||||
class CConnection;
|
||||
@@ -114,7 +115,7 @@ private:
|
||||
///< connection is scheduled for destruction.
|
||||
std::queue<std::shared_ptr<CConnection>> m_queueScheduledConnectionDestructions; ///< Scheduled connection for destruction.
|
||||
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
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
|
||||
/**
|
||||
* @brief Process control service class
|
||||
@@ -135,41 +136,40 @@ public:
|
||||
void MonitorThread();
|
||||
|
||||
sdv::EObjectStatus m_eObjectStatus = sdv::EObjectStatus::initialization_pending; ///< Object status.
|
||||
|
||||
std::mutex m_mtxProcessThreadShutdown; ///< Synchronize access
|
||||
std::mutex m_mtxProcessThreadShutdown; ///< Synchronize access
|
||||
#ifdef _WIN32
|
||||
std::map<sdv::process::TProcessID, std::pair<HANDLE,HANDLE>> m_mapProcessThreadShutdown; ///< Map with process IDs and event handles
|
||||
#elif __unix__
|
||||
std::set<sdv::process::TProcessID> m_setProcessThreadShutdown; ///< Set with process IDs
|
||||
std::set<sdv::process::TProcessID> m_setProcessThreadShutdown; ///< Set with process IDs
|
||||
#else
|
||||
#error OS is not supported!
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Process helper structure
|
||||
*/
|
||||
struct SProcessHelper
|
||||
{
|
||||
sdv::process::TProcessID tProcessID = 0; ///< Process ID
|
||||
/**
|
||||
* @brief Process helper structure
|
||||
*/
|
||||
struct SProcessHelper
|
||||
{
|
||||
sdv::process::TProcessID tProcessID = 0; ///< Process ID
|
||||
#ifdef _WIN32
|
||||
HANDLE hProcess = 0; ///< process handle
|
||||
HANDLE hProcess = 0; ///< process handle
|
||||
#elif defined __unix__
|
||||
bool bNotAChild = false; ///< When set, the process is not a child of the monitor process.
|
||||
bool bNotAChild = false; ///< When set, the process is not a child of the monitor process.
|
||||
#else
|
||||
#error OS is not supported!
|
||||
#endif
|
||||
bool bRunning = true; ///< Set when the process is running and not terminated yet.
|
||||
int64_t iRetVal = 0; ///< Process return value.
|
||||
std::map<uint32_t, sdv::process::IProcessLifetimeCallback*> mapAssociatedMonitors; ///< Map with associated monitors.
|
||||
std::mutex mtxProcess; ///< Mutex for process access.
|
||||
std::condition_variable cvWaitForProcess; ///< Condition variable to wait for process termination.
|
||||
};
|
||||
mutable std::mutex m_mtxProcesses; ///< Access control for monitor map.
|
||||
std::map<sdv::process::TProcessID, std::shared_ptr<SProcessHelper>> m_mapProcesses; ///< Monitor map
|
||||
uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie
|
||||
std::map<uint32_t, std::shared_ptr<SProcessHelper>> m_mapMonitors; ///< Map with monitors.
|
||||
bool m_bShutdown = false; ///< Set to shutdown the monitor thread.
|
||||
std::thread m_threadMonitor; ///< Monitor thread.
|
||||
std::atomic_bool bRunning = true; ///< Set when the process is running and not terminated yet.
|
||||
int64_t iRetVal = 0; ///< Process return value.
|
||||
std::map<uint32_t, sdv::process::IProcessLifetimeCallback*> mapAssociatedMonitors; ///< Map with associated monitors.
|
||||
std::mutex mtxProcess; ///< Mutex for process access.
|
||||
std::condition_variable cvWaitForProcess; ///< Condition variable to wait for process termination.
|
||||
};
|
||||
mutable std::mutex m_mtxProcesses; ///< Access control for monitor map.
|
||||
std::map<sdv::process::TProcessID, std::shared_ptr<SProcessHelper>> m_mapProcesses; ///< Monitor map
|
||||
uint32_t m_uiNextMonCookie = 1; ///< Next monitor cookie
|
||||
std::map<uint32_t, std::shared_ptr<SProcessHelper>> m_mapMonitors; ///< Map with monitors.
|
||||
std::atomic_bool m_bShutdown = false; ///< Set to shutdown the monitor thread.
|
||||
std::thread m_threadMonitor; ///< Monitor thread.
|
||||
};
|
||||
DEFINE_SDV_OBJECT(CProcessControl)
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ add_subdirectory(unit_tests/smart_ifc)
|
||||
add_subdirectory(unit_tests/toml_parser)
|
||||
add_subdirectory(unit_tests/module_control)
|
||||
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/core_loader)
|
||||
add_subdirectory(unit_tests/named_mutex)
|
||||
|
||||
@@ -11,7 +11,7 @@ or
|
||||
|
||||
# SDV TEST MACRO
|
||||
## 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
|
||||
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.
|
||||
|
||||
## 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
|
||||
#include <support/sdv_test_macro.h>
|
||||
#include "tests/include/sdv_test_macro.h"
|
||||
|
||||
TEST(SDVTestMacros, TestExpectEq)
|
||||
{
|
||||
int val1 = 5;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_EXPECT_EQ(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_EQ(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectStreq)
|
||||
@@ -82,9 +82,9 @@ TEST(SDVTestMacros, TestExpectStreq)
|
||||
std::string str1 = "test";
|
||||
std::string str2 = "test";
|
||||
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
|
||||
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
|
||||
|
||||
@@ -163,11 +163,11 @@ ViewFilter = "Fatal"
|
||||
|
||||
auto table1 = config.GetDirect("newTableArray[0]");
|
||||
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]");
|
||||
EXPECT_EQ(table2.GetType(), sdv::toml::ENodeType::node_table);
|
||||
EXPECT_TRUE(table2.GetName().empty());
|
||||
EXPECT_EQ(table2.GetName(), "newTableArray");
|
||||
|
||||
config.Clear();
|
||||
appcontrol.Shutdown();
|
||||
@@ -770,6 +770,11 @@ ViewFilter = "Fatal"
|
||||
[j."ʞ".'l']
|
||||
)"));
|
||||
|
||||
EXPECT_FALSE(sdv::toml::CTOMLParser(u8R"(
|
||||
[ j . "ʞ" . 'l' ]
|
||||
["j".'ʞ'."l"]
|
||||
)"));
|
||||
|
||||
EXPECT_FALSE(sdv::toml::CTOMLParser(R"(
|
||||
[fruit]
|
||||
apple = "red"
|
||||
|
||||
@@ -929,13 +929,13 @@ TEST(DataDispatchServiceTest, DirectRxTxSignalConcurrency)
|
||||
while (!bShutdown)
|
||||
{
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <cstring>
|
||||
#include "../../../global/process_watchdog.h"
|
||||
|
||||
#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[])
|
||||
#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);
|
||||
return RUN_ALL_TESTS();
|
||||
// When repeat is enabled, do not enable the watchdog.
|
||||
if (bRepeatEnabled)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
else
|
||||
{
|
||||
CProcessWatchdog watchdog;
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1446,7 +1446,7 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency)
|
||||
|
||||
std::atomic_uint64_t uiValueCnt = 1000;
|
||||
|
||||
bool bShutdown = false;
|
||||
bool bShutdownPublisher = false, bShutdownConsumer = false;
|
||||
std::srand(static_cast<unsigned>(std::time(0)));
|
||||
|
||||
// Thread sync
|
||||
@@ -1471,18 +1471,18 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency)
|
||||
uiInitCnt++;
|
||||
std::shared_lock<std::shared_mutex> lock(mtxStart);
|
||||
|
||||
while (!bShutdown)
|
||||
while (!bShutdownPublisher)
|
||||
{
|
||||
sdv::core::CTransaction transaction = dispatch.CreateTransaction();
|
||||
uint64_t uiValue = std::rand();
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 10));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 10));
|
||||
transaction.Finish();
|
||||
}
|
||||
}
|
||||
@@ -1513,7 +1513,7 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency)
|
||||
uiInitCnt++;
|
||||
std::shared_lock<std::shared_mutex> lock(mtxStart);
|
||||
|
||||
while (!bShutdown)
|
||||
while (!bShutdownConsumer)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
@@ -1616,9 +1616,11 @@ TEST(DataDispatchServiceTest, TransactionalRxTxSignalConcurrency)
|
||||
|
||||
// Wait for all threads to finalize
|
||||
appcontrol.SetConfigMode();
|
||||
bShutdown = true;
|
||||
bShutdownPublisher = true;
|
||||
for (std::thread& rThread : rgPublishThreads)
|
||||
if (rThread.joinable()) rThread.join();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
bShutdownConsumer = true;
|
||||
for (std::thread& rThread : rgConsumeThreads)
|
||||
if (rThread.joinable()) rThread.join();
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <interfaces/dispatch.h>
|
||||
#include <support/app_control.h>
|
||||
#include <support/timer.h>
|
||||
#include <support/sdv_test_macro.h>
|
||||
#include "../../include/sdv_test_macro.h"
|
||||
#include "../../../global/process_watchdog.h"
|
||||
#include "../global/ascformat/ascreader.cpp"
|
||||
#include "../global/ascformat/ascwriter.cpp"
|
||||
@@ -2713,38 +2713,38 @@ TEST(DbcUtilCanDLTest, CyclicTransmit)
|
||||
if (iCnt < 4)
|
||||
{
|
||||
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
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
SDV_TIMING_EXPECT_LE(vecStat[n], 6, sdv::TEST::WarningLevel::WARNING_REDUCED);
|
||||
SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_REDUCED);
|
||||
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;
|
||||
if (dPeriod > 0.051) // Max 51ms
|
||||
{
|
||||
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
|
||||
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 (dPeriod < 0.019) // Min 19ms
|
||||
{
|
||||
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
|
||||
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED);
|
||||
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2752,18 +2752,18 @@ TEST(DbcUtilCanDLTest, CyclicTransmit)
|
||||
if (dPeriod < 0.039) // Min 39ms
|
||||
{
|
||||
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
|
||||
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(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
|
||||
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(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
|
||||
SDV_TIMING_EXPECT_EQ(iCnt, 0, sdv::TEST::WarningLevel::WARNING_ENABLED);
|
||||
SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
bInit = true;
|
||||
@@ -2874,9 +2874,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit)
|
||||
if (iCnt < 4)
|
||||
{
|
||||
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
|
||||
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++)
|
||||
@@ -2885,40 +2885,40 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit)
|
||||
{
|
||||
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)
|
||||
SDV_TIMING_EXPECT_EQ(vecStat[n], 1, sdv::TEST::WarningLevel::WARNING_REDUCED);
|
||||
SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_REDUCED);
|
||||
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
|
||||
{
|
||||
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)
|
||||
SDV_TIMING_EXPECT_LE(vecStat[n], 6, sdv::TEST::WarningLevel::WARNING_REDUCED);
|
||||
SDV_EXPECT_LE_WARN(vecStat[n], 6u, sdv_test::WARNING_REDUCED);
|
||||
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;
|
||||
if (dPeriod > 0.051) // Max 51ms
|
||||
{
|
||||
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
|
||||
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 (dPeriod < 0.019) // Min 19ms
|
||||
{
|
||||
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
|
||||
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED);
|
||||
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2926,9 +2926,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveTransmit)
|
||||
if (dPeriod < 0.039) // Min 39ms
|
||||
{
|
||||
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
|
||||
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(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
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
SDV_TIMING_EXPECT_EQ(vecStat[n], 2, sdv::TEST::WarningLevel::WARNING_REDUCED);
|
||||
SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_REDUCED);
|
||||
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;
|
||||
if (dPeriod > 0.051) // Max 51ms
|
||||
{
|
||||
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
|
||||
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 (dPeriod < 0.019) // Min 19ms
|
||||
{
|
||||
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
|
||||
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED);
|
||||
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -3067,9 +3067,9 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousTransmit)
|
||||
if (dPeriod < 0.039) // Min 39ms
|
||||
{
|
||||
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
|
||||
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(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
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
SDV_TIMING_EXPECT_LE(vecStat[n], 4, sdv::TEST::WarningLevel::WARNING_REDUCED);
|
||||
SDV_EXPECT_LE_WARN(vecStat[n], 4u, sdv_test::WARNING_REDUCED);
|
||||
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)
|
||||
SDV_TIMING_EXPECT_GE(vecStat[n], 3, sdv::TEST::WarningLevel::WARNING_REDUCED);
|
||||
SDV_EXPECT_GE_WARN(vecStat[n], 3u, sdv_test::WARNING_REDUCED);
|
||||
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;
|
||||
if (dPeriod > 0.061) // Max 61ms
|
||||
{
|
||||
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
|
||||
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(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
|
||||
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(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
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
SDV_TIMING_EXPECT_EQ(vecStat[n], 2, sdv::TEST::WarningLevel::WARNING_REDUCED);
|
||||
SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_REDUCED);
|
||||
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;
|
||||
if (dPeriod > 0.051) // Max 51ms
|
||||
{
|
||||
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
|
||||
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 (dPeriod < 0.019) // Min 19ms
|
||||
{
|
||||
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
|
||||
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED);
|
||||
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -3349,9 +3349,9 @@ TEST(DbcUtilCanDLTest, CyclicAndSpontaneousDelayTransmit)
|
||||
if (dPeriod < 0.049) // Min 49ms
|
||||
{
|
||||
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
|
||||
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(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
|
||||
SDV_TIMING_EXPECT_EQ(iCnt, 0, sdv::TEST::WarningLevel::WARNING_ENABLED);
|
||||
SDV_EXPECT_EQ_WARN(iCnt, 0, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
bInit = true;
|
||||
@@ -3463,9 +3463,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit)
|
||||
if (iCnt < 4)
|
||||
{
|
||||
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
|
||||
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++)
|
||||
{
|
||||
@@ -3473,40 +3473,40 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit)
|
||||
{
|
||||
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)
|
||||
SDV_TIMING_EXPECT_EQ(vecStat[n], 1, sdv::TEST::WarningLevel::WARNING_REDUCED);
|
||||
SDV_EXPECT_EQ_WARN(vecStat[n], 1u, sdv_test::WARNING_REDUCED);
|
||||
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
|
||||
{
|
||||
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)
|
||||
SDV_TIMING_EXPECT_EQ(vecStat[n], 2, sdv::TEST::WarningLevel::WARNING_REDUCED);
|
||||
SDV_EXPECT_EQ_WARN(vecStat[n], 2u, sdv_test::WARNING_REDUCED);
|
||||
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;
|
||||
if (dPeriod > 0.051) // Max 51ms
|
||||
{
|
||||
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
|
||||
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 (dPeriod < 0.019) // Min 19ms
|
||||
{
|
||||
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
|
||||
SDV_TIMING_EXPECT_GE(dPeriod, 0.019, sdv::TEST::WarningLevel::WARNING_ENABLED);
|
||||
SDV_EXPECT_GE_WARN(dPeriod, 0.019, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -3514,9 +3514,9 @@ TEST(DbcUtilCanDLTest, CyclicIfActiveAndSpontaneousTransmit)
|
||||
if (dPeriod < 0.039) // Min 39ms
|
||||
{
|
||||
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
|
||||
SDV_TIMING_EXPECT_GE(dPeriod, 0.039, sdv::TEST::WarningLevel::WARNING_ENABLED);
|
||||
SDV_EXPECT_GE_WARN(dPeriod, 0.039, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
273
tests/include/sdv_test_macro.h
Normal file
273
tests/include/sdv_test_macro.h
Normal 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
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <thread>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
#ifdef _WIN32
|
||||
// Prevent reassignment of "interface"
|
||||
#pragma push_macro("interface")
|
||||
@@ -96,8 +97,8 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate.
|
||||
std::thread m_threadWatchdog; ///< The watchdog thread.
|
||||
std::atomic_bool m_bTerminateWatchdog = false; ///< When set, allows the thread to terminate.
|
||||
std::thread m_threadWatchdog; ///< The watchdog thread.
|
||||
};
|
||||
|
||||
#endif // !defined TEST_WATCHDOG_H
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <gtest/gtest.h>
|
||||
#include <interfaces/can.h>
|
||||
@@ -121,7 +122,7 @@ public:
|
||||
bool m_messageSent = false;
|
||||
|
||||
private:
|
||||
bool m_StopThread = false;
|
||||
std::atomic_bool m_StopThread = false;
|
||||
std::thread m_thSend2DatalinkThread;
|
||||
mutable std::mutex m_mtxReceivers;
|
||||
std::set<sdv::can::IReceive*> m_setReceivers;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "../../../global/exec_dir_helper.h"
|
||||
#include <support/sdv_test_macro.h>
|
||||
#include "../../include/sdv_test_macro.h"
|
||||
|
||||
TEST(CAscWriterTest, AddSamplesDirect)
|
||||
{
|
||||
@@ -108,16 +108,16 @@ TEST(CAscWriterTest, AddTimedSamples)
|
||||
if (dDeltaTSGenerated < dDeltaTSGroundThruth - 0.002)
|
||||
{
|
||||
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
|
||||
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(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
|
||||
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;
|
||||
EXPECT_EQ(prSampleGroundTruth.first.uiChannel, prSampleGenerated.first.uiChannel);
|
||||
@@ -136,7 +136,7 @@ TEST(CAscWriterTest, AddTimedSamples)
|
||||
TEST(CAscWriterTest, ExtendedID)
|
||||
{
|
||||
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
|
||||
asc::CAscWriter writer;
|
||||
|
||||
@@ -80,10 +80,10 @@ TEST_F(CSerdesTest, DeserializeSimpleEndianSwap)
|
||||
|
||||
TEST_F(CSerdesTest, SerializeArray)
|
||||
{
|
||||
srand((unsigned int)time(0));
|
||||
std::srand((unsigned int)time(0));
|
||||
int16_t rguiBuffer[4096];
|
||||
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;
|
||||
serializer << rguiBuffer;
|
||||
@@ -95,10 +95,10 @@ TEST_F(CSerdesTest, SerializeArray)
|
||||
|
||||
TEST_F(CSerdesTest, DeserializeArray)
|
||||
{
|
||||
srand((unsigned int)time(0));
|
||||
std::srand((unsigned int)time(0));
|
||||
int16_t rguiBuffer[4096];
|
||||
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;
|
||||
serializer << rguiBuffer;
|
||||
@@ -113,10 +113,10 @@ TEST_F(CSerdesTest, DeserializeArray)
|
||||
|
||||
TEST_F(CSerdesTest, SerializeArrayEndianSwap)
|
||||
{
|
||||
srand((unsigned int)time(0));
|
||||
std::srand((unsigned int)time(0));
|
||||
int16_t rguiBuffer[4096];
|
||||
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 =
|
||||
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)
|
||||
{
|
||||
srand((unsigned int)time(0));
|
||||
std::srand((unsigned int)time(0));
|
||||
int16_t rguiBuffer[4096];
|
||||
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 =
|
||||
sdv::GetPlatformEndianess() == sdv::EEndian::little_endian ? sdv::EEndian::big_endian : sdv::EEndian::little_endian;
|
||||
|
||||
@@ -5,33 +5,33 @@
|
||||
#include <support/interface_ptr.h>
|
||||
|
||||
/**
|
||||
* \brief Test class for instantiation tests.
|
||||
* @brief Test class for instantiation tests.
|
||||
*/
|
||||
class CCommandLineParserTest : public testing::Test
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
* @brief Constructor
|
||||
*/
|
||||
CCommandLineParserTest() = default;
|
||||
|
||||
/**
|
||||
* \brief Set up the test suite.
|
||||
* @brief Set up the test suite.
|
||||
*/
|
||||
static void SetUpTestCase();
|
||||
|
||||
/**
|
||||
* \brief Tear down the test suite.
|
||||
* @brief Tear down the test suite.
|
||||
*/
|
||||
static void TearDownTestCase();
|
||||
|
||||
/**
|
||||
* \brief Test setup.
|
||||
* @brief Test setup.
|
||||
*/
|
||||
void SetUp() override;
|
||||
|
||||
/**
|
||||
* \brief Test teardown.
|
||||
* @brief Test teardown.
|
||||
*/
|
||||
void TearDown() override;
|
||||
};
|
||||
|
||||
@@ -98,7 +98,7 @@ struct SMakeNoice
|
||||
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::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 nThreadCnt = 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;
|
||||
std::thread thread1([&]()
|
||||
{
|
||||
|
||||
@@ -5,33 +5,33 @@
|
||||
#include <support/interface_ptr.h>
|
||||
|
||||
/**
|
||||
* \brief Test class for instantiation tests.
|
||||
* @brief Test class for instantiation tests.
|
||||
*/
|
||||
class CDbcParserTest : public testing::Test
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
* @brief Constructor
|
||||
*/
|
||||
CDbcParserTest() = default;
|
||||
|
||||
/**
|
||||
* \brief Set up the test suite.
|
||||
* @brief Set up the test suite.
|
||||
*/
|
||||
static void SetUpTestCase();
|
||||
|
||||
/**
|
||||
* \brief Tear down the test suite.
|
||||
* @brief Tear down the test suite.
|
||||
*/
|
||||
static void TearDownTestCase();
|
||||
|
||||
/**
|
||||
* \brief Test setup.
|
||||
* @brief Test setup.
|
||||
*/
|
||||
void SetUp() override;
|
||||
|
||||
/**
|
||||
* \brief Test teardown.
|
||||
* @brief Test teardown.
|
||||
*/
|
||||
void TearDown() override;
|
||||
};
|
||||
|
||||
@@ -2,33 +2,33 @@
|
||||
#define LEXER_TEST_H
|
||||
|
||||
/**
|
||||
* \brief Test class for instantiation tests.
|
||||
* @brief Test class for instantiation tests.
|
||||
*/
|
||||
class CLexerTest : public testing::Test
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
* @brief Constructor
|
||||
*/
|
||||
CLexerTest() = default;
|
||||
|
||||
/**
|
||||
* \brief Set up the test suite.
|
||||
* @brief Set up the test suite.
|
||||
*/
|
||||
static void SetUpTestCase();
|
||||
|
||||
/**
|
||||
* \brief Tear down the test suite.
|
||||
* @brief Tear down the test suite.
|
||||
*/
|
||||
static void TearDownTestCase();
|
||||
|
||||
/**
|
||||
* \brief Test setup.
|
||||
* @brief Test setup.
|
||||
*/
|
||||
void SetUp() override;
|
||||
|
||||
/**
|
||||
* \brief Test teardown.
|
||||
* @brief Test teardown.
|
||||
*/
|
||||
void TearDown() override;
|
||||
|
||||
|
||||
@@ -2,33 +2,33 @@
|
||||
#define PARSER_TEST_H
|
||||
|
||||
/**
|
||||
* \brief Test class for instantiation tests.
|
||||
* @brief Test class for instantiation tests.
|
||||
*/
|
||||
class CParserTest : public testing::Test
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
* @brief Constructor
|
||||
*/
|
||||
CParserTest() = default;
|
||||
|
||||
/**
|
||||
* \brief Set up the test suite.
|
||||
* @brief Set up the test suite.
|
||||
*/
|
||||
static void SetUpTestCase();
|
||||
|
||||
/**
|
||||
* \brief Tear down the test suite.
|
||||
* @brief Tear down the test suite.
|
||||
*/
|
||||
static void TearDownTestCase();
|
||||
|
||||
/**
|
||||
* \brief Test setup.
|
||||
* @brief Test setup.
|
||||
*/
|
||||
void SetUp() override;
|
||||
|
||||
/**
|
||||
* \brief Test teardown.
|
||||
* @brief Test teardown.
|
||||
*/
|
||||
void TearDown() override;
|
||||
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
#include "../../../sdv_services/core/installation_composer.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_token.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/miscellaneous.cpp"
|
||||
#include <support/app_control.h>
|
||||
|
||||
#if defined(_WIN32) && defined(_UNICODE)
|
||||
|
||||
@@ -2108,7 +2108,7 @@ TEST_F(CInstallPackageComposerTest, DetectPackageCorruptionContent)
|
||||
for (size_t n = 0; n < 25; n++)
|
||||
{
|
||||
// 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];
|
||||
ptrPackage.get()[nPos] = ~uiStoredByte;
|
||||
|
||||
@@ -2337,7 +2337,7 @@ TEST_F(CInstallPackageComposerTest, VerifyIntegrityCorruptionContent)
|
||||
for (size_t n = 0; n < 25; n++)
|
||||
{
|
||||
// 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];
|
||||
ptrPackage.get()[nPos] = ~uiStoredByte;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../../../sdv_services/ipc_com/com_ctrl.cpp"
|
||||
#include "../../../sdv_services/ipc_com/com_channel.cpp"
|
||||
#include "../../../sdv_services/ipc_com/marshall_object.cpp"
|
||||
#include <cstring>
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
@@ -13,8 +14,29 @@ extern "C" int wmain(int argc, wchar_t* argv[])
|
||||
extern "C" int main(int argc, char* argv[])
|
||||
#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);
|
||||
return RUN_ALL_TESTS();
|
||||
// When repeat is enabled, do not enable the watchdog.
|
||||
if (bRepeatEnabled)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
else
|
||||
{
|
||||
CProcessWatchdog watchdog;
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <atomic>
|
||||
|
||||
TEST(TraceFifoTest, Connect_Disconnect)
|
||||
{
|
||||
@@ -190,7 +191,7 @@ TEST(TraceFifoTest, Simple_Publish_Monitor)
|
||||
EXPECT_TRUE(fifoReader.IsOpened());
|
||||
|
||||
std::vector<std::string> vecSent, vecReceived;
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
|
||||
// Start receiving thread until shutdown flag is set.
|
||||
std::thread thread([&]()
|
||||
@@ -238,7 +239,7 @@ TEST(TraceFifoTest, Simple_Publish_Monitor_Multi)
|
||||
EXPECT_TRUE(fifoReader2.IsOpened());
|
||||
|
||||
std::vector<std::string> vecSent, vecReceived1, vecReceived2;
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
|
||||
// Start receiving thread until shutdown flag is set.
|
||||
std::thread thread1([&]()
|
||||
@@ -297,7 +298,7 @@ TEST(TraceFifoTest, Simple_Publish_Beyond_Buffer_With_Reading)
|
||||
EXPECT_TRUE(fifoReader.IsOpened());
|
||||
|
||||
std::vector<std::string> vecSent, vecReceived;
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
|
||||
// Start receiving thread until shutdown flag is set.
|
||||
std::thread thread([&]()
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
#include "mock.h"
|
||||
#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_token.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/miscellaneous.cpp"
|
||||
#include "../../../sdv_services/core/module_control.cpp"
|
||||
#include "../../../sdv_services/core/module.cpp"
|
||||
#include "../../../sdv_services/core/app_config.cpp"
|
||||
|
||||
@@ -11,13 +11,7 @@
|
||||
class CMock
|
||||
{
|
||||
public:
|
||||
CMock() : m_root("root")
|
||||
{
|
||||
auto ptrElement = std::make_shared<CNormalTable>("Install");
|
||||
m_root.AddElement(ptrElement);
|
||||
auto ptrValue = std::make_shared<CStringNode>("Directory", "install/test");
|
||||
ptrElement->AddElement(ptrValue);
|
||||
}
|
||||
CMock() {}
|
||||
void DestroyModuleObjects(sdv::core::TModuleID) {}
|
||||
bool IsStandaloneApplication() { return true; }
|
||||
bool IsEssentialApplication() { return false; }
|
||||
@@ -40,9 +34,8 @@ public:
|
||||
sdv::core::TModuleID ContextLoad(const std::filesystem::path&, const sdv::u8string&) { return 0; }
|
||||
bool ContextUnload(sdv::core::TModuleID, bool) { return false; }
|
||||
|
||||
CNormalTable m_root;
|
||||
bool m_bIsMain = false;
|
||||
bool m_bIsIsolated = false;
|
||||
bool m_bIsMain = false;
|
||||
bool m_bIsIsolated = false;
|
||||
};
|
||||
|
||||
inline CMock& GetMock()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../../../global/ipc_named_mutex.h"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#if defined(_WIN32) && defined(_UNICODE)
|
||||
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,
|
||||
// sync will be done and the check will succeed.
|
||||
int32_t iCnt = 0;
|
||||
bool bSuccess = true;
|
||||
bool bEnable = false;
|
||||
std::atomic_bool bSuccess = true;
|
||||
std::atomic_bool bEnable = false;
|
||||
auto fn = [&]()
|
||||
{
|
||||
ipc::named_mutex mtx("HELLO");
|
||||
|
||||
if (bEnable)
|
||||
mtx.lock();
|
||||
bSuccess &= (iCnt == 0);
|
||||
bSuccess = bSuccess && (iCnt == 0);
|
||||
iCnt++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 100));
|
||||
iCnt--;
|
||||
bSuccess &= (iCnt == 0);
|
||||
bSuccess = bSuccess && (iCnt == 0);
|
||||
if (bEnable)
|
||||
mtx.unlock();
|
||||
};
|
||||
@@ -69,17 +70,17 @@ TEST(NamedMutexTest, CritSectSyncAutoLock)
|
||||
{
|
||||
// Counter function check for correct counter value.
|
||||
int32_t iCnt = 0;
|
||||
bool bSuccess = true;
|
||||
std::atomic_bool bSuccess = true;
|
||||
auto fn = [&]()
|
||||
{
|
||||
ipc::named_mutex mtx("HELLO");
|
||||
|
||||
std::unique_lock<ipc::named_mutex> lock(mtx);
|
||||
bSuccess &= (iCnt == 0);
|
||||
bSuccess = bSuccess && (iCnt == 0);
|
||||
iCnt++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(std::rand() % 100));
|
||||
iCnt--;
|
||||
bSuccess &= (iCnt == 0);
|
||||
bSuccess = bSuccess && (iCnt == 0);
|
||||
};
|
||||
|
||||
// Test sync
|
||||
@@ -94,7 +95,7 @@ TEST(NamedMutexTest, CritSectSyncAutoLock)
|
||||
|
||||
TEST(NamedMutexTest, TryLock)
|
||||
{
|
||||
bool bRunning = false;
|
||||
std::atomic_bool bRunning = false;
|
||||
auto fn = [&]()
|
||||
{
|
||||
ipc::named_mutex mtx("HELLO");
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
#include "mock.h"
|
||||
#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_token.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/miscellaneous.cpp"
|
||||
#include "../../../sdv_services/core/module_control.cpp"
|
||||
#include "../../../sdv_services/core/module.cpp"
|
||||
#include "../../../sdv_services/core/repository.cpp"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "../../../global/process_watchdog.h"
|
||||
#include "support/sdv_test_macro.h"
|
||||
#include "../../include/sdv_test_macro.h"
|
||||
|
||||
#if defined(_WIN32) && defined(_UNICODE)
|
||||
extern "C" int wmain(int argc, wchar_t* argv[])
|
||||
@@ -19,9 +19,9 @@ TEST(SDVTestMacros, TestExpectEq)
|
||||
int val1 = 5;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_EXPECT_EQ(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_EQ(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertEq)
|
||||
@@ -29,9 +29,9 @@ TEST(SDVTestMacros, TestAssertEq)
|
||||
int val1 = 5;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_ASSERT_EQ(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_EQ(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectNe)
|
||||
@@ -39,9 +39,9 @@ TEST(SDVTestMacros, TestExpectNe)
|
||||
int val1 = 5;
|
||||
int val2 = 6;
|
||||
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
|
||||
SDV_EXPECT_NE(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_NE(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertNe)
|
||||
@@ -49,45 +49,45 @@ TEST(SDVTestMacros, TestAssertNe)
|
||||
int val1 = 5;
|
||||
int val2 = 6;
|
||||
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
|
||||
SDV_ASSERT_NE(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_NE(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectTrue)
|
||||
{
|
||||
bool condition = true;
|
||||
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
|
||||
SDV_EXPECT_TRUE(condition, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_TRUE(condition, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertTrue)
|
||||
{
|
||||
bool condition = true;
|
||||
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
|
||||
SDV_ASSERT_TRUE(condition, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_TRUE(condition, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectFalse)
|
||||
{
|
||||
bool condition = false;
|
||||
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
|
||||
SDV_EXPECT_FALSE(condition, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_FALSE(condition, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertFalse)
|
||||
{
|
||||
bool condition = false;
|
||||
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
|
||||
SDV_ASSERT_FALSE(condition, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_FALSE(condition, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectLt)
|
||||
@@ -95,9 +95,9 @@ TEST(SDVTestMacros, TestExpectLt)
|
||||
int val1 = 5;
|
||||
int val2 = 6;
|
||||
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
|
||||
SDV_EXPECT_LT(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_LT(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertLt)
|
||||
@@ -105,9 +105,9 @@ TEST(SDVTestMacros, TestAssertLt)
|
||||
int val1 = 5;
|
||||
int val2 = 6;
|
||||
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
|
||||
SDV_ASSERT_LT(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_LT(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectLe)
|
||||
@@ -115,9 +115,9 @@ TEST(SDVTestMacros, TestExpectLe)
|
||||
int val1 = 5;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_EXPECT_LE(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_LE(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertLe)
|
||||
@@ -125,9 +125,9 @@ TEST(SDVTestMacros, TestAssertLe)
|
||||
int val1 = 5;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_ASSERT_LE(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_LE(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectGt)
|
||||
@@ -135,9 +135,9 @@ TEST(SDVTestMacros, TestExpectGt)
|
||||
int val1 = 6;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_EXPECT_GT(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_GT(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertGt)
|
||||
@@ -145,9 +145,9 @@ TEST(SDVTestMacros, TestAssertGt)
|
||||
int val1 = 6;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_ASSERT_GT(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_GT(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectGe)
|
||||
@@ -155,9 +155,9 @@ TEST(SDVTestMacros, TestExpectGe)
|
||||
int val1 = 6;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_EXPECT_GE(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_GE(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertGe)
|
||||
@@ -165,9 +165,9 @@ TEST(SDVTestMacros, TestAssertGe)
|
||||
int val1 = 6;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_ASSERT_GE(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_GE(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectStreq)
|
||||
@@ -175,9 +175,9 @@ TEST(SDVTestMacros, TestExpectStreq)
|
||||
std::string str1 = "test";
|
||||
std::string str2 = "test";
|
||||
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
|
||||
SDV_EXPECT_STREQ(str1, str2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_STREQ(str1, str2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertStreq)
|
||||
@@ -185,9 +185,9 @@ TEST(SDVTestMacros, TestAssertStreq)
|
||||
std::string str1 = "test";
|
||||
std::string str2 = "test";
|
||||
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
|
||||
SDV_ASSERT_STREQ(str1, str2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_STREQ(str1, str2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectStrne)
|
||||
@@ -195,9 +195,9 @@ TEST(SDVTestMacros, TestExpectStrne)
|
||||
std::string str1 = "test";
|
||||
std::string str2 = "test1";
|
||||
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
|
||||
SDV_EXPECT_STRNE(str1, str2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_STRNE(str1, str2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertStrne)
|
||||
@@ -205,9 +205,9 @@ TEST(SDVTestMacros, TestAssertStrne)
|
||||
std::string str1 = "test";
|
||||
std::string str2 = "test1";
|
||||
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
|
||||
SDV_ASSERT_STRNE(str1, str2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_STRNE(str1, str2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectStrcaseeq)
|
||||
@@ -215,9 +215,9 @@ TEST(SDVTestMacros, TestExpectStrcaseeq)
|
||||
std::string str1 = "test";
|
||||
std::string str2 = "TEST";
|
||||
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
|
||||
SDV_EXPECT_STRCASEEQ(str1, str2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_STRCASEEQ(str1, str2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertStrcaseeq)
|
||||
@@ -225,9 +225,9 @@ TEST(SDVTestMacros, TestAssertStrcaseeq)
|
||||
std::string str1 = "test";
|
||||
std::string str2 = "TEST";
|
||||
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
|
||||
SDV_ASSERT_STRCASEEQ(str1, str2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_STRCASEEQ(str1, str2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestExpectStrcasene)
|
||||
@@ -235,9 +235,9 @@ TEST(SDVTestMacros, TestExpectStrcasene)
|
||||
std::string str1 = "test";
|
||||
std::string str2 = "TEST1";
|
||||
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
|
||||
SDV_EXPECT_STRCASENE(str1, str2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_STRCASENE(str1, str2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestAssertStrcasene)
|
||||
@@ -245,9 +245,9 @@ TEST(SDVTestMacros, TestAssertStrcasene)
|
||||
std::string str1 = "test";
|
||||
std::string str2 = "TEST1";
|
||||
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
|
||||
SDV_ASSERT_STRCASENE(str1, str2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_ASSERT_STRCASENE(str1, str2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestTimingExpectEq)
|
||||
@@ -255,9 +255,9 @@ TEST(SDVTestMacros, TestTimingExpectEq)
|
||||
int val1 = 5;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_TIMING_EXPECT_EQ(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_EQ_WARN(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestTimingExpectNe)
|
||||
@@ -265,9 +265,9 @@ TEST(SDVTestMacros, TestTimingExpectNe)
|
||||
int val1 = 5;
|
||||
int val2 = 6;
|
||||
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
|
||||
SDV_TIMING_EXPECT_NE(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_NE_WARN(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestTimingExpectGt)
|
||||
@@ -275,9 +275,9 @@ TEST(SDVTestMacros, TestTimingExpectGt)
|
||||
int val1 = 6;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_TIMING_EXPECT_GT(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_GT_WARN(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestTimingExpectLt)
|
||||
@@ -285,9 +285,9 @@ TEST(SDVTestMacros, TestTimingExpectLt)
|
||||
int val1 = 5;
|
||||
int val2 = 6;
|
||||
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
|
||||
SDV_TIMING_EXPECT_LT(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_LT_WARN(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestTimingExpectGe)
|
||||
@@ -295,9 +295,9 @@ TEST(SDVTestMacros, TestTimingExpectGe)
|
||||
int val1 = 6;
|
||||
int val2 = 5;
|
||||
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
|
||||
SDV_TIMING_EXPECT_GE(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_GE_WARN(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
TEST(SDVTestMacros, TestTimingExpectLe)
|
||||
@@ -305,7 +305,7 @@ TEST(SDVTestMacros, TestTimingExpectLe)
|
||||
int val1 = 5;
|
||||
int val2 = 6;
|
||||
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
|
||||
SDV_TIMING_EXPECT_LE(val1, val2, sdv::TEST::WARNING_ENABLED);
|
||||
SDV_EXPECT_LE_WARN(val1, val2, sdv_test::WARNING_ENABLED);
|
||||
}
|
||||
|
||||
@@ -53,16 +53,11 @@ endif()
|
||||
# Add the communication unittest
|
||||
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
|
||||
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
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# Shared mem buffer test
|
||||
add_executable(UnitTest_SharedMemBufferTests
|
||||
@@ -85,16 +80,11 @@ endif()
|
||||
# Add the communication unittest
|
||||
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
|
||||
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
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# Shared mem connection test
|
||||
add_executable(UnitTest_SharedMemConnectTests
|
||||
@@ -115,16 +105,11 @@ endif()
|
||||
# Add the communication unittest
|
||||
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
|
||||
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
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
# Shared mem large data test
|
||||
@@ -146,16 +131,11 @@ endif()
|
||||
# Add the communication unittest
|
||||
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
|
||||
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
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
# Build dependencies
|
||||
add_dependencies(UnitTest_SharedMemTests_App_Repeater core_services)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#define TIME_TRACKING
|
||||
#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/mem_buffer_accessor.cpp"
|
||||
#include <sstream>
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <queue>
|
||||
#include <atomic>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <unistd.h>
|
||||
@@ -67,6 +68,8 @@ public:
|
||||
{
|
||||
// Send the same data back again (if needed).
|
||||
std::unique_lock<std::mutex> lock(m_mtxData);
|
||||
m_nReceiveCallCnt++;
|
||||
m_nPackageReceiveCnt += seqData.size();
|
||||
m_queueSendData.push(seqData);
|
||||
lock.unlock();
|
||||
|
||||
@@ -93,7 +96,10 @@ public:
|
||||
|
||||
// Send the data back to the sender
|
||||
if (m_pSend)
|
||||
{
|
||||
m_pSend->SendData(seqData);
|
||||
m_nSendCallCnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,6 +190,33 @@ public:
|
||||
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:
|
||||
sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function.
|
||||
mutable std::mutex m_mtxData; ///< Protect data access.
|
||||
@@ -191,9 +224,12 @@ private:
|
||||
std::condition_variable m_cvDisconnect; ///< Disconnect event.
|
||||
std::condition_variable m_cvReceived; ///< Receive event.
|
||||
std::thread m_threadSender; ///< Thread to send data.
|
||||
bool m_bConnected = false; ///< Set when connected was triggered.
|
||||
bool m_bDisconnect = false; ///< Set when shutdown was triggered.
|
||||
bool m_bShutdown = false; ///< Set when shutdown is processed.
|
||||
std::atomic_bool m_bConnected = false; ///< Set when connected was triggered.
|
||||
std::atomic_bool m_bDisconnect = false; ///< Set when shutdown was triggered.
|
||||
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
|
||||
sdv::TObjectPtr ptrControlConnection;
|
||||
sdv::ipc::IConnect* pControlConnect = nullptr;
|
||||
CReceiver receiverControl;
|
||||
uint64_t uiControlEventCookie = 0;
|
||||
if (!ssControlConnectString.empty())
|
||||
{
|
||||
TRACE(bServer ? "Server" : "Client", ": Start of control channel connection...");
|
||||
ptrControlConnection = mgntControlMgntChannel.Access(ssControlConnectString);
|
||||
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->RegisterStatusEventCallback(&receiverControl)) return -20;
|
||||
uiControlEventCookie = pControlConnect->RegisterStatusEventCallback(&receiverControl);
|
||||
if (!uiControlEventCookie) return -20;
|
||||
if (!pControlConnect->AsyncConnect(&receiverControl)) return -14;
|
||||
if (!pControlConnect->WaitForConnection(250)) return -5; // Note: Connection should be possible within 250ms.
|
||||
if (pControlConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -15;
|
||||
@@ -339,8 +378,10 @@ Size = 1024000
|
||||
|
||||
// Establish the connection
|
||||
sdv::ipc::IConnect* pDataConnect = ptrDataConnection.GetInterface<sdv::ipc::IConnect>();
|
||||
uint64_t uiDataEventCookie = 0;
|
||||
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->WaitForConnection(10000)) return -5; // Note: Connection should be possible within 10000ms.
|
||||
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
|
||||
// same time).
|
||||
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)
|
||||
{
|
||||
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
|
||||
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
||||
#endif
|
||||
@@ -371,9 +417,11 @@ Size = 1024000
|
||||
}
|
||||
|
||||
// Initiate shutdown
|
||||
if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStatusEventCallback(uiDataEventCookie);
|
||||
ptrDataConnection.Clear();
|
||||
mgntDataMgntChannel.Shutdown();
|
||||
if (mgntDataMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -6;
|
||||
if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStatusEventCallback(uiControlEventCookie);
|
||||
ptrControlConnection.Clear();
|
||||
mgntControlMgntChannel.Shutdown();
|
||||
if (mgntControlMgntChannel.GetStatus() != sdv::EObjectStatus::destruction_pending) return -16;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "../../../sdv_services/ipc_shared_mem/in_process_mem_buffer.h"
|
||||
#include <support/app_control.h>
|
||||
#include "pattern_gen.h"
|
||||
#include <atomic>
|
||||
|
||||
TEST(InProcessMemoryBufferTest, Instantiate)
|
||||
{
|
||||
@@ -26,7 +27,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestRx)
|
||||
CInProcMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
size_t nCorrectCnt = 0;
|
||||
std::condition_variable cvStart;
|
||||
std::mutex mtxStart;
|
||||
@@ -75,7 +76,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestTx)
|
||||
CInProcMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
size_t nCorrectCnt = 0;
|
||||
std::condition_variable cvStart;
|
||||
std::mutex mtxStart;
|
||||
@@ -124,7 +125,7 @@ TEST(InProcessMemoryBufferTest, TriggerTestRxTx)
|
||||
CInProcMemBufferRx receiver(sender.GetConnectionString());
|
||||
EXPECT_TRUE(receiver.IsValid());
|
||||
|
||||
bool bShutdown = false;
|
||||
std::atomic_bool bShutdown = false;
|
||||
size_t nCorrectCnt = 0;
|
||||
std::condition_variable cvSenderStart, cvReceiverStart;
|
||||
std::mutex mtxReceiverStart;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../../../sdv_services/ipc_shared_mem/channel_mgnt.cpp"
|
||||
#include "../../../sdv_services/ipc_shared_mem/watchdog.cpp"
|
||||
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.cpp"
|
||||
#include <cstring>
|
||||
|
||||
/**
|
||||
* @brief Load support modules to publish the needed services.
|
||||
@@ -28,8 +29,30 @@ extern "C" int wmain(int argc, wchar_t* argv[])
|
||||
extern "C" int main(int argc, char* argv[])
|
||||
#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);
|
||||
return RUN_ALL_TESTS();
|
||||
// When repeat is enabled, do not enable the watchdog.
|
||||
if (bRepeatEnabled)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
else
|
||||
{
|
||||
CProcessWatchdog watchdog;
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include "pattern_gen.h"
|
||||
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.h"
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define PATTERN_GEN_H
|
||||
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include "../../../sdv_services/ipc_shared_mem/mem_buffer_accessor.h"
|
||||
|
||||
/**
|
||||
@@ -43,8 +44,8 @@ public:
|
||||
private:
|
||||
CMemBufferAccessorTx& m_raccessorOut; //!< Reference to the output accessor
|
||||
std::thread m_thread; //!< Processing thread
|
||||
bool m_bStarted = false; //!< Set by the thread when started.
|
||||
bool m_bShutdown = false; //!< When set, shutdown the thread.
|
||||
std::atomic_bool m_bStarted = false; //!< Set by the thread when started.
|
||||
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_uiCycleCnt = 0u; //!< Amount of packets
|
||||
uint32_t m_uiPacketCnt = 0u; //!< Amount of packets
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user