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