github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/kbfscodec/codec.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  	"bytes"
     9  	"path/filepath"
    10  	"reflect"
    11  
    12  	"github.com/keybase/client/go/kbfs/ioutil"
    13  )
    14  
    15  // ExtCode is used to register codec extensions
    16  type ExtCode uint64
    17  
    18  // these track the start of a range of unique ExtCodes for various
    19  // types of extensions.
    20  const (
    21  	ExtCodeOpsRangeStart  = 1
    22  	ExtCodeListRangeStart = 101
    23  )
    24  
    25  // Codec encodes and decodes arbitrary data
    26  type Codec interface {
    27  	// Decode unmarshals the given buffer into the given object, if possible.
    28  	Decode(buf []byte, obj interface{}) error
    29  	// Encode marshals the given object into a returned buffer.
    30  	Encode(obj interface{}) ([]byte, error)
    31  	// RegisterType should be called for all types that are stored
    32  	// under ambiguous types (like interface{} or nil interface) in a
    33  	// struct that will be encoded/decoded by the codec.  Each must
    34  	// have a unique ExtCode.  Types that include other extension
    35  	// types are not supported.
    36  	RegisterType(rt reflect.Type, code ExtCode)
    37  	// RegisterIfaceSliceType should be called for all encoded slices
    38  	// that contain ambiguous interface types.  Each must have a
    39  	// unique ExtCode.  Slice element types that include other
    40  	// extension types are not supported.
    41  	//
    42  	// If non-nil, typer is used to do a type assertion during
    43  	// decoding, to convert the encoded value into the value expected
    44  	// by the rest of the code.  This is needed, for example, when the
    45  	// codec cannot decode interface types to their desired pointer
    46  	// form.
    47  	RegisterIfaceSliceType(rt reflect.Type, code ExtCode,
    48  		typer func(interface{}) reflect.Value)
    49  }
    50  
    51  // Equal returns whether or not the given objects serialize to the
    52  // same byte string. x or y (or both) can be nil.
    53  func Equal(c Codec, x, y interface{}) (bool, error) {
    54  	xBuf, err := c.Encode(x)
    55  	if err != nil {
    56  		return false, err
    57  	}
    58  	yBuf, err := c.Encode(y)
    59  	if err != nil {
    60  		return false, err
    61  	}
    62  	return bytes.Equal(xBuf, yBuf), nil
    63  }
    64  
    65  // Update encodes src into a byte string, and then decode it into the
    66  // object pointed to by dstPtr.
    67  func Update(c Codec, dstPtr interface{}, src interface{}) error {
    68  	buf, err := c.Encode(src)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	err = c.Decode(buf, dstPtr)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	return nil
    77  }
    78  
    79  // SerializeToFile serializes the given object and writes it to the
    80  // given file, making its parent directory first if necessary.
    81  func SerializeToFile(c Codec, obj interface{}, path string) error {
    82  	err := ioutil.MkdirAll(filepath.Dir(path), 0700)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	buf, err := c.Encode(obj)
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	return ioutil.WriteSerializedFile(path, buf, 0600)
    93  }
    94  
    95  // SerializeToFileIfNotExist is like SerializeToFile, but does nothing
    96  // if the file already exists.
    97  func SerializeToFileIfNotExist(c Codec, obj interface{}, path string) error {
    98  	_, err := ioutil.Stat(path)
    99  	switch {
   100  	case ioutil.IsExist(err):
   101  		return nil
   102  	case ioutil.IsNotExist(err):
   103  		// Continue.
   104  	case err != nil:
   105  		return err
   106  	}
   107  
   108  	return SerializeToFile(c, obj, path)
   109  }
   110  
   111  // DeserializeFromFile deserializes the given file into the object
   112  // pointed to by objPtr. It may return an error for which
   113  // ioutil.IsNotExist() returns true.
   114  func DeserializeFromFile(c Codec, path string, objPtr interface{}) error {
   115  	data, err := ioutil.ReadFile(path)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	err = c.Decode(data, objPtr)
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	return nil
   126  }