summaryrefslogtreecommitdiff
path: root/src/main/kotlin/net/InMessage.kt
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/kotlin/net/InMessage.kt')
-rw-r--r--src/main/kotlin/net/InMessage.kt167
1 files changed, 0 insertions, 167 deletions
diff --git a/src/main/kotlin/net/InMessage.kt b/src/main/kotlin/net/InMessage.kt
deleted file mode 100644
index 771c670..0000000
--- a/src/main/kotlin/net/InMessage.kt
+++ /dev/null
@@ -1,167 +0,0 @@
-package net
-
-import NinePMessageType
-import except.InvalidMessageException
-import java.math.BigInteger
-
-/**
- * An incoming 9P message. Upon instancing this class only one message is read, and it's represented in a way similar to
- * that of [OutMessage]. This class is supposed to be complementary, and opposite, to [OutMessage].
- *
- * @param tl The transport layer API.
- * @param maxSize The maximum message size negotiated with the remote part.
- * @param reqTag The required tag.
- * @throws InvalidMessageException if the message that is currently being read is invalid.
- */
-class InMessage(val tl: TransportLayer, maxSize: UInt, val reqTag: UShort) {
- /**
- * The total size of the message.
- */
- val size: UInt
-
- /**
- * The message type.
- */
- val type: NinePMessageType
-
- /**
- * The message tag.
- */
- val tag: UShort
-
- /**
- * A map of each integer field's name to its value.
- */
- var fieldsInt: MutableMap<String, BigInteger> = mutableMapOf()
- private set
-
- /**
- * A map of each string field's name to its value.
- */
- var fieldsStr: MutableMap<String, String> = mutableMapOf()
- private set
-
- /**
- * A map of each raw field's name to its value.
- */
- var fieldsRaw: MutableMap<String, Array<UByte>> = mutableMapOf()
- private set
-
- /**
- * An ordered collection of raw bytes that still need to be interpreted as values.
- */
- private var rawData: List<UByte>
-
- init {
- size = convInteger(this.tl.receiver(), 0, 4).toInt().toUInt()
- if (this.size > maxSize) {
- throw InvalidMessageException("Size greater than maximum size (${this.size} > ${maxSize}).")
- }
- try {
- this.type = NinePMessageType.fromByte(convInteger(this.tl.receiver(), 0, 1).toInt().toUByte())
- } catch (_: NoSuchElementException) {
- throw InvalidMessageException("Invalid 9P message type.")
- }
- tag = convInteger(this.tl.receiver(), 0, 2).toInt().toUShort()
- if (tag != reqTag) {
- // TODO: what do we do now?
- }
- this.rawData = this.tl.receive((size - (4u + 1u + 2u)).toULong()).toList()
- }
-
- /**
- * Field of an incoming 9P message. An ordered collection of fields makes a schema.
- *
- * @param name The field's name. It's typically the same you can find in the manual pages.
- * @param type The field's type.
- * @param size The field's size in bytes. If the type is [Type.STRING], this parameter is ignored.
- */
- data class Field(val name: String, val type: Type, val size: UInt) {
-
- enum class Type {
- INTEGER,
- STRING,
- RAW
- }
- }
-
- /**
- * Apply the given field to the raw data and put it in one of [fieldsInt], [fieldsStr], or [fieldsRaw]. Fields must
- * be applied strictly in order, as their application is not commutative.
- *
- * Each time a field is applied, the initial part of raw data that coincides with that field is removed.
- *
- * @param field The given field.
- */
- fun applyField(field: Field) {
- val size: Int
- when (field.type) {
- Field.Type.STRING -> {
- val str = convString(this.rawData.toList(), 0)
- size = 2 + str.length
- this.fieldsStr[field.name] = str
- }
- Field.Type.INTEGER -> {
- size = field.size.toInt()
- this.fieldsInt[field.name] = convInteger(this.rawData.toList(), 0, size)
- }
- Field.Type.RAW -> {
- size = field.size.toInt()
- this.fieldsRaw[field.name] = this.rawData.take(size).toTypedArray()
- }
- }
- this.rawData = this.rawData.drop(size)
- }
-
- /**
- * Apply the given message schema to the raw data and fill [fieldsInt], [fieldsStr], and [fieldsRaw].
- *
- * Note: This method could have been avoided by making a giant `when` block in the class constructor. However, I'd
- * rather let the caller, which is usually a method that makes a request and reads its response, decide the schema.
- * In this way, each method that needs to read a response of a specific type (and there is usually one method per
- * response type) declares its own schema, while those which cannot be easily represented by a schema (e.g. `Rwalk`)
- * are simply going to be read in a field-by-field fashion.
- *
- * @param schema The desired ordered collection of fields.
- */
- fun applySchema(schema: Iterable<Field>) {
- for (field in schema) {
- applyField(field)
- }
- }
-
- companion object {
- /**
- * Convert an [len] bytes long unsigned integer number from raw bytes.
- *
- * In 9P, binary numbers (non-textual) are specified in little-endian order (least significant byte first).
- *
- * @param len The length of the integer number in bytes. If zero, nothing is read.
- * @return the number's value.
- * @throws IllegalArgumentException if either [offset] or [len] are negative.
- */
- fun convInteger(bytes: Iterable<UByte>, offset: Int, len: Int): BigInteger {
- val bytes = bytes.drop(offset).take(len)
- var value = 0.toBigInteger()
- for (i in 0..<bytes.size) {
- value += bytes[i].toInt().toBigInteger().shl(i*8)
- }
- return value
- }
-
- /**
- * Convert a string from raw bytes.
- *
- * In 9P, strings are represented as a 2-byte integer (the string's size) followed by the actual UTF-8 string. The
- * null terminator is forbidden in 9P messages.
- *
- * @return the string.
- * @throws IllegalArgumentException if either [offset] is negative.
- */
- fun convString(bytes: Iterable<UByte>, offset: Int): String {
- val length = convInteger(bytes, 0, 2).toInt()
- val bytes = bytes.drop(offset).take(length)
- return String(ByteArray(bytes.size) { i -> bytes[i].toByte() })
- }
- }
-} \ No newline at end of file