-
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
// Copyright 2026 Shota FUJI <pockawoooh@gmail.com>
// SPDX-License-Identifier: MIT
package git
// ringBuffer is a buffer focusing on pushing an item to end and retrieving
// the sorted slice at once, without popping or removing.
//
// As git (git-go) does not have a way to load N commits until commit X, we
// have to read from a tip till we find the commit X in order to retrieve
// commits for "?after=X" query. std has no suitable container for that.
// - container/list ... Have to remember both ends then call PushBack and
// Remove.
// - container/ring ... Has no beggining or end, heavily relies on
// reassignment / shadowing. From document, it's
// unclear what'll happen to unused slots.
//
// I just want "a buffer holds last N pushed items." This is a naive implemention
// that solves the problem.
type ringBuffer[T any] struct {
buffer []T
size uint32
cursor uint32
}
func newRingBuffer[T any](size uint32) ringBuffer[T] {
return ringBuffer[T]{
buffer: make([]T, 0, size),
size: size,
cursor: 0,
}
}
func (r *ringBuffer[T]) push(item T) {
if uint32(len(r.buffer)) < r.size {
// Until buffer is filled up, just append an item as usual.
//
// [1|2|3| | | ]
// ^
//
// [1|2|3|4| | ]
// ^
r.buffer = append(r.buffer, item)
} else {
// When buffer is full, we have to move the cursor and put an item behind
// the cursor.
//
// [1|2|3|4|5|6]
// ^
//
// [7|2|3|4|5|6]
// ^
r.buffer[r.cursor] = item
r.cursor += 1
if r.cursor >= r.size {
r.cursor = 0
}
}
}
// toSlice returns sorted slice of last (size) items.
func (r ringBuffer[T]) toSlice() []T {
s := make([]T, len(r.buffer))
for i := range len(r.buffer) {
idx := (uint32(i) + r.cursor) % r.size
s[i] = r.buffer[idx]
}
return s
}