1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.tikv.common.codec;
19
20 import java.math.BigDecimal;
21 import java.nio.charset.StandardCharsets;
22 import java.sql.Date;
23 import java.sql.Timestamp;
24 import java.util.Arrays;
25 import java.util.List;
26 import org.joda.time.DateTimeZone;
27 import org.tikv.common.ExtendedDateTime;
28 import org.tikv.common.codec.Codec.DateTimeCodec;
29 import org.tikv.common.codec.Codec.DecimalCodec;
30 import org.tikv.common.codec.Codec.EnumCodec;
31 import org.tikv.common.codec.Codec.SetCodec;
32 import org.tikv.common.exception.CodecException;
33 import org.tikv.common.types.Converter;
34 import org.tikv.common.types.DataType;
35 import org.tikv.common.util.JsonUtils;
36
37 public class RowDecoderV2 {
38
39 private static final long SIGN_MASK = 0x8000000000000000L;
40
41 public static Object decodeCol(byte[] colData, DataType tp) {
42 switch (tp.getType()) {
43 case TypeLonglong:
44 case TypeLong:
45 case TypeInt24:
46 case TypeShort:
47 case TypeTiny:
48
49 return decodeInt(colData);
50 case TypeFloat:
51 return decodeFloat(colData);
52 case TypeDouble:
53 return decodeDouble(colData);
54 case TypeString:
55 case TypeVarString:
56 case TypeVarchar:
57 return new String(colData, StandardCharsets.UTF_8);
58 case TypeBlob:
59 case TypeTinyBlob:
60 case TypeMediumBlob:
61 case TypeLongBlob:
62 return colData;
63 case TypeNewDecimal:
64 return decodeDecimal(colData);
65 case TypeBit:
66 int byteSize = (int) ((tp.getLength() + 7) >>> 3);
67 return decodeBit(decodeInt(colData), byteSize);
68 case TypeDate:
69 return new Date(decodeTimestamp(colData, Converter.getLocalTimezone()).getTime());
70 case TypeDatetime:
71 return decodeTimestamp(colData, Converter.getLocalTimezone());
72 case TypeTimestamp:
73 return decodeTimestamp(colData, DateTimeZone.UTC);
74 case TypeDuration:
75 case TypeYear:
76 return decodeInt(colData);
77 case TypeEnum:
78 return decodeEnum(colData, tp.getElems());
79 case TypeSet:
80 return decodeSet(colData, tp.getElems());
81 case TypeJSON:
82 return decodeJson(colData);
83 case TypeNull:
84 return null;
85 case TypeDecimal:
86 case TypeGeometry:
87 case TypeNewDate:
88 throw new CodecException("type should not appear in colData");
89 default:
90 throw new CodecException("invalid data type " + tp.getType().name());
91 }
92 }
93
94 private static long decodeInt(byte[] val) {
95 switch (val.length) {
96 case 1:
97 return val[0];
98 case 2:
99 return new CodecDataInputLittleEndian(val).readShort();
100 case 4:
101 return new CodecDataInputLittleEndian(val).readInt();
102 default:
103 return new CodecDataInputLittleEndian(val).readLong();
104 }
105 }
106
107 private static float decodeFloat(byte[] val) {
108 return (float) decodeDouble(val);
109 }
110
111 private static double decodeDouble(byte[] val) {
112 CodecDataInput cdi = new CodecDataInput(val);
113 if (val.length < 8) {
114 throw new CodecException("insufficient bytes to decode value");
115 }
116 long u = cdi.readLong();
117
118 if ((u & SIGN_MASK) < 0) {
119 u &= ~SIGN_MASK;
120 } else {
121 u = ~u;
122 }
123 return Double.longBitsToDouble(u);
124 }
125
126 private static BigDecimal decodeDecimal(byte[] val) {
127 return DecimalCodec.readDecimal(new CodecDataInputLittleEndian(val));
128 }
129
130 private static byte[] trimLeadingZeroBytes(byte[] bytes) {
131 if (bytes.length == 0) {
132 return bytes;
133 }
134 int pos = 0, posMax = bytes.length - 1;
135 for (; pos < posMax; pos++) {
136 if (bytes[pos] != 0) {
137 break;
138 }
139 }
140 return Arrays.copyOfRange(bytes, pos, bytes.length);
141 }
142
143 private static byte[] decodeBit(long val, int byteSize) {
144 if (byteSize != -1 && (byteSize < 1 || byteSize > 8)) {
145 throw new CodecException("Invalid byteSize " + byteSize);
146 }
147 CodecDataOutput cdo = new CodecDataOutput();
148 cdo.writeLong(val);
149 if (byteSize != -1) {
150 return trimLeadingZeroBytes(cdo.toBytes());
151 } else {
152 return Arrays.copyOfRange(cdo.toBytes(), 8 - byteSize, 8);
153 }
154 }
155
156 private static Timestamp decodeTimestamp(byte[] val, DateTimeZone tz) {
157 ExtendedDateTime extendedDateTime =
158 DateTimeCodec.fromPackedLong(new CodecDataInputLittleEndian(val).readLong(), tz);
159
160
161
162
163 if (extendedDateTime == null) {
164 return DateTimeCodec.createExtendedDateTime(tz, 1, 1, 1, 0, 0, 0, 0).toTimeStamp();
165 }
166 return extendedDateTime.toTimeStamp();
167 }
168
169 private static String decodeEnum(byte[] val, List<String> elems) {
170 int idx = (int) decodeInt(val) - 1;
171 return EnumCodec.readEnumFromIndex(idx, elems);
172 }
173
174 private static String decodeSet(byte[] val, List<String> elems) {
175 long number = decodeInt(val);
176 return SetCodec.readSetFromLong(number, elems);
177 }
178
179 private static String decodeJson(byte[] val) {
180 return JsonUtils.parseJson(new CodecDataInput(val)).toString();
181 }
182 }