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.zone.weixin4j.base64; 19 20 import com.zone.weixin4j.util.ServerToolkits; 21 22 import java.util.Arrays; 23 24 /** 25 * <p> 26 * <font color="red">reference of apache pivot</font> 27 * </p> 28 * 29 * Abstract superclass for Base-N encoders and decoders. 30 * 31 * <p> 32 * This class is thread-safe. 33 * </p> 34 * 35 * @version $Id: BaseNCodec.java 1465182 2013-04-06 04:03:12Z ggregory $ 36 */ 37 public abstract class BaseNCodec { 38 39 /** 40 * Holds thread context so classes can be thread-safe. 41 * 42 * This class is not itself thread-safe; each thread must allocate its own 43 * copy. 44 * 45 * @since 1.7 46 */ 47 static class Context { 48 49 /** 50 * Place holder for the bytes we're dealing with for our based logic. 51 * Bitwise operations store and extract the encoding or decoding from 52 * this variable. 53 */ 54 int ibitWorkArea; 55 56 /** 57 * Place holder for the bytes we're dealing with for our based logic. 58 * Bitwise operations store and extract the encoding or decoding from 59 * this variable. 60 */ 61 long lbitWorkArea; 62 63 /** 64 * Buffer for streaming. 65 */ 66 byte[] buffer; 67 68 /** 69 * Position where next character should be written in the buffer. 70 */ 71 int pos; 72 73 /** 74 * Position where next character should be read from the buffer. 75 */ 76 int readPos; 77 78 /** 79 * Boolean flag to indicate the EOF has been reached. Once EOF has been 80 * reached, this object becomes useless, and must be thrown away. 81 */ 82 boolean eof; 83 84 /** 85 * Variable tracks how many characters have been written to the current 86 * line. Only used when encoding. We use it to make sure each encoded 87 * line never goes beyond lineLength (if lineLength > 0). 88 */ 89 int currentLinePos; 90 91 /** 92 * Writes to the buffer only occur after every 3/5 reads when encoding, 93 * and every 4/8 reads when decoding. This variable helps track that. 94 */ 95 int modulus; 96 97 Context() { 98 } 99 100 /** 101 * Returns a String useful for debugging (especially within a debugger.) 102 * 103 * @return a String useful for debugging. 104 */ 105 @SuppressWarnings("boxing") 106 // OK to ignore boxing here 107 @Override 108 public String toString() { 109 return String.format( 110 "%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, lbitWorkArea=%s, " 111 + "modulus=%s, pos=%s, readPos=%s]", this 112 .getClass().getSimpleName(), Arrays 113 .toString(buffer), currentLinePos, eof, 114 ibitWorkArea, lbitWorkArea, modulus, pos, readPos); 115 } 116 } 117 118 /** 119 * EOF 120 * 121 * @since 1.7 122 */ 123 static final int EOF = -1; 124 125 /** 126 * MIME chunk size per RFC 2045 section 6.8. 127 * 128 * <p> 129 * The {@value} character limit does not count the trailing CRLF, but counts 130 * all other characters, including any equal signs. 131 * </p> 132 * 133 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 134 * 6.8</a> 135 */ 136 public static final int MIME_CHUNK_SIZE = 76; 137 138 /** 139 * PEM chunk size per RFC 1421 section 4.3.2.4. 140 * 141 * <p> 142 * The {@value} character limit does not count the trailing CRLF, but counts 143 * all other characters, including any equal signs. 144 * </p> 145 * 146 * @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421 section 147 * 4.3.2.4</a> 148 */ 149 public static final int PEM_CHUNK_SIZE = 64; 150 151 private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; 152 153 /** 154 * Defines the default buffer size - currently {@value} - must be large 155 * enough for at least one encoded block+separator 156 */ 157 private static final int DEFAULT_BUFFER_SIZE = 8192; 158 159 /** Mask used to extract 8 bits, used in decoding bytes */ 160 protected static final int MASK_8BITS = 0xff; 161 162 /** 163 * Byte used to pad output. 164 */ 165 protected static final byte PAD_DEFAULT = '='; // Allow static access to 166 // default 167 168 protected final byte PAD = PAD_DEFAULT; // instance variable just in case it 169 // needs to vary later 170 171 /** 172 * Number of bytes in each full block of unencoded data, e.g. 4 for Base64 173 * and 5 for Base32 174 */ 175 private final int unencodedBlockSize; 176 177 /** 178 * Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 179 * 8 for Base32 180 */ 181 private final int encodedBlockSize; 182 183 /** 184 * Chunksize for encoding. Not used when decoding. A value of zero or less 185 * implies no chunking of the encoded data. Rounded down to nearest multiple 186 * of encodedBlockSize. 187 */ 188 protected final int lineLength; 189 190 /** 191 * Size of chunk separator. Not used unless {@link #lineLength} > 0. 192 */ 193 private final int chunkSeparatorLength; 194 195 /** 196 * Note <code>lineLength</code> is rounded down to the nearest multiple of 197 * {@link #encodedBlockSize} If <code>chunkSeparatorLength</code> is zero, 198 * then chunking is disabled. 199 * 200 * @param unencodedBlockSize 201 * the size of an unencoded block (e.g. Base64 = 3) 202 * @param encodedBlockSize 203 * the size of an encoded block (e.g. Base64 = 4) 204 * @param lineLength 205 * if > 0, use chunking with a length <code>lineLength</code> 206 * @param chunkSeparatorLength 207 * the chunk separator length, if relevant 208 */ 209 protected BaseNCodec(final int unencodedBlockSize, 210 final int encodedBlockSize, final int lineLength, 211 final int chunkSeparatorLength) { 212 this.unencodedBlockSize = unencodedBlockSize; 213 this.encodedBlockSize = encodedBlockSize; 214 final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0; 215 this.lineLength = useChunking ? (lineLength / encodedBlockSize) 216 * encodedBlockSize : 0; 217 this.chunkSeparatorLength = chunkSeparatorLength; 218 } 219 220 /** 221 * Returns true if this object has buffered data for reading. 222 * 223 * @param context 224 * the context to be used 225 * @return true if there is data still available for reading. 226 */ 227 boolean hasData(final Context context) { // package protected for access 228 // from I/O streams 229 return context.buffer != null; 230 } 231 232 /** 233 * Returns the amount of buffered data available for reading. 234 * 235 * @param context 236 * the context to be used 237 * @return The amount of buffered data available for reading. 238 */ 239 int available(final Context context) { // package protected for access from 240 // I/O streams 241 return context.buffer != null ? context.pos - context.readPos : 0; 242 } 243 244 /** 245 * Get the default buffer size. Can be overridden. 246 * 247 * @return {@link #DEFAULT_BUFFER_SIZE} 248 */ 249 protected int getDefaultBufferSize() { 250 return DEFAULT_BUFFER_SIZE; 251 } 252 253 /** 254 * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}. 255 * 256 * @param context 257 * the context to be used 258 */ 259 private byte[] resizeBuffer(final Context context) { 260 if (context.buffer == null) { 261 context.buffer = new byte[getDefaultBufferSize()]; 262 context.pos = 0; 263 context.readPos = 0; 264 } else { 265 final byte[] b = new byte[context.buffer.length 266 * DEFAULT_BUFFER_RESIZE_FACTOR]; 267 System.arraycopy(context.buffer, 0, b, 0, context.buffer.length); 268 context.buffer = b; 269 } 270 return context.buffer; 271 } 272 273 /** 274 * Ensure that the buffer has room for <code>size</code> bytes 275 * 276 * @param size 277 * minimum spare space required 278 * @param context 279 * the context to be used 280 */ 281 protected byte[] ensureBufferSize(final int size, final Context context) { 282 if ((context.buffer == null) 283 || (context.buffer.length < context.pos + size)) { 284 return resizeBuffer(context); 285 } 286 return context.buffer; 287 } 288 289 /** 290 * Extracts buffered data into the provided byte[] array, starting at 291 * position bPos, up to a maximum of bAvail bytes. Returns how many bytes 292 * were actually extracted. 293 * <p> 294 * Package protected for access from I/O streams. 295 * 296 * @param b 297 * byte[] array to extract the buffered data into. 298 * @param bPos 299 * position in byte[] array to start extraction at. 300 * @param bAvail 301 * amount of bytes we're allowed to extract. We may extract fewer 302 * (if fewer are available). 303 * @param context 304 * the context to be used 305 * @return The number of bytes successfully extracted into the provided 306 * byte[] array. 307 */ 308 int readResults(final byte[] b, final int bPos, final int bAvail, 309 final Context context) { 310 if (context.buffer != null) { 311 final int len = Math.min(available(context), bAvail); 312 System.arraycopy(context.buffer, context.readPos, b, bPos, len); 313 context.readPos += len; 314 if (context.readPos >= context.pos) { 315 context.buffer = null; // so hasData() will return false, and 316 // this method can return -1 317 } 318 return len; 319 } 320 return context.eof ? EOF : 0; 321 } 322 323 /** 324 * Checks if a byte value is whitespace or not. Whitespace is taken to mean: 325 * space, tab, CR, LF 326 * 327 * @param byteToCheck 328 * the byte to check 329 * @return true if byte is whitespace, false otherwise 330 */ 331 protected static boolean isWhiteSpace(final byte byteToCheck) { 332 switch (byteToCheck) { 333 case ' ': 334 case '\n': 335 case '\r': 336 case '\t': 337 return true; 338 default: 339 return false; 340 } 341 } 342 343 /** 344 * Encodes a byte[] containing binary data, into a String containing 345 * characters in the Base-N alphabet. Uses UTF8 encoding. 346 * 347 * @param pArray 348 * a byte array containing binary data 349 * @return A String containing only Base-N character data 350 */ 351 public String encodeToString(final byte[] pArray) { 352 return ServerToolkits.newStringUtf8(encode(pArray)); 353 } 354 355 /** 356 * Encodes a byte[] containing binary data, into a String containing 357 * characters in the appropriate alphabet. Uses UTF8 encoding. 358 * 359 * @param pArray 360 * a byte array containing binary data 361 * @return String containing only character data in the appropriate 362 * alphabet. 363 */ 364 public String encodeAsString(final byte[] pArray) { 365 return ServerToolkits.newStringUtf8(encode(pArray)); 366 } 367 368 369 370 /** 371 * Decodes a String containing characters in the Base-N alphabet. 372 * 373 * @param pArray 374 * A String containing Base-N character data 375 * @return a byte array containing binary data 376 */ 377 public byte[] decode(final String pArray) { 378 return decode(ServerToolkits.getBytesUtf8(pArray)); 379 } 380 381 /** 382 * Decodes a byte[] containing characters in the Base-N alphabet. 383 * 384 * @param pArray 385 * A byte array containing Base-N character data 386 * @return a byte array containing binary data 387 */ 388 public byte[] decode(final byte[] pArray) { 389 if (pArray == null || pArray.length == 0) { 390 return pArray; 391 } 392 final Context context = new Context(); 393 decode(pArray, 0, pArray.length, context); 394 decode(pArray, 0, EOF, context); // Notify decoder of EOF. 395 final byte[] result = new byte[context.pos]; 396 readResults(result, 0, result.length, context); 397 return result; 398 } 399 400 /** 401 * Encodes a byte[] containing binary data, into a byte[] containing 402 * characters in the alphabet. 403 * 404 * @param pArray 405 * a byte array containing binary data 406 * @return A byte array containing only the basen alphabetic character data 407 */ 408 public byte[] encode(final byte[] pArray) { 409 if (pArray == null || pArray.length == 0) { 410 return pArray; 411 } 412 final Context context = new Context(); 413 encode(pArray, 0, pArray.length, context); 414 encode(pArray, 0, EOF, context); // Notify encoder of EOF. 415 final byte[] buf = new byte[context.pos - context.readPos]; 416 readResults(buf, 0, buf.length, context); 417 return buf; 418 } 419 420 // package protected for access from I/O streams 421 abstract void encode(byte[] pArray, int i, int length, Context context); 422 423 // package protected for access from I/O streams 424 abstract void decode(byte[] pArray, int i, int length, Context context); 425 426 /** 427 * Returns whether or not the <code>octet</code> is in the current alphabet. 428 * Does not allow whitespace or pad. 429 * 430 * @param value 431 * The value to test 432 * 433 * @return {@code true} if the value is defined in the current alphabet, 434 * {@code false} otherwise. 435 */ 436 protected abstract boolean isInAlphabet(byte value); 437 438 /** 439 * Tests a given byte array to see if it contains only valid characters 440 * within the alphabet. The method optionally treats whitespace and pad as 441 * valid. 442 * 443 * @param arrayOctet 444 * byte array to test 445 * @param allowWSPad 446 * if {@code true}, then whitespace and PAD are also allowed 447 * 448 * @return {@code true} if all bytes are valid characters in the alphabet or 449 * if the byte array is empty; {@code false}, otherwise 450 */ 451 public boolean isInAlphabet(final byte[] arrayOctet, 452 final boolean allowWSPad) { 453 for (int i = 0; i < arrayOctet.length; i++) { 454 if (!isInAlphabet(arrayOctet[i]) 455 && (!allowWSPad || (arrayOctet[i] != PAD) 456 && !isWhiteSpace(arrayOctet[i]))) { 457 return false; 458 } 459 } 460 return true; 461 } 462 463 /** 464 * Tests a given String to see if it contains only valid characters within 465 * the alphabet. The method treats whitespace and PAD as valid. 466 * 467 * @param basen 468 * String to test 469 * @return {@code true} if all characters in the String are valid characters 470 * in the alphabet or if the String is empty; {@code false}, 471 * otherwise 472 * @see #isInAlphabet(byte[], boolean) 473 */ 474 public boolean isInAlphabet(final String basen) { 475 return isInAlphabet(ServerToolkits.getBytesUtf8(basen), true); 476 } 477 478 /** 479 * Tests a given byte array to see if it contains any characters within the 480 * alphabet or PAD. 481 * 482 * Intended for use in checking line-ending arrays 483 * 484 * @param arrayOctet 485 * byte array to test 486 * @return {@code true} if any byte is a valid character in the alphabet or 487 * PAD; {@code false} otherwise 488 */ 489 protected boolean containsAlphabetOrPad(final byte[] arrayOctet) { 490 if (arrayOctet == null) { 491 return false; 492 } 493 for (final byte element : arrayOctet) { 494 if (PAD == element || isInAlphabet(element)) { 495 return true; 496 } 497 } 498 return false; 499 } 500 501 /** 502 * Calculates the amount of space needed to encode the supplied array. 503 * 504 * @param pArray 505 * byte[] array which will later be encoded 506 * 507 * @return amount of space needed to encoded the supplied array. Returns a 508 * long since a max-len array will require > Integer.MAX_VALUE 509 */ 510 public long getEncodedLength(final byte[] pArray) { 511 // Calculate non-chunked size - rounded up to allow for padding 512 // cast to long is needed to avoid possibility of overflow 513 long len = ((pArray.length + unencodedBlockSize - 1) / unencodedBlockSize) 514 * (long) encodedBlockSize; 515 if (lineLength > 0) { // We're using chunking 516 // Round up to nearest multiple 517 len += ((len + lineLength - 1) / lineLength) * chunkSeparatorLength; 518 } 519 return len; 520 } 521 }