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