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.EOFException;
021import java.io.IOException;
022import java.io.InputStream;
023import java.io.OutputStream;
024import java.nio.ByteBuffer;
025import org.apache.hadoop.hbase.nio.ByteBuff;
026import org.apache.hadoop.hbase.util.Pair;
027import org.apache.yetus.audience.InterfaceAudience;
028
029import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
030
031/*
032 * It seems like as soon as somebody sets himself to the task of creating VInt encoding, his mind
033 * blanks out for a split-second and he starts the work by wrapping it in the most convoluted
034 * interface he can come up with. Custom streams that allocate memory, DataOutput that is only used
035 * to write single bytes... We operate on simple streams. Thus, we are going to have a simple
036 * implementation copy-pasted from protobuf Coded*Stream.
037 */
038@InterfaceAudience.Private
039public class StreamUtils {
040
041  public static void writeRawVInt32(OutputStream output, int value) throws IOException {
042    while (true) {
043      if ((value & ~0x7F) == 0) {
044        output.write(value);
045        return;
046      } else {
047        output.write((value & 0x7F) | 0x80);
048        value >>>= 7;
049      }
050    }
051  }
052
053  public static int readRawVarint32(InputStream input) throws IOException {
054    byte tmp = (byte) input.read();
055    if (tmp >= 0) {
056      return tmp;
057    }
058    int result = tmp & 0x7f;
059    if ((tmp = (byte) input.read()) >= 0) {
060      result |= tmp << 7;
061    } else {
062      result |= (tmp & 0x7f) << 7;
063      if ((tmp = (byte) input.read()) >= 0) {
064        result |= tmp << 14;
065      } else {
066        result |= (tmp & 0x7f) << 14;
067        if ((tmp = (byte) input.read()) >= 0) {
068          result |= tmp << 21;
069        } else {
070          result |= (tmp & 0x7f) << 21;
071          result |= (tmp = (byte) input.read()) << 28;
072          if (tmp < 0) {
073            // Discard upper 32 bits.
074            for (int i = 0; i < 5; i++) {
075              if (input.read() >= 0) {
076                return result;
077              }
078            }
079            throw new IOException("Malformed varint");
080          }
081        }
082      }
083    }
084    return result;
085  }
086
087  public static int readRawVarint32(ByteBuff input) throws IOException {
088    byte tmp = input.get();
089    if (tmp >= 0) {
090      return tmp;
091    }
092    int result = tmp & 0x7f;
093    if ((tmp = input.get()) >= 0) {
094      result |= tmp << 7;
095    } else {
096      result |= (tmp & 0x7f) << 7;
097      if ((tmp = input.get()) >= 0) {
098        result |= tmp << 14;
099      } else {
100        result |= (tmp & 0x7f) << 14;
101        if ((tmp = input.get()) >= 0) {
102          result |= tmp << 21;
103        } else {
104          result |= (tmp & 0x7f) << 21;
105          result |= (tmp = input.get()) << 28;
106          if (tmp < 0) {
107            // Discard upper 32 bits.
108            for (int i = 0; i < 5; i++) {
109              if (input.get() >= 0) {
110                return result;
111              }
112            }
113            throw new IOException("Malformed varint");
114          }
115        }
116      }
117    }
118    return result;
119  }
120
121  /**
122   * Reads a varInt value stored in an array. Input array where the varInt is available Offset in
123   * the input array where varInt is available
124   * @return A pair of integers in which first value is the actual decoded varInt value and second
125   *         value as number of bytes taken by this varInt for it's storage in the input array.
126   * @throws IOException When varint is malformed and not able to be read correctly
127   */
128  public static Pair<Integer, Integer> readRawVarint32(byte[] input, int offset)
129    throws IOException {
130    int newOffset = offset;
131    byte tmp = input[newOffset++];
132    if (tmp >= 0) {
133      return new Pair<>((int) tmp, newOffset - offset);
134    }
135    int result = tmp & 0x7f;
136    tmp = input[newOffset++];
137    if (tmp >= 0) {
138      result |= tmp << 7;
139    } else {
140      result |= (tmp & 0x7f) << 7;
141      tmp = input[newOffset++];
142      if (tmp >= 0) {
143        result |= tmp << 14;
144      } else {
145        result |= (tmp & 0x7f) << 14;
146        tmp = input[newOffset++];
147        if (tmp >= 0) {
148          result |= tmp << 21;
149        } else {
150          result |= (tmp & 0x7f) << 21;
151          tmp = input[newOffset++];
152          result |= tmp << 28;
153          if (tmp < 0) {
154            // Discard upper 32 bits.
155            for (int i = 0; i < 5; i++) {
156              tmp = input[newOffset++];
157              if (tmp >= 0) {
158                return new Pair<>(result, newOffset - offset);
159              }
160            }
161            throw new IOException("Malformed varint");
162          }
163        }
164      }
165    }
166    return new Pair<>(result, newOffset - offset);
167  }
168
169  public static Pair<Integer, Integer> readRawVarint32(ByteBuffer input, int offset)
170    throws IOException {
171    int newOffset = offset;
172    byte tmp = input.get(newOffset++);
173    if (tmp >= 0) {
174      return new Pair<>((int) tmp, newOffset - offset);
175    }
176    int result = tmp & 0x7f;
177    tmp = input.get(newOffset++);
178    if (tmp >= 0) {
179      result |= tmp << 7;
180    } else {
181      result |= (tmp & 0x7f) << 7;
182      tmp = input.get(newOffset++);
183      if (tmp >= 0) {
184        result |= tmp << 14;
185      } else {
186        result |= (tmp & 0x7f) << 14;
187        tmp = input.get(newOffset++);
188        if (tmp >= 0) {
189          result |= tmp << 21;
190        } else {
191          result |= (tmp & 0x7f) << 21;
192          tmp = input.get(newOffset++);
193          result |= tmp << 28;
194          if (tmp < 0) {
195            // Discard upper 32 bits.
196            for (int i = 0; i < 5; i++) {
197              tmp = input.get(newOffset++);
198              if (tmp >= 0) {
199                return new Pair<>(result, newOffset - offset);
200              }
201            }
202            throw new IOException("Malformed varint");
203          }
204        }
205      }
206    }
207    return new Pair<>(result, newOffset - offset);
208  }
209
210  /**
211   * Read a byte from the given stream using the read method, and throw EOFException if it returns
212   * -1, like the implementation in {@code DataInputStream}.
213   * <p/>
214   * This is useful because casting the return value of read method into byte directly will make us
215   * lose the ability to check whether there is a byte and its value is -1 or we reach EOF, as
216   * casting int -1 to byte also returns -1.
217   */
218  public static byte readByte(InputStream in) throws IOException {
219    int r = in.read();
220    if (r < 0) {
221      throw new EOFException();
222    }
223    return (byte) r;
224  }
225
226  public static short toShort(byte hi, byte lo) {
227    short s = (short) (((hi & 0xFF) << 8) | (lo & 0xFF));
228    Preconditions.checkArgument(s >= 0);
229    return s;
230  }
231
232  public static void writeShort(OutputStream out, short v) throws IOException {
233    Preconditions.checkArgument(v >= 0);
234    out.write((byte) (0xff & (v >> 8)));
235    out.write((byte) (0xff & v));
236  }
237
238  public static void writeInt(OutputStream out, int v) throws IOException {
239    out.write((byte) (0xff & (v >> 24)));
240    out.write((byte) (0xff & (v >> 16)));
241    out.write((byte) (0xff & (v >> 8)));
242    out.write((byte) (0xff & v));
243  }
244
245  public static void writeLong(OutputStream out, long v) throws IOException {
246    out.write((byte) (0xff & (v >> 56)));
247    out.write((byte) (0xff & (v >> 48)));
248    out.write((byte) (0xff & (v >> 40)));
249    out.write((byte) (0xff & (v >> 32)));
250    out.write((byte) (0xff & (v >> 24)));
251    out.write((byte) (0xff & (v >> 16)));
252    out.write((byte) (0xff & (v >> 8)));
253    out.write((byte) (0xff & v));
254  }
255
256  public static long readLong(InputStream in) throws IOException {
257    long result = 0;
258    for (int shift = 56; shift >= 0; shift -= 8) {
259      long x = in.read();
260      if (x < 0) throw new IOException("EOF");
261      result |= (x << shift);
262    }
263    return result;
264  }
265}