github.com/safing/portbase@v0.19.5/formats/dsd/dsd.go (about) 1 package dsd 2 3 // dynamic structured data 4 // check here for some benchmarks: https://github.com/alecthomas/go_serialization_benchmarks 5 6 import ( 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io" 11 12 "github.com/fxamacker/cbor/v2" 13 "github.com/ghodss/yaml" 14 "github.com/vmihailenco/msgpack/v5" 15 16 "github.com/safing/portbase/formats/varint" 17 "github.com/safing/portbase/utils" 18 ) 19 20 // Load loads an dsd structured data blob into the given interface. 21 func Load(data []byte, t interface{}) (format uint8, err error) { 22 format, read, err := loadFormat(data) 23 if err != nil { 24 return 0, err 25 } 26 27 _, ok := ValidateSerializationFormat(format) 28 if ok { 29 return format, LoadAsFormat(data[read:], format, t) 30 } 31 return DecompressAndLoad(data[read:], format, t) 32 } 33 34 // LoadAsFormat loads a data blob into the interface using the specified format. 35 func LoadAsFormat(data []byte, format uint8, t interface{}) (err error) { 36 switch format { 37 case RAW: 38 return ErrIsRaw 39 case JSON: 40 err = json.Unmarshal(data, t) 41 if err != nil { 42 return fmt.Errorf("dsd: failed to unpack json: %w, data: %s", err, utils.SafeFirst16Bytes(data)) 43 } 44 return nil 45 case YAML: 46 err = yaml.Unmarshal(data, t) 47 if err != nil { 48 return fmt.Errorf("dsd: failed to unpack yaml: %w, data: %s", err, utils.SafeFirst16Bytes(data)) 49 } 50 return nil 51 case CBOR: 52 err = cbor.Unmarshal(data, t) 53 if err != nil { 54 return fmt.Errorf("dsd: failed to unpack cbor: %w, data: %s", err, utils.SafeFirst16Bytes(data)) 55 } 56 return nil 57 case MsgPack: 58 err = msgpack.Unmarshal(data, t) 59 if err != nil { 60 return fmt.Errorf("dsd: failed to unpack msgpack: %w, data: %s", err, utils.SafeFirst16Bytes(data)) 61 } 62 return nil 63 case GenCode: 64 genCodeStruct, ok := t.(GenCodeCompatible) 65 if !ok { 66 return errors.New("dsd: gencode is not supported by the given data structure") 67 } 68 _, err = genCodeStruct.GenCodeUnmarshal(data) 69 if err != nil { 70 return fmt.Errorf("dsd: failed to unpack gencode: %w, data: %s", err, utils.SafeFirst16Bytes(data)) 71 } 72 return nil 73 default: 74 return ErrIncompatibleFormat 75 } 76 } 77 78 func loadFormat(data []byte) (format uint8, read int, err error) { 79 format, read, err = varint.Unpack8(data) 80 if err != nil { 81 return 0, 0, err 82 } 83 if len(data) <= read { 84 return 0, 0, io.ErrUnexpectedEOF 85 } 86 87 return format, read, nil 88 } 89 90 // Dump stores the interface as a dsd formatted data structure. 91 func Dump(t interface{}, format uint8) ([]byte, error) { 92 return DumpIndent(t, format, "") 93 } 94 95 // DumpIndent stores the interface as a dsd formatted data structure with indentation, if available. 96 func DumpIndent(t interface{}, format uint8, indent string) ([]byte, error) { 97 data, err := dumpWithoutIdentifier(t, format, indent) 98 if err != nil { 99 return nil, err 100 } 101 102 // TODO: Find a better way to do this. 103 return append(varint.Pack8(format), data...), nil 104 } 105 106 func dumpWithoutIdentifier(t interface{}, format uint8, indent string) ([]byte, error) { 107 format, ok := ValidateSerializationFormat(format) 108 if !ok { 109 return nil, ErrIncompatibleFormat 110 } 111 112 var data []byte 113 var err error 114 switch format { 115 case RAW: 116 var ok bool 117 data, ok = t.([]byte) 118 if !ok { 119 return nil, ErrIncompatibleFormat 120 } 121 case JSON: 122 // TODO: use SetEscapeHTML(false) 123 if indent != "" { 124 data, err = json.MarshalIndent(t, "", indent) 125 } else { 126 data, err = json.Marshal(t) 127 } 128 if err != nil { 129 return nil, err 130 } 131 case YAML: 132 data, err = yaml.Marshal(t) 133 if err != nil { 134 return nil, err 135 } 136 case CBOR: 137 data, err = cbor.Marshal(t) 138 if err != nil { 139 return nil, err 140 } 141 case MsgPack: 142 data, err = msgpack.Marshal(t) 143 if err != nil { 144 return nil, err 145 } 146 case GenCode: 147 genCodeStruct, ok := t.(GenCodeCompatible) 148 if !ok { 149 return nil, errors.New("dsd: gencode is not supported by the given data structure") 150 } 151 data, err = genCodeStruct.GenCodeMarshal(nil) 152 if err != nil { 153 return nil, fmt.Errorf("dsd: failed to pack gencode struct: %w", err) 154 } 155 default: 156 return nil, ErrIncompatibleFormat 157 } 158 159 return data, nil 160 }