aboutsummaryrefslogtreecommitdiff
path: root/pw_tokenizer/public/pw_tokenizer/base64.h
blob: 36acaf20df754cb3d075a8a5dbf4d0fda887ab4b (plain)
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// 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.

// This file provides functions for working with the prefixed Base64 format for
// tokenized messages. This format is useful for transmitting tokenized messages
// as plain text.
//
// The format uses a prefix character ($), followed by the Base64 version of the
// tokenized message. For example, consider a tokenized message with token
// 0xfeb35a42 and encoded argument 0x13. This messsage would be encoded as
// follows:
//
//            Binary: 42 5a b3 fe 13  [5 bytes]
//
//   Prefixed Base64: $Qlqz/hM=       [9 bytes]
//
#pragma once

#include <stddef.h>

#include "pw_preprocessor/util.h"
#include "pw_tokenizer/config.h"

// This character is used to mark the start of a Base64-encoded tokenized
// message. For consistency, it is recommended to always use $ if possible.
// If required, a different non-Base64 character may be used as a prefix.
#define PW_TOKENIZER_BASE64_PREFIX '$'

PW_EXTERN_C_START

// Encodes a binary tokenized message as prefixed Base64 with a null terminator.
// Returns the encoded string length (excluding the null terminator). Returns 0
// if the buffer is too small. Always null terminates if the output buffer is
// not empty.
//
// Equivalent to pw::tokenizer::PrefixedBase64Encode.
size_t pw_tokenizer_PrefixedBase64Encode(const void* binary_message,
                                         size_t binary_size_bytes,
                                         void* output_buffer,
                                         size_t output_buffer_size_bytes);

// Decodes a prefixed Base64 tokenized message to binary. Returns the size of
// the decoded binary data. The resulting data is ready to be passed to
// pw::tokenizer::Detokenizer::Detokenize. Returns 0 if the buffer is too small,
// the expected prefix character is missing, or the Base64 data is corrupt.
//
// Equivalent to pw::tokenizer::PrefixedBase64Encode.
size_t pw_tokenizer_PrefixedBase64Decode(const void* base64_message,
                                         size_t base64_size_bytes,
                                         void* output_buffer,
                                         size_t output_buffer_size);

PW_EXTERN_C_END

#ifdef __cplusplus

#include <string_view>

#include "pw_base64/base64.h"
#include "pw_span/span.h"
#include "pw_tokenizer/config.h"
#include "pw_tokenizer/tokenize.h"

namespace pw::tokenizer {

inline constexpr char kBase64Prefix = PW_TOKENIZER_BASE64_PREFIX;

#undef PW_TOKENIZER_BASE64_PREFIX  // In C++, use the variable, not the macro.

// Returns the size of a tokenized message (token + arguments) when encoded as
// prefixed Base64. Includes room for the prefix character ($) and encoded
// message. This value is the capacity needed to encode to a pw::InlineString.
constexpr size_t Base64EncodedStringSize(size_t message_size) {
  return sizeof(kBase64Prefix) + base64::EncodedSize(message_size);
}

// Same as Base64EncodedStringSize(), but for sizing char buffers. Includes room
// for the prefix character ($), encoded message, and a null terminator.
constexpr size_t Base64EncodedBufferSize(size_t message_size) {
  return Base64EncodedStringSize(message_size) + sizeof('\0');
}

// The minimum buffer size that can hold a tokenized message that is
// PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES long encoded as prefixed Base64.
inline constexpr size_t kDefaultBase64EncodedBufferSize =
    Base64EncodedBufferSize(PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES);

// Encodes a binary tokenized message as prefixed Base64 with a null terminator.
// Returns the encoded string length (excluding the null terminator). Returns 0
// if the buffer is too small. Always null terminates if the output buffer is
// not empty.
inline size_t PrefixedBase64Encode(span<const std::byte> binary_message,
                                   span<char> output_buffer) {
  return pw_tokenizer_PrefixedBase64Encode(binary_message.data(),
                                           binary_message.size(),
                                           output_buffer.data(),
                                           output_buffer.size());
}

// Also accept a span<const uint8_t> for the binary message.
inline size_t PrefixedBase64Encode(span<const uint8_t> binary_message,
                                   span<char> output_buffer) {
  return PrefixedBase64Encode(as_bytes(binary_message), output_buffer);
}

// Encodes a binary tokenized message as prefixed Base64 to a pw::InlineString,
// appending to any existing contents. Asserts if the message does not fit in
// the string.
void PrefixedBase64Encode(span<const std::byte> binary_message,
                          InlineString<>& output);

inline void PrefixedBase64Encode(span<const uint8_t> binary_message,
                                 InlineString<>& output) {
  return PrefixedBase64Encode(as_bytes(binary_message), output);
}

// Encodes a binary tokenized message as prefixed Base64 to a pw::InlineString.
// The pw::InlineString is sized to fit messages up to
// kMaxBinaryMessageSizeBytes long. Asserts if the message is larger.
template <size_t kMaxBinaryMessageSizeBytes =
              PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES>
auto PrefixedBase64Encode(span<const std::byte> binary_message) {
  static_assert(kMaxBinaryMessageSizeBytes >= 1, "Messages cannot be empty");
  InlineString<Base64EncodedStringSize(kMaxBinaryMessageSizeBytes)> string(
      1, kBase64Prefix);
  base64::Encode(binary_message, string);
  return string;
}

template <size_t kMaxBinaryMessageSizeBytes =
              PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES>
auto PrefixedBase64Encode(span<const uint8_t> binary_message) {
  return PrefixedBase64Encode<kMaxBinaryMessageSizeBytes>(
      as_bytes(binary_message));
}

// Decodes a prefixed Base64 tokenized message to binary. Returns the size of
// the decoded binary data. The resulting data is ready to be passed to
// pw::tokenizer::Detokenizer::Detokenize.
inline size_t PrefixedBase64Decode(std::string_view base64_message,
                                   span<std::byte> output_buffer) {
  return pw_tokenizer_PrefixedBase64Decode(base64_message.data(),
                                           base64_message.size(),
                                           output_buffer.data(),
                                           output_buffer.size());
}

// Decodes a prefixed Base64 tokenized message to binary in place. Returns the
// size of the decoded binary data.
inline size_t PrefixedBase64DecodeInPlace(span<std::byte> buffer) {
  return pw_tokenizer_PrefixedBase64Decode(
      buffer.data(), buffer.size(), buffer.data(), buffer.size());
}

// Decodes a prefixed Base64 tokenized message to binary in place. Resizes the
// string to fit the decoded binary data.
template <typename CharT>
inline void PrefixedBase64DecodeInPlace(InlineBasicString<CharT>& string) {
  static_assert(sizeof(CharT) == sizeof(char));
  string.resize(pw_tokenizer_PrefixedBase64Decode(
      string.data(), string.size(), string.data(), string.size()));
}

}  // namespace pw::tokenizer

#endif  // __cplusplus