1 package shark 2 3 import okio.Buffer 4 import java.io.Closeable 5 import java.io.File 6 7 /** 8 * Reads records in a Hprof source, one at a time with a specific position and size. 9 * Call [openReaderFor] to obtain a new instance. 10 */ 11 class RandomAccessHprofReader private constructor( 12 private val source: RandomAccessSource, 13 hprofHeader: HprofHeader 14 ) : Closeable { 15 private val buffer = Buffer() 16 private val reader = HprofRecordReader(hprofHeader, buffer) 17 18 /** 19 * Loads [recordSize] bytes at [recordPosition] into the buffer that backs [HprofRecordReader] 20 * then calls [withRecordReader] with that reader as a receiver. [withRecordReader] is expected 21 * to use the receiver reader to read one record of exactly [recordSize] bytes. 22 * @return the results from [withRecordReader] 23 */ readRecordnull24 fun <T> readRecord( 25 recordPosition: Long, 26 recordSize: Long, 27 withRecordReader: HprofRecordReader.() -> T 28 ): T { 29 require(recordSize > 0L) { 30 "recordSize $recordSize must be > 0" 31 } 32 var mutablePos = recordPosition 33 var mutableByteCount = recordSize 34 35 while (mutableByteCount > 0L) { 36 val bytesRead = source.read(buffer, mutablePos, mutableByteCount) 37 check(bytesRead > 0) { 38 "Requested $mutableByteCount bytes after reading ${mutablePos - recordPosition}, got 0 bytes instead." 39 } 40 mutablePos += bytesRead 41 mutableByteCount -= bytesRead 42 } 43 return withRecordReader(reader).apply { 44 check(buffer.size() == 0L) { 45 "Buffer not fully consumed: ${buffer.size()} bytes left" 46 } 47 } 48 } 49 closenull50 override fun close() { 51 source.close() 52 } 53 54 companion object { 55 openReaderFornull56 fun openReaderFor( 57 hprofFile: File, 58 hprofHeader: HprofHeader = HprofHeader.parseHeaderOf(hprofFile) 59 ): RandomAccessHprofReader { 60 val sourceProvider = FileSourceProvider(hprofFile) 61 return openReaderFor(sourceProvider, hprofHeader) 62 } 63 openReaderFornull64 fun openReaderFor( 65 hprofSourceProvider: RandomAccessSourceProvider, 66 hprofHeader: HprofHeader = hprofSourceProvider.openRandomAccessSource() 67 .use { HprofHeader.parseHeaderOf(it.asStreamingSource()) } 68 ): RandomAccessHprofReader { 69 return RandomAccessHprofReader(hprofSourceProvider.openRandomAccessSource(), hprofHeader) 70 } 71 } 72 }