summaryrefslogtreecommitdiff
path: root/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala')
-rw-r--r--examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala235
1 files changed, 235 insertions, 0 deletions
diff --git a/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala b/examples/scala-js/javalib/src/main/scala/java/nio/charset/CharsetEncoder.scala
new file mode 100644
index 0000000..37d2296
--- /dev/null
+++ b/examples/scala-js/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
+}