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