summaryrefslogtreecommitdiff
path: root/vendor/github.com/pquerna/ffjson
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/pquerna/ffjson')
-rw-r--r--vendor/github.com/pquerna/ffjson/inception/decoder.go323
-rw-r--r--vendor/github.com/pquerna/ffjson/inception/decoder_tpl.go773
-rw-r--r--vendor/github.com/pquerna/ffjson/inception/encoder.go544
-rw-r--r--vendor/github.com/pquerna/ffjson/inception/encoder_tpl.go73
-rw-r--r--vendor/github.com/pquerna/ffjson/inception/inception.go160
-rw-r--r--vendor/github.com/pquerna/ffjson/inception/reflect.go290
-rw-r--r--vendor/github.com/pquerna/ffjson/inception/tags.go79
-rw-r--r--vendor/github.com/pquerna/ffjson/inception/template.go60
-rw-r--r--vendor/github.com/pquerna/ffjson/inception/writerstack.go65
-rw-r--r--vendor/github.com/pquerna/ffjson/shared/options.go51
10 files changed, 2418 insertions, 0 deletions
diff --git a/vendor/github.com/pquerna/ffjson/inception/decoder.go b/vendor/github.com/pquerna/ffjson/inception/decoder.go
new file mode 100644
index 000000000..908347a32
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/inception/decoder.go
@@ -0,0 +1,323 @@
+/**
+ * Copyright 2014 Paul Querna
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package ffjsoninception
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+
+ "github.com/pquerna/ffjson/shared"
+)
+
+var validValues []string = []string{
+ "FFTok_left_brace",
+ "FFTok_left_bracket",
+ "FFTok_integer",
+ "FFTok_double",
+ "FFTok_string",
+ "FFTok_bool",
+ "FFTok_null",
+}
+
+func CreateUnmarshalJSON(ic *Inception, si *StructInfo) error {
+ out := ""
+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
+ if len(si.Fields) > 0 {
+ ic.OutputImports[`"bytes"`] = true
+ }
+ ic.OutputImports[`"fmt"`] = true
+
+ out += tplStr(decodeTpl["header"], header{
+ IC: ic,
+ SI: si,
+ })
+
+ out += tplStr(decodeTpl["ujFunc"], ujFunc{
+ SI: si,
+ IC: ic,
+ ValidValues: validValues,
+ ResetFields: ic.ResetFields,
+ })
+
+ ic.OutputFuncs = append(ic.OutputFuncs, out)
+
+ return nil
+}
+
+func handleField(ic *Inception, name string, typ reflect.Type, ptr bool, quoted bool) string {
+ return handleFieldAddr(ic, name, false, typ, ptr, quoted)
+}
+
+func handleFieldAddr(ic *Inception, name string, takeAddr bool, typ reflect.Type, ptr bool, quoted bool) string {
+ out := fmt.Sprintf("/* handler: %s type=%v kind=%v quoted=%t*/\n", name, typ, typ.Kind(), quoted)
+
+ umlx := typ.Implements(unmarshalFasterType) || typeInInception(ic, typ, shared.MustDecoder)
+ umlx = umlx || reflect.PtrTo(typ).Implements(unmarshalFasterType)
+
+ umlstd := typ.Implements(unmarshalerType) || reflect.PtrTo(typ).Implements(unmarshalerType)
+
+ out += tplStr(decodeTpl["handleUnmarshaler"], handleUnmarshaler{
+ IC: ic,
+ Name: name,
+ Typ: typ,
+ Ptr: reflect.Ptr,
+ TakeAddr: takeAddr || ptr,
+ UnmarshalJSONFFLexer: umlx,
+ Unmarshaler: umlstd,
+ })
+
+ if umlx || umlstd {
+ return out
+ }
+
+ // TODO(pquerna): generic handling of token type mismatching struct type
+ switch typ.Kind() {
+ case reflect.Int,
+ reflect.Int8,
+ reflect.Int16,
+ reflect.Int32,
+ reflect.Int64:
+
+ allowed := buildTokens(quoted, "FFTok_string", "FFTok_integer", "FFTok_null")
+ out += getAllowTokens(typ.Name(), allowed...)
+
+ out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseInt")
+
+ case reflect.Uint,
+ reflect.Uint8,
+ reflect.Uint16,
+ reflect.Uint32,
+ reflect.Uint64:
+
+ allowed := buildTokens(quoted, "FFTok_string", "FFTok_integer", "FFTok_null")
+ out += getAllowTokens(typ.Name(), allowed...)
+
+ out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseUint")
+
+ case reflect.Float32,
+ reflect.Float64:
+
+ allowed := buildTokens(quoted, "FFTok_string", "FFTok_double", "FFTok_integer", "FFTok_null")
+ out += getAllowTokens(typ.Name(), allowed...)
+
+ out += getNumberHandler(ic, name, takeAddr || ptr, typ, "ParseFloat")
+
+ case reflect.Bool:
+ ic.OutputImports[`"bytes"`] = true
+ ic.OutputImports[`"errors"`] = true
+
+ allowed := buildTokens(quoted, "FFTok_string", "FFTok_bool", "FFTok_null")
+ out += getAllowTokens(typ.Name(), allowed...)
+
+ out += tplStr(decodeTpl["handleBool"], handleBool{
+ Name: name,
+ Typ: typ,
+ TakeAddr: takeAddr || ptr,
+ })
+
+ case reflect.Ptr:
+ out += tplStr(decodeTpl["handlePtr"], handlePtr{
+ IC: ic,
+ Name: name,
+ Typ: typ,
+ Quoted: quoted,
+ })
+
+ case reflect.Array,
+ reflect.Slice:
+ out += getArrayHandler(ic, name, typ, ptr)
+
+ case reflect.String:
+ // Is it a json.Number?
+ if typ.PkgPath() == "encoding/json" && typ.Name() == "Number" {
+ // Fall back to json package to rely on the valid number check.
+ // See: https://github.com/golang/go/blob/f05c3aa24d815cd3869153750c9875e35fc48a6e/src/encoding/json/decode.go#L897
+ ic.OutputImports[`"encoding/json"`] = true
+ out += tplStr(decodeTpl["handleFallback"], handleFallback{
+ Name: name,
+ Typ: typ,
+ Kind: typ.Kind(),
+ })
+ } else {
+ out += tplStr(decodeTpl["handleString"], handleString{
+ IC: ic,
+ Name: name,
+ Typ: typ,
+ TakeAddr: takeAddr || ptr,
+ Quoted: quoted,
+ })
+ }
+ case reflect.Interface:
+ ic.OutputImports[`"encoding/json"`] = true
+ out += tplStr(decodeTpl["handleFallback"], handleFallback{
+ Name: name,
+ Typ: typ,
+ Kind: typ.Kind(),
+ })
+ case reflect.Map:
+ out += tplStr(decodeTpl["handleObject"], handleObject{
+ IC: ic,
+ Name: name,
+ Typ: typ,
+ Ptr: reflect.Ptr,
+ TakeAddr: takeAddr || ptr,
+ })
+ default:
+ ic.OutputImports[`"encoding/json"`] = true
+ out += tplStr(decodeTpl["handleFallback"], handleFallback{
+ Name: name,
+ Typ: typ,
+ Kind: typ.Kind(),
+ })
+ }
+
+ return out
+}
+
+func getArrayHandler(ic *Inception, name string, typ reflect.Type, ptr bool) string {
+ if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
+ ic.OutputImports[`"encoding/base64"`] = true
+ useReflectToSet := false
+ if typ.Elem().Name() != "byte" {
+ ic.OutputImports[`"reflect"`] = true
+ useReflectToSet = true
+ }
+
+ return tplStr(decodeTpl["handleByteSlice"], handleArray{
+ IC: ic,
+ Name: name,
+ Typ: typ,
+ Ptr: reflect.Ptr,
+ UseReflectToSet: useReflectToSet,
+ })
+ }
+
+ if typ.Elem().Kind() == reflect.Struct && typ.Elem().Name() != "" {
+ goto sliceOrArray
+ }
+
+ if (typ.Elem().Kind() == reflect.Struct || typ.Elem().Kind() == reflect.Map) ||
+ typ.Elem().Kind() == reflect.Array || typ.Elem().Kind() == reflect.Slice &&
+ typ.Elem().Name() == "" {
+ ic.OutputImports[`"encoding/json"`] = true
+
+ return tplStr(decodeTpl["handleFallback"], handleFallback{
+ Name: name,
+ Typ: typ,
+ Kind: typ.Kind(),
+ })
+ }
+
+sliceOrArray:
+
+ if typ.Kind() == reflect.Array {
+ return tplStr(decodeTpl["handleArray"], handleArray{
+ IC: ic,
+ Name: name,
+ Typ: typ,
+ IsPtr: ptr,
+ Ptr: reflect.Ptr,
+ })
+ }
+
+ return tplStr(decodeTpl["handleSlice"], handleArray{
+ IC: ic,
+ Name: name,
+ Typ: typ,
+ IsPtr: ptr,
+ Ptr: reflect.Ptr,
+ })
+}
+
+func getAllowTokens(name string, tokens ...string) string {
+ return tplStr(decodeTpl["allowTokens"], allowTokens{
+ Name: name,
+ Tokens: tokens,
+ })
+}
+
+func getNumberHandler(ic *Inception, name string, takeAddr bool, typ reflect.Type, parsefunc string) string {
+ return tplStr(decodeTpl["handlerNumeric"], handlerNumeric{
+ IC: ic,
+ Name: name,
+ ParseFunc: parsefunc,
+ TakeAddr: takeAddr,
+ Typ: typ,
+ })
+}
+
+func getNumberSize(typ reflect.Type) string {
+ return fmt.Sprintf("%d", typ.Bits())
+}
+
+func getType(ic *Inception, name string, typ reflect.Type) string {
+ s := typ.Name()
+
+ if typ.PkgPath() != "" && typ.PkgPath() != ic.PackagePath {
+ path := removeVendor(typ.PkgPath())
+ ic.OutputImports[`"`+path+`"`] = true
+ s = typ.String()
+ }
+
+ if s == "" {
+ return typ.String()
+ }
+
+ return s
+}
+
+// removeVendor removes everything before and including a '/vendor/'
+// substring in the package path.
+// This is needed becuase that full path can't be used in the
+// import statement.
+func removeVendor(path string) string {
+ i := strings.Index(path, "/vendor/")
+ if i == -1 {
+ return path
+ }
+ return path[i+8:]
+}
+
+func buildTokens(containsOptional bool, optional string, required ...string) []string {
+ if containsOptional {
+ return append(required, optional)
+ }
+
+ return required
+}
+
+func unquoteField(quoted bool) string {
+ // The outer quote of a string is already stripped out by
+ // the lexer. We need to check if the inner string is also
+ // quoted. If so, we will decode it as json string. If decoding
+ // fails, we will use the original string
+ if quoted {
+ return `
+ unquoted, ok := fflib.UnquoteBytes(outBuf)
+ if ok {
+ outBuf = unquoted
+ }
+ `
+ }
+ return ""
+}
+
+func getTmpVarFor(name string) string {
+ return "tmp" + strings.Replace(strings.Title(name), ".", "", -1)
+}
diff --git a/vendor/github.com/pquerna/ffjson/inception/decoder_tpl.go b/vendor/github.com/pquerna/ffjson/inception/decoder_tpl.go
new file mode 100644
index 000000000..098506122
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/inception/decoder_tpl.go
@@ -0,0 +1,773 @@
+/**
+ * Copyright 2014 Paul Querna
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package ffjsoninception
+
+import (
+ "reflect"
+ "strconv"
+ "text/template"
+)
+
+var decodeTpl map[string]*template.Template
+
+func init() {
+ decodeTpl = make(map[string]*template.Template)
+
+ funcs := map[string]string{
+ "handlerNumeric": handlerNumericTxt,
+ "allowTokens": allowTokensTxt,
+ "handleFallback": handleFallbackTxt,
+ "handleString": handleStringTxt,
+ "handleObject": handleObjectTxt,
+ "handleArray": handleArrayTxt,
+ "handleSlice": handleSliceTxt,
+ "handleByteSlice": handleByteSliceTxt,
+ "handleBool": handleBoolTxt,
+ "handlePtr": handlePtrTxt,
+ "header": headerTxt,
+ "ujFunc": ujFuncTxt,
+ "handleUnmarshaler": handleUnmarshalerTxt,
+ }
+
+ tplFuncs := template.FuncMap{
+ "getAllowTokens": getAllowTokens,
+ "getNumberSize": getNumberSize,
+ "getType": getType,
+ "handleField": handleField,
+ "handleFieldAddr": handleFieldAddr,
+ "unquoteField": unquoteField,
+ "getTmpVarFor": getTmpVarFor,
+ }
+
+ for k, v := range funcs {
+ decodeTpl[k] = template.Must(template.New(k).Funcs(tplFuncs).Parse(v))
+ }
+}
+
+type handlerNumeric struct {
+ IC *Inception
+ Name string
+ ParseFunc string
+ Typ reflect.Type
+ TakeAddr bool
+}
+
+var handlerNumericTxt = `
+{
+ {{$ic := .IC}}
+
+ if tok == fflib.FFTok_null {
+ {{if eq .TakeAddr true}}
+ {{.Name}} = nil
+ {{end}}
+ } else {
+ {{if eq .ParseFunc "ParseFloat" }}
+ tval, err := fflib.{{ .ParseFunc}}(fs.Output.Bytes(), {{getNumberSize .Typ}})
+ {{else}}
+ tval, err := fflib.{{ .ParseFunc}}(fs.Output.Bytes(), 10, {{getNumberSize .Typ}})
+ {{end}}
+
+ if err != nil {
+ return fs.WrapErr(err)
+ }
+ {{if eq .TakeAddr true}}
+ ttypval := {{getType $ic .Name .Typ}}(tval)
+ {{.Name}} = &ttypval
+ {{else}}
+ {{.Name}} = {{getType $ic .Name .Typ}}(tval)
+ {{end}}
+ }
+}
+`
+
+type allowTokens struct {
+ Name string
+ Tokens []string
+}
+
+var allowTokensTxt = `
+{
+ if {{range $index, $element := .Tokens}}{{if ne $index 0 }}&&{{end}} tok != fflib.{{$element}}{{end}} {
+ return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for {{.Name}}", tok))
+ }
+}
+`
+
+type handleFallback struct {
+ Name string
+ Typ reflect.Type
+ Kind reflect.Kind
+}
+
+var handleFallbackTxt = `
+{
+ /* Falling back. type={{printf "%v" .Typ}} kind={{printf "%v" .Kind}} */
+ tbuf, err := fs.CaptureField(tok)
+ if err != nil {
+ return fs.WrapErr(err)
+ }
+
+ err = json.Unmarshal(tbuf, &{{.Name}})
+ if err != nil {
+ return fs.WrapErr(err)
+ }
+}
+`
+
+type handleString struct {
+ IC *Inception
+ Name string
+ Typ reflect.Type
+ TakeAddr bool
+ Quoted bool
+}
+
+var handleStringTxt = `
+{
+ {{$ic := .IC}}
+
+ {{getAllowTokens .Typ.Name "FFTok_string" "FFTok_null"}}
+ if tok == fflib.FFTok_null {
+ {{if eq .TakeAddr true}}
+ {{.Name}} = nil
+ {{end}}
+ } else {
+ {{if eq .TakeAddr true}}
+ var tval {{getType $ic .Name .Typ}}
+ outBuf := fs.Output.Bytes()
+ {{unquoteField .Quoted}}
+ tval = {{getType $ic .Name .Typ}}(string(outBuf))
+ {{.Name}} = &tval
+ {{else}}
+ outBuf := fs.Output.Bytes()
+ {{unquoteField .Quoted}}
+ {{.Name}} = {{getType $ic .Name .Typ}}(string(outBuf))
+ {{end}}
+ }
+}
+`
+
+type handleObject struct {
+ IC *Inception
+ Name string
+ Typ reflect.Type
+ Ptr reflect.Kind
+ TakeAddr bool
+}
+
+var handleObjectTxt = `
+{
+ {{$ic := .IC}}
+ {{getAllowTokens .Typ.Name "FFTok_left_bracket" "FFTok_null"}}
+ if tok == fflib.FFTok_null {
+ {{.Name}} = nil
+ } else {
+
+ {{if eq .TakeAddr true}}
+ {{if eq .Typ.Elem.Kind .Ptr }}
+ {{if eq .Typ.Key.Kind .Ptr }}
+ var tval = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0)
+ {{else}}
+ var tval = make(map[{{getType $ic .Name .Typ.Key}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0)
+ {{end}}
+ {{else}}
+ {{if eq .Typ.Key.Kind .Ptr }}
+ var tval = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]{{getType $ic .Name .Typ.Elem}}, 0)
+ {{else}}
+ var tval = make(map[{{getType $ic .Name .Typ.Key}}]{{getType $ic .Name .Typ.Elem}}, 0)
+ {{end}}
+ {{end}}
+ {{else}}
+ {{if eq .Typ.Elem.Kind .Ptr }}
+ {{if eq .Typ.Key.Kind .Ptr }}
+ {{.Name}} = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0)
+ {{else}}
+ {{.Name}} = make(map[{{getType $ic .Name .Typ.Key}}]*{{getType $ic .Name .Typ.Elem.Elem}}, 0)
+ {{end}}
+ {{else}}
+ {{if eq .Typ.Key.Kind .Ptr }}
+ {{.Name}} = make(map[*{{getType $ic .Name .Typ.Key.Elem}}]{{getType $ic .Name .Typ.Elem}}, 0)
+ {{else}}
+ {{.Name}} = make(map[{{getType $ic .Name .Typ.Key}}]{{getType $ic .Name .Typ.Elem}}, 0)
+ {{end}}
+ {{end}}
+ {{end}}
+
+ wantVal := true
+
+ for {
+ {{$keyPtr := false}}
+ {{if eq .Typ.Key.Kind .Ptr }}
+ {{$keyPtr := true}}
+ var k *{{getType $ic .Name .Typ.Key.Elem}}
+ {{else}}
+ var k {{getType $ic .Name .Typ.Key}}
+ {{end}}
+
+ {{$valPtr := false}}
+ {{$tmpVar := getTmpVarFor .Name}}
+ {{if eq .Typ.Elem.Kind .Ptr }}
+ {{$valPtr := true}}
+ var {{$tmpVar}} *{{getType $ic .Name .Typ.Elem.Elem}}
+ {{else}}
+ var {{$tmpVar}} {{getType $ic .Name .Typ.Elem}}
+ {{end}}
+
+ tok = fs.Scan()
+ if tok == fflib.FFTok_error {
+ goto tokerror
+ }
+ if tok == fflib.FFTok_right_bracket {
+ break
+ }
+
+ if tok == fflib.FFTok_comma {
+ if wantVal == true {
+ // TODO(pquerna): this isn't an ideal error message, this handles
+ // things like [,,,] as an array value.
+ return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
+ }
+ continue
+ } else {
+ wantVal = true
+ }
+
+ {{handleField .IC "k" .Typ.Key $keyPtr false}}
+
+ // Expect ':' after key
+ tok = fs.Scan()
+ if tok != fflib.FFTok_colon {
+ return fs.WrapErr(fmt.Errorf("wanted colon token, but got token: %v", tok))
+ }
+
+ tok = fs.Scan()
+ {{handleField .IC $tmpVar .Typ.Elem $valPtr false}}
+
+ {{if eq .TakeAddr true}}
+ tval[k] = {{$tmpVar}}
+ {{else}}
+ {{.Name}}[k] = {{$tmpVar}}
+ {{end}}
+ wantVal = false
+ }
+
+ {{if eq .TakeAddr true}}
+ {{.Name}} = &tval
+ {{end}}
+ }
+}
+`
+
+type handleArray struct {
+ IC *Inception
+ Name string
+ Typ reflect.Type
+ Ptr reflect.Kind
+ UseReflectToSet bool
+ IsPtr bool
+}
+
+var handleArrayTxt = `
+{
+ {{$ic := .IC}}
+ {{getAllowTokens .Typ.Name "FFTok_left_brace" "FFTok_null"}}
+ {{if eq .Typ.Elem.Kind .Ptr}}
+ {{.Name}} = [{{.Typ.Len}}]*{{getType $ic .Name .Typ.Elem.Elem}}{}
+ {{else}}
+ {{.Name}} = [{{.Typ.Len}}]{{getType $ic .Name .Typ.Elem}}{}
+ {{end}}
+ if tok != fflib.FFTok_null {
+ wantVal := true
+
+ idx := 0
+ for {
+ {{$ptr := false}}
+ {{$tmpVar := getTmpVarFor .Name}}
+ {{if eq .Typ.Elem.Kind .Ptr }}
+ {{$ptr := true}}
+ var {{$tmpVar}} *{{getType $ic .Name .Typ.Elem.Elem}}
+ {{else}}
+ var {{$tmpVar}} {{getType $ic .Name .Typ.Elem}}
+ {{end}}
+
+ tok = fs.Scan()
+ if tok == fflib.FFTok_error {
+ goto tokerror
+ }
+ if tok == fflib.FFTok_right_brace {
+ break
+ }
+
+ if tok == fflib.FFTok_comma {
+ if wantVal == true {
+ // TODO(pquerna): this isn't an ideal error message, this handles
+ // things like [,,,] as an array value.
+ return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
+ }
+ continue
+ } else {
+ wantVal = true
+ }
+
+ {{handleField .IC $tmpVar .Typ.Elem $ptr false}}
+
+ // Standard json.Unmarshal ignores elements out of array bounds,
+ // that what we do as well.
+ if idx < {{.Typ.Len}} {
+ {{.Name}}[idx] = {{$tmpVar}}
+ idx++
+ }
+
+ wantVal = false
+ }
+ }
+}
+`
+
+var handleSliceTxt = `
+{
+ {{$ic := .IC}}
+ {{getAllowTokens .Typ.Name "FFTok_left_brace" "FFTok_null"}}
+ if tok == fflib.FFTok_null {
+ {{.Name}} = nil
+ } else {
+ {{if eq .Typ.Elem.Kind .Ptr }}
+ {{if eq .IsPtr true}}
+ {{.Name}} = &[]*{{getType $ic .Name .Typ.Elem.Elem}}{}
+ {{else}}
+ {{.Name}} = []*{{getType $ic .Name .Typ.Elem.Elem}}{}
+ {{end}}
+ {{else}}
+ {{if eq .IsPtr true}}
+ {{.Name}} = &[]{{getType $ic .Name .Typ.Elem}}{}
+ {{else}}
+ {{.Name}} = []{{getType $ic .Name .Typ.Elem}}{}
+ {{end}}
+ {{end}}
+
+ wantVal := true
+
+ for {
+ {{$ptr := false}}
+ {{$tmpVar := getTmpVarFor .Name}}
+ {{if eq .Typ.Elem.Kind .Ptr }}
+ {{$ptr := true}}
+ var {{$tmpVar}} *{{getType $ic .Name .Typ.Elem.Elem}}
+ {{else}}
+ var {{$tmpVar}} {{getType $ic .Name .Typ.Elem}}
+ {{end}}
+
+ tok = fs.Scan()
+ if tok == fflib.FFTok_error {
+ goto tokerror
+ }
+ if tok == fflib.FFTok_right_brace {
+ break
+ }
+
+ if tok == fflib.FFTok_comma {
+ if wantVal == true {
+ // TODO(pquerna): this isn't an ideal error message, this handles
+ // things like [,,,] as an array value.
+ return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
+ }
+ continue
+ } else {
+ wantVal = true
+ }
+
+ {{handleField .IC $tmpVar .Typ.Elem $ptr false}}
+ {{if eq .IsPtr true}}
+ *{{.Name}} = append(*{{.Name}}, {{$tmpVar}})
+ {{else}}
+ {{.Name}} = append({{.Name}}, {{$tmpVar}})
+ {{end}}
+ wantVal = false
+ }
+ }
+}
+`
+
+var handleByteSliceTxt = `
+{
+ {{getAllowTokens .Typ.Name "FFTok_string" "FFTok_null"}}
+ if tok == fflib.FFTok_null {
+ {{.Name}} = nil
+ } else {
+ b := make([]byte, base64.StdEncoding.DecodedLen(fs.Output.Len()))
+ n, err := base64.StdEncoding.Decode(b, fs.Output.Bytes())
+ if err != nil {
+ return fs.WrapErr(err)
+ }
+ {{if eq .UseReflectToSet true}}
+ v := reflect.ValueOf(&{{.Name}}).Elem()
+ v.SetBytes(b[0:n])
+ {{else}}
+ {{.Name}} = append([]byte(), b[0:n]...)
+ {{end}}
+ }
+}
+`
+
+type handleBool struct {
+ Name string
+ Typ reflect.Type
+ TakeAddr bool
+}
+
+var handleBoolTxt = `
+{
+ if tok == fflib.FFTok_null {
+ {{if eq .TakeAddr true}}
+ {{.Name}} = nil
+ {{end}}
+ } else {
+ tmpb := fs.Output.Bytes()
+
+ {{if eq .TakeAddr true}}
+ var tval bool
+ {{end}}
+
+ if bytes.Compare([]byte{'t', 'r', 'u', 'e'}, tmpb) == 0 {
+ {{if eq .TakeAddr true}}
+ tval = true
+ {{else}}
+ {{.Name}} = true
+ {{end}}
+ } else if bytes.Compare([]byte{'f', 'a', 'l', 's', 'e'}, tmpb) == 0 {
+ {{if eq .TakeAddr true}}
+ tval = false
+ {{else}}
+ {{.Name}} = false
+ {{end}}
+ } else {
+ err = errors.New("unexpected bytes for true/false value")
+ return fs.WrapErr(err)
+ }
+
+ {{if eq .TakeAddr true}}
+ {{.Name}} = &tval
+ {{end}}
+ }
+}
+`
+
+type handlePtr struct {
+ IC *Inception
+ Name string
+ Typ reflect.Type
+ Quoted bool
+}
+
+var handlePtrTxt = `
+{
+ {{$ic := .IC}}
+
+ if tok == fflib.FFTok_null {
+ {{.Name}} = nil
+ } else {
+ if {{.Name}} == nil {
+ {{.Name}} = new({{getType $ic .Typ.Elem.Name .Typ.Elem}})
+ }
+
+ {{handleFieldAddr .IC .Name true .Typ.Elem false .Quoted}}
+ }
+}
+`
+
+type header struct {
+ IC *Inception
+ SI *StructInfo
+}
+
+var headerTxt = `
+const (
+ ffjt{{.SI.Name}}base = iota
+ ffjt{{.SI.Name}}nosuchkey
+ {{with $si := .SI}}
+ {{range $index, $field := $si.Fields}}
+ {{if ne $field.JsonName "-"}}
+ ffjt{{$si.Name}}{{$field.Name}}
+ {{end}}
+ {{end}}
+ {{end}}
+)
+
+{{with $si := .SI}}
+ {{range $index, $field := $si.Fields}}
+ {{if ne $field.JsonName "-"}}
+var ffjKey{{$si.Name}}{{$field.Name}} = []byte({{$field.JsonName}})
+ {{end}}
+ {{end}}
+{{end}}
+
+`
+
+type ujFunc struct {
+ IC *Inception
+ SI *StructInfo
+ ValidValues []string
+ ResetFields bool
+}
+
+var ujFuncTxt = `
+{{$si := .SI}}
+{{$ic := .IC}}
+
+// UnmarshalJSON umarshall json - template of ffjson
+func (j *{{.SI.Name}}) UnmarshalJSON(input []byte) error {
+ fs := fflib.NewFFLexer(input)
+ return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start)
+}
+
+// UnmarshalJSONFFLexer fast json unmarshall - template ffjson
+func (j *{{.SI.Name}}) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error {
+ var err error
+ currentKey := ffjt{{.SI.Name}}base
+ _ = currentKey
+ tok := fflib.FFTok_init
+ wantedTok := fflib.FFTok_init
+
+ {{if eq .ResetFields true}}
+ {{range $index, $field := $si.Fields}}
+ var ffjSet{{$si.Name}}{{$field.Name}} = false
+ {{end}}
+ {{end}}
+
+mainparse:
+ for {
+ tok = fs.Scan()
+ // println(fmt.Sprintf("debug: tok: %v state: %v", tok, state))
+ if tok == fflib.FFTok_error {
+ goto tokerror
+ }
+
+ switch state {
+
+ case fflib.FFParse_map_start:
+ if tok != fflib.FFTok_left_bracket {
+ wantedTok = fflib.FFTok_left_bracket
+ goto wrongtokenerror
+ }
+ state = fflib.FFParse_want_key
+ continue
+
+ case fflib.FFParse_after_value:
+ if tok == fflib.FFTok_comma {
+ state = fflib.FFParse_want_key
+ } else if tok == fflib.FFTok_right_bracket {
+ goto done
+ } else {
+ wantedTok = fflib.FFTok_comma
+ goto wrongtokenerror
+ }
+
+ case fflib.FFParse_want_key:
+ // json {} ended. goto exit. woo.
+ if tok == fflib.FFTok_right_bracket {
+ goto done
+ }
+ if tok != fflib.FFTok_string {
+ wantedTok = fflib.FFTok_string
+ goto wrongtokenerror
+ }
+
+ kn := fs.Output.Bytes()
+ if len(kn) <= 0 {
+ // "" case. hrm.
+ currentKey = ffjt{{.SI.Name}}nosuchkey
+ state = fflib.FFParse_want_colon
+ goto mainparse
+ } else {
+ switch kn[0] {
+ {{range $byte, $fields := $si.FieldsByFirstByte}}
+ case '{{$byte}}':
+ {{range $index, $field := $fields}}
+ {{if ne $index 0 }}} else if {{else}}if {{end}} bytes.Equal(ffjKey{{$si.Name}}{{$field.Name}}, kn) {
+ currentKey = ffjt{{$si.Name}}{{$field.Name}}
+ state = fflib.FFParse_want_colon
+ goto mainparse
+ {{end}} }
+ {{end}}
+ }
+ {{range $index, $field := $si.ReverseFields}}
+ if {{$field.FoldFuncName}}(ffjKey{{$si.Name}}{{$field.Name}}, kn) {
+ currentKey = ffjt{{$si.Name}}{{$field.Name}}
+ state = fflib.FFParse_want_colon
+ goto mainparse
+ }
+ {{end}}
+ currentKey = ffjt{{.SI.Name}}nosuchkey
+ state = fflib.FFParse_want_colon
+ goto mainparse
+ }
+
+ case fflib.FFParse_want_colon:
+ if tok != fflib.FFTok_colon {
+ wantedTok = fflib.FFTok_colon
+ goto wrongtokenerror
+ }
+ state = fflib.FFParse_want_value
+ continue
+ case fflib.FFParse_want_value:
+
+ if {{range $index, $v := .ValidValues}}{{if ne $index 0 }}||{{end}}tok == fflib.{{$v}}{{end}} {
+ switch currentKey {
+ {{range $index, $field := $si.Fields}}
+ case ffjt{{$si.Name}}{{$field.Name}}:
+ goto handle_{{$field.Name}}
+ {{end}}
+ case ffjt{{$si.Name}}nosuchkey:
+ err = fs.SkipField(tok)
+ if err != nil {
+ return fs.WrapErr(err)
+ }
+ state = fflib.FFParse_after_value
+ goto mainparse
+ }
+ } else {
+ goto wantedvalue
+ }
+ }
+ }
+{{range $index, $field := $si.Fields}}
+handle_{{$field.Name}}:
+ {{with $fieldName := $field.Name | printf "j.%s"}}
+ {{handleField $ic $fieldName $field.Typ $field.Pointer $field.ForceString}}
+ {{if eq $.ResetFields true}}
+ ffjSet{{$si.Name}}{{$field.Name}} = true
+ {{end}}
+ state = fflib.FFParse_after_value
+ goto mainparse
+ {{end}}
+{{end}}
+
+wantedvalue:
+ return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
+wrongtokenerror:
+ return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String()))
+tokerror:
+ if fs.BigError != nil {
+ return fs.WrapErr(fs.BigError)
+ }
+ err = fs.Error.ToError()
+ if err != nil {
+ return fs.WrapErr(err)
+ }
+ panic("ffjson-generated: unreachable, please report bug.")
+done:
+{{if eq .ResetFields true}}
+{{range $index, $field := $si.Fields}}
+ if !ffjSet{{$si.Name}}{{$field.Name}} {
+ {{with $fieldName := $field.Name | printf "j.%s"}}
+ {{if eq $field.Pointer true}}
+ {{$fieldName}} = nil
+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Interface), 10) + `}}
+ {{$fieldName}} = nil
+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Slice), 10) + `}}
+ {{$fieldName}} = nil
+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Array), 10) + `}}
+ {{$fieldName}} = [{{$field.Typ.Len}}]{{getType $ic $fieldName $field.Typ.Elem}}{}
+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Map), 10) + `}}
+ {{$fieldName}} = nil
+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Bool), 10) + `}}
+ {{$fieldName}} = false
+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.String), 10) + `}}
+ {{$fieldName}} = ""
+ {{else if eq $field.Typ.Kind ` + strconv.FormatUint(uint64(reflect.Struct), 10) + `}}
+ {{$fieldName}} = {{getType $ic $fieldName $field.Typ}}{}
+ {{else}}
+ {{$fieldName}} = {{getType $ic $fieldName $field.Typ}}(0)
+ {{end}}
+ {{end}}
+ }
+{{end}}
+{{end}}
+ return nil
+}
+`
+
+type handleUnmarshaler struct {
+ IC *Inception
+ Name string
+ Typ reflect.Type
+ Ptr reflect.Kind
+ TakeAddr bool
+ UnmarshalJSONFFLexer bool
+ Unmarshaler bool
+}
+
+var handleUnmarshalerTxt = `
+ {{$ic := .IC}}
+
+ {{if eq .UnmarshalJSONFFLexer true}}
+ {
+ if tok == fflib.FFTok_null {
+ {{if eq .Typ.Kind .Ptr }}
+ {{.Name}} = nil
+ {{end}}
+ {{if eq .TakeAddr true }}
+ {{.Name}} = nil
+ {{end}}
+ } else {
+ {{if eq .Typ.Kind .Ptr }}
+ if {{.Name}} == nil {
+ {{.Name}} = new({{getType $ic .Typ.Elem.Name .Typ.Elem}})
+ }
+ {{end}}
+ {{if eq .TakeAddr true }}
+ if {{.Name}} == nil {
+ {{.Name}} = new({{getType $ic .Typ.Name .Typ}})
+ }
+ {{end}}
+ err = {{.Name}}.UnmarshalJSONFFLexer(fs, fflib.FFParse_want_key)
+ if err != nil {
+ return err
+ }
+ }
+ state = fflib.FFParse_after_value
+ }
+ {{else}}
+ {{if eq .Unmarshaler true}}
+ {
+ if tok == fflib.FFTok_null {
+ {{if eq .TakeAddr true }}
+ {{.Name}} = nil
+ {{end}}
+ } else {
+
+ tbuf, err := fs.CaptureField(tok)
+ if err != nil {
+ return fs.WrapErr(err)
+ }
+
+ {{if eq .TakeAddr true }}
+ if {{.Name}} == nil {
+ {{.Name}} = new({{getType $ic .Typ.Name .Typ}})
+ }
+ {{end}}
+ err = {{.Name}}.UnmarshalJSON(tbuf)
+ if err != nil {
+ return fs.WrapErr(err)
+ }
+ }
+ state = fflib.FFParse_after_value
+ }
+ {{end}}
+ {{end}}
+`
diff --git a/vendor/github.com/pquerna/ffjson/inception/encoder.go b/vendor/github.com/pquerna/ffjson/inception/encoder.go
new file mode 100644
index 000000000..3e37a2814
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/inception/encoder.go
@@ -0,0 +1,544 @@
+/**
+ * Copyright 2014 Paul Querna
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package ffjsoninception
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/pquerna/ffjson/shared"
+)
+
+func typeInInception(ic *Inception, typ reflect.Type, f shared.Feature) bool {
+ for _, v := range ic.objs {
+ if v.Typ == typ {
+ return v.Options.HasFeature(f)
+ }
+ if typ.Kind() == reflect.Ptr {
+ if v.Typ == typ.Elem() {
+ return v.Options.HasFeature(f)
+ }
+ }
+ }
+
+ return false
+}
+
+func getOmitEmpty(ic *Inception, sf *StructField) string {
+ ptname := "j." + sf.Name
+ if sf.Pointer {
+ ptname = "*" + ptname
+ return "if true {\n"
+ }
+ switch sf.Typ.Kind() {
+
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ return "if len(" + ptname + ") != 0 {" + "\n"
+
+ case reflect.Int,
+ reflect.Int8,
+ reflect.Int16,
+ reflect.Int32,
+ reflect.Int64,
+ reflect.Uint,
+ reflect.Uint8,
+ reflect.Uint16,
+ reflect.Uint32,
+ reflect.Uint64,
+ reflect.Uintptr,
+ reflect.Float32,
+ reflect.Float64:
+ return "if " + ptname + " != 0 {" + "\n"
+
+ case reflect.Bool:
+ return "if " + ptname + " != false {" + "\n"
+
+ case reflect.Interface, reflect.Ptr:
+ return "if " + ptname + " != nil {" + "\n"
+
+ default:
+ // TODO(pquerna): fix types
+ return "if true {" + "\n"
+ }
+}
+
+func getMapValue(ic *Inception, name string, typ reflect.Type, ptr bool, forceString bool) string {
+ var out = ""
+
+ if typ.Key().Kind() != reflect.String {
+ out += fmt.Sprintf("/* Falling back. type=%v kind=%v */\n", typ, typ.Kind())
+ out += ic.q.Flush()
+ out += "err = buf.Encode(" + name + ")" + "\n"
+ out += "if err != nil {" + "\n"
+ out += " return err" + "\n"
+ out += "}" + "\n"
+ return out
+ }
+
+ var elemKind reflect.Kind
+ elemKind = typ.Elem().Kind()
+
+ switch elemKind {
+ case reflect.String,
+ reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr,
+ reflect.Float32,
+ reflect.Float64,
+ reflect.Bool:
+
+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
+
+ out += "if " + name + " == nil {" + "\n"
+ ic.q.Write("null")
+ out += ic.q.GetQueued()
+ ic.q.DeleteLast()
+ out += "} else {" + "\n"
+ out += ic.q.WriteFlush("{ ")
+ out += " for key, value := range " + name + " {" + "\n"
+ out += " fflib.WriteJsonString(buf, key)" + "\n"
+ out += " buf.WriteString(`:`)" + "\n"
+ out += getGetInnerValue(ic, "value", typ.Elem(), false, forceString)
+ out += " buf.WriteByte(',')" + "\n"
+ out += " }" + "\n"
+ out += "buf.Rewind(1)" + "\n"
+ out += ic.q.WriteFlush("}")
+ out += "}" + "\n"
+
+ default:
+ out += ic.q.Flush()
+ out += fmt.Sprintf("/* Falling back. type=%v kind=%v */\n", typ, typ.Kind())
+ out += "err = buf.Encode(" + name + ")" + "\n"
+ out += "if err != nil {" + "\n"
+ out += " return err" + "\n"
+ out += "}" + "\n"
+ }
+ return out
+}
+
+func getGetInnerValue(ic *Inception, name string, typ reflect.Type, ptr bool, forceString bool) string {
+ var out = ""
+
+ // Flush if not bool or maps
+ if typ.Kind() != reflect.Bool && typ.Kind() != reflect.Map && typ.Kind() != reflect.Struct {
+ out += ic.q.Flush()
+ }
+
+ if typ.Implements(marshalerFasterType) ||
+ reflect.PtrTo(typ).Implements(marshalerFasterType) ||
+ typeInInception(ic, typ, shared.MustEncoder) ||
+ typ.Implements(marshalerType) ||
+ reflect.PtrTo(typ).Implements(marshalerType) {
+
+ out += ic.q.Flush()
+ out += tplStr(encodeTpl["handleMarshaler"], handleMarshaler{
+ IC: ic,
+ Name: name,
+ Typ: typ,
+ Ptr: reflect.Ptr,
+ MarshalJSONBuf: typ.Implements(marshalerFasterType) || reflect.PtrTo(typ).Implements(marshalerFasterType) || typeInInception(ic, typ, shared.MustEncoder),
+ Marshaler: typ.Implements(marshalerType) || reflect.PtrTo(typ).Implements(marshalerType),
+ })
+ return out
+ }
+
+ ptname := name
+ if ptr {
+ ptname = "*" + name
+ }
+
+ switch typ.Kind() {
+ case reflect.Int,
+ reflect.Int8,
+ reflect.Int16,
+ reflect.Int32,
+ reflect.Int64:
+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
+ out += "fflib.FormatBits2(buf, uint64(" + ptname + "), 10, " + ptname + " < 0)" + "\n"
+ case reflect.Uint,
+ reflect.Uint8,
+ reflect.Uint16,
+ reflect.Uint32,
+ reflect.Uint64,
+ reflect.Uintptr:
+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
+ out += "fflib.FormatBits2(buf, uint64(" + ptname + "), 10, false)" + "\n"
+ case reflect.Float32:
+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
+ out += "fflib.AppendFloat(buf, float64(" + ptname + "), 'g', -1, 32)" + "\n"
+ case reflect.Float64:
+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
+ out += "fflib.AppendFloat(buf, float64(" + ptname + "), 'g', -1, 64)" + "\n"
+ case reflect.Array,
+ reflect.Slice:
+
+ // Arrays cannot be nil
+ if typ.Kind() != reflect.Array {
+ out += "if " + name + "!= nil {" + "\n"
+ }
+ // Array and slice values encode as JSON arrays, except that
+ // []byte encodes as a base64-encoded string, and a nil slice
+ // encodes as the null JSON object.
+ if typ.Kind() == reflect.Slice && typ.Elem().Kind() == reflect.Uint8 {
+ ic.OutputImports[`"encoding/base64"`] = true
+
+ out += "buf.WriteString(`\"`)" + "\n"
+ out += `{` + "\n"
+ out += `enc := base64.NewEncoder(base64.StdEncoding, buf)` + "\n"
+ if typ.Elem().Name() != "byte" {
+ ic.OutputImports[`"reflect"`] = true
+ out += `enc.Write(reflect.Indirect(reflect.ValueOf(` + ptname + `)).Bytes())` + "\n"
+
+ } else {
+ out += `enc.Write(` + ptname + `)` + "\n"
+ }
+ out += `enc.Close()` + "\n"
+ out += `}` + "\n"
+ out += "buf.WriteString(`\"`)" + "\n"
+ } else {
+ out += "buf.WriteString(`[`)" + "\n"
+ out += "for i, v := range " + ptname + "{" + "\n"
+ out += "if i != 0 {" + "\n"
+ out += "buf.WriteString(`,`)" + "\n"
+ out += "}" + "\n"
+ out += getGetInnerValue(ic, "v", typ.Elem(), false, false)
+ out += "}" + "\n"
+ out += "buf.WriteString(`]`)" + "\n"
+ }
+ if typ.Kind() != reflect.Array {
+ out += "} else {" + "\n"
+ out += "buf.WriteString(`null`)" + "\n"
+ out += "}" + "\n"
+ }
+ case reflect.String:
+ // Is it a json.Number?
+ if typ.PkgPath() == "encoding/json" && typ.Name() == "Number" {
+ // Fall back to json package to rely on the valid number check.
+ // See: https://github.com/golang/go/blob/92cd6e3af9f423ab4d8ac78f24e7fd81c31a8ce6/src/encoding/json/encode.go#L550
+ out += fmt.Sprintf("/* json.Number */\n")
+ out += "err = buf.Encode(" + name + ")" + "\n"
+ out += "if err != nil {" + "\n"
+ out += " return err" + "\n"
+ out += "}" + "\n"
+ } else {
+ ic.OutputImports[`fflib "github.com/pquerna/ffjson/fflib/v1"`] = true
+ if forceString {
+ // Forcestring on strings does double-escaping of the entire value.
+ // We create a temporary buffer, encode to that an re-encode it.
+ out += "{" + "\n"
+ out += "tmpbuf := fflib.Buffer{}" + "\n"
+ out += "tmpbuf.Grow(len(" + ptname + ") + 16)" + "\n"
+ out += "fflib.WriteJsonString(&tmpbuf, string(" + ptname + "))" + "\n"
+ out += "fflib.WriteJsonString(buf, string( tmpbuf.Bytes() " + `))` + "\n"
+ out += "}" + "\n"
+ } else {
+ out += "fflib.WriteJsonString(buf, string(" + ptname + "))" + "\n"
+ }
+ }
+ case reflect.Ptr:
+ out += "if " + name + "!= nil {" + "\n"
+ switch typ.Elem().Kind() {
+ case reflect.Struct:
+ out += getGetInnerValue(ic, name, typ.Elem(), false, false)
+ default:
+ out += getGetInnerValue(ic, "*"+name, typ.Elem(), false, false)
+ }
+ out += "} else {" + "\n"
+ out += "buf.WriteString(`null`)" + "\n"
+ out += "}" + "\n"
+ case reflect.Bool:
+ out += "if " + ptname + " {" + "\n"
+ ic.q.Write("true")
+ out += ic.q.GetQueued()
+ out += "} else {" + "\n"
+ // Delete 'true'
+ ic.q.DeleteLast()
+ out += ic.q.WriteFlush("false")
+ out += "}" + "\n"
+ case reflect.Interface:
+ out += fmt.Sprintf("/* Interface types must use runtime reflection. type=%v kind=%v */\n", typ, typ.Kind())
+ out += "err = buf.Encode(" + name + ")" + "\n"
+ out += "if err != nil {" + "\n"
+ out += " return err" + "\n"
+ out += "}" + "\n"
+ case reflect.Map:
+ out += getMapValue(ic, ptname, typ, ptr, forceString)
+ case reflect.Struct:
+ if typ.Name() == "" {
+ ic.q.Write("{")
+ ic.q.Write(" ")
+ out += fmt.Sprintf("/* Inline struct. type=%v kind=%v */\n", typ, typ.Kind())
+ newV := reflect.Indirect(reflect.New(typ)).Interface()
+ fields := extractFields(newV)
+
+ // Output all fields
+ for _, field := range fields {
+ // Adjust field name
+ field.Name = name + "." + field.Name
+ out += getField(ic, field, "")
+ }
+
+ if lastConditional(fields) {
+ out += ic.q.Flush()
+ out += `buf.Rewind(1)` + "\n"
+ } else {
+ ic.q.DeleteLast()
+ }
+ out += ic.q.WriteFlush("}")
+ } else {
+ out += fmt.Sprintf("/* Struct fall back. type=%v kind=%v */\n", typ, typ.Kind())
+ out += ic.q.Flush()
+ if ptr {
+ out += "err = buf.Encode(" + name + ")" + "\n"
+ } else {
+ // We send pointer to avoid copying entire struct
+ out += "err = buf.Encode(&" + name + ")" + "\n"
+ }
+ out += "if err != nil {" + "\n"
+ out += " return err" + "\n"
+ out += "}" + "\n"
+ }
+ default:
+ out += fmt.Sprintf("/* Falling back. type=%v kind=%v */\n", typ, typ.Kind())
+ out += "err = buf.Encode(" + name + ")" + "\n"
+ out += "if err != nil {" + "\n"
+ out += " return err" + "\n"
+ out += "}" + "\n"
+ }
+
+ return out
+}
+
+func getValue(ic *Inception, sf *StructField, prefix string) string {
+ closequote := false
+ if sf.ForceString {
+ switch sf.Typ.Kind() {
+ case reflect.Int,
+ reflect.Int8,
+ reflect.Int16,
+ reflect.Int32,
+ reflect.Int64,
+ reflect.Uint,
+ reflect.Uint8,
+ reflect.Uint16,
+ reflect.Uint32,
+ reflect.Uint64,
+ reflect.Uintptr,
+ reflect.Float32,
+ reflect.Float64,
+ reflect.Bool:
+ ic.q.Write(`"`)
+ closequote = true
+ }
+ }
+ out := getGetInnerValue(ic, prefix+sf.Name, sf.Typ, sf.Pointer, sf.ForceString)
+ if closequote {
+ if sf.Pointer {
+ out += ic.q.WriteFlush(`"`)
+ } else {
+ ic.q.Write(`"`)
+ }
+ }
+
+ return out
+}
+
+func p2(v uint32) uint32 {
+ v--
+ v |= v >> 1
+ v |= v >> 2
+ v |= v >> 4
+ v |= v >> 8
+ v |= v >> 16
+ v++
+ return v
+}
+
+func getTypeSize(t reflect.Type) uint32 {
+ switch t.Kind() {
+ case reflect.String:
+ // TODO: consider runtime analysis.
+ return 32
+ case reflect.Array, reflect.Map, reflect.Slice:
+ // TODO: consider runtime analysis.
+ return 4 * getTypeSize(t.Elem())
+ case reflect.Int,
+ reflect.Int8,
+ reflect.Int16,
+ reflect.Int32,
+ reflect.Uint,
+ reflect.Uint8,
+ reflect.Uint16,
+ reflect.Uint32:
+ return 8
+ case reflect.Int64,
+ reflect.Uint64,
+ reflect.Uintptr:
+ return 16
+ case reflect.Float32,
+ reflect.Float64:
+ return 16
+ case reflect.Bool:
+ return 4
+ case reflect.Ptr:
+ return getTypeSize(t.Elem())
+ default:
+ return 16
+ }
+}
+
+func getTotalSize(si *StructInfo) uint32 {
+ rv := uint32(si.Typ.Size())
+ for _, f := range si.Fields {
+ rv += getTypeSize(f.Typ)
+ }
+ return rv
+}
+
+func getBufGrowSize(si *StructInfo) uint32 {
+
+ // TOOD(pquerna): automatically calc a better grow size based on history
+ // of a struct.
+ return p2(getTotalSize(si))
+}
+
+func isIntish(t reflect.Type) bool {
+ if t.Kind() >= reflect.Int && t.Kind() <= reflect.Uintptr {
+ return true
+ }
+ if t.Kind() == reflect.Array || t.Kind() == reflect.Slice || t.Kind() == reflect.Ptr {
+ if t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 {
+ // base64 special case.
+ return false
+ } else {
+ return isIntish(t.Elem())
+ }
+ }
+ return false
+}
+
+func getField(ic *Inception, f *StructField, prefix string) string {
+ out := ""
+ if f.OmitEmpty {
+ out += ic.q.Flush()
+ if f.Pointer {
+ out += "if " + prefix + f.Name + " != nil {" + "\n"
+ }
+ out += getOmitEmpty(ic, f)
+ }
+
+ if f.Pointer && !f.OmitEmpty {
+ // Pointer values encode as the value pointed to. A nil pointer encodes as the null JSON object.
+ out += "if " + prefix + f.Name + " != nil {" + "\n"
+ }
+
+ // JsonName is already escaped and quoted.
+ // getInnervalue should flush
+ ic.q.Write(f.JsonName + ":")
+ // We save a copy in case we need it
+ t := ic.q
+
+ out += getValue(ic, f, prefix)
+ ic.q.Write(",")
+
+ if f.Pointer && !f.OmitEmpty {
+ out += "} else {" + "\n"
+ out += t.WriteFlush("null")
+ out += "}" + "\n"
+ }
+
+ if f.OmitEmpty {
+ out += ic.q.Flush()
+ if f.Pointer {
+ out += "}" + "\n"
+ }
+ out += "}" + "\n"
+ }
+ return out
+}
+
+// We check if the last field is conditional.
+func lastConditional(fields []*StructField) bool {
+ if len(fields) > 0 {
+ f := fields[len(fields)-1]
+ return f.OmitEmpty
+ }
+ return false
+}
+
+func CreateMarshalJSON(ic *Inception, si *StructInfo) error {
+ conditionalWrites := lastConditional(si.Fields)
+ out := ""
+
+ out += "// MarshalJSON marshal bytes to json - template\n"
+ out += `func (j *` + si.Name + `) MarshalJSON() ([]byte, error) {` + "\n"
+ out += `var buf fflib.Buffer` + "\n"
+
+ out += `if j == nil {` + "\n"
+ out += ` buf.WriteString("null")` + "\n"
+ out += " return buf.Bytes(), nil" + "\n"
+ out += `}` + "\n"
+
+ out += `err := j.MarshalJSONBuf(&buf)` + "\n"
+ out += `if err != nil {` + "\n"
+ out += " return nil, err" + "\n"
+ out += `}` + "\n"
+ out += `return buf.Bytes(), nil` + "\n"
+ out += `}` + "\n"
+
+ out += "// MarshalJSONBuf marshal buff to json - template\n"
+ out += `func (j *` + si.Name + `) MarshalJSONBuf(buf fflib.EncodingBuffer) (error) {` + "\n"
+ out += ` if j == nil {` + "\n"
+ out += ` buf.WriteString("null")` + "\n"
+ out += " return nil" + "\n"
+ out += ` }` + "\n"
+
+ out += `var err error` + "\n"
+ out += `var obj []byte` + "\n"
+ out += `_ = obj` + "\n"
+ out += `_ = err` + "\n"
+
+ ic.q.Write("{")
+
+ // The extra space is inserted here.
+ // If nothing is written to the field this will be deleted
+ // instead of the last comma.
+ if conditionalWrites || len(si.Fields) == 0 {
+ ic.q.Write(" ")
+ }
+
+ for _, f := range si.Fields {
+ out += getField(ic, f, "j.")
+ }
+
+ // Handling the last comma is tricky.
+ // If the last field has omitempty, conditionalWrites is set.
+ // If something has been written, we delete the last comma,
+ // by backing up the buffer, otherwise it will delete a space.
+ if conditionalWrites {
+ out += ic.q.Flush()
+ out += `buf.Rewind(1)` + "\n"
+ } else {
+ ic.q.DeleteLast()
+ }
+
+ out += ic.q.WriteFlush("}")
+ out += `return nil` + "\n"
+ out += `}` + "\n"
+ ic.OutputFuncs = append(ic.OutputFuncs, out)
+ return nil
+}
diff --git a/vendor/github.com/pquerna/ffjson/inception/encoder_tpl.go b/vendor/github.com/pquerna/ffjson/inception/encoder_tpl.go
new file mode 100644
index 000000000..22ab5292e
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/inception/encoder_tpl.go
@@ -0,0 +1,73 @@
+/**
+ * Copyright 2014 Paul Querna
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package ffjsoninception
+
+import (
+ "reflect"
+ "text/template"
+)
+
+var encodeTpl map[string]*template.Template
+
+func init() {
+ encodeTpl = make(map[string]*template.Template)
+
+ funcs := map[string]string{
+ "handleMarshaler": handleMarshalerTxt,
+ }
+ tplFuncs := template.FuncMap{}
+
+ for k, v := range funcs {
+ encodeTpl[k] = template.Must(template.New(k).Funcs(tplFuncs).Parse(v))
+ }
+}
+
+type handleMarshaler struct {
+ IC *Inception
+ Name string
+ Typ reflect.Type
+ Ptr reflect.Kind
+ MarshalJSONBuf bool
+ Marshaler bool
+}
+
+var handleMarshalerTxt = `
+ {
+ {{if eq .Typ.Kind .Ptr}}
+ if {{.Name}} == nil {
+ buf.WriteString("null")
+ } else {
+ {{end}}
+
+ {{if eq .MarshalJSONBuf true}}
+ err = {{.Name}}.MarshalJSONBuf(buf)
+ if err != nil {
+ return err
+ }
+ {{else if eq .Marshaler true}}
+ obj, err = {{.Name}}.MarshalJSON()
+ if err != nil {
+ return err
+ }
+ buf.Write(obj)
+ {{end}}
+ {{if eq .Typ.Kind .Ptr}}
+ }
+ {{end}}
+ }
+`
diff --git a/vendor/github.com/pquerna/ffjson/inception/inception.go b/vendor/github.com/pquerna/ffjson/inception/inception.go
new file mode 100644
index 000000000..10cb2712c
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/inception/inception.go
@@ -0,0 +1,160 @@
+/**
+ * Copyright 2014 Paul Querna
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package ffjsoninception
+
+import (
+ "errors"
+ "fmt"
+ "github.com/pquerna/ffjson/shared"
+ "io/ioutil"
+ "os"
+ "reflect"
+ "sort"
+)
+
+type Inception struct {
+ objs []*StructInfo
+ InputPath string
+ OutputPath string
+ PackageName string
+ PackagePath string
+ OutputImports map[string]bool
+ OutputFuncs []string
+ q ConditionalWrite
+ ResetFields bool
+}
+
+func NewInception(inputPath string, packageName string, outputPath string, resetFields bool) *Inception {
+ return &Inception{
+ objs: make([]*StructInfo, 0),
+ InputPath: inputPath,
+ OutputPath: outputPath,
+ PackageName: packageName,
+ OutputFuncs: make([]string, 0),
+ OutputImports: make(map[string]bool),
+ ResetFields: resetFields,
+ }
+}
+
+func (i *Inception) AddMany(objs []shared.InceptionType) {
+ for _, obj := range objs {
+ i.Add(obj)
+ }
+}
+
+func (i *Inception) Add(obj shared.InceptionType) {
+ i.objs = append(i.objs, NewStructInfo(obj))
+ i.PackagePath = i.objs[0].Typ.PkgPath()
+}
+
+func (i *Inception) wantUnmarshal(si *StructInfo) bool {
+ if si.Options.SkipDecoder {
+ return false
+ }
+ typ := si.Typ
+ umlx := typ.Implements(unmarshalFasterType) || reflect.PtrTo(typ).Implements(unmarshalFasterType)
+ umlstd := typ.Implements(unmarshalerType) || reflect.PtrTo(typ).Implements(unmarshalerType)
+ if umlstd && !umlx {
+ // structure has UnmarshalJSON, but not our faster version -- skip it.
+ return false
+ }
+ return true
+}
+
+func (i *Inception) wantMarshal(si *StructInfo) bool {
+ if si.Options.SkipEncoder {
+ return false
+ }
+ typ := si.Typ
+ mlx := typ.Implements(marshalerFasterType) || reflect.PtrTo(typ).Implements(marshalerFasterType)
+ mlstd := typ.Implements(marshalerType) || reflect.PtrTo(typ).Implements(marshalerType)
+ if mlstd && !mlx {
+ // structure has MarshalJSON, but not our faster version -- skip it.
+ return false
+ }
+ return true
+}
+
+type sortedStructs []*StructInfo
+
+func (p sortedStructs) Len() int { return len(p) }
+func (p sortedStructs) Less(i, j int) bool { return p[i].Name < p[j].Name }
+func (p sortedStructs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p sortedStructs) Sort() { sort.Sort(p) }
+
+func (i *Inception) generateCode() error {
+ // We sort the structs by name, so output if predictable.
+ sorted := sortedStructs(i.objs)
+ sorted.Sort()
+
+ for _, si := range sorted {
+ if i.wantMarshal(si) {
+ err := CreateMarshalJSON(i, si)
+ if err != nil {
+ return err
+ }
+ }
+
+ if i.wantUnmarshal(si) {
+ err := CreateUnmarshalJSON(i, si)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (i *Inception) handleError(err error) {
+ fmt.Fprintf(os.Stderr, "Error: %s:\n\n", err)
+ os.Exit(1)
+}
+
+func (i *Inception) Execute() {
+ if len(os.Args) != 1 {
+ i.handleError(errors.New(fmt.Sprintf("Internal ffjson error: inception executable takes no args: %v", os.Args)))
+ return
+ }
+
+ err := i.generateCode()
+ if err != nil {
+ i.handleError(err)
+ return
+ }
+
+ data, err := RenderTemplate(i)
+ if err != nil {
+ i.handleError(err)
+ return
+ }
+
+ stat, err := os.Stat(i.InputPath)
+
+ if err != nil {
+ i.handleError(err)
+ return
+ }
+
+ err = ioutil.WriteFile(i.OutputPath, data, stat.Mode())
+
+ if err != nil {
+ i.handleError(err)
+ return
+ }
+
+}
diff --git a/vendor/github.com/pquerna/ffjson/inception/reflect.go b/vendor/github.com/pquerna/ffjson/inception/reflect.go
new file mode 100644
index 000000000..8fb0bd5cb
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/inception/reflect.go
@@ -0,0 +1,290 @@
+/**
+ * Copyright 2014 Paul Querna
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package ffjsoninception
+
+import (
+ fflib "github.com/pquerna/ffjson/fflib/v1"
+ "github.com/pquerna/ffjson/shared"
+
+ "bytes"
+ "encoding/json"
+ "reflect"
+ "unicode/utf8"
+)
+
+type StructField struct {
+ Name string
+ JsonName string
+ FoldFuncName string
+ Typ reflect.Type
+ OmitEmpty bool
+ ForceString bool
+ HasMarshalJSON bool
+ HasUnmarshalJSON bool
+ Pointer bool
+ Tagged bool
+}
+
+type FieldByJsonName []*StructField
+
+func (a FieldByJsonName) Len() int { return len(a) }
+func (a FieldByJsonName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a FieldByJsonName) Less(i, j int) bool { return a[i].JsonName < a[j].JsonName }
+
+type StructInfo struct {
+ Name string
+ Obj interface{}
+ Typ reflect.Type
+ Fields []*StructField
+ Options shared.StructOptions
+}
+
+func NewStructInfo(obj shared.InceptionType) *StructInfo {
+ t := reflect.TypeOf(obj.Obj)
+ return &StructInfo{
+ Obj: obj.Obj,
+ Name: t.Name(),
+ Typ: t,
+ Fields: extractFields(obj.Obj),
+ Options: obj.Options,
+ }
+}
+
+func (si *StructInfo) FieldsByFirstByte() map[string][]*StructField {
+ rv := make(map[string][]*StructField)
+ for _, f := range si.Fields {
+ b := string(f.JsonName[1])
+ rv[b] = append(rv[b], f)
+ }
+ return rv
+}
+
+func (si *StructInfo) ReverseFields() []*StructField {
+ var i int
+ rv := make([]*StructField, 0)
+ for i = len(si.Fields) - 1; i >= 0; i-- {
+ rv = append(rv, si.Fields[i])
+ }
+ return rv
+}
+
+const (
+ caseMask = ^byte(0x20) // Mask to ignore case in ASCII.
+)
+
+func foldFunc(key []byte) string {
+ nonLetter := false
+ special := false // special letter
+ for _, b := range key {
+ if b >= utf8.RuneSelf {
+ return "bytes.EqualFold"
+ }
+ upper := b & caseMask
+ if upper < 'A' || upper > 'Z' {
+ nonLetter = true
+ } else if upper == 'K' || upper == 'S' {
+ // See above for why these letters are special.
+ special = true
+ }
+ }
+ if special {
+ return "fflib.EqualFoldRight"
+ }
+ if nonLetter {
+ return "fflib.AsciiEqualFold"
+ }
+ return "fflib.SimpleLetterEqualFold"
+}
+
+type MarshalerFaster interface {
+ MarshalJSONBuf(buf fflib.EncodingBuffer) error
+}
+
+type UnmarshalFaster interface {
+ UnmarshalJSONFFLexer(l *fflib.FFLexer, state fflib.FFParseState) error
+}
+
+var marshalerType = reflect.TypeOf(new(json.Marshaler)).Elem()
+var marshalerFasterType = reflect.TypeOf(new(MarshalerFaster)).Elem()
+var unmarshalerType = reflect.TypeOf(new(json.Unmarshaler)).Elem()
+var unmarshalFasterType = reflect.TypeOf(new(UnmarshalFaster)).Elem()
+
+// extractFields returns a list of fields that JSON should recognize for the given type.
+// The algorithm is breadth-first search over the set of structs to include - the top struct
+// and then any reachable anonymous structs.
+func extractFields(obj interface{}) []*StructField {
+ t := reflect.TypeOf(obj)
+ // Anonymous fields to explore at the current level and the next.
+ current := []StructField{}
+ next := []StructField{{Typ: t}}
+
+ // Count of queued names for current level and the next.
+ count := map[reflect.Type]int{}
+ nextCount := map[reflect.Type]int{}
+
+ // Types already visited at an earlier level.
+ visited := map[reflect.Type]bool{}
+
+ // Fields found.
+ var fields []*StructField
+
+ for len(next) > 0 {
+ current, next = next, current[:0]
+ count, nextCount = nextCount, map[reflect.Type]int{}
+
+ for _, f := range current {
+ if visited[f.Typ] {
+ continue
+ }
+ visited[f.Typ] = true
+
+ // Scan f.typ for fields to include.
+ for i := 0; i < f.Typ.NumField(); i++ {
+ sf := f.Typ.Field(i)
+ if sf.PkgPath != "" { // unexported
+ continue
+ }
+ tag := sf.Tag.Get("json")
+ if tag == "-" {
+ continue
+ }
+ name, opts := parseTag(tag)
+ if !isValidTag(name) {
+ name = ""
+ }
+
+ ft := sf.Type
+ ptr := false
+ if ft.Kind() == reflect.Ptr {
+ ptr = true
+ }
+
+ if ft.Name() == "" && ft.Kind() == reflect.Ptr {
+ // Follow pointer.
+ ft = ft.Elem()
+ }
+
+ // Record found field and index sequence.
+ if name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
+ tagged := name != ""
+ if name == "" {
+ name = sf.Name
+ }
+
+ var buf bytes.Buffer
+ fflib.WriteJsonString(&buf, name)
+
+ field := &StructField{
+ Name: sf.Name,
+ JsonName: string(buf.Bytes()),
+ FoldFuncName: foldFunc([]byte(name)),
+ Typ: ft,
+ HasMarshalJSON: ft.Implements(marshalerType),
+ HasUnmarshalJSON: ft.Implements(unmarshalerType),
+ OmitEmpty: opts.Contains("omitempty"),
+ ForceString: opts.Contains("string"),
+ Pointer: ptr,
+ Tagged: tagged,
+ }
+
+ fields = append(fields, field)
+
+ if count[f.Typ] > 1 {
+ // If there were multiple instances, add a second,
+ // so that the annihilation code will see a duplicate.
+ // It only cares about the distinction between 1 or 2,
+ // so don't bother generating any more copies.
+ fields = append(fields, fields[len(fields)-1])
+ }
+ continue
+ }
+
+ // Record new anonymous struct to explore in next round.
+ nextCount[ft]++
+ if nextCount[ft] == 1 {
+ next = append(next, StructField{
+ Name: ft.Name(),
+ Typ: ft,
+ })
+ }
+ }
+ }
+ }
+
+ // Delete all fields that are hidden by the Go rules for embedded fields,
+ // except that fields with JSON tags are promoted.
+
+ // The fields are sorted in primary order of name, secondary order
+ // of field index length. Loop over names; for each name, delete
+ // hidden fields by choosing the one dominant field that survives.
+ out := fields[:0]
+ for advance, i := 0, 0; i < len(fields); i += advance {
+ // One iteration per name.
+ // Find the sequence of fields with the name of this first field.
+ fi := fields[i]
+ name := fi.JsonName
+ for advance = 1; i+advance < len(fields); advance++ {
+ fj := fields[i+advance]
+ if fj.JsonName != name {
+ break
+ }
+ }
+ if advance == 1 { // Only one field with this name
+ out = append(out, fi)
+ continue
+ }
+ dominant, ok := dominantField(fields[i : i+advance])
+ if ok {
+ out = append(out, dominant)
+ }
+ }
+
+ fields = out
+
+ return fields
+}
+
+// dominantField looks through the fields, all of which are known to
+// have the same name, to find the single field that dominates the
+// others using Go's embedding rules, modified by the presence of
+// JSON tags. If there are multiple top-level fields, the boolean
+// will be false: This condition is an error in Go and we skip all
+// the fields.
+func dominantField(fields []*StructField) (*StructField, bool) {
+ tagged := -1 // Index of first tagged field.
+ for i, f := range fields {
+ if f.Tagged {
+ if tagged >= 0 {
+ // Multiple tagged fields at the same level: conflict.
+ // Return no field.
+ return nil, false
+ }
+ tagged = i
+ }
+ }
+ if tagged >= 0 {
+ return fields[tagged], true
+ }
+ // All remaining fields have the same length. If there's more than one,
+ // we have a conflict (two fields named "X" at the same level) and we
+ // return no field.
+ if len(fields) > 1 {
+ return nil, false
+ }
+ return fields[0], true
+}
diff --git a/vendor/github.com/pquerna/ffjson/inception/tags.go b/vendor/github.com/pquerna/ffjson/inception/tags.go
new file mode 100644
index 000000000..ccce101b8
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/inception/tags.go
@@ -0,0 +1,79 @@
+/**
+ * Copyright 2014 Paul Querna
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package ffjsoninception
+
+import (
+ "strings"
+ "unicode"
+)
+
+// from: http://golang.org/src/pkg/encoding/json/tags.go
+
+// tagOptions is the string following a comma in a struct field's "json"
+// tag, or the empty string. It does not include the leading comma.
+type tagOptions string
+
+// parseTag splits a struct field's json tag into its name and
+// comma-separated options.
+func parseTag(tag string) (string, tagOptions) {
+ if idx := strings.Index(tag, ","); idx != -1 {
+ return tag[:idx], tagOptions(tag[idx+1:])
+ }
+ return tag, tagOptions("")
+}
+
+// Contains reports whether a comma-separated list of options
+// contains a particular substr flag. substr must be surrounded by a
+// string boundary or commas.
+func (o tagOptions) Contains(optionName string) bool {
+ if len(o) == 0 {
+ return false
+ }
+ s := string(o)
+ for s != "" {
+ var next string
+ i := strings.Index(s, ",")
+ if i >= 0 {
+ s, next = s[:i], s[i+1:]
+ }
+ if s == optionName {
+ return true
+ }
+ s = next
+ }
+ return false
+}
+
+func isValidTag(s string) bool {
+ if s == "" {
+ return false
+ }
+ for _, c := range s {
+ switch {
+ case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
+ // Backslash and quote chars are reserved, but
+ // otherwise any punctuation chars are allowed
+ // in a tag name.
+ default:
+ if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
+ return false
+ }
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/pquerna/ffjson/inception/template.go b/vendor/github.com/pquerna/ffjson/inception/template.go
new file mode 100644
index 000000000..121a23dd8
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/inception/template.go
@@ -0,0 +1,60 @@
+/**
+ * Copyright 2014 Paul Querna
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package ffjsoninception
+
+import (
+ "bytes"
+ "go/format"
+ "text/template"
+)
+
+const ffjsonTemplate = `
+// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
+// source: {{.InputPath}}
+
+package {{.PackageName}}
+
+import (
+{{range $k, $v := .OutputImports}}{{$k}}
+{{end}}
+)
+
+{{range .OutputFuncs}}
+{{.}}
+{{end}}
+
+`
+
+func RenderTemplate(ic *Inception) ([]byte, error) {
+ t := template.Must(template.New("ffjson.go").Parse(ffjsonTemplate))
+ buf := new(bytes.Buffer)
+ err := t.Execute(buf, ic)
+ if err != nil {
+ return nil, err
+ }
+ return format.Source(buf.Bytes())
+}
+
+func tplStr(t *template.Template, data interface{}) string {
+ buf := bytes.Buffer{}
+ err := t.Execute(&buf, data)
+ if err != nil {
+ panic(err)
+ }
+ return buf.String()
+}
diff --git a/vendor/github.com/pquerna/ffjson/inception/writerstack.go b/vendor/github.com/pquerna/ffjson/inception/writerstack.go
new file mode 100644
index 000000000..1521961c9
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/inception/writerstack.go
@@ -0,0 +1,65 @@
+package ffjsoninception
+
+import "strings"
+
+// ConditionalWrite is a stack containing a number of pending writes
+type ConditionalWrite struct {
+ Queued []string
+}
+
+// Write will add a string to be written
+func (w *ConditionalWrite) Write(s string) {
+ w.Queued = append(w.Queued, s)
+}
+
+// DeleteLast will delete the last added write
+func (w *ConditionalWrite) DeleteLast() {
+ if len(w.Queued) == 0 {
+ return
+ }
+ w.Queued = w.Queued[:len(w.Queued)-1]
+}
+
+// Last will return the last added write
+func (w *ConditionalWrite) Last() string {
+ if len(w.Queued) == 0 {
+ return ""
+ }
+ return w.Queued[len(w.Queued)-1]
+}
+
+// Flush will return all queued writes, and return
+// "" (empty string) in nothing has been queued
+// "buf.WriteByte('" + byte + "')" + '\n' if one bute has been queued.
+// "buf.WriteString(`" + string + "`)" + "\n" if more than one byte has been queued.
+func (w *ConditionalWrite) Flush() string {
+ combined := strings.Join(w.Queued, "")
+ if len(combined) == 0 {
+ return ""
+ }
+
+ w.Queued = nil
+ if len(combined) == 1 {
+ return "buf.WriteByte('" + combined + "')" + "\n"
+ }
+ return "buf.WriteString(`" + combined + "`)" + "\n"
+}
+
+func (w *ConditionalWrite) FlushTo(out string) string {
+ out += w.Flush()
+ return out
+}
+
+// WriteFlush will add a string and return the Flush result for the queue
+func (w *ConditionalWrite) WriteFlush(s string) string {
+ w.Write(s)
+ return w.Flush()
+}
+
+// GetQueued will return the current queued content without flushing.
+func (w *ConditionalWrite) GetQueued() string {
+ t := w.Queued
+ s := w.Flush()
+ w.Queued = t
+ return s
+}
diff --git a/vendor/github.com/pquerna/ffjson/shared/options.go b/vendor/github.com/pquerna/ffjson/shared/options.go
new file mode 100644
index 000000000..d74edc135
--- /dev/null
+++ b/vendor/github.com/pquerna/ffjson/shared/options.go
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2014 Paul Querna, Klaus Post
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package shared
+
+type StructOptions struct {
+ SkipDecoder bool
+ SkipEncoder bool
+}
+
+type InceptionType struct {
+ Obj interface{}
+ Options StructOptions
+}
+type Feature int
+
+const (
+ Nothing Feature = 0
+ MustDecoder = 1 << 1
+ MustEncoder = 1 << 2
+ MustEncDec = MustDecoder | MustEncoder
+)
+
+func (i InceptionType) HasFeature(f Feature) bool {
+ return i.HasFeature(f)
+}
+
+func (s StructOptions) HasFeature(f Feature) bool {
+ hasNeeded := true
+ if f&MustDecoder != 0 && s.SkipDecoder {
+ hasNeeded = false
+ }
+ if f&MustEncoder != 0 && s.SkipEncoder {
+ hasNeeded = false
+ }
+ return hasNeeded
+}