francoispqt / gojay

high performance JSON encoder/decoder with stream API for Golang
MIT License
2.11k stars 113 forks source link

panic on example stream program #144

Open celer opened 4 years ago

celer commented 4 years ago

Modifying the streaming encoder to write to stdout causes a panic:

panic: runtime error: index out of range [-1]

goroutine 28 [running]:
github.com/francoispqt/gojay.(*Encoder).getPreviousRune(...)
        /home/vagrant/go/src/github.com/francoispqt/gojay/encode.go:201
github.com/francoispqt/gojay.(*Encoder).Object(0xc0000af080, 0x4ec100, 0xc00009a930)
        /home/vagrant/go/src/github.com/francoispqt/gojay/encode_object.go:114 +0x61c
main.StreamChan.MarshalStream(0xc000094d20, 0xc00009a750)
        /tmp/gojay/test.go:33 +0xf2
github.com/francoispqt/gojay.consume(0xc00009a2a0, 0xc00009a750, 0x4ec080, 0xc000094d20)
        /home/vagrant/go/src/github.com/francoispqt/gojay/encode_stream.go:193 +0x90
created by github.com/francoispqt/gojay.(*StreamEncoder).EncodeStream
        /home/vagrant/go/src/github.com/francoispqt/gojay/encode_stream.go:53 +0x1cf
exit status 2

with this program:

package main

import (
    "os"

    "github.com/francoispqt/gojay"
)

// Our structure which will be pushed to our stream
type user struct {
    id    int
    name  string
    email string
}

func (u *user) MarshalJSONObject(enc *gojay.Encoder) {
    enc.IntKey("id", u.id)
    enc.StringKey("name", u.name)
    enc.StringKey("email", u.email)
}
func (u *user) IsNil() bool {
    return u == nil
}

// Our MarshalerStream implementation
type StreamChan chan *user

func (s StreamChan) MarshalStream(enc *gojay.StreamEncoder) {
    select {
    case <-enc.Done():
        return
    case o := <-s:
        enc.Object(o)
    }
}

// Our main function
func main() {
    // we borrow an encoder set stdout as the writer,
    // set the number of consumer to 10
    // and tell the encoder to separate each encoded element
    // added to the channel by a new line character
    enc := gojay.Stream.BorrowEncoder(os.Stdout).NConsumer(10).LineDelimited()
    // instantiate our MarshalerStream
    s := StreamChan(make(chan *user))
    // start the stream encoder
    // will block its goroutine until enc.Cancel(error) is called
    // or until something is written to the channel
    go enc.EncodeStream(s)
    // write to our MarshalerStream
    for i := 0; i < 1000; i++ {
        s <- &user{i, "username", "user@email.com"}
    }
    // Wait
    <-enc.Done()
}