All Downloads are FREE. Search and download functionalities are using the official Maven repository.

value.AvroFingerprint.kt Maven / Gradle / Ivy

package io.toolisticon.kotlin.avro.value

import _ktx.StringKtx.trimToNull
import io.toolisticon.kotlin.avro.value.SingleObjectEncodedBytes.Companion.verifyAvroHeader
import org.apache.avro.Protocol
import org.apache.avro.Schema
import org.apache.avro.SchemaNormalization
import org.apache.avro.message.BadHeaderException
import java.nio.ByteBuffer
import java.nio.ByteOrder

/**
 * The schema fingerprint as defined by [SchemaNormalization.parsingFingerprint64].
 *
 * In the Avro context, fingerprints of Parsing Canonical Form can be useful in a number of applications;
 * for example, to cache encoder and decoder objects, to tag data items with a short substitute for the
 * writer’s full schema, and to quickly negotiate common-case schemas between readers and writers.
 */
@JvmInline
value class AvroFingerprint(override val value: Long) : Comparable by value, ValueType {
  companion object {
    val NULL = AvroFingerprint(0L)

    fun parsingFingerprint64(schema: Schema) = SchemaNormalization.parsingFingerprint64(schema)
    fun fingerprint64(bytes: ByteArray) = SchemaNormalization.fingerprint64(bytes)

    fun Iterable.sum() = fold(NULL) { acc, avroFingerprint -> acc + avroFingerprint }
    fun Long.toBytes(): ByteArrayValue = ByteArrayValue(ByteBuffer.allocate(Long.SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN).putLong(this))
    fun ByteArrayValue.readLong(index: Int = 0): Long = buffer.order(ByteOrder.LITTLE_ENDIAN).getLong(index)

    fun ofNullable(string: String?): AvroFingerprint = string?.trimToNull()?.let {
      AvroFingerprint(fingerprint64(it.encodeToByteArray()))
    } ?: NULL


    fun of(field: Schema.Field) = of(field.schema())
    fun of(schema: Schema) = AvroFingerprint(parsingFingerprint64(schema))

    fun of(message: Protocol.Message) = listOf(
      ofNullable(message.name),
      of(message.errors),
      of(message.request),
      of(message.response)
    ).sum()

    /**
     * Extracts the writer-schema fingerprint from given [bytes].
     *
     * @param bytes assumed to be a valid avro single object encoded byte array
     * @throws BadHeaderException if bytes is not a valid single object encoded byte array
     */
    @Throws(BadHeaderException::class)
    fun of(bytes: ByteArrayValue) = AvroFingerprint(
      verifyAvroHeader(bytes).readLong(index = 2)
    )
  }

  operator fun plus(other: AvroFingerprint): AvroFingerprint = AvroFingerprint(this.value + other.value)
  operator fun plus(others: List): AvroFingerprint = (others + this).sum()

  val hex: HexString get() = byteValue.hex
  val byteValue: ByteArrayValue get() = value.toBytes()

  override fun toString() = hex.value
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy