"Fossies" - the Fresh Open Source Software Archive 
Member "AdGuardHome-0.104.3/internal/querylog/qlog_reader.go" (19 Nov 2020, 3934 Bytes) of package /linux/misc/dns/AdGuardHome-0.104.3.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
See also the latest
Fossies "Diffs" side-by-side code changes report for "qlog_reader.go":
0.104.1_vs_0.104.3.
1 package querylog
2
3 import (
4 "errors"
5 "fmt"
6 "io"
7
8 "github.com/AdguardTeam/AdGuardHome/internal/agherr"
9 )
10
11 // QLogReader allows reading from multiple query log files in the reverse order.
12 //
13 // Please note that this is a stateful object.
14 // Internally, it contains a pointer to a particular query log file, and
15 // to a specific position in this file, and it reads lines in reverse order
16 // starting from that position.
17 type QLogReader struct {
18 // qFiles - array with the query log files
19 // The order is - from oldest to newest
20 qFiles []*QLogFile
21
22 currentFile int // Index of the current file
23 }
24
25 // NewQLogReader initializes a QLogReader instance
26 // with the specified files
27 func NewQLogReader(files []string) (*QLogReader, error) {
28 qFiles := make([]*QLogFile, 0)
29
30 for _, f := range files {
31 q, err := NewQLogFile(f)
32 if err != nil {
33 // Close what we've already opened
34 _ = closeQFiles(qFiles)
35 return nil, err
36 }
37
38 qFiles = append(qFiles, q)
39 }
40
41 return &QLogReader{
42 qFiles: qFiles,
43 currentFile: (len(qFiles) - 1),
44 }, nil
45 }
46
47 // Seek performs binary search of a query log record with the specified timestamp.
48 // If the record is found, it sets QLogReader's position to point to that line,
49 // so that the next ReadNext call returned this line.
50 //
51 // Returns nil if the record is successfully found.
52 // Returns an error if for some reason we could not find a record with the specified timestamp.
53 func (r *QLogReader) Seek(timestamp int64) (err error) {
54 for i := len(r.qFiles) - 1; i >= 0; i-- {
55 q := r.qFiles[i]
56 _, _, err = q.Seek(timestamp)
57 if err == nil {
58 // Search is finished, and the searched element have
59 // been found. Update currentFile only, position is
60 // already set properly in QLogFile.
61 r.currentFile = i
62
63 return nil
64 } else if errors.Is(err, ErrTSTooEarly) {
65 // Look at the next file, since we've reached the end of
66 // this one.
67 continue
68 } else if errors.Is(err, ErrTSTooLate) {
69 // Just seek to the start then. timestamp is probably
70 // between the end of the previous one and the start of
71 // this one.
72 return r.SeekStart()
73 } else if errors.Is(err, ErrTSNotFound) {
74 break
75 }
76 }
77
78 return fmt.Errorf("querylog: %w", err)
79 }
80
81 // SeekStart changes the current position to the end of the newest file
82 // Please note that we're reading query log in the reverse order
83 // and that's why log start is actually the end of file
84 //
85 // Returns nil if we were able to change the current position.
86 // Returns error in any other case.
87 func (r *QLogReader) SeekStart() error {
88 if len(r.qFiles) == 0 {
89 return nil
90 }
91
92 r.currentFile = len(r.qFiles) - 1
93 _, err := r.qFiles[r.currentFile].SeekStart()
94 return err
95 }
96
97 // ReadNext reads the next line (in the reverse order) from the query log files.
98 // and shifts the current position left to the next (actually prev) line (or the next file).
99 // returns io.EOF if there's nothing to read more.
100 func (r *QLogReader) ReadNext() (string, error) {
101 if len(r.qFiles) == 0 {
102 return "", io.EOF
103 }
104
105 for r.currentFile >= 0 {
106 q := r.qFiles[r.currentFile]
107 line, err := q.ReadNext()
108 if err != nil {
109 // Shift to the older file
110 r.currentFile--
111 if r.currentFile < 0 {
112 break
113 }
114
115 q = r.qFiles[r.currentFile]
116
117 // Set it's position to the start right away
118 _, err = q.SeekStart()
119
120 // This is unexpected, return an error right away
121 if err != nil {
122 return "", err
123 }
124 } else {
125 return line, nil
126 }
127 }
128
129 // Nothing to read anymore
130 return "", io.EOF
131 }
132
133 // Close closes the QLogReader
134 func (r *QLogReader) Close() error {
135 return closeQFiles(r.qFiles)
136 }
137
138 // closeQFiles - helper method to close multiple QLogFile instances
139 func closeQFiles(qFiles []*QLogFile) error {
140 var errs []error
141
142 for _, q := range qFiles {
143 err := q.Close()
144 if err != nil {
145 errs = append(errs, err)
146 }
147 }
148
149 if len(errs) > 0 {
150 return agherr.Many("Error while closing QLogReader", errs...)
151 }
152
153 return nil
154 }