]> Cypherpunks.ru repositories - netstring.git/blob - reader.go
547a7dcbcf3e79517f0366b8886e79d1e4cc19a0
[netstring.git] / reader.go
1 /*
2 netstring -- netstring format serialization library
3 Copyright (C) 2015-2020 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         "io"
23         "strconv"
24 )
25
26 type Reader struct {
27         reader *bufio.Reader
28         prefix []byte
29         err    error
30         size   uint64
31         n      int
32 }
33
34 func NewReader(r io.Reader) *Reader {
35         return &Reader{
36                 reader: bufio.NewReader(r),
37                 prefix: make([]byte, MaxPrefixSize),
38         }
39 }
40
41 // Parse incoming netstring prefix. It returns netstring's incoming
42 // data length. After using this method you can call either Read()
43 // or Discard() methods. User can check if incoming data length is
44 // too big.
45 func (self *Reader) Iter() (size uint64, err error) {
46         self.n = 0
47         for self.n < MaxPrefixSize {
48                 self.prefix[self.n], self.err = self.reader.ReadByte()
49                 if self.err != nil {
50                         return 0, self.err
51                 }
52                 if self.prefix[self.n] == ':' {
53                         break
54                 }
55                 self.n++
56         }
57         self.size, self.err = strconv.ParseUint(
58                 string(self.prefix[:self.n]), 10, 64,
59         )
60         if self.err != nil {
61                 return 0, self.err
62         }
63         return self.size, nil
64 }
65
66 func (self *Reader) terminator() bool {
67         self.prefix[0], self.err = self.reader.ReadByte()
68         if self.err != nil {
69                 return false
70         }
71         if self.prefix[0] != ',' {
72                 self.err = ErrTerminator
73                 return false
74         }
75         return true
76 }
77
78 // Receive the full netstring message. This method is called after
79 // Iter() and user must preallocate buf. If buf size is smaller than
80 // incoming data size, then function will return an error. Also it
81 // checks the final terminator character and will return an error if
82 // it won't find it.
83 func (self *Reader) Read(buf []byte) error {
84         if self.err != nil {
85                 return ErrState
86         }
87         if uint64(cap(buf)) < self.size {
88                 return ErrBufSize
89         }
90         _, self.err = io.ReadAtLeast(self.reader, buf, int(self.size))
91         if self.err != nil {
92                 return self.err
93         }
94         if !self.terminator() {
95                 return self.err
96         }
97         return nil
98 }
99
100 // Discard (skip) netstring message. This method is called after Iter().
101 // It reads and ignores data from the reader and checks that terminator
102 // character is valid.
103 func (self *Reader) Discard() error {
104         if self.err != nil {
105                 return ErrState
106         }
107         if _, self.err = self.reader.Discard(int(self.size)); self.err != nil {
108                 return self.err
109         }
110         if !self.terminator() {
111                 return self.err
112         }
113         return nil
114 }