pw_tokenizer: Custom tokenization macro support
- Make the pw::tokenizer::EncodeArgs function and pw_tokenizer_ArgTypes
typedef public.
- Update the pw::tokenizer::EncodedMessage class to make it simpler to
use. Also, use memcpy instead of aliasing the token.
Change-Id: Ic99930ccf3e002e4fbef5112f6b2d37c910e6ee9
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/39984
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
diff --git a/pw_log_tokenized/docs.rst b/pw_log_tokenized/docs.rst
index f64f524..3c100ed 100644
--- a/pw_log_tokenized/docs.rst
+++ b/pw_log_tokenized/docs.rst
@@ -39,13 +39,18 @@
See the documentation for :ref:`module-pw_tokenizer` for further details.
-Applications may select a different macro than
+Using a custom macro
+--------------------
+Applications may use their own macro instead of
``PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD`` by setting the
``PW_LOG_TOKENIZED_ENCODE_MESSAGE`` config macro. This macro should take
arguments equivalent to ``PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD``:
.. c:function:: PW_LOG_TOKENIZED_ENCODE_MESSAGE(pw_tokenizer_Payload log_metadata, const char* message, ...)
+For instructions on how to implement a custom tokenization macro, see
+:ref:`module-pw_tokenizer-custom-macro`.
+
Build targets
-------------
The GN build for ``pw_log_tokenized`` has two targets: ``pw_log_tokenized`` and
@@ -57,8 +62,7 @@
Python package
==============
-``pw_log_tokenized`` includes a Python package for decoding tokenized messages
-logs.
+``pw_log_tokenized`` includes a Python package for decoding tokenized logs.
pw_log_tokenized
----------------
diff --git a/pw_tokenizer/argument_types_test.cc b/pw_tokenizer/argument_types_test.cc
index 67c1d17..cf3b22b 100644
--- a/pw_tokenizer/argument_types_test.cc
+++ b/pw_tokenizer/argument_types_test.cc
@@ -187,38 +187,38 @@
TEST(ArgumentTypes, MultipleArgs) {
// clang-format off
- static_assert(PW_TOKENIZER_ARG_TYPES(1) == 1); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2) == 2); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3) == 3); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4) == 4); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5) == 5); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6) == 6); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7) == 7); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8) == 8); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9) == 9); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 10); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) == 11); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) == 12); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) == 13); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) == 14); // NOLINT
+ static_assert(PW_TOKENIZER_ARG_TYPES(1) == 1);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2) == 2);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3) == 3);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4) == 4);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5) == 5);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6) == 6);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7) == 7);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8) == 8);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9) == 9);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 10);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) == 11);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) == 12);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) == 13);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) == 14);
#if PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES >= 8
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) == 14); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) == 15); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) == 16); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17) == 17); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18) == 18); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) == 19); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) == 20); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) == 21); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22) == 22); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23) == 23); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) == 24); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25) == 25); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26) == 26); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27) == 27); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28) == 28); // NOLINT
- static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29) == 29); // NOLINT
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) == 14);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) == 15);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) == 16);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17) == 17);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18) == 18);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) == 19);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) == 20);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) == 21);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22) == 22);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23) == 23);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) == 24);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25) == 25);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26) == 26);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27) == 27);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28) == 28);
+ static_assert(PW_TOKENIZER_ARG_TYPES(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29) == 29);
#endif // PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES
// clang-format on
}
diff --git a/pw_tokenizer/argument_types_test_c.c b/pw_tokenizer/argument_types_test_c.c
index 2030587..849357d 100644
--- a/pw_tokenizer/argument_types_test_c.c
+++ b/pw_tokenizer/argument_types_test_c.c
@@ -72,10 +72,10 @@
static char char_array[16];
// Define the test functions that are called by the C++ unit test.
-#define DEFINE_TEST_FUNCTION(name, ...) \
- _pw_tokenizer_ArgTypes pw_TestTokenizer##name(void) { \
- (void)char_array; \
- return PW_TOKENIZER_ARG_TYPES(__VA_ARGS__); \
+#define DEFINE_TEST_FUNCTION(name, ...) \
+ pw_tokenizer_ArgTypes pw_TestTokenizer##name(void) { \
+ (void)char_array; \
+ return PW_TOKENIZER_ARG_TYPES(__VA_ARGS__); \
}
DEFINE_TEST_FUNCTION(NoArgs);
diff --git a/pw_tokenizer/docs.rst b/pw_tokenizer/docs.rst
index eec6773..ee9d154 100644
--- a/pw_tokenizer/docs.rst
+++ b/pw_tokenizer/docs.rst
@@ -233,8 +233,97 @@
widely expanded macros, such as a logging macro, because it will result in
larger code size than its alternatives.
-Example: binary logging
-^^^^^^^^^^^^^^^^^^^^^^^
+.. _module-pw_tokenizer-custom-macro:
+
+Tokenize with a custom macro
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Projects may need more flexbility than the standard ``pw_tokenizer`` macros
+provide. To support this, projects may define custom tokenization macros. This
+requires the use of two low-level ``pw_tokenizer`` macros:
+
+.. c:macro:: PW_TOKENIZE_FORMAT_STRING(domain, mask, format, ...)
+
+ Tokenizes a format string and sets the ``_pw_tokenizer_token`` variable to the
+ token. Must be used in its own scope, since the same variable is used in every
+ invocation.
+
+ The tokenized string uses the specified :ref:`tokenization domain
+ <module-pw_tokenizer-domains>`. Use ``PW_TOKENIZER_DEFAULT_DOMAIN`` for the
+ default. The token also may be masked; use ``UINT32_MAX`` to keep all bits.
+
+.. c:macro:: PW_TOKENIZER_ARG_TYPES(...)
+
+ Converts a series of arguments to a compact format that replaces the format
+ string literal.
+
+Use these two macros within the custom tokenization macro to call a function
+that does the encoding. The following example implements a custom tokenization
+macro for use with :ref:`module-pw_log_tokenized`.
+
+.. code-block:: cpp
+
+ #include "pw_tokenizer/tokenize.h"
+
+ #ifndef __cplusplus
+ extern "C" {
+ #endif
+
+ void EncodeTokenizedMessage(pw_tokenizer_Payload metadata,
+ pw_tokenizer_Token token,
+ pw_tokenizer_ArgTypes types,
+ ...);
+
+ #ifndef __cplusplus
+ } // extern "C"
+ #endif
+
+ #define PW_LOG_TOKENIZED_ENCODE_MESSAGE(metadata, format, ...) \
+ do { \
+ _PW_TOKENIZE_FORMAT_STRING( \
+ PW_TOKENIZER_DEFAULT_DOMAIN, UINT32_MAX, format, __VA_ARGS__); \
+ EncodeTokenizedMessage(payload, \
+ _pw_tokenizer_token, \
+ PW_TOKENIZER_ARG_TYPES(__VA_ARGS__) \
+ PW_COMMA_ARGS(__VA_ARGS__)); \
+ } while (0)
+
+In this example, the ``EncodeTokenizedMessage`` function would handle encoding
+and processing the message. Encoding is done by the
+``pw::tokenizer::EncodedMessage`` class or ``pw::tokenizer::EncodeArgs``
+function from ``pw_tokenizer/encode_args.h``. The encoded message can then be
+transmitted or stored as needed.
+
+.. code-block:: cpp
+
+ #include "pw_log_tokenized/log_tokenized.h"
+ #include "pw_tokenizer/encode_args.h"
+
+ void HandleTokenizedMessage(pw::log_tokenized::Metadata metadata,
+ std::span<std::byte> message);
+
+ extern "C" void EncodeTokenizedMessage(const pw_tokenizer_Payload metadata,
+ const pw_tokenizer_Token token,
+ const pw_tokenizer_ArgTypes types,
+ ...) {
+ va_list args;
+ va_start(args, types);
+ pw::tokenizer::EncodedMessage encoded_message(token, types, args);
+ va_end(args);
+
+ HandleTokenizedMessage(metadata, encoded_message);
+ }
+
+.. admonition:: When to use a custom macro
+
+ Use existing tokenization macros whenever possible. A custom macro may be
+ needed to support use cases like the following:
+
+ * Variations of ``PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD`` that take
+ different arguments.
+ * Supporting global handler macros that use different handler functions.
+
+Binary logging with pw_tokenizer
+--------------------------------
String tokenization is perfect for logging. Consider the following log macro,
which gathers the file, line number, and log message. It calls the ``RecordLog``
function, which formats the log string, collects a timestamp, and transmits the
@@ -363,6 +452,8 @@
calculated values will differ between C and C++ for strings longer than
``PW_TOKENIZER_CFG_C_HASH_LENGTH`` characters.
+.. _module-pw_tokenizer-domains:
+
Tokenization domains
--------------------
``pw_tokenizer`` supports having multiple tokenization domains. Domains are a
diff --git a/pw_tokenizer/encode_args.cc b/pw_tokenizer/encode_args.cc
index 041d51f..93cd6c4 100644
--- a/pw_tokenizer/encode_args.cc
+++ b/pw_tokenizer/encode_args.cc
@@ -38,15 +38,15 @@
static_assert(0b10u == static_cast<uint8_t>(ArgType::kDouble));
static_assert(0b11u == static_cast<uint8_t>(ArgType::kString));
-size_t EncodeInt(int value, const std::span<uint8_t>& output) {
+size_t EncodeInt(int value, const std::span<std::byte>& output) {
return varint::Encode(value, std::as_writable_bytes(output));
}
-size_t EncodeInt64(int64_t value, const std::span<uint8_t>& output) {
+size_t EncodeInt64(int64_t value, const std::span<std::byte>& output) {
return varint::Encode(value, std::as_writable_bytes(output));
}
-size_t EncodeFloat(float value, const std::span<uint8_t>& output) {
+size_t EncodeFloat(float value, const std::span<std::byte>& output) {
if (output.size() < sizeof(value)) {
return 0;
}
@@ -54,7 +54,7 @@
return sizeof(value);
}
-size_t EncodeString(const char* string, const std::span<uint8_t>& output) {
+size_t EncodeString(const char* string, const std::span<std::byte>& output) {
// The top bit of the status byte indicates if the string was truncated.
static constexpr size_t kMaxStringLength = 0x7Fu;
@@ -71,17 +71,17 @@
// Scan the string to find out how many bytes to copy.
size_t bytes_to_copy = 0;
- uint8_t overflow_bit = 0;
+ std::byte overflow_bit = std::byte(0);
while (string[bytes_to_copy] != '\0') {
if (bytes_to_copy == max_bytes) {
- overflow_bit = '\x80';
+ overflow_bit = std::byte('\x80');
break;
}
bytes_to_copy += 1;
}
- output[0] = bytes_to_copy | overflow_bit;
+ output[0] = static_cast<std::byte>(bytes_to_copy) | overflow_bit;
std::memcpy(output.data() + 1, string, bytes_to_copy);
return bytes_to_copy + 1; // include the status byte in the total
@@ -89,9 +89,9 @@
} // namespace
-size_t EncodeArgs(_pw_tokenizer_ArgTypes types,
+size_t EncodeArgs(pw_tokenizer_ArgTypes types,
va_list args,
- std::span<uint8_t> output) {
+ std::span<std::byte> output) {
size_t arg_count = types & PW_TOKENIZER_TYPE_COUNT_MASK;
types >>= PW_TOKENIZER_TYPE_COUNT_SIZE_BITS;
diff --git a/pw_tokenizer/public/pw_tokenizer/encode_args.h b/pw_tokenizer/public/pw_tokenizer/encode_args.h
index d16c3bf..19f4e88 100644
--- a/pw_tokenizer/public/pw_tokenizer/encode_args.h
+++ b/pw_tokenizer/public/pw_tokenizer/encode_args.h
@@ -13,9 +13,9 @@
// the License.
#pragma once
-#include <array>
#include <cstdarg>
#include <cstddef>
+#include <cstring>
#include <span>
#include "pw_tokenizer/config.h"
@@ -25,12 +25,64 @@
namespace pw {
namespace tokenizer {
-// Buffer for encoding a tokenized string and arguments.
-struct EncodedMessage {
- pw_tokenizer_Token token;
- std::array<uint8_t,
- PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES - sizeof(token)>
- args;
+// Encodes a tokenized string's arguments to a buffer. The
+// pw_tokenizer_ArgTypes parameter specifies the argument types, in place of a
+// format string.
+//
+// Most tokenization implementations may use the EncodedMessage class below.
+size_t EncodeArgs(pw_tokenizer_ArgTypes types,
+ va_list args,
+ std::span<std::byte> output);
+
+// Encodes a tokenized message to a fixed size buffer. The size of the buffer is
+// determined by the PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES config macro.
+//
+// This class is used to encode tokenized messages passed in from the
+// tokenization macros. The macros provided by pw_tokenizer use this class, and
+// projects that elect to define their own versions of the tokenization macros
+// should use it when possible.
+//
+// To use the pw::Tokenizer::EncodedMessage, construct it with the token,
+// argument types, and va_list from the variadic arguments:
+//
+// void SendLogMessage(std::span<std::byte> log_data);
+//
+// extern "C" void TokenizeToSendLogMessage(pw_tokenizer_Token token,
+// pw_tokenizer_ArgTypes types,
+// ...) {
+// va_list args;
+// va_start(args, types);
+// EncodedMessage encoded_message(token, types, args);
+// va_end(args);
+//
+// SendLogMessage(encoded_message); // EncodedMessage converts to std::span
+// }
+//
+class EncodedMessage {
+ public:
+ // Encodes a tokenized message to an internal buffer.
+ EncodedMessage(pw_tokenizer_Token token,
+ pw_tokenizer_ArgTypes types,
+ va_list args) {
+ std::memcpy(data_, &token, sizeof(token));
+ args_size_ = EncodeArgs(
+ types, args, std::span<std::byte>(data_).subspan(sizeof(token)));
+ }
+
+ // The binary-encoded tokenized message.
+ const std::byte* data() const { return data_; }
+
+ // Returns the data() as a pointer to uint8_t instead of std::byte.
+ const uint8_t* data_as_uint8() const {
+ return reinterpret_cast<const uint8_t*>(data());
+ }
+
+ // The size of the encoded tokenized message in bytes.
+ size_t size() const { return sizeof(pw_tokenizer_Token) + args_size_; }
+
+ private:
+ std::byte data_[PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES];
+ size_t args_size_;
};
static_assert(PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES >=
@@ -38,17 +90,5 @@
"PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES must be at least "
"large enough for a token (4 bytes)");
-static_assert(offsetof(EncodedMessage, args) == sizeof(EncodedMessage::token) &&
- PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES ==
- sizeof(EncodedMessage),
- "EncodedMessage should not have padding bytes between members");
-
-// Encodes a tokenized string's arguments to a buffer. The
-// _pw_tokenizer_ArgTypes parameter specifies the argument types, in place of a
-// format string.
-size_t EncodeArgs(_pw_tokenizer_ArgTypes types,
- va_list args,
- std::span<uint8_t> output);
-
} // namespace tokenizer
} // namespace pw
diff --git a/pw_tokenizer/public/pw_tokenizer/internal/argument_types.h b/pw_tokenizer/public/pw_tokenizer/internal/argument_types.h
index 1f431e4..79f631d 100644
--- a/pw_tokenizer/public/pw_tokenizer/internal/argument_types.h
+++ b/pw_tokenizer/public/pw_tokenizer/internal/argument_types.h
@@ -31,7 +31,7 @@
#define PW_TOKENIZER_TYPE_COUNT_SIZE_BITS 4u
#define PW_TOKENIZER_TYPE_COUNT_MASK 0x0Fu
-typedef uint32_t _pw_tokenizer_ArgTypes;
+typedef uint32_t pw_tokenizer_ArgTypes;
#elif PW_TOKENIZER_CFG_ARG_TYPES_SIZE_BYTES == 8
@@ -42,7 +42,7 @@
#define PW_TOKENIZER_TYPE_COUNT_SIZE_BITS 6u
#define PW_TOKENIZER_TYPE_COUNT_MASK 0x1Fu // only 5 bits will be needed
-typedef uint64_t _pw_tokenizer_ArgTypes;
+typedef uint64_t pw_tokenizer_ArgTypes;
#else
@@ -52,7 +52,7 @@
// The tokenized string encoding function is a variadic function that works
// similarly to printf. Instead of a format string, however, the argument types
-// are packed into a _pw_tokenizer_ArgTypes.
+// are packed into a pw_tokenizer_ArgTypes.
//
// The four supported argument types are represented by two-bit argument codes.
// Just four types are required because only printf-compatible arguments are
@@ -62,10 +62,10 @@
// char* values cannot be printed as pointers with %p. These arguments are
// always encoded as strings. To format a char* as an address, cast it to void*
// or an integer.
-#define PW_TOKENIZER_ARG_TYPE_INT ((_pw_tokenizer_ArgTypes)0)
-#define PW_TOKENIZER_ARG_TYPE_INT64 ((_pw_tokenizer_ArgTypes)1)
-#define PW_TOKENIZER_ARG_TYPE_DOUBLE ((_pw_tokenizer_ArgTypes)2)
-#define PW_TOKENIZER_ARG_TYPE_STRING ((_pw_tokenizer_ArgTypes)3)
+#define PW_TOKENIZER_ARG_TYPE_INT ((pw_tokenizer_ArgTypes)0)
+#define PW_TOKENIZER_ARG_TYPE_INT64 ((pw_tokenizer_ArgTypes)1)
+#define PW_TOKENIZER_ARG_TYPE_DOUBLE ((pw_tokenizer_ArgTypes)2)
+#define PW_TOKENIZER_ARG_TYPE_STRING ((pw_tokenizer_ArgTypes)3)
// Select the int argument type based on the size of the type. Values smaller
// than int are promoted to int.
@@ -89,7 +89,7 @@
// This function selects the matching type enum for supported argument types.
template <typename T>
-constexpr _pw_tokenizer_ArgTypes VarargsType() {
+constexpr pw_tokenizer_ArgTypes VarargsType() {
using ArgType = std::decay_t<T>;
if constexpr (std::is_floating_point<ArgType>()) {
@@ -116,26 +116,26 @@
template <typename T, bool kDontCare1, bool kDontCare2>
struct SelectVarargsType<T, true, kDontCare1, kDontCare2> {
- static constexpr _pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_DOUBLE;
+ static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_DOUBLE;
};
template <typename T, bool kDontCare>
struct SelectVarargsType<T, false, true, kDontCare> {
- static constexpr _pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_STRING;
+ static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_STRING;
};
template <typename T>
struct SelectVarargsType<T, false, false, true> {
- static constexpr _pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_INT64;
+ static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_INT64;
};
template <typename T>
struct SelectVarargsType<T, false, false, false> {
- static constexpr _pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_INT;
+ static constexpr pw_tokenizer_ArgTypes kValue = PW_TOKENIZER_ARG_TYPE_INT;
};
template <typename T>
-constexpr _pw_tokenizer_ArgTypes VarargsType() {
+constexpr pw_tokenizer_ArgTypes VarargsType() {
return SelectVarargsType<typename std::decay<T>::type>::kValue;
}
@@ -174,8 +174,8 @@
#endif // __cplusplus
-// Encodes the types of the provided arguments as a _pw_tokenizer_ArgTypes
-// value. Depending on the size of _pw_tokenizer_ArgTypes, the bottom 4 or 6
+// Encodes the types of the provided arguments as a pw_tokenizer_ArgTypes
+// value. Depending on the size of pw_tokenizer_ArgTypes, the bottom 4 or 6
// bits store the number of arguments and the remaining bits store the types,
// two bits per type.
//
@@ -184,4 +184,4 @@
#define PW_TOKENIZER_ARG_TYPES(...) \
PW_DELEGATE_BY_ARG_COUNT(_PW_TOKENIZER_TYPES_, __VA_ARGS__)
-#define _PW_TOKENIZER_TYPES_0() ((_pw_tokenizer_ArgTypes)0)
+#define _PW_TOKENIZER_TYPES_0() ((pw_tokenizer_ArgTypes)0)
diff --git a/pw_tokenizer/public/pw_tokenizer/tokenize.h b/pw_tokenizer/public/pw_tokenizer/tokenize.h
index 7b2fa49..cf21cd6 100644
--- a/pw_tokenizer/public/pw_tokenizer/tokenize.h
+++ b/pw_tokenizer/public/pw_tokenizer/tokenize.h
@@ -115,15 +115,15 @@
domain, UINT32_MAX, buffer, buffer_size_pointer, format, __VA_ARGS__)
// Same as PW_TOKENIZE_TO_BUFFER_DOMAIN, but applies a mask to the token.
-#define PW_TOKENIZE_TO_BUFFER_MASK( \
- domain, mask, buffer, buffer_size_pointer, format, ...) \
- do { \
- _PW_TOKENIZE_FORMAT_STRING(domain, mask, format, __VA_ARGS__); \
- _pw_tokenizer_ToBuffer(buffer, \
- buffer_size_pointer, \
- _pw_tokenizer_token, \
- PW_TOKENIZER_ARG_TYPES(__VA_ARGS__) \
- PW_COMMA_ARGS(__VA_ARGS__)); \
+#define PW_TOKENIZE_TO_BUFFER_MASK( \
+ domain, mask, buffer, buffer_size_pointer, format, ...) \
+ do { \
+ PW_TOKENIZE_FORMAT_STRING(domain, mask, format, __VA_ARGS__); \
+ _pw_tokenizer_ToBuffer(buffer, \
+ buffer_size_pointer, \
+ _pw_tokenizer_token, \
+ PW_TOKENIZER_ARG_TYPES(__VA_ARGS__) \
+ PW_COMMA_ARGS(__VA_ARGS__)); \
} while (0)
// Encodes a tokenized string and arguments to a buffer on the stack. The
@@ -166,7 +166,7 @@
// Same as PW_TOKENIZE_TO_CALLBACK_DOMAIN, but applies a mask to the token.
#define PW_TOKENIZE_TO_CALLBACK_MASK(domain, mask, callback, format, ...) \
do { \
- _PW_TOKENIZE_FORMAT_STRING(domain, mask, format, __VA_ARGS__); \
+ PW_TOKENIZE_FORMAT_STRING(domain, mask, format, __VA_ARGS__); \
_pw_tokenizer_ToCallback(callback, \
_pw_tokenizer_token, \
PW_TOKENIZER_ARG_TYPES(__VA_ARGS__) \
@@ -180,13 +180,13 @@
void _pw_tokenizer_ToBuffer(void* buffer,
size_t* buffer_size_bytes, // input and output arg
pw_tokenizer_Token token,
- _pw_tokenizer_ArgTypes types,
+ pw_tokenizer_ArgTypes types,
...);
void _pw_tokenizer_ToCallback(void (*callback)(const uint8_t* encoded_message,
size_t size_bytes),
pw_tokenizer_Token token,
- _pw_tokenizer_ArgTypes types,
+ pw_tokenizer_ArgTypes types,
...);
// This empty function allows the compiler to check the format string.
@@ -204,9 +204,9 @@
// This macro takes a printf-style format string and corresponding arguments. It
// checks that the arguments are correct, stores the format string in a special
-// section, and calculates the string's token at compile time.
+// section, and calculates the string's token at compile time. This
// clang-format off
-#define _PW_TOKENIZE_FORMAT_STRING(domain, mask, format, ...) \
+#define PW_TOKENIZE_FORMAT_STRING(domain, mask, format, ...) \
if (0) { /* Do not execute to prevent double evaluation of the arguments. */ \
pw_tokenizer_CheckFormatString(format PW_COMMA_ARGS(__VA_ARGS__)); \
} \
diff --git a/pw_tokenizer/public/pw_tokenizer/tokenize_to_global_handler.h b/pw_tokenizer/public/pw_tokenizer/tokenize_to_global_handler.h
index 72a5e7d..ca7e0b9 100644
--- a/pw_tokenizer/public/pw_tokenizer/tokenize_to_global_handler.h
+++ b/pw_tokenizer/public/pw_tokenizer/tokenize_to_global_handler.h
@@ -54,7 +54,7 @@
// token.
#define PW_TOKENIZE_TO_GLOBAL_HANDLER_MASK(domain, mask, format, ...) \
do { \
- _PW_TOKENIZE_FORMAT_STRING(domain, mask, format, __VA_ARGS__); \
+ PW_TOKENIZE_FORMAT_STRING(domain, mask, format, __VA_ARGS__); \
_pw_tokenizer_ToGlobalHandler(_pw_tokenizer_token, \
PW_TOKENIZER_ARG_TYPES(__VA_ARGS__) \
PW_COMMA_ARGS(__VA_ARGS__)); \
@@ -71,7 +71,7 @@
// This function encodes the tokenized strings. Do not call it directly;
// instead, use the PW_TOKENIZE_TO_GLOBAL_HANDLER macro.
void _pw_tokenizer_ToGlobalHandler(pw_tokenizer_Token token,
- _pw_tokenizer_ArgTypes types,
+ pw_tokenizer_ArgTypes types,
...);
PW_EXTERN_C_END
diff --git a/pw_tokenizer/public/pw_tokenizer/tokenize_to_global_handler_with_payload.h b/pw_tokenizer/public/pw_tokenizer/tokenize_to_global_handler_with_payload.h
index 2577e48..1faace3 100644
--- a/pw_tokenizer/public/pw_tokenizer/tokenize_to_global_handler_with_payload.h
+++ b/pw_tokenizer/public/pw_tokenizer/tokenize_to_global_handler_with_payload.h
@@ -56,7 +56,7 @@
#define PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD_MASK( \
domain, mask, payload, format, ...) \
do { \
- _PW_TOKENIZE_FORMAT_STRING(domain, mask, format, __VA_ARGS__); \
+ PW_TOKENIZE_FORMAT_STRING(domain, mask, format, __VA_ARGS__); \
_pw_tokenizer_ToGlobalHandlerWithPayload( \
payload, \
_pw_tokenizer_token, \
@@ -79,7 +79,7 @@
// instead, use the PW_TOKENIZE_TO_GLOBAL_HANDLER_WITH_PAYLOAD macro.
void _pw_tokenizer_ToGlobalHandlerWithPayload(pw_tokenizer_Payload payload,
pw_tokenizer_Token token,
- _pw_tokenizer_ArgTypes types,
+ pw_tokenizer_ArgTypes types,
...);
PW_EXTERN_C_END
diff --git a/pw_tokenizer/pw_tokenizer_private/argument_types_test.h b/pw_tokenizer/pw_tokenizer_private/argument_types_test.h
index b616392..71f3ef2 100644
--- a/pw_tokenizer/pw_tokenizer_private/argument_types_test.h
+++ b/pw_tokenizer/pw_tokenizer_private/argument_types_test.h
@@ -20,27 +20,27 @@
PW_EXTERN_C_START
-_pw_tokenizer_ArgTypes pw_TestTokenizerNoArgs(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerNoArgs(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerChar(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerUint8(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerUint16(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerInt32(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerInt64(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerUint64(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerFloat(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerDouble(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerString(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerMutableString(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerChar(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerUint8(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerUint16(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerInt32(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerInt64(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerUint64(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerFloat(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerDouble(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerString(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerMutableString(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerIntFloat(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerUint64Char(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerStringString(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerUint16Int(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerFloatString(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerIntFloat(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerUint64Char(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerStringString(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerUint16Int(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerFloatString(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerNull(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerPointer(void);
-_pw_tokenizer_ArgTypes pw_TestTokenizerPointerPointer(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerNull(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerPointer(void);
+pw_tokenizer_ArgTypes pw_TestTokenizerPointerPointer(void);
PW_EXTERN_C_END
diff --git a/pw_tokenizer/tokenize.cc b/pw_tokenizer/tokenize.cc
index f34a81d..d515f0c 100644
--- a/pw_tokenizer/tokenize.cc
+++ b/pw_tokenizer/tokenize.cc
@@ -54,7 +54,7 @@
#endif // __APPLE__
constexpr Metadata metadata[] PW_TOKENIZER_INFO_SECTION = {
- {"hash_length_bytes", PW_TOKENIZER_CFG_C_HASH_LENGTH},
+ {"c_hash_length_bytes", PW_TOKENIZER_CFG_C_HASH_LENGTH},
{"sizeof_long", sizeof(long)}, // %l conversion specifier
{"sizeof_intmax_t", sizeof(intmax_t)}, // %j conversion specifier
{"sizeof_size_t", sizeof(size_t)}, // %z conversion specifier
@@ -66,7 +66,7 @@
extern "C" void _pw_tokenizer_ToBuffer(void* buffer,
size_t* buffer_size_bytes,
Token token,
- _pw_tokenizer_ArgTypes types,
+ pw_tokenizer_ArgTypes types,
...) {
if (*buffer_size_bytes < sizeof(token)) {
*buffer_size_bytes = 0;
@@ -80,8 +80,8 @@
const size_t encoded_bytes = EncodeArgs(
types,
args,
- std::span<uint8_t>(static_cast<uint8_t*>(buffer) + sizeof(token),
- *buffer_size_bytes - sizeof(token)));
+ std::span<std::byte>(static_cast<std::byte*>(buffer) + sizeof(token),
+ *buffer_size_bytes - sizeof(token)));
va_end(args);
*buffer_size_bytes = sizeof(token) + encoded_bytes;
@@ -90,18 +90,14 @@
extern "C" void _pw_tokenizer_ToCallback(
void (*callback)(const uint8_t* encoded_message, size_t size_bytes),
Token token,
- _pw_tokenizer_ArgTypes types,
+ pw_tokenizer_ArgTypes types,
...) {
- EncodedMessage encoded;
- encoded.token = token;
-
va_list args;
va_start(args, types);
- const size_t encoded_bytes = EncodeArgs(types, args, encoded.args);
+ EncodedMessage encoded(token, types, args);
va_end(args);
- callback(reinterpret_cast<const uint8_t*>(&encoded),
- sizeof(encoded.token) + encoded_bytes);
+ callback(encoded.data_as_uint8(), encoded.size());
}
} // namespace tokenizer
diff --git a/pw_tokenizer/tokenize_test.cc b/pw_tokenizer/tokenize_test.cc
index 393d8f7..ff87f51 100644
--- a/pw_tokenizer/tokenize_test.cc
+++ b/pw_tokenizer/tokenize_test.cc
@@ -401,6 +401,14 @@
EXPECT_TRUE(std::all_of(buffer_, std::end(buffer_), is_untouched));
}
+TEST_F(TokenizeToBuffer, CharArray) {
+ size_t message_size = sizeof(buffer_);
+ PW_TOKENIZE_TO_BUFFER(buffer_, &message_size, __func__);
+ constexpr auto expected = ExpectedData(__func__);
+ ASSERT_EQ(expected.size(), message_size);
+ EXPECT_EQ(std::memcmp(expected.data(), buffer_, expected.size()), 0);
+}
+
TEST_F(TokenizeToBuffer, C_StringShortFloat) {
size_t size = sizeof(buffer_);
pw_tokenizer_ToBufferTest_StringShortFloat(buffer_, &size);
@@ -517,6 +525,13 @@
EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
}
+TEST_F(TokenizeToCallback, CharArray) {
+ PW_TOKENIZE_TO_CALLBACK(SetMessage, __func__);
+ constexpr auto expected = ExpectedData(__func__);
+ ASSERT_EQ(expected.size(), message_size_bytes_);
+ EXPECT_EQ(std::memcmp(expected.data(), message_, expected.size()), 0);
+}
+
TEST_F(TokenizeToCallback, C_SequentialZigZag) {
pw_tokenizer_ToCallbackTest_SequentialZigZag(SetMessage);
diff --git a/pw_tokenizer/tokenize_to_global_handler.cc b/pw_tokenizer/tokenize_to_global_handler.cc
index 78b79a0..5ac2755 100644
--- a/pw_tokenizer/tokenize_to_global_handler.cc
+++ b/pw_tokenizer/tokenize_to_global_handler.cc
@@ -20,18 +20,14 @@
namespace tokenizer {
extern "C" void _pw_tokenizer_ToGlobalHandler(pw_tokenizer_Token token,
- _pw_tokenizer_ArgTypes types,
+ pw_tokenizer_ArgTypes types,
...) {
- EncodedMessage encoded;
- encoded.token = token;
-
va_list args;
va_start(args, types);
- const size_t encoded_bytes = EncodeArgs(types, args, encoded.args);
+ EncodedMessage encoded(token, types, args);
va_end(args);
- pw_tokenizer_HandleEncodedMessage(reinterpret_cast<const uint8_t*>(&encoded),
- sizeof(encoded.token) + encoded_bytes);
+ pw_tokenizer_HandleEncodedMessage(encoded.data_as_uint8(), encoded.size());
}
} // namespace tokenizer
diff --git a/pw_tokenizer/tokenize_to_global_handler_with_payload.cc b/pw_tokenizer/tokenize_to_global_handler_with_payload.cc
index 0380aef..44b02c6 100644
--- a/pw_tokenizer/tokenize_to_global_handler_with_payload.cc
+++ b/pw_tokenizer/tokenize_to_global_handler_with_payload.cc
@@ -22,20 +22,15 @@
extern "C" void _pw_tokenizer_ToGlobalHandlerWithPayload(
const pw_tokenizer_Payload payload,
pw_tokenizer_Token token,
- _pw_tokenizer_ArgTypes types,
+ pw_tokenizer_ArgTypes types,
...) {
- EncodedMessage encoded;
- encoded.token = token;
-
va_list args;
va_start(args, types);
- const size_t encoded_bytes = EncodeArgs(types, args, encoded.args);
+ EncodedMessage encoded(token, types, args);
va_end(args);
pw_tokenizer_HandleEncodedMessageWithPayload(
- payload,
- reinterpret_cast<const uint8_t*>(&encoded),
- sizeof(encoded.token) + encoded_bytes);
+ payload, encoded.data_as_uint8(), encoded.size());
}
} // namespace tokenizer