Skip to content

Commit

Permalink
Introduce utility class for encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
Joe-Abraham committed Aug 13, 2024
1 parent 2abfadf commit 8b8b9e1
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 82 deletions.
61 changes: 12 additions & 49 deletions velox/common/encode/Base64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@

namespace facebook::velox::encoding {

// Encoding base to be used.
constexpr static int kBase = 64;

// Constants defining the size in bytes of binary and encoded blocks for Base64
// encoding.
// Size of a binary block in bytes (3 bytes = 24 bits)
Expand Down Expand Up @@ -87,15 +90,6 @@ constexpr const Base64::ReverseIndex kBase64UrlReverseIndexTable = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255};

// Validate the character in charset with ReverseIndex table
constexpr bool checkForwardIndex(
uint8_t idx,
const Base64::Charset& charset,
const Base64::ReverseIndex& reverseIndex) {
return (reverseIndex[static_cast<uint8_t>(charset[idx])] == idx) &&
(idx > 0 ? checkForwardIndex(idx - 1, charset, reverseIndex) : true);
}

// Verify that for every entry in kBase64Charset, the corresponding entry
// in kBase64ReverseIndexTable is correct.
static_assert(
Expand All @@ -114,32 +108,12 @@ static_assert(
kBase64UrlReverseIndexTable),
"kBase64UrlCharset has incorrect entries");

// Searches for a character within a charset up to a certain index.
constexpr bool findCharacterInCharset(
const Base64::Charset& charset,
uint8_t idx,
const char c) {
return idx < charset.size() &&
((charset[idx] == c) || findCharacterInCharset(charset, idx + 1, c));
}

// Checks the consistency of a reverse index mapping for a given character
// set.
constexpr bool checkReverseIndex(
uint8_t idx,
const Base64::Charset& charset,
const Base64::ReverseIndex& reverseIndex) {
return (reverseIndex[idx] == 255
? !findCharacterInCharset(charset, 0, static_cast<char>(idx))
: (charset[reverseIndex[idx]] == idx)) &&
(idx > 0 ? checkReverseIndex(idx - 1, charset, reverseIndex) : true);
}

// Verify that for every entry in kBase64ReverseIndexTable, the corresponding
// entry in kBase64Charset is correct.
static_assert(
checkReverseIndex(
sizeof(kBase64ReverseIndexTable) - 1,
kBase,
kBase64Charset,
kBase64ReverseIndexTable),
"kBase64ReverseIndexTable has incorrect entries.");
Expand Down Expand Up @@ -326,17 +300,6 @@ void Base64::decode(const char* data, size_t size, char* output) {
Base64::decode(data, size, output, out_len);
}

// static
uint8_t Base64::base64ReverseLookup(
char p,
const Base64::ReverseIndex& reverseIndex) {
auto curr = reverseIndex[(uint8_t)p];
if (curr >= 0x40) {
VELOX_USER_FAIL("decode() - invalid input string: invalid characters");
}
return curr;
}

// static
size_t
Base64::decode(const char* src, size_t src_len, char* dst, size_t dst_len) {
Expand Down Expand Up @@ -409,10 +372,10 @@ size_t Base64::decodeImpl(
// Each character of the 4 encode 6 bits of the original, grab each with
// the appropriate shifts to rebuild the original and then split that back
// into the original 8 bit bytes.
uint32_t last = (base64ReverseLookup(src[0], reverseIndex) << 18) |
(base64ReverseLookup(src[1], reverseIndex) << 12) |
(base64ReverseLookup(src[2], reverseIndex) << 6) |
base64ReverseLookup(src[3], reverseIndex);
uint32_t last = (baseReverseLookup(kBase, src[0], reverseIndex) << 18) |
(baseReverseLookup(kBase, src[1], reverseIndex) << 12) |
(baseReverseLookup(kBase, src[2], reverseIndex) << 6) |
baseReverseLookup(kBase, src[3], reverseIndex);
dst[0] = (last >> 16) & 0xff;
dst[1] = (last >> 8) & 0xff;
dst[2] = last & 0xff;
Expand All @@ -421,14 +384,14 @@ size_t Base64::decodeImpl(
// Handle the last 2-4 characters. This is similar to the above, but the
// last 2 characters may or may not exist.
DCHECK(src_len >= 2);
uint32_t last = (base64ReverseLookup(src[0], reverseIndex) << 18) |
(base64ReverseLookup(src[1], reverseIndex) << 12);
uint32_t last = (baseReverseLookup(kBase, src[0], reverseIndex) << 18) |
(baseReverseLookup(kBase, src[1], reverseIndex) << 12);
dst[0] = (last >> 16) & 0xff;
if (src_len > 2) {
last |= base64ReverseLookup(src[2], reverseIndex) << 6;
last |= baseReverseLookup(kBase, src[2], reverseIndex) << 6;
dst[1] = (last >> 8) & 0xff;
if (src_len > 3) {
last |= base64ReverseLookup(src[3], reverseIndex);
last |= baseReverseLookup(kBase, src[3], reverseIndex);
dst[2] = last & 0xff;
}
}
Expand Down
23 changes: 1 addition & 22 deletions velox/common/encode/Base64.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <folly/io/IOBuf.h>

#include "velox/common/base/GTestMacros.h"
#include "velox/common/encode/EncoderUtils.h"

namespace facebook::velox::encoding {

Expand Down Expand Up @@ -112,25 +113,6 @@ class Base64 {
decodeUrl(const char* src, size_t src_len, char* dst, size_t dst_len);

private:
/// Checks if there is padding in encoded data.
static inline bool isPadded(const char* data, size_t len) {
return (len > 0 && data[len - 1] == kPadding);
}

/// Counts the number of padding characters in encoded data.
static inline size_t numPadding(const char* src, size_t len) {
size_t numPadding{0};
while (len > 0 && src[len - 1] == kPadding) {
numPadding++;
len--;
}
return numPadding;
}

/// Performs a reverse lookup in the reverse index to retrieve the original
/// index of a character in the base.
static uint8_t base64ReverseLookup(char p, const ReverseIndex& reverseIndex);

/// Encodes the specified data using the provided charset.
template <class T>
static std::string
Expand All @@ -151,9 +133,6 @@ class Base64 {
char* dst,
size_t dst_len,
const ReverseIndex& table);

VELOX_FRIEND_TEST(Base64Test, checksPadding);
VELOX_FRIEND_TEST(Base64Test, countsPaddingCorrectly);
};

} // namespace facebook::velox::encoding
117 changes: 117 additions & 0 deletions velox/common/encode/EncoderUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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
*
* http://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.
*/
#pragma once

#include <exception>
#include <map>
#include <string>

#include <folly/Range.h>

#include "velox/common/base/Exceptions.h"

namespace facebook::velox::encoding {

const size_t kCharsetSize = 64;
const size_t kReverseIndexSize = 256;

/// Character set used for encoding purposes.
/// Contains specific characters that form the encoding scheme.
using Charset = std::array<char, kCharsetSize>;

/// Reverse lookup table for decoding purposes.
/// Maps each possible encoded character to its corresponding numeric value
/// within the encoding base.
using ReverseIndex = std::array<uint8_t, kReverseIndexSize>;

/// Padding character used in encoding.
const static char kPadding = '=';
/// Checks if there is padding in encoded data.
static inline bool isPadded(const char* data, size_t len) {
return (len > 0 && data[len - 1] == kPadding) ? true : false;
}

/// Counts the number of padding characters in encoded data.
static inline size_t numPadding(const char* src, size_t len) {
size_t numPadding{0};
while (len > 0 && src[len - 1] == kPadding) {
numPadding++;
len--;
}
return numPadding;
}
/// Performs a reverse lookup in the reverse index to retrieve the original
/// index of a character in the base.
inline uint8_t
baseReverseLookup(int base, char p, const ReverseIndex& reverseIndex) {
auto curr = reverseIndex[(uint8_t)p];
if (curr >= base) {
VELOX_USER_FAIL("decode() - invalid input string: invalid characters");
}
return curr;
}

// Validate the character in charset with ReverseIndex table
static constexpr bool checkForwardIndex(
uint8_t idx,
const Charset& charset,
const ReverseIndex& reverseIndex) {
for (uint8_t i = 0; i <= idx; ++i) {
if (!(reverseIndex[static_cast<uint8_t>(charset[i])] == i)) {
return false;
}
}
return true;
}

/// Searches for a character within a charset up to a certain index.
constexpr bool findCharacterInCharSet(
const Charset& charset,
int base,
uint8_t idx,
const char c) {
for (; idx < base; ++idx) {
if (charset[idx] == c) {
return true;
}
}
return false;
}

/// Checks the consistency of a reverse index mapping for a given character
/// set.
static constexpr bool checkReverseIndex(
uint8_t idx,
int base,
const Charset& charset,
const ReverseIndex& reverseIndex) {
for (uint8_t currentIdx = idx; currentIdx != static_cast<uint8_t>(-1);
--currentIdx) {
if (reverseIndex[currentIdx] == 255) {
if (findCharacterInCharSet(
charset, base, 0, static_cast<char>(currentIdx))) {
return false;
}
} else {
if (!(charset[reverseIndex[currentIdx]] == currentIdx)) {
return false;
}
}
}
return true;
}

} // namespace facebook::velox::encoding
10 changes: 0 additions & 10 deletions velox/common/encode/tests/Base64Test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,4 @@ TEST_F(Base64Test, calculateDecodedSizeProperSize) {
EXPECT_EQ(14, encoded_size);
}

TEST_F(Base64Test, checksPadding) {
EXPECT_TRUE(Base64::isPadded("ABC=", 4));
EXPECT_FALSE(Base64::isPadded("ABC", 3));
}

TEST_F(Base64Test, countsPaddingCorrectly) {
EXPECT_EQ(0, Base64::numPadding("ABC", 3));
EXPECT_EQ(1, Base64::numPadding("ABC=", 4));
EXPECT_EQ(2, Base64::numPadding("AB==", 4));
}
} // namespace facebook::velox::encoding
2 changes: 1 addition & 1 deletion velox/common/encode/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

add_executable(velox_common_encode_test Base64Test.cpp)
add_executable(velox_common_encode_test Base64Test.cpp EncoderUtilsTests.cpp)
add_test(velox_common_encode_test velox_common_encode_test)
target_link_libraries(
velox_common_encode_test
Expand Down
Loading

0 comments on commit 8b8b9e1

Please sign in to comment.