Add Base64::EncodedSize to tidy up allocations (flutter/engine#46624)

As a follow-up to #46543, this adds a dedicated function to compute the
size of the buffer needed to encode data using Base64 instead of calling
the encode function with nullptr.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide] and the [C++,
Objective-C, Java style guides].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [x] I added new tests to check the change I am making or feature I am
adding, or the PR is [test-exempt]. See [testing the engine] for
instructions on writing and running engine tests.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I signed the [CLA].
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
[test-exempt]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
[C++, Objective-C, Java style guides]:
https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
[testing the engine]:
https://github.com/flutter/flutter/wiki/Testing-the-engine
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
[Discord]: https://github.com/flutter/flutter/wiki/Chat
This commit is contained in:
Kevin Lubick
2023-10-06 16:50:20 -04:00
committed by GitHub
parent f29ce2aa7a
commit 5d70f10ebb
6 changed files with 48 additions and 49 deletions

View File

@@ -111,10 +111,7 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) {
wstream.write(snapshot_data->data(), snapshot_data->size());
wstream.flush();
// TODO(kjlubick) We shouldn't need to call Encode once to pre-flight the
// encode length. It should be ceil(4/3 * sksl.value->size()).
size_t b64_size =
Base64::Encode(snapshot_data->data(), snapshot_data->size(), nullptr);
size_t b64_size = Base64::EncodedSize(snapshot_data->size());
sk_sp<SkData> b64_data = SkData::MakeUninitialized(b64_size + 1);
char* b64_char = static_cast<char*>(b64_data->writable_data());
Base64::Encode(snapshot_data->data(), snapshot_data->size(), b64_char);

View File

@@ -116,42 +116,41 @@ Base64::Error Base64::Decode(const void* srcv,
}
size_t Base64::Encode(const void* srcv, size_t length, void* dstv) {
FML_DCHECK(dstv);
const unsigned char* src = static_cast<const unsigned char*>(srcv);
unsigned char* dst = static_cast<unsigned char*>(dstv);
const char* encode = kDefaultEncode;
if (dst) {
size_t remainder = length % 3;
char unsigned const* const end = &src[length - remainder];
while (src < end) {
unsigned a = *src++;
unsigned b = *src++;
unsigned c = *src++;
int d = c & 0x3F;
c = (c >> 6 | b << 2) & 0x3F;
b = (b >> 4 | a << 4) & 0x3F;
a = a >> 2;
*dst++ = encode[a];
*dst++ = encode[b];
*dst++ = encode[c];
*dst++ = encode[d];
}
if (remainder > 0) {
int k1 = 0;
int k2 = EncodePad;
int a = (uint8_t)*src++;
if (remainder == 2) {
int b = *src++;
k1 = b >> 4;
k2 = (b << 2) & 0x3F;
}
*dst++ = encode[a >> 2];
*dst++ = encode[(k1 | a << 4) & 0x3F];
*dst++ = encode[k2];
*dst++ = encode[EncodePad];
}
size_t remainder = length % 3;
char unsigned const* const end = &src[length - remainder];
while (src < end) {
unsigned a = *src++;
unsigned b = *src++;
unsigned c = *src++;
int d = c & 0x3F;
c = (c >> 6 | b << 2) & 0x3F;
b = (b >> 4 | a << 4) & 0x3F;
a = a >> 2;
*dst++ = encode[a];
*dst++ = encode[b];
*dst++ = encode[c];
*dst++ = encode[d];
}
return (length + 2) / 3 * 4;
if (remainder > 0) {
int k1 = 0;
int k2 = EncodePad;
int a = (uint8_t)*src++;
if (remainder == 2) {
int b = *src++;
k1 = b >> 4;
k2 = (b << 2) & 0x3F;
}
*dst++ = encode[a >> 2];
*dst++ = encode[(k1 | a << 4) & 0x3F];
*dst++ = encode[k2];
*dst++ = encode[EncodePad];
}
return EncodedSize(length);
}
} // namespace flutter

View File

@@ -20,21 +20,27 @@ struct Base64 {
/**
Base64 encodes src into dst.
Normally this is called once with 'dst' nullptr to get the required size,
then again with an allocated 'dst' pointer to do the actual encoding.
@param dst nullptr or a pointer to a buffer large enough to receive the
result
@param dst a pointer to a buffer large enough to receive the result.
@return the required length of dst for encoding.
*/
static size_t Encode(const void* src, size_t length, void* dst);
/**
Returns the length of the buffer that needs to be allocated to encode
srcDataLength bytes.
*/
static size_t EncodedSize(size_t srcDataLength) {
// Take the floor of division by 3 to find the number of groups that need to
// be encoded. Each group takes 4 bytes to be represented in base64.
return ((srcDataLength + 2) / 3) * 4;
}
/**
Base64 decodes src into dst.
Normally this is called once with 'dst' nullptr to get the required size,
then again with an allocated 'dst' pointer to do the actual encoding.
This can be called once with 'dst' nullptr to get the required size,
then again with an allocated 'dst' pointer to do the actual decoding.
@param dst nullptr or a pointer to a buffer large enough to receive the
result

View File

@@ -17,6 +17,7 @@ TEST(Base64, EncodeStrings) {
char buffer[256];
size_t len = Base64::Encode(input.c_str(), input.length(), &buffer);
FML_CHECK(len <= 256);
ASSERT_EQ(len, Base64::EncodedSize(input.length()));
std::string actual(buffer, len);
ASSERT_STREQ(actual.c_str(), output.c_str());
};
@@ -34,6 +35,7 @@ TEST(Base64, EncodeBytes) {
char buffer[512];
size_t len = Base64::Encode(input, num, &buffer);
FML_CHECK(len <= 512);
ASSERT_EQ(len, Base64::EncodedSize(num));
std::string actual(buffer, len);
ASSERT_STREQ(actual.c_str(), output.c_str());
};

View File

@@ -910,9 +910,7 @@ Rasterizer::Screenshot Rasterizer::ScreenshotLastLayerTree(
}
if (base64_encode) {
// TODO(kjlubick) We shouldn't need to call Encode once to pre-flight the
// encode length. It should be ceil(4/3 * sksl.value->size()).
size_t b64_size = Base64::Encode(data->data(), data->size(), nullptr);
size_t b64_size = Base64::EncodedSize(data->size());
auto b64_data = SkData::MakeUninitialized(b64_size);
Base64::Encode(data->data(), data->size(), b64_data->writable_data());
return Rasterizer::Screenshot{b64_data, layer_tree->frame_size(), format};

View File

@@ -1840,10 +1840,7 @@ bool Shell::OnServiceProtocolGetSkSLs(
PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess();
std::vector<PersistentCache::SkSLCache> sksls = persistent_cache->LoadSkSLs();
for (const auto& sksl : sksls) {
// TODO(kjlubick) We shouldn't need to call Encode once to pre-flight the
// encode length. It should be ceil(4/3 * sksl.value->size()).
size_t b64_size =
Base64::Encode(sksl.value->data(), sksl.value->size(), nullptr);
size_t b64_size = Base64::EncodedSize(sksl.value->size());
sk_sp<SkData> b64_data = SkData::MakeUninitialized(b64_size + 1);
char* b64_char = static_cast<char*>(b64_data->writable_data());
Base64::Encode(sksl.value->data(), sksl.value->size(), b64_char);