github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/atomickit/bytestring.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package atomickit
     7  
     8  import (
     9  	"reflect"
    10  	"sync/atomic"
    11  	"unsafe"
    12  
    13  	"github.com/insolar/vanilla/longbits"
    14  	"github.com/insolar/vanilla/throw"
    15  )
    16  
    17  func NewOnceByteString(v longbits.ByteString) OnceByteString {
    18  	header := (*reflect.StringHeader)(unsafe.Pointer(&v))
    19  
    20  	return OnceByteString{
    21  		len: Int{ len(v) },
    22  		str: unsafe.Pointer(header.Data),
    23  	}
    24  }
    25  
    26  type OnceByteString struct {
    27  	len Int
    28  	str unsafe.Pointer
    29  }
    30  
    31  func (p *OnceByteString) Load() longbits.ByteString {
    32  	s, _ := p.TryLoad()
    33  	return s
    34  }
    35  
    36  const (
    37  	strLenUpdating = -1
    38  	strLenZero = -2
    39  )
    40  
    41  func (p *OnceByteString) TryLoad() (longbits.ByteString, bool) {
    42  	ln := p.len.Load()
    43  	if ln <= 0 {
    44  		return "", ln == strLenZero
    45  	}
    46  
    47  	str := atomic.LoadPointer(&p.str)
    48  	if str == nil {
    49  		panic(throw.IllegalState())
    50  	}
    51  
    52  	var result string
    53  	resultHeader := (*reflect.StringHeader)(unsafe.Pointer(&result))
    54  	resultHeader.Len = ln
    55  	resultHeader.Data = uintptr(str)
    56  
    57  	return longbits.WrapStr(result), true
    58  }
    59  
    60  func (p *OnceByteString) StoreOnce(v longbits.ByteString) bool {
    61  	switch header := (*reflect.StringHeader)(unsafe.Pointer(&v)); {
    62  	case header.Len == 0:
    63  		if p.len.CompareAndSwap(0, strLenZero) {
    64  			return true
    65  		}
    66  		for i := 0; p.len.Load() == strLenUpdating; i++ {
    67  			spinWait(i)
    68  		}
    69  		return false
    70  	case !p.len.CompareAndSwap(0, strLenUpdating):
    71  		return false
    72  	default:
    73  		atomic.StorePointer(&p.str, unsafe.Pointer(header.Data))
    74  		p.len.Store(header.Len)
    75  		return true
    76  	}
    77  }
    78  
    79  func (p *OnceByteString) MustStore(v longbits.ByteString) {
    80  	if !p.StoreOnce(v) {
    81  		panic(throw.IllegalState())
    82  	}
    83  }
    84  
    85  func (p *OnceByteString) CompareAndStore(new longbits.ByteString) bool {
    86  	switch header := (*reflect.StringHeader)(unsafe.Pointer(&new)); {
    87  	case header.Len == 0:
    88  		return p.len.Load() == 0
    89  	case !p.len.CompareAndSwap(0, -1):
    90  		return p.Load() == new
    91  	default:
    92  		atomic.StorePointer(&p.str, unsafe.Pointer(header.Data))
    93  		p.len.Store(header.Len)
    94  		return true
    95  	}
    96  }
    97  
    98  func (p *OnceByteString) String() string {
    99  	return p.Load().String()
   100  }