diff options
Diffstat (limited to 'vendor/github.com/pquerna/ffjson/inception/decoder_tpl.go')
-rw-r--r-- | vendor/github.com/pquerna/ffjson/inception/decoder_tpl.go | 773 |
1 files changed, 773 insertions, 0 deletions
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}} +` |