1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package com.jsql.util.bruter;
19
20 import org.apache.commons.codec.BinaryDecoder;
21 import org.apache.commons.codec.BinaryEncoder;
22 import org.apache.commons.codec.DecoderException;
23 import org.apache.commons.codec.EncoderException;
24 import org.apache.commons.codec.binary.StringUtils;
25
26 import java.util.Arrays;
27 import java.util.Objects;
28
29 /**
30 * Abstract superclass for Base-N encoders and decoders.
31 *
32 * <p>
33 * This class is thread-safe.
34 * </p>
35 *
36 * You can set the decoding behavior when the input bytes contain leftover trailing bits that cannot be created by a valid
37 * encoding. These can be bits that are unused from the final character or entire characters. The default mode is
38 * lenient decoding.
39 * <ul>
40 * <li>Lenient: Any trailing bits are composed into 8-bit bytes where possible. The remainder are discarded.
41 * <li>Strict: The decoding will raise an {@link IllegalArgumentException} if trailing bits are not part of a valid
42 * encoding. Any unused bits from the final character must be zero. Impossible counts of entire final characters are not
43 * allowed.
44 * </ul>
45 * <p>
46 * When strict decoding is enabled it is expected that the decoded bytes will be re-encoded to a byte array that matches
47 * the original, i.e. no changes occur on the final character. This requires that the input bytes use the same padding
48 * and alphabet as the encoder.
49 * </p>
50 */
51 public abstract class BaseNCodec implements BinaryEncoder, BinaryDecoder {
52
53 /**
54 * EOF
55 *
56 * @since 1.7
57 */
58 private static final int EOF = -1;
59
60 /**
61 * MIME chunk size per RFC 2045 section 6.8.
62 *
63 * <p>
64 * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
65 * equal signs.
66 * </p>
67 *
68 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
69 */
70 public static final int MIME_CHUNK_SIZE = 76;
71
72 /**
73 * PEM chunk size per RFC 1421 section 4.3.2.4.
74 *
75 * <p>
76 * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any
77 * equal signs.
78 * </p>
79 *
80 * @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421 section 4.3.2.4</a>
81 */
82 public static final int PEM_CHUNK_SIZE = 64;
83
84 private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2;
85
86 /**
87 * Defines the default buffer size - currently {@value}
88 * - must be large enough for at least one encoded block+separator
89 */
90 private static final int DEFAULT_BUFFER_SIZE = 8192;
91
92 /**
93 * The maximum size buffer to allocate.
94 *
95 * <p>This is set to the same size used in the JDK {@code java.util.ArrayList}:</p>
96 * <blockquote>
97 * Some VMs reserve some header words in an array.
98 * Attempts to allocate larger arrays may result in
99 * OutOfMemoryError: Requested array size exceeds VM limit.
100 * </blockquote>
101 */
102 private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
103
104 /** Mask used to extract 8 bits, used in decoding bytes */
105 protected static final int MASK_8BITS = 0xff;
106
107 /**
108 * Byte used to pad output.
109 */
110 protected static final byte PAD_DEFAULT = '='; // Allow static access to default
111
112 /**
113 * The default decoding policy.
114 * @since 1.15
115 */
116 protected static final CodecPolicy DECODING_POLICY_DEFAULT = CodecPolicy.LENIENT;
117
118 /**
119 * Chunk separator per RFC 2045 section 2.1.
120 *
121 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
122 */
123 private static final byte[] CHUNK_SEPARATOR = {'\r', '\n'};
124
125 protected final byte pad; // instance variable just in case it needs to vary later
126
127 /** Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32 */
128 private final int unencodedBlockSize;
129
130 /** Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32 */
131 private final int encodedBlockSize;
132
133 /**
134 * Chunksize for encoding. Not used when decoding.
135 * A value of zero or less implies no chunking of the encoded data.
136 * Rounded down to nearest multiple of encodedBlockSize.
137 */
138 protected final int lineLength;
139
140 /**
141 * Size of chunk separator. Not used unless {@link #lineLength} > 0.
142 */
143 private final int chunkSeparatorLength;
144
145 /**
146 * Defines the decoding behavior when the input bytes contain leftover trailing bits that
147 * cannot be created by a valid encoding. These can be bits that are unused from the final
148 * character or entire characters. The default mode is lenient decoding. Set this to
149 * {@code true} to enable strict decoding.
150 * <ul>
151 * <li>Lenient: Any trailing bits are composed into 8-bit bytes where possible.
152 * The remainder are discarded.
153 * <li>Strict: The decoding will raise an {@link IllegalArgumentException} if trailing bits
154 * are not part of a valid encoding. Any unused bits from the final character must
155 * be zero. Impossible counts of entire final characters are not allowed.
156 * </ul>
157 *
158 * <p>When strict decoding is enabled it is expected that the decoded bytes will be re-encoded
159 * to a byte array that matches the original, i.e. no changes occur on the final
160 * character. This requires that the input bytes use the same padding and alphabet
161 * as the encoder.
162 */
163 private final CodecPolicy decodingPolicy;
164
165 /**
166 * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size.
167 * If {@code chunkSeparatorLength} is zero, then chunking is disabled.
168 * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3)
169 * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
170 * @param lineLength if > 0, use chunking with a length {@code lineLength}
171 * @param chunkSeparatorLength the chunk separator length, if relevant
172 */
173 protected BaseNCodec(
174 final int unencodedBlockSize,
175 final int encodedBlockSize,
176 final int lineLength,
177 final int chunkSeparatorLength
178 ) {
179 this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, BaseNCodec.PAD_DEFAULT);
180 }
181
182 /**
183 * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size.
184 * If {@code chunkSeparatorLength} is zero, then chunking is disabled.
185 * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3)
186 * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
187 * @param lineLength if > 0, use chunking with a length {@code lineLength}
188 * @param chunkSeparatorLength the chunk separator length, if relevant
189 * @param pad byte used as padding byte.
190 */
191 protected BaseNCodec(
192 final int unencodedBlockSize,
193 final int encodedBlockSize,
194 final int lineLength,
195 final int chunkSeparatorLength,
196 final byte pad
197 ) {
198 this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, pad, BaseNCodec.DECODING_POLICY_DEFAULT);
199 }
200
201 /**
202 * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size.
203 * If {@code chunkSeparatorLength} is zero, then chunking is disabled.
204 * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3)
205 * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
206 * @param lineLength if > 0, use chunking with a length {@code lineLength}
207 * @param chunkSeparatorLength the chunk separator length, if relevant
208 * @param pad byte used as padding byte.
209 * @param decodingPolicy Decoding policy.
210 * @since 1.15
211 */
212 protected BaseNCodec(
213 final int unencodedBlockSize,
214 final int encodedBlockSize,
215 final int lineLength,
216 final int chunkSeparatorLength,
217 final byte pad,
218 final CodecPolicy decodingPolicy
219 ) {
220 this.unencodedBlockSize = unencodedBlockSize;
221 this.encodedBlockSize = encodedBlockSize;
222 final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0;
223 this.lineLength = useChunking ? lineLength / encodedBlockSize * encodedBlockSize : 0;
224 this.chunkSeparatorLength = chunkSeparatorLength;
225 this.pad = pad;
226 this.decodingPolicy = Objects.requireNonNull(decodingPolicy, "codecPolicy");
227 }
228
229 /**
230 * Compares two {@code int} values numerically treating the values
231 * as unsigned. Taken from JDK 1.8.
232 *
233 * @param x the first {@code int} to compare
234 * @param y the second {@code int} to compare
235 * @return the value {@code 0} if {@code x == y}; a value less
236 * than {@code 0} if {@code x < y} as unsigned values; and
237 * a value greater than {@code 0} if {@code x > y} as
238 * unsigned values
239 */
240 private static int compareUnsigned(final int x, final int y) {
241 return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
242 }
243
244 /**
245 * Create a positive capacity at least as large the minimum required capacity.
246 * If the minimum capacity is negative then this throws an OutOfMemoryError as no array
247 * can be allocated.
248 *
249 * @param minCapacity the minimum capacity
250 * @return the capacity
251 * @throws OutOfMemoryError if the {@code minCapacity} is negative
252 */
253 private static int createPositiveCapacity(final int minCapacity) {
254 if (minCapacity < 0) {
255 // overflow
256 throw new OutOfMemoryError("Unable to allocate array size: " + (minCapacity & 0xffffffffL));
257 }
258 // This is called when we require buffer expansion to a very big array.
259 // Use the conservative maximum buffer size if possible, otherwise the biggest required.
260 //
261 // Note: In this situation JDK 1.8 java.util.ArrayList returns Integer.MAX_VALUE.
262 // This excludes some VMs that can exceed MAX_BUFFER_SIZE but not allocate a full
263 // Integer.MAX_VALUE length array.
264 // The result is that we may have to allocate an array of this size more than once if
265 // the capacity must be expanded again.
266 return Math.max(minCapacity, BaseNCodec.MAX_BUFFER_SIZE);
267 }
268
269 /**
270 * Gets a copy of the chunk separator per RFC 2045 section 2.1.
271 *
272 * @return the chunk separator
273 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
274 * @since 1.15
275 */
276 public static byte[] getChunkSeparator() {
277 return BaseNCodec.CHUNK_SEPARATOR.clone();
278 }
279
280 /**
281 * Checks if a byte value is whitespace or not.
282 * Whitespace is taken to mean: space, tab, CR, LF
283 * @param byteToCheck
284 * the byte to check
285 * @return true if byte is whitespace, false otherwise
286 */
287 protected static boolean isWhiteSpace(final byte byteToCheck) {
288 switch (byteToCheck) {
289 case ' ' :
290 case '\n' :
291 case '\r' :
292 case '\t' :
293 return true;
294 default :
295 return false;
296 }
297 }
298
299 /**
300 * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}.
301 * @param context the context to be used
302 * @param minCapacity the minimum required capacity
303 * @return the resized byte[] buffer
304 * @throws OutOfMemoryError if the {@code minCapacity} is negative
305 */
306 private static byte[] resizeBuffer(final Context context, final int minCapacity) {
307 // Overflow-conscious code treats the min and new capacity as unsigned.
308 final int oldCapacity = context.buffer.length;
309 int newCapacity = oldCapacity * BaseNCodec.DEFAULT_BUFFER_RESIZE_FACTOR;
310 if (BaseNCodec.compareUnsigned(newCapacity, minCapacity) < 0) {
311 newCapacity = minCapacity;
312 }
313 if (BaseNCodec.compareUnsigned(newCapacity, BaseNCodec.MAX_BUFFER_SIZE) > 0) {
314 newCapacity = BaseNCodec.createPositiveCapacity(minCapacity);
315 }
316 final var b = new byte[newCapacity];
317 System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
318 context.buffer = b;
319 return b;
320 }
321
322 /**
323 * Returns the amount of buffered data available for reading.
324 *
325 * @param context the context to be used
326 * @return The amount of buffered data available for reading.
327 */
328 private int available(final Context context) { // package protected for access from I/O streams
329 return context.buffer != null ? context.pos - context.readPos : 0;
330 }
331
332 /**
333 * Tests a given byte array to see if it contains any characters within the alphabet or PAD.
334 *
335 * Intended for use in checking line-ending arrays
336 *
337 * @param arrayOctet
338 * byte array to test
339 * @return {@code true} if any byte is a valid character in the alphabet or PAD; {@code false} otherwise
340 */
341 protected boolean containsAlphabetOrPad(final byte[] arrayOctet) {
342 if (arrayOctet == null) {
343 return false;
344 }
345 for (final byte element : arrayOctet) {
346 if (this.pad == element || this.isInAlphabet(element)) {
347 return true;
348 }
349 }
350 return false;
351 }
352
353 /**
354 * Decodes a byte[] containing characters in the Base-N alphabet.
355 *
356 * @param pArray
357 * A byte array containing Base-N character data
358 * @return a byte array containing binary data
359 */
360 @Override
361 public byte[] decode(final byte[] pArray) {
362 if (pArray == null || pArray.length == 0) {
363 return pArray;
364 }
365 final var context = new Context();
366 this.decode(pArray, 0, pArray.length, context);
367 this.decode(pArray, 0, BaseNCodec.EOF, context); // Notify decoder of EOF.
368 final var result = new byte[context.pos];
369 this.readResults(result, 0, result.length, context);
370 return result;
371 }
372
373 // package protected for access from I/O streams
374 public abstract void decode(byte[] pArray, int i, int length, Context context);
375
376 /**
377 * Decodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of
378 * the Decoder interface, and will throw a DecoderException if the supplied object is not of type byte[] or String.
379 *
380 * @param obj
381 * Object to decode
382 * @return An object (of type byte[]) containing the binary data which corresponds to the byte[] or String
383 * supplied.
384 * @throws DecoderException
385 * if the parameter supplied is not of type byte[]
386 */
387 @Override
388 public Object decode(final Object obj) throws DecoderException {
389 if (obj instanceof byte[]) {
390 return this.decode((byte[]) obj);
391 } else if (obj instanceof String) {
392 return this.decode((String) obj);
393 } else {
394 throw new DecoderException("Parameter supplied to Base-N decode is not a byte[] or a String");
395 }
396 }
397
398 /**
399 * Decodes a String containing characters in the Base-N alphabet.
400 *
401 * @param pArray
402 * A String containing Base-N character data
403 * @return a byte array containing binary data
404 */
405 public byte[] decode(final String pArray) {
406 return this.decode(StringUtils.getBytesUtf8(pArray));
407 }
408
409 /**
410 * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet.
411 *
412 * @param pArray
413 * a byte array containing binary data
414 * @return A byte array containing only the base N alphabetic character data
415 */
416 @Override
417 public byte[] encode(final byte[] pArray) {
418 if (pArray == null || pArray.length == 0) {
419 return pArray;
420 }
421 return this.encode(pArray, 0, pArray.length);
422 }
423
424 /**
425 * Encodes a byte[] containing binary data, into a byte[] containing
426 * characters in the alphabet.
427 *
428 * @param pArray
429 * a byte array containing binary data
430 * @param offset
431 * initial offset of the subarray.
432 * @param length
433 * length of the subarray.
434 * @return A byte array containing only the base N alphabetic character data
435 * @since 1.11
436 */
437 public byte[] encode(final byte[] pArray, final int offset, final int length) {
438 if (pArray == null || pArray.length == 0) {
439 return pArray;
440 }
441 final var context = new Context();
442 this.encode(pArray, offset, length, context);
443 this.encode(pArray, offset, BaseNCodec.EOF, context); // Notify encoder of EOF.
444 final var buf = new byte[context.pos - context.readPos];
445 this.readResults(buf, 0, buf.length, context);
446 return buf;
447 }
448
449 // package protected for access from I/O streams
450 public abstract void encode(byte[] pArray, int i, int length, Context context);
451
452 /**
453 * Encodes an Object using the Base-N algorithm. This method is provided in order to satisfy the requirements of
454 * the Encoder interface, and will throw an EncoderException if the supplied object is not of type byte[].
455 *
456 * @param obj
457 * Object to encode
458 * @return An object (of type byte[]) containing the Base-N encoded data which corresponds to the byte[] supplied.
459 * @throws EncoderException
460 * if the parameter supplied is not of type byte[]
461 */
462 @Override
463 public Object encode(final Object obj) throws EncoderException {
464 if (!(obj instanceof byte[])) {
465 throw new EncoderException("Parameter supplied to Base-N encode is not a byte[]");
466 }
467 return this.encode((byte[]) obj);
468 }
469
470 /**
471 * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet.
472 * Uses UTF8 encoding.
473 *
474 * @param pArray a byte array containing binary data
475 * @return String containing only character data in the appropriate alphabet.
476 * @since 1.5
477 * This is a duplicate of {@link #encodeToString(byte[])}; it was merged during refactoring.
478 */
479 public String encodeAsString(final byte[] pArray){
480 return StringUtils.newStringUtf8(this.encode(pArray));
481 }
482
483 /**
484 * Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet.
485 * Uses UTF8 encoding.
486 *
487 * @param pArray
488 * a byte array containing binary data
489 * @return A String containing only Base-N character data
490 */
491 public String encodeToString(final byte[] pArray) {
492 return StringUtils.newStringUtf8(this.encode(pArray));
493 }
494
495 /**
496 * Ensure that the buffer has room for {@code size} bytes
497 *
498 * @param size minimum spare space required
499 * @param context the context to be used
500 * @return the buffer
501 */
502 protected byte[] ensureBufferSize(final int size, final Context context) {
503 if (context.buffer == null) {
504 context.buffer = new byte[Math.max(size, this.getDefaultBufferSize())];
505 context.pos = 0;
506 context.readPos = 0;
507 // Overflow-conscious:
508 // x + y > z == x + y - z > 0
509 } else if (context.pos + size - context.buffer.length > 0) {
510 return BaseNCodec.resizeBuffer(context, context.pos + size);
511 }
512 return context.buffer;
513 }
514
515 /**
516 * Returns the decoding behavior policy.
517 *
518 * <p>
519 * The default is lenient. If the decoding policy is strict, then decoding will raise an
520 * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding. Decoding will compose
521 * trailing bits into 8-bit bytes and discard the remainder.
522 * </p>
523 *
524 * @since 1.15
525 */
526 public CodecPolicy getCodecPolicy() {
527 return this.decodingPolicy;
528 }
529
530 /**
531 * Get the default buffer size. Can be overridden.
532 *
533 * @return the default buffer size.
534 */
535 protected int getDefaultBufferSize() {
536 return BaseNCodec.DEFAULT_BUFFER_SIZE;
537 }
538
539 /**
540 * Calculates the amount of space needed to encode the supplied array.
541 *
542 * @param pArray byte[] array which will later be encoded
543 *
544 * @return amount of space needed to encode the supplied array.
545 * Returns a long since a max-len array will require > Integer.MAX_VALUE
546 */
547 public long getEncodedLength(final byte[] pArray) {
548 // Calculate non-chunked size - rounded up to allow for padding
549 // cast to long is needed to avoid possibility of overflow
550 long len = (pArray.length + this.unencodedBlockSize-1) / this.unencodedBlockSize * (long) this.encodedBlockSize;
551 if (this.lineLength > 0) { // We're using chunking
552 // Round up to nearest multiple
553 len += (len + this.lineLength-1) / this.lineLength * this.chunkSeparatorLength;
554 }
555 return len;
556 }
557
558 /**
559 * Returns true if this object has buffered data for reading.
560 *
561 * @param context the context to be used
562 * @return true if there is data still available for reading.
563 */
564 public boolean hasData(final Context context) { // package protected for access from I/O streams
565 return context.buffer != null;
566 }
567
568 /**
569 * Returns whether the {@code octet} is in the current alphabet.
570 * Does not allow whitespace or pad.
571 *
572 * @param value The value to test
573 *
574 * @return {@code true} if the value is defined in the current alphabet, {@code false} otherwise.
575 */
576 protected abstract boolean isInAlphabet(byte value);
577
578 /**
579 * Tests a given byte array to see if it contains only valid characters within the alphabet.
580 * The method optionally treats whitespace and pad as valid.
581 *
582 * @param arrayOctet byte array to test
583 * @param allowWSPad if {@code true}, then whitespace and PAD are also allowed
584 *
585 * @return {@code true} if all bytes are valid characters in the alphabet or if the byte array is empty;
586 * {@code false}, otherwise
587 */
588 public boolean isInAlphabet(final byte[] arrayOctet, final boolean allowWSPad) {
589 for (final byte octet : arrayOctet) {
590 if (
591 !this.isInAlphabet(octet)
592 && (!allowWSPad || (octet != this.pad)
593 && !BaseNCodec.isWhiteSpace(octet))
594 ) {
595 return false;
596 }
597 }
598 return true;
599 }
600
601 /**
602 * Tests a given String to see if it contains only valid characters within the alphabet.
603 * The method treats whitespace and PAD as valid.
604 *
605 * @param basen String to test
606 * @return {@code true} if all characters in the String are valid characters in the alphabet or if
607 * the String is empty; {@code false}, otherwise
608 * @see #isInAlphabet(byte[], boolean)
609 */
610 public boolean isInAlphabet(final String basen) {
611 return this.isInAlphabet(StringUtils.getBytesUtf8(basen), true);
612 }
613
614 /**
615 * Returns true if decoding behavior is strict. Decoding will raise an {@link IllegalArgumentException} if trailing
616 * bits are not part of a valid encoding.
617 *
618 * <p>
619 * The default is false for lenient decoding. Decoding will compose trailing bits into 8-bit bytes and discard the
620 * remainder.
621 * </p>
622 *
623 * @return true if using strict decoding
624 * @since 1.15
625 */
626 public boolean isStrictDecoding() {
627 return this.decodingPolicy == CodecPolicy.STRICT;
628 }
629
630 /**
631 * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail
632 * bytes. Returns how many bytes were actually extracted.
633 * <p>
634 * Package protected for access from I/O streams.
635 *
636 * @param b byte[] array to extract the buffered data into.
637 * @param bPos position in byte[] array to start extraction at.
638 * @param bAvail amount of bytes we're allowed to extract. We may extract fewer (if fewer are available).
639 * @param context the context to be used
640 */
641 private void readResults(final byte[] b, final int bPos, final int bAvail, final Context context) {
642 if (context.buffer != null) {
643 final int len = Math.min(this.available(context), bAvail);
644 System.arraycopy(context.buffer, context.readPos, b, bPos, len);
645 context.readPos += len;
646 if (context.readPos >= context.pos) {
647 context.buffer = null; // so hasData() will return false, and this method can return -1
648 }
649 }
650 }
651
652 /**
653 * Holds thread context so classes can be thread-safe.
654 *
655 * This class is not itself thread-safe; each thread must allocate its own copy.
656 *
657 * @since 1.7
658 */
659 protected static class Context {
660
661 /**
662 * Placeholder for the bytes we're dealing with for our based logic.
663 * Bitwise operations store and extract the encoding or decoding from this variable.
664 */
665 protected int ibitWorkArea;
666
667 /**
668 * Placeholder for the bytes we're dealing with for our based logic.
669 * Bitwise operations store and extract the encoding or decoding from this variable.
670 */
671 protected long lbitWorkArea;
672
673 /**
674 * Buffer for streaming.
675 */
676 protected byte[] buffer;
677
678 /**
679 * Position where next character should be written in the buffer.
680 */
681 protected int pos;
682
683 /**
684 * Position where next character should be read from the buffer.
685 */
686 protected int readPos;
687
688 /**
689 * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless,
690 * and must be thrown away.
691 */
692 protected boolean eof;
693
694 /**
695 * Variable tracks how many characters have been written to the current line. Only used when encoding. We use
696 * it to make sure each encoded line never goes beyond lineLength (if lineLength > 0).
697 */
698 protected int currentLinePos;
699
700 /**
701 * Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. This
702 * variable helps track that.
703 */
704 protected int modulus;
705
706 /**
707 * Returns a String useful for debugging (especially within a debugger.)
708 *
709 * @return a String useful for debugging.
710 */
711 @SuppressWarnings("boxing") // OK to ignore boxing here
712 @Override
713 public String toString() {
714 return String.format(
715 "%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, lbitWorkArea=%s, modulus=%s, pos=%s, readPos=%s]",
716 this.getClass().getSimpleName(),
717 Arrays.toString(this.buffer),
718 this.currentLinePos,
719 this.eof,
720 this.ibitWorkArea,
721 this.lbitWorkArea,
722 this.modulus,
723 this.pos,
724 this.readPos
725 );
726 }
727 }
728 }
729