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