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.nio;
019
020import java.io.IOException;
021import java.nio.ByteBuffer;
022import java.nio.channels.ReadableByteChannel;
023
024import org.apache.hadoop.hbase.util.ByteBufferUtils;
025import org.apache.hadoop.hbase.util.ObjectIntPair;
026import org.apache.hadoop.hbase.util.UnsafeAccess;
027import org.apache.hadoop.hbase.util.UnsafeAvailChecker;
028import org.apache.yetus.audience.InterfaceAudience;
029import sun.nio.ch.DirectBuffer;
030
031import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
032
033/**
034 * An implementation of ByteBuff where a single BB backs the BBI. This just acts
035 * as a wrapper over a normal BB - offheap or onheap
036 */
037@InterfaceAudience.Private
038public class SingleByteBuff extends ByteBuff {
039
040  private static final boolean UNSAFE_AVAIL = UnsafeAvailChecker.isAvailable();
041  private static final boolean UNSAFE_UNALIGNED = UnsafeAvailChecker.unaligned();
042
043  // Underlying BB
044  private final ByteBuffer buf;
045
046  // To access primitive values from underlying ByteBuffer using Unsafe
047  private long unsafeOffset;
048  private Object unsafeRef = null;
049
050  public SingleByteBuff(ByteBuffer buf) {
051    this.buf = buf;
052    if (buf.hasArray()) {
053      this.unsafeOffset = UnsafeAccess.BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset();
054      this.unsafeRef = buf.array();
055    } else {
056      this.unsafeOffset = ((DirectBuffer) buf).address();
057    }
058  }
059
060  @Override
061  public int position() {
062    return this.buf.position();
063  }
064
065  @Override
066  public SingleByteBuff position(int position) {
067    this.buf.position(position);
068    return this;
069  }
070
071  @Override
072  public SingleByteBuff skip(int len) {
073    this.buf.position(this.buf.position() + len);
074    return this;
075  }
076
077  @Override
078  public SingleByteBuff moveBack(int len) {
079    this.buf.position(this.buf.position() - len);
080    return this;
081  }
082
083  @Override
084  public int capacity() {
085    return this.buf.capacity();
086  }
087
088  @Override
089  public int limit() {
090    return this.buf.limit();
091  }
092
093  @Override
094  public SingleByteBuff limit(int limit) {
095    this.buf.limit(limit);
096    return this;
097  }
098
099  @Override
100  public SingleByteBuff rewind() {
101    this.buf.rewind();
102    return this;
103  }
104
105  @Override
106  public SingleByteBuff mark() {
107    this.buf.mark();
108    return this;
109  }
110
111  @Override
112  public ByteBuffer asSubByteBuffer(int length) {
113    // Just return the single BB that is available
114    return this.buf;
115  }
116
117  @Override
118  public void asSubByteBuffer(int offset, int length, ObjectIntPair<ByteBuffer> pair) {
119    // Just return the single BB that is available
120    pair.setFirst(this.buf);
121    pair.setSecond(offset);
122  }
123
124  @Override
125  public int remaining() {
126    return this.buf.remaining();
127  }
128
129  @Override
130  public boolean hasRemaining() {
131    return buf.hasRemaining();
132  }
133
134  @Override
135  public SingleByteBuff reset() {
136    this.buf.reset();
137    return this;
138  }
139
140  @Override
141  public SingleByteBuff slice() {
142    return new SingleByteBuff(this.buf.slice());
143  }
144
145  @Override
146  public SingleByteBuff duplicate() {
147    return new SingleByteBuff(this.buf.duplicate());
148  }
149
150  @Override
151  public byte get() {
152    return buf.get();
153  }
154
155  @Override
156  public byte get(int index) {
157    if (UNSAFE_AVAIL) {
158      return UnsafeAccess.toByte(this.unsafeRef, this.unsafeOffset + index);
159    }
160    return this.buf.get(index);
161  }
162
163  @Override
164  public byte getByteAfterPosition(int offset) {
165    return get(this.buf.position() + offset);
166  }
167
168  @Override
169  public SingleByteBuff put(byte b) {
170    this.buf.put(b);
171    return this;
172  }
173
174  @Override
175  public SingleByteBuff put(int index, byte b) {
176    buf.put(index, b);
177    return this;
178  }
179
180  @Override
181  public void get(byte[] dst, int offset, int length) {
182    ByteBufferUtils.copyFromBufferToArray(dst, buf, buf.position(), offset, length);
183    buf.position(buf.position() + length);
184  }
185
186  @Override
187  public void get(int sourceOffset, byte[] dst, int offset, int length) {
188    ByteBufferUtils.copyFromBufferToArray(dst, buf, sourceOffset, offset, length);
189  }
190
191  @Override
192  public void get(byte[] dst) {
193    get(dst, 0, dst.length);
194  }
195
196  @Override
197  public SingleByteBuff put(int offset, ByteBuff src, int srcOffset, int length) {
198    if (src instanceof SingleByteBuff) {
199      ByteBufferUtils.copyFromBufferToBuffer(((SingleByteBuff) src).buf, this.buf, srcOffset,
200          offset, length);
201    } else {
202      // TODO we can do some optimization here? Call to asSubByteBuffer might
203      // create a copy.
204      ObjectIntPair<ByteBuffer> pair = new ObjectIntPair<>();
205      src.asSubByteBuffer(srcOffset, length, pair);
206      if (pair.getFirst() != null) {
207        ByteBufferUtils.copyFromBufferToBuffer(pair.getFirst(), this.buf, pair.getSecond(), offset,
208            length);
209      }
210    }
211    return this;
212  }
213
214  @Override
215  public SingleByteBuff put(byte[] src, int offset, int length) {
216    ByteBufferUtils.copyFromArrayToBuffer(this.buf, src, offset, length);
217    return this;
218  }
219
220  @Override
221  public SingleByteBuff put(byte[] src) {
222    return put(src, 0, src.length);
223  }
224
225  @Override
226  public boolean hasArray() {
227    return this.buf.hasArray();
228  }
229
230  @Override
231  public byte[] array() {
232    return this.buf.array();
233  }
234
235  @Override
236  public int arrayOffset() {
237    return this.buf.arrayOffset();
238  }
239
240  @Override
241  public short getShort() {
242    return this.buf.getShort();
243  }
244
245  @Override
246  public short getShort(int index) {
247    if (UNSAFE_UNALIGNED) {
248      return UnsafeAccess.toShort(unsafeRef, unsafeOffset + index);
249    }
250    return this.buf.getShort(index);
251  }
252
253  @Override
254  public short getShortAfterPosition(int offset) {
255    return getShort(this.buf.position() + offset);
256  }
257
258  @Override
259  public int getInt() {
260    return this.buf.getInt();
261  }
262
263  @Override
264  public SingleByteBuff putInt(int value) {
265    ByteBufferUtils.putInt(this.buf, value);
266    return this;
267  }
268
269  @Override
270  public int getInt(int index) {
271    if (UNSAFE_UNALIGNED) {
272      return UnsafeAccess.toInt(unsafeRef, unsafeOffset + index);
273    }
274    return this.buf.getInt(index);
275  }
276
277  @Override
278  public int getIntAfterPosition(int offset) {
279    return getInt(this.buf.position() + offset);
280  }
281
282  @Override
283  public long getLong() {
284    return this.buf.getLong();
285  }
286
287  @Override
288  public SingleByteBuff putLong(long value) {
289    ByteBufferUtils.putLong(this.buf, value);
290    return this;
291  }
292
293  @Override
294  public long getLong(int index) {
295    if (UNSAFE_UNALIGNED) {
296      return UnsafeAccess.toLong(unsafeRef, unsafeOffset + index);
297    }
298    return this.buf.getLong(index);
299  }
300
301  @Override
302  public long getLongAfterPosition(int offset) {
303    return getLong(this.buf.position() + offset);
304  }
305
306  @Override
307  public byte[] toBytes(int offset, int length) {
308    byte[] output = new byte[length];
309    ByteBufferUtils.copyFromBufferToArray(output, buf, offset, 0, length);
310    return output;
311  }
312
313  @Override
314  public void get(ByteBuffer out, int sourceOffset, int length) {
315    ByteBufferUtils.copyFromBufferToBuffer(buf, out, sourceOffset, length);
316  }
317
318  @Override
319  public int read(ReadableByteChannel channel) throws IOException {
320    return channelRead(channel, buf);
321  }
322
323  @Override
324  public boolean equals(Object obj) {
325    if(!(obj instanceof SingleByteBuff)) return false;
326    return this.buf.equals(((SingleByteBuff)obj).buf);
327  }
328
329  @Override
330  public int hashCode() {
331    return this.buf.hashCode();
332  }
333
334  /**
335   * @return the ByteBuffer which this wraps.
336   */
337  @VisibleForTesting
338  public ByteBuffer getEnclosingByteBuffer() {
339    return this.buf;
340  }
341}