1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.netty.handler.codec.base64;
21
22 import io.netty.buffer.ByteBuf;
23 import io.netty.buffer.ByteBufAllocator;
24 import io.netty.buffer.Unpooled;
25 import io.netty.util.ByteProcessor;
26 import io.netty.util.internal.ObjectUtil;
27 import io.netty.util.internal.PlatformDependent;
28
29 import java.nio.ByteOrder;
30
31
32
33
34
35
36
37
38
39 public final class Base64 {
40
41
42 private static final int MAX_LINE_LENGTH = 76;
43
44
45 private static final byte EQUALS_SIGN = (byte) '=';
46
47
48 private static final byte NEW_LINE = (byte) '\n';
49
50 private static final byte WHITE_SPACE_ENC = -5;
51
52 private static final byte EQUALS_SIGN_ENC = -1;
53
54 private static byte[] alphabet(Base64Dialect dialect) {
55 return ObjectUtil.checkNotNull(dialect, "dialect").alphabet;
56 }
57
58 private static byte[] decodabet(Base64Dialect dialect) {
59 return ObjectUtil.checkNotNull(dialect, "dialect").decodabet;
60 }
61
62 private static boolean breakLines(Base64Dialect dialect) {
63 return ObjectUtil.checkNotNull(dialect, "dialect").breakLinesByDefault;
64 }
65
66 public static ByteBuf encode(ByteBuf src) {
67 return encode(src, Base64Dialect.STANDARD);
68 }
69
70 public static ByteBuf encode(ByteBuf src, Base64Dialect dialect) {
71 return encode(src, breakLines(dialect), dialect);
72 }
73
74 public static ByteBuf encode(ByteBuf src, Base64Dialect dialect, boolean addPadding) {
75 return encode(src, breakLines(dialect), dialect, addPadding);
76 }
77
78 public static ByteBuf encode(ByteBuf src, boolean breakLines) {
79 return encode(src, breakLines, Base64Dialect.STANDARD);
80 }
81
82 public static ByteBuf encode(ByteBuf src, boolean breakLines, boolean addPadding) {
83 return encode(src, breakLines, Base64Dialect.STANDARD, addPadding);
84 }
85
86 public static ByteBuf encode(ByteBuf src, boolean breakLines, Base64Dialect dialect) {
87 ObjectUtil.checkNotNull(src, "src");
88
89 ByteBuf dest = encode(src, src.readerIndex(), src.readableBytes(), breakLines, dialect);
90 src.readerIndex(src.writerIndex());
91 return dest;
92 }
93
94 public static ByteBuf encode(ByteBuf src, boolean breakLines, Base64Dialect dialect, boolean addPadding) {
95 ObjectUtil.checkNotNull(src, "src");
96
97 ByteBuf dest = encode(src, src.readerIndex(), src.readableBytes(), breakLines, dialect, addPadding);
98 src.readerIndex(src.writerIndex());
99 return dest;
100 }
101
102 public static ByteBuf encode(ByteBuf src, int off, int len) {
103 return encode(src, off, len, Base64Dialect.STANDARD);
104 }
105
106 public static ByteBuf encode(ByteBuf src, int off, int len, Base64Dialect dialect) {
107 return encode(src, off, len, breakLines(dialect), dialect);
108 }
109
110 public static ByteBuf encode(
111 ByteBuf src, int off, int len, boolean breakLines) {
112 return encode(src, off, len, breakLines, Base64Dialect.STANDARD);
113 }
114
115 public static ByteBuf encode(
116 ByteBuf src, int off, int len, boolean breakLines, Base64Dialect dialect) {
117 return encode(src, off, len, breakLines, dialect, src.alloc(), true);
118 }
119
120 public static ByteBuf encode(
121 ByteBuf src, int off, int len, boolean breakLines, Base64Dialect dialect, boolean addPadding) {
122 return encode(src, off, len, breakLines, dialect, src.alloc(), addPadding);
123 }
124
125 public static ByteBuf encode(
126 ByteBuf src, int off, int len, boolean breakLines, Base64Dialect dialect, ByteBufAllocator allocator) {
127 return encode(src, off, len, breakLines, dialect, allocator, true);
128 }
129
130 private static ByteBuf encode(
131 ByteBuf src, int off, int len, boolean breakLines, Base64Dialect dialect, ByteBufAllocator allocator,
132 boolean addPadding) {
133 ObjectUtil.checkNotNull(src, "src");
134 ObjectUtil.checkNotNull(dialect, "dialect");
135
136 int capacity = encodedBufferSize(len, breakLines);
137 ByteBuf destBuf = allocator.buffer(capacity).order(src.order());
138
139 ByteBuf dest = destBuf.unwrap() == null || !destBuf.isContiguous() ? destBuf :
140 destBuf.hasArray() ?
141 Unpooled.wrappedBuffer(destBuf.array(), destBuf.arrayOffset(), capacity).order(src.order()) :
142 Unpooled.wrappedBuffer(destBuf.internalNioBuffer(0, capacity)).order(src.order());
143
144 byte[] alphabet = alphabet(dialect);
145 int d = 0;
146 int e = 0;
147 int len2 = len - 2;
148 int lineLength = 0;
149 for (; d < len2; d += 3, e += 4) {
150 encode3to4(src, d + off, 3, dest, e, alphabet, addPadding);
151
152 lineLength += 4;
153
154 if (breakLines && lineLength == MAX_LINE_LENGTH) {
155 dest.setByte(e + 4, NEW_LINE);
156 e ++;
157 lineLength = 0;
158 }
159 }
160
161 if (d < len) {
162 e += encode3to4(src, d + off, len - d, dest, e, alphabet, addPadding);
163 }
164
165
166 if (e > 1 && dest.getByte(e - 1) == NEW_LINE) {
167 e--;
168 }
169 if (dest != destBuf) {
170 dest.release();
171 }
172 return destBuf.setIndex(0, e);
173 }
174
175 private static int encode3to4(
176 ByteBuf src, int srcOffset, int numSigBytes, ByteBuf dest, int destOffset, byte[] alphabet,
177 boolean addPadding) {
178
179
180
181
182
183
184
185
186
187
188
189 if (src.order() == ByteOrder.BIG_ENDIAN) {
190 final int inBuff;
191 switch (numSigBytes) {
192 case 1:
193 inBuff = toInt(src.getByte(srcOffset));
194 break;
195 case 2:
196 inBuff = toIntBE(src.getShort(srcOffset));
197 break;
198 default:
199 inBuff = numSigBytes <= 0 ? 0 : toIntBE(src.getMedium(srcOffset));
200 break;
201 }
202 return encode3to4BigEndian(inBuff, numSigBytes, dest, destOffset, alphabet, addPadding);
203 } else {
204 final int inBuff;
205 switch (numSigBytes) {
206 case 1:
207 inBuff = toInt(src.getByte(srcOffset));
208 break;
209 case 2:
210 inBuff = toIntLE(src.getShort(srcOffset));
211 break;
212 default:
213 inBuff = numSigBytes <= 0 ? 0 : toIntLE(src.getMedium(srcOffset));
214 break;
215 }
216 return encode3to4LittleEndian(inBuff, numSigBytes, dest, destOffset, alphabet, addPadding);
217 }
218 }
219
220
221 static int encodedBufferSize(int len, boolean breakLines) {
222
223 long len43 = ((long) len << 2) / 3;
224
225
226 long ret = (len43 + 3) & ~3;
227
228 if (breakLines) {
229 ret += len43 / MAX_LINE_LENGTH;
230 }
231
232 return ret < Integer.MAX_VALUE ? (int) ret : Integer.MAX_VALUE;
233 }
234
235 private static int toInt(byte value) {
236 return (value & 0xff) << 16;
237 }
238
239 private static int toIntBE(short value) {
240 return (value & 0xff00) << 8 | (value & 0xff) << 8;
241 }
242
243 private static int toIntLE(short value) {
244 return (value & 0xff) << 16 | (value & 0xff00);
245 }
246
247 private static int toIntBE(int mediumValue) {
248 return (mediumValue & 0xff0000) | (mediumValue & 0xff00) | (mediumValue & 0xff);
249 }
250
251 private static int toIntLE(int mediumValue) {
252 return (mediumValue & 0xff) << 16 | (mediumValue & 0xff00) | (mediumValue & 0xff0000) >>> 16;
253 }
254
255 private static int encode3to4BigEndian(
256 int inBuff, int numSigBytes, ByteBuf dest, int destOffset, byte[] alphabet, boolean addPadding) {
257
258 switch (numSigBytes) {
259 case 3:
260 dest.setInt(destOffset, alphabet[inBuff >>> 18 ] << 24 |
261 alphabet[inBuff >>> 12 & 0x3f] << 16 |
262 alphabet[inBuff >>> 6 & 0x3f] << 8 |
263 alphabet[inBuff & 0x3f]);
264 return 4;
265 case 2:
266 if (addPadding) {
267 dest.setInt(destOffset, alphabet[inBuff >>> 18 ] << 24 |
268 alphabet[inBuff >>> 12 & 0x3f] << 16 |
269 alphabet[inBuff >>> 6 & 0x3f] << 8 |
270 EQUALS_SIGN);
271 return 4;
272 } else {
273 dest.setMedium(destOffset, alphabet[inBuff >>> 18 ] << 16 |
274 alphabet[inBuff >>> 12 & 0x3f] << 8 |
275 alphabet[inBuff >>> 6 & 0x3f ]);
276 return 3;
277 }
278 case 1:
279 if (addPadding) {
280 dest.setInt(destOffset, alphabet[inBuff >>> 18 ] << 24 |
281 alphabet[inBuff >>> 12 & 0x3f] << 16 |
282 EQUALS_SIGN << 8 |
283 EQUALS_SIGN);
284 return 4;
285 } else {
286 dest.setShort(destOffset, alphabet[inBuff >>> 18 ] << 8 |
287 alphabet[inBuff >>> 12 & 0x3f]);
288 return 2;
289 }
290 default:
291
292 return 0;
293 }
294 }
295
296 private static int encode3to4LittleEndian(
297 int inBuff, int numSigBytes, ByteBuf dest, int destOffset, byte[] alphabet, boolean addPadding) {
298
299 switch (numSigBytes) {
300 case 3:
301 dest.setInt(destOffset, alphabet[inBuff >>> 18 ] |
302 alphabet[inBuff >>> 12 & 0x3f] << 8 |
303 alphabet[inBuff >>> 6 & 0x3f] << 16 |
304 alphabet[inBuff & 0x3f] << 24);
305 return 4;
306 case 2:
307 if (addPadding) {
308 dest.setInt(destOffset, alphabet[inBuff >>> 18 ] |
309 alphabet[inBuff >>> 12 & 0x3f ] << 8 |
310 alphabet[inBuff >>> 6 & 0x3f ] << 16 |
311 EQUALS_SIGN << 24);
312 return 4;
313 } else {
314 dest.setMedium(destOffset, alphabet[inBuff >>> 18 ] |
315 alphabet[inBuff >>> 12 & 0x3f] << 8 |
316 alphabet[inBuff >>> 6 & 0x3f] << 16);
317 return 3;
318 }
319 case 1:
320 if (addPadding) {
321 dest.setInt(destOffset, alphabet[inBuff >>> 18 ] |
322 alphabet[inBuff >>> 12 & 0x3f] << 8 |
323 EQUALS_SIGN << 16 |
324 EQUALS_SIGN << 24);
325 return 4;
326 } else {
327 dest.setShort(destOffset, alphabet[inBuff >>> 18 ] |
328 alphabet[inBuff >>> 12 & 0x3f] << 8);
329 return 2;
330 }
331 default:
332
333 return 0;
334 }
335 }
336
337 public static ByteBuf decode(ByteBuf src) {
338 return decode(src, Base64Dialect.STANDARD);
339 }
340
341 public static ByteBuf decode(ByteBuf src, Base64Dialect dialect) {
342 ObjectUtil.checkNotNull(src, "src");
343
344 ByteBuf dest = decode(src, src.readerIndex(), src.readableBytes(), dialect);
345 src.readerIndex(src.writerIndex());
346 return dest;
347 }
348
349 public static ByteBuf decode(
350 ByteBuf src, int off, int len) {
351 return decode(src, off, len, Base64Dialect.STANDARD);
352 }
353
354 public static ByteBuf decode(
355 ByteBuf src, int off, int len, Base64Dialect dialect) {
356 return decode(src, off, len, dialect, src.alloc());
357 }
358
359 public static ByteBuf decode(
360 ByteBuf src, int off, int len, Base64Dialect dialect, ByteBufAllocator allocator) {
361 ObjectUtil.checkNotNull(src, "src");
362 ObjectUtil.checkNotNull(dialect, "dialect");
363
364
365 return new Decoder().decode(src, off, len, allocator, dialect);
366 }
367
368
369 static int decodedBufferSize(int len) {
370 return len - (len >>> 2);
371 }
372
373 private static final class Decoder implements ByteProcessor {
374 private final byte[] b4 = new byte[4];
375 private int b4Posn;
376 private byte[] decodabet;
377 private int outBuffPosn;
378 private ByteBuf dest;
379
380 ByteBuf decode(ByteBuf src, int off, int len, ByteBufAllocator allocator, Base64Dialect dialect) {
381 dest = allocator.buffer(decodedBufferSize(len)).order(src.order());
382
383 decodabet = decodabet(dialect);
384 try {
385 src.forEachByte(off, len, this);
386
387
388 if (b4Posn == 1) {
389 throw new IllegalArgumentException(
390 "Invalid Base64 input, single remaining character implies incorrect length or padding");
391 } else if (b4Posn == 2) {
392 b4[2] = EQUALS_SIGN;
393 b4[3] = EQUALS_SIGN;
394 outBuffPosn += decode4to3(b4, dest, outBuffPosn, decodabet);
395 } else if (b4Posn == 3) {
396 b4[3] = EQUALS_SIGN;
397 outBuffPosn += decode4to3(b4, dest, outBuffPosn, decodabet);
398 }
399
400 return dest.slice(0, outBuffPosn);
401 } catch (Throwable cause) {
402 dest.release();
403 PlatformDependent.throwException(cause);
404 return null;
405 }
406 }
407
408 @Override
409 public boolean process(byte value) throws Exception {
410 if (value > 0) {
411 byte sbiDecode = decodabet[value];
412 if (sbiDecode >= WHITE_SPACE_ENC) {
413 if (sbiDecode >= EQUALS_SIGN_ENC) {
414 b4[b4Posn ++] = value;
415 if (b4Posn > 3) {
416 outBuffPosn += decode4to3(b4, dest, outBuffPosn, decodabet);
417 b4Posn = 0;
418
419
420 return value != EQUALS_SIGN;
421 }
422 }
423 return true;
424 }
425 }
426 throw new IllegalArgumentException(
427 "invalid Base64 input character: " + (short) (value & 0xFF) + " (decimal)");
428 }
429
430 private static int decode4to3(byte[] src, ByteBuf dest, int destOffset, byte[] decodabet) {
431 final byte src0 = src[0];
432 final byte src1 = src[1];
433 final byte src2 = src[2];
434 final int decodedValue;
435 if (src2 == EQUALS_SIGN) {
436
437 try {
438 decodedValue = (decodabet[src0] & 0xff) << 2 | (decodabet[src1] & 0xff) >>> 4;
439 } catch (IndexOutOfBoundsException ignored) {
440 throw new IllegalArgumentException("not encoded in Base64");
441 }
442 dest.setByte(destOffset, decodedValue);
443 return 1;
444 }
445
446 final byte src3 = src[3];
447 if (src3 == EQUALS_SIGN) {
448
449 final byte b1 = decodabet[src1];
450
451 try {
452 if (dest.order() == ByteOrder.BIG_ENDIAN) {
453
454
455 decodedValue = ((decodabet[src0] & 0x3f) << 2 | (b1 & 0xf0) >> 4) << 8 |
456 (b1 & 0xf) << 4 | (decodabet[src2] & 0xfc) >>> 2;
457 } else {
458
459 decodedValue = (decodabet[src0] & 0x3f) << 2 | (b1 & 0xf0) >> 4 |
460 ((b1 & 0xf) << 4 | (decodabet[src2] & 0xfc) >>> 2) << 8;
461 }
462 } catch (IndexOutOfBoundsException ignored) {
463 throw new IllegalArgumentException("not encoded in Base64");
464 }
465 dest.setShort(destOffset, decodedValue);
466 return 2;
467 }
468
469
470 try {
471 if (dest.order() == ByteOrder.BIG_ENDIAN) {
472 decodedValue = (decodabet[src0] & 0x3f) << 18 |
473 (decodabet[src1] & 0xff) << 12 |
474 (decodabet[src2] & 0xff) << 6 |
475 decodabet[src3] & 0xff;
476 } else {
477 final byte b1 = decodabet[src1];
478 final byte b2 = decodabet[src2];
479
480
481
482
483
484 decodedValue = (decodabet[src0] & 0x3f) << 2 |
485
486 (b1 & 0xf) << 12 |
487
488 (b1 & 0xf0) >>> 4 |
489
490 (b2 & 0x3) << 22 |
491
492 (b2 & 0xfc) << 6 |
493 (decodabet[src3] & 0xff) << 16;
494 }
495 } catch (IndexOutOfBoundsException ignored) {
496 throw new IllegalArgumentException("not encoded in Base64");
497 }
498 dest.setMedium(destOffset, decodedValue);
499 return 3;
500 }
501 }
502
503 private Base64() {
504
505 }
506 }