add ThreadLocal and Context

This commit is contained in:
tursom 2022-04-02 15:25:35 +08:00
parent 9602d80b4e
commit 81cc3651c8
30 changed files with 601 additions and 72 deletions

View File

@ -8,27 +8,29 @@ import (
type (
//HashMap
//TODO impl
HashMap[K lang.Object, V any] struct {
NodeMap[K, V]
slot []*hashMapNode[K, V]
loadFactor float32
size int
}
abstractHashMapNode[K lang.Object, V any] struct {
m *HashMap[K, V]
hash int32
}
hashMapNode[K lang.Object, V any] struct {
lang.BaseObject
m *HashMap[K, V]
abstractHashMapNode[K, V]
key K
value V
next *hashMapNode[K, V]
hash int32
}
emptyHashMapSlot[K lang.Object, V any] struct {
lang.BaseObject
m *HashMap[K, V]
hashCode int32
index int
abstractHashMapNode[K, V]
}
)
@ -45,23 +47,23 @@ func NewHashMapInitCap[K lang.Object, V any](initialCapacity int, loadFactor flo
return m
}
func (h *HashMap[K, V]) String() string {
return MapToString[K, V](h).String()
func (m *HashMap[K, V]) String() string {
return MapToString[K, V](m).String()
}
func (h *HashMap[K, V]) findSlot(k K) MapNode[K, V] {
func (m *HashMap[K, V]) findSlot(k K) MapNode[K, V] {
hashCode := lang.HashCode(k)
hashCode ^= hashCode >> 16
index := int(hashCode) % len(h.slot)
root := h.slot[index]
index := int(hashCode) % len(m.slot)
root := m.slot[index]
if root == nil {
return &emptyHashMapSlot[K, V]{m: h, hashCode: hashCode, index: index}
return &emptyHashMapSlot[K, V]{abstractHashMapNode: abstractHashMapNode[K, V]{m: m, hash: hashCode}}
}
return root
}
func (h *HashMap[K, V]) Loop(f func(K, V) exceptions.Exception) (err exceptions.Exception) {
for _, node := range h.slot {
func (m *HashMap[K, V]) Loop(f func(K, V) exceptions.Exception) (err exceptions.Exception) {
for _, node := range m.slot {
for node != nil {
err = f(node.GetKey(), node.GetValue())
if err != nil {
@ -73,32 +75,74 @@ func (h *HashMap[K, V]) Loop(f func(K, V) exceptions.Exception) (err exceptions.
return
}
func (m *HashMap[K, V]) resize() {
slot := m.slot
resize := len(slot)
load := int(float32(m.size) / m.loadFactor)
for load < resize {
resize >>= 1
}
for load > resize {
resize <<= 1
}
if resize == len(slot) {
return
}
m.slot = make([]*hashMapNode[K, V], resize)
for _, node := range slot {
for node != nil {
next := node.next
index := node.index()
node.next = m.slot[index]
m.slot[index] = node
node = next
}
}
}
func (m *HashMap[K, V]) resizeFromRemove() {
if len(m.slot) > int(float32(m.size)/m.loadFactor) {
m.resize()
}
}
func (m *HashMap[K, V]) resizeFromAdd() {
if len(m.slot) < int(float32(m.size)/m.loadFactor) {
m.resize()
}
}
func (n *abstractHashMapNode[K, V]) index() int {
return int(n.hash) % len(n.m.slot)
}
func (e *emptyHashMapSlot[K, V]) GetKey() K {
//TODO implement me
panic("implement me")
return lang.Nil[K]()
}
func (e *emptyHashMapSlot[K, V]) GetValue() V {
//TODO implement me
panic("implement me")
return lang.Nil[V]()
}
func (e *emptyHashMapSlot[K, V]) SetValue(value V) {
//TODO implement me
panic("implement me")
func (e *emptyHashMapSlot[K, V]) SetValue(_ V) {
}
func (e *emptyHashMapSlot[K, V]) CreateNext(key K) MapNode[K, V] {
index := e.index()
node := &hashMapNode[K, V]{
key: key,
hash: lang.HashCode(key),
key: key,
abstractHashMapNode: abstractHashMapNode[K, V]{m: e.m, hash: lang.HashCode(key)},
next: e.m.slot[index],
}
e.m.slot[e.index] = node
e.m.slot[index] = node
e.m.size++
e.m.resizeFromAdd()
return node
}
func (e *emptyHashMapSlot[K, V]) GetNext() MapNode[K, V] {
node := e.m.slot[e.index]
node := e.m.slot[e.index()]
// required
if node == nil {
return nil
}
@ -106,9 +150,11 @@ func (e *emptyHashMapSlot[K, V]) GetNext() MapNode[K, V] {
}
func (e *emptyHashMapSlot[K, V]) RemoveNext() {
node := e.m.slot[e.index]
node := e.m.slot[e.index()]
if node != nil {
e.m.slot[e.index] = node.next
e.m.slot[e.index()] = node.next
e.m.size--
e.m.resizeFromRemove()
}
}
@ -129,7 +175,16 @@ func (s *hashMapNode[K, V]) SetValue(value V) {
}
func (s *hashMapNode[K, V]) CreateNext(key K) MapNode[K, V] {
s.next = &hashMapNode[K, V]{key: key, next: s.next, hash: lang.HashCode(key)}
s.next = &hashMapNode[K, V]{
key: key,
next: s.next,
abstractHashMapNode: abstractHashMapNode[K, V]{
m: s.m,
hash: lang.HashCode(key),
},
}
s.m.size++
s.m.resizeFromAdd()
return s.next
}
@ -143,5 +198,7 @@ func (s *hashMapNode[K, V]) GetNext() MapNode[K, V] {
func (s *hashMapNode[K, V]) RemoveNext() {
if s.next != nil {
s.next = s.next.next
s.m.size--
s.m.resizeFromRemove()
}
}

View File

@ -13,4 +13,6 @@ func TestHashMap_put(t *testing.T) {
m.Put(lang.Int(i), i+1)
}
fmt.Println(m)
m.resize()
fmt.Println(m)
}

View File

@ -14,8 +14,12 @@ type (
Map[K lang.Object, V any] interface {
MapLooper[K, V]
Put(k K, v V) (bool, exceptions.Exception)
Get(k K) (V, bool, exceptions.Exception)
}
MutableMap[K lang.Object, V any] interface {
Map[K, V]
Put(k K, v V) (bool, exceptions.Exception)
Remove(k K) (V, bool, exceptions.Exception)
}

View File

@ -1,5 +1,5 @@
package exceptions
var ElementFound = NewRuntimeException("", DefaultExceptionConfig().SetGetStackTrace(false).SetExceptionName("ElementFound"))
var ElementNotFound = NewRuntimeException("", DefaultExceptionConfig().SetGetStackTrace(false).SetExceptionName("ElementNotFound"))
var CollectionLoopFinished = NewRuntimeException("", DefaultExceptionConfig().SetGetStackTrace(false).SetExceptionName("CollectionLoopFinished"))
var ElementFound = NewRuntimeException("", DefaultExceptionConfig().SetGetStackTrace(false).SetExceptionName("github.com.tursom.GoCollections.exceptions.ElementFound"))
var ElementNotFound = NewElementNotFoundException("", nil)
var CollectionLoopFinished = NewRuntimeException("", DefaultExceptionConfig().SetGetStackTrace(false).SetExceptionName("github.com.tursom.GoCollections.exceptions.CollectionLoopFinished"))

View File

@ -6,6 +6,7 @@ type ElementNotFoundException struct {
func NewElementNotFoundException(message string, config *ExceptionConfig) *ElementNotFoundException {
return &ElementNotFoundException{
NewRuntimeException(message, config.AddSkipStack(1).SetExceptionName("ElementNotFoundException")),
NewRuntimeException(message, config.AddSkipStack(1).
SetExceptionName("github.com.tursom.GoCollections.exceptions.ElementNotFoundException")),
}
}

View File

@ -48,7 +48,8 @@ func BuildStackTraceByArray(builder *strings.Builder, trace []StackTrace) {
}
func BuildStackTrace(builder *strings.Builder, e Exception) {
builder.WriteString(fmt.Sprintf("exception caused %s: %s\n", e.Name(), e.Message()))
builder.WriteString(e.Error())
builder.WriteString("\n")
if e.StackTrace() == nil {
return
}

View File

@ -7,6 +7,10 @@ type ExceptionConfig struct {
ExceptionName string
}
func Cfg() *ExceptionConfig {
return DefaultExceptionConfig()
}
func DefaultExceptionConfig() *ExceptionConfig {
return &ExceptionConfig{
SkipStack: 0,

View File

@ -6,6 +6,7 @@ type IllegalParameterException struct {
func NewIllegalParameterException(message string, config *ExceptionConfig) *IllegalParameterException {
return &IllegalParameterException{
NewRuntimeException(message, config.AddSkipStack(1).SetExceptionName("IllegalParameterException")),
NewRuntimeException(message, config.AddSkipStack(1).
SetExceptionName("github.com.tursom.GoCollections.exceptions.IllegalParameterException")),
}
}

View File

@ -6,7 +6,8 @@ type IndexOutOfBound struct {
func NewIndexOutOfBound(message string, config *ExceptionConfig) *IndexOutOfBound {
return &IndexOutOfBound{
NewRuntimeException(message, config.AddSkipStack(1).SetExceptionName("IndexOutOfBound")),
NewRuntimeException(message, config.AddSkipStack(1).
SetExceptionName("github.com.tursom.GoCollections.exceptions.IndexOutOfBound")),
}
}
@ -14,7 +15,7 @@ func CatchIndexOutOfBound[T any](f func() T, config *ExceptionConfig) (r T, err
defer func() {
r := recover()
if r != nil {
err = NewIndexOutOfBound("", config.AddSkipStack(3).SetCause(PackageAny(r)))
err = NewIndexOutOfBound("", config.AddSkipStack(3).SetCause(r))
}
}()
r = f()

View File

@ -11,7 +11,8 @@ type NPE struct {
func NewNPE(message string, config *ExceptionConfig) *NPE {
return &NPE{
NewRuntimeException(message, config.AddSkipStack(1).SetExceptionName("NPE")),
NewRuntimeException(message, config.AddSkipStack(1).
SetExceptionName("github.com.tursom.GoCollections.exceptions.NPE")),
}
}

View File

@ -6,6 +6,7 @@ type OperationNotSupportedException struct {
func NewOperationNotSupportedException(message string, config *ExceptionConfig) *OperationNotSupportedException {
return &OperationNotSupportedException{
NewRuntimeException(message, config.AddSkipStack(1).SetExceptionName("OperationNotSupportedException")),
NewRuntimeException(message, config.AddSkipStack(1).
SetExceptionName("github.com.tursom.GoCollections.exceptions.OperationNotSupportedException")),
}
}

View File

@ -21,8 +21,9 @@ func NewPackageException(err any, config *ExceptionConfig) *PackageException {
t := reflect.TypeOf(err)
message = fmt.Sprintf("%s (%s)", message, t.Name())
return &PackageException{
RuntimeException: NewRuntimeException(message, config.AddSkipStack(1).SetExceptionName("PackageException")),
err: err,
RuntimeException: NewRuntimeException(message, config.AddSkipStack(1).
SetExceptionName("github.com.tursom.GoCollections.exceptions.PackageException")),
err: err,
}
}

View File

@ -1,6 +1,7 @@
package exceptions
import (
"fmt"
"github.com/tursom/GoCollections/lang"
"io"
"os"
@ -36,7 +37,7 @@ func NewRuntimeException(message string, config *ExceptionConfig) RuntimeExcepti
}
}
exceptionName := "RuntimeException"
exceptionName := "github.com.tursom.GoCollections.exceptions.RuntimeException"
if len(config.ExceptionName) != 0 {
exceptionName = config.ExceptionName
}
@ -55,9 +56,17 @@ func (o RuntimeException) Cause() Exception {
}
func (o RuntimeException) Error() string {
builder := strings.Builder{}
o.BuildPrintStackTrace(&builder)
return builder.String()
message := o.message
if len(message) == 0 {
if o.cause != nil {
message = fmt.Sprintf("%s: %s", o.Name(), o.cause.Error())
} else {
message = o.Name()
}
} else {
message = fmt.Sprintf("%s: %s", o.Name(), message)
}
return message
}
func (o RuntimeException) Message() string {
@ -77,7 +86,9 @@ func (o RuntimeException) PrintStackTrace() {
}
func (o RuntimeException) PrintStackTraceTo(writer io.Writer) {
bytes := []byte(o.Error())
builder := strings.Builder{}
o.BuildPrintStackTrace(&builder)
bytes := []byte(builder.String())
writeBytes := 0
for writeBytes < len(bytes) {
write, err := writer.Write(bytes[writeBytes:])

View File

@ -0,0 +1,12 @@
package exceptions
import (
"testing"
)
func TestRuntimeException_PrintStackTrace(t *testing.T) {
exception := NewRuntimeException("test1", DefaultExceptionConfig().SetCause(1))
exception = NewRuntimeException("test2", DefaultExceptionConfig().SetCause(exception))
exception = NewRuntimeException("", DefaultExceptionConfig().SetCause(exception))
exception.PrintStackTrace()
}

View File

@ -0,0 +1,13 @@
package exceptions
import (
"fmt"
"testing"
"unsafe"
)
func TestGetStackTrace(t *testing.T) {
fmt.Println(unsafe.Sizeof(StackTrace{}))
fmt.Println(unsafe.Sizeof(make([]StackTrace, 0, 16)))
fmt.Println(unsafe.Sizeof(make([]StackTrace, 0, 16)) + unsafe.Sizeof(StackTrace{})*16)
}

View File

@ -12,7 +12,8 @@ type TypeCastException struct {
func NewTypeCastException(message string, config *ExceptionConfig) *TypeCastException {
return &TypeCastException{
NewRuntimeException(message, config.AddSkipStack(1).SetExceptionName("TypeCastException")),
NewRuntimeException(message, config.AddSkipStack(1).
SetExceptionName("github.com.tursom.GoCollections.exceptions.TypeCastException")),
}
}

View File

@ -6,6 +6,7 @@ type WrongCallHostException struct {
func NewWrongCallHostException(message string) WrongCallHostException {
return WrongCallHostException{
NewRuntimeException(message, DefaultExceptionConfig().AddSkipStack(1).SetExceptionName("WrongCallHostException")),
NewRuntimeException(message, DefaultExceptionConfig().AddSkipStack(1).
SetExceptionName("github.com.tursom.GoCollections.exceptions.WrongCallHostException")),
}
}

5
go.mod
View File

@ -2,4 +2,7 @@ module github.com/tursom/GoCollections
go 1.18
require github.com/petermattis/goid v0.0.0-20220302125637-5f11c28912df
require (
github.com/petermattis/goid v0.0.0-20220302125637-5f11c28912df
github.com/timandy/routine v1.0.5
)

View File

@ -32,10 +32,10 @@ func Cast[T any](v any) T {
}
}
func ForceCast[T any](v unsafe.Pointer) T {
func ForceCast[T any](v unsafe.Pointer) *T {
if v == nil {
return Nil[T]()
return nil
} else {
return Cast[T](v)
return (*T)(v)
}
}

View File

@ -23,6 +23,10 @@ type (
Any = Object
IsBaseObject interface {
AsBaseObject() *BaseObject
}
BaseObject struct {
}
)
@ -54,6 +58,10 @@ func (b *BaseObject) AsObject() Object {
return b
}
func (b *BaseObject) AsBaseObject() *BaseObject {
return b
}
func (b *BaseObject) Equals(o Object) bool {
return b == o
}
@ -73,3 +81,14 @@ func (b *BaseObject) ToString() String {
func (b *BaseObject) HashCode() int32 {
return Hash64(b)
}
func (b *BaseObject) Compare(t IsBaseObject) int {
o := t.AsBaseObject()
if b == o {
return 0
} else if uintptr(unsafe.Pointer(b)) > uintptr(unsafe.Pointer(o)) {
return 1
} else {
return -1
}
}

74
lang/ThreadLocal.go Normal file
View File

@ -0,0 +1,74 @@
package lang
import (
"github.com/timandy/routine"
)
//goland:noinspection GoUnusedGlobalVariable
var (
ThreadLocalGo = routine.Go
ThreadLocalGoWait = routine.GoWait
ThreadLocalGoWaitResult = routine.GoWaitResult
)
type (
ThreadLocal[T any] interface {
Object
Get() T
Put(value T)
Remove()
}
threadLocalImpl[T any] struct {
BaseObject
threadLocal routine.ThreadLocal
}
)
func GoId() int64 {
return routine.Goid()
}
//goland:noinspection GoUnusedExportedFunction
func NewThreadLocal[T any]() ThreadLocal[T] {
return &threadLocalImpl[T]{
threadLocal: routine.NewInheritableThreadLocal(),
}
}
//goland:noinspection GoUnusedExportedFunction
func NewThreadLocalWithInitial[T any](supplier func() T) ThreadLocal[T] {
return &threadLocalImpl[T]{
threadLocal: routine.NewThreadLocalWithInitial(func() routine.Any {
return supplier()
}),
}
}
//goland:noinspection GoUnusedExportedFunction
func NewInheritableThreadLocal[T any]() ThreadLocal[T] {
return &threadLocalImpl[T]{
threadLocal: routine.NewInheritableThreadLocal(),
}
}
//goland:noinspection GoUnusedExportedFunction
func NewInheritableThreadLocalWithInitial[T any](supplier func() T) ThreadLocal[T] {
return &threadLocalImpl[T]{
threadLocal: routine.NewInheritableThreadLocalWithInitial(func() routine.Any {
return supplier()
}),
}
}
func (t *threadLocalImpl[T]) Get() T {
return Cast[T](t.threadLocal.Get())
}
func (t *threadLocalImpl[T]) Put(value T) {
t.threadLocal.Set(value)
}
func (t *threadLocalImpl[T]) Remove() {
t.threadLocal.Remove()
}

15
lang/ThreadLocal_test.go Normal file
View File

@ -0,0 +1,15 @@
package lang
import (
"fmt"
"testing"
)
func TestThreadLocalImpl(t1 *testing.T) {
local := NewThreadLocal[int]()
fmt.Println(local.Get())
local.Put(1)
fmt.Println(local.Get())
local.Remove()
fmt.Println(local.Get())
}

110
lang/atomic/Array.go Normal file
View File

@ -0,0 +1,110 @@
package atomic
type (
Array[T any] struct {
atomic *Atomic[T]
array []T
}
Int32Array struct {
Array[int32]
}
Int64Array struct {
Array[int64]
}
UInt32Array struct {
Array[uint32]
}
UInt64Array struct {
Array[uint64]
}
)
func NewArray[T any](size int) *Array[*T] {
return &Array[*T]{
atomic: GetAtomic[T](),
array: make([]*T, size),
}
}
func CapArray[T any](array []*T) *Array[*T] {
return &Array[*T]{
atomic: GetAtomic[T](),
array: array,
}
}
func NewInt32Array(size int) *Int32Array {
return &Int32Array{
Array[int32]{
atomic: &Int32F,
array: make([]int32, size),
},
}
}
func NewInt64Array(size int) *Int64Array {
return &Int64Array{
Array[int64]{
atomic: &Int64F,
array: make([]int64, size),
},
}
}
func NewUInt32Array(size int) *UInt32Array {
return &UInt32Array{
Array[uint32]{
atomic: &UInt32F,
array: make([]uint32, size),
},
}
}
func NewUInt64Array(size int) *UInt64Array {
return &UInt64Array{
Array[uint64]{
atomic: &UInt64F,
array: make([]uint64, size),
},
}
}
func (a *Array[T]) Len() int {
return len(a.array)
}
func (a *Array[T]) Array() []T {
return a.array
}
func (a *Array[T]) Get(index int) T {
return a.atomic.Load(&a.array[index])
}
func (a *Array[T]) Set(index int, p T) {
a.atomic.Store(&a.array[index], p)
}
func (a *Array[T]) Swap(index int, p T) (old T) {
return a.atomic.Swap(&a.array[index], p)
}
func (a *Array[T]) CompareAndSwap(index int, old, new T) (swapped bool) {
return a.atomic.CompareAndSwap(&a.array[index], old, new)
}
func (a *Int32Array) Add(index int, value int32) {
AddInt32(&a.array[index], value)
}
func (a *Int64Array) Add(index int, value int64) {
AddInt64(&a.array[index], value)
}
func (a *UInt32Array) Add(index int, value uint32) {
AddUInt32(&a.array[index], value)
}
func (a *UInt64Array) Add(index int, value uint64) {
AddUInt64(&a.array[index], value)
}

View File

@ -5,6 +5,58 @@ import (
"unsafe"
)
type (
Atomic[T any] struct {
Swap func(addr *T, new T) (old T)
CompareAndSwap func(addr *T, old, new T) (swapped bool)
Load func(addr *T) (val T)
Store func(addr *T, val T)
}
)
//goland:noinspection GoUnusedGlobalVariable
var (
Int32F = Atomic[int32]{
SwapInt32,
CompareAndSwapInt32,
LoadInt32,
StoreInt32,
}
Int64F = Atomic[int64]{
SwapInt64,
CompareAndSwapInt64,
LoadInt64,
StoreInt64,
}
UInt32F = Atomic[uint32]{
SwapUInt32,
CompareAndSwapUInt32,
LoadUint32,
StoreUInt32,
}
UInt64F = Atomic[uint64]{
SwapUInt64,
CompareAndSwapUInt64,
LoadUint64,
StoreUInt64,
}
PointerF = Atomic[unsafe.Pointer]{
UnsafeSwapPointer,
UnsafeCompareAndSwapPointer,
UnsafeLoadPointer,
UnsafeStorePointer,
}
)
func GetAtomic[T any]() *Atomic[*T] {
return &Atomic[*T]{
SwapPointer[T],
CompareAndSwapPointer[T],
LoadPointer[T],
StorePointer[T],
}
}
func SwapInt32(addr *int32, new int32) (old int32) {
return atomic.SwapInt32(addr, new)
}
@ -13,11 +65,11 @@ func SwapInt64(addr *int64, new int64) (old int64) {
return atomic.SwapInt64(addr, new)
}
func SwapUint32(addr *uint32, new uint32) (old uint32) {
func SwapUInt32(addr *uint32, new uint32) (old uint32) {
return atomic.SwapUint32(addr, new)
}
func SwapUint64(addr *uint64, new uint64) (old uint64) {
func SwapUInt64(addr *uint64, new uint64) (old uint64) {
return atomic.SwapUint64(addr, new)
}
@ -37,11 +89,11 @@ func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool) {
return atomic.CompareAndSwapInt64(addr, old, new)
}
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool) {
func CompareAndSwapUInt32(addr *uint32, old, new uint32) (swapped bool) {
return atomic.CompareAndSwapUint32(addr, old, new)
}
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool) {
func CompareAndSwapUInt64(addr *uint64, old, new uint64) (swapped bool) {
return atomic.CompareAndSwapUint64(addr, old, new)
}
@ -57,7 +109,7 @@ func AddInt32(addr *int32, delta int32) (new int32) {
return atomic.AddInt32(addr, delta)
}
func AddUint32(addr *uint32, delta uint32) (new uint32) {
func AddUInt32(addr *uint32, delta uint32) (new uint32) {
return atomic.AddUint32(addr, delta)
}
@ -65,7 +117,7 @@ func AddInt64(addr *int64, delta int64) (new int64) {
return atomic.AddInt64(addr, delta)
}
func AddUint64(addr *uint64, delta uint64) (new uint64) {
func AddUInt64(addr *uint64, delta uint64) (new uint64) {
return atomic.AddUint64(addr, delta)
}
@ -105,11 +157,11 @@ func StoreInt64(addr *int64, val int64) {
atomic.StoreInt64(addr, val)
}
func StoreUint32(addr *uint32, val uint32) {
func StoreUInt32(addr *uint32, val uint32) {
atomic.StoreUint32(addr, val)
}
func StoreUint64(addr *uint64, val uint64) {
func StoreUInt64(addr *uint64, val uint64) {
atomic.StoreUint64(addr, val)
}

View File

@ -5,9 +5,21 @@ import (
"unsafe"
)
type Reference[T any] struct {
reference *T
}
//goland:noinspection GoUnusedGlobalVariable
var (
UnsafeLoadPointer = atomic.LoadPointer
UnsafeStorePointer = atomic.StorePointer
UnsafeSwapPointer = atomic.SwapPointer
UnsafeCompareAndSwapPointer = atomic.CompareAndSwapPointer
)
type (
Pointer = unsafe.Pointer
PPointer = *unsafe.Pointer
Reference[T any] struct {
reference *T
}
)
func NewReference[T any](reference *T) *Reference[T] {
return &Reference[T]{reference}
@ -29,18 +41,22 @@ func (v *Reference[T]) CompareAndSwap(old, new *T) (swapped bool) {
return CompareAndSwapPointer(&v.reference, old, new)
}
func AsPPointer[T any](p **T) *unsafe.Pointer {
return (*unsafe.Pointer)(unsafe.Pointer(p))
}
func LoadPointer[T any](addr **T) (val *T) {
return (*T)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(addr))))
return (*T)(atomic.LoadPointer(AsPPointer(addr)))
}
func StorePointer[T any](addr **T, val *T) {
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(addr)), unsafe.Pointer(val))
atomic.StorePointer(AsPPointer(addr), unsafe.Pointer(val))
}
func SwapPointer[T any](addr **T, new *T) (old *T) {
return (*T)(atomic.SwapPointer((*unsafe.Pointer)(unsafe.Pointer(addr)), unsafe.Pointer(new)))
return (*T)(atomic.SwapPointer(AsPPointer(addr), unsafe.Pointer(new)))
}
func CompareAndSwapPointer[T any](addr **T, old, new *T) (swapped bool) {
return atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(addr)), unsafe.Pointer(old), unsafe.Pointer(new))
return atomic.CompareAndSwapPointer(AsPPointer(addr), Pointer(old), Pointer(new))
}

View File

@ -7,11 +7,11 @@ import (
)
func AsList[T lang.Object](arr []T) collections.List[T] {
return &arrayList[T]{arr}
return &arrayList[T]{array: arr}
}
func CheckedGet[T any](array []T, index int) (T, exceptions.Exception) {
return exceptions.CatchIndexOutOfBound(func() T {
return array[index]
}, exceptions.DefaultExceptionConfig().AddSkipStack(3))
}, exceptions.Cfg().AddSkipStack(3))
}

107
util/Context.go Normal file
View File

@ -0,0 +1,107 @@
package util
import (
"github.com/tursom/GoCollections/lang"
"github.com/tursom/GoCollections/lang/atomic"
"sync"
)
var (
contextId atomic.Int32
)
type (
Context struct {
lang.BaseObject
contextId int32
keyId atomic.Int32
}
ContextMap interface {
Get(key *ContextKey[any]) (any, bool)
Set(key *ContextKey[any], value any)
}
ContextKey[T any] struct {
lang.BaseObject
contextId int32
id int32
}
contextMapImpl struct {
lang.BaseObject
contextId int32
array []any
}
concurrentContextMap struct {
contextMapImpl
lock sync.Mutex
}
)
func NewContext() *Context {
return &Context{
contextId: contextId.Add(1),
}
}
func AllocateContextKey[T any](ctx *Context) *ContextKey[T] {
return &ContextKey[T]{
contextId: ctx.contextId,
id: ctx.keyId.Add(1) - 1,
}
}
func (c *Context) NewMap() ContextMap {
return &contextMapImpl{
contextId: c.contextId,
//array: make([]any, c.keyId.Load()),
}
}
func (c *Context) NewConcurrentMap() ContextMap {
return &concurrentContextMap{
contextMapImpl: contextMapImpl{
contextId: c.contextId,
//array: make([]any, c.keyId.Load()),
},
}
}
func (k *ContextKey[T]) Get(m ContextMap) T {
value, _ := k.TryGet(m)
return value
}
func (k *ContextKey[T]) TryGet(m ContextMap) (T, bool) {
value, ok := m.Get(k.asNormalKey())
return lang.Cast[T](value), ok
}
func (k *ContextKey[T]) Set(m ContextMap, value T) {
m.Set(k.asNormalKey(), value)
}
func (m *contextMapImpl) Get(key *ContextKey[any]) (any, bool) {
if len(m.array) < int(key.id) {
return nil, false
} else {
return m.array[key.id], true
}
}
func (m *contextMapImpl) Set(key *ContextKey[any], value any) {
if len(m.array) < int(key.id) {
newArray := make([]any, key.id)
copy(newArray, m.array)
m.array = newArray
}
m.array[key.id] = value
}
func (m *concurrentContextMap) Set(key *ContextKey[any], value any) {
m.lock.Lock()
defer m.lock.Unlock()
m.contextMapImpl.Set(key, value)
}
func (k *ContextKey[T]) asNormalKey() *ContextKey[any] {
return (*ContextKey[any])(k)
}

20
util/Context_test.go Normal file
View File

@ -0,0 +1,20 @@
package util
import (
"fmt"
"testing"
)
func TestContextKey(t *testing.T) {
ctx := NewContext()
key := AllocateContextKey[int](ctx)
m := ctx.NewConcurrentMap()
fmt.Println(key.Get(m))
fmt.Println(key.TryGet(m))
key.Set(m, 100)
fmt.Println(key.Get(m))
fmt.Println(key.TryGet(m))
}

View File

@ -7,6 +7,7 @@ import (
)
type arrayList[T lang.Object] struct {
lang.BaseObject
array []T
}
@ -35,7 +36,7 @@ func (a *arrayList[T]) Get(index int) (T, exceptions.Exception) {
}
func (a *arrayList[T]) SubList(from, to int) collections.List[T] {
return &arrayList[T]{a.array[from:to]}
return &arrayList[T]{array: a.array[from:to]}
}
type arrayListIterator[T lang.Object] struct {
@ -51,7 +52,7 @@ func (a *arrayListIterator[T]) Next() (r T, err exceptions.Exception) {
defer func() {
r := recover()
if r != nil {
err = exceptions.NewNPE(r, nil)
err = exceptions.NewNPE("", exceptions.Cfg().SetCause(r))
}
}()
i := a.index

View File

@ -7,7 +7,7 @@ import (
)
func Test_arrayList_Get(t *testing.T) {
l := &arrayList[lang.Int]{[]lang.Int{1, 2}}
l := arrayList[lang.Int]{array: []lang.Int{1, 2}}
for i := 0; i < l.Size()+1; i++ {
r, err := l.Get(i)
if err != nil {