136 lines
4.7 KiB
Kotlin
136 lines
4.7 KiB
Kotlin
/*
|
|
* Copyright 2017 Christian Basler
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package ch.dissem.msgpack.types
|
|
|
|
import ch.dissem.msgpack.types.Utils.bytes
|
|
import java.io.IOException
|
|
import java.io.InputStream
|
|
import java.io.OutputStream
|
|
import java.nio.ByteBuffer
|
|
import java.nio.charset.Charset
|
|
|
|
/**
|
|
* Representation of a msgpack encoded string. The encoding is automatically selected according to the string's length.
|
|
*
|
|
* The default encoding is UTF-8.
|
|
*/
|
|
data class MPString(override val value: String) : MPType<String>, CharSequence {
|
|
|
|
@Throws(IOException::class)
|
|
override fun pack(out: OutputStream) {
|
|
val bytes = value.toByteArray(encoding)
|
|
val size = bytes.size
|
|
when {
|
|
size < 32 -> out.write(FIXSTR_PREFIX + size)
|
|
size < STR8_LIMIT -> {
|
|
out.write(STR8_PREFIX)
|
|
out.write(size)
|
|
}
|
|
size < STR16_LIMIT -> {
|
|
out.write(STR16_PREFIX)
|
|
out.write(ByteBuffer.allocate(2).putShort(size.toShort()).array())
|
|
}
|
|
else -> {
|
|
out.write(STR32_PREFIX)
|
|
out.write(ByteBuffer.allocate(4).putInt(size).array())
|
|
}
|
|
}
|
|
out.write(bytes)
|
|
}
|
|
|
|
override val length: Int = value.length
|
|
|
|
override fun get(index: Int): Char {
|
|
return value[index]
|
|
}
|
|
|
|
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
|
|
return value.subSequence(startIndex, endIndex)
|
|
}
|
|
|
|
override fun toString(): String {
|
|
return value
|
|
}
|
|
|
|
override fun toJson(): String {
|
|
val result = StringBuilder(value.length + 4)
|
|
result.append('"')
|
|
value.forEach {
|
|
when (it) {
|
|
'\\', '"', '/' -> result.append('\\').append(it)
|
|
'\b' -> result.append("\\b")
|
|
'\t' -> result.append("\\t")
|
|
'\n' -> result.append("\\n")
|
|
'\r' -> result.append("\\r")
|
|
else -> if (it < ' ') {
|
|
result.append("\\u")
|
|
val hex = Integer.toHexString(it.toInt())
|
|
var j = 0
|
|
while (j + hex.length < 4) {
|
|
result.append('0')
|
|
j++
|
|
}
|
|
result.append(hex)
|
|
} else {
|
|
result.append(it)
|
|
}
|
|
}
|
|
}
|
|
result.append('"')
|
|
return result.toString()
|
|
}
|
|
|
|
class Unpacker : MPType.Unpacker<MPString> {
|
|
override fun doesUnpack(firstByte: Int): Boolean {
|
|
return firstByte == STR8_PREFIX || firstByte == STR16_PREFIX || firstByte == STR32_PREFIX
|
|
|| firstByte and FIXSTR_PREFIX_FILTER == FIXSTR_PREFIX
|
|
}
|
|
|
|
@Throws(IOException::class)
|
|
override fun unpack(firstByte: Int, input: InputStream): MPString {
|
|
val size: Int = when {
|
|
firstByte and FIXSTR_PREFIX_FILTER == FIXSTR_PREFIX -> firstByte and FIXSTR_FILTER
|
|
firstByte == STR8_PREFIX -> input.read()
|
|
firstByte == STR16_PREFIX -> input.read() shl 8 or input.read()
|
|
firstByte == STR32_PREFIX -> input.read() shl 24 or (input.read() shl 16) or (input.read() shl 8) or input.read()
|
|
else -> throw IllegalArgumentException(String.format("Unexpected first byte 0x%02x", firstByte))
|
|
}
|
|
return MPString(String(bytes(input, size).array(), encoding))
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
private val FIXSTR_PREFIX = 160
|
|
private val FIXSTR_PREFIX_FILTER = 224
|
|
private val STR8_PREFIX = 0xD9
|
|
private val STR8_LIMIT = 256
|
|
private val STR16_PREFIX = 0xDA
|
|
private val STR16_LIMIT = 65536
|
|
private val STR32_PREFIX = 0xDB
|
|
private val FIXSTR_FILTER = 31
|
|
|
|
/**
|
|
* Use this if for some messed up reason you really need to use something else than UTF-8.
|
|
* Ask yourself: why should I? Is this really necessary?
|
|
*
|
|
* It will set the encoding for all [MPString]s, but if you have inconsistent encoding in your
|
|
* format you're lost anyway.
|
|
*/
|
|
var encoding = Charset.forName("UTF-8")
|
|
}
|
|
}
|