1
1
package datadog .trace .api ;
2
2
3
- import com .google .common .io .BaseEncoding ;
4
3
import java .nio .charset .StandardCharsets ;
4
+ import java .util .Arrays ;
5
5
import java .util .concurrent .ThreadLocalRandom ;
6
6
import lombok .extern .slf4j .Slf4j ;
7
7
@@ -18,7 +18,7 @@ public class DDId {
18
18
public static final DDId ZERO = new DDId (0 , "0" );
19
19
public static final DDId MAX = new DDId (-1 , "18446744073709551615" ); // All bits set
20
20
21
- private static final BaseEncoding BASE64 = BaseEncoding . base64 ();
21
+ private static final Base64Decoder BASE64 = new Base64Decoder ();
22
22
23
23
// Convenience constant used from tests
24
24
private static final DDId ONE = DDId .from (1 );
@@ -66,13 +66,17 @@ public static DDId from(String s) throws NumberFormatException {
66
66
// we have reports of Kakfa mirror maker base64 encoding record headers
67
67
// attempting to base64 decode the ids here rather than in the Kafka instrumentation
68
68
// helps users understand they have a problem without making other users pay for it
69
- if (null != s && BASE64 .canDecode (s )) {
70
- String decoded = new String (BASE64 .decode (s ), StandardCharsets .ISO_8859_1 );
71
- log .debug (
72
- "id {} was base 64 encoded to {}, it was decoded but this indicates there is a problem elsewhere in your system" ,
73
- decoded ,
74
- s );
75
- return DDId .create (parseUnsignedLong (decoded ), decoded );
69
+ if (null != s ) {
70
+ try {
71
+ String decoded = new String (BASE64 .decode (s ), StandardCharsets .ISO_8859_1 );
72
+ log .debug (
73
+ "id {} was base 64 encoded to {}, it was decoded but this indicates there is a problem elsewhere in your system" ,
74
+ decoded ,
75
+ s );
76
+ return DDId .create (parseUnsignedLong (decoded ), decoded );
77
+ } catch (Exception ignored ) {
78
+
79
+ }
76
80
}
77
81
throw e ;
78
82
}
@@ -94,13 +98,16 @@ public static DDId fromHex(String s) throws NumberFormatException {
94
98
// we have reports of Kakfa mirror maker base64 encoding record headers
95
99
// attempting to base64 decode the ids here rather than in the Kafka instrumentation
96
100
// helps users understand they have a problem without making other users pay for it
97
- if (null != s && BASE64 .canDecode (s )) {
98
- String decoded = new String (BASE64 .decode (s ), StandardCharsets .ISO_8859_1 );
99
- log .debug (
100
- "id {} was base 64 encoded to {}, it was decoded but this indicates there is a problem elsewhere in your system" ,
101
- decoded ,
102
- s );
103
- return DDId .create (parseUnsignedLongHex (decoded ), null );
101
+ if (null != s ) {
102
+ try {
103
+ String decoded = new String (BASE64 .decode (s ), StandardCharsets .ISO_8859_1 );
104
+ log .debug (
105
+ "id {} was base 64 encoded to {}, it was decoded but this indicates there is a problem elsewhere in your system" ,
106
+ decoded ,
107
+ s );
108
+ return DDId .create (parseUnsignedLongHex (decoded ), null );
109
+ } catch (Exception ignored ) {
110
+ }
104
111
}
105
112
throw e ;
106
113
}
@@ -209,6 +216,133 @@ private static long parseUnsignedLongHex(String s) throws NumberFormatException
209
216
}
210
217
}
211
218
219
+ // TODO - can be removed when JDK7 is dropped (adapted from JDK8 source)
220
+ private static class Base64Decoder {
221
+
222
+ private static final int [] BASE_64 = new int [256 ];
223
+
224
+ static {
225
+ Arrays .fill (BASE_64 , -1 );
226
+ int i = 0 ;
227
+ for (char c = 'A' ; c <= 'Z' ; ++c ) {
228
+ BASE_64 [c ] = i ++;
229
+ }
230
+ for (char c = 'a' ; c <= 'z' ; ++c ) {
231
+ BASE_64 [c ] = i ++;
232
+ }
233
+ for (char c = '0' ; c <= '9' ; ++c ) {
234
+ BASE_64 [c ] = i ++;
235
+ }
236
+ BASE_64 ['+' ] = i ++;
237
+ BASE_64 ['/' ] = i ;
238
+ BASE_64 ['=' ] = -2 ;
239
+ }
240
+
241
+ public byte [] decode (byte [] src ) {
242
+ byte [] dst = new byte [outLength (src , 0 , src .length )];
243
+ int ret = decode0 (src , 0 , src .length , dst );
244
+ if (ret != dst .length ) {
245
+ dst = Arrays .copyOf (dst , ret );
246
+ }
247
+ return dst ;
248
+ }
249
+
250
+ public byte [] decode (String src ) {
251
+ return decode (src .getBytes (StandardCharsets .ISO_8859_1 ));
252
+ }
253
+
254
+ private int outLength (byte [] src , int sp , int sl ) {
255
+ int paddings = 0 ;
256
+ int len = sl - sp ;
257
+ if (len == 0 ) return 0 ;
258
+ if (len < 2 ) {
259
+ if (BASE_64 [0 ] == -1 ) return 0 ;
260
+ throw new IllegalArgumentException (
261
+ "Input byte[] should at least have 2 bytes for base64 bytes" );
262
+ }
263
+ // scan all bytes to fill out all non-alphabet. a performance
264
+ // trade-off of pre-scan or Arrays.copyOf
265
+ int n = 0 ;
266
+ while (sp < sl ) {
267
+ int b = src [sp ++] & 0xff ;
268
+ if (b == '=' ) {
269
+ len -= (sl - sp + 1 );
270
+ break ;
271
+ }
272
+ if ((b = BASE_64 [b ]) == -1 ) n ++;
273
+ }
274
+ len -= n ;
275
+ if ((len & 0x3 ) != 0 ) paddings = 4 - (len & 0x3 );
276
+ return 3 * ((len + 3 ) / 4 ) - paddings ;
277
+ }
278
+
279
+ private int decode0 (byte [] src , int sp , int sl , byte [] dst ) {
280
+ int dp = 0 ;
281
+ int bits = 0 ;
282
+ int shiftto = 18 ; // pos of first byte of 4-byte atom
283
+
284
+ while (sp < sl ) {
285
+ if (shiftto == 18 && sp + 4 < sl ) { // fast path
286
+ int sl0 = sp + ((sl - sp ) & ~0b11);
287
+ while (sp < sl0 ) {
288
+ int b1 = BASE_64 [src [sp ++] & 0xff ];
289
+ int b2 = BASE_64 [src [sp ++] & 0xff ];
290
+ int b3 = BASE_64 [src [sp ++] & 0xff ];
291
+ int b4 = BASE_64 [src [sp ++] & 0xff ];
292
+ if ((b1 | b2 | b3 | b4 ) < 0 ) { // non base64 byte
293
+ sp -= 4 ;
294
+ break ;
295
+ }
296
+ int bits0 = b1 << 18 | b2 << 12 | b3 << 6 | b4 ;
297
+ dst [dp ++] = (byte ) (bits0 >> 16 );
298
+ dst [dp ++] = (byte ) (bits0 >> 8 );
299
+ dst [dp ++] = (byte ) (bits0 );
300
+ }
301
+ if (sp >= sl ) break ;
302
+ }
303
+ int b = src [sp ++] & 0xff ;
304
+ if ((b = BASE_64 [b ]) < 0 ) {
305
+ if (b == -2 ) { // padding byte '='
306
+ // = shiftto==18 unnecessary padding
307
+ // x= shiftto==12 a dangling single x
308
+ // x to be handled together with non-padding case
309
+ // xx= shiftto==6&&sp==sl missing last =
310
+ // xx=y shiftto==6 last is not =
311
+ if (shiftto == 6 && (sp == sl || src [sp ++] != '=' ) || shiftto == 18 ) {
312
+ throw new IllegalArgumentException ("Input byte array has wrong 4-byte ending unit" );
313
+ }
314
+ break ;
315
+ }
316
+ }
317
+ bits |= (b << shiftto );
318
+ shiftto -= 6 ;
319
+ if (shiftto < 0 ) {
320
+ dst [dp ++] = (byte ) (bits >> 16 );
321
+ dst [dp ++] = (byte ) (bits >> 8 );
322
+ dst [dp ++] = (byte ) (bits );
323
+ shiftto = 18 ;
324
+ bits = 0 ;
325
+ }
326
+ }
327
+ // reached end of byte array or hit padding '=' characters.
328
+ if (shiftto == 6 ) {
329
+ dst [dp ++] = (byte ) (bits >> 16 );
330
+ } else if (shiftto == 0 ) {
331
+ dst [dp ++] = (byte ) (bits >> 16 );
332
+ dst [dp ++] = (byte ) (bits >> 8 );
333
+ } else if (shiftto == 12 ) {
334
+ // dangling single "x", incorrectly encoded.
335
+ throw new IllegalArgumentException ("Last unit does not have enough valid bits" );
336
+ }
337
+ // ignore all non-base64 character
338
+ while (sp < sl ) {
339
+ if (BASE_64 [src [sp ++] & 0xff ] < 0 ) continue ;
340
+ throw new IllegalArgumentException ("Input byte array has incorrect ending byte at " + sp );
341
+ }
342
+ return dp ;
343
+ }
344
+ }
345
+
212
346
// TODO Can be removed when Java7 support is removed
213
347
private static String toUnsignedString (long l ) {
214
348
if (l >= 0 ) return Long .toString (l );
0 commit comments