فهرست منبع

codec: Fix UnmarshalJSON support

We changed the json decoding so it would reduce the number of unreads
and potentially keep track of the last byte read, which indicated
that the last json value was completely read.

However, this broke UnmarshalJSON support, which needed to use the
"cached" byte plus the subsequent bytes.

Fix this by adding an uncacheRead method to the decDriver, and letting
Decoder have a nextValueBytes method which is called from jsonUnmarshal.

Fixes #112
Ugorji Nwoke 10 سال پیش
والد
کامیت
f1f1a805ed
5فایلهای تغییر یافته به همراه25 افزوده شده و 18 حذف شده
  1. 12 7
      codec/decode.go
  2. 2 5
      codec/gen-helper.generated.go
  3. 2 5
      codec/gen-helper.go.tmpl
  4. 7 0
      codec/json.go
  5. 2 1
      codec/noop.go

+ 12 - 7
codec/decode.go

@@ -88,11 +88,13 @@ type decDriver interface {
 	ReadArrayStart() int
 
 	reset()
+	uncacheRead()
 }
 
 type decNoSeparator struct{}
 
-func (_ decNoSeparator) ReadEnd() {}
+func (_ decNoSeparator) ReadEnd()     {}
+func (_ decNoSeparator) uncacheRead() {}
 
 type DecodeOptions struct {
 	// MapType specifies type to use during schema-less decoding of a map in the stream.
@@ -475,12 +477,8 @@ func (f *decFnInfo) textUnmarshal(rv reflect.Value) {
 func (f *decFnInfo) jsonUnmarshal(rv reflect.Value) {
 	tm := f.getValueForUnmarshalInterface(rv, f.ti.junmIndir).(jsonUnmarshaler)
 	// bs := f.d.d.DecodeBytes(f.d.b[:], true, true)
-	// grab the bytes to be read, as UnmarshalJSON wants the full JSON to unmarshal it itself.
-	f.d.r.track()
-	f.d.swallow()
-	bs := f.d.r.stopTrack()
-	// fmt.Printf(">>>>>> REFLECTION JSON: %s\n", bs)
-	fnerr := tm.UnmarshalJSON(bs)
+	// grab the bytes to be read, as UnmarshalJSON needs the full JSON so as to unmarshal it itself.
+	fnerr := tm.UnmarshalJSON(f.d.nextValueBytes())
 	if fnerr != nil {
 		panic(fnerr)
 	}
@@ -1864,6 +1862,13 @@ func (d *Decoder) intern(s string) {
 	}
 }
 
+func (d *Decoder) nextValueBytes() []byte {
+	d.d.uncacheRead()
+	d.r.track()
+	d.swallow()
+	return d.r.stopTrack()
+}
+
 // --------------------------------------------------
 
 // decSliceHelper assists when decoding into a slice, from a map or an array in the stream.

+ 2 - 5
codec/gen-helper.generated.go

@@ -176,11 +176,8 @@ func (f genHelperDecoder) DecTextUnmarshal(tm encoding.TextUnmarshaler) {
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecJSONUnmarshal(tm jsonUnmarshaler) {
 	// bs := f.dd.DecodeBytes(f.d.b[:], true, true)
-	f.d.r.track()
-	f.d.swallow()
-	bs := f.d.r.stopTrack()
-	// fmt.Printf(">>>>>> CODECGEN JSON: %s\n", bs)
-	fnerr := tm.UnmarshalJSON(bs)
+	// grab the bytes to be read, as UnmarshalJSON needs the full JSON so as to unmarshal it itself.
+	fnerr := tm.UnmarshalJSON(f.d.nextValueBytes())
 	if fnerr != nil {
 		panic(fnerr)
 	}

+ 2 - 5
codec/gen-helper.go.tmpl

@@ -158,11 +158,8 @@ func (f genHelperDecoder) DecTextUnmarshal(tm encoding.TextUnmarshaler) {
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecJSONUnmarshal(tm jsonUnmarshaler) {
 	// bs := f.dd.DecodeBytes(f.d.b[:], true, true)
-	f.d.r.track()
-	f.d.swallow()
-	bs := f.d.r.stopTrack()
-	// fmt.Printf(">>>>>> CODECGEN JSON: %s\n", bs)
-	fnerr := tm.UnmarshalJSON(bs)
+	// grab the bytes to be read, as UnmarshalJSON needs the full JSON so as to unmarshal it itself.
+	fnerr := tm.UnmarshalJSON(f.d.nextValueBytes())
 	if fnerr != nil {
 		panic(fnerr)
 	}

+ 7 - 0
codec/json.go

@@ -398,6 +398,13 @@ func jsonIsWS(b byte) bool {
 // 	d.tok = b
 // }
 
+func (d *jsonDecDriver) uncacheRead() {
+	if d.tok != 0 {
+		d.r.unreadn1()
+		d.tok = 0
+	}
+}
+
 func (d *jsonDecDriver) sendContainerState(c containerState) {
 	if d.tok == 0 {
 		var b byte

+ 2 - 1
codec/noop.go

@@ -56,7 +56,8 @@ func (h *noopDrv) m(v int) int { h.i++; return h.i % v }
 func (h *noopDrv) newEncDriver(e *Encoder) encDriver { h.e = e; return h }
 func (h *noopDrv) newDecDriver(d *Decoder) decDriver { h.d = d; return h }
 
-func (h *noopDrv) reset() {}
+func (h *noopDrv) reset()       {}
+func (h *noopDrv) uncacheRead() {}
 
 // --- encDriver