Skip to content

Commit

Permalink
Allows the binary reader to avoid allocating PresenceBitmaps when a m…
Browse files Browse the repository at this point in the history
…acro signature has less than 5 required parameters.
  • Loading branch information
tgregg committed Jan 16, 2025
1 parent 16b3cd0 commit 96772b3
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 17 deletions.
12 changes: 5 additions & 7 deletions src/main/java/com/amazon/ion/impl/IonCursorBinary.java
Original file line number Diff line number Diff line change
Expand Up @@ -1910,18 +1910,16 @@ private boolean slowIsDelimitedEnd_1_1() {
* @return the presence bitmap.
*/
protected PresenceBitmap loadPresenceBitmap(List<Macro.Parameter> signature) {
// TODO performance: reuse or pool the presence bitmaps. Ideally, it would not be necessary to allocate them
// at all, but especially not when an invocation does not even have an argument encoding bitmap.
PresenceBitmap presenceBitmap = new PresenceBitmap();
presenceBitmap.initialize(signature);
// TODO performance: reuse or pool the presence bitmaps.
// Note: when there is no AEB, the following initializes the presence bitmap to "EXPRESSION" for each parameter.
PresenceBitmap presenceBitmap = PresenceBitmap.create(signature);
if (presenceBitmap.getByteSize() > 0) {
if (fillArgumentEncodingBitmap(presenceBitmap.getByteSize()) == IonCursor.Event.NEEDS_DATA) {
throw new UnsupportedOperationException("TODO: support continuable parsing of AEBs.");
}
presenceBitmap.readFrom(buffer, (int) valueMarker.startIndex);
presenceBitmap.validate();
}
// Note: when there is no AEB, the following initializes the presence bitmap to "EXPRESSION" for each parameter.
presenceBitmap.readFrom(buffer, (int) valueMarker.startIndex);
presenceBitmap.validate();
return presenceBitmap;
}

Expand Down
57 changes: 47 additions & 10 deletions src/main/java/com/amazon/ion/impl/bin/PresenceBitmap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ import com.amazon.ion.impl.macro.Macro.*
* TODO: Consider whether we can "compile" a specific function that can read the presence bits when we compile a macro.
* That _might_ be more efficient than this approach.
*/
internal class PresenceBitmap {
internal class PresenceBitmap(
var signature: List<Parameter>,
/** The number of parameters for which presence bits must be written. */
private var size: Int,
/** The total number of parameters in the macro signature */
var totalParameterCount: Int
) {

constructor() : this(emptyList(), 0, 0)

companion object {
const val VOID = 0b00L
Expand All @@ -61,17 +69,45 @@ internal class PresenceBitmap {
private const val PB_BITS_PER_SLOT = 2

const val MAX_SUPPORTED_PARAMETERS = PB_SLOTS_PER_LONG * 4
}

var signature: List<Parameter> = emptyList()
private set
private val ZERO_PARAMETERS: PresenceBitmap = allRequired(0)
private val ONE_REQUIRED_PARAMETER: PresenceBitmap = allRequired(1)
private val TWO_REQUIRED_PARAMETERS: PresenceBitmap = allRequired(2)
private val THREE_REQUIRED_PARAMETERS: PresenceBitmap = allRequired(3)
private val FOUR_REQUIRED_PARAMETERS: PresenceBitmap = allRequired(4)

/** The number of parameters for which presence bits must be written. */
private var size: Int = 0
/** Creates a PresenceBitmap for the given number of required parameters */
private fun allRequired(numberOfParameters: Int): PresenceBitmap {
if (numberOfParameters > MAX_SUPPORTED_PARAMETERS) throw IonException("Macros with more than 128 parameters are not supported by this implementation.")
val bitmap = PresenceBitmap(emptyList(), 0, numberOfParameters)
for (i in 0 until numberOfParameters) {
bitmap.setUnchecked(i, EXPRESSION)
}
return bitmap
}

/** The total number of parameters in the macro signature */
val totalParameterCount: Int
get() = signature.size
/** Creates or reuses a [PresenceBitmap] for the given signature. */
@JvmStatic
fun create(signature: List<Parameter>): PresenceBitmap {
if (signature.size > MAX_SUPPORTED_PARAMETERS) throw IonException("Macros with more than 128 parameters are not supported by this implementation.")
// Calculate the actual number of presence bits that will be encoded for the given signature.
val nonRequiredParametersCount = signature.count { it.cardinality != ParameterCardinality.ExactlyOne }
val usePresenceBits = nonRequiredParametersCount > PRESENCE_BITS_SIZE_THRESHOLD || signature.any { it.type.taglessEncodingKind != null }
val size = if (usePresenceBits) nonRequiredParametersCount else 0
return if (size > 0) {
PresenceBitmap(signature, nonRequiredParametersCount, signature.size)
} else {
when (signature.size) {
0 -> ZERO_PARAMETERS
1 -> ONE_REQUIRED_PARAMETER
2 -> TWO_REQUIRED_PARAMETERS
3 -> THREE_REQUIRED_PARAMETERS
4 -> FOUR_REQUIRED_PARAMETERS
else -> allRequired(signature.size)
}
}
}
}

/** The first 32 presence bits slots */
private var a: Long = 0
Expand All @@ -86,7 +122,7 @@ internal class PresenceBitmap {
val byteSize: Int
get() = size divideByRoundingUp PB_SLOTS_PER_BYTE

/** Resets this [PresenceBitmap] for the given [macro]. */
/** Resets this [PresenceBitmap] for the given signature. */
fun initialize(signature: List<Parameter>) {
if (signature.size > MAX_SUPPORTED_PARAMETERS) throw IonException("Macros with more than 128 parameters are not supported by this implementation.")
this.signature = signature
Expand All @@ -99,6 +135,7 @@ internal class PresenceBitmap {
val nonRequiredParametersCount = signature.count { it.cardinality != ParameterCardinality.ExactlyOne }
val usePresenceBits = nonRequiredParametersCount > PRESENCE_BITS_SIZE_THRESHOLD || signature.any { it.type.taglessEncodingKind != null }
size = if (usePresenceBits) nonRequiredParametersCount else 0
totalParameterCount = signature.size
}

/**
Expand Down

0 comments on commit 96772b3

Please sign in to comment.