aboutsummaryrefslogtreecommitdiff
path: root/pkg/monitor/chunks.go
blob: 87871b9d61fb7618c0f61684a7007e7e957576f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package monitor

//
// A min heap of chunks, oredered on each chunk's start index.
//
// Credit: inspiration to use a heap from Aaron Gable, see
// https://github.com/aarongable/ctaudit
//

import (
	"container/heap"
	"crypto/sha256"
)

type chunk struct {
	startIndex uint64              // Index of the first leaf
	leafHashes [][sha256.Size]byte // List of consecutive leaf hashes
	matches    []LogEntry          // Leaves that matches some criteria
	errors     []error             // Errors that ocurred while parsing leaves
}

type chunks []*chunk

func newChunks() *chunks {
	var h chunks
	heap.Init((*internal)(&h))
	return &h
}

func (h *chunks) push(c *chunk) {
	heap.Push((*internal)(h), c)
}

func (h *chunks) pop() *chunk {
	x := heap.Pop((*internal)(h))
	return x.(*chunk)
}

// gap returns true if there's a gap between the provided start index and the
// top most chunk.  If the top most chunk is in sequence, it is merged with
// any following chunks that are also in sequence to form one larger chunk.
func (h *chunks) gap(start uint64) bool {
	if len(*h) == 0 {
		return true
	}

	top := h.pop()
	if start != top.startIndex {
		h.push(top)
		return true
	}

	for len(*h) > 0 {
		c := h.pop()
		if c.startIndex != top.startIndex+uint64(len(top.leafHashes)) {
			h.push(c)
			break
		}

		top.leafHashes = append(top.leafHashes, c.leafHashes...)
		top.matches = append(top.matches, c.matches...)
		top.errors = append(top.errors, c.errors...)
	}

	h.push(top)
	return false
}

// internal implements the heap interface, see example:
// https://cs.opensource.google/go/go/+/refs/tags/go1.21.5:src/container/heap/example_intheap_test.go
type internal chunks

func (h internal) Len() int           { return len(h) }
func (h internal) Less(i, j int) bool { return h[i].startIndex < h[j].startIndex }
func (h internal) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *internal) Push(x any) {
	*h = append(*h, x.(*chunk))
}

func (h *internal) Pop() any {
	old := *h
	n := len(old)
	x := old[n-1]
	old[n-1] = nil // avoid memory leak
	*h = old[:n-1]
	return x
}