mirror of
https://github.com/containers/podman.git
synced 2026-01-31 09:21:40 -05:00
As the title says, vendor Buildah v1.10.1 Signed-off-by: TomSweeneyRedHat <tsweeney@redhat.com>
545 lines
14 KiB
Go
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
|
|
}
|