| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- /*
- Copyright 2024 The Kubernetes Authors.
- 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 cbor
- import (
- "fmt"
- "reflect"
- "sync"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime"
- )
- var sharedTranscoders transcoders
- var rawTypeTranscodeFuncs = map[reflect.Type]func(reflect.Value) error{
- reflect.TypeFor[runtime.RawExtension](): func(rv reflect.Value) error {
- if !rv.CanAddr() {
- return nil
- }
- re := rv.Addr().Interface().(*runtime.RawExtension)
- if re.Raw == nil {
- // When Raw is nil it encodes to null. Don't change nil Raw values during
- // transcoding, they would have unmarshalled from JSON as nil too.
- return nil
- }
- j, err := re.MarshalJSON()
- if err != nil {
- return fmt.Errorf("failed to transcode RawExtension to JSON: %w", err)
- }
- re.Raw = j
- return nil
- },
- reflect.TypeFor[metav1.FieldsV1](): func(rv reflect.Value) error {
- if !rv.CanAddr() {
- return nil
- }
- fields := rv.Addr().Interface().(*metav1.FieldsV1)
- if fields.Raw == nil {
- // When Raw is nil it encodes to null. Don't change nil Raw values during
- // transcoding, they would have unmarshalled from JSON as nil too.
- return nil
- }
- j, err := fields.MarshalJSON()
- if err != nil {
- return fmt.Errorf("failed to transcode FieldsV1 to JSON: %w", err)
- }
- fields.Raw = j
- return nil
- },
- }
- func transcodeRawTypes(v interface{}) error {
- if v == nil {
- return nil
- }
- rv := reflect.ValueOf(v)
- return sharedTranscoders.getTranscoder(rv.Type()).fn(rv)
- }
- type transcoder struct {
- fn func(rv reflect.Value) error
- }
- var noop = transcoder{
- fn: func(reflect.Value) error {
- return nil
- },
- }
- type transcoders struct {
- lock sync.RWMutex
- m map[reflect.Type]**transcoder
- }
- func (ts *transcoders) getTranscoder(rt reflect.Type) transcoder {
- ts.lock.RLock()
- tpp, ok := ts.m[rt]
- ts.lock.RUnlock()
- if ok {
- return **tpp
- }
- ts.lock.Lock()
- defer ts.lock.Unlock()
- tp := ts.getTranscoderLocked(rt)
- return *tp
- }
- func (ts *transcoders) getTranscoderLocked(rt reflect.Type) *transcoder {
- if tpp, ok := ts.m[rt]; ok {
- // A transcoder for this type was cached while waiting to acquire the lock.
- return *tpp
- }
- // Cache the transcoder now, before populating fn, so that circular references between types
- // don't overflow the call stack.
- t := new(transcoder)
- if ts.m == nil {
- ts.m = make(map[reflect.Type]**transcoder)
- }
- ts.m[rt] = &t
- for rawType, fn := range rawTypeTranscodeFuncs {
- if rt == rawType {
- t = &transcoder{fn: fn}
- return t
- }
- }
- switch rt.Kind() {
- case reflect.Array:
- te := ts.getTranscoderLocked(rt.Elem())
- rtlen := rt.Len()
- if rtlen == 0 || te == &noop {
- t = &noop
- break
- }
- t.fn = func(rv reflect.Value) error {
- for i := 0; i < rtlen; i++ {
- if err := te.fn(rv.Index(i)); err != nil {
- return err
- }
- }
- return nil
- }
- case reflect.Interface:
- // Any interface value might have a dynamic type involving RawExtension. It needs to
- // be checked.
- t.fn = func(rv reflect.Value) error {
- if rv.IsNil() {
- return nil
- }
- rv = rv.Elem()
- // The interface element's type is dynamic so its transcoder can't be
- // determined statically.
- return ts.getTranscoder(rv.Type()).fn(rv)
- }
- case reflect.Map:
- rtk := rt.Key()
- tk := ts.getTranscoderLocked(rtk)
- rte := rt.Elem()
- te := ts.getTranscoderLocked(rte)
- if tk == &noop && te == &noop {
- t = &noop
- break
- }
- t.fn = func(rv reflect.Value) error {
- iter := rv.MapRange()
- rvk := reflect.New(rtk).Elem()
- rve := reflect.New(rte).Elem()
- for iter.Next() {
- rvk.SetIterKey(iter)
- if err := tk.fn(rvk); err != nil {
- return err
- }
- rve.SetIterValue(iter)
- if err := te.fn(rve); err != nil {
- return err
- }
- }
- return nil
- }
- case reflect.Pointer:
- te := ts.getTranscoderLocked(rt.Elem())
- if te == &noop {
- t = &noop
- break
- }
- t.fn = func(rv reflect.Value) error {
- if rv.IsNil() {
- return nil
- }
- return te.fn(rv.Elem())
- }
- case reflect.Slice:
- te := ts.getTranscoderLocked(rt.Elem())
- if te == &noop {
- t = &noop
- break
- }
- t.fn = func(rv reflect.Value) error {
- for i := 0; i < rv.Len(); i++ {
- if err := te.fn(rv.Index(i)); err != nil {
- return err
- }
- }
- return nil
- }
- case reflect.Struct:
- type fieldTranscoder struct {
- Index int
- Transcoder *transcoder
- }
- var fieldTranscoders []fieldTranscoder
- for i := 0; i < rt.NumField(); i++ {
- f := rt.Field(i)
- tf := ts.getTranscoderLocked(f.Type)
- if tf == &noop {
- continue
- }
- fieldTranscoders = append(fieldTranscoders, fieldTranscoder{Index: i, Transcoder: tf})
- }
- if len(fieldTranscoders) == 0 {
- t = &noop
- break
- }
- t.fn = func(rv reflect.Value) error {
- for _, ft := range fieldTranscoders {
- if err := ft.Transcoder.fn(rv.Field(ft.Index)); err != nil {
- return err
- }
- }
- return nil
- }
- default:
- t = &noop
- }
- return t
- }
|