mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
support base85 encoding/decoding
PiperOrigin-RevId: 725692677
This commit is contained in:
committed by
Copybara-Service
parent
7b31ce68c4
commit
58eea077e1
@@ -416,6 +416,7 @@ let package = Package(
|
||||
"internal/crypto/BUILD",
|
||||
"internal/crypto/BUILD.gn",
|
||||
"internal/crypto_cros/BUILD",
|
||||
"internal/encoding/BUILD",
|
||||
"internal/interop/BUILD",
|
||||
"internal/weave/BUILD",
|
||||
"internal/weave/sockets/BUILD",
|
||||
@@ -505,6 +506,7 @@ let package = Package(
|
||||
"internal/crypto_cros/sha2_unittest.cc",
|
||||
"internal/crypto_cros/signature_verifier_unittest.cc",
|
||||
"internal/crypto_cros/symmetric_key_unittest.cc",
|
||||
"internal/encoding/base85_test.cc",
|
||||
"internal/data/leveldb_data_set_test.cc",
|
||||
"internal/data/memory_data_set_test.cc",
|
||||
"internal/flags/nearby_flags_test.cc",
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright 2025 Google LLC
|
||||
#
|
||||
# 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.
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//:__subpackages__"])
|
||||
|
||||
cc_library(
|
||||
name = "base85",
|
||||
srcs = ["base85.cc"],
|
||||
hdrs = ["base85.h"],
|
||||
compatible_with = ["//buildenv/target:non_prod"],
|
||||
deps = [
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:string_view",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "base85_test",
|
||||
srcs = ["base85_test.cc"],
|
||||
deps = [
|
||||
":base85",
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "internal/encoding/base85.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace nearby {
|
||||
namespace encoding {
|
||||
namespace {
|
||||
|
||||
constexpr int kBase85CharsNumber = 85;
|
||||
constexpr uint8_t kBase85Zero = 'z';
|
||||
constexpr unsigned char kBase85EncodeChars[] = {
|
||||
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>',
|
||||
'?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
||||
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\',
|
||||
']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
|
||||
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u'};
|
||||
|
||||
constexpr uint8_t kBase85LastEncodeChar =
|
||||
kBase85EncodeChars[kBase85CharsNumber - 1];
|
||||
|
||||
constexpr uint8_t kBase85DecodeCharsCount[] = {0, 0, 1, 2, 3};
|
||||
|
||||
static uint8_t kBase85DecodeChars[256];
|
||||
|
||||
void InitBase85DecodeChars() {
|
||||
if (kBase85DecodeChars[kBase85LastEncodeChar]) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::fill(kBase85DecodeChars, kBase85DecodeChars + 256, 0xff);
|
||||
|
||||
for (uint8_t i = 0; i < kBase85CharsNumber; ++i) {
|
||||
kBase85DecodeChars[kBase85EncodeChars[i]] = i;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string Base85Encode(const std::string& input, bool padding) {
|
||||
size_t result_size =
|
||||
5 * (input.size() >> 2) + (((input.size() % 4) * 5 + 3) >> 2);
|
||||
if (padding) {
|
||||
result_size = 5 * (input.size() >> 2) + ((input.size() % 4 != 0) ? 5 : 0);
|
||||
}
|
||||
|
||||
std::string result;
|
||||
result.resize(result_size);
|
||||
char* result_buffer = result.data();
|
||||
char* result_buffer_end = result.data() + result.size();
|
||||
|
||||
size_t bytes = input.size();
|
||||
const char* data = input.data();
|
||||
while (bytes) {
|
||||
int count;
|
||||
uint32_t value = 0;
|
||||
for (count = 24; count >= 0; count -= 8) {
|
||||
uint32_t ch = *data++;
|
||||
value |= ch << count;
|
||||
if (--bytes == 0) break;
|
||||
}
|
||||
|
||||
if (value == 0) {
|
||||
// special case, use 'z' to encode it
|
||||
result_buffer[0] = kBase85Zero;
|
||||
++result_buffer;
|
||||
continue;
|
||||
}
|
||||
|
||||
int changed_count = 0;
|
||||
for (count = 4; count >= 0; count--) {
|
||||
int val = value % 85;
|
||||
value /= 85;
|
||||
if (result_buffer + count < result_buffer_end) {
|
||||
result_buffer[count] = kBase85EncodeChars[val];
|
||||
++changed_count;
|
||||
}
|
||||
}
|
||||
|
||||
result_buffer += changed_count;
|
||||
}
|
||||
|
||||
if (result_buffer != result_buffer_end) {
|
||||
// special case, may include z encoding
|
||||
return std::string(result.data(), result_buffer - result.data());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<std::string> Base85Decode(const std::string& input) {
|
||||
InitBase85DecodeChars();
|
||||
|
||||
size_t length = 0;
|
||||
size_t pos = 0;
|
||||
for (; pos < input.size();) {
|
||||
if (input[pos] == kBase85Zero) {
|
||||
// special zero case
|
||||
length += 4;
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
if (pos + 5 > input.size()) {
|
||||
size_t remain = input.size() - pos;
|
||||
if (remain == 1) {
|
||||
return std::nullopt;
|
||||
}
|
||||
length += kBase85DecodeCharsCount[remain];
|
||||
break;
|
||||
}
|
||||
pos += 5;
|
||||
length += 4;
|
||||
}
|
||||
|
||||
std::string result;
|
||||
result.resize(length);
|
||||
|
||||
const char* input_buffer = input.data();
|
||||
const char* input_buffer_end = input_buffer + input.size();
|
||||
char* result_buffer = result.data();
|
||||
char* result_buffer_end = result_buffer + result.size();
|
||||
|
||||
while (length) {
|
||||
int count = 4;
|
||||
int decoded_char;
|
||||
uint8_t input_char;
|
||||
uint32_t value = 0;
|
||||
do {
|
||||
if (input_buffer < input_buffer_end) {
|
||||
input_char = *input_buffer++;
|
||||
// handle special case 'z'
|
||||
if (count == 4 && input_char == kBase85Zero) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
*result_buffer++ = 0x00;
|
||||
}
|
||||
length -= 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
decoded_char = kBase85DecodeChars[input_char];
|
||||
if (decoded_char == 0xff) return std::nullopt;
|
||||
} else {
|
||||
decoded_char = kBase85LastEncodeChar;
|
||||
}
|
||||
value = value * 85 + decoded_char;
|
||||
} while (--count);
|
||||
if (input_buffer < input_buffer_end) {
|
||||
input_char = *input_buffer++;
|
||||
decoded_char = kBase85DecodeChars[input_char];
|
||||
if (decoded_char == 0xff) return std::nullopt;
|
||||
} else {
|
||||
decoded_char = kBase85LastEncodeChar;
|
||||
}
|
||||
/* Detect overflow. */
|
||||
if (0xffffffff / 85 < value || 0xffffffff - decoded_char < (value *= 85))
|
||||
return std::nullopt;
|
||||
value += decoded_char;
|
||||
|
||||
count = (length < 4) ? length : 4;
|
||||
length -= count;
|
||||
do {
|
||||
value = (value << 8) | (value >> 24);
|
||||
if (result_buffer < result_buffer_end) {
|
||||
*result_buffer++ = value;
|
||||
}
|
||||
} while (--count);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace encoding
|
||||
} // namespace nearby
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef THIRD_PARTY_NEARBY_INTERNAL_ENCODING_BASE85_H_
|
||||
#define THIRD_PARTY_NEARBY_INTERNAL_ENCODING_BASE85_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace nearby {
|
||||
namespace encoding {
|
||||
|
||||
// Encodes the input string to Base85. If pad is true, the output string will
|
||||
// not be truncated.
|
||||
std::string Base85Encode(const std::string& input, bool padding = false);
|
||||
|
||||
// Decodes the input string from Base85.
|
||||
std::optional<std::string> Base85Decode(const std::string& input);
|
||||
|
||||
} // namespace encoding
|
||||
} // namespace nearby
|
||||
|
||||
#endif // THIRD_PARTY_NEARBY_INTERNAL_ENCODING_BASE85_H_
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "internal/encoding/base85.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace encoding {
|
||||
namespace {
|
||||
|
||||
TEST(Base85Test, EncodeAndDecodeString) {
|
||||
EXPECT_EQ(Base85Encode(""), "");
|
||||
EXPECT_EQ(Base85Decode(Base85Encode("")), "");
|
||||
EXPECT_EQ(Base85Encode("b"), "@K");
|
||||
EXPECT_EQ(Base85Decode(Base85Encode("b")), "b");
|
||||
EXPECT_EQ(Base85Encode("ab"), "@:B");
|
||||
EXPECT_EQ(Base85Decode(Base85Encode("ab")), "ab");
|
||||
EXPECT_EQ(Base85Encode("xyz"), "G^4T");
|
||||
EXPECT_EQ(Base85Decode(Base85Encode("xyz")), "xyz");
|
||||
EXPECT_EQ(Base85Encode("true"), "FE2M8");
|
||||
EXPECT_EQ(Base85Decode(Base85Encode("true")), "true");
|
||||
EXPECT_EQ(Base85Encode("ab", /*padding=*/true), "@:B3:");
|
||||
std::string expected_decoded_string = "ab";
|
||||
expected_decoded_string.push_back('\0');
|
||||
expected_decoded_string.push_back('\0');
|
||||
EXPECT_EQ(Base85Decode(Base85Encode("ab", /*padding=*/true)),
|
||||
expected_decoded_string);
|
||||
EXPECT_EQ(Base85Encode("hello,world"), "BOu!rD_-*NEbo7");
|
||||
EXPECT_EQ(Base85Decode("BOu!rD_-*NEbo7"), "hello,world");
|
||||
EXPECT_EQ(Base85Encode("abcdefgh"), "@:E_WAS,Rg");
|
||||
EXPECT_EQ(Base85Decode(Base85Encode("abcdefgh")), "abcdefgh");
|
||||
}
|
||||
|
||||
TEST(Base85Test, EncodeAndDecodeBytes) {
|
||||
std::string data;
|
||||
data.push_back(0x00);
|
||||
EXPECT_EQ(Base85Encode(data), "z");
|
||||
std::string expected_data;
|
||||
expected_data.push_back(0x00);
|
||||
expected_data.push_back(0x00);
|
||||
expected_data.push_back(0x00);
|
||||
expected_data.push_back(0x00);
|
||||
EXPECT_EQ(Base85Decode(Base85Encode(data)), expected_data);
|
||||
data.clear();
|
||||
data.push_back(0x00);
|
||||
data.push_back(0x01);
|
||||
EXPECT_EQ(Base85Encode(data), "!!*");
|
||||
data.clear();
|
||||
data.push_back(0x00);
|
||||
data.push_back(0x01);
|
||||
data.push_back(0xff);
|
||||
EXPECT_EQ(Base85Encode(data), "!!3*");
|
||||
EXPECT_EQ(Base85Decode(Base85Encode(data)), data);
|
||||
}
|
||||
|
||||
TEST(Base85Test, InvalideEncodedString) {
|
||||
EXPECT_FALSE(Base85Decode("abhjki"));
|
||||
EXPECT_FALSE(Base85Decode("abz"));
|
||||
EXPECT_FALSE(Base85Decode("u`qL["));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace encoding
|
||||
} // namespace nearby
|
||||
Reference in New Issue
Block a user