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 }