]> Cypherpunks.ru repositories - netstring.git/blob - r.go
Simplify :-finding
[netstring.git] / r.go
1 /*
2 netstring -- netstring format serialization library
3 Copyright (C) 2015-2023 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 package netstring
19
20 import (
21         "bufio"
22         "errors"
23         "io"
24         "strconv"
25 )
26
27 type Reader struct {
28         r    *bufio.Reader
29         left uint64
30         eof  bool
31 }
32
33 // Create new Reader.
34 // Pay attention that bufio.Reader is used to read from it.
35 func NewReader(r io.Reader) *Reader {
36         return &Reader{r: bufio.NewReader(r), eof: true}
37 }
38
39 // Parse netstring prefix. It returns data length.
40 // After this method you have to call either Read() or Discard().
41 // io.EOF is returned when underlying reader has no data anymore.
42 func (r *Reader) Next() (uint64, error) {
43         if !r.eof {
44                 return 0, errors.New("current chunk is unread")
45         }
46         lenRaw, err := r.r.ReadSlice(':')
47         if err != nil {
48                 return 0, err
49         }
50         size, err := strconv.ParseUint(string(lenRaw[:len(lenRaw)-1]), 10, 64)
51         if err != nil {
52                 return 0, err
53         }
54         r.left = size
55         r.eof = false
56         return size, nil
57 }
58
59 func (r *Reader) checkTerminator() error {
60         b, err := r.r.ReadByte()
61         if err != nil {
62                 return err
63         }
64         if b != ',' {
65                 return errors.New("no terminator found")
66         }
67         return nil
68 }
69
70 // Read current netstring chunk.
71 // This method must be called after Next().
72 // Terminator check and error raising are performed only at the end.
73 func (r *Reader) Read(buf []byte) (n int, err error) {
74         if r.eof {
75                 return 0, io.EOF
76         }
77         if uint64(len(buf)) > r.left {
78                 buf = buf[:r.left]
79         }
80         n, err = r.r.Read(buf)
81         r.left -= uint64(n)
82         if r.left == 0 {
83                 err = r.checkTerminator()
84                 if err == nil {
85                         r.eof = true
86                 }
87         }
88         return
89 }
90
91 // Discard remaining bytes in the chunk, possibly fully skipping it.
92 // This method must be called after Next().
93 func (r *Reader) Discard() (err error) {
94         _, err = r.r.Discard(int(r.left))
95         if err != nil {
96                 return
97         }
98         err = r.checkTerminator()
99         if err != nil {
100                 return
101         }
102         r.eof = true
103         return
104 }