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