1 package shark
2 
3 import okio.Buffer
4 import kotlin.math.absoluteValue
5 
6 /**
7  * Captures IO read metrics without using much memory.
8  */
9 class ConstantMemoryMetricsDualSourceProvider(
10   private val realSourceProvider: DualSourceProvider
11 ) : DualSourceProvider {
12 
13   var randomAccessByteReads = 0L
14     internal set
15 
16   var randomAccessReadCount = 0L
17     internal set
18 
19   var randomAccessByteTravel = 0L
20     internal set
21 
22   private var lastRandomAccessPosition = -1L
23   private var minPosition = -1L
24   private var maxPosition = -1L
25 
updateRandomAccessStatsOnReadnull26   private fun updateRandomAccessStatsOnRead(
27     position: Long,
28     bytesRead: Long
29   ) {
30     randomAccessByteReads += bytesRead
31     randomAccessReadCount++
32     if (lastRandomAccessPosition != -1L) {
33       randomAccessByteTravel += (position - lastRandomAccessPosition).absoluteValue
34       minPosition = minPosition.coerceAtMost(position)
35       maxPosition = maxPosition.coerceAtLeast(position)
36     } else {
37       minPosition = position
38       maxPosition = position
39     }
40 
41 
42     lastRandomAccessPosition = position
43   }
44 
45   val byteTravelRange
46     get() = (maxPosition - minPosition)
47 
openStreamingSourcenull48   override fun openStreamingSource() = realSourceProvider.openStreamingSource()
49 
50   override fun openRandomAccessSource(): RandomAccessSource {
51     val randomAccessSource = realSourceProvider.openRandomAccessSource()
52     return object : RandomAccessSource {
53       override fun read(
54         sink: Buffer,
55         position: Long,
56         byteCount: Long
57       ): Long {
58         val bytesRead = randomAccessSource.read(sink, position, byteCount)
59         updateRandomAccessStatsOnRead(position, bytesRead)
60         return bytesRead
61       }
62 
63       override fun close() = randomAccessSource.close()
64     }
65   }
66 }