blob: 9f8b3a822c815c5cf087e8ce83e8b87b56fbb216 [file] [log] [blame]
Nathan Haroldf3e659e2017-04-25 18:27:08 -07001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.telephony.uicc;
18
19import android.content.res.Resources;
20import android.content.res.Resources.NotFoundException;
21import android.graphics.Bitmap;
22import android.graphics.Color;
23import android.telephony.Rlog;
24
25import com.android.internal.telephony.GsmAlphabet;
26
27import java.io.UnsupportedEncodingException;
28
29/**
30 * Various methods, useful for dealing with SIM data.
31 */
32public class IccUtils {
33 static final String LOG_TAG="IccUtils";
34
Holly Jiuyu Sunf77821d2017-12-11 13:28:03 -080035 // A table mapping from a number to a hex character for fast encoding hex strings.
36 private static final char[] HEX_CHARS = {
37 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
38 };
39
40
Nathan Haroldf3e659e2017-04-25 18:27:08 -070041 /**
42 * Many fields in GSM SIM's are stored as nibble-swizzled BCD
43 *
44 * Assumes left-justified field that may be padded right with 0xf
45 * values.
46 *
47 * Stops on invalid BCD value, returning string so far
48 */
49 public static String
50 bcdToString(byte[] data, int offset, int length) {
51 StringBuilder ret = new StringBuilder(length*2);
52
53 for (int i = offset ; i < offset + length ; i++) {
54 int v;
55
56 v = data[i] & 0xf;
57 if (v > 9) break;
58 ret.append((char)('0' + v));
59
60 v = (data[i] >> 4) & 0xf;
61 // Some PLMNs have 'f' as high nibble, ignore it
62 if (v == 0xf) continue;
63 if (v > 9) break;
64 ret.append((char)('0' + v));
65 }
66
67 return ret.toString();
68 }
69
70 /**
Holly Jiuyu Sunf77821d2017-12-11 13:28:03 -080071 * Converts a bcd byte array to String with offset 0 and byte array length.
72 */
73 public static String bcdToString(byte[] data) {
74 return bcdToString(data, 0, data.length);
75 }
76
77 /**
78 * Converts BCD string to bytes.
79 *
80 * @param bcd This should have an even length. If not, an "0" will be appended to the string.
81 */
82 public static byte[] bcdToBytes(String bcd) {
83 byte[] output = new byte[(bcd.length() + 1) / 2];
84 bcdToBytes(bcd, output);
85 return output;
86 }
87
88 /**
89 * Converts BCD string to bytes and put it into the given byte array.
90 *
91 * @param bcd This should have an even length. If not, an "0" will be appended to the string.
92 * @param bytes If the array size is less than needed, the rest of the BCD string isn't be
93 * converted. If the array size is more than needed, the rest of array remains unchanged.
94 */
95 public static void bcdToBytes(String bcd, byte[] bytes) {
96 if (bcd.length() % 2 != 0) {
97 bcd += "0";
98 }
99 int size = Math.min(bytes.length * 2, bcd.length());
100 for (int i = 0, j = 0; i + 1 < size; i += 2, j++) {
101 bytes[j] = (byte) (charToByte(bcd.charAt(i + 1)) << 4 | charToByte(bcd.charAt(i)));
102 }
103 }
104
105 /**
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700106 * PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3
107 * Returns a concatenated string of MCC+MNC, stripping
kun.tang8cd6dad2017-07-04 17:33:52 +0800108 * all invalid character 'f'
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700109 */
110 public static String bcdPlmnToString(byte[] data, int offset) {
111 if (offset + 3 > data.length) {
112 return null;
113 }
114 byte[] trans = new byte[3];
115 trans[0] = (byte) ((data[0 + offset] << 4) | ((data[0 + offset] >> 4) & 0xF));
116 trans[1] = (byte) ((data[1 + offset] << 4) | (data[2 + offset] & 0xF));
117 trans[2] = (byte) ((data[2 + offset] & 0xF0) | ((data[1 + offset] >> 4) & 0xF));
118 String ret = bytesToHexString(trans);
119
kun.tang8cd6dad2017-07-04 17:33:52 +0800120 // For a valid plmn we trim all character 'f'
121 if (ret.contains("f")) {
122 ret = ret.replaceAll("f", "");
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700123 }
124 return ret;
125 }
126
127 /**
128 * Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH
129 */
130 public static String
131 bchToString(byte[] data, int offset, int length) {
132 StringBuilder ret = new StringBuilder(length*2);
133
134 for (int i = offset ; i < offset + length ; i++) {
135 int v;
136
137 v = data[i] & 0xf;
Holly Jiuyu Sunf77821d2017-12-11 13:28:03 -0800138 ret.append(HEX_CHARS[v]);
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700139
140 v = (data[i] >> 4) & 0xf;
Holly Jiuyu Sunf77821d2017-12-11 13:28:03 -0800141 ret.append(HEX_CHARS[v]);
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700142 }
143
144 return ret.toString();
145 }
146
147 /**
148 * Decode cdma byte into String.
149 */
150 public static String
151 cdmaBcdToString(byte[] data, int offset, int length) {
152 StringBuilder ret = new StringBuilder(length);
153
154 int count = 0;
155 for (int i = offset; count < length; i++) {
156 int v;
157 v = data[i] & 0xf;
158 if (v > 9) v = 0;
159 ret.append((char)('0' + v));
160
161 if (++count == length) break;
162
163 v = (data[i] >> 4) & 0xf;
164 if (v > 9) v = 0;
165 ret.append((char)('0' + v));
166 ++count;
167 }
168 return ret.toString();
169 }
170
171 /**
172 * Decodes a GSM-style BCD byte, returning an int ranging from 0-99.
173 *
174 * In GSM land, the least significant BCD digit is stored in the most
175 * significant nibble.
176 *
177 * Out-of-range digits are treated as 0 for the sake of the time stamp,
178 * because of this:
179 *
180 * TS 23.040 section 9.2.3.11
181 * "if the MS receives a non-integer value in the SCTS, it shall
182 * assume the digit is set to 0 but shall store the entire field
183 * exactly as received"
184 */
185 public static int
186 gsmBcdByteToInt(byte b) {
187 int ret = 0;
188
189 // treat out-of-range BCD values as 0
190 if ((b & 0xf0) <= 0x90) {
191 ret = (b >> 4) & 0xf;
192 }
193
194 if ((b & 0x0f) <= 0x09) {
195 ret += (b & 0xf) * 10;
196 }
197
198 return ret;
199 }
200
201 /**
202 * Decodes a CDMA style BCD byte like {@link #gsmBcdByteToInt}, but
203 * opposite nibble format. The least significant BCD digit
204 * is in the least significant nibble and the most significant
205 * is in the most significant nibble.
206 */
207 public static int
208 cdmaBcdByteToInt(byte b) {
209 int ret = 0;
210
211 // treat out-of-range BCD values as 0
212 if ((b & 0xf0) <= 0x90) {
213 ret = ((b >> 4) & 0xf) * 10;
214 }
215
216 if ((b & 0x0f) <= 0x09) {
217 ret += (b & 0xf);
218 }
219
220 return ret;
221 }
222
223 /**
224 * Decodes a string field that's formatted like the EF[ADN] alpha
225 * identifier
226 *
227 * From TS 51.011 10.5.1:
228 * Coding:
229 * this alpha tagging shall use either
230 * - the SMS default 7 bit coded alphabet as defined in
231 * TS 23.038 [12] with bit 8 set to 0. The alpha identifier
232 * shall be left justified. Unused bytes shall be set to 'FF'; or
233 * - one of the UCS2 coded options as defined in annex B.
234 *
235 * Annex B from TS 11.11 V8.13.0:
236 * 1) If the first octet in the alpha string is '80', then the
237 * remaining octets are 16 bit UCS2 characters ...
238 * 2) if the first octet in the alpha string is '81', then the
239 * second octet contains a value indicating the number of
240 * characters in the string, and the third octet contains an
241 * 8 bit number which defines bits 15 to 8 of a 16 bit
242 * base pointer, where bit 16 is set to zero and bits 7 to 1
243 * are also set to zero. These sixteen bits constitute a
244 * base pointer to a "half page" in the UCS2 code space, to be
245 * used with some or all of the remaining octets in the string.
246 * The fourth and subsequent octets contain codings as follows:
247 * If bit 8 of the octet is set to zero, the remaining 7 bits
248 * of the octet contain a GSM Default Alphabet character,
249 * whereas if bit 8 of the octet is set to one, then the
250 * remaining seven bits are an offset value added to the
251 * 16 bit base pointer defined earlier...
252 * 3) If the first octet of the alpha string is set to '82', then
253 * the second octet contains a value indicating the number of
254 * characters in the string, and the third and fourth octets
255 * contain a 16 bit number which defines the complete 16 bit
256 * base pointer to a "half page" in the UCS2 code space...
257 */
258 public static String
259 adnStringFieldToString(byte[] data, int offset, int length) {
260 if (length == 0) {
261 return "";
262 }
263 if (length >= 1) {
264 if (data[offset] == (byte) 0x80) {
265 int ucslen = (length - 1) / 2;
266 String ret = null;
267
268 try {
269 ret = new String(data, offset + 1, ucslen * 2, "utf-16be");
270 } catch (UnsupportedEncodingException ex) {
271 Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException",
272 ex);
273 }
274
275 if (ret != null) {
276 // trim off trailing FFFF characters
277
278 ucslen = ret.length();
279 while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF')
280 ucslen--;
281
282 return ret.substring(0, ucslen);
283 }
284 }
285 }
286
287 boolean isucs2 = false;
288 char base = '\0';
289 int len = 0;
290
291 if (length >= 3 && data[offset] == (byte) 0x81) {
292 len = data[offset + 1] & 0xFF;
293 if (len > length - 3)
294 len = length - 3;
295
296 base = (char) ((data[offset + 2] & 0xFF) << 7);
297 offset += 3;
298 isucs2 = true;
299 } else if (length >= 4 && data[offset] == (byte) 0x82) {
300 len = data[offset + 1] & 0xFF;
301 if (len > length - 4)
302 len = length - 4;
303
304 base = (char) (((data[offset + 2] & 0xFF) << 8) |
305 (data[offset + 3] & 0xFF));
306 offset += 4;
307 isucs2 = true;
308 }
309
310 if (isucs2) {
311 StringBuilder ret = new StringBuilder();
312
313 while (len > 0) {
314 // UCS2 subset case
315
316 if (data[offset] < 0) {
317 ret.append((char) (base + (data[offset] & 0x7F)));
318 offset++;
319 len--;
320 }
321
322 // GSM character set case
323
324 int count = 0;
325 while (count < len && data[offset + count] >= 0)
326 count++;
327
328 ret.append(GsmAlphabet.gsm8BitUnpackedToString(data,
329 offset, count));
330
331 offset += count;
332 len -= count;
333 }
334
335 return ret.toString();
336 }
337
338 Resources resource = Resources.getSystem();
339 String defaultCharset = "";
340 try {
341 defaultCharset =
342 resource.getString(com.android.internal.R.string.gsm_alphabet_default_charset);
343 } catch (NotFoundException e) {
344 // Ignore Exception and defaultCharset is set to a empty string.
345 }
346 return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
347 }
348
Holly Jiuyu Sunf77821d2017-12-11 13:28:03 -0800349 public static int
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700350 hexCharToInt(char c) {
351 if (c >= '0' && c <= '9') return (c - '0');
352 if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
353 if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
354
355 throw new RuntimeException ("invalid hex char '" + c + "'");
356 }
357
358 /**
359 * Converts a hex String to a byte array.
360 *
361 * @param s A string of hexadecimal characters, must be an even number of
362 * chars long
363 *
364 * @return byte array representation
365 *
366 * @throws RuntimeException on invalid format
367 */
368 public static byte[]
369 hexStringToBytes(String s) {
370 byte[] ret;
371
372 if (s == null) return null;
373
374 int sz = s.length();
375
376 ret = new byte[sz/2];
377
378 for (int i=0 ; i <sz ; i+=2) {
379 ret[i/2] = (byte) ((hexCharToInt(s.charAt(i)) << 4)
380 | hexCharToInt(s.charAt(i+1)));
381 }
382
383 return ret;
384 }
385
386
387 /**
388 * Converts a byte array into a String of hexadecimal characters.
389 *
390 * @param bytes an array of bytes
391 *
392 * @return hex string representation of bytes array
393 */
394 public static String
395 bytesToHexString(byte[] bytes) {
396 if (bytes == null) return null;
397
398 StringBuilder ret = new StringBuilder(2*bytes.length);
399
400 for (int i = 0 ; i < bytes.length ; i++) {
401 int b;
402
403 b = 0x0f & (bytes[i] >> 4);
404
Holly Jiuyu Sunf77821d2017-12-11 13:28:03 -0800405 ret.append(HEX_CHARS[b]);
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700406
407 b = 0x0f & bytes[i];
408
Holly Jiuyu Sunf77821d2017-12-11 13:28:03 -0800409 ret.append(HEX_CHARS[b]);
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700410 }
411
412 return ret.toString();
413 }
414
415
416 /**
417 * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string
418 * "offset" points to "octet 3", the coding scheme byte
419 * empty string returned on decode error
420 */
421 public static String
422 networkNameToString(byte[] data, int offset, int length) {
423 String ret;
424
425 if ((data[offset] & 0x80) != 0x80 || length < 1) {
426 return "";
427 }
428
429 switch ((data[offset] >>> 4) & 0x7) {
430 case 0:
431 // SMS character set
432 int countSeptets;
433 int unusedBits = data[offset] & 7;
434 countSeptets = (((length - 1) * 8) - unusedBits) / 7 ;
435 ret = GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets);
436 break;
437 case 1:
438 // UCS2
439 try {
440 ret = new String(data,
441 offset + 1, length - 1, "utf-16");
442 } catch (UnsupportedEncodingException ex) {
443 ret = "";
444 Rlog.e(LOG_TAG,"implausible UnsupportedEncodingException", ex);
445 }
446 break;
447
448 // unsupported encoding
449 default:
450 ret = "";
451 break;
452 }
453
454 // "Add CI"
455 // "The MS should add the letters for the Country's Initials and
456 // a separator (e.g. a space) to the text string"
457
458 if ((data[offset] & 0x40) != 0) {
459 // FIXME(mkf) add country initials here
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700460 }
461
462 return ret;
463 }
464
465 /**
466 * Convert a TS 131.102 image instance of code scheme '11' into Bitmap
467 * @param data The raw data
468 * @param length The length of image body
469 * @return The bitmap
470 */
471 public static Bitmap parseToBnW(byte[] data, int length){
472 int valueIndex = 0;
473 int width = data[valueIndex++] & 0xFF;
474 int height = data[valueIndex++] & 0xFF;
475 int numOfPixels = width*height;
476
477 int[] pixels = new int[numOfPixels];
478
479 int pixelIndex = 0;
480 int bitIndex = 7;
481 byte currentByte = 0x00;
482 while (pixelIndex < numOfPixels) {
483 // reassign data and index for every byte (8 bits).
484 if (pixelIndex % 8 == 0) {
485 currentByte = data[valueIndex++];
486 bitIndex = 7;
487 }
488 pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01);
489 }
490
491 if (pixelIndex != numOfPixels) {
492 Rlog.e(LOG_TAG, "parse end and size error");
493 }
494 return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
495 }
496
497 private static int bitToRGB(int bit){
498 if(bit == 1){
499 return Color.WHITE;
500 } else {
501 return Color.BLACK;
502 }
503 }
504
505 /**
506 * a TS 131.102 image instance of code scheme '11' into color Bitmap
507 *
508 * @param data The raw data
509 * @param length the length of image body
510 * @param transparency with or without transparency
511 * @return The color bitmap
512 */
513 public static Bitmap parseToRGB(byte[] data, int length,
514 boolean transparency) {
515 int valueIndex = 0;
516 int width = data[valueIndex++] & 0xFF;
517 int height = data[valueIndex++] & 0xFF;
518 int bits = data[valueIndex++] & 0xFF;
519 int colorNumber = data[valueIndex++] & 0xFF;
520 int clutOffset = ((data[valueIndex++] & 0xFF) << 8)
521 | (data[valueIndex++] & 0xFF);
522
523 int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber);
524 if (true == transparency) {
525 colorIndexArray[colorNumber - 1] = Color.TRANSPARENT;
526 }
527
528 int[] resultArray = null;
529 if (0 == (8 % bits)) {
530 resultArray = mapTo2OrderBitColor(data, valueIndex,
531 (width * height), colorIndexArray, bits);
532 } else {
533 resultArray = mapToNon2OrderBitColor(data, valueIndex,
534 (width * height), colorIndexArray, bits);
535 }
536
537 return Bitmap.createBitmap(resultArray, width, height,
538 Bitmap.Config.RGB_565);
539 }
540
541 private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex,
542 int length, int[] colorArray, int bits) {
543 if (0 != (8 % bits)) {
544 Rlog.e(LOG_TAG, "not event number of color");
545 return mapToNon2OrderBitColor(data, valueIndex, length, colorArray,
546 bits);
547 }
548
549 int mask = 0x01;
550 switch (bits) {
551 case 1:
552 mask = 0x01;
553 break;
554 case 2:
555 mask = 0x03;
556 break;
557 case 4:
558 mask = 0x0F;
559 break;
560 case 8:
561 mask = 0xFF;
562 break;
563 }
564
565 int[] resultArray = new int[length];
566 int resultIndex = 0;
567 int run = 8 / bits;
568 while (resultIndex < length) {
569 byte tempByte = data[valueIndex++];
570 for (int runIndex = 0; runIndex < run; ++runIndex) {
571 int offset = run - runIndex - 1;
572 resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits))
573 & mask];
574 }
575 }
576 return resultArray;
577 }
578
579 private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex,
580 int length, int[] colorArray, int bits) {
581 if (0 == (8 % bits)) {
582 Rlog.e(LOG_TAG, "not odd number of color");
583 return mapTo2OrderBitColor(data, valueIndex, length, colorArray,
584 bits);
585 }
586
587 int[] resultArray = new int[length];
588 // TODO fix me:
589 return resultArray;
590 }
591
592 private static int[] getCLUT(byte[] rawData, int offset, int number) {
593 if (null == rawData) {
594 return null;
595 }
596
597 int[] result = new int[number];
598 int endIndex = offset + (number * 3); // 1 color use 3 bytes
599 int valueIndex = offset;
600 int colorIndex = 0;
601 int alpha = 0xff << 24;
602 do {
603 result[colorIndex++] = alpha
604 | ((rawData[valueIndex++] & 0xFF) << 16)
605 | ((rawData[valueIndex++] & 0xFF) << 8)
606 | ((rawData[valueIndex++] & 0xFF));
607 } while (valueIndex < endIndex);
608 return result;
609 }
Yujing Gucf94c672017-10-26 16:06:09 +0800610
611 public static String getDecimalSubstring(String iccId) {
612 int position;
613 for (position = 0; position < iccId.length(); position ++) {
614 if (!Character.isDigit(iccId.charAt(position))) break;
615 }
616 return iccId.substring( 0, position );
617 }
Holly Jiuyu Sunf77821d2017-12-11 13:28:03 -0800618
619 /**
620 * Converts a series of bytes to an integer. This method currently only supports positive 32-bit
621 * integers.
622 *
623 * @param src The source bytes.
624 * @param offset The position of the first byte of the data to be converted. The data is base
625 * 256 with the most significant digit first.
626 * @param length The length of the data to be converted. It must be <= 4.
627 * @throws IllegalArgumentException If {@code length} is bigger than 4 or {@code src} cannot be
628 * parsed as a positive integer.
629 * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length}
630 * exceeds the bounds of {@code src}.
631 */
632 public static int bytesToInt(byte[] src, int offset, int length) {
633 if (length > 4) {
634 throw new IllegalArgumentException(
635 "length must be <= 4 (only 32-bit integer supported): " + length);
636 }
637 if (offset < 0 || length < 0 || offset + length > src.length) {
638 throw new IndexOutOfBoundsException(
639 "Out of the bounds: src=["
640 + src.length
641 + "], offset="
642 + offset
643 + ", length="
644 + length);
645 }
646 int result = 0;
647 for (int i = 0; i < length; i++) {
648 result = (result << 8) | (src[offset + i] & 0xFF);
649 }
650 if (result < 0) {
651 throw new IllegalArgumentException(
652 "src cannot be parsed as a positive integer: " + result);
653 }
654 return result;
655 }
656
657 /**
658 * Converts a series of bytes to a raw long variable which can be both positive and negative.
659 * This method currently only supports 64-bit long variable.
660 *
661 * @param src The source bytes.
662 * @param offset The position of the first byte of the data to be converted. The data is base
663 * 256 with the most significant digit first.
664 * @param length The length of the data to be converted. It must be <= 8.
665 * @throws IllegalArgumentException If {@code length} is bigger than 8.
666 * @throws IndexOutOfBoundsException If the range defined by {@code offset} and {@code length}
667 * exceeds the bounds of {@code src}.
668 */
669 public static long bytesToRawLong(byte[] src, int offset, int length) {
670 if (length > 8) {
671 throw new IllegalArgumentException(
672 "length must be <= 8 (only 64-bit long supported): " + length);
673 }
674 if (offset < 0 || length < 0 || offset + length > src.length) {
675 throw new IndexOutOfBoundsException(
676 "Out of the bounds: src=["
677 + src.length
678 + "], offset="
679 + offset
680 + ", length="
681 + length);
682 }
683 long result = 0;
684 for (int i = 0; i < length; i++) {
685 result = (result << 8) | (src[offset + i] & 0xFF);
686 }
687 return result;
688 }
689
690 /**
691 * Converts an integer to a new byte array with base 256 and the most significant digit first.
692 *
693 * @throws IllegalArgumentException If {@code value} is negative.
694 */
695 public static byte[] unsignedIntToBytes(int value) {
696 if (value < 0) {
697 throw new IllegalArgumentException("value must be 0 or positive: " + value);
698 }
699 byte[] bytes = new byte[byteNumForUnsignedInt(value)];
700 unsignedIntToBytes(value, bytes, 0);
701 return bytes;
702 }
703
704 /**
705 * Converts an integer to a new byte array with base 256 and the most significant digit first.
706 * The first byte's highest bit is used for sign. If the most significant digit is larger than
707 * 127, an extra byte (0) will be prepended before it. This method currently doesn't support
708 * negative values.
709 *
710 * @throws IllegalArgumentException If {@code value} is negative.
711 */
712 public static byte[] signedIntToBytes(int value) {
713 if (value < 0) {
714 throw new IllegalArgumentException("value must be 0 or positive: " + value);
715 }
716 byte[] bytes = new byte[byteNumForSignedInt(value)];
717 signedIntToBytes(value, bytes, 0);
718 return bytes;
719 }
720
721 /**
722 * Converts an integer to a series of bytes with base 256 and the most significant digit first.
723 *
724 * @param value The integer to be converted.
725 * @param dest The destination byte array.
726 * @param offset The start offset of the byte array.
727 * @return The number of byte needeed.
728 * @throws IllegalArgumentException If {@code value} is negative.
729 * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}.
730 */
731 public static int unsignedIntToBytes(int value, byte[] dest, int offset) {
732 return intToBytes(value, dest, offset, false);
733 }
734
735 /**
736 * Converts an integer to a series of bytes with base 256 and the most significant digit first.
737 * The first byte's highest bit is used for sign. If the most significant digit is larger than
738 * 127, an extra byte (0) will be prepended before it. This method currently doesn't support
739 * negative values.
740 *
741 * @throws IllegalArgumentException If {@code value} is negative.
742 * @throws IndexOutOfBoundsException If {@code offset} exceeds the bounds of {@code dest}.
743 */
744 public static int signedIntToBytes(int value, byte[] dest, int offset) {
745 return intToBytes(value, dest, offset, true);
746 }
747
748 /**
749 * Calculates the number of required bytes to represent {@code value}. The bytes will be base
750 * 256 with the most significant digit first.
751 *
752 * @throws IllegalArgumentException If {@code value} is negative.
753 */
754 public static int byteNumForUnsignedInt(int value) {
755 return byteNumForInt(value, false);
756 }
757
758 /**
759 * Calculates the number of required bytes to represent {@code value}. The bytes will be base
760 * 256 with the most significant digit first. If the most significant digit is larger than 127,
761 * an extra byte (0) will be prepended before it. This method currently only supports positive
762 * integers.
763 *
764 * @throws IllegalArgumentException If {@code value} is negative.
765 */
766 public static int byteNumForSignedInt(int value) {
767 return byteNumForInt(value, true);
768 }
769
770 private static int intToBytes(int value, byte[] dest, int offset, boolean signed) {
771 int l = byteNumForInt(value, signed);
772 if (offset < 0 || offset + l > dest.length) {
773 throw new IndexOutOfBoundsException("Not enough space to write. Required bytes: " + l);
774 }
775 for (int i = l - 1, v = value; i >= 0; i--, v >>>= 8) {
776 byte b = (byte) (v & 0xFF);
777 dest[offset + i] = b;
778 }
779 return l;
780 }
781
782 private static int byteNumForInt(int value, boolean signed) {
783 if (value < 0) {
784 throw new IllegalArgumentException("value must be 0 or positive: " + value);
785 }
786 if (signed) {
787 if (value <= 0x7F) {
788 return 1;
789 }
790 if (value <= 0x7FFF) {
791 return 2;
792 }
793 if (value <= 0x7FFFFF) {
794 return 3;
795 }
796 } else {
797 if (value <= 0xFF) {
798 return 1;
799 }
800 if (value <= 0xFFFF) {
801 return 2;
802 }
803 if (value <= 0xFFFFFF) {
804 return 3;
805 }
806 }
807 return 4;
808 }
809
810
811 /**
812 * Counts the number of trailing zero bits of a byte.
813 */
814 public static byte countTrailingZeros(byte b) {
815 if (b == 0) {
816 return 8;
817 }
818 int v = b & 0xFF;
819 byte c = 7;
820 if ((v & 0x0F) != 0) {
821 c -= 4;
822 }
823 if ((v & 0x33) != 0) {
824 c -= 2;
825 }
826 if ((v & 0x55) != 0) {
827 c -= 1;
828 }
829 return c;
830 }
831
832 /**
833 * Converts a byte to a hex string.
834 */
835 public static String byteToHex(byte b) {
836 return new String(new char[] {HEX_CHARS[(b & 0xFF) >>> 4], HEX_CHARS[b & 0xF]});
837 }
838
839 /**
840 * Converts a character of [0-9a-aA-F] to its hex value in a byte. If the character is not a
841 * hex number, 0 will be returned.
842 */
843 private static byte charToByte(char c) {
844 if (c >= 0x30 && c <= 0x39) {
845 return (byte) (c - 0x30);
846 } else if (c >= 0x41 && c <= 0x46) {
847 return (byte) (c - 0x37);
848 } else if (c >= 0x61 && c <= 0x66) {
849 return (byte) (c - 0x57);
850 }
851 return 0;
852 }
Nathan Haroldf3e659e2017-04-25 18:27:08 -0700853}