diff --git a/nise-backend/src/main/kotlin/com/nisemoe/nise/service/CompressJudgements.kt b/nise-backend/src/main/kotlin/com/nisemoe/nise/service/CompressJudgements.kt index 2ffad37..a6dac70 100644 --- a/nise-backend/src/main/kotlin/com/nisemoe/nise/service/CompressJudgements.kt +++ b/nise-backend/src/main/kotlin/com/nisemoe/nise/service/CompressJudgements.kt @@ -5,10 +5,10 @@ import com.aayushatharva.brotli4j.decoder.Decoder import com.aayushatharva.brotli4j.encoder.Encoder import com.nisemoe.nise.integrations.CircleguardService import org.springframework.stereotype.Service +import java.io.ByteArrayOutputStream import java.nio.ByteBuffer import kotlin.math.round - @Service class CompressJudgements { @@ -19,25 +19,55 @@ class CompressJudgements { Brotli4jLoader.ensureAvailability() } + fun ByteBuffer.putVLQ(value: Int) { + var currentValue = value + do { + var temp = (currentValue and 0x7F) + currentValue = currentValue ushr 7 + if (currentValue != 0) { + temp = temp or 0x80 + } + this.put(temp.toByte()) + } while (currentValue != 0) + } + + fun ByteBuffer.getVLQ(): Int { + var result = 0 + var shift = 0 + var b: Byte + do { + b = this.get() + result = result or ((b.toInt() and 0x7F) shl shift) + shift += 7 + } while (b.toInt() and 0x80 != 0) + return result + } + fun serialize(judgements: List): ByteArray { - val buffer = ByteBuffer.allocate(judgements.size * (2 + 4 + 4 + 1 + 2 + 2 + 2)) - var lastTime = 0.0 + val byteStream = ByteArrayOutputStream() + var lastTimestamp = 0.0 judgements.forEach { judgement -> - val deltaTime = (judgement.time - lastTime).toInt() - buffer.putShort(deltaTime.toShort()) + byteStream.use { stream -> + /** + * We allocate an arbitrary amount of buffer which *hopefully* is enough. + */ + ByteBuffer.allocate(4096).let { buffer -> + buffer.putVLQ((judgement.time - lastTimestamp).toInt()) + buffer.putVLQ(round(judgement.x * 100).toInt()) + buffer.putVLQ(round(judgement.y * 100).toInt()) + buffer.put(judgement.type.ordinal.toByte()) + buffer.putVLQ((judgement.distance_center * 100).toInt()) + buffer.putVLQ((judgement.distance_edge * 100).toInt()) + buffer.putVLQ(judgement.error.toInt()) - buffer.putInt((round(judgement.x * 1000)).toInt()) - buffer.putInt((round(judgement.y * 1000)).toInt()) - - buffer.put(judgement.type.ordinal.toByte()) - buffer.putShort((judgement.distance_center * 100).toInt().toShort()) - buffer.putShort((judgement.distance_edge * 100).toInt().toShort()) - buffer.putShort(judgement.error.toInt().toShort()) - - lastTime = judgement.time + lastTimestamp = judgement.time + stream.write(buffer.array(), 0, buffer.position()) + } + } } - return Encoder.compress(buffer.array(), brotliParameters) + + return Encoder.compress(byteStream.toByteArray(), brotliParameters) } fun deserialize(compressedData: ByteArray): List { @@ -47,28 +77,18 @@ class CompressJudgements { var lastTime = 0.0 while (buffer.hasRemaining()) { - val deltaTime = buffer.short.toInt() + val deltaTime = buffer.getVLQ() lastTime += deltaTime - val deltaX = buffer.getInt() - val deltaY = buffer.getInt() - - val typeOrdinal = buffer.get().toInt() - val type = CircleguardService.JudgementType.entries[typeOrdinal] - - val distanceCenter = buffer.short.toInt() / 100.0 - val distanceEdge = buffer.short.toInt() / 100.0 - val error = buffer.short.toInt() - judgements.add( CircleguardService.ScoreJudgement( time = lastTime, - x = deltaX / 1000.0, - y = deltaY / 1000.0, - type = type, - distance_center = distanceCenter, - distance_edge = distanceEdge, - error = error.toDouble() + x = buffer.getVLQ() / 100.0, + y = buffer.getVLQ() / 100.0, + type = CircleguardService.JudgementType.entries[buffer.get().toInt()], + distance_center = buffer.getVLQ() / 100.0, + distance_edge = buffer.getVLQ() / 100.0, + error = buffer.getVLQ().toDouble() )) }