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 }