@@ -17,86 +17,75 @@ internal object PrimitiveDecoder {
1717 private const val BYTE_BIT_MASK = 0xFF
1818 private const val BYTE_BIT_MASK_L = 0xFFL
1919 private const val INT_BIT_MASK = 0xFF_FF_FF_FFL
20+ private const val BYTE_BIT_MASK_UL = 0xFFuL
2021
2122 @JvmStatic
22- private fun ByteArray.getShort (position : Int ): Short {
23- return (
24- (this [position].toInt() and 0xFF ) or
25- ((this [position + 1 ].toInt() and 0xFF ) shl 8 )
26- ).toShort()
27- }
28- @JvmStatic
29- private fun ByteArray.getInt24 (position : Int ): Int {
30- return (this [position].toInt() and 0xFF ) or
31- ((this [position + 1 ].toInt() and 0xFF ) shl 8 ) or
32- // Shift left into 4th byte and then back down a byte here spreads the sign
33- // across high byte, which is needed for negatives
34- ((this [position + 2 ].toInt() and 0xFF ) shl 24 shr 8 )
23+ fun readFixedInt8AsShort (source : ByteArray , start : Int ): Short {
24+ // TODO: ion-java#1114
25+ if (source.size < start + 1 ) throw IonException (" Incomplete data: start=$start , length=1, limit=${source.size} " )
26+ return source[start].toShort()
3527 }
28+
3629 @JvmStatic
37- private fun ByteArray.getInt (position : Int ): Int {
38- return (this [position].toInt() and 0xFF ) or
39- ((this [position + 1 ].toInt() and 0xFF ) shl 8 ) or
40- ((this [position + 2 ].toInt() and 0xFF ) shl 16 ) or
41- ((this [position + 3 ].toInt() and 0xFF ) shl 24 )
30+ fun readFixedInt16 (source : ByteArray , start : Int ): Short {
31+ // TODO: ion-java#1114
32+ if (source.size < start + 2 ) throw IonException (" Incomplete data: start=$start , length=2, limit=${source.size} " )
33+ return read2IntBytes(source[start], source, start).shr(16 ).toShort()
4234 }
35+
4336 @JvmStatic
44- private fun ByteArray.getLong (position : Int ): Long {
45- return (this [position].toLong() and 0xFF ) or
46- ((this [position + 1 ].toLong() and 0xFF ) shl 8 ) or
47- ((this [position + 2 ].toLong() and 0xFF ) shl 16 ) or
48- ((this [position + 3 ].toLong() and 0xFF ) shl 24 ) or
49- ((this [position + 4 ].toLong() and 0xFF ) shl 32 ) or
50- ((this [position + 5 ].toLong() and 0xFF ) shl 40 ) or
51- ((this [position + 6 ].toLong() and 0xFF ) shl 48 ) or
52- ((this [position + 7 ].toLong() and 0xFF ) shl 56 )
37+ fun readFixedInt24AsInt (source : ByteArray , start : Int ): Int {
38+ // TODO: ion-java#1114
39+ if (source.size < start + 3 ) throw IonException (" Incomplete data: start=$start , length=3, limit=${source.size} " )
40+ return read3IntBytes(source[start], source, start).shr(8 )
5341 }
5442
5543 @JvmStatic
56- fun ByteArray. readFixedInt8AsShort ( start : Int ): Short {
44+ fun readFixedInt32 ( source : ByteArray , start : Int ): Int {
5745 // TODO: ion-java#1114
58- if (this .size < start + 1 ) throw IonException (" Incomplete data: start=$start , length=${ 1 } , limit=${this .size} " )
59- return this [start].toShort( )
46+ if (source .size < start + 4 ) throw IonException (" Incomplete data: start=$start , length=4 , limit=${source .size} " )
47+ return read4IntBytes(source [start], source, start )
6048 }
6149
6250 @JvmStatic
63- fun ByteArray. readFixedInt16AsShort ( start : Int ): Short {
51+ fun readFixedInt64 ( source : ByteArray , start : Int ): Long {
6452 // TODO: ion-java#1114
65- if (this .size < start + 2 ) throw IonException (" Incomplete data: start=$start , length=${ 2 } , limit=${this .size} " )
66- return this .getShort( start)
53+ if (source .size < start + 4 ) throw IonException (" Incomplete data: start=$start , length=4 , limit=${source .size} " )
54+ return read8IntBytes(source[start], source, start)
6755 }
6856
6957 @JvmStatic
70- fun ByteArray. readFixedInt24AsInt ( start : Int ) : Int {
58+ fun readFixedIntAsLong ( source : ByteArray , start : Int , length : Int ): Long {
7159 // TODO: ion-java#1114
72- if (this .size < start + 3 ) throw IonException (" Incomplete data: start=$start , length=${3 } , limit=${this .size} " )
73- return this .getInt24(start)
60+ if (source.size < start + length) throw IonException (" Incomplete data: start=$start , length=$length , limit=${source.size} " )
61+ val firstByte = source[start]
62+ return when (length) {
63+ 1 -> firstByte.toLong()
64+ 2 -> read2IntBytes(firstByte, source, start).shr(16 ).toLong()
65+ 3 -> read3IntBytes(firstByte, source, start).shr(8 ).toLong()
66+ 4 -> read4IntBytes(firstByte, source, start).toLong()
67+ 5 -> read5IntBytes(firstByte, source, start).shr(24 )
68+ 6 -> read6IntBytes(firstByte, source, start).shr(16 )
69+ 7 -> read7IntBytes(firstByte, source, start).shr(8 )
70+ 8 -> read8IntBytes(firstByte, source, start)
71+ // TODO: Technically, it's possible that the FixedInt is over-padded with 0-bytes, but we can deal with that later.
72+ else -> throw IonException (" FixedInt with length $length is too large to fit in a Long" )
73+ }
7474 }
7575
7676 @JvmStatic
77- fun ByteArray.readFixedInt32AsInt (start : Int ): Int {
78- // TODO: ion-java#1114
79- if (this .size < start + 4 ) throw IonException (" Incomplete data: start=$start , length=${4 } , limit=${this .size} " )
80- return this .getInt(start)
77+ fun readFixedUInt16 (source : ByteArray , position : Int ): UShort {
78+ return read2IntBytes(source[position], source, position).shr(16 ).toUShort()
8179 }
8280
8381 @JvmStatic
84- fun ByteArray.readFixedIntAsInt (start : Int , length : Int ): Int {
85- // TODO: ion-java#1114
86- if (this .size < start + length) throw IonException (" Incomplete data: start=$start , length=$length , limit=${this .size} " )
87- return (this .getInt(start - 4 + length) shr ((4 - length) * 8 ))
82+ fun readFixedUInt32 (source : ByteArray , position : Int ): UInt {
83+ return read4IntBytes(source[position], source, position).toUInt()
8884 }
8985
9086 @JvmStatic
91- fun ByteArray.readFixedIntAsLong (start : Int , length : Int ): Long {
92- // TODO: ion-java#1114
93- if (this .size < start + length) throw IonException (" Incomplete data: start=$start , length=$length , limit=${this .size} " )
94- if (length > 4 ) {
95- // TODO: See if we can simplify some of the calculations
96- return this .getLong(start - 8 + length) shr ((8 - length) * 8 )
97- } else {
98- return (this .getInt(start - 4 + length) shr ((4 - length) * 8 )).toLong()
99- }
87+ fun readFixedUInt64 (source : ByteArray , position : Int ): ULong {
88+ return read8IntBytes(source[position], source, position).toULong()
10089 }
10190
10291 // ==== FLEX INT AND UINT FUNCTIONS ==== //
@@ -253,6 +242,33 @@ internal object PrimitiveDecoder {
253242 return value.toLong().and (INT_BIT_MASK ) or numBytes.toLong().shl(Int .SIZE_BITS )
254243 }
255244
245+ /* *
246+ * Reads a FlexUInt as a ULong. Throws if value is too large for a ULong.
247+ */
248+ @JvmStatic
249+ fun readFlexUIntAsULong (source : ByteArray , position : Int ): ULong {
250+ val firstByte = source[position]
251+ val numBytes = firstByte.countTrailingZeroBits() + 1
252+ val value = when (numBytes) {
253+ 1 -> firstByte.toULong().and (BYTE_BIT_MASK_UL ).shr(1 )
254+ // These `shr` amounts are not arbitrary. See the `read*IntBytes` method documentation.
255+ 2 -> read2IntBytes(firstByte, source, position).ushr(18 ).toULong()
256+ 3 -> read3IntBytes(firstByte, source, position).ushr(11 ).toULong()
257+ 4 -> read4IntBytes(firstByte, source, position).ushr(4 ).toULong()
258+ 5 -> read5IntBytes(firstByte, source, position).ushr(29 ).toULong()
259+ 6 -> read6IntBytes(firstByte, source, position).ushr(22 ).toULong()
260+ 7 -> read7IntBytes(firstByte, source, position).ushr(15 ).toULong()
261+ 8 -> read8IntBytes(firstByte, source, position).ushr(8 ).toULong()
262+ else -> {
263+ val bigInt = readFlexUIntAsBigInteger(source, position)
264+ // bitLength() does not include a sign bit.
265+ if (bigInt.bitLength() > Long .SIZE_BITS ) throw IonException (" FlexInt value too large to find in a ULong" )
266+ bigInt.toLong().toULong()
267+ }
268+ }
269+ return value
270+ }
271+
256272 /* *
257273 * Does exactly what the function name says. This is slow. This method should rarely, if ever, be invoked.
258274 */
0 commit comments