-
Notifications
You must be signed in to change notification settings - Fork 99
/
Snapshot.kt
166 lines (135 loc) · 4.54 KB
/
Snapshot.kt
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
@file:Suppress("EXPERIMENTAL_API_USAGE")
@file:JvmName("Snapshots")
package com.squareup.workflow1
import okio.Buffer
import okio.BufferedSink
import okio.BufferedSource
import okio.ByteString
import okio.ByteString.Companion.encodeUtf8
import kotlin.jvm.JvmName
import kotlin.jvm.JvmStatic
/**
* A lazy wrapper of [ByteString]. Allows [Workflow]s to capture their state frequently, without
* worrying about performing unnecessary serialization work.
*/
public class Snapshot
private constructor(private val toByteString: () -> ByteString) {
public companion object {
@JvmStatic
public fun of(string: String): Snapshot =
Snapshot { string.encodeUtf8() }
@JvmStatic
public fun of(byteString: ByteString): Snapshot =
Snapshot { byteString }
@JvmStatic
public fun of(lazy: () -> ByteString): Snapshot =
Snapshot(lazy)
@JvmStatic
public fun of(integer: Int): Snapshot {
return Snapshot {
with(Buffer()) {
writeInt(integer)
readByteString()
}
}
}
/** Create a snapshot by writing to a nice ergonomic [BufferedSink]. */
@JvmStatic
public fun write(lazy: (BufferedSink) -> Unit): Snapshot =
of {
Buffer().apply(lazy)
.readByteString()
}
}
@get:JvmName("bytes")
public val bytes: ByteString by lazy { toByteString() }
/**
* Returns a `String` describing the [bytes] of this `Snapshot`.
*
* **This method forces serialization, calling it may be expensive.**
*/
override fun toString(): String = "Snapshot($bytes)"
/**
* Compares `Snapshot`s by comparing their [bytes].
*
* **This method forces serialization, calling it may be expensive.**
*/
override fun equals(other: Any?): Boolean =
(other as? Snapshot)?.let { bytes == it.bytes } ?: false
/**
* Calculates hashcode using [bytes].
*
* **This method forces serialization, calling it may be expensive.**
*/
override fun hashCode(): Int = bytes.hashCode()
}
public fun <T : Any> BufferedSink.writeNullable(
obj: T?,
writer: BufferedSink.(T) -> Unit
): BufferedSink = apply {
writeBooleanAsInt(obj != null)
obj?.let { writer(it) }
}
public fun <T : Any> BufferedSource.readNullable(reader: BufferedSource.() -> T): T? {
return if (readBooleanFromInt()) reader() else null
}
public fun BufferedSink.writeBooleanAsInt(bool: Boolean): BufferedSink =
writeInt(if (bool) 1 else 0)
public fun BufferedSource.readBooleanFromInt(): Boolean = readInt() == 1
public fun BufferedSink.writeFloat(float: Float): BufferedSink = writeInt(float.toRawBits())
public fun BufferedSource.readFloat(): Float = Float.fromBits(readInt())
public fun BufferedSink.writeUtf8WithLength(str: String): BufferedSink {
return writeByteStringWithLength(str.encodeUtf8())
}
public fun BufferedSource.readUtf8WithLength(): String = readByteStringWithLength().utf8()
public fun BufferedSink.writeOptionalUtf8WithLength(str: String?): BufferedSink = apply {
writeNullable(str) { writeUtf8WithLength(it) }
}
public fun BufferedSource.readOptionalUtf8WithLength(): String? {
return readNullable { readUtf8WithLength() }
}
public fun BufferedSink.writeByteStringWithLength(bytes: ByteString): BufferedSink = apply {
writeInt(bytes.size)
.write(bytes)
}
public fun BufferedSource.readByteStringWithLength(): ByteString {
val size = readInt()
return readByteString(size.toLong())
}
public inline fun <reified T : Enum<T>> BufferedSource.readOptionalEnumByOrdinal(): T? {
return readNullable { readEnumByOrdinal<T>() }
}
public fun <T : Enum<T>> BufferedSink.writeOptionalEnumByOrdinal(enumVal: T?): BufferedSink {
return writeNullable(enumVal) { writeEnumByOrdinal(it) }
}
public inline fun <reified T : Enum<T>> BufferedSource.readEnumByOrdinal(): T {
return enumValues<T>()[readInt()]
}
public fun <T : Enum<T>> BufferedSink.writeEnumByOrdinal(enumVal: T): BufferedSink {
return writeInt(enumVal.ordinal)
}
public inline fun <T> BufferedSink.writeList(
values: List<T>,
writer: BufferedSink.(T) -> Unit
): BufferedSink = apply {
writeInt(values.size)
values.forEach { writer(it) }
}
public inline fun <T> BufferedSource.readList(
reader: BufferedSource.() -> T
): List<T> = List(readInt()) { reader() }
/**
* Runs `block` with a `BufferedSource` that will read from this `ByteString`.
*
* Lets you do stuff like:
* ```
* myBlob.parse {
* MyValueObject(
* name = it.readUtf8WithLength(),
* age = it.readInt()
* )
* }
* ```
*/
public inline fun <T> ByteString.parse(block: (BufferedSource) -> T): T =
block(Buffer().write(this))