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  }