Files
podman/vendor/github.com/pquerna/ffjson/inception/encoder.go
TomSweeneyRedHat 711474d92e Vendor Buildah 1.10.1
As the title says, vendor Buildah v1.10.1

Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
2019-08-08 16:06:40 -04:00

545 lines
14 KiB
Go

/**
* 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
}