1 /* 2 * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3 */ 4 5 package kotlinx.serialization.protobuf.internal 6 7 import kotlinx.serialization.* 8 9 internal class ByteArrayInput(private var array: ByteArray, private val endIndex: Int = array.size) { 10 private var position: Int = 0 11 val availableBytes: Int get() = endIndex - position 12 slicenull13 fun slice(size: Int): ByteArrayInput { 14 ensureEnoughBytes(size) 15 val result = ByteArrayInput(array, position + size) 16 result.position = position 17 position += size 18 return result 19 } 20 readnull21 fun read(): Int { 22 return if (position < endIndex) array[position++].toInt() and 0xFF else -1 23 } 24 readExactNBytesnull25 fun readExactNBytes(bytesCount: Int): ByteArray { 26 ensureEnoughBytes(bytesCount) 27 val b = ByteArray(bytesCount) 28 val length = b.size 29 // Are there any bytes available? 30 val copied = if (endIndex - position < length) endIndex - position else length 31 array.copyInto(destination = b, destinationOffset = 0, startIndex = position, endIndex = position + copied) 32 position += copied 33 return b 34 } 35 36 skipExactNBytesnull37 fun skipExactNBytes(bytesCount: Int) { 38 ensureEnoughBytes(bytesCount) 39 position += bytesCount 40 } 41 ensureEnoughBytesnull42 private fun ensureEnoughBytes(bytesCount: Int) { 43 if (bytesCount > availableBytes) { 44 throw SerializationException("Unexpected EOF, available $availableBytes bytes, requested: $bytesCount") 45 } 46 } 47 readStringnull48 fun readString(length: Int): String { 49 val result = array.decodeToString(position, position + length) 50 position += length 51 return result 52 } 53 readVarint32null54 fun readVarint32(): Int { 55 if (position == endIndex) { 56 eof() 57 } 58 59 // Fast-path: unrolled loop for single and two byte values 60 var currentPosition = position 61 var result = array[currentPosition++].toInt() 62 if (result >= 0) { 63 position = currentPosition 64 return result 65 } else if (endIndex - position > 1) { 66 result = result xor (array[currentPosition++].toInt() shl 7) 67 if (result < 0) { 68 position = currentPosition 69 return result xor (0.inv() shl 7) 70 } 71 } 72 73 return readVarint32SlowPath() 74 } 75 readVarint64null76 fun readVarint64(eofAllowed: Boolean): Long { 77 if (position == endIndex) { 78 if (eofAllowed) return -1 79 else eof() 80 } 81 82 // Fast-path: single and two byte values 83 var currentPosition = position 84 var result = array[currentPosition++].toLong() 85 if (result >= 0) { 86 position = currentPosition 87 return result 88 } else if (endIndex - position > 1) { 89 result = result xor (array[currentPosition++].toLong() shl 7) 90 if (result < 0) { 91 position = currentPosition 92 return result xor (0L.inv() shl 7) 93 } 94 } 95 96 return readVarint64SlowPath() 97 } 98 eofnull99 private fun eof() { 100 throw SerializationException("Unexpected EOF") 101 } 102 readVarint64SlowPathnull103 private fun readVarint64SlowPath(): Long { 104 var result = 0L 105 var shift = 0 106 while (shift < 64) { 107 val byte = read() 108 result = result or ((byte and 0x7F).toLong() shl shift) 109 if (byte and 0x80 == 0) { 110 return result 111 } 112 shift += 7 113 } 114 throw SerializationException("Input stream is malformed: Varint too long (exceeded 64 bits)") 115 } 116 readVarint32SlowPathnull117 private fun readVarint32SlowPath(): Int { 118 var result = 0 119 var shift = 0 120 while (shift < 32) { 121 val byte = read() 122 result = result or ((byte and 0x7F) shl shift) 123 if (byte and 0x80 == 0) { 124 return result 125 } 126 shift += 7 127 } 128 throw SerializationException("Input stream is malformed: Varint too long (exceeded 32 bits)") 129 } 130 } 131 132 internal class ByteArrayOutput { 133 134 private companion object { 135 /* 136 * Map number of leading zeroes -> varint size 137 * See the explanation in this blogpost: https://richardstartin.github.io/posts/dont-use-protobuf-for-telemetry 138 */ <lambda>null139 private val VAR_INT_LENGTHS = IntArray(65) { 140 (63 - it) / 7 141 } 142 } 143 144 private var array: ByteArray = ByteArray(32) 145 private var position: Int = 0 146 ensureCapacitynull147 private fun ensureCapacity(elementsToAppend: Int) { 148 if (position + elementsToAppend <= array.size) { 149 return 150 } 151 val newArray = ByteArray((position + elementsToAppend).takeHighestOneBit() shl 1) 152 array.copyInto(newArray) 153 array = newArray 154 } 155 sizenull156 fun size(): Int { 157 return position 158 } 159 toByteArraynull160 fun toByteArray(): ByteArray { 161 val newArray = ByteArray(position) 162 array.copyInto(newArray, startIndex = 0, endIndex = this.position) 163 return newArray 164 } 165 writenull166 fun write(buffer: ByteArray) { 167 val count = buffer.size 168 if (count == 0) { 169 return 170 } 171 172 ensureCapacity(count) 173 buffer.copyInto( 174 destination = array, 175 destinationOffset = this.position, 176 startIndex = 0, 177 endIndex = count 178 ) 179 this.position += count 180 } 181 writenull182 fun write(output: ByteArrayOutput) { 183 val count = output.size() 184 ensureCapacity(count) 185 output.array.copyInto( 186 destination = array, 187 destinationOffset = this.position, 188 startIndex = 0, 189 endIndex = count 190 ) 191 this.position += count 192 } 193 writeIntnull194 fun writeInt(intValue: Int) { 195 ensureCapacity(4) 196 for (i in 3 downTo 0) { 197 array[position++] = (intValue shr i * 8).toByte() 198 } 199 } 200 writeLongnull201 fun writeLong(longValue: Long) { 202 ensureCapacity(8) 203 for (i in 7 downTo 0) { 204 array[position++] = (longValue shr i * 8).toByte() 205 } 206 } 207 encodeVarint32null208 fun encodeVarint32(value: Int) { 209 // Fast-path: unrolled loop for single byte 210 ensureCapacity(5) 211 if (value and 0x7F.inv() == 0) { 212 array[position++] = value.toByte() 213 return 214 } 215 val length = varIntLength(value.toLong()) 216 encodeVarint(value.toLong(), length) 217 } 218 encodeVarint64null219 fun encodeVarint64(value: Long) { 220 val length = varIntLength(value) 221 ensureCapacity(length + 1) 222 encodeVarint(value, length) 223 } 224 encodeVarintnull225 private fun encodeVarint(value: Long, length: Int) { 226 var current = value 227 for (i in 0 until length) { 228 array[position + i] = ((current and 0x7F) or 0x80).toByte() 229 current = current ushr 7 230 } 231 array[position + length] = current.toByte() 232 position += length + 1 233 } 234 varIntLengthnull235 private fun varIntLength(value: Long): Int { 236 return VAR_INT_LENGTHS[value.countLeadingZeroBits()] 237 } 238 } 239