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