summaryrefslogtreecommitdiff
path: root/internal/statsengine
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-24 12:09:31 +0200
committerPaul Buetow <paul@buetow.org>2026-02-24 12:09:31 +0200
commit0d4ef22478a470d86ce907beedcaa726d0d46c73 (patch)
tree0fef36eba7374f2a63dc5d00f93e51ec766eb2b1 /internal/statsengine
parenta403ca152b6268eacf2804c2d857ead16af37ef3 (diff)
statsengine: stop cloning snapshot accessors on reads
Diffstat (limited to 'internal/statsengine')
-rw-r--r--internal/statsengine/snapshot.go35
-rw-r--r--internal/statsengine/snapshot_test.go14
2 files changed, 28 insertions, 21 deletions
diff --git a/internal/statsengine/snapshot.go b/internal/statsengine/snapshot.go
index 6583514..6e38739 100644
--- a/internal/statsengine/snapshot.go
+++ b/internal/statsengine/snapshot.go
@@ -154,24 +154,28 @@ func (h HistogramSnapshot) Clone() HistogramSnapshot {
}
}
-// LatencySeriesNs returns a defensive copy of latency sparkline samples.
+// LatencySeriesNs returns latency sparkline samples.
+// Callers must treat returned data as read-only.
func (s Snapshot) LatencySeriesNs() []float64 {
- return slices.Clone(s.latencySeriesNs)
+ return s.latencySeriesNs
}
-// GapSeriesNs returns a defensive copy of inter-syscall gap sparkline samples.
+// GapSeriesNs returns inter-syscall gap sparkline samples.
+// Callers must treat returned data as read-only.
func (s Snapshot) GapSeriesNs() []float64 {
- return slices.Clone(s.gapSeriesNs)
+ return s.gapSeriesNs
}
-// ThroughputSeriesB returns a defensive copy of throughput sparkline samples.
+// ThroughputSeriesB returns throughput sparkline samples.
+// Callers must treat returned data as read-only.
func (s Snapshot) ThroughputSeriesB() []float64 {
- return slices.Clone(s.throughputSeriesB)
+ return s.throughputSeriesB
}
-// Syscalls returns a defensive copy of per-syscall snapshot rows.
+// Syscalls returns per-syscall snapshot rows.
+// Callers must treat returned data as read-only.
func (s Snapshot) Syscalls() []SyscallSnapshot {
- return slices.Clone(s.syscalls)
+ return s.syscalls
}
// SyscallsCount returns number of syscall rows without cloning backing slices.
@@ -179,9 +183,10 @@ func (s Snapshot) SyscallsCount() int {
return len(s.syscalls)
}
-// Files returns a defensive copy of per-file snapshot rows.
+// Files returns per-file snapshot rows.
+// Callers must treat returned data as read-only.
func (s Snapshot) Files() []FileSnapshot {
- return slices.Clone(s.files)
+ return s.files
}
// FilesCount returns number of file rows without cloning backing slices.
@@ -189,9 +194,10 @@ func (s Snapshot) FilesCount() int {
return len(s.files)
}
-// Processes returns a defensive copy of per-process snapshot rows.
+// Processes returns per-process snapshot rows.
+// Callers must treat returned data as read-only.
func (s Snapshot) Processes() []ProcessSnapshot {
- return slices.Clone(s.processes)
+ return s.processes
}
// ProcessesCount returns number of process rows without cloning backing slices.
@@ -199,7 +205,8 @@ func (s Snapshot) ProcessesCount() int {
return len(s.processes)
}
-// Buckets returns a defensive copy of histogram buckets.
+// Buckets returns histogram buckets.
+// Callers must treat returned data as read-only.
func (h HistogramSnapshot) Buckets() []HistogramBucketSnapshot {
- return slices.Clone(h.buckets)
+ return h.buckets
}
diff --git a/internal/statsengine/snapshot_test.go b/internal/statsengine/snapshot_test.go
index 065eded..e5c3caa 100644
--- a/internal/statsengine/snapshot_test.go
+++ b/internal/statsengine/snapshot_test.go
@@ -58,7 +58,7 @@ func TestNewSnapshotDefensivelyCopiesSlices(t *testing.T) {
}
}
-func TestSnapshotAccessorsReturnCopies(t *testing.T) {
+func TestSnapshotAccessorsReturnReadOnlyViews(t *testing.T) {
s := NewSnapshot(
[]float64{1},
[]float64{2},
@@ -72,20 +72,20 @@ func TestSnapshotAccessorsReturnCopies(t *testing.T) {
lat := s.LatencySeriesNs()
lat[0] = 100
- if got := s.LatencySeriesNs()[0]; got != 1 {
- t.Fatalf("latency accessor leaked mutability: got %v", got)
+ if got := s.LatencySeriesNs()[0]; got != 100 {
+ t.Fatalf("expected accessor to return backing slice view, got %v", got)
}
syscalls := s.Syscalls()
syscalls[0].Name = "write"
- if got := s.Syscalls()[0].Name; got != "read" {
- t.Fatalf("syscalls accessor leaked mutability: got %q", got)
+ if got := s.Syscalls()[0].Name; got != "write" {
+ t.Fatalf("expected accessor to return backing slice view, got %q", got)
}
buckets := s.LatencyHistogram.Buckets()
buckets[0].Count = 99
- if got := s.LatencyHistogram.Buckets()[0].Count; got != 1 {
- t.Fatalf("bucket accessor leaked mutability: got %d", got)
+ if got := s.LatencyHistogram.Buckets()[0].Count; got != 99 {
+ t.Fatalf("expected accessor to return backing slice view, got %d", got)
}
}