/* netstring -- netstring format serialization library Copyright (C) 2015-2023 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package netstring import ( "bufio" "errors" "io" "strconv" ) type Reader struct { r *bufio.Reader left uint64 eof bool } // Create new Reader. // Pay attention that bufio.Reader is used to read from it. func NewReader(r io.Reader) *Reader { return &Reader{r: bufio.NewReader(r), eof: true} } // Parse netstring prefix. It returns data length. // After this method you have to call either Read() or Discard(). // io.EOF is returned when underlying reader has no data anymore. func (r *Reader) Next() (uint64, error) { if !r.eof { return 0, errors.New("current chunk is unread") } lenRaw, err := r.r.ReadSlice(':') if err != nil { return 0, err } size, err := strconv.ParseUint(string(lenRaw[:len(lenRaw)-1]), 10, 64) if err != nil { return 0, err } r.left = size r.eof = false return size, nil } func (r *Reader) checkTerminator() error { b, err := r.r.ReadByte() if err != nil { return err } if b != ',' { return errors.New("no terminator found") } return nil } // Read current netstring chunk. // This method must be called after Next(). // Terminator check and error raising are performed only at the end. func (r *Reader) Read(buf []byte) (n int, err error) { if r.eof { return 0, io.EOF } if uint64(len(buf)) > r.left { buf = buf[:r.left] } n, err = r.r.Read(buf) r.left -= uint64(n) if r.left == 0 { err = r.checkTerminator() if err == nil { r.eof = true } } return } // Discard remaining bytes in the chunk, possibly fully skipping it. // This method must be called after Next(). func (r *Reader) Discard() (err error) { _, err = r.r.Discard(int(r.left)) if err != nil { return } err = r.checkTerminator() if err != nil { return } r.eof = true return }