]> Cypherpunks.ru repositories - netstring.git/blob - reader.go
netstring format serialization library
[netstring.git] / reader.go
1 /*
2 netstring -- netstring format serialization library
3 Copyright (C) 2015 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, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package netstring
20
21 import (
22         "bufio"
23         "io"
24         "strconv"
25 )
26
27 type Reader struct {
28         reader *bufio.Reader
29         prefix []byte
30         err    error
31         size   uint64
32         n      int
33 }
34
35 func NewReader(r io.Reader) *Reader {
36         return &Reader{
37                 reader: bufio.NewReader(r),
38                 prefix: make([]byte, MaxPrefixSize),
39         }
40 }
41
42 // Parse incoming netstring prefix. It returns netstring's incoming
43 // data length. After using this method you can call either Read()
44 // or Discard() methods. User can check if incoming data length is
45 // too big.
46 func (self *Reader) Iter() (size uint64, err error) {
47         self.n = 0
48         for self.n < MaxPrefixSize {
49                 self.prefix[self.n], self.err = self.reader.ReadByte()
50                 if self.err != nil {
51                         return 0, self.err
52                 }
53                 if self.prefix[self.n] == ':' {
54                         break
55                 }
56                 self.n++
57         }
58         self.size, self.err = strconv.ParseUint(
59                 string(self.prefix[:self.n]), 10, 64,
60         )
61         if self.err != nil {
62                 return 0, self.err
63         }
64         return self.size, nil
65 }
66
67 func (self *Reader) terminator() bool {
68         self.prefix[0], self.err = self.reader.ReadByte()
69         if self.err != nil {
70                 return false
71         }
72         if self.prefix[0] != ',' {
73                 self.err = ErrTerminator
74                 return false
75         }
76         return true
77 }
78
79 // Receive the full netstring message. This method is called after
80 // Iter() and user must preallocate buf. If buf size is smaller than
81 // incoming data size, then function will return an error. Also it
82 // checks the final terminator character and will return an error if
83 // it won't find it.
84 func (self *Reader) Read(buf []byte) error {
85         if self.err != nil {
86                 return ErrState
87         }
88         if uint64(cap(buf)) < self.size {
89                 return ErrBufSize
90         }
91         _, self.err = io.ReadAtLeast(self.reader, buf, int(self.size))
92         if self.err != nil {
93                 return self.err
94         }
95         if !self.terminator() {
96                 return self.err
97         }
98         return nil
99 }
100
101 // Discard (skip) netstring message. This method is called after Iter().
102 // It reads and ignores data from the reader and checks that terminator
103 // character is valid.
104 func (self *Reader) Discard() error {
105         if self.err != nil {
106                 return ErrState
107         }
108         if _, self.err = self.reader.Discard(int(self.size)); self.err != nil {
109                 return self.err
110         }
111         if !self.terminator() {
112                 return self.err
113         }
114         return nil
115 }