github.com/safing/portbase@v0.19.5/database/record/wrapper.go (about) 1 package record 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 8 "github.com/safing/portbase/container" 9 "github.com/safing/portbase/database/accessor" 10 "github.com/safing/portbase/formats/dsd" 11 "github.com/safing/portbase/formats/varint" 12 ) 13 14 // Wrapper wraps raw data and implements the Record interface. 15 type Wrapper struct { 16 Base 17 sync.Mutex 18 19 Format uint8 20 Data []byte 21 } 22 23 // NewRawWrapper returns a record wrapper for the given data, including metadata. This is normally only used by storage backends when loading records. 24 func NewRawWrapper(database, key string, data []byte) (*Wrapper, error) { 25 version, offset, err := varint.Unpack8(data) 26 if err != nil { 27 return nil, err 28 } 29 if version != 1 { 30 return nil, fmt.Errorf("incompatible record version: %d", version) 31 } 32 33 metaSection, n, err := varint.GetNextBlock(data[offset:]) 34 if err != nil { 35 return nil, fmt.Errorf("could not get meta section: %w", err) 36 } 37 offset += n 38 39 newMeta := &Meta{} 40 _, err = dsd.Load(metaSection, newMeta) 41 if err != nil { 42 return nil, fmt.Errorf("could not unmarshal meta section: %w", err) 43 } 44 45 var format uint8 = dsd.RAW 46 if !newMeta.IsDeleted() { 47 format, n, err = varint.Unpack8(data[offset:]) 48 if err != nil { 49 return nil, fmt.Errorf("could not get dsd format: %w", err) 50 } 51 offset += n 52 } 53 54 return &Wrapper{ 55 Base{ 56 database, 57 key, 58 newMeta, 59 }, 60 sync.Mutex{}, 61 format, 62 data[offset:], 63 }, nil 64 } 65 66 // NewWrapper returns a new record wrapper for the given data. 67 func NewWrapper(key string, meta *Meta, format uint8, data []byte) (*Wrapper, error) { 68 dbName, dbKey := ParseKey(key) 69 70 return &Wrapper{ 71 Base{ 72 dbName: dbName, 73 dbKey: dbKey, 74 meta: meta, 75 }, 76 sync.Mutex{}, 77 format, 78 data, 79 }, nil 80 } 81 82 // Marshal marshals the object, without the database key or metadata. 83 func (w *Wrapper) Marshal(r Record, format uint8) ([]byte, error) { 84 if w.Meta() == nil { 85 return nil, errors.New("missing meta") 86 } 87 88 if w.Meta().Deleted > 0 { 89 return nil, nil 90 } 91 92 if format != dsd.AUTO && format != w.Format { 93 return nil, errors.New("could not dump model, wrapped object format mismatch") 94 } 95 96 data := make([]byte, len(w.Data)+1) 97 data[0] = w.Format 98 copy(data[1:], w.Data) 99 100 return data, nil 101 } 102 103 // MarshalRecord packs the object, including metadata, into a byte array for saving in a database. 104 func (w *Wrapper) MarshalRecord(r Record) ([]byte, error) { 105 // Duplication necessary, as the version from Base would call Base.Marshal instead of Wrapper.Marshal 106 107 if w.Meta() == nil { 108 return nil, errors.New("missing meta") 109 } 110 111 // version 112 c := container.New([]byte{1}) 113 114 // meta 115 metaSection, err := dsd.Dump(w.meta, dsd.GenCode) 116 if err != nil { 117 return nil, err 118 } 119 c.AppendAsBlock(metaSection) 120 121 // data 122 dataSection, err := w.Marshal(r, dsd.AUTO) 123 if err != nil { 124 return nil, err 125 } 126 c.Append(dataSection) 127 128 return c.CompileData(), nil 129 } 130 131 // IsWrapped returns whether the record is a Wrapper. 132 func (w *Wrapper) IsWrapped() bool { 133 return true 134 } 135 136 // Unwrap unwraps data into a record. 137 func Unwrap(wrapped, r Record) error { 138 wrapper, ok := wrapped.(*Wrapper) 139 if !ok { 140 return fmt.Errorf("cannot unwrap %T", wrapped) 141 } 142 143 err := dsd.LoadAsFormat(wrapper.Data, wrapper.Format, r) 144 if err != nil { 145 return fmt.Errorf("failed to unwrap %T: %w", r, err) 146 } 147 148 r.SetKey(wrapped.Key()) 149 r.SetMeta(wrapped.Meta()) 150 151 return nil 152 } 153 154 // GetAccessor returns an accessor for this record, if available. 155 func (w *Wrapper) GetAccessor(self Record) accessor.Accessor { 156 if w.Format == dsd.JSON && len(w.Data) > 0 { 157 return accessor.NewJSONBytesAccessor(&w.Data) 158 } 159 return nil 160 }