@@ -135,36 +135,36 @@ static boolean isURLBase64WithoutPadding(String id) {
135
135
// 'xxx=' and 'xxx' could be considered the same id
136
136
final int length = id .length ();
137
137
switch (length & 0x03 ) {
138
- case 0 :
139
- break ;
140
- case 1 :
141
- return false ;
142
- case 2 :
143
- // the last 2 symbols (12 bits) are encoding 1 byte (8 bits)
144
- // so the last symbol only actually uses 8-6=2 bits and can only take 4 values
145
- char last = id .charAt (length - 1 );
146
- if (last != 'A' && last != 'Q' && last != 'g' && last != 'w' ) {
138
+ case 0 :
139
+ break ;
140
+ case 1 :
147
141
return false ;
148
- }
149
- break ;
150
- case 3 :
151
- // The last 3 symbols (18 bits) are encoding 2 bytes (16 bits)
152
- // so the last symbol only actually uses 16-12=4 bits and can only take 16 values
153
- last = id .charAt (length - 1 );
154
- if (last != 'A' && last != 'E' && last != 'I' && last != 'M' && last != 'Q' && last != 'U' && last != 'Y'
142
+ case 2 :
143
+ // the last 2 symbols (12 bits) are encoding 1 byte (8 bits)
144
+ // so the last symbol only actually uses 8-6=2 bits and can only take 4 values
145
+ char last = id .charAt (length - 1 );
146
+ if (last != 'A' && last != 'Q' && last != 'g' && last != 'w' ) {
147
+ return false ;
148
+ }
149
+ break ;
150
+ case 3 :
151
+ // The last 3 symbols (18 bits) are encoding 2 bytes (16 bits)
152
+ // so the last symbol only actually uses 16-12=4 bits and can only take 16 values
153
+ last = id .charAt (length - 1 );
154
+ if (last != 'A' && last != 'E' && last != 'I' && last != 'M' && last != 'Q' && last != 'U' && last != 'Y'
155
155
&& last != 'c' && last != 'g' && last != 'k' && last != 'o' && last != 's' && last != 'w'
156
156
&& last != '0' && last != '4' && last != '8' ) {
157
- return false ;
158
- }
159
- break ;
160
- default :
161
- // number & 0x03 is always in [0,3]
162
- throw new AssertionError ("Impossible case" );
157
+ return false ;
158
+ }
159
+ break ;
160
+ default :
161
+ // number & 0x03 is always in [0,3]
162
+ throw new AssertionError ("Impossible case" );
163
163
}
164
164
for (int i = 0 ; i < length ; ++i ) {
165
165
final char c = id .charAt (i );
166
166
final boolean allowed =
167
- (c >= '0' && c <= '9' ) ||
167
+ (c >= '0' && c <= '9' ) ||
168
168
(c >= 'A' && c <= 'Z' ) ||
169
169
(c >= 'a' && c <= 'z' ) ||
170
170
c == '-' || c == '_' ;
@@ -244,16 +244,16 @@ public static BytesRef encodeId(String id) {
244
244
}
245
245
}
246
246
247
- private static String decodeNumericId (byte [] idBytes ) {
248
- assert Byte .toUnsignedInt (idBytes [0 ]) == NUMERIC ;
249
- int length = (idBytes . length - 1 ) * 2 ;
247
+ private static String decodeNumericId (byte [] idBytes , int offset , int len ) {
248
+ assert Byte .toUnsignedInt (idBytes [offset ]) == NUMERIC ;
249
+ int length = (len - 1 ) * 2 ;
250
250
char [] chars = new char [length ];
251
- for (int i = 1 ; i < idBytes . length ; ++i ) {
252
- final int b = Byte .toUnsignedInt (idBytes [i ]);
251
+ for (int i = 1 ; i < len ; ++i ) {
252
+ final int b = Byte .toUnsignedInt (idBytes [offset + i ]);
253
253
final int b1 = (b >>> 4 );
254
254
final int b2 = b & 0x0f ;
255
255
chars [(i - 1 ) * 2 ] = (char ) (b1 + '0' );
256
- if (i == idBytes . length - 1 && b2 == 0x0f ) {
256
+ if (i == len - 1 && b2 == 0x0f ) {
257
257
length --;
258
258
break ;
259
259
}
@@ -262,33 +262,41 @@ private static String decodeNumericId(byte[] idBytes) {
262
262
return new String (chars , 0 , length );
263
263
}
264
264
265
- private static String decodeUtf8Id (byte [] idBytes ) {
266
- assert Byte .toUnsignedInt (idBytes [0 ]) == UTF8 ;
267
- return new BytesRef (idBytes , 1 , idBytes . length - 1 ).utf8ToString ();
265
+ private static String decodeUtf8Id (byte [] idBytes , int offset , int length ) {
266
+ assert Byte .toUnsignedInt (idBytes [offset ]) == UTF8 ;
267
+ return new BytesRef (idBytes , offset + 1 , length - 1 ).utf8ToString ();
268
268
}
269
269
270
- private static String decodeBase64Id (byte [] idBytes ) {
271
- assert Byte .toUnsignedInt (idBytes [0 ]) <= BASE64_ESCAPE ;
272
- if (Byte .toUnsignedInt (idBytes [0 ]) == BASE64_ESCAPE ) {
273
- idBytes = Arrays .copyOfRange (idBytes , 1 , idBytes .length );
270
+ private static String decodeBase64Id (byte [] idBytes , int offset , int length ) {
271
+ assert Byte .toUnsignedInt (idBytes [offset ]) <= BASE64_ESCAPE ;
272
+ if (Byte .toUnsignedInt (idBytes [offset ]) == BASE64_ESCAPE ) {
273
+ idBytes = Arrays .copyOfRange (idBytes , offset + 1 , offset + length );
274
+ } else if ((idBytes .length == length && offset == 0 ) == false ) { // no need to copy if it's not a slice
275
+ idBytes = Arrays .copyOfRange (idBytes , offset , offset + length );
274
276
}
275
277
return Base64 .getUrlEncoder ().withoutPadding ().encodeToString (idBytes );
276
278
}
277
279
278
280
/** Decode an indexed id back to its original form.
279
281
* @see #encodeId */
280
282
public static String decodeId (byte [] idBytes ) {
281
- if (idBytes .length == 0 ) {
283
+ return decodeId (idBytes , 0 , idBytes .length );
284
+ }
285
+
286
+ /** Decode an indexed id back to its original form.
287
+ * @see #encodeId */
288
+ public static String decodeId (byte [] idBytes , int offset , int length ) {
289
+ if (length == 0 ) {
282
290
throw new IllegalArgumentException ("Ids can't be empty" );
283
291
}
284
- final int magicChar = Byte .toUnsignedInt (idBytes [0 ]);
292
+ final int magicChar = Byte .toUnsignedInt (idBytes [offset ]);
285
293
switch (magicChar ) {
286
- case NUMERIC :
287
- return decodeNumericId (idBytes );
288
- case UTF8 :
289
- return decodeUtf8Id (idBytes );
290
- default :
291
- return decodeBase64Id (idBytes );
294
+ case NUMERIC :
295
+ return decodeNumericId (idBytes , offset , length );
296
+ case UTF8 :
297
+ return decodeUtf8Id (idBytes , offset , length );
298
+ default :
299
+ return decodeBase64Id (idBytes , offset , length );
292
300
}
293
301
}
294
302
}
0 commit comments