1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.nio;
19
20import java.nio.channels.FileChannel.MapMode;
21
22import libcore.io.Memory;
23import libcore.io.SizeOf;
24
25class DirectByteBuffer extends MappedByteBuffer {
26  // This is the offset into {@code Buffer.block} at which this buffer logically starts.
27  // TODO: rewrite this so we set 'block' to an OffsetMemoryBlock?
28  protected final int offset;
29
30  private final boolean isReadOnly;
31
32  protected DirectByteBuffer(MemoryBlock block, int capacity, int offset, boolean isReadOnly, MapMode mapMode) {
33    super(block, capacity, mapMode, block.toLong() + offset);
34
35    long baseSize = block.getSize();
36    // We're throwing this exception after we passed a bogus value
37    // to the superclass constructor, but it doesn't make any
38    // difference in this case.
39    if (baseSize >= 0 && (capacity + offset) > baseSize) {
40      throw new IllegalArgumentException("capacity + offset > baseSize");
41    }
42
43    this.offset = offset;
44    this.isReadOnly = isReadOnly;
45  }
46
47  // Used by the JNI NewDirectByteBuffer function.
48  DirectByteBuffer(long address, int capacity) {
49    this(MemoryBlock.wrapFromJni(address, capacity), capacity, 0, false, null);
50  }
51
52  private static DirectByteBuffer copy(DirectByteBuffer other, int markOfOther, boolean isReadOnly) {
53    other.checkNotFreed();
54    DirectByteBuffer buf = new DirectByteBuffer(other.block, other.capacity(), other.offset, isReadOnly, other.mapMode);
55    buf.limit = other.limit;
56    buf.position = other.position();
57    buf.mark = markOfOther;
58    return buf;
59  }
60
61  @Override public ByteBuffer asReadOnlyBuffer() {
62    return copy(this, mark, true);
63  }
64
65  @Override public ByteBuffer compact() {
66    checkIsAccessible();
67    if (isReadOnly) {
68      throw new ReadOnlyBufferException();
69    }
70    Memory.memmove(this, 0, this, position, remaining());
71    position = limit - position;
72    limit = capacity;
73    mark = UNSET_MARK;
74    return this;
75  }
76
77  @Override public ByteBuffer duplicate() {
78    return copy(this, mark, isReadOnly);
79  }
80
81  @Override public ByteBuffer slice() {
82    checkNotFreed();
83    return new DirectByteBuffer(block, remaining(), offset + position, isReadOnly, mapMode);
84  }
85
86  @Override public boolean isReadOnly() {
87    return isReadOnly;
88  }
89
90  @Override byte[] protectedArray() {
91    checkIsAccessible();
92    if (isReadOnly) {
93      throw new ReadOnlyBufferException();
94    }
95    byte[] array = this.block.array();
96    if (array == null) {
97      throw new UnsupportedOperationException();
98    }
99    return array;
100  }
101
102  @Override int protectedArrayOffset() {
103    protectedArray(); // Throw if we don't have an array or are read-only.
104    return offset;
105  }
106
107  @Override boolean protectedHasArray() {
108    return !isReadOnly && (block.array() != null);
109  }
110
111  @Override public final ByteBuffer get(byte[] dst, int dstOffset, int byteCount) {
112    checkIsAccessible();
113    checkGetBounds(1, dst.length, dstOffset, byteCount);
114    this.block.peekByteArray(offset + position, dst, dstOffset, byteCount);
115    position += byteCount;
116    return this;
117  }
118
119  final void get(char[] dst, int dstOffset, int charCount) {
120    checkIsAccessible();
121    int byteCount = checkGetBounds(SizeOf.CHAR, dst.length, dstOffset, charCount);
122    this.block.peekCharArray(offset + position, dst, dstOffset, charCount, order.needsSwap);
123    position += byteCount;
124  }
125
126  final void get(double[] dst, int dstOffset, int doubleCount) {
127    checkIsAccessible();
128    int byteCount = checkGetBounds(SizeOf.DOUBLE, dst.length, dstOffset, doubleCount);
129    this.block.peekDoubleArray(offset + position, dst, dstOffset, doubleCount, order.needsSwap);
130    position += byteCount;
131  }
132
133  final void get(float[] dst, int dstOffset, int floatCount) {
134    checkIsAccessible();
135    int byteCount = checkGetBounds(SizeOf.FLOAT, dst.length, dstOffset, floatCount);
136    this.block.peekFloatArray(offset + position, dst, dstOffset, floatCount, order.needsSwap);
137    position += byteCount;
138  }
139
140  final void get(int[] dst, int dstOffset, int intCount) {
141    checkIsAccessible();
142    int byteCount = checkGetBounds(SizeOf.INT, dst.length, dstOffset, intCount);
143    this.block.peekIntArray(offset + position, dst, dstOffset, intCount, order.needsSwap);
144    position += byteCount;
145  }
146
147  final void get(long[] dst, int dstOffset, int longCount) {
148    checkIsAccessible();
149    int byteCount = checkGetBounds(SizeOf.LONG, dst.length, dstOffset, longCount);
150    this.block.peekLongArray(offset + position, dst, dstOffset, longCount, order.needsSwap);
151    position += byteCount;
152  }
153
154  final void get(short[] dst, int dstOffset, int shortCount) {
155    checkIsAccessible();
156    int byteCount = checkGetBounds(SizeOf.SHORT, dst.length, dstOffset, shortCount);
157    this.block.peekShortArray(offset + position, dst, dstOffset, shortCount, order.needsSwap);
158    position += byteCount;
159  }
160
161  @Override public final byte get() {
162    checkIsAccessible();
163    if (position == limit) {
164      throw new BufferUnderflowException();
165    }
166    return this.block.peekByte(offset + position++);
167  }
168
169  @Override public final byte get(int index) {
170    checkIsAccessible();
171    checkIndex(index);
172    return this.block.peekByte(offset + index);
173  }
174
175  @Override public final char getChar() {
176    checkIsAccessible();
177    int newPosition = position + SizeOf.CHAR;
178    if (newPosition > limit) {
179      throw new BufferUnderflowException();
180    }
181    char result = (char) this.block.peekShort(offset + position, order);
182    position = newPosition;
183    return result;
184  }
185
186  @Override public final char getChar(int index) {
187    checkIsAccessible();
188    checkIndex(index, SizeOf.CHAR);
189    return (char) this.block.peekShort(offset + index, order);
190  }
191
192  @Override public final double getDouble() {
193    checkIsAccessible();
194    int newPosition = position + SizeOf.DOUBLE;
195    if (newPosition > limit) {
196      throw new BufferUnderflowException();
197    }
198    double result = Double.longBitsToDouble(this.block.peekLong(offset + position, order));
199    position = newPosition;
200    return result;
201  }
202
203  @Override public final double getDouble(int index) {
204    checkIsAccessible();
205    checkIndex(index, SizeOf.DOUBLE);
206    return Double.longBitsToDouble(this.block.peekLong(offset + index, order));
207  }
208
209  @Override public final float getFloat() {
210    checkIsAccessible();
211    int newPosition = position + SizeOf.FLOAT;
212    if (newPosition > limit) {
213      throw new BufferUnderflowException();
214    }
215    float result = Float.intBitsToFloat(this.block.peekInt(offset + position, order));
216    position = newPosition;
217    return result;
218  }
219
220  @Override public final float getFloat(int index) {
221    checkIsAccessible();
222    checkIndex(index, SizeOf.FLOAT);
223    return Float.intBitsToFloat(this.block.peekInt(offset + index, order));
224  }
225
226  @Override public final int getInt() {
227    checkIsAccessible();
228    int newPosition = position + SizeOf.INT;
229    if (newPosition > limit) {
230      throw new BufferUnderflowException();
231    }
232    int result = this.block.peekInt(offset + position, order);
233    position = newPosition;
234    return result;
235  }
236
237  @Override public final int getInt(int index) {
238    checkIsAccessible();
239    checkIndex(index, SizeOf.INT);
240    return this.block.peekInt(offset + index, order);
241  }
242
243  @Override public final long getLong() {
244    checkIsAccessible();
245    int newPosition = position + SizeOf.LONG;
246    if (newPosition > limit) {
247      throw new BufferUnderflowException();
248    }
249    long result = this.block.peekLong(offset + position, order);
250    position = newPosition;
251    return result;
252  }
253
254  @Override public final long getLong(int index) {
255    checkIsAccessible();
256    checkIndex(index, SizeOf.LONG);
257    return this.block.peekLong(offset + index, order);
258  }
259
260  @Override public final short getShort() {
261    checkIsAccessible();
262    int newPosition = position + SizeOf.SHORT;
263    if (newPosition > limit) {
264      throw new BufferUnderflowException();
265    }
266    short result = this.block.peekShort(offset + position, order);
267    position = newPosition;
268    return result;
269  }
270
271  @Override public final short getShort(int index) {
272    checkIsAccessible();
273    checkIndex(index, SizeOf.SHORT);
274    return this.block.peekShort(offset + index, order);
275  }
276
277  @Override public final boolean isDirect() {
278    return true;
279  }
280
281  /** @hide */
282  @Override public final boolean isAccessible() {
283    return block.isAccessible();
284  }
285
286  /** @hide */
287  @Override public void setAccessible(boolean accessible) {
288    block.setAccessible(accessible);
289  }
290
291  /**
292   * Invalidates the buffer. Subsequent operations which touch the inner
293   * buffer will throw {@link IllegalStateException}.
294   */
295  public final void free() {
296    block.free();
297  }
298
299  @Override public final CharBuffer asCharBuffer() {
300    checkNotFreed();
301    return ByteBufferAsCharBuffer.asCharBuffer(this);
302  }
303
304  @Override public final DoubleBuffer asDoubleBuffer() {
305    checkNotFreed();
306    return ByteBufferAsDoubleBuffer.asDoubleBuffer(this);
307  }
308
309  @Override public final FloatBuffer asFloatBuffer() {
310    checkNotFreed();
311    return ByteBufferAsFloatBuffer.asFloatBuffer(this);
312  }
313
314  @Override public final IntBuffer asIntBuffer() {
315    checkNotFreed();
316    return ByteBufferAsIntBuffer.asIntBuffer(this);
317  }
318
319  @Override public final LongBuffer asLongBuffer() {
320    checkNotFreed();
321    return ByteBufferAsLongBuffer.asLongBuffer(this);
322  }
323
324  @Override public final ShortBuffer asShortBuffer() {
325    checkNotFreed();
326    return ByteBufferAsShortBuffer.asShortBuffer(this);
327  }
328
329  @Override public ByteBuffer put(byte value) {
330    checkIsAccessible();
331    if (isReadOnly) {
332      throw new ReadOnlyBufferException();
333    }
334    if (position == limit) {
335      throw new BufferOverflowException();
336    }
337    this.block.pokeByte(offset + position++, value);
338    return this;
339  }
340
341  @Override public ByteBuffer put(int index, byte value) {
342    checkIsAccessible();
343    if (isReadOnly) {
344      throw new ReadOnlyBufferException();
345    }
346    checkIndex(index);
347    this.block.pokeByte(offset + index, value);
348    return this;
349  }
350
351  @Override public ByteBuffer put(byte[] src, int srcOffset, int byteCount) {
352    checkIsAccessible();
353    if (isReadOnly) {
354      throw new ReadOnlyBufferException();
355    }
356    checkPutBounds(1, src.length, srcOffset, byteCount);
357    this.block.pokeByteArray(offset + position, src, srcOffset, byteCount);
358    position += byteCount;
359    return this;
360  }
361
362  final void put(char[] src, int srcOffset, int charCount) {
363    checkIsAccessible();
364    int byteCount = checkPutBounds(SizeOf.CHAR, src.length, srcOffset, charCount);
365    this.block.pokeCharArray(offset + position, src, srcOffset, charCount, order.needsSwap);
366    position += byteCount;
367  }
368
369  final void put(double[] src, int srcOffset, int doubleCount) {
370    checkIsAccessible();
371    int byteCount = checkPutBounds(SizeOf.DOUBLE, src.length, srcOffset, doubleCount);
372    this.block.pokeDoubleArray(offset + position, src, srcOffset, doubleCount, order.needsSwap);
373    position += byteCount;
374  }
375
376  final void put(float[] src, int srcOffset, int floatCount) {
377    checkIsAccessible();
378    int byteCount = checkPutBounds(SizeOf.FLOAT, src.length, srcOffset, floatCount);
379    this.block.pokeFloatArray(offset + position, src, srcOffset, floatCount, order.needsSwap);
380    position += byteCount;
381  }
382
383  final void put(int[] src, int srcOffset, int intCount) {
384    checkIsAccessible();
385    int byteCount = checkPutBounds(SizeOf.INT, src.length, srcOffset, intCount);
386    this.block.pokeIntArray(offset + position, src, srcOffset, intCount, order.needsSwap);
387    position += byteCount;
388  }
389
390  final void put(long[] src, int srcOffset, int longCount) {
391    checkIsAccessible();
392    int byteCount = checkPutBounds(SizeOf.LONG, src.length, srcOffset, longCount);
393    this.block.pokeLongArray(offset + position, src, srcOffset, longCount, order.needsSwap);
394    position += byteCount;
395  }
396
397  final void put(short[] src, int srcOffset, int shortCount) {
398    checkIsAccessible();
399    int byteCount = checkPutBounds(SizeOf.SHORT, src.length, srcOffset, shortCount);
400    this.block.pokeShortArray(offset + position, src, srcOffset, shortCount, order.needsSwap);
401    position += byteCount;
402  }
403
404  @Override public ByteBuffer putChar(char value) {
405    checkIsAccessible();
406    if (isReadOnly) {
407      throw new ReadOnlyBufferException();
408    }
409    int newPosition = position + SizeOf.CHAR;
410    if (newPosition > limit) {
411      throw new BufferOverflowException();
412    }
413    this.block.pokeShort(offset + position, (short) value, order);
414    position = newPosition;
415    return this;
416  }
417
418  @Override public ByteBuffer putChar(int index, char value) {
419    checkIsAccessible();
420    if (isReadOnly) {
421      throw new ReadOnlyBufferException();
422    }
423    checkIndex(index, SizeOf.CHAR);
424    this.block.pokeShort(offset + index, (short) value, order);
425    return this;
426  }
427
428  @Override public ByteBuffer putDouble(double value) {
429    checkIsAccessible();
430    if (isReadOnly) {
431      throw new ReadOnlyBufferException();
432    }
433    int newPosition = position + SizeOf.DOUBLE;
434    if (newPosition > limit) {
435      throw new BufferOverflowException();
436    }
437    this.block.pokeLong(offset + position, Double.doubleToRawLongBits(value), order);
438    position = newPosition;
439    return this;
440  }
441
442  @Override public ByteBuffer putDouble(int index, double value) {
443    checkIsAccessible();
444    if (isReadOnly) {
445      throw new ReadOnlyBufferException();
446    }
447    checkIndex(index, SizeOf.DOUBLE);
448    this.block.pokeLong(offset + index, Double.doubleToRawLongBits(value), order);
449    return this;
450  }
451
452  @Override public ByteBuffer putFloat(float value) {
453    checkIsAccessible();
454    if (isReadOnly) {
455      throw new ReadOnlyBufferException();
456    }
457    int newPosition = position + SizeOf.FLOAT;
458    if (newPosition > limit) {
459      throw new BufferOverflowException();
460    }
461    this.block.pokeInt(offset + position, Float.floatToRawIntBits(value), order);
462    position = newPosition;
463    return this;
464  }
465
466  @Override public ByteBuffer putFloat(int index, float value) {
467    checkIsAccessible();
468    if (isReadOnly) {
469      throw new ReadOnlyBufferException();
470    }
471    checkIndex(index, SizeOf.FLOAT);
472    this.block.pokeInt(offset + index, Float.floatToRawIntBits(value), order);
473    return this;
474  }
475
476  @Override public ByteBuffer putInt(int value) {
477    checkIsAccessible();
478    if (isReadOnly) {
479      throw new ReadOnlyBufferException();
480    }
481    int newPosition = position + SizeOf.INT;
482    if (newPosition > limit) {
483      throw new BufferOverflowException();
484    }
485    this.block.pokeInt(offset + position, value, order);
486    position = newPosition;
487    return this;
488  }
489
490  @Override public ByteBuffer putInt(int index, int value) {
491    checkIsAccessible();
492    if (isReadOnly) {
493      throw new ReadOnlyBufferException();
494    }
495    checkIndex(index, SizeOf.INT);
496    this.block.pokeInt(offset + index, value, order);
497    return this;
498  }
499
500  @Override public ByteBuffer putLong(long value) {
501    checkIsAccessible();
502    if (isReadOnly) {
503      throw new ReadOnlyBufferException();
504    }
505    int newPosition = position + SizeOf.LONG;
506    if (newPosition > limit) {
507      throw new BufferOverflowException();
508    }
509    this.block.pokeLong(offset + position, value, order);
510    position = newPosition;
511    return this;
512  }
513
514  @Override public ByteBuffer putLong(int index, long value) {
515    checkIsAccessible();
516    if (isReadOnly) {
517      throw new ReadOnlyBufferException();
518    }
519    checkIndex(index, SizeOf.LONG);
520    this.block.pokeLong(offset + index, value, order);
521    return this;
522  }
523
524  @Override public ByteBuffer putShort(short value) {
525    checkIsAccessible();
526    if (isReadOnly) {
527      throw new ReadOnlyBufferException();
528    }
529    int newPosition = position + SizeOf.SHORT;
530    if (newPosition > limit) {
531      throw new BufferOverflowException();
532    }
533    this.block.pokeShort(offset + position, value, order);
534    position = newPosition;
535    return this;
536  }
537
538  @Override public ByteBuffer putShort(int index, short value) {
539    checkIsAccessible();
540    if (isReadOnly) {
541      throw new ReadOnlyBufferException();
542    }
543    checkIndex(index, SizeOf.SHORT);
544    this.block.pokeShort(offset + index, value, order);
545    return this;
546  }
547
548  private void checkIsAccessible() {
549    checkNotFreed();
550    if (!block.isAccessible()) {
551      throw new IllegalStateException("buffer is inaccessible");
552    }
553  }
554
555  private void checkNotFreed() {
556    if (block.isFreed()) {
557      throw new IllegalStateException("buffer was freed");
558    }
559  }
560
561}
562