File: base32.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (132 lines) | stat: -rw-r--r-- 4,188 bytes parent folder | download | duplicates (9)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/base32/base32.h"

#include <stddef.h>

#include <array>
#include <limits>
#include <string_view>

#include "base/check_op.h"
#include "base/numerics/safe_math.h"

namespace base32 {

namespace {

constexpr auto kEncoding =
    std::to_array<const char>("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567");
static_assert(kEncoding.size() == 33);  // 32 symbols + null terminator
constexpr char kPaddingChar = '=';

// Returns a 5 bit number between [0,31] matching the provided base 32 encoded
// character. Returns 0xff on error.
uint8_t ReverseMapping(char input_char) {
  if (input_char >= 'A' && input_char <= 'Z')
    return input_char - 'A';
  if (input_char >= '2' && input_char <= '7')
    return input_char - '2' + 26;
  return 0xff;
}

}  // namespace

std::string Base32Encode(base::span<const uint8_t> input,
                         Base32EncodePolicy policy) {
  if (input.empty())
    return std::string();

  // Per RFC4648, the output is formed of 8 characters per 40 bits of input and
  // another 8 characters for the last group of [1,39] bits in the input.
  // That is: ceil(input.size() * 8.0 / 40.0) * 8 ==
  //          ceil(input.size() / 5.0) * 8 ==
  //          ((input.size() + 4) / 5) * 8.
  const size_t padded_length = ((input.size() + 4) / 5) * 8;

  // When no padding is used, the output is exactly 1 character per 5 bits of
  // input and one more for the last [1,4] bits.
  // That is: ceil(input.size() * 8.0 / 5.0) ==
  //          (input.size() * 8 + 4) / 5.
  const size_t unpadded_length =
      ((base::MakeCheckedNum(input.size()) * 8 + 4) / 5).ValueOrDie();

  std::string output;
  const size_t encoded_length = policy == Base32EncodePolicy::INCLUDE_PADDING
                                    ? padded_length
                                    : unpadded_length;
  output.reserve(encoded_length);

  // A bit stream which will be read from the left and appended to from the
  // right as it's emptied.
  uint16_t bit_stream = (static_cast<uint8_t>(input[0]) << 8);
  size_t next_byte_index = 1;
  int free_bits = 8;
  while (free_bits < 16) {
    // Extract the 5 leftmost bits in the stream
    output.push_back(kEncoding[bit_stream >> 11]);
    bit_stream <<= 5;
    free_bits += 5;

    // If there is enough room in the bit stream, inject another byte (if there
    // are any left...).
    if (free_bits >= 8 && next_byte_index < input.size()) {
      free_bits -= 8;
      bit_stream += static_cast<uint8_t>(input[next_byte_index++]) << free_bits;
    }
  }

  if (policy == Base32EncodePolicy::INCLUDE_PADDING) {
    output.append(padded_length - unpadded_length, kPaddingChar);
  }

  DCHECK_EQ(encoded_length, output.size());
  return output;
}

std::vector<uint8_t> Base32Decode(std::string_view input) {
  // Remove padding, if any
  const size_t padding_index = input.find(kPaddingChar);
  if (padding_index != std::string_view::npos) {
    input.remove_suffix(input.size() - padding_index);
  }

  if (input.empty())
    return std::vector<uint8_t>();

  const size_t decoded_length =
      (base::MakeCheckedNum(input.size()) * 5 / 8).ValueOrDie();

  std::vector<uint8_t> output;
  output.reserve(decoded_length);

  // A bit stream which will be read from the left and appended to from the
  // right as it's emptied.
  uint16_t bit_stream = 0;
  size_t free_bits = 16;
  for (char input_char : input) {
    const uint8_t decoded_5bits = ReverseMapping(input_char);
    // If an invalid character is read from the input, then stop decoding.
    if (decoded_5bits >= 32)
      return std::vector<uint8_t>();

    // Place the next decoded 5-bits in the stream.
    bit_stream |= decoded_5bits << (free_bits - 5);
    free_bits -= 5;

    // If the stream is filled with a byte, flush the stream of that byte and
    // append it to the output.
    if (free_bits <= 8) {
      output.push_back(static_cast<uint8_t>(bit_stream >> 8));
      bit_stream <<= 8;
      free_bits += 8;
    }
  }

  DCHECK_EQ(decoded_length, output.size());
  return output;
}

}  // namespace base32