Completely re-write base64 logic, make API suck less in doing so
This commit is contained in:
		
							parent
							
								
									822bb97a5f
								
							
						
					
					
						commit
						d0c534e5fa
					
				| @ -1,93 +1,125 @@ | |||||||
| #ifndef OSRM_BASE64_HPP | #ifndef OSRM_BASE64_HPP | ||||||
| #define OSRM_BASE64_HPP | #define OSRM_BASE64_HPP | ||||||
| 
 | 
 | ||||||
| #include <boost/assert.hpp> |  | ||||||
| #include <boost/archive/iterators/base64_from_binary.hpp> |  | ||||||
| #include <boost/archive/iterators/binary_from_base64.hpp> |  | ||||||
| #include <boost/archive/iterators/transform_width.hpp> |  | ||||||
| 
 |  | ||||||
| #include <algorithm> |  | ||||||
| #include <iterator> |  | ||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <iterator> | ||||||
|  | #include <type_traits> | ||||||
| 
 | 
 | ||||||
| #include <cstdint> | #include <cstddef> | ||||||
| #include <climits> | #include <climits> | ||||||
| 
 | 
 | ||||||
|  | #include <boost/archive/iterators/binary_from_base64.hpp> | ||||||
|  | #include <boost/archive/iterators/base64_from_binary.hpp> | ||||||
|  | #include <boost/archive/iterators/transform_width.hpp> | ||||||
|  | #include <boost/algorithm/string/trim.hpp> | ||||||
|  | #include <boost/range/algorithm/copy.hpp> | ||||||
|  | 
 | ||||||
| // RFC 4648 "The Base16, Base32, and Base64 Data Encodings"
 | // RFC 4648 "The Base16, Base32, and Base64 Data Encodings"
 | ||||||
| // See: https://tools.ietf.org/html/rfc4648
 | // See: https://tools.ietf.org/html/rfc4648
 | ||||||
|  | // Implementation adapted from: http://stackoverflow.com/a/28471421
 | ||||||
|  | 
 | ||||||
|  | // The C++ standard guarantees none of this by default, but we need it in the following.
 | ||||||
|  | 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"); | ||||||
| 
 | 
 | ||||||
| namespace osrm | namespace osrm | ||||||
| { | { | ||||||
| namespace engine | namespace engine | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| namespace detail | // Encoding Implementation
 | ||||||
|  | 
 | ||||||
|  | // Encodes a chunk of memory to Base64.
 | ||||||
|  | inline std::string encodeBase64(const unsigned char *first, std::size_t size) | ||||||
| { | { | ||||||
| static_assert(CHAR_BIT == 8u, "we assume a byte holds 8 bits"); |     using namespace boost::archive::iterators; | ||||||
| static_assert(sizeof(char) == 1u, "we assume a char is one byte large"); |  | ||||||
| 
 | 
 | ||||||
| using Base64FromBinary = boost::archive::iterators::base64_from_binary< |     const std::string bytes{first, first + size}; | ||||||
|     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< |     using Iter = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>; | ||||||
|     boost::archive::iterators::binary_from_base64<std::string::const_iterator>, |  | ||||||
|     8, // get a view of 8 bit
 |  | ||||||
|     6  // from a sequence of 6 bit
 |  | ||||||
|     >; |  | ||||||
| } // ns detail
 |  | ||||||
| 
 | 
 | ||||||
| template <typename T> std::string encodeBase64(const T &x) |     Iter view_first{begin(bytes)}; | ||||||
|  |     Iter view_last{end(bytes)}; | ||||||
|  | 
 | ||||||
|  |     std::string encoded{view_first, view_last}; | ||||||
|  | 
 | ||||||
|  |     return encoded.append((3 - size % 3) % 3, '='); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // C++11 standard 3.9.1/1: Plain char, signed char, and unsigned char are three distinct types
 | ||||||
|  | 
 | ||||||
|  | // Overload for signed char catches (not only but also) C-string literals.
 | ||||||
|  | inline std::string encodeBase64(const signed char *first, std::size_t size) | ||||||
|  | { | ||||||
|  |     return encodeBase64(reinterpret_cast<const unsigned char *>(first), size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Overload for char catches (not only but also) C-string literals.
 | ||||||
|  | inline std::string encodeBase64(const char *first, std::size_t size) | ||||||
|  | { | ||||||
|  |     return encodeBase64(reinterpret_cast<const unsigned char *>(first), size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Convenience specialization, encoding from string instead of byte-dumping it.
 | ||||||
|  | inline std::string encodeBase64(const std::string &x) { return encodeBase64(x.data(), x.size()); } | ||||||
|  | 
 | ||||||
|  | // Encode any sufficiently trivial object to Base64.
 | ||||||
|  | template <typename T> std::string encodeBase64Bytewise(const T &x) | ||||||
| { | { | ||||||
| #if not defined __GNUC__ or __GNUC__ > 4 | #if not defined __GNUC__ or __GNUC__ > 4 | ||||||
|     static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type"); |     static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type"); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     std::vector<unsigned char> bytes{reinterpret_cast<const char *>(&x), |     return encodeBase64(reinterpret_cast<const unsigned char *>(&x), sizeof(T)); | ||||||
|                                      reinterpret_cast<const char *>(&x) + sizeof(T)}; |  | ||||||
|     BOOST_ASSERT(!bytes.empty()); |  | ||||||
| 
 |  | ||||||
|     std::size_t bytes_to_pad{0}; |  | ||||||
| 
 |  | ||||||
|     while (bytes.size() % 3 != 0) |  | ||||||
|     { |  | ||||||
|         bytes_to_pad += 1; |  | ||||||
|         bytes.push_back(0); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     BOOST_ASSERT(bytes_to_pad == 0 || bytes_to_pad == 1 || bytes_to_pad == 2); | // Decoding Implementation
 | ||||||
|     BOOST_ASSERT_MSG(0 == bytes.size() % 3, "base64 input data size is not a multiple of 3"); |  | ||||||
| 
 | 
 | ||||||
|     std::string encoded{detail::Base64FromBinary{bytes.data()}, | // Decodes into a chunk of memory that is at least as large as the input.
 | ||||||
|                         detail::Base64FromBinary{bytes.data() + (bytes.size() - bytes_to_pad)}}; | template <typename OutputIter> void decodeBase64(const std::string &encoded, OutputIter out) | ||||||
|  | { | ||||||
|  |     using namespace boost::archive::iterators; | ||||||
|  |     using namespace boost::algorithm; | ||||||
| 
 | 
 | ||||||
|     std::replace(begin(encoded), end(encoded), '+', '-'); |     using Iter = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>; | ||||||
|     std::replace(begin(encoded), end(encoded), '/', '_'); |  | ||||||
| 
 | 
 | ||||||
|     return encoded; |     Iter view_first{begin(encoded)}; | ||||||
|  |     Iter view_last{end(encoded)}; | ||||||
|  | 
 | ||||||
|  |     const auto null = [](const unsigned char c) | ||||||
|  |     { | ||||||
|  |         return c == '\0'; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const auto bytes = trim_right_copy_if(std::string{view_first, view_last}, null); | ||||||
|  | 
 | ||||||
|  |     boost::copy(bytes, out); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename T> T decodeBase64(std::string encoded) | // Convenience specialization, filling string instead of byte-dumping into it.
 | ||||||
|  | inline std::string decodeBase64(const std::string &encoded) | ||||||
| { | { | ||||||
| #if not defined __GNUC__ or __GNUC__ > 4 |     std::string rv; | ||||||
|     static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type"); |  | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
|     std::replace(begin(encoded), end(encoded), '-', '+'); |     decodeBase64(encoded, std::back_inserter(rv)); | ||||||
|     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; |     return rv; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // Decodes from Base 64 to any sufficiently trivial object.
 | ||||||
|  | template <typename T> T decodeBase64Bytewise(const std::string &encoded) | ||||||
|  | { | ||||||
|  | #if not defined __GNUC__ or __GNUC__ > 4 | ||||||
|  |     static_assert(std::is_trivially_copyable<T>::value, "requires a trivially copyable type"); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     T x; | ||||||
|  | 
 | ||||||
|  |     decodeBase64(encoded, reinterpret_cast<unsigned char *>(&x)); | ||||||
|  | 
 | ||||||
|  |     return x; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // ns engine
 | } // ns engine
 | ||||||
| } // ns osrm
 | } // ns osrm
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -18,13 +18,13 @@ bool Hint::IsValid(const util::Coordinate new_input_coordinates, | |||||||
|            facade.GetCheckSum() == data_checksum; |            facade.GetCheckSum() == data_checksum; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string Hint::ToBase64() const { return encodeBase64(*this); } | std::string Hint::ToBase64() const { return encodeBase64Bytewise(*this); } | ||||||
| 
 | 
 | ||||||
| Hint Hint::FromBase64(const std::string &base64Hint) | Hint Hint::FromBase64(const std::string &base64Hint) | ||||||
| { | { | ||||||
|     BOOST_ASSERT_MSG(base64Hint.size() == ENCODED_HINT_SIZE, "Hint has invalid size"); |     BOOST_ASSERT_MSG(base64Hint.size() == ENCODED_HINT_SIZE, "Hint has invalid size"); | ||||||
| 
 | 
 | ||||||
|     return decodeBase64<Hint>(base64Hint); |     return decodeBase64Bytewise<Hint>(base64Hint); | ||||||
| } | } | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user