1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_BIG_ENDIAN_H
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_BIG_ENDIAN_H
17 
18 #include "google/cloud/status.h"
19 #include "google/cloud/status_or.h"
20 #include "google/cloud/version.h"
21 #include <array>
22 #include <cstdint>
23 #include <limits>
24 #include <string>
25 #include <type_traits>
26 
27 namespace google {
28 namespace cloud {
29 inline namespace GOOGLE_CLOUD_CPP_NS {
30 namespace internal {
31 
32 // Encodes signed or unsigned integers as a big-endian sequence of bytes. The
33 // returned string has a size matching `sizeof(T)`. Example:
34 //
35 //   std::string s = EncodeBigEndian(std::int32_t{255});
36 //   assert(s == std::string("\0\0\0\xFF", 4));
37 //
38 template <typename T,
39           typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
EncodeBigEndian(T value)40 std::string EncodeBigEndian(T value) {
41   static_assert(std::numeric_limits<unsigned char>::digits == 8,
42                 "This code assumes an 8-bit char");
43   using unsigned_type = typename std::make_unsigned<T>::type;
44   unsigned_type const n = *reinterpret_cast<unsigned_type*>(&value);
45   auto shift = sizeof(n) * 8;
46   std::array<std::uint8_t, sizeof(n)> a;
47   for (auto& c : a) {
48     shift -= 8;
49     c = static_cast<std::uint8_t>(n >> shift);
50   }
51   return {reinterpret_cast<char const*>(a.data()), a.size()};
52 }
53 
54 // Decodes the given string as a big-endian sequence of bytes representing an
55 // integer of the specified type. Returns an error status if the given string
56 // is the wrong size for the specified type. Example:
57 //
58 //   std::string s("\0\0\0\xFF", 4);
59 //   StatusOr<std::int32_t> decoded = DecodeBigEndian(s);
60 //   if (decoded) assert(*decoded == 255);
61 //
62 template <typename T,
63           typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
DecodeBigEndian(std::string const & value)64 StatusOr<T> DecodeBigEndian(std::string const& value) {
65   static_assert(std::numeric_limits<unsigned char>::digits == 8,
66                 "This code assumes an 8-bit char");
67   if (value.size() != sizeof(T)) {
68     auto const msg = "Given value with " + std::to_string(value.size()) +
69                      " bytes; expected " + std::to_string(sizeof(T));
70     return Status(StatusCode::kInvalidArgument, msg);
71   }
72   using unsigned_type = typename std::make_unsigned<T>::type;
73   auto shift = sizeof(T) * 8;
74   unsigned_type result = 0;
75   for (auto const& c : value) {
76     auto const n = *reinterpret_cast<std::uint8_t const*>(&c);
77     shift -= 8;
78     result = static_cast<unsigned_type>(result | (unsigned_type{n} << shift));
79   }
80   return *reinterpret_cast<T*>(&result);
81 }
82 
83 }  // namespace internal
84 }  // namespace GOOGLE_CLOUD_CPP_NS
85 }  // namespace cloud
86 }  // namespace google
87 
88 #endif  // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_BIG_ENDIAN_H
89