pw_tokenizer: Base64EncodedBufferSize function
Rename Base64EncodedSize to Base64EncodedBufferSize and have it include
the null terminator. This makes it easier to use directly to size
buffers for Base64 encoding.
Change-Id: Ied24eed67f8d3c5e42a405c98e0b20b30d543536
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/20300
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
diff --git a/pw_tokenizer/base64.cc b/pw_tokenizer/base64.cc
index 7aab4af..2b87161 100644
--- a/pw_tokenizer/base64.cc
+++ b/pw_tokenizer/base64.cc
@@ -22,9 +22,9 @@
void* output_buffer,
size_t output_buffer_size_bytes) {
char* output = static_cast<char*>(output_buffer);
- const size_t encoded_size = Base64EncodedSize(binary_size_bytes);
+ const size_t encoded_size = Base64EncodedBufferSize(binary_size_bytes);
- if (output_buffer_size_bytes < encoded_size + sizeof('\0')) {
+ if (output_buffer_size_bytes < encoded_size) {
if (output_buffer_size_bytes > 0u) {
output[0] = '\0';
}
@@ -36,8 +36,8 @@
base64::Encode(std::span(static_cast<const std::byte*>(binary_message),
binary_size_bytes),
&output[1]);
- output[encoded_size] = '\0';
- return encoded_size;
+ output[encoded_size - 1] = '\0';
+ return encoded_size - sizeof('\0'); // exclude the null terminator
}
extern "C" size_t pw_tokenizer_PrefixedBase64Decode(const void* base64_message,
diff --git a/pw_tokenizer/base64_test.cc b/pw_tokenizer/base64_test.cc
index e751b81..fbbd064 100644
--- a/pw_tokenizer/base64_test.cc
+++ b/pw_tokenizer/base64_test.cc
@@ -74,6 +74,7 @@
TEST_F(PrefixedBase64, Encode_EmptyInput_WritesPrefix) {
EXPECT_EQ(1u, PrefixedBase64Encode(std::span<byte>(), base64_));
EXPECT_EQ('$', base64_[0]);
+ EXPECT_EQ('\0', base64_[1]);
}
TEST_F(PrefixedBase64, Encode_EmptyOutput_WritesNothing) {
@@ -114,7 +115,7 @@
}
TEST_F(PrefixedBase64, EncodeToVector_VectorTooSmall_OnlyNullTerminates) {
- constexpr byte big[Base64EncodedSize(
+ constexpr byte big[Base64EncodedBufferSize(
PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES + 1)] = {};
auto buffer = PrefixedBase64Encode(big);
@@ -122,6 +123,19 @@
EXPECT_EQ('\0', buffer[0]);
}
+TEST_F(PrefixedBase64, Base64EncodedBufferSize_Empty_RoomForPrefixAndNull) {
+ EXPECT_EQ(2u, Base64EncodedBufferSize(0));
+}
+
+TEST_F(PrefixedBase64, Base64EncodedBufferSize_PositiveSizes) {
+ for (unsigned i = 1; i <= 3; ++i) {
+ EXPECT_EQ(6u, Base64EncodedBufferSize(i));
+ }
+ for (unsigned i = 4; i <= 6; ++i) {
+ EXPECT_EQ(10u, Base64EncodedBufferSize(i));
+ }
+}
+
TEST_F(PrefixedBase64, Decode) {
for (auto& [binary, base64] : kTestData) {
EXPECT_EQ(binary.size(), PrefixedBase64Decode(base64, binary_));
diff --git a/pw_tokenizer/public/pw_tokenizer/base64.h b/pw_tokenizer/public/pw_tokenizer/base64.h
index 42d1f5e..73ae3c2 100644
--- a/pw_tokenizer/public/pw_tokenizer/base64.h
+++ b/pw_tokenizer/public/pw_tokenizer/base64.h
@@ -70,22 +70,24 @@
#include "pw_base64/base64.h"
#include "pw_containers/vector.h"
#include "pw_tokenizer/config.h"
+#include "pw_tokenizer/tokenize.h"
namespace pw::tokenizer {
inline constexpr char kBase64Prefix = PW_TOKENIZER_BASE64_PREFIX;
-// Returns the size of a Base64-encoded tokenized message. Includes the prefix
-// character ($) and the encoded data, but excludes the null terminator.
-constexpr size_t Base64EncodedSize(size_t data) {
- return sizeof(kBase64Prefix) + base64::EncodedSize(data);
+// Returns the size of a tokenized message (token + arguments) when encoded as
+// prefixed Base64. This can be used to size a buffer for encoding. Includes
+// room for the prefix character ($), encoded message, and a null terminator.
+constexpr size_t Base64EncodedBufferSize(size_t message_size) {
+ return sizeof(kBase64Prefix) + base64::EncodedSize(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 kBase64EncodedBufferSize =
- Base64EncodedSize(PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES) +
- sizeof('\0');
+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
@@ -109,29 +111,30 @@
// PW_TOKENIZER_CFG_ENCODING_BUFFER_SIZE_BYTES long encoded as prefixed Base64.
// The returned vector always contains a null-terminated Base64 string. If
// size() is zero, the binary message did not fit.
-template <size_t buffer_size = kBase64EncodedBufferSize>
+template <size_t buffer_size = kDefaultBase64EncodedBufferSize>
Vector<char, buffer_size> PrefixedBase64Encode(
std::span<const std::byte> binary_message) {
- static_assert(buffer_size >= Base64EncodedSize(sizeof(uint32_t)));
+ static_assert(buffer_size >= Base64EncodedBufferSize(sizeof(Token)),
+ "Buffer must be large enough for at least the uint32_t token");
Vector<char, buffer_size> output;
- const size_t encoded_size = Base64EncodedSize(binary_message.size());
+ const size_t encoded_size = Base64EncodedBufferSize(binary_message.size());
// Make sure the encoded data and a null terminator can fit.
- if (encoded_size + sizeof('\0') > buffer_size) {
+ if (buffer_size < encoded_size) {
output[0] = '\0';
return output;
}
- output.resize(encoded_size);
+ output.resize(encoded_size - 1); // exclude null terminator
output[0] = kBase64Prefix;
base64::Encode(binary_message, &output[1]);
- output[encoded_size] = '\0';
+ output[encoded_size - 1] = '\0';
return output;
}
// Encode to a pw::Vector from std::span<const uint8_t>.
-template <size_t buffer_size = kBase64EncodedBufferSize>
+template <size_t buffer_size = kDefaultBase64EncodedBufferSize>
Vector<char, buffer_size> PrefixedBase64Encode(
std::span<const uint8_t> binary_message) {
return PrefixedBase64Encode(std::as_bytes(binary_message));