diff options
Diffstat (limited to 'src/main/kotlin/net/InMessage.kt')
-rw-r--r-- | src/main/kotlin/net/InMessage.kt | 167 |
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 |