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