github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/kbfscodec/codec_msgpack.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package kbfscodec
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  
    11  	"github.com/keybase/go-codec/codec"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  // ext is a no-op extension that's useful for tagging interfaces with
    16  // a type.  Note that it cannot be used for anything that has nested
    17  // extensions.
    18  type ext struct {
    19  	// codec should NOT encode extension types
    20  	codec Codec
    21  }
    22  
    23  // ConvertExt implements the codec.Ext interface for ext.
    24  func (e ext) ConvertExt(v interface{}) interface{} {
    25  	panic("ConvertExt not supported")
    26  }
    27  
    28  // UpdateExt implements the codec.Ext interface for ext.
    29  func (e ext) UpdateExt(dest interface{}, v interface{}) {
    30  	panic("UpdateExt not supported")
    31  }
    32  
    33  // WriteExt implements the codec.Ext interface for ext.
    34  func (e ext) WriteExt(v interface{}) (buf []byte) {
    35  	buf, err := e.codec.Encode(v)
    36  	if err != nil {
    37  		panic(fmt.Sprintf("Couldn't encode data in %v", v))
    38  	}
    39  	return buf
    40  }
    41  
    42  // ReadExt implements the codec.Ext interface for ext.
    43  func (e ext) ReadExt(v interface{}, buf []byte) {
    44  	err := e.codec.Decode(buf, v)
    45  	if err != nil {
    46  		panic(fmt.Sprintf("Couldn't decode data into %v", v))
    47  	}
    48  }
    49  
    50  // extSlice is an extension that's useful for slices that contain
    51  // extension types as elements.  The contained extension types cannot
    52  // themselves contain nested extension types.
    53  type extSlice struct {
    54  	// codec SHOULD encode extension types
    55  	codec Codec
    56  	typer func(interface{}) reflect.Value
    57  }
    58  
    59  // ConvertExt implements the codec.Ext interface for extSlice.
    60  func (es extSlice) ConvertExt(v interface{}) interface{} {
    61  	panic("ConvertExt not supported")
    62  }
    63  
    64  // UpdateExt implements the codec.Ext interface for extSlice.
    65  func (es extSlice) UpdateExt(dest interface{}, v interface{}) {
    66  	panic("UpdateExt not supported")
    67  }
    68  
    69  // WriteExt implements the codec.Ext interface for extSlice.
    70  func (es extSlice) WriteExt(v interface{}) (buf []byte) {
    71  	val := reflect.ValueOf(v)
    72  	if val.Kind() != reflect.Slice {
    73  		panic(fmt.Sprintf("Non-slice passed to extSlice.WriteExt %v",
    74  			val.Kind()))
    75  	}
    76  
    77  	ifaceArray := make([]interface{}, val.Len())
    78  	for i := 0; i < val.Len(); i++ {
    79  		ifaceArray[i] = val.Index(i).Interface()
    80  	}
    81  
    82  	buf, err := es.codec.Encode(ifaceArray)
    83  	if err != nil {
    84  		panic(fmt.Sprintf("Couldn't encode data in %v", v))
    85  	}
    86  	return buf
    87  }
    88  
    89  // ReadExt implements the codec.Ext interface for extSlice.
    90  func (es extSlice) ReadExt(v interface{}, buf []byte) {
    91  	// ReadExt actually receives a pointer to the list
    92  	val := reflect.ValueOf(v)
    93  	if val.Kind() != reflect.Ptr {
    94  		panic(fmt.Sprintf("Non-pointer passed to extSlice.ReadExt: %v",
    95  			val.Kind()))
    96  	}
    97  
    98  	val = val.Elem()
    99  	if val.Kind() != reflect.Slice {
   100  		panic(fmt.Sprintf("Non-slice passed to extSlice.ReadExt %v",
   101  			val.Kind()))
   102  	}
   103  
   104  	var ifaceArray []interface{}
   105  	err := es.codec.Decode(buf, &ifaceArray)
   106  	if err != nil {
   107  		panic(fmt.Sprintf("Couldn't decode data into %v", v))
   108  	}
   109  
   110  	if len(ifaceArray) > 0 {
   111  		val.Set(reflect.MakeSlice(val.Type(), len(ifaceArray),
   112  			len(ifaceArray)))
   113  	}
   114  
   115  	for i, v := range ifaceArray {
   116  		if es.typer != nil {
   117  			val.Index(i).Set(es.typer(v))
   118  		} else {
   119  			val.Index(i).Set(reflect.ValueOf(v))
   120  		}
   121  	}
   122  }
   123  
   124  // CodecMsgpack implements the Codec interface using msgpack
   125  // marshaling and unmarshaling.
   126  type CodecMsgpack struct {
   127  	h        codec.Handle
   128  	ExtCodec *CodecMsgpack
   129  }
   130  
   131  // newCodecMsgpackHelper constructs a new CodecMsgpack that may or may
   132  // not handle unknown fields.
   133  func newCodecMsgpackHelper(handleUnknownFields bool) *CodecMsgpack {
   134  	handle := codec.MsgpackHandle{}
   135  	handle.Canonical = true
   136  	handle.WriteExt = true
   137  	handle.DecodeUnknownFields = handleUnknownFields
   138  	handle.EncodeUnknownFields = handleUnknownFields
   139  
   140  	// save a codec that doesn't write extensions, so that we can just
   141  	// call Encode/Decode when we want to (de)serialize extension
   142  	// types.
   143  	handleNoExt := handle
   144  	handleNoExt.WriteExt = false
   145  	ExtCodec := &CodecMsgpack{&handleNoExt, nil}
   146  	return &CodecMsgpack{&handle, ExtCodec}
   147  }
   148  
   149  // NewMsgpack constructs a new CodecMsgpack.
   150  func NewMsgpack() *CodecMsgpack {
   151  	return newCodecMsgpackHelper(true)
   152  }
   153  
   154  // NewMsgpackNoUnknownFields constructs a new CodecMsgpack that
   155  // doesn't handle unknown fields.
   156  func NewMsgpackNoUnknownFields() *CodecMsgpack {
   157  	return newCodecMsgpackHelper(false)
   158  }
   159  
   160  // Decode implements the Codec interface for CodecMsgpack
   161  func (c *CodecMsgpack) Decode(buf []byte, obj interface{}) error {
   162  	err := codec.NewDecoderBytes(buf, c.h).Decode(obj)
   163  	if err != nil {
   164  		return errors.Wrap(err, "failed to decode")
   165  	}
   166  	return nil
   167  }
   168  
   169  // Encode implements the Codec interface for CodecMsgpack
   170  func (c *CodecMsgpack) Encode(obj interface{}) (buf []byte, err error) {
   171  	err = codec.NewEncoderBytes(&buf, c.h).Encode(obj)
   172  	if err != nil {
   173  		return nil, errors.Wrap(err, "failed to encode")
   174  	}
   175  	return buf, nil
   176  }
   177  
   178  // RegisterType implements the Codec interface for CodecMsgpack
   179  func (c *CodecMsgpack) RegisterType(rt reflect.Type, code ExtCode) {
   180  	err := c.h.(*codec.MsgpackHandle).SetBytesExt(
   181  		rt, uint64(code), ext{c.ExtCodec})
   182  	if err != nil {
   183  		panic(err)
   184  	}
   185  }
   186  
   187  // RegisterIfaceSliceType implements the Codec interface for CodecMsgpack
   188  func (c *CodecMsgpack) RegisterIfaceSliceType(
   189  	rt reflect.Type, code ExtCode, typer func(interface{}) reflect.Value) {
   190  	err := c.h.(*codec.MsgpackHandle).SetBytesExt(
   191  		rt, uint64(code), extSlice{c, typer})
   192  	if err != nil {
   193  		panic(err)
   194  	}
   195  }