Shaka Player Embedded
base_64.cc
Go to the documentation of this file.
1 // Copyright 2016 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 // https://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 #include "src/js/base_64.h"
16 
17 #include "src/js/js_error.h"
19 
20 namespace shaka {
21 namespace js {
22 
23 namespace {
24 
25 constexpr const char kCodes[] =
26  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
27 
28 // Gets the low |n| bits of |in|.
29 #define GET_LOW_BITS(in, n) ((in) & ((1 << (n)) - 1))
30 // Gets the given (zero-indexed) bits [a, b) of |in|.
31 #define GET_BITS(in, a, b) GET_LOW_BITS((in) >> (a), (b) - (a))
32 // Calculates a/b using round-up division (only works for positive numbers).
33 #define CEIL_DIVIDE(a, b) ((((a)-1) / (b)) + 1)
34 
35 int32_t DecodeChar(char c) {
36  const char* it = strchr(kCodes, c);
37  if (it == nullptr)
38  return -1;
39  return it - kCodes;
40 }
41 
42 JsError BadEncoding() {
43  return JsError::TypeError(
44  "The string to be decoded is not correctly encoded.");
45 }
46 
47 } // namespace
48 
52 }
53 
54 // https://en.wikipedia.org/wiki/Base64
55 // Text | M | a | n |
56 // ASCI | 77 (0x4d) | 97 (0x61) | 110 (0x6e) |
57 // Bits | 0 1 0 0 1 1 0 1 0 1 1 0 0 0 0 1 0 1 1 0 1 1 1 0 |
58 // Index | 19 | 22 | 5 | 46 |
59 // Base64 | T | W | F | u |
60 // | <----------------- 24-bits -----------------> |
61 
62 std::string Base64::Encode(ByteString input) {
63  if (input.empty())
64  return "";
65 
66  // Stores a 24-bit block that is treated as an array where insertions occur
67  // from high to low.
68  uint32_t temp = 0;
69  size_t out_i = 0;
70  const size_t out_size = CEIL_DIVIDE(input.size(), 3) * 4;
71  std::string result(out_size, '\0');
72  for (size_t i = 0; i < input.size(); i++) {
73  // "insert" 8-bits of data
74  temp |= (input[i] << ((2 - (i % 3)) * 8));
75 
76  if (i % 3 == 2) {
77  result[out_i++] = kCodes[GET_BITS(temp, 18, 24)];
78  result[out_i++] = kCodes[GET_BITS(temp, 12, 18)];
79  result[out_i++] = kCodes[GET_BITS(temp, 6, 12)];
80  result[out_i++] = kCodes[GET_BITS(temp, 0, 6)];
81  temp = 0;
82  }
83  }
84 
85  if (input.size() % 3 == 1) {
86  result[out_i++] = kCodes[GET_BITS(temp, 18, 24)];
87  result[out_i++] = kCodes[GET_BITS(temp, 12, 18)];
88  result[out_i++] = '=';
89  result[out_i++] = '=';
90  } else if (input.size() % 3 == 2) {
91  result[out_i++] = kCodes[GET_BITS(temp, 18, 24)];
92  result[out_i++] = kCodes[GET_BITS(temp, 12, 18)];
93  result[out_i++] = kCodes[GET_BITS(temp, 6, 12)];
94  result[out_i++] = '=';
95  }
96 
97  DCHECK_EQ(out_size, out_i);
98  return result;
99 }
100 
101 ExceptionOr<ByteString> Base64::Decode(const std::string& input) {
102  if (input.empty())
103  return "";
104 
105  const size_t out_size_max = CEIL_DIVIDE(input.size() * 3, 4);
106  std::string result(out_size_max, '\0');
107 
108  // Stores 24-bits of data that is treated like an array where insertions occur
109  // from high to low.
110  uint32_t temp = 0;
111  size_t out_i = 0;
112  size_t i;
113  for (i = 0; i < input.size(); i++) {
114  if (input[i] == '=') {
115  // We want i to remain at the first '=', so we need an inner loop.
116  for (size_t j = i; j < input.size(); j++) {
117  if (input[j] != '=')
118  return BadEncoding();
119  }
120  break;
121  }
122 
123  const int32_t decoded = DecodeChar(input[i]);
124  if (decoded < 0)
125  return BadEncoding();
126  // "insert" 6-bits of data
127  temp |= (decoded << ((3 - (i % 4)) * 6));
128 
129  if (i % 4 == 3) {
130  result[out_i++] = GET_BITS(temp, 16, 24);
131  result[out_i++] = GET_BITS(temp, 8, 16);
132  result[out_i++] = GET_BITS(temp, 0, 8);
133  temp = 0;
134  }
135  }
136 
137  switch (i % 4) {
138  case 1:
139  return BadEncoding();
140  case 2:
141  result[out_i++] = GET_BITS(temp, 16, 24);
142  break;
143  case 3:
144  result[out_i++] = GET_BITS(temp, 16, 24);
145  result[out_i++] = GET_BITS(temp, 8, 16);
146  break;
147  }
148  result.resize(out_i);
149  return result;
150 }
151 
152 std::string Base64::EncodeUrl(ByteString input) {
153  std::string ret = Encode(input);
154  for (char& c : ret) {
155  if (c == '+')
156  c = '-';
157  else if (c == '/')
158  c = '_';
159  }
160  ret.erase(ret.find('=')); // Erase any trailing '='
161  return ret;
162 }
163 
164 ExceptionOr<ByteString> Base64::DecodeUrl(const std::string& input) {
165  std::string converted_input = input;
166  for (char& c : converted_input) {
167  if (c == '-')
168  c = '+';
169  else if (c == '_')
170  c = '/';
171  }
172  // Note Decode() will ignore any missing '='
173  return Decode(converted_input);
174 }
175 
176 } // namespace js
177 } // namespace shaka
static std::string EncodeUrl(ByteString input)
Definition: base_64.cc:152
static ExceptionOr< ByteString > DecodeUrl(const std::string &input)
Definition: base_64.cc:164
#define GET_BITS(in, a, b)
Definition: base_64.cc:31
static ExceptionOr< ByteString > Decode(const std::string &input)
Definition: base_64.cc:101
void RegisterGlobalFunction(const std::string &name, Func &&callback)
#define CEIL_DIVIDE(a, b)
Definition: base_64.cc:33
static JsError TypeError(const std::string &message)
Definition: js_error.cc:81
static std::string Encode(ByteString input)
Definition: base_64.cc:62
static void Install()
Definition: base_64.cc:49