|
12 | 12 | // See the License for the specific language governing permissions and |
13 | 13 | // limitations under the License. |
14 | 14 |
|
15 | | -#include "google/cloud/spanner/internal/base64.h" |
| 15 | +#include "google/cloud/spanner/bytes.h" |
| 16 | +#include "google/cloud/status.h" |
16 | 17 | #include <array> |
17 | 18 | #include <climits> |
18 | | -#include <string> |
19 | 19 |
|
20 | 20 | namespace google { |
21 | 21 | namespace cloud { |
22 | 22 | namespace spanner { |
23 | 23 | inline namespace SPANNER_CLIENT_NS { |
24 | | -namespace internal { |
| 24 | + |
25 | 25 | namespace { |
26 | 26 |
|
27 | 27 | constexpr char kPadding = '='; |
@@ -51,83 +51,105 @@ constexpr std::array<unsigned char, UCHAR_MAX + 1> kCharToIndexExcessOne = {{ |
51 | 51 | // UCHAR_MAX is required to be at least 255, meaning std::string::value_type |
52 | 52 | // can always hold an octet. If UCHAR_MAX > 255, however, we have no way to |
53 | 53 | // base64 encode large values. So, we demand exactly 255. |
54 | | -static_assert(UCHAR_MAX == 255, "required by Base64Encode()"); |
| 54 | +static_assert(UCHAR_MAX == 255, "required by base64 encoder"); |
55 | 55 |
|
56 | 56 | } // namespace |
57 | 57 |
|
58 | | -std::string Base64Encode(std::string const& bytes) { |
59 | | - std::string encoded; |
60 | | - auto* p = reinterpret_cast<unsigned char const*>(bytes.data()); |
61 | | - auto* const ep = p + bytes.size(); |
62 | | - encoded.reserve((ep - p + 2) / 3 * 4); // 3 octets to 4 sextets |
63 | | - while (ep - p >= 3) { |
64 | | - unsigned int const v = p[0] << 16 | p[1] << 8 | p[2]; |
65 | | - encoded.push_back(kIndexToChar[v >> 18]); |
66 | | - encoded.push_back(kIndexToChar[v >> 12 & 0x3f]); |
67 | | - encoded.push_back(kIndexToChar[v >> 6 & 0x3f]); |
68 | | - encoded.push_back(kIndexToChar[v & 0x3f]); |
69 | | - p += 3; |
70 | | - } |
71 | | - switch (ep - p) { |
| 58 | +void Bytes::Encoder::Flush() { |
| 59 | + unsigned int const v = buf_[0] << 16 | buf_[1] << 8 | buf_[2]; |
| 60 | + rep_.push_back(kIndexToChar[v >> 18]); |
| 61 | + rep_.push_back(kIndexToChar[v >> 12 & 0x3f]); |
| 62 | + rep_.push_back(kIndexToChar[v >> 6 & 0x3f]); |
| 63 | + rep_.push_back(kIndexToChar[v & 0x3f]); |
| 64 | + len_ = 0; |
| 65 | +} |
| 66 | + |
| 67 | +void Bytes::Encoder::FlushAndPad() { |
| 68 | + switch (len_) { |
72 | 69 | case 2: { |
73 | | - unsigned int const v = p[0] << 16 | p[1] << 8; |
74 | | - encoded.push_back(kIndexToChar[v >> 18]); |
75 | | - encoded.push_back(kIndexToChar[v >> 12 & 0x3f]); |
76 | | - encoded.push_back(kIndexToChar[v >> 6 & 0x3f]); |
77 | | - encoded.push_back(kPadding); |
| 70 | + unsigned int const v = buf_[0] << 16 | buf_[1] << 8; |
| 71 | + rep_.push_back(kIndexToChar[v >> 18]); |
| 72 | + rep_.push_back(kIndexToChar[v >> 12 & 0x3f]); |
| 73 | + rep_.push_back(kIndexToChar[v >> 6 & 0x3f]); |
| 74 | + rep_.push_back(kPadding); |
78 | 75 | break; |
79 | 76 | } |
80 | 77 | case 1: { |
81 | | - unsigned int const v = p[0] << 16; |
82 | | - encoded.push_back(kIndexToChar[v >> 18]); |
83 | | - encoded.push_back(kIndexToChar[v >> 12 & 0x3f]); |
84 | | - encoded.append(2, kPadding); |
| 78 | + unsigned int const v = buf_[0] << 16; |
| 79 | + rep_.push_back(kIndexToChar[v >> 18]); |
| 80 | + rep_.push_back(kIndexToChar[v >> 12 & 0x3f]); |
| 81 | + rep_.append(2, kPadding); |
85 | 82 | break; |
86 | 83 | } |
87 | 84 | } |
88 | | - return encoded; |
89 | 85 | } |
90 | 86 |
|
91 | | -StatusOr<std::string> Base64Decode(std::string const& base64) { |
92 | | - std::string decoded; |
93 | | - auto* p = reinterpret_cast<unsigned char const*>(base64.data()); |
94 | | - auto* ep = p + base64.size(); |
95 | | - decoded.reserve((ep - p + 3) / 4 * 3); // 4 sextets to 3 octets |
| 87 | +void Bytes::Decoder::iterator::Fill() { |
| 88 | + if (pos_ != end_) { |
| 89 | + unsigned char p0 = *pos_++; |
| 90 | + unsigned char p1 = *pos_++; |
| 91 | + unsigned char p2 = *pos_++; |
| 92 | + unsigned char p3 = *pos_++; |
| 93 | + auto i0 = kCharToIndexExcessOne[p0] - 1; |
| 94 | + auto i1 = kCharToIndexExcessOne[p1] - 1; |
| 95 | + if (p3 == kPadding) { |
| 96 | + if (p2 == kPadding) { |
| 97 | + buf_[++len_] = i0 << 2 | i1 >> 4; |
| 98 | + } else { |
| 99 | + auto i2 = kCharToIndexExcessOne[p2] - 1; |
| 100 | + buf_[++len_] = i1 << 4 | i2 >> 2; |
| 101 | + buf_[++len_] = i0 << 2 | i1 >> 4; |
| 102 | + } |
| 103 | + } else { |
| 104 | + auto i2 = kCharToIndexExcessOne[p2] - 1; |
| 105 | + auto i3 = kCharToIndexExcessOne[p3] - 1; |
| 106 | + buf_[++len_] = i2 << 6 | i3; |
| 107 | + buf_[++len_] = i1 << 4 | i2 >> 2; |
| 108 | + buf_[++len_] = i0 << 2 | i1 >> 4; |
| 109 | + } |
| 110 | + } |
| 111 | +} |
| 112 | + |
| 113 | +namespace internal { |
| 114 | + |
| 115 | +// Construction from a base64-encoded US-ASCII `std::string`. |
| 116 | +StatusOr<Bytes> BytesFromBase64(std::string input) { |
| 117 | + auto* p = reinterpret_cast<unsigned char const*>(input.data()); |
| 118 | + auto* ep = p + input.size(); |
96 | 119 | while (ep - p >= 4) { |
97 | 120 | auto i0 = kCharToIndexExcessOne[p[0]]; |
98 | 121 | auto i1 = kCharToIndexExcessOne[p[1]]; |
99 | 122 | if (--i0 >= 64 || --i1 >= 64) break; |
100 | 123 | if (p[3] == kPadding) { |
101 | 124 | if (p[2] == kPadding) { |
102 | 125 | if ((i1 & 0xf) != 0) break; |
103 | | - decoded.push_back(i0 << 2 | i1 >> 4); |
104 | 126 | } else { |
105 | 127 | auto i2 = kCharToIndexExcessOne[p[2]]; |
106 | 128 | if (--i2 >= 64 || (i2 & 0x3) != 0) break; |
107 | | - decoded.push_back(i0 << 2 | i1 >> 4); |
108 | | - decoded.push_back(i1 << 4 | i2 >> 2); |
109 | 129 | } |
110 | 130 | p += 4; |
111 | 131 | break; |
112 | 132 | } |
113 | 133 | auto i2 = kCharToIndexExcessOne[p[2]]; |
114 | 134 | auto i3 = kCharToIndexExcessOne[p[3]]; |
115 | 135 | if (--i2 >= 64 || --i3 >= 64) break; |
116 | | - decoded.push_back(i0 << 2 | i1 >> 4); |
117 | | - decoded.push_back(i1 << 4 | i2 >> 2); |
118 | | - decoded.push_back(i2 << 6 | i3); |
119 | 136 | p += 4; |
120 | 137 | } |
121 | 138 | if (p != ep) { |
122 | | - auto const offset = reinterpret_cast<char const*>(p) - base64.data(); |
123 | | - auto const bad_chunk = base64.substr(offset, 4); |
| 139 | + auto const offset = reinterpret_cast<char const*>(p) - input.data(); |
| 140 | + auto const bad_chunk = input.substr(offset, 4); |
124 | 141 | auto message = "Invalid base64 chunk \"" + bad_chunk + "\"" + |
125 | 142 | " at offset " + std::to_string(offset); |
126 | 143 | return Status(StatusCode::kInvalidArgument, std::move(message)); |
127 | 144 | } |
128 | | - return decoded; |
| 145 | + Bytes bytes; |
| 146 | + bytes.base64_rep_ = std::move(input); |
| 147 | + return bytes; |
129 | 148 | } |
130 | 149 |
|
| 150 | +// Conversion to a base64-encoded US-ASCII `std::string`. |
| 151 | +std::string BytesToBase64(Bytes const& b) { return b.base64_rep_; } |
| 152 | + |
131 | 153 | } // namespace internal |
132 | 154 | } // namespace SPANNER_CLIENT_NS |
133 | 155 | } // namespace spanner |
|
0 commit comments