Browse Source

codec: at start of encode, ensure bufioEncWriter's buffer is not nil

We previously introduces a mechanism where calling Release was optional,
and we would implicitly call Release if a full Encode was completed.

Unfortunately, this broke the use-case where an Encoder was used
multiple times in sequence.

We fix this now, by doing what we should have done before i.e. in addition
to calling Release (which nullifies the []byte buffer and returns it to the
pool), we should also grab a buffer from the pool at the beginning of each
Encode call.

Fixes #278
Ugorji Nwoke 6 years ago
parent
commit
497916c003
3 changed files with 37 additions and 1 deletions
  1. 23 0
      codec/codec_test.go
  2. 13 1
      codec/encode.go
  3. 1 0
      codec/z_all_test.go

+ 23 - 0
codec/codec_test.go

@@ -2429,6 +2429,25 @@ func doTestMaxDepth(t *testing.T, name string, h Handle) {
 	}
 }
 
+func doTestMultipleEncDec(t *testing.T, name string, h Handle) {
+	testOnce.Do(testInitAll)
+	// encode a string multiple times.
+	// decode it multiple times.
+	// ensure we get the value each time
+	var s1 = "ugorji"
+	var s2 = "nwoke"
+	var s11, s21 string
+	var buf bytes.Buffer
+	e := NewEncoder(&buf, h)
+	e.MustEncode(s1)
+	e.MustEncode(s2)
+	d := NewDecoder(&buf, h)
+	d.MustDecode(&s11)
+	d.MustDecode(&s21)
+	testDeepEqualErr(s1, s11, t, name+"-multiple-encode")
+	testDeepEqualErr(s2, s21, t, name+"-multiple-encode")
+}
+
 // -----------------
 
 func TestJsonDecodeNonStringScalarInStringContext(t *testing.T) {
@@ -3218,6 +3237,10 @@ func TestSimpleMaxDepth(t *testing.T) {
 	doTestMaxDepth(t, "simple", testSimpleH)
 }
 
+func TestMultipleEncDec(t *testing.T) {
+	doTestMultipleEncDec(t, "json", testJsonH)
+}
+
 // TODO:
 //
 // Add Tests for the following:

+ 13 - 1
codec/encode.go

@@ -294,7 +294,9 @@ func (z *bufioEncWriter) reset(w io.Writer, bufsize int) {
 	if bufsize <= 0 {
 		bufsize = defEncByteBufSize
 	}
-	if cap(z.buf) >= bufsize {
+	if z.buf == nil {
+		z.buf = z.bytesBufPooler.get(bufsize)
+	} else if cap(z.buf) >= bufsize {
 		z.buf = z.buf[:cap(z.buf)]
 	} else {
 		z.bytesBufPooler.end() // potentially return old one to pool
@@ -1471,11 +1473,21 @@ func (e *Encoder) MustEncode(v interface{}) {
 }
 
 func (e *Encoder) mustEncode(v interface{}) {
+	// ensure the bufioEncWriter buffer is not nil (e.g. if Release() was called)
+	if e.wf != nil && e.wf.buf == nil {
+		if e.h.WriterBufferSize > 0 {
+			e.wf.buf = e.wf.bytesBufPooler.get(e.h.WriterBufferSize)
+		} else {
+			e.wf.buf = e.wf.bytesBufPooler.get(defEncByteBufSize)
+		}
+	}
+
 	e.calls++
 	e.encode(v)
 	e.e.atEndOfEncode()
 	e.w.end()
 	e.calls--
+
 	if !e.h.ExplicitRelease && e.calls == 0 {
 		e.Release()
 	}

+ 1 - 0
codec/z_all_test.go

@@ -304,6 +304,7 @@ func testNonHandlesGroup(t *testing.T) {
 	t.Run("TestAtomic", TestAtomic)
 	t.Run("TestAllEncCircularRef", TestAllEncCircularRef)
 	t.Run("TestAllAnonCycle", TestAllAnonCycle)
+	t.Run("TestMultipleEncDec", TestMultipleEncDec)
 }
 
 func TestCodecSuite(t *testing.T) {