Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(588)

Side by Side Diff: src/pkg/encoding/json/encode.go

Issue 5416046: code review 5416046: json: speed up encoding, caching reflect calls (Closed)
Patch Set: diff -r 4382717b7ffc https://go.googlecode.com/hg/ Created 13 years, 3 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2010 The Go Authors. All rights reserved. 1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 // Package json implements encoding and decoding of JSON objects as defined in 5 // Package json implements encoding and decoding of JSON objects as defined in
6 // RFC 4627. 6 // RFC 4627.
7 // 7 //
8 // See "JSON and Go" for an introduction to this package: 8 // See "JSON and Go" for an introduction to this package:
9 // http://blog.golang.org/2011/01/json-and-go.html 9 // http://blog.golang.org/2011/01/json-and-go.html
10 package json 10 package json
11 11
12 import ( 12 import (
13 "bytes" 13 "bytes"
14 "encoding/base64" 14 "encoding/base64"
15 "reflect" 15 "reflect"
16 "runtime" 16 "runtime"
17 "sort" 17 "sort"
18 "strconv" 18 "strconv"
19 "sync"
19 "unicode" 20 "unicode"
20 "unicode/utf8" 21 "unicode/utf8"
21 ) 22 )
22 23
23 // Marshal returns the JSON encoding of v. 24 // Marshal returns the JSON encoding of v.
24 // 25 //
25 // Marshal traverses the value v recursively. 26 // Marshal traverses the value v recursively.
26 // If an encountered value implements the Marshaler interface 27 // If an encountered value implements the Marshaler interface
27 // and is not a nil pointer, Marshal calls its MarshalJSON method 28 // and is not a nil pointer, Marshal calls its MarshalJSON method
28 // to produce JSON. The nil pointer exception is not strictly necessary 29 // to produce JSON. The nil pointer exception is not strictly necessary
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 type interfaceOrPtrValue interface { 190 type interfaceOrPtrValue interface {
190 IsNil() bool 191 IsNil() bool
191 Elem() reflect.Value 192 Elem() reflect.Value
192 } 193 }
193 194
194 var hex = "0123456789abcdef" 195 var hex = "0123456789abcdef"
195 196
196 // An encodeState encodes JSON into a bytes.Buffer. 197 // An encodeState encodes JSON into a bytes.Buffer.
197 type encodeState struct { 198 type encodeState struct {
198 bytes.Buffer // accumulated output 199 bytes.Buffer // accumulated output
200
201 // tagCache is a cache of seen StructTag's "json" fields.
rsc 2011/11/21 15:06:51 Unused, I believe.
bradfitz 2011/11/21 15:25:54 Indeed.
202 // the map starts out nil.
203 tagCache map[reflect.StructTag]string
199 } 204 }
200 205
201 func (e *encodeState) marshal(v interface{}) (err error) { 206 func (e *encodeState) marshal(v interface{}) (err error) {
202 defer func() { 207 defer func() {
203 if r := recover(); r != nil { 208 if r := recover(); r != nil {
204 if _, ok := r.(runtime.Error); ok { 209 if _, ok := r.(runtime.Error); ok {
205 panic(r) 210 panic(r)
206 } 211 }
207 err = r.(error) 212 err = r.(error)
208 } 213 }
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 if err != nil { 293 if err != nil {
289 e.error(err) 294 e.error(err)
290 } 295 }
291 e.string(string(sb)) 296 e.string(string(sb))
292 } else { 297 } else {
293 e.string(v.String()) 298 e.string(v.String())
294 } 299 }
295 300
296 case reflect.Struct: 301 case reflect.Struct:
297 e.WriteByte('{') 302 e.WriteByte('{')
298 t := v.Type()
299 n := v.NumField()
300 first := true 303 first := true
301 » » for i := 0; i < n; i++ { 304 » » for _, ef := range encodeFields(v.Type()) {
302 » » » f := t.Field(i) 305 » » » fieldValue := v.Field(ef.i)
303 » » » if f.PkgPath != "" { 306 » » » if ef.omitEmpty && isEmptyValue(fieldValue) {
304 » » » » continue
305 » » » }
306 » » » tag, omitEmpty, quoted := f.Name, false, false
307 » » » if tv := f.Tag.Get("json"); tv != "" {
308 » » » » if tv == "-" {
309 » » » » » continue
310 » » » » }
311 » » » » name, opts := parseTag(tv)
312 » » » » if isValidTag(name) {
313 » » » » » tag = name
314 » » » » }
315 » » » » omitEmpty = opts.Contains("omitempty")
316 » » » » quoted = opts.Contains("string")
317 » » » }
318 » » » fieldValue := v.Field(i)
319 » » » if omitEmpty && isEmptyValue(fieldValue) {
320 continue 307 continue
321 } 308 }
322 if first { 309 if first {
323 first = false 310 first = false
324 } else { 311 } else {
325 e.WriteByte(',') 312 e.WriteByte(',')
326 } 313 }
327 » » » e.string(tag) 314 » » » e.string(ef.tag)
328 e.WriteByte(':') 315 e.WriteByte(':')
329 » » » e.reflectValueQuoted(fieldValue, quoted) 316 » » » e.reflectValueQuoted(fieldValue, ef.quoted)
330 } 317 }
331 e.WriteByte('}') 318 e.WriteByte('}')
332 319
333 case reflect.Map: 320 case reflect.Map:
334 if v.Type().Key().Kind() != reflect.String { 321 if v.Type().Key().Kind() != reflect.String {
335 e.error(&UnsupportedTypeError{v.Type()}) 322 e.error(&UnsupportedTypeError{v.Type()})
336 } 323 }
337 if v.IsNil() { 324 if v.IsNil() {
338 e.WriteString("null") 325 e.WriteString("null")
339 break 326 break
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 e.error(&InvalidUTF8Error{s}) 450 e.error(&InvalidUTF8Error{s})
464 } 451 }
465 i += size 452 i += size
466 } 453 }
467 if start < len(s) { 454 if start < len(s) {
468 e.WriteString(s[start:]) 455 e.WriteString(s[start:])
469 } 456 }
470 e.WriteByte('"') 457 e.WriteByte('"')
471 return e.Len() - len0, nil 458 return e.Len() - len0, nil
472 } 459 }
460
461 // encodeField contains information about how to encode a field of a
462 // struct.
463 type encodeField struct {
464 i int // field index in struct
465 tag string
466 quoted bool
467 omitEmpty bool
468 }
469
470 var (
471 typeCacheLock sync.RWMutex
472 encodeFieldsCache = make(map[reflect.Type][]encodeField)
473 )
474
475 // encodeFields returns a slice of encodeField for a given
476 // struct type.
477 func encodeFields(t reflect.Type) []encodeField {
478 typeCacheLock.RLock()
479 defer typeCacheLock.RUnlock()
rsc 2011/11/21 15:06:51 The locking here is suspect. I think you need: t
bradfitz 2011/11/21 15:25:54 uh, whoops.
480 fs, ok := encodeFieldsCache[t]
481 if ok {
482 return fs
483 }
484 v := reflect.Zero(t)
485 n := v.NumField()
486 for i := 0; i < n; i++ {
487 f := t.Field(i)
488 if f.PkgPath != "" {
489 continue
490 }
491 var ef encodeField
492 ef.i = i
493 ef.tag = f.Name
494
495 tv := f.Tag.Get("json")
496 if tv != "" {
497 if tv == "-" {
498 continue
499 }
500 name, opts := parseTag(tv)
501 if isValidTag(name) {
502 ef.tag = name
503 }
504 ef.omitEmpty = opts.Contains("omitempty")
505 ef.quoted = opts.Contains("string")
506 }
507 fs = append(fs, ef)
508 }
509 encodeFieldsCache[t] = fs
510 return fs
511 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b