main

square/leakcanary

Last updated at: 29/12/2023 09:40

FieldValuesReader.kt

TLDR

This file defines the FieldValuesReader class, which is responsible for reading field values from an InstanceDumpRecord object.

Methods

readValue

This method reads and returns the value of a specific field in the InstanceDumpRecord. It takes a FieldRecord object as a parameter and returns a ValueHolder object.

readId

This private method reads and returns an ID value. The length of the ID is determined by the identifierByteSize parameter.

readBoolean

This private method reads and returns a boolean value from the record.fieldValues array.

readByte

This private method reads and returns a byte value from the record.fieldValues array.

readInt

This private method reads and returns an integer value from the record.fieldValues array.

readShort

This private method reads and returns a short value from the record.fieldValues array.

readLong

This private method reads and returns a long value from the record.fieldValues array.

readFloat

This private method reads and returns a float value by combining four bytes from the record.fieldValues array.

readDouble

This private method reads and returns a double value by combining eight bytes from the record.fieldValues array.

readChar

This private method reads and returns a character value from the record.fieldValues array.

Class

FieldValuesReader

This class is responsible for reading field values from an InstanceDumpRecord object. It has a constructor that takes an InstanceDumpRecord object and an identifierByteSize value. The class provides the readValue method to read field values and a set of private methods to read specific types of values.

package shark.internal

import shark.HprofRecord.HeapDumpRecord.ObjectRecord.ClassDumpRecord.FieldRecord
import shark.HprofRecord.HeapDumpRecord.ObjectRecord.InstanceDumpRecord
import shark.PrimitiveType
import shark.PrimitiveType.BOOLEAN
import shark.PrimitiveType.BYTE
import shark.PrimitiveType.CHAR
import shark.PrimitiveType.DOUBLE
import shark.PrimitiveType.FLOAT
import shark.PrimitiveType.INT
import shark.PrimitiveType.LONG
import shark.PrimitiveType.SHORT
import shark.ValueHolder
import shark.ValueHolder.BooleanHolder
import shark.ValueHolder.ByteHolder
import shark.ValueHolder.CharHolder
import shark.ValueHolder.DoubleHolder
import shark.ValueHolder.FloatHolder
import shark.ValueHolder.IntHolder
import shark.ValueHolder.LongHolder
import shark.ValueHolder.ReferenceHolder
import shark.ValueHolder.ShortHolder

internal class FieldValuesReader(
  private val record: InstanceDumpRecord,
  private val identifierByteSize: Int
) {

  private var position = 0

  fun readValue(field: FieldRecord): ValueHolder {
    return when (field.type) {
      PrimitiveType.REFERENCE_HPROF_TYPE -> ReferenceHolder(readId())
      BOOLEAN_TYPE -> BooleanHolder(readBoolean())
      CHAR_TYPE -> CharHolder(readChar())
      FLOAT_TYPE -> FloatHolder(readFloat())
      DOUBLE_TYPE -> DoubleHolder(readDouble())
      BYTE_TYPE -> ByteHolder(readByte())
      SHORT_TYPE -> ShortHolder(readShort())
      INT_TYPE -> IntHolder(readInt())
      LONG_TYPE -> LongHolder(readLong())
      else -> throw IllegalStateException("Unknown type ${field.type}")
    }
  }

  private fun readId(): Long {
    // As long as we don't interpret IDs, reading signed values here is fine.
    return when (identifierByteSize) {
      1 -> readByte().toLong()
      2 -> readShort().toLong()
      4 -> readInt().toLong()
      8 -> readLong()
      else -> throw IllegalArgumentException("ID Length must be 1, 2, 4, or 8")
    }
  }

  private fun readBoolean(): Boolean {
    val value = record.fieldValues[position]
    position++
    return value != 0.toByte()
  }

  private fun readByte(): Byte {
    val value = record.fieldValues[position]
    position++
    return value
  }

  private fun readInt(): Int {
    val value = record.fieldValues.readInt(position)
    position += 4
    return value
  }

  private fun readShort(): Short {
    val value = record.fieldValues.readShort(position)
    position += 2
    return value
  }

  private fun readLong(): Long {
    val value = record.fieldValues.readLong(position)
    position += 8
    return value
  }

  private fun readFloat(): Float {
    return Float.fromBits(readInt())
  }

  private fun readDouble(): Double {
    return Double.fromBits(readLong())
  }

  private fun readChar(): Char {
    val string = String(record.fieldValues, position, 2, Charsets.UTF_16BE)
    position += 2
    return string[0]
  }

  companion object {
    private val BOOLEAN_TYPE = BOOLEAN.hprofType
    private val CHAR_TYPE = CHAR.hprofType
    private val FLOAT_TYPE = FLOAT.hprofType
    private val DOUBLE_TYPE = DOUBLE.hprofType
    private val BYTE_TYPE = BYTE.hprofType
    private val SHORT_TYPE = SHORT.hprofType
    private val INT_TYPE = INT.hprofType
    private val LONG_TYPE = LONG.hprofType
  }
}