summaryrefslogtreecommitdiff
path: root/javalib/src/main/scala/java/nio
diff options
context:
space:
mode:
Diffstat (limited to 'javalib/src/main/scala/java/nio')
-rw-r--r--javalib/src/main/scala/java/nio/Buffer.scala83
-rw-r--r--javalib/src/main/scala/java/nio/BufferOverflowException.scala3
-rw-r--r--javalib/src/main/scala/java/nio/BufferUnderflowException.scala3
-rw-r--r--javalib/src/main/scala/java/nio/ByteBuffer.scala227
-rw-r--r--javalib/src/main/scala/java/nio/ByteOrder.scala15
-rw-r--r--javalib/src/main/scala/java/nio/CharBuffer.scala228
-rw-r--r--javalib/src/main/scala/java/nio/HeapByteBuffer.scala129
-rw-r--r--javalib/src/main/scala/java/nio/HeapCharBuffer.scala138
-rw-r--r--javalib/src/main/scala/java/nio/InvalidMarkException.scala3
-rw-r--r--javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala3
-rw-r--r--javalib/src/main/scala/java/nio/StringCharBuffer.scala102
-rw-r--r--javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala3
-rw-r--r--javalib/src/main/scala/java/nio/charset/Charset.scala103
-rw-r--r--javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala217
-rw-r--r--javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala235
-rw-r--r--javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala3
-rw-r--r--javalib/src/main/scala/java/nio/charset/CoderResult.scala78
-rw-r--r--javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala11
-rw-r--r--javalib/src/main/scala/java/nio/charset/MalformedInputException.scala9
-rw-r--r--javalib/src/main/scala/java/nio/charset/StandardCharsets.scala14
-rw-r--r--javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala9
-rw-r--r--javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala6
22 files changed, 1622 insertions, 0 deletions
diff --git a/javalib/src/main/scala/java/nio/Buffer.scala b/javalib/src/main/scala/java/nio/Buffer.scala
new file mode 100644
index 0000000..be7ab7f
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/Buffer.scala
@@ -0,0 +1,83 @@
+package java.nio
+
+abstract class Buffer private[nio] (val _capacity: Int) {
+ private var _limit: Int = capacity
+ private var _position: Int = 0
+ private[nio] var _mark: Int = -1
+
+ final def capacity(): Int = _capacity
+
+ final def position(): Int = _position
+
+ final def position(newPosition: Int): Buffer = {
+ if (newPosition < 0 || newPosition > limit())
+ throw new IllegalArgumentException
+ _position = newPosition
+ if (_mark > newPosition)
+ _mark = -1
+ this
+ }
+
+ final def limit(): Int = _limit
+
+ final def limit(newLimit: Int): Buffer = {
+ if (newLimit < 0 || newLimit > capacity())
+ throw new IllegalArgumentException
+ _limit = newLimit
+ if (_position > newLimit) {
+ _position = newLimit
+ if (_mark > newLimit)
+ _mark = -1
+ }
+ this
+ }
+
+ final def mark(): Buffer = {
+ _mark = _position
+ this
+ }
+
+ final def reset(): Buffer = {
+ if (_mark == -1)
+ throw new InvalidMarkException
+ _position = _mark
+ this
+ }
+
+ final def clear(): Buffer = {
+ _mark = -1
+ _position = 0
+ _limit = capacity
+ this
+ }
+
+ final def flip(): Buffer = {
+ _mark = -1
+ _limit = _position
+ _position = 0
+ this
+ }
+
+ final def rewind(): Buffer = {
+ _mark = -1
+ _position = 0
+ this
+ }
+
+ @inline final def remaining(): Int = limit - position
+
+ @inline final def hasRemaining(): Boolean = position != limit
+
+ def isReadOnly(): Boolean
+
+ def hasArray(): Boolean
+
+ def array(): Object
+
+ def arrayOffset(): Int
+
+ def isDirect(): Boolean
+
+ override def toString(): String =
+ s"${getClass.getName}[pos=$position lim=$limit cap=$capacity]"
+}
diff --git a/javalib/src/main/scala/java/nio/BufferOverflowException.scala b/javalib/src/main/scala/java/nio/BufferOverflowException.scala
new file mode 100644
index 0000000..03f359e
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/BufferOverflowException.scala
@@ -0,0 +1,3 @@
+package java.nio
+
+class BufferOverflowException extends RuntimeException
diff --git a/javalib/src/main/scala/java/nio/BufferUnderflowException.scala b/javalib/src/main/scala/java/nio/BufferUnderflowException.scala
new file mode 100644
index 0000000..e286975
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/BufferUnderflowException.scala
@@ -0,0 +1,3 @@
+package java.nio
+
+class BufferUnderflowException extends RuntimeException
diff --git a/javalib/src/main/scala/java/nio/ByteBuffer.scala b/javalib/src/main/scala/java/nio/ByteBuffer.scala
new file mode 100644
index 0000000..b743b39
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/ByteBuffer.scala
@@ -0,0 +1,227 @@
+package java.nio
+
+object ByteBuffer {
+ private final val HashSeed = -547316498 // "java.nio.ByteBuffer".##
+
+ def allocate(capacity: Int): ByteBuffer =
+ wrap(new Array[Byte](capacity))
+
+ def allocateDirect(capacity: Int): ByteBuffer =
+ allocate(capacity)
+
+ def wrap(array: Array[Byte], offset: Int, length: Int): ByteBuffer =
+ HeapByteBuffer.wrap(array, 0, array.length, offset, length, false)
+
+ def wrap(array: Array[Byte]): ByteBuffer =
+ wrap(array, 0, array.length)
+}
+
+abstract class ByteBuffer private[nio] (
+ _capacity: Int, private[nio] val _array: Array[Byte],
+ private[nio] val _arrayOffset: Int)
+ extends Buffer(_capacity) with Comparable[ByteBuffer] {
+
+ def this(_capacity: Int) = this(_capacity, null, -1)
+
+ private var _order: ByteOrder = ByteOrder.BIG_ENDIAN
+
+ def slice(): ByteBuffer
+
+ def duplicate(): ByteBuffer
+
+ def asReadOnlyBuffer(): ByteBuffer
+
+ def get(): Byte
+
+ def put(b: Byte): ByteBuffer
+
+ def get(index: Int): Byte
+
+ def put(index: Int, b: Byte): ByteBuffer
+
+ def get(dst: Array[Byte], offset: Int, length: Int): ByteBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+ if (remaining < length)
+ throw new BufferUnderflowException
+
+ var i = offset
+ while (i != end) {
+ dst(i) = get()
+ i += 1
+ }
+
+ this
+ }
+
+ def get(dst: Array[Byte]): ByteBuffer =
+ get(dst, 0, dst.length)
+
+ def put(src: ByteBuffer): ByteBuffer = {
+ if (src eq this)
+ throw new IllegalArgumentException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (src.remaining > remaining)
+ throw new BufferOverflowException
+
+ var n = src.remaining
+ if (src._array != null) { // even if read-only
+ val pos = src.position
+ put(src._array, src._arrayOffset + pos, n)
+ src.position(pos + n)
+ } else {
+ while (n != 0) {
+ put(src.get())
+ n -= 1
+ }
+ }
+
+ this
+ }
+
+ def put(src: Array[Byte], offset: Int, length: Int): ByteBuffer = {
+ val end = offset + length
+ if (offset < 0 || length < 0 || end > src.length)
+ throw new IndexOutOfBoundsException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (remaining < length)
+ throw new BufferOverflowException
+
+ var i = offset
+ while (i != end) {
+ put(src(i))
+ i += 1
+ }
+
+ this
+ }
+
+ final def put(src: Array[Byte]): ByteBuffer =
+ put(src, 0, src.length)
+
+ @inline final def hasArray(): Boolean = _array != null && !isReadOnly
+
+ @inline final def array(): Array[Byte] = {
+ val a = _array
+ if (a == null)
+ throw new UnsupportedOperationException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ a
+ }
+
+ @inline final def arrayOffset(): Int = {
+ val o = _arrayOffset
+ if (o == -1)
+ throw new UnsupportedOperationException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ o
+ }
+
+ def compact(): ByteBuffer
+
+ // Not implemented:
+ //def isDirect(): Boolean
+
+ // toString(): String inherited from Buffer
+
+ override def hashCode(): Int = {
+ import scala.util.hashing.MurmurHash3._
+ val start = position
+ val end = limit
+ var h = ByteBuffer.HashSeed
+ var i = start
+ while (i != end) {
+ h = mix(h, get().##)
+ i += 1
+ }
+ position(start)
+ finalizeHash(h, end-start)
+ }
+
+ override def equals(that: Any): Boolean = that match {
+ case that: ByteBuffer => compareTo(that) == 0
+ case _ => false
+ }
+
+ def compareTo(that: ByteBuffer): Int = {
+ if (this eq that) {
+ 0
+ } else {
+ val thisStart = this.position
+ val thisRemaining = this.remaining
+ val thatStart = that.position
+ val thatRemaining = that.remaining
+ val shortestLength = Math.min(thisRemaining, thatRemaining)
+
+ var i = 0
+ while (i != shortestLength) {
+ val cmp = this.get().compareTo(that.get())
+ if (cmp != 0) {
+ this.position(thisStart)
+ that.position(thatStart)
+ return cmp
+ }
+ i += 1
+ }
+
+ this.position(thisStart)
+ that.position(thatStart)
+ thisRemaining.compareTo(thatRemaining)
+ }
+ }
+
+ final def order(): ByteOrder = _order
+
+ final def order(bo: ByteOrder): ByteBuffer = {
+ if (bo == null)
+ throw new NullPointerException
+ _order = bo
+ this
+ }
+
+ /* Not implemented:
+
+ def getChar(): Char
+ def putChar(value: Char): ByteBuffer
+ def getChar(index: Int): Char
+ def putChar(index: Int, value: Char): ByteBuffer
+ def asCharBuffer(): CharBuffer
+
+ def getShort(): Short
+ def putShort(value: Short): ByteBuffer
+ def getShort(index: Int): Short
+ def putShort(index: Int, value: Short): ByteBuffer
+ def asShortBuffer(): ShortBuffer
+
+ def getInt(): Int
+ def putInt(value: Int): ByteBuffer
+ def getInt(index: Int): Int
+ def putInt(index: Int, value: Int): ByteBuffer
+ def asIntBuffer(): IntBuffer
+
+ def getLong(): Long
+ def putLong(value: Long): ByteBuffer
+ def getLong(index: Int): Long
+ def putLong(index: Int, value: Long): ByteBuffer
+ def asLongBuffer(): LongBuffer
+
+ def getFloat(): Float
+ def putFloat(value: Float): ByteBuffer
+ def getFloat(index: Int): Float
+ def putFloat(index: Int, value: Float): ByteBuffer
+ def asFloatBuffer(): FloatBuffer
+
+ def getDouble(): Double
+ def putDouble(value: Double): ByteBuffer
+ def getDouble(index: Int): Double
+ def putDouble(index: Int, value: Double): ByteBuffer
+ def asDoubleBuffer(): DoubleBuffer
+
+ */
+}
diff --git a/javalib/src/main/scala/java/nio/ByteOrder.scala b/javalib/src/main/scala/java/nio/ByteOrder.scala
new file mode 100644
index 0000000..20bac6a
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/ByteOrder.scala
@@ -0,0 +1,15 @@
+package java.nio
+
+final class ByteOrder private (name: String) {
+ override def toString(): String = name
+}
+
+object ByteOrder {
+ val BIG_ENDIAN: ByteOrder = new ByteOrder("BIG_ENDIAN")
+ val LITTLE_ENDIAN: ByteOrder = new ByteOrder("LITTLE_ENDIAN")
+
+ def nativeOrder(): ByteOrder = {
+ if (scala.scalajs.runtime.Bits.areTypedArraysBigEndian) BIG_ENDIAN
+ else LITTLE_ENDIAN
+ }
+}
diff --git a/javalib/src/main/scala/java/nio/CharBuffer.scala b/javalib/src/main/scala/java/nio/CharBuffer.scala
new file mode 100644
index 0000000..5e74953
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/CharBuffer.scala
@@ -0,0 +1,228 @@
+package java.nio
+
+object CharBuffer {
+ private final val HashSeed = -182887236 // "java.nio.CharBuffer".##
+
+ def allocate(capacity: Int): CharBuffer =
+ wrap(new Array[Char](capacity))
+
+ def wrap(array: Array[Char], offset: Int, length: Int): CharBuffer =
+ HeapCharBuffer.wrap(array, 0, array.length, offset, length, false)
+
+ def wrap(array: Array[Char]): CharBuffer =
+ wrap(array, 0, array.length)
+
+ def wrap(csq: CharSequence, start: Int, end: Int): CharBuffer =
+ StringCharBuffer.wrap(csq, 0, csq.length, start, end)
+
+ def wrap(csq: CharSequence): CharBuffer =
+ wrap(csq, 0, csq.length)
+}
+
+abstract class CharBuffer private[nio] (
+ _capacity: Int, private[nio] val _array: Array[Char],
+ private[nio] val _arrayOffset: Int)
+ extends Buffer(_capacity) with Comparable[CharBuffer]
+ with CharSequence with Appendable with Readable {
+
+ def this(_capacity: Int) = this(_capacity, null, -1)
+
+ def read(target: CharBuffer): Int = {
+ // Attention: this method must not change this buffer's position
+ val n = remaining
+ if (n == 0) -1
+ else if (_array != null) { // even if read-only
+ target.put(_array, _arrayOffset, n)
+ n
+ } else {
+ val savedPos = position
+ target.put(this)
+ position(savedPos)
+ n
+ }
+ }
+
+ def slice(): CharBuffer
+
+ def duplicate(): CharBuffer
+
+ def asReadOnlyBuffer(): CharBuffer
+
+ def get(): Char
+
+ def put(c: Char): CharBuffer
+
+ def get(index: Int): Char
+
+ def put(index: Int, c: Char): CharBuffer
+
+ def get(dst: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+ if (remaining < length)
+ throw new BufferUnderflowException
+
+ var i = offset
+ while (i != end) {
+ dst(i) = get()
+ i += 1
+ }
+
+ this
+ }
+
+ def get(dst: Array[Char]): CharBuffer =
+ get(dst, 0, dst.length)
+
+ def put(src: CharBuffer): CharBuffer = {
+ if (src eq this)
+ throw new IllegalArgumentException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (src.remaining > remaining)
+ throw new BufferOverflowException
+
+ var n = src.remaining
+ if (src._array != null) { // even if read-only
+ val pos = src.position
+ put(src._array, src._arrayOffset + pos, n)
+ src.position(pos + n)
+ } else {
+ while (n != 0) {
+ put(src.get())
+ n -= 1
+ }
+ }
+
+ this
+ }
+
+ def put(src: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+ if (offset < 0 || length < 0 || end > src.length)
+ throw new IndexOutOfBoundsException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (remaining < length)
+ throw new BufferOverflowException
+
+ var i = offset
+ while (i != end) {
+ put(src(i))
+ i += 1
+ }
+
+ this
+ }
+
+ final def put(src: Array[Char]): CharBuffer =
+ put(src, 0, src.length)
+
+ def put(src: String, start: Int, end: Int): CharBuffer =
+ put(CharBuffer.wrap(src, start, end))
+
+ final def put(src: String): CharBuffer =
+ put(src, 0, src.length)
+
+ @inline final def hasArray(): Boolean = _array != null && !isReadOnly
+
+ @inline final def array(): Array[Char] = {
+ val a = _array
+ if (a == null)
+ throw new UnsupportedOperationException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ a
+ }
+
+ @inline final def arrayOffset(): Int = {
+ val o = _arrayOffset
+ if (o == -1)
+ throw new UnsupportedOperationException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ o
+ }
+
+ def compact(): CharBuffer
+
+ // Not implemented:
+ //def isDirect(): Boolean
+
+ override def hashCode(): Int = {
+ import scala.util.hashing.MurmurHash3._
+ val start = position
+ val end = limit
+ var h = CharBuffer.HashSeed
+ var i = start
+ while (i != end) {
+ h = mix(h, get().##)
+ i += 1
+ }
+ position(start)
+ finalizeHash(h, end-start)
+ }
+
+ override def equals(that: Any): Boolean = that match {
+ case that: CharBuffer => compareTo(that) == 0
+ case _ => false
+ }
+
+ def compareTo(that: CharBuffer): Int = {
+ if (this eq that) {
+ 0
+ } else {
+ val thisStart = this.position
+ val thisRemaining = this.remaining
+ val thatStart = that.position
+ val thatRemaining = that.remaining
+ val shortestLength = Math.min(thisRemaining, thatRemaining)
+
+ var i = 0
+ while (i != shortestLength) {
+ val cmp = this.get().compareTo(that.get())
+ if (cmp != 0) {
+ this.position(thisStart)
+ that.position(thatStart)
+ return cmp
+ }
+ i += 1
+ }
+
+ this.position(thisStart)
+ that.position(thatStart)
+ thisRemaining.compareTo(thatRemaining)
+ }
+ }
+
+ override def toString(): String = {
+ if (_array != null) { // even if read-only
+ new String(_array, position + _arrayOffset, remaining)
+ } else {
+ val chars = new Array[Char](remaining)
+ val savedPos = position
+ get(chars)
+ position(savedPos)
+ new String(chars)
+ }
+ }
+
+ final def length(): Int = remaining
+
+ final def charAt(index: Int): Char = get(position + index)
+
+ def subSequence(start: Int, end: Int): CharSequence
+
+ def append(csq: CharSequence): CharBuffer =
+ put(csq.toString())
+
+ def append(csq: CharSequence, start: Int, end: Int): CharBuffer =
+ put(csq.subSequence(start, end).toString())
+
+ def append(c: Char): CharBuffer =
+ put(c)
+
+ def order(): ByteOrder
+}
diff --git a/javalib/src/main/scala/java/nio/HeapByteBuffer.scala b/javalib/src/main/scala/java/nio/HeapByteBuffer.scala
new file mode 100644
index 0000000..ed3fd29
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/HeapByteBuffer.scala
@@ -0,0 +1,129 @@
+package java.nio
+
+private[nio] final class HeapByteBuffer private (
+ _capacity: Int, _array0: Array[Byte], _arrayOffset0: Int,
+ _initialPosition: Int, _initialLimit: Int, _readOnly: Boolean)
+ extends ByteBuffer(_capacity, _array0, _arrayOffset0) {
+
+ position(_initialPosition)
+ limit(_initialLimit)
+
+ def isReadOnly(): Boolean = _readOnly
+
+ def isDirect(): Boolean = false
+
+ def slice(): ByteBuffer = {
+ val cap = remaining
+ new HeapByteBuffer(cap, _array, _arrayOffset+position, 0, cap, isReadOnly)
+ }
+
+ def duplicate(): ByteBuffer = {
+ val result = new HeapByteBuffer(capacity, _array, _arrayOffset,
+ position, limit, isReadOnly)
+ result._mark = this._mark
+ result
+ }
+
+ def asReadOnlyBuffer(): ByteBuffer = {
+ val result = new HeapByteBuffer(capacity, _array, _arrayOffset,
+ position, limit, true)
+ result._mark = this._mark
+ result
+ }
+
+ def get(): Byte = {
+ if (!hasRemaining)
+ throw new BufferUnderflowException
+ val p = position
+ position(p + 1)
+ _array(_arrayOffset + p)
+ }
+
+ def put(b: Byte): ByteBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (!hasRemaining)
+ throw new BufferOverflowException
+ val p = position
+ _array(_arrayOffset + p) = b
+ position(p + 1)
+ this
+ }
+
+ def get(index: Int): Byte = {
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _array(_arrayOffset + index)
+ }
+
+ def put(index: Int, b: Byte): ByteBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _array(_arrayOffset + index) = b
+ this
+ }
+
+ override def get(dst: Array[Byte], offset: Int, length: Int): ByteBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferUnderflowException
+
+ System.arraycopy(_array, startPos + _arrayOffset, dst, offset, length)
+ position(endPos)
+
+ this
+ }
+
+ override def put(src: Array[Byte], offset: Int, length: Int): ByteBuffer = {
+ val end = offset + length
+ if (offset < 0 || length < 0 || end > src.length)
+ throw new IndexOutOfBoundsException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferOverflowException
+
+ System.arraycopy(src, offset, _array, startPos + _arrayOffset, length)
+ position(endPos)
+
+ this
+ }
+
+ def compact(): ByteBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+
+ val offset = _arrayOffset
+ val len = remaining
+ System.arraycopy(_array, offset + position, _array, offset, len)
+ _mark = -1
+ limit(capacity)
+ position(len)
+ this
+ }
+}
+
+private[nio] object HeapByteBuffer {
+ private[nio] def wrap(array: Array[Byte], arrayOffset: Int, capacity: Int,
+ initialPosition: Int, initialLength: Int,
+ isReadOnly: Boolean): ByteBuffer = {
+ if (arrayOffset < 0 || capacity < 0 || arrayOffset+capacity > array.length)
+ throw new IndexOutOfBoundsException
+ val initialLimit = initialPosition + initialLength
+ if (initialPosition < 0 || initialLength < 0 || initialLimit > capacity)
+ throw new IndexOutOfBoundsException
+ new HeapByteBuffer(capacity, array, arrayOffset,
+ initialPosition, initialLimit, isReadOnly)
+ }
+}
diff --git a/javalib/src/main/scala/java/nio/HeapCharBuffer.scala b/javalib/src/main/scala/java/nio/HeapCharBuffer.scala
new file mode 100644
index 0000000..546c55d
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/HeapCharBuffer.scala
@@ -0,0 +1,138 @@
+package java.nio
+
+private[nio] final class HeapCharBuffer private (
+ _capacity: Int, _array0: Array[Char], _arrayOffset0: Int,
+ _initialPosition: Int, _initialLimit: Int, _readOnly: Boolean)
+ extends CharBuffer(_capacity, _array0, _arrayOffset0) {
+
+ position(_initialPosition)
+ limit(_initialLimit)
+
+ def isReadOnly(): Boolean = _readOnly
+
+ def isDirect(): Boolean = false
+
+ def slice(): CharBuffer = {
+ val cap = remaining
+ new HeapCharBuffer(cap, _array, _arrayOffset + position, 0, cap, isReadOnly)
+ }
+
+ def duplicate(): CharBuffer = {
+ val result = new HeapCharBuffer(capacity, _array, _arrayOffset,
+ position, limit, isReadOnly)
+ result._mark = this._mark
+ result
+ }
+
+ def asReadOnlyBuffer(): CharBuffer = {
+ val result = new HeapCharBuffer(capacity, _array, _arrayOffset,
+ position, limit, true)
+ result._mark = this._mark
+ result
+ }
+
+ def subSequence(start: Int, end: Int): CharBuffer = {
+ if (start < 0 || end < start || end > remaining)
+ throw new IndexOutOfBoundsException
+ new HeapCharBuffer(capacity, _array, _arrayOffset,
+ position + start, position + end, isReadOnly)
+ }
+
+ def get(): Char = {
+ if (!hasRemaining)
+ throw new BufferUnderflowException
+ val p = position
+ position(p + 1)
+ _array(_arrayOffset + p)
+ }
+
+ def put(c: Char): CharBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (!hasRemaining)
+ throw new BufferOverflowException
+ val p = position
+ _array(_arrayOffset + p) = c
+ position(p + 1)
+ this
+ }
+
+ def get(index: Int): Char = {
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _array(_arrayOffset + index)
+ }
+
+ def put(index: Int, b: Char): CharBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _array(_arrayOffset + index) = b
+ this
+ }
+
+ override def get(dst: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferUnderflowException
+
+ System.arraycopy(_array, startPos + _arrayOffset, dst, offset, length)
+ position(endPos)
+
+ this
+ }
+
+ override def put(src: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+ if (offset < 0 || length < 0 || end > src.length)
+ throw new IndexOutOfBoundsException
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferOverflowException
+
+ System.arraycopy(src, offset, _array, startPos + _arrayOffset, length)
+ position(endPos)
+
+ this
+ }
+
+ def compact(): CharBuffer = {
+ if (isReadOnly)
+ throw new ReadOnlyBufferException
+
+ val offset = _arrayOffset
+ val len = remaining
+ System.arraycopy(_array, offset + position, _array, offset, len)
+ _mark = -1
+ limit(capacity)
+ position(len)
+ this
+ }
+
+ def order(): ByteOrder = ByteOrder.nativeOrder()
+}
+
+private[nio] object HeapCharBuffer {
+ private[nio] def wrap(array: Array[Char], arrayOffset: Int, capacity: Int,
+ initialPosition: Int, initialLength: Int,
+ isReadOnly: Boolean): CharBuffer = {
+ if (arrayOffset < 0 || capacity < 0 || arrayOffset+capacity > array.length)
+ throw new IndexOutOfBoundsException
+ val initialLimit = initialPosition + initialLength
+ if (initialPosition < 0 || initialLength < 0 || initialLimit > capacity)
+ throw new IndexOutOfBoundsException
+ new HeapCharBuffer(capacity, array, arrayOffset,
+ initialPosition, initialLimit, isReadOnly)
+ }
+}
diff --git a/javalib/src/main/scala/java/nio/InvalidMarkException.scala b/javalib/src/main/scala/java/nio/InvalidMarkException.scala
new file mode 100644
index 0000000..c2d3714
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/InvalidMarkException.scala
@@ -0,0 +1,3 @@
+package java.nio
+
+class InvalidMarkException extends IllegalStateException
diff --git a/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala b/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala
new file mode 100644
index 0000000..ee0868b
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/ReadOnlyBufferException.scala
@@ -0,0 +1,3 @@
+package java.nio
+
+class ReadOnlyBufferException extends UnsupportedOperationException
diff --git a/javalib/src/main/scala/java/nio/StringCharBuffer.scala b/javalib/src/main/scala/java/nio/StringCharBuffer.scala
new file mode 100644
index 0000000..25bc594
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/StringCharBuffer.scala
@@ -0,0 +1,102 @@
+package java.nio
+
+private[nio] final class StringCharBuffer private (
+ _capacity: Int, private[this] var _csq: CharSequence,
+ private[this] var _csqOffset: Int,
+ _initialPosition: Int, _initialLimit: Int)
+ extends CharBuffer(_capacity) {
+
+ position(_initialPosition)
+ limit(_initialLimit)
+
+ def isReadOnly(): Boolean = true
+
+ def isDirect(): Boolean = false
+
+ def slice(): CharBuffer = {
+ val cap = remaining
+ new StringCharBuffer(cap, _csq, _csqOffset + position, 0, cap)
+ }
+
+ def duplicate(): CharBuffer = {
+ val result = new StringCharBuffer(capacity, _csq, _csqOffset,
+ position, limit)
+ result._mark = this._mark
+ result
+ }
+
+ def asReadOnlyBuffer(): CharBuffer = duplicate()
+
+ def subSequence(start: Int, end: Int): CharBuffer = {
+ if (start < 0 || end < start || end > remaining)
+ throw new IndexOutOfBoundsException
+ new StringCharBuffer(capacity, _csq, _csqOffset,
+ position + start, position + end)
+ }
+
+ def get(): Char = {
+ if (!hasRemaining)
+ throw new BufferUnderflowException
+ val p = position
+ position(p + 1)
+ _csq.charAt(_csqOffset + p)
+ }
+
+ def put(c: Char): CharBuffer =
+ throw new ReadOnlyBufferException
+
+ def get(index: Int): Char = {
+ if (index < 0 || index >= limit)
+ throw new IndexOutOfBoundsException
+ _csq.charAt(_csqOffset + index)
+ }
+
+ def put(index: Int, b: Char): CharBuffer =
+ throw new ReadOnlyBufferException
+
+ override def get(dst: Array[Char], offset: Int, length: Int): CharBuffer = {
+ val end = offset + length
+
+ if (offset < 0 || length < 0 || end > dst.length)
+ throw new IndexOutOfBoundsException
+
+ val startPos = position
+ val endPos = startPos + length
+ if (endPos > limit)
+ throw new BufferUnderflowException
+
+ var i = offset
+ var j = startPos + _csqOffset
+ while (i != end) {
+ dst(i) = _csq.charAt(j)
+ i += 1
+ j += 1
+ }
+ position(endPos)
+
+ this
+ }
+
+ def compact(): CharBuffer =
+ throw new ReadOnlyBufferException
+
+ override def toString(): String = {
+ val offset = _csqOffset
+ _csq.subSequence(position + offset, limit + offset).toString()
+ }
+
+ def order(): ByteOrder = ByteOrder.nativeOrder()
+}
+
+private[nio] object StringCharBuffer {
+ private[nio] def wrap(csq: CharSequence, csqOffset: Int, capacity: Int,
+ initialPosition: Int, initialLength: Int): CharBuffer = {
+ if (csqOffset < 0 || capacity < 0 || csqOffset+capacity > csq.length)
+ throw new IndexOutOfBoundsException
+ val initialLimit = initialPosition + initialLength
+ if (initialPosition < 0 || initialLength < 0 || initialLimit > capacity)
+ throw new IndexOutOfBoundsException
+ new StringCharBuffer(capacity, csq, csqOffset,
+ initialPosition, initialLimit)
+ }
+}
diff --git a/javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala b/javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala
new file mode 100644
index 0000000..8017348
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/CharacterCodingException.scala
@@ -0,0 +1,3 @@
+package java.nio.charset
+
+class CharacterCodingException extends java.io.IOException
diff --git a/javalib/src/main/scala/java/nio/charset/Charset.scala b/javalib/src/main/scala/java/nio/charset/Charset.scala
new file mode 100644
index 0000000..6d1af47
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/Charset.scala
@@ -0,0 +1,103 @@
+package java.nio.charset
+
+import java.nio.{ByteBuffer, CharBuffer}
+
+import scala.scalajs.js
+
+abstract class Charset protected (canonicalName: String,
+ aliases: Array[String]) extends AnyRef with Comparable[Charset] {
+ final def name(): String = canonicalName
+
+ override final def equals(that: Any): Boolean = that match {
+ case that: Charset => this.name == that.name
+ case _ => false
+ }
+
+ override final def toString(): String = name()
+
+ override final def hashCode(): Int = name.##
+
+ override final def compareTo(that: Charset): Int =
+ name.compareToIgnoreCase(that.name)
+
+ def contains(cs: Charset): Boolean
+
+ def newDecoder(): CharsetDecoder
+ def newEncoder(): CharsetEncoder
+
+ def canEncode(): Boolean = true
+
+ private lazy val cachedDecoder = {
+ this.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE)
+ }
+
+ private lazy val cachedEncoder = {
+ this.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE)
+ }
+
+ final def decode(bb: ByteBuffer): CharBuffer =
+ cachedDecoder.decode(bb)
+
+ final def encode(cb: CharBuffer): ByteBuffer =
+ cachedEncoder.encode(cb)
+
+ final def encode(str: String): ByteBuffer =
+ encode(CharBuffer.wrap(str))
+
+ def displayName(): String = name
+}
+
+object Charset {
+ import StandardCharsets._
+
+ def defaultCharset(): Charset =
+ UTF_8
+
+ def forName(charsetName: String): Charset =
+ CharsetMap.getOrElse(charsetName.toLowerCase,
+ throw new UnsupportedCharsetException(charsetName))
+
+ def isSupported(charsetName: String): Boolean =
+ CharsetMap.contains(charsetName.toLowerCase)
+
+ private lazy val CharsetMap = {
+ val m = js.Dictionary.empty[Charset]
+
+ // All these lists where obtained by experimentation on the JDK
+
+ for (s <- Seq("iso-8859-1", "iso8859-1", "iso_8859_1", "iso8859_1",
+ "iso_8859-1", "8859_1", "iso_8859-1:1987",
+ "latin1", "csisolatin1", "l1",
+ "ibm-819", "ibm819", "cp819", "819",
+ "iso-ir-100"))
+ m(s) = ISO_8859_1
+
+ for (s <- Seq("us-ascii", "ascii7", "ascii", "csascii",
+ "default",
+ "cp367", "ibm367",
+ "iso646-us", "646", "iso_646.irv:1983", "iso_646.irv:1991",
+ "ansi_x3.4-1986", "ansi_x3.4-1968",
+ "iso-ir-6"))
+ m(s) = US_ASCII
+
+ for (s <- Seq("utf-8", "utf_8", "utf8", "unicode-1-1-utf-8"))
+ m(s) = UTF_8
+
+ for (s <- Seq("utf-16be", "utf_16be", "x-utf-16be",
+ "iso-10646-ucs-2", "unicodebigunmarked"))
+ m(s) = UTF_16BE
+
+ for (s <- Seq("utf-16le", "utf_16le", "x-utf-16le",
+ "unicodelittleunmarked"))
+ m(s) = UTF_16LE
+
+ for (s <- Seq("utf-16", "utf_16", "unicode", "unicodebig"))
+ m(s) = UTF_16
+
+ m
+ }
+}
diff --git a/javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala b/javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala
new file mode 100644
index 0000000..a3532ba
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/CharsetDecoder.scala
@@ -0,0 +1,217 @@
+package java.nio.charset
+
+import scala.annotation.{switch, tailrec}
+
+import java.nio._
+
+abstract class CharsetDecoder protected (cs: Charset,
+ _averageCharsPerByte: Float, _maxCharsPerByte: Float) {
+
+ import CharsetDecoder._
+
+ // Config
+
+ private[this] var _replacement: String = "\uFFFD"
+ private[this] var _malformedInputAction: CodingErrorAction =
+ CodingErrorAction.REPORT
+ private[this] var _unmappableCharacterAction: CodingErrorAction =
+ CodingErrorAction.REPORT
+
+ // Status
+
+ private[this] var status: Int = INIT
+
+ // Methods
+
+ final def charset(): Charset = cs
+
+ final def replacement(): String = _replacement
+
+ final def replaceWith(newReplacement: String): CharsetDecoder = {
+ if (newReplacement == null || newReplacement == "")
+ throw new IllegalArgumentException("Invalid replacement: "+newReplacement)
+ if (newReplacement.length > maxCharsPerByte)
+ throw new IllegalArgumentException(
+ "Replacement string cannot be longer than maxCharsPerByte")
+ _replacement = newReplacement
+ implReplaceWith(newReplacement)
+ this
+ }
+
+ protected def implReplaceWith(newReplacement: String): Unit = ()
+
+ def malformedInputAction(): CodingErrorAction = _malformedInputAction
+
+ final def onMalformedInput(newAction: CodingErrorAction): CharsetDecoder = {
+ if (newAction == null)
+ throw new IllegalArgumentException("null CodingErrorAction")
+ _malformedInputAction = newAction
+ implOnMalformedInput(newAction)
+ this
+ }
+
+ protected def implOnMalformedInput(newAction: CodingErrorAction): Unit = ()
+
+ def unmappableCharacterAction(): CodingErrorAction = _unmappableCharacterAction
+
+ final def onUnmappableCharacter(newAction: CodingErrorAction): CharsetDecoder = {
+ if (newAction == null)
+ throw new IllegalArgumentException("null CodingErrorAction")
+ _unmappableCharacterAction = newAction
+ implOnUnmappableCharacter(newAction)
+ this
+ }
+
+ protected def implOnUnmappableCharacter(newAction: CodingErrorAction): Unit = ()
+
+ final def averageCharsPerByte(): Float = _averageCharsPerByte
+ final def maxCharsPerByte(): Float = _maxCharsPerByte
+
+ final def decode(in: ByteBuffer, out: CharBuffer,
+ endOfInput: Boolean): CoderResult = {
+
+ if (status == FLUSHED || (!endOfInput && status == END))
+ throw new IllegalStateException
+
+ status = if (endOfInput) END else ONGOING
+
+ @inline
+ @tailrec
+ def loop(): CoderResult = {
+ val result1 = try {
+ decodeLoop(in, out)
+ } catch {
+ case ex: BufferOverflowException =>
+ throw new CoderMalfunctionError(ex)
+ case ex: BufferUnderflowException =>
+ throw new CoderMalfunctionError(ex)
+ }
+
+ val result2 = if (result1.isUnderflow) {
+ val remaining = in.remaining
+ if (endOfInput && remaining > 0)
+ CoderResult.malformedForLength(remaining)
+ else
+ result1
+ } else {
+ result1
+ }
+
+ if (result2.isUnderflow || result2.isOverflow) {
+ result2
+ } else {
+ val action =
+ if (result2.isUnmappable) unmappableCharacterAction
+ else malformedInputAction
+
+ action match {
+ case CodingErrorAction.REPLACE =>
+ if (out.remaining < replacement.length) {
+ CoderResult.OVERFLOW
+ } else {
+ out.put(replacement)
+ in.position(in.position + result2.length)
+ loop()
+ }
+ case CodingErrorAction.REPORT =>
+ result2
+ case CodingErrorAction.IGNORE =>
+ in.position(in.position + result2.length)
+ loop()
+ }
+ }
+ }
+
+ loop()
+ }
+
+ final def flush(out: CharBuffer): CoderResult = {
+ (status: @switch) match {
+ case END =>
+ val result = implFlush(out)
+ if (result.isUnderflow)
+ status = FLUSHED
+ result
+ case FLUSHED =>
+ CoderResult.UNDERFLOW
+ case _ =>
+ throw new IllegalStateException
+ }
+ }
+
+ protected def implFlush(out: CharBuffer): CoderResult =
+ CoderResult.UNDERFLOW
+
+ final def reset(): CharsetDecoder = {
+ status = INIT
+ implReset()
+ this
+ }
+
+ protected def implReset(): Unit = ()
+
+ protected def decodeLoop(in: ByteBuffer, out: CharBuffer): CoderResult
+
+ final def decode(in: ByteBuffer): CharBuffer = {
+ def grow(out: CharBuffer): CharBuffer = {
+ if (out.capacity == 0) {
+ CharBuffer.allocate(1)
+ } else {
+ val result = CharBuffer.allocate(out.capacity*2)
+ out.flip()
+ result.put(out)
+ result
+ }
+ }
+
+ @inline
+ @tailrec
+ def loopDecode(out: CharBuffer): CharBuffer = {
+ val result = decode(in, out, endOfInput = true)
+ if (result.isUnderflow) {
+ assert(!in.hasRemaining)
+ out
+ } else if (result.isOverflow) {
+ loopDecode(grow(out))
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ @inline
+ @tailrec
+ def loopFlush(out: CharBuffer): CharBuffer = {
+ val result = flush(out)
+ if (result.isUnderflow) {
+ out
+ } else if (result.isOverflow) {
+ loopFlush(grow(out))
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ reset()
+ val initLength = (in.remaining.toDouble * averageCharsPerByte).toInt
+ val out = loopFlush(loopDecode(CharBuffer.allocate(initLength)))
+ out.flip()
+ out
+ }
+
+ def isAutoDetecting(): Boolean = false
+
+ def isCharsetDetected(): Boolean =
+ throw new UnsupportedOperationException
+
+ def detectedCharset(): Charset =
+ throw new UnsupportedOperationException
+}
+
+object CharsetDecoder {
+ private final val INIT = 1
+ private final val ONGOING = 2
+ private final val END = 3
+ private final val FLUSHED = 4
+}
diff --git a/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala b/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala
new file mode 100644
index 0000000..37d2296
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala
@@ -0,0 +1,235 @@
+package java.nio.charset
+
+import scala.annotation.{switch, tailrec}
+
+import java.nio._
+
+abstract class CharsetEncoder protected (cs: Charset,
+ _averageBytesPerChar: Float, _maxBytesPerChar: Float,
+ private[this] var _replacement: Array[Byte]) {
+
+ import CharsetEncoder._
+
+ protected def this(cs: Charset, _averageBytesPerChar: Float,
+ _maxBytesPerChar: Float) =
+ this(cs, _averageBytesPerChar, _averageBytesPerChar, Array('?'.toByte))
+
+ // Config
+
+ private[this] var _malformedInputAction: CodingErrorAction =
+ CodingErrorAction.REPORT
+ private[this] var _unmappableCharacterAction: CodingErrorAction =
+ CodingErrorAction.REPORT
+
+ // Status
+
+ private[this] var status: Int = INIT
+
+ // Methods
+
+ final def charset(): Charset = cs
+
+ final def replacement(): Array[Byte] = _replacement
+
+ final def replaceWith(newReplacement: Array[Byte]): CharsetEncoder = {
+ if (newReplacement == null || newReplacement.length == 0 ||
+ newReplacement.length > maxBytesPerChar ||
+ !isLegalReplacement(newReplacement))
+ throw new IllegalArgumentException
+
+ _replacement = newReplacement
+ implReplaceWith(newReplacement)
+ this
+ }
+
+ protected def implReplaceWith(newReplacement: Array[Byte]): Unit = ()
+
+ def isLegalReplacement(repl: Array[Byte]): Boolean = {
+ val decoder = charset.newDecoder
+ val replBuf = ByteBuffer.wrap(repl)
+
+ @inline
+ @tailrec
+ def loop(outBufSize: Int): Boolean = {
+ val result = decoder.decode(replBuf, CharBuffer.allocate(outBufSize), true)
+ if (result.isOverflow) {
+ loop(outBufSize * 2)
+ } else {
+ !replBuf.hasRemaining
+ }
+ }
+
+ loop(2)
+ }
+
+ def malformedInputAction(): CodingErrorAction = _malformedInputAction
+
+ final def onMalformedInput(newAction: CodingErrorAction): CharsetEncoder = {
+ if (newAction == null)
+ throw new IllegalArgumentException("null CodingErrorAction")
+ _malformedInputAction = newAction
+ implOnMalformedInput(newAction)
+ this
+ }
+
+ protected def implOnMalformedInput(newAction: CodingErrorAction): Unit = ()
+
+ def unmappableCharacterAction(): CodingErrorAction = _unmappableCharacterAction
+
+ final def onUnmappableCharacter(newAction: CodingErrorAction): CharsetEncoder = {
+ if (newAction == null)
+ throw new IllegalArgumentException("null CodingErrorAction")
+ _unmappableCharacterAction = newAction
+ implOnUnmappableCharacter(newAction)
+ this
+ }
+
+ protected def implOnUnmappableCharacter(newAction: CodingErrorAction): Unit = ()
+
+ final def averageBytesPerChar(): Float = _averageBytesPerChar
+ final def maxBytesPerChar(): Float = _maxBytesPerChar
+
+ final def encode(in: CharBuffer, out: ByteBuffer,
+ endOfInput: Boolean): CoderResult = {
+
+ if (status == FLUSHED || (!endOfInput && status == END))
+ throw new IllegalStateException
+
+ status = if (endOfInput) END else ONGOING
+
+ @inline
+ @tailrec
+ def loop(): CoderResult = {
+ val result1 = try {
+ encodeLoop(in, out)
+ } catch {
+ case ex: BufferOverflowException =>
+ throw new CoderMalfunctionError(ex)
+ case ex: BufferUnderflowException =>
+ throw new CoderMalfunctionError(ex)
+ }
+
+ val result2 = if (result1.isUnderflow) {
+ val remaining = in.remaining
+ if (endOfInput && remaining > 0)
+ CoderResult.malformedForLength(remaining)
+ else
+ result1
+ } else {
+ result1
+ }
+
+ if (result2.isUnderflow || result2.isOverflow) {
+ result2
+ } else {
+ val action =
+ if (result2.isUnmappable) unmappableCharacterAction
+ else malformedInputAction
+
+ action match {
+ case CodingErrorAction.REPLACE =>
+ if (out.remaining < replacement.length) {
+ CoderResult.OVERFLOW
+ } else {
+ out.put(replacement)
+ in.position(in.position + result2.length)
+ loop()
+ }
+ case CodingErrorAction.REPORT =>
+ result2
+ case CodingErrorAction.IGNORE =>
+ in.position(in.position + result2.length)
+ loop()
+ }
+ }
+ }
+
+ loop()
+ }
+
+ final def flush(out: ByteBuffer): CoderResult = {
+ (status: @switch) match {
+ case END =>
+ val result = implFlush(out)
+ if (result.isUnderflow)
+ status = FLUSHED
+ result
+ case FLUSHED =>
+ CoderResult.UNDERFLOW
+ case _ =>
+ throw new IllegalStateException
+ }
+ }
+
+ protected def implFlush(out: ByteBuffer): CoderResult =
+ CoderResult.UNDERFLOW
+
+ final def reset(): CharsetEncoder = {
+ status = INIT
+ implReset()
+ this
+ }
+
+ protected def implReset(): Unit = ()
+
+ protected def encodeLoop(arg1: CharBuffer, arg2: ByteBuffer): CoderResult
+
+ final def encode(in: CharBuffer): ByteBuffer = {
+ def grow(out: ByteBuffer): ByteBuffer = {
+ if (out.capacity == 0) {
+ ByteBuffer.allocate(1)
+ } else {
+ val result = ByteBuffer.allocate(out.capacity*2)
+ out.flip()
+ result.put(out)
+ result
+ }
+ }
+
+ if (in.remaining == 0) {
+ ByteBuffer.allocate(0)
+ } else {
+ @inline
+ @tailrec
+ def loopEncode(out: ByteBuffer): ByteBuffer = {
+ val result = encode(in, out, endOfInput = true)
+ if (result.isUnderflow) {
+ assert(!in.hasRemaining)
+ out
+ } else if (result.isOverflow) {
+ loopEncode(grow(out))
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ @inline
+ @tailrec
+ def loopFlush(out: ByteBuffer): ByteBuffer = {
+ val result = flush(out)
+ if (result.isUnderflow) {
+ out
+ } else if (result.isOverflow) {
+ loopFlush(grow(out))
+ } else {
+ result.throwException()
+ throw new AssertionError("should not get here")
+ }
+ }
+
+ reset()
+ val initLength = (in.remaining * averageBytesPerChar).toInt
+ val out = loopFlush(loopEncode(ByteBuffer.allocate(initLength)))
+ out.flip()
+ out
+ }
+ }
+}
+
+object CharsetEncoder {
+ private final val INIT = 0
+ private final val ONGOING = 1
+ private final val END = 2
+ private final val FLUSHED = 3
+}
diff --git a/javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala b/javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala
new file mode 100644
index 0000000..33174f3
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/CoderMalfunctionError.scala
@@ -0,0 +1,3 @@
+package java.nio.charset
+
+class CoderMalfunctionError(cause: Exception) extends Error(cause)
diff --git a/javalib/src/main/scala/java/nio/charset/CoderResult.scala b/javalib/src/main/scala/java/nio/charset/CoderResult.scala
new file mode 100644
index 0000000..fdc63cc
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/CoderResult.scala
@@ -0,0 +1,78 @@
+package java.nio.charset
+
+import scala.annotation.switch
+
+import scala.collection.mutable
+
+import java.nio._
+
+class CoderResult private (kind: Int, _length: Int) {
+ import CoderResult._
+
+ @inline def isUnderflow(): Boolean = kind == Underflow
+ @inline def isOverflow(): Boolean = kind == Overflow
+ @inline def isMalformed(): Boolean = kind == Malformed
+ @inline def isUnmappable(): Boolean = kind == Unmappable
+
+ @inline def isError(): Boolean = isMalformed || isUnmappable
+
+ @inline def length(): Int = {
+ val l = _length
+ if (l < 0)
+ throw new UnsupportedOperationException
+ l
+ }
+
+ def throwException(): Unit = (kind: @switch) match {
+ case Overflow => throw new BufferOverflowException
+ case Underflow => throw new BufferUnderflowException
+ case Malformed => throw new MalformedInputException(_length)
+ case Unmappable => throw new UnmappableCharacterException(_length)
+ }
+}
+
+object CoderResult {
+ private final val Underflow = 0
+ private final val Overflow = 1
+ private final val Malformed = 2
+ private final val Unmappable = 3
+
+ val OVERFLOW: CoderResult = new CoderResult(Overflow, -1)
+ val UNDERFLOW: CoderResult = new CoderResult(Underflow, -1)
+
+ private val Malformed1 = new CoderResult(Malformed, 1)
+ private val Malformed2 = new CoderResult(Malformed, 2)
+ private val Malformed3 = new CoderResult(Malformed, 3)
+ private val Malformed4 = new CoderResult(Malformed, 4)
+
+ private val uniqueMalformed = mutable.Map.empty[Int, CoderResult]
+
+ private val Unmappable1 = new CoderResult(Unmappable, 1)
+ private val Unmappable2 = new CoderResult(Unmappable, 2)
+ private val Unmappable3 = new CoderResult(Unmappable, 3)
+ private val Unmappable4 = new CoderResult(Unmappable, 4)
+
+ private val uniqueUnmappable = mutable.Map.empty[Int, CoderResult]
+
+ @inline def malformedForLength(length: Int): CoderResult = (length: @switch) match {
+ case 1 => Malformed1
+ case 2 => Malformed2
+ case 3 => Malformed3
+ case 4 => Malformed4
+ case _ => malformedForLengthImpl(length)
+ }
+
+ private def malformedForLengthImpl(length: Int): CoderResult =
+ uniqueMalformed.getOrElseUpdate(length, new CoderResult(Malformed, length))
+
+ @inline def unmappableForLength(length: Int): CoderResult = (length: @switch) match {
+ case 1 => Unmappable1
+ case 2 => Unmappable2
+ case 3 => Unmappable3
+ case 4 => Unmappable4
+ case _ => unmappableForLengthImpl(length)
+ }
+
+ private def unmappableForLengthImpl(length: Int): CoderResult =
+ uniqueUnmappable.getOrElseUpdate(length, new CoderResult(Unmappable, length))
+}
diff --git a/javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala b/javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala
new file mode 100644
index 0000000..63b48bb
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/CodingErrorAction.scala
@@ -0,0 +1,11 @@
+package java.nio.charset
+
+class CodingErrorAction private (name: String) {
+ override def toString(): String = name
+}
+
+object CodingErrorAction {
+ val IGNORE = new CodingErrorAction("IGNORE")
+ val REPLACE = new CodingErrorAction("REPLACE")
+ val REPORT = new CodingErrorAction("REPORT")
+}
diff --git a/javalib/src/main/scala/java/nio/charset/MalformedInputException.scala b/javalib/src/main/scala/java/nio/charset/MalformedInputException.scala
new file mode 100644
index 0000000..4c91c1b
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/MalformedInputException.scala
@@ -0,0 +1,9 @@
+package java.nio.charset
+
+class MalformedInputException(
+ inputLength: Int) extends CharacterCodingException {
+ def getInputLength(): Int = inputLength
+
+ override def getMessage(): String =
+ "Input length = " + inputLength
+}
diff --git a/javalib/src/main/scala/java/nio/charset/StandardCharsets.scala b/javalib/src/main/scala/java/nio/charset/StandardCharsets.scala
new file mode 100644
index 0000000..38f3f98
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/StandardCharsets.scala
@@ -0,0 +1,14 @@
+package java.nio.charset
+
+final class StandardCharsets private {}
+
+object StandardCharsets {
+ import scala.scalajs.niocharset.{StandardCharsets => SC}
+
+ def ISO_8859_1: Charset = SC.ISO_8859_1
+ def US_ASCII: Charset = SC.US_ASCII
+ def UTF_8: Charset = SC.UTF_8
+ def UTF_16BE: Charset = SC.UTF_16BE
+ def UTF_16LE: Charset = SC.UTF_16LE
+ def UTF_16: Charset = SC.UTF_16
+}
diff --git a/javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala b/javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala
new file mode 100644
index 0000000..5748f70
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/UnmappableCharacterException.scala
@@ -0,0 +1,9 @@
+package java.nio.charset
+
+class UnmappableCharacterException(
+ inputLength: Int) extends CharacterCodingException {
+ def getInputLength(): Int = inputLength
+
+ override def getMessage(): String =
+ "Input length = " + inputLength
+}
diff --git a/javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala b/javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala
new file mode 100644
index 0000000..97a7a4e
--- /dev/null
+++ b/javalib/src/main/scala/java/nio/charset/UnsupportedCharsetException.scala
@@ -0,0 +1,6 @@
+package java.nio.charset
+
+class UnsupportedCharsetException(
+ charsetName: String) extends IllegalArgumentException(charsetName) {
+ def getCharsetName(): String = charsetName
+}