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; std::string hint;
for (const auto i : util::irange<std::size_t>(0, raw_route.segment_end_coordinates.size())) 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); hint = encodeBase64(raw_route.segment_end_coordinates[i].source_phantom);
json_location_hint_array.values.push_back(hint); 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_location_hint_array.values.emplace_back(std::move(hint));
json_hint_object.values["locations"] = json_location_hint_array; json_hint_object.values["locations"] = json_location_hint_array;

View File

@ -11,59 +11,75 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstdint>
#include <climits>
namespace osrm namespace osrm
{ {
namespace engine namespace engine
{ {
struct ObjectEncoder namespace detail
{ {
using base64_t = boost::archive::iterators::base64_from_binary< static_assert(CHAR_BIT == 8u, "we assume a byte holds 8 bits");
boost::archive::iterators::transform_width<const char *, 6, 8>>; 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::binary_from_base64<std::string::const_iterator>, boost::archive::iterators::transform_width<const char *, // sequence of chars
8, 6, // get view of 6 bit
6>; 8 // from sequence of 8 bit
>>;
template <class ObjectT> static void EncodeToBase64(const ObjectT &object, std::string &encoded) using BinaryFromBase64 = boost::archive::iterators::transform_width<
{ boost::archive::iterators::binary_from_base64<std::string::const_iterator>,
const char *char_ptr_to_object = reinterpret_cast<const char *>(&object); 8, // get a view of 8 bit
std::vector<unsigned char> data(sizeof(object)); 6 // from a sequence of 6 bit
std::copy(char_ptr_to_object, char_ptr_to_object + sizeof(ObjectT), data.begin()); >;
} // ns detail
unsigned char number_of_padded_chars = 0; // is in {0,1,2}; template <typename T> std::string encodeBase64(const T &x)
while (data.size() % 3 != 0) {
{ // static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type");
++number_of_padded_chars;
data.push_back(0x00);
}
BOOST_ASSERT_MSG(0 == data.size() % 3, "base64 input data size is not a multiple of 3!"); std::vector<unsigned char> bytes{reinterpret_cast<const char *>(&x),
encoded.resize(sizeof(ObjectT)); reinterpret_cast<const char *>(&x) + sizeof(T)};
encoded.assign(base64_t(&data[0]), BOOST_ASSERT(!bytes.empty());
base64_t(&data[0] + (data.size() - number_of_padded_chars)));
std::replace(begin(encoded), end(encoded), '+', '-');
std::replace(begin(encoded), end(encoded), '/', '_');
}
template <class ObjectT> static void DecodeFromBase64(const std::string &input, ObjectT &object) const auto next_divisible_by_three = ((bytes.size() / 3u) + 1u) * 3u;
{ BOOST_ASSERT(next_divisible_by_three >= bytes.size());
try
{
std::string encoded(input);
std::replace(begin(encoded), end(encoded), '-', '+');
std::replace(begin(encoded), end(encoded), '_', '/');
std::copy(binary_t(encoded.begin()), binary_t(encoded.begin() + encoded.length()), const auto bytes_to_pad = next_divisible_by_three - bytes.size();
reinterpret_cast<char *>(&object)); BOOST_ASSERT(bytes_to_pad == 0 || bytes_to_pad == 1 || bytes_to_pad == 2);
}
catch (...) 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)}};
std::replace(begin(encoded), end(encoded), '+', '-');
std::replace(begin(encoded), end(encoded), '/', '_');
return encoded;
} }
template <typename T> T decodeBase64(std::string encoded)
{
// 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), '_', '/');
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 */ #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() && if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty()) !route_parameters.hints[i].empty())
{ {
PhantomNode current_phantom_node; auto current_phantom_node = decodeBase64<PhantomNode>(route_parameters.hints[i]);
ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], current_phantom_node);
if (current_phantom_node.IsValid(facade->GetNumberOfNodes())) if (current_phantom_node.IsValid(facade->GetNumberOfNodes()))
{ {
if (route_parameters.is_source[i]) 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() && if (checksum_OK && i < route_parameters.hints.size() &&
!route_parameters.hints[i].empty()) !route_parameters.hints[i].empty())
{ {
PhantomNode current_phantom_node; auto current_phantom_node = decodeBase64<PhantomNode>(route_parameters.hints[i]);
ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], current_phantom_node);
if (current_phantom_node.IsValid(facade->GetNumberOfNodes())) if (current_phantom_node.IsValid(facade->GetNumberOfNodes()))
{ {
phantom_node_list.push_back(std::move(current_phantom_node)); phantom_node_list.push_back(std::move(current_phantom_node));
@ -317,7 +316,6 @@ template <class DataFacadeT> class RoundTripPlugin final : public BasePlugin
// } // }
// return s; // return s;
// }(); // }();
} }
else else
{ {

View File

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