• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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