diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-13 05:11:45 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-13 05:11:45 +0000 |
commit | df4aa89a5e7acb315655f193e7f549e8d32367e2 (patch) | |
tree | eb5eccc07097c5fcf940967f33ab84a7d47c96fe /libgo/go/encoding/json | |
parent | f83fa0bf8f411697ec908cfa86ee6faf4cd9c476 (diff) | |
download | gcc-df4aa89a5e7acb315655f193e7f549e8d32367e2.zip gcc-df4aa89a5e7acb315655f193e7f549e8d32367e2.tar.gz gcc-df4aa89a5e7acb315655f193e7f549e8d32367e2.tar.bz2 |
libgo: Update to weekly.2011-12-22.
From-SVN: r183150
Diffstat (limited to 'libgo/go/encoding/json')
-rw-r--r-- | libgo/go/encoding/json/bench_test.go | 10 | ||||
-rw-r--r-- | libgo/go/encoding/json/decode.go | 71 | ||||
-rw-r--r-- | libgo/go/encoding/json/decode_test.go | 39 | ||||
-rw-r--r-- | libgo/go/encoding/json/encode.go | 25 |
4 files changed, 99 insertions, 46 deletions
diff --git a/libgo/go/encoding/json/bench_test.go b/libgo/go/encoding/json/bench_test.go index f0c5201..333c1c0 100644 --- a/libgo/go/encoding/json/bench_test.go +++ b/libgo/go/encoding/json/bench_test.go @@ -84,7 +84,7 @@ func BenchmarkCodeEncoder(b *testing.B) { enc := NewEncoder(ioutil.Discard) for i := 0; i < b.N; i++ { if err := enc.Encode(&codeStruct); err != nil { - panic(err) + b.Fatal("Encode:", err) } } b.SetBytes(int64(len(codeJSON))) @@ -98,7 +98,7 @@ func BenchmarkCodeMarshal(b *testing.B) { } for i := 0; i < b.N; i++ { if _, err := Marshal(&codeStruct); err != nil { - panic(err) + b.Fatal("Marshal:", err) } } b.SetBytes(int64(len(codeJSON))) @@ -120,7 +120,7 @@ func BenchmarkCodeDecoder(b *testing.B) { buf.WriteByte('\n') buf.WriteByte('\n') if err := dec.Decode(&r); err != nil { - panic(err) + b.Fatal("Decode:", err) } } b.SetBytes(int64(len(codeJSON))) @@ -135,7 +135,7 @@ func BenchmarkCodeUnmarshal(b *testing.B) { for i := 0; i < b.N; i++ { var r codeResponse if err := Unmarshal(codeJSON, &r); err != nil { - panic(err) + b.Fatal("Unmmarshal:", err) } } b.SetBytes(int64(len(codeJSON))) @@ -150,7 +150,7 @@ func BenchmarkCodeUnmarshalReuse(b *testing.B) { var r codeResponse for i := 0; i < b.N; i++ { if err := Unmarshal(codeJSON, &r); err != nil { - panic(err) + b.Fatal("Unmmarshal:", err) } } b.SetBytes(int64(len(codeJSON))) diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go index 0a70092..8287b33 100644 --- a/libgo/go/encoding/json/decode.go +++ b/libgo/go/encoding/json/decode.go @@ -228,7 +228,9 @@ func (d *decodeState) value(v reflect.Value) { // Feed in an empty string - the shortest, simplest value - // so that it knows we got to the end of the value. if d.scan.redo { - panic("redo") + // rewind. + d.scan.redo = false + d.scan.step = stateBeginValue } d.scan.step(&d.scan, '"') d.scan.step(&d.scan, '"') @@ -317,25 +319,22 @@ func (d *decodeState) array(v reflect.Value) { } v = pv - // Decoding into nil interface? Switch to non-reflect code. - iv := v - ok := iv.Kind() == reflect.Interface - if ok { - iv.Set(reflect.ValueOf(d.arrayInterface())) - return - } - // Check type of target. - av := v - if av.Kind() != reflect.Array && av.Kind() != reflect.Slice { + switch v.Kind() { + default: d.saveError(&UnmarshalTypeError{"array", v.Type()}) d.off-- d.next() return + case reflect.Interface: + // Decoding into nil interface? Switch to non-reflect code. + v.Set(reflect.ValueOf(d.arrayInterface())) + return + case reflect.Array: + case reflect.Slice: + break } - sv := v - i := 0 for { // Look ahead for ] - can only happen on first iteration. @@ -349,23 +348,25 @@ func (d *decodeState) array(v reflect.Value) { d.scan.undo(op) // Get element of array, growing if necessary. - if i >= av.Cap() && sv.IsValid() { - newcap := sv.Cap() + sv.Cap()/2 - if newcap < 4 { - newcap = 4 + if v.Kind() == reflect.Slice { + // Grow slice if necessary + if i >= v.Cap() { + newcap := v.Cap() + v.Cap()/2 + if newcap < 4 { + newcap = 4 + } + newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) + reflect.Copy(newv, v) + v.Set(newv) + } + if i >= v.Len() { + v.SetLen(i + 1) } - newv := reflect.MakeSlice(sv.Type(), sv.Len(), newcap) - reflect.Copy(newv, sv) - sv.Set(newv) - } - if i >= av.Len() && sv.IsValid() { - // Must be slice; gave up on array during i >= av.Cap(). - sv.SetLen(i + 1) } - // Decode into element. - if i < av.Len() { - d.value(av.Index(i)) + if i < v.Len() { + // Decode into element. + d.value(v.Index(i)) } else { // Ran out of fixed array: skip. d.value(reflect.Value{}) @@ -382,19 +383,19 @@ func (d *decodeState) array(v reflect.Value) { } } - if i < av.Len() { - if !sv.IsValid() { + if i < v.Len() { + if v.Kind() == reflect.Array { // Array. Zero the rest. - z := reflect.Zero(av.Type().Elem()) - for ; i < av.Len(); i++ { - av.Index(i).Set(z) + z := reflect.Zero(v.Type().Elem()) + for ; i < v.Len(); i++ { + v.Index(i).Set(z) } } else { - sv.SetLen(i) + v.SetLen(i) } } - if i == 0 && av.Kind() == reflect.Slice && sv.IsNil() { - sv.Set(reflect.MakeSlice(sv.Type(), 0, 0)) + if i == 0 && v.Kind() == reflect.Slice { + v.Set(reflect.MakeSlice(v.Type(), 0, 0)) } } diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go index bf3953e..05c8a06 100644 --- a/libgo/go/encoding/json/decode_test.go +++ b/libgo/go/encoding/json/decode_test.go @@ -6,6 +6,7 @@ package json import ( "bytes" + "fmt" "reflect" "strings" "testing" @@ -73,6 +74,12 @@ var unmarshalTests = []unmarshalTest{ // syntax errors {`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}}, + {`[1, 2, 3+]`, nil, nil, &SyntaxError{"invalid character '+' after array element", 9}}, + + // array tests + {`[1, 2, 3]`, new([3]int), [3]int{1, 2, 3}, nil}, + {`[1, 2, 3]`, new([1]int), [1]int{1}, nil}, + {`[1, 2, 3]`, new([5]int), [5]int{1, 2, 3, 0, 0}, nil}, // composite tests {allValueIndent, new(All), allValue, nil}, @@ -242,6 +249,38 @@ func TestHTMLEscape(t *testing.T) { } } +// WrongString is a struct that's misusing the ,string modifier. +type WrongString struct { + Message string `json:"result,string"` +} + +type wrongStringTest struct { + in, err string +} + +// TODO(bradfitz): as part of Issue 2331, fix these tests' expected +// error values to be helpful, rather than the confusing messages they +// are now. +var wrongStringTests = []wrongStringTest{ + {`{"result":"x"}`, "JSON decoder out of sync - data changing underfoot?"}, + {`{"result":"foo"}`, "json: cannot unmarshal bool into Go value of type string"}, + {`{"result":"123"}`, "json: cannot unmarshal number into Go value of type string"}, +} + +// If people misuse the ,string modifier, the error message should be +// helpful, telling the user that they're doing it wrong. +func TestErrorMessageFromMisusedString(t *testing.T) { + for n, tt := range wrongStringTests { + r := strings.NewReader(tt.in) + var s WrongString + err := NewDecoder(r).Decode(&s) + got := fmt.Sprintf("%v", err) + if got != tt.err { + t.Errorf("%d. got err = %q, want %q", n, got, tt.err) + } + } +} + func noSpace(c rune) rune { if isSpace(c) { return -1 diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index ff8e80c..3d2f4fc 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -197,6 +197,7 @@ var hex = "0123456789abcdef" // An encodeState encodes JSON into a bytes.Buffer. type encodeState struct { bytes.Buffer // accumulated output + scratch [64]byte } func (e *encodeState) marshal(v interface{}) (err error) { @@ -275,14 +276,26 @@ func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - writeString(e, strconv.FormatInt(v.Int(), 10)) - + b := strconv.AppendInt(e.scratch[:0], v.Int(), 10) + if quoted { + writeString(e, string(b)) + } else { + e.Write(b) + } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - writeString(e, strconv.FormatUint(v.Uint(), 10)) - + b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10) + if quoted { + writeString(e, string(b)) + } else { + e.Write(b) + } case reflect.Float32, reflect.Float64: - writeString(e, strconv.FormatFloat(v.Float(), 'g', -1, v.Type().Bits())) - + b := strconv.AppendFloat(e.scratch[:0], v.Float(), 'g', -1, v.Type().Bits()) + if quoted { + writeString(e, string(b)) + } else { + e.Write(b) + } case reflect.String: if quoted { sb, err := Marshal(v.String()) |