@@ -70,61 +70,74 @@ BaseNEncoder::encode(const std::vector<uint8_t>& input) {
7070 return (encoded_output);
7171 }
7272
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.
7887 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) {
8090 if (bytes == input.end ()) {
8191 break ;
8292 }
8393
94+ // Grab the next byte.
8495 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.
8699 ++bytes;
87100 }
88101
102+ // Do we need more bits in this digit index?
89103 if (cnt < bits_per_digit_) {
104+ // Yes, so shift the index over to make room for the next bit.
90105 digit_idx <<= 1 ;
91106 } 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.
96109 encoded_output.push_back (digit_set_[digit_idx]);
97- #endif
98-
99110 digit_idx = 0 ;
100111 cnt = 0 ;
101112 }
102113
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) {
105118 digit_idx |= 1 ;
106119 }
107120
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 ;
109124 ++cnt;
110125 }
111126
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));
121134
122135 // Add padding as needed.
123136 if (digits_per_group_) {
124137 auto rem = encoded_output.size () % digits_per_group_;
125138 if (rem) {
126- auto need = digits_per_group_ - rem + 1 ;
127- while (--need ) {
139+ auto need = digits_per_group_ - rem;
140+ while (need-- ) {
128141 encoded_output.push_back (pad_char_);
129142 }
130143 }
@@ -135,26 +148,34 @@ BaseNEncoder::encode(const std::vector<uint8_t>& input) {
135148
136149void
137150BaseNEncoder::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+
138161 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+
144168 for (const auto enc_digit : encoded_str) {
169+ // If it's a pad char, count it and go on.
145170 if (pad_char_ && enc_digit == pad_char_) {
146171 pad_cnt++;
147172 continue ;
148173 }
149174
150- // Translate the b64 digit to bits.
151- #if 0
175+ // Translate the encoded digit to its binary bits.
152176 uint8_t dig_bits = digitToBits (enc_digit);
153- #else
154- uint8_t dig_bits = bits_table_[enc_digit];
155- #endif
156177
157- // Skip whitespace.
178+ // Skip whitespace. The choice of 0xee to signify white-space was arbitrary.
158179 if (dig_bits == 0xee ) {
159180 continue ;
160181 }
@@ -165,7 +186,7 @@ BaseNEncoder::decode(const std::string& encoded_str, std::vector<uint8_t>& outpu
165186 << algorithm_ << " char set" << " : " << encoded_str);
166187 }
167188
168- // Error if pads are in the middle.
189+ // Error if pad characters occur in the middle.
169190 if (pad_cnt) {
170191 isc_throw (isc::BadValue, " pad mixed with digits in "
171192 << algorithm_ << " : " << encoded_str);
@@ -174,13 +195,13 @@ BaseNEncoder::decode(const std::string& encoded_str, std::vector<uint8_t>& outpu
174195 // Bump the valid character count.
175196 dig_cnt++;
176197
177- // Shift off what we don't need .
198+ // Shift off the unused bits .
178199 dig_bits <<= shift_bits;
179200
180201 // Add digit's decoded bits to current byte.
181202 for (auto i = 0 ; i < bits_per_digit_; ++i) {
182203 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 .
184205 cur_byte <<= 1 ;
185206 } else {
186207 // Add the completed byte to the output.
@@ -217,8 +238,22 @@ BaseNEncoder::decode(const std::string& encoded_str, std::vector<uint8_t>& outpu
217238 << algorithm_ << " : " << encoded_str);
218239 }
219240
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 ;
222257 if (padbits > bits_per_digit_ * (pad_cnt + 1 )) {
223258 isc_throw (isc::BadValue, " Invalid padding for "
224259 << algorithm_ << " : " << encoded_str);
@@ -334,7 +369,6 @@ decodeHex(const string& encoded_str, vector<uint8_t>& output) {
334369 encoder.decode (encoded_str, output);
335370}
336371
337-
338372} // namespace encode
339373} // namespace util
340374} // namespace isc
0 commit comments