mirror of
https://git.mirrors.martin98.com/https://github.com/ceph/ceph-csi.git
synced 2025-08-19 01:15:55 +08:00

Several packages are only used while running the e2e suite. These packages are less important to update, as the they can not influence the final executable that is part of the Ceph-CSI container-image. By moving these dependencies out of the main Ceph-CSI go.mod, it is easier to identify if a reported CVE affects Ceph-CSI, or only the testing (like most of the Kubernetes CVEs). Signed-off-by: Niels de Vos <ndevos@ibm.com>
261 lines
6.6 KiB
Go
261 lines
6.6 KiB
Go
// Copyright (c) Faye Amacker. All rights reserved.
|
|
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
|
|
|
package cbor
|
|
|
|
import (
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type field struct {
|
|
name string
|
|
nameAsInt int64 // used to decoder to match field name with CBOR int
|
|
cborName []byte
|
|
cborNameByteString []byte // major type 2 name encoding iff cborName has major type 3
|
|
idx []int
|
|
typ reflect.Type
|
|
ef encodeFunc
|
|
ief isEmptyFunc
|
|
typInfo *typeInfo // used to decoder to reuse type info
|
|
tagged bool // used to choose dominant field (at the same level tagged fields dominate untagged fields)
|
|
omitEmpty bool // used to skip empty field
|
|
keyAsInt bool // used to encode/decode field name as int
|
|
}
|
|
|
|
type fields []*field
|
|
|
|
// indexFieldSorter sorts fields by field idx at each level, breaking ties with idx depth.
|
|
type indexFieldSorter struct {
|
|
fields fields
|
|
}
|
|
|
|
func (x *indexFieldSorter) Len() int {
|
|
return len(x.fields)
|
|
}
|
|
|
|
func (x *indexFieldSorter) Swap(i, j int) {
|
|
x.fields[i], x.fields[j] = x.fields[j], x.fields[i]
|
|
}
|
|
|
|
func (x *indexFieldSorter) Less(i, j int) bool {
|
|
iIdx, jIdx := x.fields[i].idx, x.fields[j].idx
|
|
for k := 0; k < len(iIdx) && k < len(jIdx); k++ {
|
|
if iIdx[k] != jIdx[k] {
|
|
return iIdx[k] < jIdx[k]
|
|
}
|
|
}
|
|
return len(iIdx) <= len(jIdx)
|
|
}
|
|
|
|
// nameLevelAndTagFieldSorter sorts fields by field name, idx depth, and presence of tag.
|
|
type nameLevelAndTagFieldSorter struct {
|
|
fields fields
|
|
}
|
|
|
|
func (x *nameLevelAndTagFieldSorter) Len() int {
|
|
return len(x.fields)
|
|
}
|
|
|
|
func (x *nameLevelAndTagFieldSorter) Swap(i, j int) {
|
|
x.fields[i], x.fields[j] = x.fields[j], x.fields[i]
|
|
}
|
|
|
|
func (x *nameLevelAndTagFieldSorter) Less(i, j int) bool {
|
|
fi, fj := x.fields[i], x.fields[j]
|
|
if fi.name != fj.name {
|
|
return fi.name < fj.name
|
|
}
|
|
if len(fi.idx) != len(fj.idx) {
|
|
return len(fi.idx) < len(fj.idx)
|
|
}
|
|
if fi.tagged != fj.tagged {
|
|
return fi.tagged
|
|
}
|
|
return i < j // Field i and j have the same name, depth, and tagged status. Nothing else matters.
|
|
}
|
|
|
|
// getFields returns visible fields of struct type t following visibility rules for JSON encoding.
|
|
func getFields(t reflect.Type) (flds fields, structOptions string) {
|
|
// Get special field "_" tag options
|
|
if f, ok := t.FieldByName("_"); ok {
|
|
tag := f.Tag.Get("cbor")
|
|
if tag != "-" {
|
|
structOptions = tag
|
|
}
|
|
}
|
|
|
|
// nTypes contains next level anonymous fields' types and indexes
|
|
// (there can be multiple fields of the same type at the same level)
|
|
flds, nTypes := appendFields(t, nil, nil, nil)
|
|
|
|
if len(nTypes) > 0 {
|
|
|
|
var cTypes map[reflect.Type][][]int // current level anonymous fields' types and indexes
|
|
vTypes := map[reflect.Type]bool{t: true} // visited field types at less nested levels
|
|
|
|
for len(nTypes) > 0 {
|
|
cTypes, nTypes = nTypes, nil
|
|
|
|
for t, idx := range cTypes {
|
|
// If there are multiple anonymous fields of the same struct type at the same level, all are ignored.
|
|
if len(idx) > 1 {
|
|
continue
|
|
}
|
|
|
|
// Anonymous field of the same type at deeper nested level is ignored.
|
|
if vTypes[t] {
|
|
continue
|
|
}
|
|
vTypes[t] = true
|
|
|
|
flds, nTypes = appendFields(t, idx[0], flds, nTypes)
|
|
}
|
|
}
|
|
}
|
|
|
|
sort.Sort(&nameLevelAndTagFieldSorter{flds})
|
|
|
|
// Keep visible fields.
|
|
j := 0 // index of next unique field
|
|
for i := 0; i < len(flds); {
|
|
name := flds[i].name
|
|
if i == len(flds)-1 || // last field
|
|
name != flds[i+1].name || // field i has unique field name
|
|
len(flds[i].idx) < len(flds[i+1].idx) || // field i is at a less nested level than field i+1
|
|
(flds[i].tagged && !flds[i+1].tagged) { // field i is tagged while field i+1 is not
|
|
flds[j] = flds[i]
|
|
j++
|
|
}
|
|
|
|
// Skip fields with the same field name.
|
|
for i++; i < len(flds) && name == flds[i].name; i++ { //nolint:revive
|
|
}
|
|
}
|
|
if j != len(flds) {
|
|
flds = flds[:j]
|
|
}
|
|
|
|
// Sort fields by field index
|
|
sort.Sort(&indexFieldSorter{flds})
|
|
|
|
return flds, structOptions
|
|
}
|
|
|
|
// appendFields appends type t's exportable fields to flds and anonymous struct fields to nTypes .
|
|
func appendFields(
|
|
t reflect.Type,
|
|
idx []int,
|
|
flds fields,
|
|
nTypes map[reflect.Type][][]int,
|
|
) (
|
|
_flds fields,
|
|
_nTypes map[reflect.Type][][]int,
|
|
) {
|
|
for i := 0; i < t.NumField(); i++ {
|
|
f := t.Field(i)
|
|
|
|
ft := f.Type
|
|
for ft.Kind() == reflect.Ptr {
|
|
ft = ft.Elem()
|
|
}
|
|
|
|
if !isFieldExportable(f, ft.Kind()) {
|
|
continue
|
|
}
|
|
|
|
tag := f.Tag.Get("cbor")
|
|
if tag == "" {
|
|
tag = f.Tag.Get("json")
|
|
}
|
|
if tag == "-" {
|
|
continue
|
|
}
|
|
|
|
tagged := tag != ""
|
|
|
|
// Parse field tag options
|
|
var tagFieldName string
|
|
var omitempty, keyasint bool
|
|
for j := 0; tag != ""; j++ {
|
|
var token string
|
|
idx := strings.IndexByte(tag, ',')
|
|
if idx == -1 {
|
|
token, tag = tag, ""
|
|
} else {
|
|
token, tag = tag[:idx], tag[idx+1:]
|
|
}
|
|
if j == 0 {
|
|
tagFieldName = token
|
|
} else {
|
|
switch token {
|
|
case "omitempty":
|
|
omitempty = true
|
|
case "keyasint":
|
|
keyasint = true
|
|
}
|
|
}
|
|
}
|
|
|
|
fieldName := tagFieldName
|
|
if tagFieldName == "" {
|
|
fieldName = f.Name
|
|
}
|
|
|
|
fIdx := make([]int, len(idx)+1)
|
|
copy(fIdx, idx)
|
|
fIdx[len(fIdx)-1] = i
|
|
|
|
if !f.Anonymous || ft.Kind() != reflect.Struct || tagFieldName != "" {
|
|
flds = append(flds, &field{
|
|
name: fieldName,
|
|
idx: fIdx,
|
|
typ: f.Type,
|
|
omitEmpty: omitempty,
|
|
keyAsInt: keyasint,
|
|
tagged: tagged})
|
|
} else {
|
|
if nTypes == nil {
|
|
nTypes = make(map[reflect.Type][][]int)
|
|
}
|
|
nTypes[ft] = append(nTypes[ft], fIdx)
|
|
}
|
|
}
|
|
|
|
return flds, nTypes
|
|
}
|
|
|
|
// isFieldExportable returns true if f is an exportable (regular or anonymous) field or
|
|
// a nonexportable anonymous field of struct type.
|
|
// Nonexportable anonymous field of struct type can contain exportable fields.
|
|
func isFieldExportable(f reflect.StructField, fk reflect.Kind) bool { //nolint:gocritic // ignore hugeParam
|
|
exportable := f.PkgPath == ""
|
|
return exportable || (f.Anonymous && fk == reflect.Struct)
|
|
}
|
|
|
|
type embeddedFieldNullPtrFunc func(reflect.Value) (reflect.Value, error)
|
|
|
|
// getFieldValue returns field value of struct v by index. When encountering null pointer
|
|
// to anonymous (embedded) struct field, f is called with the last traversed field value.
|
|
func getFieldValue(v reflect.Value, idx []int, f embeddedFieldNullPtrFunc) (fv reflect.Value, err error) {
|
|
fv = v
|
|
for i, n := range idx {
|
|
fv = fv.Field(n)
|
|
|
|
if i < len(idx)-1 {
|
|
if fv.Kind() == reflect.Ptr && fv.Type().Elem().Kind() == reflect.Struct {
|
|
if fv.IsNil() {
|
|
// Null pointer to embedded struct field
|
|
fv, err = f(fv)
|
|
if err != nil || !fv.IsValid() {
|
|
return fv, err
|
|
}
|
|
}
|
|
fv = fv.Elem()
|
|
}
|
|
}
|
|
}
|
|
return fv, nil
|
|
}
|