Refactors Base64 encoding and decoding, it's almost beautiful now

This commit is contained in:
Daniel J. Hofmann 2016-02-02 14:49:41 +01:00
parent ec01c2a119
commit aac21f932b
5 changed files with 65 additions and 52 deletions

View File

@ -277,10 +277,10 @@ ApiResponseGenerator<DataFacadeT>::BuildHintData(const InternalRouteResult &raw_
std::string hint;
for (const auto i : util::irange<std::size_t>(0, raw_route.segment_end_coordinates.size()))
{
ObjectEncoder::EncodeToBase64(raw_route.segment_end_coordinates[i].source_phantom, hint);
json_location_hint_array.values.push_back(hint);
hint = encodeBase64(raw_route.segment_end_coordinates[i].source_phantom);
json_location_hint_array.values.push_back(std::move(hint));
}
ObjectEncoder::EncodeToBase64(raw_route.segment_end_coordinates.back().target_phantom, hint);
hint = encodeBase64(raw_route.segment_end_coordinates.back().target_phantom);
json_location_hint_array.values.emplace_back(std::move(hint));
json_hint_object.values["locations"] = json_location_hint_array;

View File

@ -11,59 +11,75 @@
#include <string>
#include <vector>
#include <cstdint>
#include <climits>
namespace osrm
{
namespace engine
{
struct ObjectEncoder
namespace detail
{
using base64_t = boost::archive::iterators::base64_from_binary<
boost::archive::iterators::transform_width<const char *, 6, 8>>;
static_assert(CHAR_BIT == 8u, "we assume a byte holds 8 bits");
static_assert(sizeof(char) == 1u, "we assume a char is one byte large");
using binary_t = boost::archive::iterators::transform_width<
using Base64FromBinary = boost::archive::iterators::base64_from_binary<
boost::archive::iterators::transform_width<const char *, // sequence of chars
6, // get view of 6 bit
8 // from sequence of 8 bit
>>;
using BinaryFromBase64 = boost::archive::iterators::transform_width<
boost::archive::iterators::binary_from_base64<std::string::const_iterator>,
8,
6>;
8, // get a view of 8 bit
6 // from a sequence of 6 bit
>;
} // ns detail
template <class ObjectT> static void EncodeToBase64(const ObjectT &object, std::string &encoded)
template <typename T> std::string encodeBase64(const T &x)
{
const char *char_ptr_to_object = reinterpret_cast<const char *>(&object);
std::vector<unsigned char> data(sizeof(object));
std::copy(char_ptr_to_object, char_ptr_to_object + sizeof(ObjectT), data.begin());
// static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
unsigned char number_of_padded_chars = 0; // is in {0,1,2};
while (data.size() % 3 != 0)
{
++number_of_padded_chars;
data.push_back(0x00);
}
std::vector<unsigned char> bytes{reinterpret_cast<const char *>(&x),
reinterpret_cast<const char *>(&x) + sizeof(T)};
BOOST_ASSERT(!bytes.empty());
const auto next_divisible_by_three = ((bytes.size() / 3u) + 1u) * 3u;
BOOST_ASSERT(next_divisible_by_three >= bytes.size());
const auto bytes_to_pad = next_divisible_by_three - bytes.size();
BOOST_ASSERT(bytes_to_pad == 0 || bytes_to_pad == 1 || bytes_to_pad == 2);
bytes.insert(end(bytes), bytes_to_pad, 0x00);
BOOST_ASSERT_MSG(0 == bytes.size() % 3, "base64 input data size is not a multiple of 3");
std::string encoded{detail::Base64FromBinary{bytes.data()},
detail::Base64FromBinary{bytes.data() + (bytes.size() - bytes_to_pad)}};
BOOST_ASSERT_MSG(0 == data.size() % 3, "base64 input data size is not a multiple of 3!");
encoded.resize(sizeof(ObjectT));
encoded.assign(base64_t(&data[0]),
base64_t(&data[0] + (data.size() - number_of_padded_chars)));
std::replace(begin(encoded), end(encoded), '+', '-');
std::replace(begin(encoded), end(encoded), '/', '_');
return encoded;
}
template <class ObjectT> static void DecodeFromBase64(const std::string &input, ObjectT &object)
template <typename T> T decodeBase64(std::string encoded)
{
try
{
std::string encoded(input);
// static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
std::replace(begin(encoded), end(encoded), '-', '+');
std::replace(begin(encoded), end(encoded), '_', '/');
std::copy(binary_t(encoded.begin()), binary_t(encoded.begin() + encoded.length()),
reinterpret_cast<char *>(&object));
}
catch (...)
{
}
}
};
}
T rv;
std::copy(detail::BinaryFromBase64{begin(encoded)},
detail::BinaryFromBase64{begin(encoded) + encoded.length()},
reinterpret_cast<char *>(&rv));
return rv;
}
} // ns engine
} // ns osrm
#endif /* OBJECT_ENCODER_HPP */

View File

@ -94,8 +94,7 @@ template <class DataFacadeT> class DistanceTablePlugin final : public BasePlugin
if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty())
{
PhantomNode current_phantom_node;
ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], current_phantom_node);
auto current_phantom_node = decodeBase64<PhantomNode>(route_parameters.hints[i]);
if (current_phantom_node.IsValid(facade->GetNumberOfNodes()))
{
if (route_parameters.is_source[i])

View File

@ -64,8 +64,7 @@ template <class DataFacadeT> class RoundTripPlugin final : public BasePlugin
if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty())
{
PhantomNode current_phantom_node;
ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], current_phantom_node);
auto current_phantom_node = decodeBase64<PhantomNode>(route_parameters.hints[i]);
if (current_phantom_node.IsValid(facade->GetNumberOfNodes()))
{
phantom_node_list.push_back(std::move(current_phantom_node));
@ -317,7 +316,6 @@ template <class DataFacadeT> class RoundTripPlugin final : public BasePlugin
// }
// return s;
// }();
}
else
{

View File

@ -83,8 +83,8 @@ template <class DataFacadeT> class ViaRoutePlugin final : public BasePlugin
if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty())
{
ObjectEncoder::DecodeFromBase64(route_parameters.hints[i],
phantom_node_pair_list[i].first);
phantom_node_pair_list[i].first =
decodeBase64<PhantomNode>(route_parameters.hints[i]);
if (phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes()))
{
continue;
@ -111,8 +111,8 @@ template <class DataFacadeT> class ViaRoutePlugin final : public BasePlugin
auto snapped_phantoms = snapPhantomNodes(phantom_node_pair_list);
InternalRouteResult raw_route;
auto build_phantom_pairs = [&raw_route](const PhantomNode &first_node,
const PhantomNode &second_node)
auto build_phantom_pairs =
[&raw_route](const PhantomNode &first_node, const PhantomNode &second_node)
{
raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node});
};