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