1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15
16 #include <stdarg.h>
17 #include <stddef.h>
18 #include <stdint.h>
19
20 #include "pw_polyfill/standard.h"
21 #include "pw_preprocessor/util.h"
22 #include "pw_tokenizer/internal/argument_types.h"
23
24 #if PW_CXX_STANDARD_IS_SUPPORTED(17)
25
26 #include <cstring>
27
28 #include "pw_polyfill/standard.h"
29 #include "pw_span/span.h"
30 #include "pw_tokenizer/config.h"
31 #include "pw_tokenizer/tokenize.h"
32
33 namespace pw::tokenizer {
34 namespace internal {
35
36 // Returns the maximum encoded size of an argument of the specified type.
37 template <typename T>
ArgEncodedSizeBytes()38 constexpr size_t ArgEncodedSizeBytes() {
39 constexpr pw_tokenizer_ArgTypes kType = VarargsType<T>();
40 if constexpr (kType == PW_TOKENIZER_ARG_TYPE_DOUBLE) {
41 return sizeof(float);
42 } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_STRING) {
43 return 1; // Size of the length byte only
44 } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT64) {
45 return 10; // Max size of a varint-encoded 64-bit integer
46 } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT) {
47 return sizeof(T) + 1; // Max size of zig-zag varint integer <= 32-bits
48 } else {
49 static_assert(sizeof(T) != sizeof(T), "Unsupported argument type");
50 }
51 }
52
53 } // namespace internal
54
55 /// Calculates the minimum buffer size to allocate that is guaranteed to support
56 /// encoding the specified arguments.
57 ///
58 /// The contents of strings are NOT included in this total. The string's
59 /// length/status byte is guaranteed to fit, but the string contents may be
60 /// truncated. Encoding is considered to succeed as long as the string's
61 /// length/status byte is written, even if the actual string is truncated.
62 ///
63 /// Examples:
64 ///
65 /// - Message with no arguments:
66 /// `MinEncodingBufferSizeBytes() == 4`
67 /// - Message with an int argument
68 /// `MinEncodingBufferSizeBytes<int>() == 9 (4 + 5)`
69 template <typename... ArgTypes>
MinEncodingBufferSizeBytes()70 constexpr size_t MinEncodingBufferSizeBytes() {
71 return (sizeof(pw_tokenizer_Token) + ... +
72 internal::ArgEncodedSizeBytes<ArgTypes>());
73 }
74
75 /// Encodes a tokenized string's arguments to a buffer. The
76 /// @cpp_type{pw_tokenizer_ArgTypes} parameter specifies the argument types, in
77 /// place of a format string.
78 ///
79 /// Most tokenization implementations should use the @cpp_class{EncodedMessage}
80 /// class.
81 size_t EncodeArgs(pw_tokenizer_ArgTypes types,
82 va_list args,
83 span<std::byte> output);
84
85 /// Encodes a tokenized message to a fixed size buffer. By default, the buffer
86 /// size is set by the @c_macro{PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES}
87 /// config macro. This class is used to encode tokenized messages passed in from
88 /// tokenization macros.
89 ///
90 /// To use `pw::tokenizer::EncodedMessage`, construct it with the token,
91 /// argument types, and `va_list` from the variadic arguments:
92 ///
93 /// @code{.cpp}
94 /// void SendLogMessage(span<std::byte> log_data);
95 ///
96 /// extern "C" void TokenizeToSendLogMessage(pw_tokenizer_Token token,
97 /// pw_tokenizer_ArgTypes types,
98 /// ...) {
99 /// va_list args;
100 /// va_start(args, types);
101 /// EncodedMessage encoded_message(token, types, args);
102 /// va_end(args);
103 ///
104 /// SendLogMessage(encoded_message); // EncodedMessage converts to span
105 /// }
106 /// @endcode
107 template <size_t kMaxSizeBytes = PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES>
108 class EncodedMessage {
109 public:
110 // Encodes a tokenized message to an internal buffer.
EncodedMessage(pw_tokenizer_Token token,pw_tokenizer_ArgTypes types,va_list args)111 EncodedMessage(pw_tokenizer_Token token,
112 pw_tokenizer_ArgTypes types,
113 va_list args) {
114 std::memcpy(data_, &token, sizeof(token));
115 size_ =
116 sizeof(token) +
117 EncodeArgs(types, args, span<std::byte>(data_).subspan(sizeof(token)));
118 }
119
120 /// The binary-encoded tokenized message.
data()121 const std::byte* data() const { return data_; }
122
123 /// Returns `data()` as a pointer to `uint8_t` instead of `std::byte`.
data_as_uint8()124 const uint8_t* data_as_uint8() const {
125 return reinterpret_cast<const uint8_t*>(data());
126 }
127
128 /// The size of the encoded tokenized message in bytes.
size()129 size_t size() const { return size_; }
130
131 private:
132 static_assert(kMaxSizeBytes >= sizeof(pw_tokenizer_Token),
133 "The encoding buffer must be at least large enough for a token "
134 "(4 bytes)");
135
136 std::byte data_[kMaxSizeBytes];
137 size_t size_;
138 };
139
140 } // namespace pw::tokenizer
141
142 #endif // PW_CXX_STANDARD_IS_SUPPORTED(17)
143
144 PW_EXTERN_C_START
145
146 /// C function that encodes arguments to a tokenized buffer. Use the
147 /// @cpp_func{pw::tokenizer::EncodeArgs} function from C++.
148 size_t pw_tokenizer_EncodeArgs(pw_tokenizer_ArgTypes types,
149 va_list args,
150 void* output_buffer,
151 size_t output_buffer_size);
152 PW_EXTERN_C_END
153