|
- package events
-
- import (
- "bytes"
- "encoding/json"
- "errors"
- "io"
- "os"
- "time"
-
- "sdr-wideband-suite/internal/detector"
- )
-
- const (
- defaultLimit = 200
- maxLimit = 2000
- readChunk = 64 * 1024
- )
-
- // ReadRecent reads the newest events from a JSONL file.
- // If since is non-zero, older events (by End time) are skipped.
- func ReadRecent(path string, limit int, since time.Time) ([]detector.Event, error) {
- if limit <= 0 {
- limit = defaultLimit
- }
- if limit > maxLimit {
- limit = maxLimit
- }
-
- file, err := os.Open(path)
- if err != nil {
- if errors.Is(err, os.ErrNotExist) {
- return nil, nil
- }
- return nil, err
- }
- defer file.Close()
-
- info, err := file.Stat()
- if err != nil {
- return nil, err
- }
- if info.Size() == 0 {
- return nil, nil
- }
-
- lines, err := readLinesReverse(file, info.Size(), limit)
- if err != nil {
- return nil, err
- }
-
- events := make([]detector.Event, 0, len(lines))
- for _, line := range lines {
- var ev detector.Event
- if err := json.Unmarshal([]byte(line), &ev); err != nil {
- continue
- }
- if !since.IsZero() && ev.End.Before(since) {
- break
- }
- events = append(events, ev)
- }
-
- for i, j := 0, len(events)-1; i < j; i, j = i+1, j-1 {
- events[i], events[j] = events[j], events[i]
- }
- return events, nil
- }
-
- func readLinesReverse(file *os.File, size int64, limit int) ([]string, error) {
- pos := size
- remainder := []byte{}
- lines := make([]string, 0, limit)
-
- for pos > 0 && len(lines) < limit {
- chunkSize := int64(readChunk)
- if chunkSize > pos {
- chunkSize = pos
- }
- pos -= chunkSize
- buf := make([]byte, chunkSize)
- n, err := file.ReadAt(buf, pos)
- if err != nil && !errors.Is(err, io.EOF) {
- return nil, err
- }
- buf = buf[:n]
- data := append(buf, remainder...)
-
- i := len(data)
- for i > 0 && len(lines) < limit {
- j := bytes.LastIndexByte(data[:i], '\n')
- if j == -1 {
- break
- }
- line := bytes.TrimSpace(data[j+1 : i])
- if len(line) > 0 {
- lines = append(lines, string(line))
- }
- i = j
- }
- remainder = data[:i]
- }
-
- if len(lines) < limit {
- line := bytes.TrimSpace(remainder)
- if len(line) > 0 {
- lines = append(lines, string(line))
- }
- }
- return lines, nil
- }
|