diff options
Diffstat (limited to 'binstream.go')
-rw-r--r-- | binstream.go | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/binstream.go b/binstream.go new file mode 100644 index 0000000..4240517 --- /dev/null +++ b/binstream.go @@ -0,0 +1,114 @@ +package binproto + +import ( + "encoding/binary" + "errors" + "github.com/kch42/kagus" + "io" + "sync" +) + +// BinstreamReader reads a binary stream from a binproto stream. +type BinstreamReader struct { + r io.Reader + err error + toread int + surMu *sync.Mutex // The mutex of the parent SimpleUnitReader +} + +// Read implements io.Reader. +func (bsr *BinstreamReader) Read(p []byte) (int, error) { + if bsr.err != nil { + return 0, bsr.err + } + + if bsr.toread == 0 { + var _toread int32 + if err := binary.Read(bsr.r, binary.LittleEndian, &_toread); err != nil { + bsr.err = err + return 0, err + } + + if _toread < 0 { + bsr.toread = -1 + bsr.err = io.EOF + bsr.surMu.Unlock() // TODO: Unlock on other conditions? + return 0, io.EOF + } + + bsr.toread = int(_toread) + } + + want := len(p) + if bsr.toread < want { + want = bsr.toread + } + n, err := bsr.r.Read(p[:want]) + + switch err { + case nil: + bsr.toread -= n + return n, nil + case io.EOF: + // NOTE: Perhaps we should log this? IDK... + bsr.err = errors.New("binstream terminated abnormally") + return n, bsr.err + } + + bsr.err = err + return n, err +} + +// FastForward skips to the end of the stream. Use this, if the data is useless. +func (bsr *BinstreamReader) FastForward() error { + nirvana := kagus.NewNirvanaWriter() + _, err := io.Copy(nirvana, bsr) + return err +} + +// BinstreamReader writes a binary stream to a binproto stream. +type BinstreamWriter struct { + w io.Writer + err error +} + +// Write implements io.Writer. +func (bsw *BinstreamWriter) Write(p []byte) (int, error) { + if bsw.err != nil { + return 0, bsw.err + } + + l := len(p) + if l == 0 { + return 0, nil + } + + if err := binary.Write(bsw.w, binary.LittleEndian, int32(l)); err != nil { + bsw.err = err + return 0, err + } + + n, err := bsw.w.Write(p) + if err != nil { + bsw.err = err + } + + return n, err +} + +// Close implements io.Closer. You MUST close a stream, so it is terminated properly. +func (bsw *BinstreamWriter) Close() error { + switch bsw.err { + case nil: + case io.EOF: + return nil + default: + return bsw.err + } + if err := binary.Write(bsw.w, binary.LittleEndian, int32(-1)); err != nil { + return err + } + + bsw.err = io.EOF + return nil +} |