Skip to content

Commit

Permalink
classlib: refactor nio buffers implementation so that they wrap typed…
Browse files Browse the repository at this point in the history
… arrays in JS

Fix #990
  • Loading branch information
konsoletyper committed Jan 20, 2025
1 parent 13e7cc9 commit 957bc6f
Show file tree
Hide file tree
Showing 45 changed files with 2,317 additions and 372 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ public static int reverse(int i) {

public static int reverseBytes(int i) {
i = ((i & 0xFF00FF00) >>> 8) | ((i & 0x00FF00FF) << 8);
i = (i >>> 16) + (i << 16);
i = (i >>> 16) | (i << 16);
return i;
}

Expand Down
16 changes: 7 additions & 9 deletions classlib/src/main/java/org/teavm/classlib/java/nio/TBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,19 @@
package org.teavm.classlib.java.nio;

public abstract class TBuffer {
int capacity;
int position;
int limit;
int mark = -1;

TBuffer(int capacity) {
this.capacity = capacity;
limit = capacity;
TBuffer() {
}

public final int capacity() {
return capacity;
return capacityImpl();
}

abstract int capacityImpl();

public final int position() {
return position;
}
Expand All @@ -51,9 +50,8 @@ public final int limit() {
}

public TBuffer limit(int newLimit) {
if (newLimit < 0 || newLimit > capacity) {
throw new IllegalArgumentException("New limit " + newLimit + " is outside of range [0;"
+ capacity + "]");
if (newLimit < 0 || newLimit > capacity()) {
throw new IllegalArgumentException();
}
if (mark > newLimit) {
mark = -1;
Expand All @@ -80,7 +78,7 @@ public TBuffer reset() {

public TBuffer clear() {
position = 0;
limit = capacity;
limit = capacity();
mark = -1;
return this;
}
Expand Down
148 changes: 95 additions & 53 deletions classlib/src/main/java/org/teavm/classlib/java/nio/TByteBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,52 @@
*/
package org.teavm.classlib.java.nio;

import java.nio.BufferUnderflowException;
import java.util.Objects;
import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.java.lang.TComparable;
import org.teavm.jso.typedarrays.Int8Array;

public abstract class TByteBuffer extends TBuffer implements TComparable<TByteBuffer> {
int start;
byte[] array;
TByteOrder order = TByteOrder.BIG_ENDIAN;

TByteBuffer(int start, int capacity, byte[] array, int position, int limit) {
super(capacity);
this.start = start;
this.array = array;
this.position = position;
this.limit = limit;
TByteBuffer() {
}

public static TByteBuffer allocateDirect(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("Capacity is negative: " + capacity);
}
if (PlatformDetector.isJavaScript()) {
var result = new TByteBufferJsImpl(null, 0, new Int8Array(capacity), true, false);
result.limit = capacity;
return result;
}
return new TByteBufferImpl(capacity, true);
}

public static TByteBuffer allocate(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("Capacity is negative: " + capacity);
}
if (PlatformDetector.isJavaScript()) {
var array = new byte[capacity];
var result = new TByteBufferJsImpl(array, 0, Int8Array.fromJavaArray(array), false, false);
result.limit = capacity;
return result;
}
return new TByteBufferImpl(capacity, false);
}

public static TByteBuffer wrap(byte[] array, int offset, int length) {
Objects.checkFromIndexSize(offset, length, array.length);
if (PlatformDetector.isJavaScript()) {
var data = Int8Array.fromJavaArray(array);
var result = new TByteBufferJsImpl(array, 0, data, false, false);
result.position = offset;
result.limit = offset + length;
return result;
}
return new TByteBufferImpl(0, array.length, array, offset, offset + length, false, false);
}

Expand All @@ -69,59 +83,65 @@ public static TByteBuffer wrap(byte[] array) {
public abstract TByteBuffer put(int index, byte b);

public TByteBuffer get(byte[] dst, int offset, int length) {
if (offset < 0 || offset > dst.length) {
throw new IndexOutOfBoundsException("Offset " + offset + " is outside of range [0;" + dst.length + ")");
}
if (offset + length > dst.length) {
throw new IndexOutOfBoundsException("The last byte in dst " + (offset + length) + " is outside "
+ "of array of size " + dst.length);
}
if (remaining() < length) {
throw new TBufferUnderflowException();
if (length < 0 || offset < 0 || offset + length > dst.length) {
throw new IndexOutOfBoundsException();
}
if (length < 0) {
throw new IndexOutOfBoundsException("Length " + length + " must be non-negative");
}
int pos = position + start;
for (int i = 0; i < length; ++i) {
dst[offset++] = array[pos++];
if (length > remaining()) {
throw new BufferUnderflowException();
}
getImpl(position, dst, offset, length);
position += length;
return this;
}

public TByteBuffer get(int index, byte[] dst, int offset, int length) {
if (length < 0 || offset < 0 || offset + length > dst.length || index < 0 || index + length > limit()) {
throw new IndexOutOfBoundsException();
}
getImpl(index, dst, offset, length);
return this;
}

abstract void getImpl(int index, byte[] dst, int offset, int length);

public TByteBuffer get(byte[] dst) {
return get(dst, 0, dst.length);
}

public TByteBuffer put(TByteBuffer src) {
return put(src.array, src.start + src.position, src.remaining());
return put(position(), src, src.position(), src.remaining());
}

public TByteBuffer put(byte[] src, int offset, int length) {
if (length == 0) {
public TByteBuffer put(int index, TByteBuffer src, int offset, int length) {
if (src.remaining() == 0) {
return this;
}
if (isReadOnly()) {
throw new TReadOnlyBufferException();
}
if (remaining() < length) {
throw new TBufferOverflowException();
if (length < 0 || offset < 0 || offset + length > src.limit() || index < 0 || index + length > limit()) {
throw new IndexOutOfBoundsException();
}
if (offset < 0 || offset > src.length) {
throw new IndexOutOfBoundsException("Offset " + offset + " is outside of range [0;" + src.length + ")");
putImpl(index, src, offset, length);
return this;
}

abstract void putImpl(int index, TByteBuffer src, int offset, int length);

public TByteBuffer put(byte[] src, int offset, int length) {
if (length == 0) {
return this;
}
if (offset + length > src.length) {
throw new IndexOutOfBoundsException("The last byte in src " + (offset + length) + " is outside "
+ "of array of size " + src.length);
if (isReadOnly()) {
throw new TReadOnlyBufferException();
}
if (length < 0) {
throw new IndexOutOfBoundsException("Length " + length + " must be non-negative");
if (length < 0 || offset < 0 || offset + length > src.length) {
throw new IndexOutOfBoundsException();
}
int pos = position + start;
for (int i = 0; i < length; ++i) {
array[pos++] = src[offset++];
if (length > remaining()) {
throw new TBufferOverflowException();
}
putImpl(src, offset, position, length);
position += length;
return this;
}
Expand All @@ -130,38 +150,60 @@ public final TByteBuffer put(byte[] src) {
return put(src, 0, src.length);
}

public TByteBuffer put(int index, byte[] src, int offset, int length) {
if (length == 0) {
return this;
}
if (isReadOnly()) {
throw new TReadOnlyBufferException();
}
if (length < 0 || offset < 0 || offset + length > src.length || index < 0 || index + length > limit()) {
throw new IndexOutOfBoundsException();
}
putImpl(src, offset, length, length);
return this;
}

abstract void putImpl(byte[] src, int srcOffset, int destOffset, int length);

@Override
public boolean hasArray() {
return true;
public final boolean hasArray() {
return hasArrayImpl();
}

@Override
public final byte[] array() {
return array;
return arrayImpl();
}

@Override
public int arrayOffset() {
return start;
public final int arrayOffset() {
return arrayOffsetImpl();
}

abstract boolean hasArrayImpl();

abstract byte[] arrayImpl();

abstract int arrayOffsetImpl();

public abstract TByteBuffer compact();

@Override
public abstract boolean isDirect();

@Override
public String toString() {
return "[ByteBuffer position=" + position + ", limit=" + limit + ", capacity=" + capacity + ", mark "
return "[ByteBuffer position=" + position + ", limit=" + limit + ", capacity=" + capacity() + ", mark "
+ (mark >= 0 ? " at " + mark : " is not set") + "]";
}

@Override
public int hashCode() {
int hashCode = 0;
int pos = position + start;
for (int i = position; i < limit; ++i) {
hashCode = 31 * hashCode + array[pos++];
int pos = position();
for (int i = position(); i < limit; ++i) {
hashCode = 31 * hashCode + get(pos);
}
return hashCode;
}
Expand All @@ -179,10 +221,10 @@ public boolean equals(Object obj) {
if (sz != other.remaining()) {
return false;
}
int a = position + start;
int b = other.position + other.start;
int a = position();
int b = other.position();
for (int i = 0; i < sz; ++i) {
if (array[a++] != other.array[b++]) {
if (get(a++) != other.get(b++)) {
return false;
}
}
Expand All @@ -195,10 +237,10 @@ public int compareTo(TByteBuffer other) {
return 0;
}
int sz = Math.min(remaining(), other.remaining());
int a = position + start;
int b = other.position + other.start;
int a = position();
int b = other.position();
for (int i = 0; i < sz; ++i) {
int r = Byte.compare(array[a++], other.array[b++]);
int r = Byte.compare(get(a++), other.get(b++));
if (r != 0) {
return r;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,46 @@
package org.teavm.classlib.java.nio;

class TByteBufferImpl extends TByteBuffer {
private int capacity;
private int start;
byte[] array;
private boolean direct;
private boolean readOnly;

public TByteBufferImpl(int capacity, boolean direct) {
TByteBufferImpl(int capacity, boolean direct) {
this(0, capacity, new byte[capacity], 0, capacity, direct, false);
}

public TByteBufferImpl(int start, int capacity, byte[] array, int position, int limit,
boolean direct, boolean readOnly) {
super(start, capacity, array, position, limit);
TByteBufferImpl(int start, int capacity, byte[] array, int position, int limit, boolean direct, boolean readOnly) {
this.start = start;
this.capacity = capacity;
this.array = array;
this.position = position;
this.limit = limit;
this.direct = direct;
this.readOnly = readOnly;
}

@Override
boolean hasArrayImpl() {
return true;
}

@Override
byte[] arrayImpl() {
return array;
}

@Override
int arrayOffsetImpl() {
return start;
}

@Override
int capacityImpl() {
return capacity;
}

@Override
public TByteBuffer slice() {
return new TByteBufferImpl(position + start, limit - position, array, 0, limit - position,
Expand Down Expand Up @@ -86,6 +112,27 @@ public TByteBuffer put(int index, byte b) {
return this;
}

@Override
void getImpl(int index, byte[] dst, int offset, int length) {
System.arraycopy(array, start + index, dst, offset, length);
}

@Override
void putImpl(int index, TByteBuffer src, int offset, int length) {
if (src.hasArray()) {
System.arraycopy(src.array(), src.position(), array, start + offset, length);
} else {
for (var i = 0; i < length; i++) {
put(index + i, src.get(offset + i));
}
}
}

@Override
void putImpl(byte[] src, int srcOffset, int destOffset, int length) {
System.arraycopy(src, srcOffset, array, start + destOffset, length);
}

@Override
public TByteBuffer compact() {
if (readOnly) {
Expand Down
Loading

0 comments on commit 957bc6f

Please sign in to comment.