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 static org.apache.hadoop.hbase.io.ByteBuffAllocator.NONE;
021
022import java.io.IOException;
023import java.nio.ByteBuffer;
024import java.nio.channels.FileChannel;
025import java.nio.channels.ReadableByteChannel;
026import org.apache.hadoop.hbase.io.ByteBuffAllocator.Recycler;
027import org.apache.hadoop.hbase.unsafe.HBasePlatformDependent;
028import org.apache.hadoop.hbase.util.ByteBufferUtils;
029import org.apache.hadoop.hbase.util.ObjectIntPair;
030import org.apache.hadoop.hbase.util.UnsafeAccess;
031import org.apache.yetus.audience.InterfaceAudience;
032
033/**
034 * An implementation of ByteBuff where a single BB backs the BBI. This just acts as a wrapper over a
035 * normal BB - offheap or onheap
036 */
037@InterfaceAudience.Private
038public class SingleByteBuff extends ByteBuff {
039
040  private static final boolean UNSAFE_AVAIL = HBasePlatformDependent.isUnsafeAvailable();
041  private static final boolean UNSAFE_UNALIGNED = HBasePlatformDependent.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(NONE, buf);
052  }
053
054  public SingleByteBuff(Recycler recycler, ByteBuffer buf) {
055    this(new RefCnt(recycler), buf);
056  }
057
058  SingleByteBuff(RefCnt refCnt, ByteBuffer buf) {
059    this.refCnt = refCnt;
060    this.buf = buf;
061    if (buf.hasArray()) {
062      this.unsafeOffset = UnsafeAccess.BYTE_ARRAY_BASE_OFFSET + buf.arrayOffset();
063      this.unsafeRef = buf.array();
064    } else {
065      this.unsafeOffset = UnsafeAccess.directBufferAddress(buf);
066    }
067  }
068
069  @Override
070  public int position() {
071    checkRefCount();
072    return this.buf.position();
073  }
074
075  @Override
076  public SingleByteBuff position(int position) {
077    checkRefCount();
078    this.buf.position(position);
079    return this;
080  }
081
082  @Override
083  public SingleByteBuff skip(int len) {
084    checkRefCount();
085    this.buf.position(this.buf.position() + len);
086    return this;
087  }
088
089  @Override
090  public SingleByteBuff moveBack(int len) {
091    checkRefCount();
092    this.buf.position(this.buf.position() - len);
093    return this;
094  }
095
096  @Override
097  public int capacity() {
098    checkRefCount();
099    return this.buf.capacity();
100  }
101
102  @Override
103  public int limit() {
104    checkRefCount();
105    return this.buf.limit();
106  }
107
108  @Override
109  public SingleByteBuff limit(int limit) {
110    checkRefCount();
111    this.buf.limit(limit);
112    return this;
113  }
114
115  @Override
116  public SingleByteBuff rewind() {
117    checkRefCount();
118    this.buf.rewind();
119    return this;
120  }
121
122  @Override
123  public SingleByteBuff mark() {
124    checkRefCount();
125    this.buf.mark();
126    return this;
127  }
128
129  @Override
130  public ByteBuffer asSubByteBuffer(int length) {
131    checkRefCount();
132    // Just return the single BB that is available
133    return this.buf;
134  }
135
136  @Override
137  public void asSubByteBuffer(int offset, int length, ObjectIntPair<ByteBuffer> pair) {
138    checkRefCount();
139    // Just return the single BB that is available
140    pair.setFirst(this.buf);
141    pair.setSecond(offset);
142  }
143
144  @Override
145  public int remaining() {
146    checkRefCount();
147    return this.buf.remaining();
148  }
149
150  @Override
151  public boolean hasRemaining() {
152    checkRefCount();
153    return buf.hasRemaining();
154  }
155
156  @Override
157  public SingleByteBuff reset() {
158    checkRefCount();
159    this.buf.reset();
160    return this;
161  }
162
163  @Override
164  public SingleByteBuff slice() {
165    checkRefCount();
166    return new SingleByteBuff(this.refCnt, this.buf.slice());
167  }
168
169  @Override
170  public SingleByteBuff duplicate() {
171    checkRefCount();
172    return new SingleByteBuff(this.refCnt, this.buf.duplicate());
173  }
174
175  @Override
176  public byte get() {
177    checkRefCount();
178    return buf.get();
179  }
180
181  @Override
182  public byte get(int index) {
183    checkRefCount();
184    if (UNSAFE_AVAIL) {
185      return UnsafeAccess.toByte(this.unsafeRef, this.unsafeOffset + index);
186    }
187    return this.buf.get(index);
188  }
189
190  @Override
191  public byte getByteAfterPosition(int offset) {
192    checkRefCount();
193    return get(this.buf.position() + offset);
194  }
195
196  @Override
197  public SingleByteBuff put(byte b) {
198    checkRefCount();
199    this.buf.put(b);
200    return this;
201  }
202
203  @Override
204  public SingleByteBuff put(int index, byte b) {
205    checkRefCount();
206    buf.put(index, b);
207    return this;
208  }
209
210  @Override
211  public void get(byte[] dst, int offset, int length) {
212    checkRefCount();
213    ByteBufferUtils.copyFromBufferToArray(dst, buf, buf.position(), offset, length);
214    buf.position(buf.position() + length);
215  }
216
217  @Override
218  public void get(int sourceOffset, byte[] dst, int offset, int length) {
219    checkRefCount();
220    ByteBufferUtils.copyFromBufferToArray(dst, buf, sourceOffset, offset, length);
221  }
222
223  @Override
224  public void get(byte[] dst) {
225    get(dst, 0, dst.length);
226  }
227
228  @Override
229  public SingleByteBuff put(int offset, ByteBuff src, int srcOffset, int length) {
230    checkRefCount();
231    if (src instanceof SingleByteBuff) {
232      ByteBufferUtils.copyFromBufferToBuffer(((SingleByteBuff) src).buf, this.buf, srcOffset,
233        offset, length);
234    } else {
235      // TODO we can do some optimization here? Call to asSubByteBuffer might
236      // create a copy.
237      ObjectIntPair<ByteBuffer> pair = new ObjectIntPair<>();
238      src.asSubByteBuffer(srcOffset, length, pair);
239      if (pair.getFirst() != null) {
240        ByteBufferUtils.copyFromBufferToBuffer(pair.getFirst(), this.buf, pair.getSecond(), offset,
241          length);
242      }
243    }
244    return this;
245  }
246
247  @Override
248  public SingleByteBuff put(byte[] src, int offset, int length) {
249    checkRefCount();
250    ByteBufferUtils.copyFromArrayToBuffer(this.buf, src, offset, length);
251    return this;
252  }
253
254  @Override
255  public SingleByteBuff put(byte[] src) {
256    checkRefCount();
257    return put(src, 0, src.length);
258  }
259
260  @Override
261  public boolean hasArray() {
262    checkRefCount();
263    return this.buf.hasArray();
264  }
265
266  @Override
267  public byte[] array() {
268    checkRefCount();
269    return this.buf.array();
270  }
271
272  @Override
273  public int arrayOffset() {
274    checkRefCount();
275    return this.buf.arrayOffset();
276  }
277
278  @Override
279  public short getShort() {
280    checkRefCount();
281    return this.buf.getShort();
282  }
283
284  @Override
285  public short getShort(int index) {
286    checkRefCount();
287    if (UNSAFE_UNALIGNED) {
288      return UnsafeAccess.toShort(unsafeRef, unsafeOffset + index);
289    }
290    return this.buf.getShort(index);
291  }
292
293  @Override
294  public short getShortAfterPosition(int offset) {
295    checkRefCount();
296    return getShort(this.buf.position() + offset);
297  }
298
299  @Override
300  public int getInt() {
301    checkRefCount();
302    return this.buf.getInt();
303  }
304
305  @Override
306  public SingleByteBuff putInt(int value) {
307    checkRefCount();
308    ByteBufferUtils.putInt(this.buf, value);
309    return this;
310  }
311
312  @Override
313  public int getInt(int index) {
314    checkRefCount();
315    if (UNSAFE_UNALIGNED) {
316      return UnsafeAccess.toInt(unsafeRef, unsafeOffset + index);
317    }
318    return this.buf.getInt(index);
319  }
320
321  @Override
322  public int getIntAfterPosition(int offset) {
323    checkRefCount();
324    return getInt(this.buf.position() + offset);
325  }
326
327  @Override
328  public long getLong() {
329    checkRefCount();
330    return this.buf.getLong();
331  }
332
333  @Override
334  public SingleByteBuff putLong(long value) {
335    checkRefCount();
336    ByteBufferUtils.putLong(this.buf, value);
337    return this;
338  }
339
340  @Override
341  public long getLong(int index) {
342    checkRefCount();
343    if (UNSAFE_UNALIGNED) {
344      return UnsafeAccess.toLong(unsafeRef, unsafeOffset + index);
345    }
346    return this.buf.getLong(index);
347  }
348
349  @Override
350  public long getLongAfterPosition(int offset) {
351    checkRefCount();
352    return getLong(this.buf.position() + offset);
353  }
354
355  @Override
356  public byte[] toBytes(int offset, int length) {
357    checkRefCount();
358    byte[] output = new byte[length];
359    ByteBufferUtils.copyFromBufferToArray(output, buf, offset, 0, length);
360    return output;
361  }
362
363  @Override
364  public void get(ByteBuffer out, int sourceOffset, int length) {
365    checkRefCount();
366    ByteBufferUtils.copyFromBufferToBuffer(buf, out, sourceOffset, length);
367  }
368
369  @Override
370  public int read(ReadableByteChannel channel) throws IOException {
371    checkRefCount();
372    return read(channel, buf, 0, CHANNEL_READER);
373  }
374
375  @Override
376  public int read(FileChannel channel, long offset) throws IOException {
377    checkRefCount();
378    return read(channel, buf, offset, FILE_READER);
379  }
380
381  @Override
382  public int write(FileChannel channel, long offset) throws IOException {
383    checkRefCount();
384    int total = 0;
385    while (buf.hasRemaining()) {
386      int len = channel.write(buf, offset);
387      total += len;
388      offset += len;
389    }
390    return total;
391  }
392
393  @Override
394  public ByteBuffer[] nioByteBuffers() {
395    checkRefCount();
396    return new ByteBuffer[] { this.buf };
397  }
398
399  @Override
400  public boolean equals(Object obj) {
401    if (!(obj instanceof SingleByteBuff)) {
402      return false;
403    }
404    return this.buf.equals(((SingleByteBuff) obj).buf);
405  }
406
407  @Override
408  public int hashCode() {
409    return this.buf.hashCode();
410  }
411
412  @Override
413  public SingleByteBuff retain() {
414    refCnt.retain();
415    return this;
416  }
417}