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}