-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathMessage.kt
More file actions
111 lines (91 loc) · 2.62 KB
/
Message.kt
File metadata and controls
111 lines (91 loc) · 2.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package mutex
import mutex.MessageFieldType.*
import java.io.*
/**
* Inter-process communication message.
*/
class Message(val bytes: ByteArray) {
override fun toString(): String = parse {
generateSequence {
when (cur) {
END -> null
INT -> readInt().toString()
STR -> readString()
ENUM -> readEnumName()
}
}.joinToString(separator = ", ", prefix = "Message(", postfix = ")")
}
}
/**
* Builder class for env messages.
*/
class MessageBuilder {
private val out = ByteArrayOutputStream()
private val data = DataOutputStream(out)
fun writeInt(value: Int) {
writeField(INT)
data.writeInt(value)
}
fun writeString(str: String) {
writeField(STR)
data.writeUTF(str)
}
fun writeEnum(enum: Enum<*>) {
writeField(ENUM)
data.writeUTF(enum.name)
}
fun build(): Message = Message(out.toByteArray())
private fun writeField(t: MessageFieldType) {
data.writeByte(t.ordinal)
}
}
/**
* Parser class for messages.
*/
class MessageParser(message: Message) {
private val data = DataInputStream(ByteArrayInputStream(message.bytes))
private var curField: MessageFieldType? = null
fun readInt(): Int {
check(cur == INT) { "Expected int field, but $cur found" }
return data.readInt().also { done() }
}
fun readString(): String {
check(cur == STR) { "Expected string field, but $cur found" }
return data.readUTF().also { done() }
}
fun readEnumName(): String {
check(cur == ENUM) { "Expected enum field, but $cur found" }
return data.readUTF().also { done() }
}
inline fun <reified T : Enum<T>> readEnum(): T = enumValueOf(readEnumName())
val cur: MessageFieldType
get() = curField ?: run {
val b = data.read()
when (b) {
-1 -> END
in 1 until FIELD_TYPES.size -> FIELD_TYPES[b]
else -> error("Unexpected field type $b")
}
}.also {
curField = it
}
private fun done() {
curField = null
}
}
/**
* Type of the message field.
*/
enum class MessageFieldType { END, INT, STR, ENUM }
private val FIELD_TYPES = enumValues<MessageFieldType>()
/**
* Builds the message.
*/
@Suppress("FunctionName")
inline fun Message(builder: MessageBuilder.() -> Unit): Message =
MessageBuilder().apply { builder() }.build()
/**
* Parses the message.
*/
inline fun <T> Message.parse(parser: MessageParser.() -> T): T =
MessageParser(this).run { parser() }