Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2aba42dd3ba0
Choose a base ref
...
head repository: mockingbirdnest/Principia
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: dda9bf9f4f5a
Choose a head ref
  • 7 commits
  • 8 files changed
  • 1 contributor

Commits on Dec 15, 2018

  1. Base 64 implementation.

    pleroy committed Dec 15, 2018
    Copy the full SHA
    39cd8ba View commit details
  2. A test.

    pleroy committed Dec 15, 2018
    Copy the full SHA
    33ba500 View commit details
  3. Benchmark.

    pleroy committed Dec 15, 2018
    Copy the full SHA
    7be3931 View commit details
  4. Merge.

    pleroy committed Dec 15, 2018
    Copy the full SHA
    4312cce View commit details
  5. Lint.

    pleroy committed Dec 15, 2018
    Copy the full SHA
    b8dc720 View commit details
  6. After egg's review.

    pleroy committed Dec 15, 2018
    Copy the full SHA
    0095159 View commit details

Commits on Dec 16, 2018

  1. Merge pull request #2028 from pleroy/Base64

    An encoder for base 64
    pleroy authored Dec 16, 2018
    Copy the full SHA
    dda9bf9 View commit details
Showing with 205 additions and 17 deletions.
  1. +3 −0 base/base.vcxproj
  2. +9 −0 base/base.vcxproj.filters
  3. +39 −0 base/base64.hpp
  4. +81 −0 base/base64_body.hpp
  5. +53 −0 base/base64_test.cpp
  6. +8 −9 base/encoder.hpp
  7. +8 −8 base/hexadecimal.hpp
  8. +4 −0 benchmarks/encoder.cpp
3 changes: 3 additions & 0 deletions base/base.vcxproj
Original file line number Diff line number Diff line change
@@ -24,6 +24,8 @@
<ClInclude Include="array_body.hpp" />
<ClInclude Include="base32768.hpp" />
<ClInclude Include="base32768_body.hpp" />
<ClInclude Include="base64.hpp" />
<ClInclude Include="base64_body.hpp" />
<ClInclude Include="bundle.hpp" />
<ClInclude Include="disjoint_sets.hpp" />
<ClInclude Include="disjoint_sets_body.hpp" />
@@ -72,6 +74,7 @@
<ItemGroup>
<ClCompile Include="array_test.cpp" />
<ClCompile Include="base32768_test.cpp" />
<ClCompile Include="base64_test.cpp" />
<ClCompile Include="bundle.cpp" />
<ClCompile Include="bundle_test.cpp" />
<ClCompile Include="disjoint_sets_test.cpp" />
9 changes: 9 additions & 0 deletions base/base.vcxproj.filters
Original file line number Diff line number Diff line change
@@ -158,6 +158,12 @@
<ClInclude Include="encoder.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="base64.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="base64_body.hpp">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="not_null_test.cpp">
@@ -205,5 +211,8 @@
<ClCompile Include="base32768_test.cpp">
<Filter>Test Files</Filter>
</ClCompile>
<ClCompile Include="base64_test.cpp">
<Filter>Test Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
39 changes: 39 additions & 0 deletions base/base64.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

#pragma once

#include <cstdint>

#include "base/encoder.hpp"

namespace principia {
namespace base {
namespace internal_base64 {

// This function implements RFC 4648 section 5 (base64url). The encoded text is
// *not* padded.
template<bool null_terminated>
class Base64Encoder : public Encoder<char, null_terminated> {
public:
void Encode(Array<std::uint8_t const> input,
Array<char> output) override;

UniqueArray<char> Encode(Array<std::uint8_t const> input) override;

std::int64_t EncodedLength(Array<std::uint8_t const> input) override;

void Decode(Array<char const> input,
Array<std::uint8_t> output) override;

UniqueArray<std::uint8_t> Decode(Array<char const> input) override;

std::int64_t DecodedLength(Array<char const> input) override;
};

} // namespace internal_base64

using internal_base64::Base64Encoder;

} // namespace base
} // namespace principia

#include "base/base64_body.hpp"
81 changes: 81 additions & 0 deletions base/base64_body.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@

#pragma once

#include "base/base64.hpp"

#include <string>

#include "absl/strings/escaping.h"

namespace principia {
namespace base {
namespace internal_base64 {

constexpr std::int64_t bits_per_byte = 8;
constexpr std::int64_t bits_per_char = 6;

template<bool null_terminated>
void principia::base::internal_base64::Base64Encoder<null_terminated>::Encode(
Array<std::uint8_t const> input,
Array<char> output) {
std::string_view const input_view(reinterpret_cast<const char*>(input.data),
input.size);
std::string output_string;
absl::WebSafeBase64Escape(input_view, &output_string);
if constexpr (null_terminated) {
std::memcpy(output.data, output_string.c_str(), output_string.size() + 1);
} else {
std::memcpy(output.data, output_string.c_str(), output_string.size());
}
}

template<bool null_terminated>
UniqueArray<char> Base64Encoder<null_terminated>::Encode(
Array<std::uint8_t const> input) {
UniqueArray<char> output(EncodedLength(input));
if (output.size > 0) {
Encode(input, output.get());
}
return output;
}

template<bool null_terminated>
std::int64_t Base64Encoder<null_terminated>::EncodedLength(
Array<std::uint8_t const> input) {
std::int64_t const nonterminated_length =
(bits_per_byte * input.size + bits_per_char - 1) / bits_per_char;
if constexpr (null_terminated) {
return nonterminated_length + 1;
} else {
return nonterminated_length;
}
}

template<bool null_terminated>
void Base64Encoder<null_terminated>::Decode(Array<char const> input,
Array<std::uint8_t> output) {
std::string_view const input_view(input.data, input.size);
std::string output_string;
absl::WebSafeBase64Unescape(input_view, &output_string);
std::memcpy(output.data, output_string.c_str(), output_string.size());
}

template<bool null_terminated>
UniqueArray<std::uint8_t> Base64Encoder<null_terminated>::Decode(
Array<char const> input) {
UniqueArray<std::uint8_t> output(DecodedLength(input));
if (output.size > 0) {
Decode(input, output.get());
}
return output;
}

template<bool null_terminated>
std::int64_t Base64Encoder<null_terminated>::DecodedLength(
Array<char const> input) {
return bits_per_char * input.size / bits_per_byte;
}

} // namespace internal_base64
} // namespace base
} // namespace principia
53 changes: 53 additions & 0 deletions base/base64_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@

#include "base/base64.hpp"

#include <string>

#include "gtest/gtest.h"

namespace principia {
namespace base {

class Base64Test : public ::testing::Test {
protected:
Base64Encoder</*null_terminated=*/false> encoder_;
};

TEST_F(Base64Test, WellKnownData) {
// From https://en.wikipedia.org/wiki/Base64.
std::string const decoded_string =
"Man is distinguished, not only by his reason, but by this singular "
"passion from other animals, which is a lust of the mind, that by a "
"perseverance of delight in the continued and indefatigable generation "
"of knowledge, exceeds the short vehemence of any carnal pleasure.";
std::string const encoded_string =
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0"
"aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1"
"c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0"
"aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdl"
"LCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4";

{
Array<std::uint8_t const> decoded_array(
reinterpret_cast<std::uint8_t const*>(decoded_string.c_str()),
decoded_string.size());
auto const encoded_array = encoder_.Encode(decoded_array);
EXPECT_EQ(encoded_string.size(), encoder_.EncodedLength(decoded_array));
for (std::int64_t i = 0; i < encoded_array.size; ++i) {
EXPECT_EQ(encoded_string[i], encoded_array.data[i]) << i;
}
}
{
Array<char const> encoded_array(encoded_string.c_str(),
encoded_string.size());
auto const decoded_array = encoder_.Decode(encoded_array);
EXPECT_EQ(decoded_string.size(), encoder_.DecodedLength(encoded_array));
for (std::int64_t i = 0; i < decoded_array.size; ++i) {
EXPECT_EQ(decoded_string[i],
static_cast<char>(decoded_array.data[i])) << i;
}
}
}

} // namespace base
} // namespace principia
17 changes: 8 additions & 9 deletions base/encoder.hpp
Original file line number Diff line number Diff line change
@@ -21,26 +21,25 @@ class Encoder {

// Encodes |input| into |output|, which must be large enough to hold the
// encoded form.
inline virtual void Encode(Array<std::uint8_t const> input,
Array<Char> output) = 0;
virtual void Encode(Array<std::uint8_t const> input,
Array<Char> output) = 0;

// Same as above but the storage is allocated by the callee.
inline virtual UniqueArray<Char> Encode(Array<std::uint8_t const> input) = 0;
virtual UniqueArray<Char> Encode(Array<std::uint8_t const> input) = 0;

// Length of the encoded form, in Char.
inline virtual std::int64_t
EncodedLength(Array<std::uint8_t const> input) = 0;
virtual std::int64_t EncodedLength(Array<std::uint8_t const> input) = 0;

// Decodes |input| into |output|, which must be large enough to hold the
// decoded form. The input may or may not be null-terminated.
inline virtual void Decode(Array<Char const> input,
Array<std::uint8_t> output) = 0;
virtual void Decode(Array<Char const> input,
Array<std::uint8_t> output) = 0;

// Same as above but the storage is allocated by the callee.
inline virtual UniqueArray<std::uint8_t> Decode(Array<Char const> input) = 0;
virtual UniqueArray<std::uint8_t> Decode(Array<Char const> input) = 0;

// Length of the decoded form, in uint8_t.
inline virtual std::int64_t DecodedLength(Array<Char const> input) = 0;
virtual std::int64_t DecodedLength(Array<Char const> input) = 0;
};

} // namespace internal_encoder
16 changes: 8 additions & 8 deletions base/hexadecimal.hpp
Original file line number Diff line number Diff line change
@@ -18,25 +18,25 @@ class HexadecimalEncoder : public Encoder<char, null_terminated> {
// |input.size|. The range
// [&output.data[input.size << 1], &output.data[output.size][ is left
// unmodified.
inline void Encode(Array<std::uint8_t const> input,
Array<char> output) override;
void Encode(Array<std::uint8_t const> input,
Array<char> output) override;

inline UniqueArray<char> Encode(Array<std::uint8_t const> input) override;
UniqueArray<char> Encode(Array<std::uint8_t const> input) override;

inline std::int64_t EncodedLength(Array<std::uint8_t const> input) override;
std::int64_t EncodedLength(Array<std::uint8_t const> input) override;

// Invalid digits are read as 0. If |input.size| is odd, the last character
// of the input is ignored. Ignores case. Either |output.data <=
// &input.data[1]| or |&input.data[input.size & ~1] <= output.data| must hold,
// in particular, |input.data == output.data| is valid. |output.size| must be
// at least |input.size / 2|. The range
// [&output[input.size / 2], &output[output.size][ is left unmodified.
inline void Decode(Array<char const> input,
Array<std::uint8_t> output) override;
void Decode(Array<char const> input,
Array<std::uint8_t> output) override;

inline UniqueArray<std::uint8_t> Decode(Array<char const> input) override;
UniqueArray<std::uint8_t> Decode(Array<char const> input) override;

inline std::int64_t DecodedLength(Array<char const> input) override;
std::int64_t DecodedLength(Array<char const> input) override;
};

} // namespace internal_hexadecimal
4 changes: 4 additions & 0 deletions benchmarks/encoder.cpp
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
#include <random>

#include "base/array.hpp"
#include "base/base64.hpp"
#include "base/base32768.hpp"
#include "base/hexadecimal.hpp"
#include "benchmark/benchmark.h"
@@ -93,10 +94,13 @@ void BM_Decode(benchmark::State& state) {
}

using Encoder16 = HexadecimalEncoder</*null_terminated=*/false>;
using Encoder64 = Base64Encoder</*null_terminated=*/false>;
using Encoder32768 = Base32768Encoder</*null_terminated=*/false>;

BENCHMARK_TEMPLATE(BM_Encode, Encoder16);
BENCHMARK_TEMPLATE(BM_Decode, Encoder16);
BENCHMARK_TEMPLATE(BM_Encode, Encoder64);
BENCHMARK_TEMPLATE(BM_Decode, Encoder64);
#if !PRINCIPIA_COMPILER_MSVC || \
!(_MSC_FULL_VER == 191526608 || \
_MSC_FULL_VER == 191526731 || \