github.com/aretext/aretext@v1.3.0/syntax/parser/tracking.go (about) 1 package parser 2 3 import ( 4 "io" 5 "math" 6 7 "github.com/aretext/aretext/text" 8 ) 9 10 // TrackingReader tracks the number of runes read by a reader iter and all its clones. 11 // It updates a shared counter, so clones of this iter should NOT be used in other threads. 12 // Copying the struct produces a new, independent iterator. 13 type TrackingRuneIter struct { 14 reader text.Reader 15 eof bool 16 limit uint64 17 numRead uint64 18 maxRead *uint64 19 } 20 21 // NewTrackingRuneIter starts tracking an existing rune iter. 22 func NewTrackingRuneIter(reader text.Reader) TrackingRuneIter { 23 var maxRead uint64 24 return TrackingRuneIter{ 25 reader: reader, 26 limit: math.MaxUint64, 27 maxRead: &maxRead, 28 } 29 } 30 31 // NextRune returns the next rune from the underlying reader and advances the iterator. 32 func (iter *TrackingRuneIter) NextRune() (rune, error) { 33 if iter.limit == 0 { 34 return '\x00', io.EOF 35 } 36 37 r, _, err := iter.reader.ReadRune() 38 39 if err == nil && iter.limit > 0 { 40 iter.limit-- 41 } 42 43 if err == nil || (err == io.EOF && !iter.eof) { 44 iter.eof = bool(err == io.EOF) 45 iter.numRead++ 46 if iter.numRead > *iter.maxRead { 47 *iter.maxRead = iter.numRead 48 } 49 } 50 51 return r, err 52 } 53 54 // Skip advances the iterator by the specified number of positions or the end of the file, whichever comes first. 55 func (iter *TrackingRuneIter) Skip(n uint64) uint64 { 56 for i := uint64(0); i < n; i++ { 57 _, err := iter.NextRune() 58 if err != nil { 59 return i 60 } 61 } 62 return n 63 } 64 65 // Limit sets the maximum number of runes this reader can produce. 66 func (iter *TrackingRuneIter) Limit(n uint64) { 67 iter.limit = n 68 } 69 70 // MaxRead returns the maximum number of runes read by this iter and all its clones. 71 func (iter *TrackingRuneIter) MaxRead() uint64 { 72 return *iter.maxRead 73 }