github.com/jxskiss/gopkg@v0.17.3/sqlutil/types_binary.go (about) 1 package sqlutil 2 3 import ( 4 "database/sql/driver" 5 "fmt" 6 "sync" 7 "sync/atomic" 8 "unsafe" 9 ) 10 11 // NewLazyBinary creates a new lazy binary wrapper, delaying the 12 // unmarshalling work until it is first needed. 13 func NewLazyBinary(raw []byte) LazyBinary { 14 return LazyBinary{raw: raw} 15 } 16 17 // LazyBinary is a lazy wrapper around a binary value, where the underlying 18 // object will be unmarshalled the first time it is needed and cached. 19 // It implements sql/driver.Valuer and sql.Scanner. 20 // 21 // LazyBinary provides same concurrency safety as []byte, it's safe for 22 // concurrent read, but not safe for concurrent write or read/write. 23 // 24 // See types_test.go for example usage. 25 type LazyBinary struct { 26 raw []byte 27 obj unsafe.Pointer // *lazyobj 28 } 29 30 type lazyobj struct { 31 mu sync.Mutex 32 data interface{} 33 err error 34 } 35 36 // Value implements driver.Valuer interface. 37 func (p LazyBinary) Value() (driver.Value, error) { 38 return p.raw, nil 39 } 40 41 // Scan implements sql.Scanner interface. 42 func (p *LazyBinary) Scan(src interface{}) error { 43 if src != nil { 44 // NOTE 45 // We MUST copy the src byte slice here, database/sql.Scanner says: 46 // 47 // Reference types such as []byte are only valid until the next call to Scan 48 // and should not be retained. Their underlying memory is owned by the driver. 49 // If retention is necessary, copy their values before the next call to Scan. 50 51 b, ok := src.([]byte) 52 if !ok { 53 return fmt.Errorf("sqlutil: wants []byte but got %T", src) 54 } 55 tmp := make([]byte, len(b)) 56 copy(tmp, b) 57 p.raw = tmp 58 atomic.StorePointer(&p.obj, nil) 59 } 60 return nil 61 } 62 63 // Unmarshaler is a function which unmarshalls data from a byte slice. 64 type Unmarshaler func([]byte) (interface{}, error) 65 66 // GetBytes returns the underlying byte slice. 67 func (p *LazyBinary) GetBytes() []byte { 68 return p.raw 69 } 70 71 // Get returns the underlying data wrapped by the LazyBinary wrapper, 72 // if the data has not been unmarshalled, it will be unmarshalled using 73 // the provided unmarshalFunc. 74 // The unmarshalling work will do only once, the result data and error 75 // will be cached and reused for further calling. 76 func (p *LazyBinary) Get(unmarshalFunc Unmarshaler) (interface{}, error) { 77 obj, created := p.getobj() 78 defer obj.mu.Unlock() 79 if created { 80 obj.data, obj.err = unmarshalFunc(p.raw) 81 return obj.data, obj.err 82 } 83 obj.mu.Lock() 84 return obj.data, obj.err 85 } 86 87 // Set sets the data and marshaled bytes to the LazyBinary wrapper. 88 // If the param data is nil, the underlying cache will be removed. 89 func (p *LazyBinary) Set(b []byte, data interface{}) { 90 p.raw = b 91 if data == nil { 92 atomic.StorePointer(&p.obj, nil) 93 return 94 } 95 obj, created := p.getobj() 96 if !created { 97 obj.mu.Lock() 98 } 99 obj.data = data 100 obj.err = nil 101 obj.mu.Unlock() 102 } 103 104 func (p *LazyBinary) getobj() (*lazyobj, bool) { 105 ptr := atomic.LoadPointer(&p.obj) 106 if ptr != nil { 107 return (*lazyobj)(ptr), false 108 } 109 tmp := &lazyobj{} 110 tmp.mu.Lock() 111 swapped := atomic.CompareAndSwapPointer(&p.obj, nil, unsafe.Pointer(tmp)) 112 if swapped { 113 return tmp, true 114 } 115 ptr = atomic.LoadPointer(&p.obj) 116 return (*lazyobj)(ptr), false 117 }