@@ -70,61 +70,74 @@ BaseNEncoder::encode(const std::vector<uint8_t>& input) {
70
70
return (encoded_output);
71
71
}
72
72
73
- uint8_t cur_bit = 0x0 ;
74
- int cnt = 0 ;
75
- int digit_idx = 0 ;
76
- int cur_byte = 0 ;
77
- auto bytes = input.begin ();
73
+ // Iterate over the input bytes as a bit stream. We add input bits
74
+ // to a digit set index value until we have enough (bits_per_digit). We
75
+ // look up a digit in the digit set add it to the encoded output and start over
76
+ // on the next index value. When we have exhausted the bits in the current
77
+ // byte, get the next byte from input and continue. In other words, we pull bits
78
+ // from the left side of the input bit stream and push them into the right side of
79
+ // the index value. Each time we have done bits_per_digit bits we look up
80
+ // the digit and start the index value over.
81
+
82
+ int digit_idx = 0 ; // Digit index we are currently constructing.
83
+ int cnt = 0 ; // How many bits we have in the current digit idx
84
+ int cur_byte = 0 ; // Current input byte.
85
+ uint8_t cur_bit_mask = 0x0 ; // Bitmask of the current bit in the current byte.
86
+ auto bytes = input.begin (); // Start with the first byte.
78
87
while (1 ) {
79
- if (!cur_bit) {
88
+ // If the current bitmask is zero, it's time for the next input byte.
89
+ if (!cur_bit_mask) {
80
90
if (bytes == input.end ()) {
81
91
break ;
82
92
}
83
93
94
+ // Grab the next byte.
84
95
cur_byte = *bytes;
85
- cur_bit = 0x80 ;
96
+ // Start at the bitmask at the left-most bit.
97
+ cur_bit_mask = 0x80 ;
98
+ // Bump the iterator.
86
99
++bytes;
87
100
}
88
101
102
+ // Do we need more bits in this digit index?
89
103
if (cnt < bits_per_digit_) {
104
+ // Yes, so shift the index over to make room for the next bit.
90
105
digit_idx <<= 1 ;
91
106
} else {
92
- #if 0
93
- // Have a complete digit index, look up digit and add it.
94
- encoded_output.push_back(bitsToDigit(digit_idx));
95
- #else
107
+ // No, the index is complete, lookup its digit and add it to the
108
+ // output. Start over for the next index.
96
109
encoded_output.push_back (digit_set_[digit_idx]);
97
- #endif
98
-
99
110
digit_idx = 0 ;
100
111
cnt = 0 ;
101
112
}
102
113
103
- // Add the current bit to the digit index.
104
- if (cur_byte & cur_bit) {
114
+ // If the current bit in the current byte is set,
115
+ // set the right-most digit index bit to 1 (otherwise
116
+ // its left as zero).
117
+ if (cur_byte & cur_bit_mask) {
105
118
digit_idx |= 1 ;
106
119
}
107
120
108
- cur_bit >>= 1 ;
121
+ // Shift the cur_bit mask to select the next input bit and
122
+ // bump the number of bits in the current index.
123
+ cur_bit_mask >>= 1 ;
109
124
++cnt;
110
125
}
111
126
112
- // We've exhausted bits, but have left over
113
- if (cnt) {
114
- digit_idx <<= (bits_per_digit_ - cnt);
115
- #if 0
116
- encoded_output.push_back(bitsToDigit(digit_idx));
117
- #else
118
- encoded_output.push_back (digit_set_[digit_idx]);
119
- #endif
120
- }
127
+ // We've exhausted the input bits but have bits in the
128
+ // digit index. This means the remaining bits in our
129
+ // last index are zeros (pad bits). Shift "in" the
130
+ // required number of bits and add the corresponding
131
+ // digit.
132
+ digit_idx <<= (bits_per_digit_ - cnt);
133
+ encoded_output.push_back (bitsToDigit (digit_idx));
121
134
122
135
// Add padding as needed.
123
136
if (digits_per_group_) {
124
137
auto rem = encoded_output.size () % digits_per_group_;
125
138
if (rem) {
126
- auto need = digits_per_group_ - rem + 1 ;
127
- while (--need ) {
139
+ auto need = digits_per_group_ - rem;
140
+ while (need-- ) {
128
141
encoded_output.push_back (pad_char_);
129
142
}
130
143
}
@@ -135,26 +148,34 @@ BaseNEncoder::encode(const std::vector<uint8_t>& input) {
135
148
136
149
void
137
150
BaseNEncoder::decode (const std::string& encoded_str, std::vector<uint8_t >& output) {
151
+
152
+ // Mechanics are the essentially the same as encode(). We iterate over the encoded
153
+ // string's digits, discarding whitespaces. We lookup the digit's binary value
154
+ // in the lookup table, keeping only binary value's right-most, bits_per_digit bits.
155
+ // The remaining bits are then shifted out from the left of binary value into the
156
+ // right of the currently accumulating output byte until the byte is complete
157
+ // (8 bits) or the value's bits are exhausted. Completed bytes are added to the
158
+ // output buffer. We continue building bytes until we've exhausted the encoded
159
+ // string.
160
+
138
161
output.clear ();
139
- size_t dig_cnt = 0 ;
140
- size_t pad_cnt = 0 ;
141
- size_t shift_bits = 8 - bits_per_digit_;
142
- uint8_t cur_byte = 0 ;
143
- size_t cur_bit_cnt = 0 ;
162
+ size_t dig_cnt = 0 ; // Tracks how many encoded digits we see.
163
+ size_t pad_cnt = 0 ; // Tracks how many pad characters we see.
164
+ size_t shift_bits = 8 - bits_per_digit_; // Number of unused bits in digit data values.
165
+ uint8_t cur_byte = 0 ; // Current output byte.
166
+ size_t cur_bit_cnt = 0 ; // How man bits we have added to the current byte.
167
+
144
168
for (const auto enc_digit : encoded_str) {
169
+ // If it's a pad char, count it and go on.
145
170
if (pad_char_ && enc_digit == pad_char_) {
146
171
pad_cnt++;
147
172
continue ;
148
173
}
149
174
150
- // Translate the b64 digit to bits.
151
- #if 0
175
+ // Translate the encoded digit to its binary bits.
152
176
uint8_t dig_bits = digitToBits (enc_digit);
153
- #else
154
- uint8_t dig_bits = bits_table_[enc_digit];
155
- #endif
156
177
157
- // Skip whitespace.
178
+ // Skip whitespace. The choice of 0xee to signify white-space was arbitrary.
158
179
if (dig_bits == 0xee ) {
159
180
continue ;
160
181
}
@@ -165,7 +186,7 @@ BaseNEncoder::decode(const std::string& encoded_str, std::vector<uint8_t>& outpu
165
186
<< algorithm_ << " char set" << " : " << encoded_str);
166
187
}
167
188
168
- // Error if pads are in the middle.
189
+ // Error if pad characters occur in the middle.
169
190
if (pad_cnt) {
170
191
isc_throw (isc::BadValue, " pad mixed with digits in "
171
192
<< algorithm_ << " : " << encoded_str);
@@ -174,13 +195,13 @@ BaseNEncoder::decode(const std::string& encoded_str, std::vector<uint8_t>& outpu
174
195
// Bump the valid character count.
175
196
dig_cnt++;
176
197
177
- // Shift off what we don't need .
198
+ // Shift off the unused bits .
178
199
dig_bits <<= shift_bits;
179
200
180
201
// Add digit's decoded bits to current byte.
181
202
for (auto i = 0 ; i < bits_per_digit_; ++i) {
182
203
if (cur_bit_cnt < 8 ) {
183
- // Shift contents to make room for next gbit .
204
+ // Shift contents over one to make room for next bit .
184
205
cur_byte <<= 1 ;
185
206
} else {
186
207
// Add the completed byte to the output.
@@ -217,8 +238,22 @@ BaseNEncoder::decode(const std::string& encoded_str, std::vector<uint8_t>& outpu
217
238
<< algorithm_ << " : " << encoded_str);
218
239
}
219
240
220
- // Check for invalid number of pad characters.
221
- const size_t padbits = ((pad_cnt * bits_per_digit_) + 7 ) & ~7 ;
241
+ // Check for an invalid number of pad bits.
242
+ // Calculate the number of pad bits corresponding to the pad
243
+ // characters. In general, the pad bits consist of all-zero
244
+ // trailing bits of the last encoded character plus the zero bits
245
+ // represented by each pad character.
246
+ // 1st pad 2nd pad 3rd pad...
247
+ // +++===== ======= ===... (+: from encoded chars, =: from pad chars)
248
+ // 0000...0 0......0 000...
249
+ // 0 7 8 15 16.... (bits)
250
+ // The number of bits for the '==...' part is padchars * BitsPerChunk.
251
+ // So the total number of pad bits is the smallest multiple of 8
252
+ // that is >= padchars * BitsPerChunk.
253
+ // (Below, note the common idiom of the bitwise AND with ~7. It clears the
254
+ // lowest three bits, so has the effect of rounding the result down to the
255
+ // nearest multiple of 8)
256
+ const size_t padbits = ((pad_cnt * bits_per_digit_) + 0x07 ) & ~0x07 ;
222
257
if (padbits > bits_per_digit_ * (pad_cnt + 1 )) {
223
258
isc_throw (isc::BadValue, " Invalid padding for "
224
259
<< algorithm_ << " : " << encoded_str);
@@ -334,7 +369,6 @@ decodeHex(const string& encoded_str, vector<uint8_t>& output) {
334
369
encoder.decode (encoded_str, output);
335
370
}
336
371
337
-
338
372
} // namespace encode
339
373
} // namespace util
340
374
} // namespace isc
0 commit comments