osbase/logtree: add WithStartPosition option

To allow users to not always request all messages,
we introduce another option to the logtree.LogReader
which allows for starting at a specific global log id.
This, for example, makes implementing scrollback easier.

Change-Id: I1773288f670f476706d94baf3f052fe1e5da9eb0
Reviewed-on: https://review.monogon.dev/c/monogon/+/4452
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/osbase/logtree/journal.go b/osbase/logtree/journal.go
index c6cc503..8697b21 100644
--- a/osbase/logtree/journal.go
+++ b/osbase/logtree/journal.go
@@ -5,7 +5,7 @@
 
 import (
 	"errors"
-	"sort"
+	"slices"
 	"strings"
 	"sync"
 
@@ -96,6 +96,10 @@
 	// provided filters (eg. to limit events to subtrees that interest that particular
 	// subscriber).
 	subscribers []*subscriber
+
+	// seq is a counter tracking the total amount of log entries appended since
+	// creation.
+	seq uint64
 }
 
 // newJournal creates a new empty journal. All journals are independent from
@@ -169,6 +173,19 @@
 	return e.leveled != nil
 }
 
+func filterStartPosition(count int, pos int, direction ReadDirection) filter {
+	return func(e *entry) bool {
+		switch direction {
+		case ReadDirectionAfter:
+			return e.seqGlobal >= uint64(pos) && (count == BacklogAllAvailable || e.seqGlobal < uint64(pos+count))
+		case ReadDirectionBefore:
+			return e.seqGlobal < uint64(pos) && (count == BacklogAllAvailable || e.seqGlobal >= uint64(max(0, pos-count)))
+		default:
+			panic("unreachable")
+		}
+	}
+}
+
 // scanEntries does a linear scan through the global entry list and returns all
 // entries that match the given filters. If retrieving entries for an exact event,
 // getEntries should be used instead, as it will leverage DN-local linked lists to
@@ -198,9 +215,8 @@
 	}
 
 	// Reverse entries back into chronological order.
-	sort.SliceStable(res, func(i, j int) bool {
-		return i > j
-	})
+	slices.Reverse(res)
+
 	return
 }
 
@@ -234,9 +250,8 @@
 	}
 
 	// Reverse entries back into chronological order.
-	sort.SliceStable(res, func(i, j int) bool {
-		return i > j
-	})
+	slices.Reverse(res)
+
 	return
 }