Skip to content

Commit 8d925c6

Browse files
authored
Adds opcode handlers for Tagless UInt values (#1130)
1 parent 7b6936e commit 8d925c6

10 files changed

Lines changed: 611 additions & 422 deletions

File tree

‎src/main/java/com/amazon/ion/bytecode/NumericReader.kt‎

Lines changed: 0 additions & 85 deletions
This file was deleted.

‎src/main/java/com/amazon/ion/bytecode/bin11/bytearray/FloatOpcodeHandlers.kt‎

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
package com.amazon.ion.bytecode.bin11.bytearray
44

55
import com.amazon.ion.bytecode.BytecodeEmitter
6-
import com.amazon.ion.bytecode.NumericReader.readDouble
7-
import com.amazon.ion.bytecode.NumericReader.readFloat
8-
import com.amazon.ion.bytecode.NumericReader.readShort
96
import com.amazon.ion.bytecode.util.AppendableConstantPoolView
107
import com.amazon.ion.bytecode.util.BytecodeBuffer
118
import com.amazon.ion.bytecode.util.asHalfToFloat
@@ -24,10 +21,7 @@ internal object Float0OpcodeHandler : OpcodeToBytecodeHandler {
2421
macroIndices: IntArray,
2522
symbolTable: Array<String?>
2623
): Int {
27-
BytecodeEmitter.emitFloatValue(
28-
destination,
29-
0f
30-
)
24+
BytecodeEmitter.emitFloatValue(destination, 0f)
3125
return 0
3226
}
3327
}
@@ -46,10 +40,8 @@ internal object Float16OpcodeHandler : OpcodeToBytecodeHandler {
4640
macroIndices: IntArray,
4741
symbolTable: Array<String?>
4842
): Int {
49-
BytecodeEmitter.emitFloatValue(
50-
destination,
51-
source.readShort(position).asHalfToFloat()
52-
)
43+
val floatValue = PrimitiveDecoder.readFixedInt16(source, position).asHalfToFloat()
44+
BytecodeEmitter.emitFloatValue(destination, floatValue)
5345
return 2
5446
}
5547
}
@@ -68,10 +60,8 @@ internal object Float32OpcodeHandler : OpcodeToBytecodeHandler {
6860
macroIndices: IntArray,
6961
symbolTable: Array<String?>
7062
): Int {
71-
BytecodeEmitter.emitFloatValue(
72-
destination,
73-
source.readFloat(position)
74-
)
63+
val floatValue = Float.fromBits(PrimitiveDecoder.readFixedInt32(source, position))
64+
BytecodeEmitter.emitFloatValue(destination, floatValue)
7565
return 4
7666
}
7767
}
@@ -90,10 +80,8 @@ internal object DoubleOpcodeHandler : OpcodeToBytecodeHandler {
9080
macroIndices: IntArray,
9181
symbolTable: Array<String?>
9282
): Int {
93-
BytecodeEmitter.emitDoubleValue(
94-
destination,
95-
source.readDouble(position)
96-
)
83+
val doubleValue = Double.fromBits(PrimitiveDecoder.readFixedInt64(source, position))
84+
BytecodeEmitter.emitDoubleValue(destination, doubleValue)
9785
return 8
9886
}
9987
}

‎src/main/java/com/amazon/ion/bytecode/bin11/bytearray/IntOpcodeHandlers.kt‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
package com.amazon.ion.bytecode.bin11.bytearray
44

55
import com.amazon.ion.bytecode.BytecodeEmitter
6-
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedInt16AsShort
6+
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedInt16
77
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedInt24AsInt
8-
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedInt32AsInt
8+
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedInt32
99
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedInt8AsShort
1010
import com.amazon.ion.bytecode.bin11.bytearray.PrimitiveDecoder.readFixedIntAsLong
1111
import com.amazon.ion.bytecode.util.AppendableConstantPoolView
@@ -49,7 +49,7 @@ internal object Int8OpcodeHandler : OpcodeToBytecodeHandler {
4949
): Int {
5050
BytecodeEmitter.emitInt16Value(
5151
destination,
52-
source.readFixedInt8AsShort(position)
52+
readFixedInt8AsShort(source, position)
5353
)
5454
return 1
5555
}
@@ -71,7 +71,7 @@ internal object Int16OpcodeHandler : OpcodeToBytecodeHandler {
7171
): Int {
7272
BytecodeEmitter.emitInt16Value(
7373
destination,
74-
source.readFixedInt16AsShort(position)
74+
readFixedInt16(source, position)
7575
)
7676
return 2
7777
}
@@ -93,7 +93,7 @@ internal object Int24OpcodeHandler : OpcodeToBytecodeHandler {
9393
): Int {
9494
BytecodeEmitter.emitInt32Value(
9595
destination,
96-
source.readFixedInt24AsInt(position)
96+
readFixedInt24AsInt(source, position)
9797
)
9898
return 3
9999
}
@@ -115,7 +115,7 @@ internal object Int32OpcodeHandler : OpcodeToBytecodeHandler {
115115
): Int {
116116
BytecodeEmitter.emitInt32Value(
117117
destination,
118-
source.readFixedInt32AsInt(position)
118+
readFixedInt32(source, position)
119119
)
120120
return 4
121121
}
@@ -138,7 +138,7 @@ internal object LongIntOpcodeHandler : OpcodeToBytecodeHandler {
138138
val fixedIntLength = opcode and 0xF
139139
BytecodeEmitter.emitInt64Value(
140140
destination,
141-
source.readFixedIntAsLong(position, fixedIntLength)
141+
readFixedIntAsLong(source, position, fixedIntLength)
142142
)
143143
return fixedIntLength
144144
}

‎src/main/java/com/amazon/ion/bytecode/bin11/bytearray/PrimitiveDecoder.kt‎

Lines changed: 72 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,86 +17,81 @@ 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 + 8) throw IonException("Incomplete data: start=$start, length=8, 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 {
77+
fun readFixedUInt16(source: ByteArray, position: Int): UShort {
7878
// 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)
79+
if (source.size < position + 2) throw IonException("Incomplete data: start=$position, length=2, limit=${source.size}")
80+
return read2IntBytes(source[position], source, position).shr(16).toUShort()
8181
}
8282

8383
@JvmStatic
84-
fun ByteArray.readFixedIntAsInt(start: Int, length: Int): Int {
84+
fun readFixedUInt32(source: ByteArray, position: Int): UInt {
8585
// 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))
86+
if (source.size < position + 4) throw IonException("Incomplete data: start=$position, length=4, limit=${source.size}")
87+
return read4IntBytes(source[position], source, position).toUInt()
8888
}
8989

9090
@JvmStatic
91-
fun ByteArray.readFixedIntAsLong(start: Int, length: Int): Long {
91+
fun readFixedUInt64(source: ByteArray, position: Int): ULong {
9292
// 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-
}
93+
if (source.size < position + 8) throw IonException("Incomplete data: start=$position, length=8, limit=${source.size}")
94+
return read8IntBytes(source[position], source, position).toULong()
10095
}
10196

10297
// ==== FLEX INT AND UINT FUNCTIONS ==== //
@@ -253,6 +248,33 @@ internal object PrimitiveDecoder {
253248
return value.toLong().and(INT_BIT_MASK) or numBytes.toLong().shl(Int.SIZE_BITS)
254249
}
255250

251+
/**
252+
* Reads a FlexUInt as a ULong. Throws if value is too large for a ULong.
253+
*/
254+
@JvmStatic
255+
fun readFlexUIntAsULong(source: ByteArray, position: Int): ULong {
256+
val firstByte = source[position]
257+
val numBytes = firstByte.countTrailingZeroBits() + 1
258+
val value = when (numBytes) {
259+
1 -> firstByte.toULong().and(BYTE_BIT_MASK_UL).shr(1)
260+
// These `shr` amounts are not arbitrary. See the `read*IntBytes` method documentation.
261+
2 -> read2IntBytes(firstByte, source, position).ushr(18).toULong()
262+
3 -> read3IntBytes(firstByte, source, position).ushr(11).toULong()
263+
4 -> read4IntBytes(firstByte, source, position).ushr(4).toULong()
264+
5 -> read5IntBytes(firstByte, source, position).ushr(29).toULong()
265+
6 -> read6IntBytes(firstByte, source, position).ushr(22).toULong()
266+
7 -> read7IntBytes(firstByte, source, position).ushr(15).toULong()
267+
8 -> read8IntBytes(firstByte, source, position).ushr(8).toULong()
268+
else -> {
269+
val bigInt = readFlexUIntAsBigInteger(source, position)
270+
// bitLength() does not include a sign bit.
271+
if (bigInt.bitLength() > Long.SIZE_BITS) throw IonException("FlexInt value too large to find in a ULong")
272+
bigInt.toLong().toULong()
273+
}
274+
}
275+
return value
276+
}
277+
256278
/**
257279
* Does exactly what the function name says. This is slow. This method should rarely, if ever, be invoked.
258280
*/

0 commit comments

Comments
 (0)