1
15
16 package software.amazon.awssdk.utils.internal;
17
18 import software.amazon.awssdk.annotations.SdkInternalApi;
19
20
25 @SdkInternalApi
26 public final class Base16Codec {
27 private static final int OFFSET_OF_LITTLE_A = 'a' - 10;
28 private static final int OFFSET_OF_A = 'A' - 10;
29 private static final int MASK_4BITS = (1 << 4) - 1;
30 private final byte[] alphabets;
31
32 Base16Codec() {
33 this(true);
34 }
35
36 Base16Codec(boolean upperCase) {
37 this.alphabets = upperCase
38 ? CodecUtils.toBytesDirect("0123456789ABCDEF")
39 : CodecUtils.toBytesDirect("0123456789abcdef");
40 }
41
42 public byte[] encode(byte[] src) {
43 byte[] dest = new byte[src.length * 2];
44 byte p;
45
46 for (int i = 0, j = 0; i < src.length; i++) {
47 p = src[i];
48 dest[j++] = alphabets[p >>> 4 & MASK_4BITS];
49 dest[j++] = alphabets[p & MASK_4BITS];
50 }
51 return dest;
52 }
53
54 public byte[] decode(byte[] src, final int length) {
55 if (length % 2 != 0) {
56 throw new IllegalArgumentException(
57 "Input is expected to be encoded in multiple of 2 bytes but found: "
58 + length
59 );
60 }
61 byte[] dest = new byte[length / 2];
62
63 for (int i = 0, j = 0; j < dest.length; j++) {
64 dest[j] = (byte)
65 (
66 pos(src[i++]) << 4 | pos(src[i++])
67 )
68 ;
69
70 }
71 return dest;
72 }
73
74 protected int pos(byte in) {
75 int pos = LazyHolder.DECODED[in];
76
77 if (pos > -1) {
78 return pos;
79 }
80 throw new IllegalArgumentException("Invalid base 16 character: '" + (char) in + "'");
81 }
82
83 private static class LazyHolder {
84 private static final byte[] DECODED = decodeTable();
85
86 private static byte[] decodeTable() {
87 byte[] dest = new byte['f' + 1];
88
89 for (int i = 0; i <= 'f'; i++) {
90 if (i >= '0' && i <= '9') {
91 dest[i] = (byte) (i - '0');
92 } else if (i >= 'A' && i <= 'F') {
93 dest[i] = (byte) (i - OFFSET_OF_A);
94 } else if (i >= 'a') {
95 dest[i] = (byte) (i - OFFSET_OF_LITTLE_A);
96 } else {
97 dest[i] = -1;
98 }
99 }
100 return dest;
101 }
102 }
103 }
104