001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.io.util;
019
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.OutputStream;
023import java.nio.ByteBuffer;
024import org.apache.hadoop.hbase.nio.ByteBuff;
025import org.apache.hadoop.hbase.util.Pair;
026import org.apache.yetus.audience.InterfaceAudience;
027
028import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
029
030/*
031 * It seems like as soon as somebody sets himself to the task of creating VInt encoding, his mind
032 * blanks out for a split-second and he starts the work by wrapping it in the most convoluted
033 * interface he can come up with. Custom streams that allocate memory, DataOutput that is only used
034 * to write single bytes... We operate on simple streams. Thus, we are going to have a simple
035 * implementation copy-pasted from protobuf Coded*Stream.
036 */
037@InterfaceAudience.Private
038public class StreamUtils {
039
040  public static void writeRawVInt32(OutputStream output, int value) throws IOException {
041    while (true) {
042      if ((value & ~0x7F) == 0) {
043        output.write(value);
044        return;
045      } else {
046        output.write((value & 0x7F) | 0x80);
047        value >>>= 7;
048      }
049    }
050  }
051
052  public static int readRawVarint32(InputStream input) throws IOException {
053    byte tmp = (byte) input.read();
054    if (tmp >= 0) {
055      return tmp;
056    }
057    int result = tmp & 0x7f;
058    if ((tmp = (byte) input.read()) >= 0) {
059      result |= tmp << 7;
060    } else {
061      result |= (tmp & 0x7f) << 7;
062      if ((tmp = (byte) input.read()) >= 0) {
063        result |= tmp << 14;
064      } else {
065        result |= (tmp & 0x7f) << 14;
066        if ((tmp = (byte) input.read()) >= 0) {
067          result |= tmp << 21;
068        } else {
069          result |= (tmp & 0x7f) << 21;
070          result |= (tmp = (byte) input.read()) << 28;
071          if (tmp < 0) {
072            // Discard upper 32 bits.
073            for (int i = 0; i < 5; i++) {
074              if (input.read() >= 0) {
075                return result;
076              }
077            }
078            throw new IOException("Malformed varint");
079          }
080        }
081      }
082    }
083    return result;
084  }
085
086  public static int readRawVarint32(ByteBuff input) throws IOException {
087    byte tmp = input.get();
088    if (tmp >= 0) {
089      return tmp;
090    }
091    int result = tmp & 0x7f;
092    if ((tmp = input.get()) >= 0) {
093      result |= tmp << 7;
094    } else {
095      result |= (tmp & 0x7f) << 7;
096      if ((tmp = input.get()) >= 0) {
097        result |= tmp << 14;
098      } else {
099        result |= (tmp & 0x7f) << 14;
100        if ((tmp = input.get()) >= 0) {
101          result |= tmp << 21;
102        } else {
103          result |= (tmp & 0x7f) << 21;
104          result |= (tmp = input.get()) << 28;
105          if (tmp < 0) {
106            // Discard upper 32 bits.
107            for (int i = 0; i < 5; i++) {
108              if (input.get() >= 0) {
109                return result;
110              }
111            }
112            throw new IOException("Malformed varint");
113          }
114        }
115      }
116    }
117    return result;
118  }
119
120  /**
121   * Reads a varInt value stored in an array. n * Input array where the varInt is available n *
122   * Offset in the input array where varInt is available
123   * @return A pair of integers in which first value is the actual decoded varInt value and second
124   *         value as number of bytes taken by this varInt for it's storage in the input array.
125   * @throws IOException When varint is malformed and not able to be read correctly
126   */
127  public static Pair<Integer, Integer> readRawVarint32(byte[] input, int offset)
128    throws IOException {
129    int newOffset = offset;
130    byte tmp = input[newOffset++];
131    if (tmp >= 0) {
132      return new Pair<>((int) tmp, newOffset - offset);
133    }
134    int result = tmp & 0x7f;
135    tmp = input[newOffset++];
136    if (tmp >= 0) {
137      result |= tmp << 7;
138    } else {
139      result |= (tmp & 0x7f) << 7;
140      tmp = input[newOffset++];
141      if (tmp >= 0) {
142        result |= tmp << 14;
143      } else {
144        result |= (tmp & 0x7f) << 14;
145        tmp = input[newOffset++];
146        if (tmp >= 0) {
147          result |= tmp << 21;
148        } else {
149          result |= (tmp & 0x7f) << 21;
150          tmp = input[newOffset++];
151          result |= tmp << 28;
152          if (tmp < 0) {
153            // Discard upper 32 bits.
154            for (int i = 0; i < 5; i++) {
155              tmp = input[newOffset++];
156              if (tmp >= 0) {
157                return new Pair<>(result, newOffset - offset);
158              }
159            }
160            throw new IOException("Malformed varint");
161          }
162        }
163      }
164    }
165    return new Pair<>(result, newOffset - offset);
166  }
167
168  public static Pair<Integer, Integer> readRawVarint32(ByteBuffer input, int offset)
169    throws IOException {
170    int newOffset = offset;
171    byte tmp = input.get(newOffset++);
172    if (tmp >= 0) {
173      return new Pair<>((int) tmp, newOffset - offset);
174    }
175    int result = tmp & 0x7f;
176    tmp = input.get(newOffset++);
177    if (tmp >= 0) {
178      result |= tmp << 7;
179    } else {
180      result |= (tmp & 0x7f) << 7;
181      tmp = input.get(newOffset++);
182      if (tmp >= 0) {
183        result |= tmp << 14;
184      } else {
185        result |= (tmp & 0x7f) << 14;
186        tmp = input.get(newOffset++);
187        if (tmp >= 0) {
188          result |= tmp << 21;
189        } else {
190          result |= (tmp & 0x7f) << 21;
191          tmp = input.get(newOffset++);
192          result |= tmp << 28;
193          if (tmp < 0) {
194            // Discard upper 32 bits.
195            for (int i = 0; i < 5; i++) {
196              tmp = input.get(newOffset++);
197              if (tmp >= 0) {
198                return new Pair<>(result, newOffset - offset);
199              }
200            }
201            throw new IOException("Malformed varint");
202          }
203        }
204      }
205    }
206    return new Pair<>(result, newOffset - offset);
207  }
208
209  public static short toShort(byte hi, byte lo) {
210    short s = (short) (((hi & 0xFF) << 8) | (lo & 0xFF));
211    Preconditions.checkArgument(s >= 0);
212    return s;
213  }
214
215  public static void writeShort(OutputStream out, short v) throws IOException {
216    Preconditions.checkArgument(v >= 0);
217    out.write((byte) (0xff & (v >> 8)));
218    out.write((byte) (0xff & v));
219  }
220
221  public static void writeInt(OutputStream out, int v) throws IOException {
222    out.write((byte) (0xff & (v >> 24)));
223    out.write((byte) (0xff & (v >> 16)));
224    out.write((byte) (0xff & (v >> 8)));
225    out.write((byte) (0xff & v));
226  }
227
228  public static void writeLong(OutputStream out, long v) throws IOException {
229    out.write((byte) (0xff & (v >> 56)));
230    out.write((byte) (0xff & (v >> 48)));
231    out.write((byte) (0xff & (v >> 40)));
232    out.write((byte) (0xff & (v >> 32)));
233    out.write((byte) (0xff & (v >> 24)));
234    out.write((byte) (0xff & (v >> 16)));
235    out.write((byte) (0xff & (v >> 8)));
236    out.write((byte) (0xff & v));
237  }
238
239  public static long readLong(InputStream in) throws IOException {
240    long result = 0;
241    for (int shift = 56; shift >= 0; shift -= 8) {
242      long x = in.read();
243      if (x < 0) throw new IOException("EOF");
244      result |= (x << shift);
245    }
246    return result;
247  }
248}