xref: /aosp_15_r20/external/pigweed/pw_tokenizer/public/pw_tokenizer/encode_args.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 #include "pw_varint/varint.h"
24 
25 #ifdef __cplusplus
26 
27 #include <cstring>
28 
29 #include "pw_polyfill/standard.h"
30 #include "pw_span/span.h"
31 #include "pw_tokenizer/config.h"
32 #include "pw_tokenizer/tokenize.h"
33 
34 namespace pw::tokenizer {
35 namespace internal {
36 
37 // Returns the maximum encoded size of an argument of the specified type.
38 template <typename T>
ArgEncodedSizeBytes()39 constexpr size_t ArgEncodedSizeBytes() {
40   constexpr pw_tokenizer_ArgTypes kType = VarargsType<T>();
41   if constexpr (kType == PW_TOKENIZER_ARG_TYPE_DOUBLE) {
42     return sizeof(float);
43   } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_STRING) {
44     return 1;  // Size of the length byte only
45   } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT64) {
46     return 10;  // Max size of a varint-encoded 64-bit integer
47   } else if constexpr (kType == PW_TOKENIZER_ARG_TYPE_INT) {
48     return sizeof(T) + 1;  // Max size of zig-zag varint integer <= 32-bits
49   } else {
50     static_assert(sizeof(T) != sizeof(T), "Unsupported argument type");
51   }
52 }
53 
54 }  // namespace internal
55 
56 /// Calculates the minimum buffer size to allocate that is guaranteed to support
57 /// encoding the specified arguments.
58 ///
59 /// The contents of strings are NOT included in this total. The string's
60 /// length/status byte is guaranteed to fit, but the string contents may be
61 /// truncated. Encoding is considered to succeed as long as the string's
62 /// length/status byte is written, even if the actual string is truncated.
63 ///
64 /// Examples:
65 ///
66 /// - Message with no arguments:
67 ///       `MinEncodingBufferSizeBytes() == 4`
68 /// - Message with an int argument
69 ///       `MinEncodingBufferSizeBytes<int>() == 9 (4 + 5)`
70 template <typename... ArgTypes>
MinEncodingBufferSizeBytes()71 constexpr size_t MinEncodingBufferSizeBytes() {
72   return (sizeof(pw_tokenizer_Token) + ... +
73           internal::ArgEncodedSizeBytes<ArgTypes>());
74 }
75 
76 /// Encodes a tokenized string's arguments to a buffer. The
77 /// @cpp_type{pw_tokenizer_ArgTypes} parameter specifies the argument types, in
78 /// place of a format string.
79 ///
80 /// Most tokenization implementations should use the @cpp_class{EncodedMessage}
81 /// class.
82 size_t EncodeArgs(pw_tokenizer_ArgTypes types,
83                   va_list args,
84                   span<std::byte> output);
85 
86 /// Encodes a tokenized message to a fixed size buffer. This class is used to
87 /// encode tokenized messages passed in from tokenization macros.
88 ///
89 /// To use `pw::tokenizer::EncodedMessage`, construct it with the token,
90 /// argument types, and `va_list` from the variadic arguments:
91 ///
92 /// @code{.cpp}
93 ///   void SendLogMessage(span<std::byte> log_data);
94 ///
95 ///   extern "C" void TokenizeToSendLogMessage(pw_tokenizer_Token token,
96 ///                                            pw_tokenizer_ArgTypes types,
97 ///                                            ...) {
98 ///     va_list args;
99 ///     va_start(args, types);
100 ///     EncodedMessage encoded_message(token, types, args);
101 ///     va_end(args);
102 ///
103 ///     SendLogMessage(encoded_message);  // EncodedMessage converts to span
104 ///   }
105 /// @endcode
106 template <size_t kMaxSizeBytes>
107 class EncodedMessage {
108  public:
109   // Encodes a tokenized message to an internal buffer.
EncodedMessage(pw_tokenizer_Token token,pw_tokenizer_ArgTypes types,va_list args)110   EncodedMessage(pw_tokenizer_Token token,
111                  pw_tokenizer_ArgTypes types,
112                  va_list args) {
113     std::memcpy(data_, &token, sizeof(token));
114     size_ =
115         sizeof(token) +
116         EncodeArgs(types, args, span<std::byte>(data_).subspan(sizeof(token)));
117   }
118 
119   /// The binary-encoded tokenized message.
data()120   const std::byte* data() const { return data_; }
121 
122   /// Returns `data()` as a pointer to `uint8_t` instead of `std::byte`.
data_as_uint8()123   const uint8_t* data_as_uint8() const {
124     return reinterpret_cast<const uint8_t*>(data());
125   }
126 
127   /// The size of the encoded tokenized message in bytes.
size()128   size_t size() const { return size_; }
129 
130  private:
131   static_assert(kMaxSizeBytes >= sizeof(pw_tokenizer_Token),
132                 "The encoding buffer must be at least large enough for a token "
133                 "(4 bytes)");
134 
135   std::byte data_[kMaxSizeBytes];
136   size_t size_;
137 };
138 
139 }  // namespace pw::tokenizer
140 
141 #endif  // __cplusplus
142 
143 PW_EXTERN_C_START
144 
145 /// C function that encodes arguments to a tokenized buffer. Use the
146 /// @cpp_func{pw::tokenizer::EncodeArgs} function from C++.
147 size_t pw_tokenizer_EncodeArgs(pw_tokenizer_ArgTypes types,
148                                va_list args,
149                                void* output_buffer,
150                                size_t output_buffer_size);
151 
152 /// Encodes an `int` with the standard integer encoding: zig-zag + LEB128.
153 /// This function is only necessary when manually encoding tokenized messages.
pw_tokenizer_EncodeInt(int value,void * output,size_t output_size_bytes)154 static inline size_t pw_tokenizer_EncodeInt(int value,
155                                             void* output,
156                                             size_t output_size_bytes) {
157   return pw_varint_Encode32(
158       pw_varint_ZigZagEncode32(value), output, output_size_bytes);
159 }
160 
161 /// Encodes an `int64_t` with the standard integer encoding: zig-zag + LEB128.
162 /// This function is only necessary when manually encoding tokenized messages.
pw_tokenizer_EncodeInt64(int64_t value,void * output,size_t output_size_bytes)163 static inline size_t pw_tokenizer_EncodeInt64(int64_t value,
164                                               void* output,
165                                               size_t output_size_bytes) {
166   return pw_varint_Encode64(
167       pw_varint_ZigZagEncode64(value), output, output_size_bytes);
168 }
169 
170 PW_EXTERN_C_END
171