| // Copyright 2020 The Pigweed Authors |
| // |
| // 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 |
| // |
| // https://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. |
| #pragma once |
| |
| #include <stdarg.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include "pw_polyfill/standard.h" |
| #include "pw_preprocessor/util.h" |
| #include "pw_tokenizer/internal/argument_types.h" |
| #include "pw_varint/varint.h" |
| |
| #if PW_CXX_STANDARD_IS_SUPPORTED(17) |
| |
| #include <cstring> |
| |
| #include "pw_polyfill/standard.h" |
| #include "pw_span/span.h" |
| #include "pw_tokenizer/config.h" |
| #include "pw_tokenizer/tokenize.h" |
| |
| namespace pw::tokenizer { |
| namespace internal { |
| |
| // Returns the maximum encoded size of an argument of the specified type. |
| template <typename T> |
| constexpr size_t ArgEncodedSizeBytes() { |
| constexpr pw_tokenizer_ArgTypes kType = VarargsType<T>(); |
| if constexpr (kType == PW_TOKENIZER_ARG_TYPE_DOUBLE) { |
| return sizeof(float); |
| } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_STRING) { |
| return 1; // Size of the length byte only |
| } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT64) { |
| return 10; // Max size of a varint-encoded 64-bit integer |
| } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT) { |
| return sizeof(T) + 1; // Max size of zig-zag varint integer <= 32-bits |
| } else { |
| static_assert(sizeof(T) != sizeof(T), "Unsupported argument type"); |
| } |
| } |
| |
| } // namespace internal |
| |
| /// Calculates the minimum buffer size to allocate that is guaranteed to support |
| /// encoding the specified arguments. |
| /// |
| /// The contents of strings are NOT included in this total. The string's |
| /// length/status byte is guaranteed to fit, but the string contents may be |
| /// truncated. Encoding is considered to succeed as long as the string's |
| /// length/status byte is written, even if the actual string is truncated. |
| /// |
| /// Examples: |
| /// |
| /// - Message with no arguments: |
| /// `MinEncodingBufferSizeBytes() == 4` |
| /// - Message with an int argument |
| /// `MinEncodingBufferSizeBytes<int>() == 9 (4 + 5)` |
| template <typename... ArgTypes> |
| constexpr size_t MinEncodingBufferSizeBytes() { |
| return (sizeof(pw_tokenizer_Token) + ... + |
| internal::ArgEncodedSizeBytes<ArgTypes>()); |
| } |
| |
| /// Encodes a tokenized string's arguments to a buffer. The |
| /// @cpp_type{pw_tokenizer_ArgTypes} parameter specifies the argument types, in |
| /// place of a format string. |
| /// |
| /// Most tokenization implementations should use the @cpp_class{EncodedMessage} |
| /// class. |
| size_t EncodeArgs(pw_tokenizer_ArgTypes types, |
| va_list args, |
| span<std::byte> output); |
| |
| /// Encodes a tokenized message to a fixed size buffer. This class is used to |
| /// encode tokenized messages passed in from tokenization macros. |
| /// |
| /// To use `pw::tokenizer::EncodedMessage`, construct it with the token, |
| /// argument types, and `va_list` from the variadic arguments: |
| /// |
| /// @code{.cpp} |
| /// void SendLogMessage(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 span |
| /// } |
| /// @endcode |
| template <size_t kMaxSizeBytes> |
| 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)); |
| size_ = |
| sizeof(token) + |
| EncodeArgs(types, args, span<std::byte>(data_).subspan(sizeof(token))); |
| } |
| |
| /// The binary-encoded tokenized message. |
| const std::byte* data() const { return data_; } |
| |
| /// Returns `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 size_; } |
| |
| private: |
| static_assert(kMaxSizeBytes >= sizeof(pw_tokenizer_Token), |
| "The encoding buffer must be at least large enough for a token " |
| "(4 bytes)"); |
| |
| std::byte data_[kMaxSizeBytes]; |
| size_t size_; |
| }; |
| |
| } // namespace pw::tokenizer |
| |
| #endif // PW_CXX_STANDARD_IS_SUPPORTED(17) |
| |
| PW_EXTERN_C_START |
| |
| /// C function that encodes arguments to a tokenized buffer. Use the |
| /// @cpp_func{pw::tokenizer::EncodeArgs} function from C++. |
| size_t pw_tokenizer_EncodeArgs(pw_tokenizer_ArgTypes types, |
| va_list args, |
| void* output_buffer, |
| size_t output_buffer_size); |
| |
| /// Encodes an `int` with the standard integer encoding: zig-zag + LEB128. |
| /// This function is only necessary when manually encoding tokenized messages. |
| static inline size_t pw_tokenizer_EncodeInt(int value, |
| void* output, |
| size_t output_size_bytes) { |
| return pw_varint_Encode32( |
| pw_varint_ZigZagEncode32(value), output, output_size_bytes); |
| } |
| |
| /// Encodes an `int64_t` with the standard integer encoding: zig-zag + LEB128. |
| /// This function is only necessary when manually encoding tokenized messages. |
| static inline size_t pw_tokenizer_EncodeInt64(int64_t value, |
| void* output, |
| size_t output_size_bytes) { |
| return pw_varint_Encode64( |
| pw_varint_ZigZagEncode64(value), output, output_size_bytes); |
| } |
| |
| PW_EXTERN_C_END |