Skip to content

Commit 532d49c

Browse files
committed
Performance improvements
1 parent 1069917 commit 532d49c

File tree

4 files changed

+92
-66
lines changed

4 files changed

+92
-66
lines changed

core/common/src/internal/format/formatter/FormatterOperation.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ internal class UnsignedIntFormatterStructure<in T>(
2828
}
2929

3030
override fun format(obj: T, builder: Appendable, minusNotRequired: Boolean) {
31-
val numberStr = number(obj).toString()
32-
val zeroPaddingStr = '0'.toString().repeat(maxOf(0, zeroPadding - numberStr.length))
33-
builder.append(zeroPaddingStr, numberStr)
31+
val num = number(obj)
32+
val numberStr = num.toString()
33+
repeat(zeroPadding - numberStr.length) { builder.append('0') }
34+
builder.append(numberStr)
3435
}
3536
}
3637

core/common/src/internal/format/parser/NumberConsumer.kt

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,28 @@ internal sealed class NumberConsumer<in Receiver>(
2222
* if [length] is `null`, with a string consisting of any number of digits. [consume] itself does not
2323
* necessarily check the length of the input string, instead expecting to be passed a valid one.
2424
*
25-
* @throws NumberFormatException if the given [input] is too large a number.
25+
* Returns `null` on success and a `NumberConsumptionError` on failure.
2626
*/
27-
abstract fun Receiver.consume(input: String)
27+
abstract fun Receiver.consume(input: String): NumberConsumptionError?
28+
}
29+
30+
internal sealed interface NumberConsumptionError {
31+
fun errorMessage(): String
32+
object ExpectedInt: NumberConsumptionError {
33+
override fun errorMessage() = "expected an Int value"
34+
}
35+
object ExpectedLong: NumberConsumptionError {
36+
override fun errorMessage() = "expected a Long value"
37+
}
38+
class TooManyDigits(val maxDigits: Int): NumberConsumptionError {
39+
override fun errorMessage() = "expected at most $maxDigits digits"
40+
}
41+
class TooFewDigits(val minDigits: Int): NumberConsumptionError {
42+
override fun errorMessage() = "expected at least $minDigits digits"
43+
}
44+
class WrongConstant(val expected: String): NumberConsumptionError {
45+
override fun errorMessage() = "expected '$expected'"
46+
}
2847
}
2948

3049
/**
@@ -43,20 +62,23 @@ internal class UnsignedIntConsumer<in Receiver>(
4362
require(length == null || length in 1..9) { "Invalid length for field $whatThisExpects: $length" }
4463
}
4564

46-
override fun Receiver.consume(input: String) {
65+
override fun Receiver.consume(input: String): NumberConsumptionError? {
4766
if (maxLength != null) {
4867
if (input.length > maxLength) {
49-
throw NumberFormatException("Expected at most $maxLength digits for $whatThisExpects but got $input")
68+
return NumberConsumptionError.TooManyDigits(maxLength)
5069
}
5170
}
5271
if (minLength != null) {
5372
if (input.length < minLength) {
54-
throw NumberFormatException("Expected at least $minLength digits for $whatThisExpects but got $input")
73+
return NumberConsumptionError.TooFewDigits(minLength)
5574
}
5675
}
57-
when (val result = input.toIntOrNull()) {
58-
null -> throw NumberFormatException("Expected an Int value for $whatThisExpects but got $input")
59-
else -> setter(this, if (multiplyByMinus1) -result else result)
76+
return when (val result = input.toIntOrNull()) {
77+
null -> NumberConsumptionError.ExpectedInt
78+
else -> {
79+
setter(this, if (multiplyByMinus1) -result else result)
80+
null
81+
}
6082
}
6183
}
6284
}
@@ -72,16 +94,15 @@ internal class ReducedIntConsumer<in Receiver>(
7294
private val baseMod = base % modulo
7395
private val baseFloor = base - baseMod
7496

75-
override fun Receiver.consume(input: String) {
76-
when (val result = input.toIntOrNull()) {
77-
null -> throw NumberFormatException("Expected an Int value for $whatThisExpects but got $input")
78-
else -> {
79-
setter(this, if (result >= baseMod) {
80-
baseFloor + result
81-
} else {
82-
baseFloor + modulo + result
83-
})
84-
}
97+
override fun Receiver.consume(input: String): NumberConsumptionError? = when (val result = input.toIntOrNull()) {
98+
null -> NumberConsumptionError.ExpectedInt
99+
else -> {
100+
setter(this, if (result >= baseMod) {
101+
baseFloor + result
102+
} else {
103+
baseFloor + modulo + result
104+
})
105+
null
85106
}
86107
}
87108
}
@@ -92,8 +113,10 @@ internal class ReducedIntConsumer<in Receiver>(
92113
internal class ConstantNumberConsumer<in Receiver>(
93114
private val expected: String
94115
) : NumberConsumer<Receiver>(expected.length, "the predefined string $expected") {
95-
override fun Receiver.consume(input: String) {
96-
require(input == expected) { "Expected '$expected' but got $input" }
116+
override fun Receiver.consume(input: String): NumberConsumptionError? = if (input == expected) {
117+
NumberConsumptionError.WrongConstant(expected)
118+
} else {
119+
null
97120
}
98121
}
99122

@@ -111,8 +134,11 @@ internal class UnsignedLongConsumer<in Receiver>(
111134
}
112135

113136
override fun Receiver.consume(input: String) = when (val result = input.toLongOrNull()) {
114-
null -> throw NumberFormatException("Expected a Long value for $whatThisExpects but got $input")
115-
else -> setter(this, result)
137+
null -> NumberConsumptionError.ExpectedLong
138+
else -> {
139+
setter(this, result)
140+
null
141+
}
116142
}
117143
}
118144

@@ -127,14 +153,17 @@ internal class FractionPartConsumer<in Receiver>(
127153
// TODO: bounds on maxLength
128154
}
129155

130-
override fun Receiver.consume(input: String) {
131-
if (minLength != null && input.length < minLength)
132-
throw NumberFormatException("Expected at least $minLength digits for $whatThisExpects but got $input")
133-
if (maxLength != null && input.length > maxLength)
134-
throw NumberFormatException("Expected at most $maxLength digits for $whatThisExpects but got $input")
135-
when (val numerator = input.toIntOrNull()) {
136-
null -> throw NumberFormatException("Expected at most a 9-digit value for $whatThisExpects but got $input")
137-
else -> setter(this, DecimalFraction(numerator, input.length))
156+
override fun Receiver.consume(input: String): NumberConsumptionError? = when {
157+
minLength != null && input.length < minLength ->
158+
NumberConsumptionError.TooFewDigits(minLength)
159+
maxLength != null && input.length > maxLength ->
160+
NumberConsumptionError.TooManyDigits(maxLength)
161+
else -> when (val numerator = input.toIntOrNull()) {
162+
null -> NumberConsumptionError.TooManyDigits(9)
163+
else -> {
164+
setter(this, DecimalFraction(numerator, input.length))
165+
null
166+
}
138167
}
139168
}
140169
}

core/common/src/internal/format/parser/Parser.kt

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -138,34 +138,35 @@ internal class Parser<Output>(
138138
onError: (ParseError) -> Unit,
139139
onSuccess: (Int, Output) -> Unit
140140
) {
141-
var states = mutableListOf(ParserState(defaultState(), StructureIndex(0, commands), startIndex))
141+
var states = mutableListOf(ParserState(defaultState(), 0, commands, startIndex))
142142
while (states.isNotEmpty()) {
143-
states = states.flatMap { state ->
144-
val index = state.commandPosition
145-
if (index.operationIndex < index.parserStructure.operations.size) {
146-
val newIndex = StructureIndex(index.operationIndex + 1, index.parserStructure)
147-
val command = state.commandPosition.parserStructure.operations[state.commandPosition.operationIndex]
143+
val newStates = mutableListOf<ParserState<Output>>()
144+
for (state in states) {
145+
if (state.operationIndex < state.parserStructure.operations.size) {
146+
val command = state.parserStructure.operations[state.operationIndex]
148147
val result = with(command) { state.output.consume(input, state.inputPosition) }
149148
if (result.isOk()) {
150-
listOf(ParserState(state.output, newIndex, result.tryGetIndex()!!))
149+
newStates.add(
150+
ParserState(state.output, state.operationIndex + 1, state.parserStructure, result.tryGetIndex()!!)
151+
)
151152
} else {
152153
onError(result.tryGetError()!!)
153-
emptyList()
154154
}
155155
} else {
156-
index.parserStructure.followedBy.map { nextStructure ->
157-
ParserState(copyState(state.output), StructureIndex(0, nextStructure), state.inputPosition)
158-
}.also {
159-
if (it.isEmpty()) {
160-
if (allowDanglingInput || state.inputPosition == input.length) {
161-
onSuccess(state.inputPosition, state.output)
162-
} else {
163-
onError(ParseError(state.inputPosition) { "There is more input to consume" })
164-
}
156+
if (state.parserStructure.followedBy.isEmpty()) {
157+
if (allowDanglingInput || state.inputPosition == input.length) {
158+
onSuccess(state.inputPosition, state.output)
159+
} else {
160+
onError(ParseError(state.inputPosition) { "There is more input to consume" })
161+
}
162+
} else {
163+
for (nextStructure in state.parserStructure.followedBy) {
164+
newStates.add(ParserState(copyState(state.output), 0, nextStructure, state.inputPosition))
165165
}
166166
}
167167
}
168-
}.toMutableList()
168+
}
169+
states = newStates
169170
}
170171
}
171172

@@ -185,12 +186,8 @@ internal class Parser<Output>(
185186

186187
private inner class ParserState<Output>(
187188
val output: Output,
188-
val commandPosition: StructureIndex<Output>,
189-
val inputPosition: Int,
190-
)
191-
192-
private class StructureIndex<in Output>(
193189
val operationIndex: Int,
194-
val parserStructure: ParserStructure<Output>
190+
val parserStructure: ParserStructure<Output>,
191+
val inputPosition: Int,
195192
)
196193
}

core/common/src/internal/format/parser/ParserOperation.kt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,17 @@ internal class NumberSpanParserOperation<Output>(
7979
return ParseResult.Error(startIndex) {
8080
"Only found $digitsInRow digits in a row, but need to parse $whatThisExpects"
8181
}
82-
val lengths = consumers.map { it.length ?: (digitsInRow - minLength + 1) }
8382
var index = startIndex
84-
for (i in lengths.indices) {
85-
val numberString = input.substring(index, index + lengths[i])
86-
try {
87-
with(consumers[i]) { consume(numberString) }
88-
} catch (e: Throwable) {
89-
return ParseResult.Error(index, e) {
90-
"Can not interpret the string '$numberString' as ${consumers[i].whatThisExpects}"
83+
for (i in consumers.indices) {
84+
val length = consumers[i].length ?: (digitsInRow - minLength + 1)
85+
val numberString = input.substring(index, index + length)
86+
val error = with(consumers[i]) { consume(numberString) }
87+
if (error != null) {
88+
return ParseResult.Error(index) {
89+
"Can not interpret the string '$numberString' as ${consumers[i].whatThisExpects}: ${error.errorMessage()}"
9190
}
9291
}
93-
index += lengths[i]
92+
index += length
9493
}
9594
return ParseResult.Ok(index)
9695
}

0 commit comments

Comments
 (0)