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.key;
19  
20  import static java.util.Objects.requireNonNull;
21  import static org.tikv.common.codec.KeyUtils.formatBytes;
22  
23  import com.google.common.primitives.Bytes;
24  import com.google.protobuf.ByteString;
25  import java.io.Serializable;
26  import java.util.Arrays;
27  import javax.annotation.Nonnull;
28  import org.tikv.common.codec.CodecDataOutput;
29  import org.tikv.common.types.DataType;
30  import org.tikv.common.util.FastByteComparisons;
31  
32  public class Key implements Comparable<Key>, Serializable {
33    public static final Key EMPTY = createEmpty();
34    public static final Key NULL = createNull();
35    public static final Key MIN = createTypelessMin();
36    public static final Key MAX = createTypelessMax();
37    protected static final byte[] TBL_PREFIX = new byte[] {'t'};
38    protected final byte[] value;
39    protected final int infFlag;
40  
41    private Key(byte[] value, boolean negative) {
42      this.value = requireNonNull(value, "value is null");
43      this.infFlag = (value.length == 0 ? 1 : 0) * (negative ? -1 : 1);
44    }
45  
46    protected Key(byte[] value) {
47      this(value, false);
48    }
49  
50    public static Key toRawKey(ByteString bytes, boolean negative) {
51      return new Key(bytes.toByteArray(), negative);
52    }
53  
54    public static Key toRawKey(ByteString bytes) {
55      return new Key(bytes.toByteArray());
56    }
57  
58    public static Key toRawKey(byte[] bytes, boolean negative) {
59      return new Key(bytes, negative);
60    }
61  
62    public static Key toRawKey(byte[] bytes) {
63      return new Key(bytes);
64    }
65  
66    private static Key createNull() {
67      CodecDataOutput cdo = new CodecDataOutput();
68      DataType.encodeNull(cdo);
69      return new Key(cdo.toBytes()) {
70        @Override
71        public String toString() {
72          return "null";
73        }
74      };
75    }
76  
77    private static Key createEmpty() {
78      return new Key(new byte[0]) {
79        @Override
80        public Key next() {
81          return this;
82        }
83  
84        @Override
85        public String toString() {
86          return "EMPTY";
87        }
88      };
89    }
90  
91    private static Key createTypelessMin() {
92      CodecDataOutput cdo = new CodecDataOutput();
93      DataType.encodeIndex(cdo);
94      return new Key(cdo.toBytes()) {
95        @Override
96        public String toString() {
97          return "MIN";
98        }
99      };
100   }
101 
102   private static Key createTypelessMax() {
103     CodecDataOutput cdo = new CodecDataOutput();
104     DataType.encodeMaxValue(cdo);
105     return new Key(cdo.toBytes()) {
106       @Override
107       public String toString() {
108         return "MAX";
109       }
110     };
111   }
112 
113   /**
114    * The prefixNext key for bytes domain
115    *
116    * <p>It first plus one at LSB and if LSB overflows, a zero byte is appended at the end Original
117    * bytes will be reused if possible
118    *
119    * @return encoded results
120    */
121   static byte[] prefixNext(byte[] value) {
122     int i;
123     byte[] newVal = Arrays.copyOf(value, value.length);
124     for (i = newVal.length - 1; i >= 0; i--) {
125       newVal[i]++;
126       if (newVal[i] != 0) {
127         break;
128       }
129     }
130     if (i == -1) {
131       return Arrays.copyOf(value, value.length + 1);
132     } else {
133       return newVal;
134     }
135   }
136 
137   /**
138    * Next key simply append a zero byte to previous key.
139    *
140    * @return next key with a zero byte appended
141    */
142   public Key next() {
143     return toRawKey(Arrays.copyOf(value, value.length + 1));
144   }
145 
146   /**
147    * nextPrefix key will be key with next available rid. For example, if the current key is
148    * prefix_rid, after calling this method, the return value should be prefix_rid+1
149    *
150    * @return a new key current rid+1.
151    */
152   public Key nextPrefix() {
153     return toRawKey(prefixNext(value));
154   }
155 
156   @Override
157   public int compareTo(@Nonnull Key other) {
158     if ((this.infFlag | other.infFlag) != 0) {
159       return this.infFlag - other.infFlag;
160     }
161     return FastByteComparisons.compareTo(value, other.value);
162   }
163 
164   @Override
165   public boolean equals(Object other) {
166     if (other == this) {
167       return true;
168     }
169     if (other instanceof Key) {
170       return compareTo((Key) other) == 0;
171     } else {
172       return false;
173     }
174   }
175 
176   public Key append(Key other) {
177     if (other == null) {
178       return this;
179     }
180     return Key.toRawKey(Bytes.concat(getBytes(), other.getBytes()));
181   }
182 
183   @Override
184   public int hashCode() {
185     return Arrays.hashCode(value);
186   }
187 
188   public byte[] getBytes() {
189     return value;
190   }
191 
192   public ByteString toByteString() {
193     return ByteString.copyFrom(value);
194   }
195 
196   public int getInfFlag() {
197     return infFlag;
198   }
199 
200   @Override
201   public String toString() {
202     if (infFlag < 0) {
203       return "-INF";
204     } else if (infFlag > 0) {
205       return "+INF";
206     } else {
207       return String.format("{%s}", formatBytes(value));
208     }
209   }
210 }