View Javadoc
1   /*
2    * Copyright 2021 TiKV Project Authors.
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   */
17  
18  package org.tikv.common.codec;
19  
20  import java.util.Arrays;
21  import org.tikv.common.exception.InvalidCodecFormatException;
22  
23  public class RowV2 {
24    // CodecVer is the constant number that represent the new row format.
25    public static int CODEC_VER = 0x80;
26    // small:  colID byte[], offsets int[], optimized for most cases.
27    // large:  colID long[], offsets long[].
28    boolean large;
29    int numNotNullCols;
30    int numNullCols;
31    byte[] colIDs;
32    int[] offsets;
33    byte[] data;
34    // for large row
35    int[] colIDs32;
36    int[] offsets32;
37  
38    private RowV2(byte[] rowData) {
39      fromBytes(rowData);
40    }
41  
42    public static RowV2 createNew(byte[] rowData) {
43      return new RowV2(rowData);
44    }
45  
46    public static RowV2 createEmpty() {
47      return new RowV2(false, 0, 0);
48    }
49  
50    private RowV2(boolean large, int numNotNullCols, int numNullCols) {
51      this.large = large;
52      this.numNotNullCols = numNotNullCols;
53      this.numNullCols = numNullCols;
54    }
55  
56    public byte[] getData(int i) {
57      int start = 0, end = 0;
58      if (this.large) {
59        if (i > 0) {
60          start = this.offsets32[i - 1];
61        }
62        end = this.offsets32[i];
63      } else {
64        if (i > 0) {
65          start = this.offsets[i - 1];
66        }
67        end = this.offsets[i];
68      }
69      return Arrays.copyOfRange(this.data, start, end);
70    }
71  
72    private void fromBytes(byte[] rowData) {
73      CodecDataInputLittleEndian cdi = new CodecDataInputLittleEndian(rowData);
74      if (cdi.readUnsignedByte() != CODEC_VER) {
75        throw new InvalidCodecFormatException("invalid codec version");
76      }
77      this.large = (cdi.readUnsignedByte() & 1) > 0;
78      this.numNotNullCols = cdi.readUnsignedShort();
79      this.numNullCols = cdi.readUnsignedShort();
80      int cursor = 6;
81      if (this.large) {
82        int numCols = this.numNotNullCols + this.numNullCols;
83        int colIDsLen = numCols * 4;
84        this.colIDs32 = new int[numCols];
85        for (int i = 0; i < numCols; i++) {
86          this.colIDs32[i] = cdi.readInt();
87        }
88        cursor += colIDsLen;
89        numCols = this.numNotNullCols;
90        int offsetsLen = numCols * 4;
91        this.offsets32 = new int[numCols];
92        for (int i = 0; i < numCols; i++) {
93          this.offsets32[i] = cdi.readInt();
94        }
95        cursor += offsetsLen;
96      } else {
97        int numCols = this.numNotNullCols + this.numNullCols;
98        int colIDsLen = numCols;
99        this.colIDs = new byte[numCols];
100       cdi.readFully(this.colIDs, 0, numCols);
101       cursor += colIDsLen;
102       numCols = this.numNotNullCols;
103       int offsetsLen = numCols * 2;
104       this.offsets = new int[numCols];
105       for (int i = 0; i < numCols; i++) {
106         this.offsets[i] = cdi.readUnsignedShort();
107       }
108       cursor += offsetsLen;
109     }
110     this.data = Arrays.copyOfRange(rowData, cursor, rowData.length);
111   }
112 
113   private void writeShortArray(CodecDataOutput cdo, int[] arr) {
114     for (int value : arr) {
115       cdo.writeShort(value);
116     }
117   }
118 
119   private void writeIntArray(CodecDataOutput cdo, int[] arr) {
120     for (int value : arr) {
121       cdo.writeInt(value);
122     }
123   }
124 
125   public byte[] toBytes() {
126     CodecDataOutputLittleEndian cdo = new CodecDataOutputLittleEndian();
127     cdo.write(CODEC_VER);
128     cdo.write(this.large ? 1 : 0);
129     cdo.writeShort(this.numNotNullCols);
130     cdo.writeShort(this.numNullCols);
131     if (this.large) {
132       writeIntArray(cdo, this.colIDs32);
133       writeIntArray(cdo, this.offsets32);
134     } else {
135       cdo.write(this.colIDs);
136       writeShortArray(cdo, this.offsets);
137     }
138     cdo.write(this.data);
139     return cdo.toBytes();
140   }
141 
142   private int binarySearch(int i, int j, long colID) {
143     while (i < j) {
144       int h = (int) ((i + (long) j) >> 1);
145       // i <= h < j
146       long v;
147       if (this.large) {
148         v = this.colIDs32[h];
149       } else {
150         v = this.colIDs[h] & 0xFF;
151       }
152       if (v < colID) {
153         i = h + 1;
154       } else if (v > colID) {
155         j = h;
156       } else {
157         return h;
158       }
159     }
160     return -1;
161   }
162 
163   public ColIDSearchResult findColID(long colID) {
164     int i = 0, j = this.numNotNullCols;
165     ColIDSearchResult result = new ColIDSearchResult(-1, false, false);
166     result.idx = binarySearch(i, j, colID);
167     if (result.idx != -1) {
168       return result;
169     }
170 
171     // Search the column in null columns array.
172     i = this.numNotNullCols;
173     j = this.numNotNullCols + this.numNullCols;
174     int id = binarySearch(i, j, colID);
175     if (id != -1) {
176       // colID found in null cols.
177       result.isNull = true;
178     } else {
179       result.notFound = true;
180     }
181     return result;
182   }
183 
184   public void initColIDs() {
185     int numCols = this.numNotNullCols + this.numNullCols;
186     this.colIDs = new byte[numCols];
187   }
188 
189   public void initColIDs32() {
190     int numCols = this.numNotNullCols + this.numNullCols;
191     this.colIDs32 = new int[numCols];
192   }
193 
194   public void initOffsets() {
195     this.offsets = new int[this.numNotNullCols];
196   }
197 
198   public void initOffsets32() {
199     this.offsets32 = new int[this.numNotNullCols];
200   }
201 
202   public static class ColIDSearchResult {
203     int idx;
204     boolean isNull;
205     boolean notFound;
206 
207     private ColIDSearchResult(int idx, boolean isNull, boolean notFound) {
208       this.idx = idx;
209       this.isNull = isNull;
210       this.notFound = notFound;
211     }
212   }
213 }