diff --git a/collections/HashMap.go b/collections/HashMap.go index 5ed303c..4ab9790 100644 --- a/collections/HashMap.go +++ b/collections/HashMap.go @@ -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() } } diff --git a/collections/HashMap_test.go b/collections/HashMap_test.go index 90aa5e4..d9a3ce3 100644 --- a/collections/HashMap_test.go +++ b/collections/HashMap_test.go @@ -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) } diff --git a/collections/Map.go b/collections/Map.go index 635d590..f43bd0f 100644 --- a/collections/Map.go +++ b/collections/Map.go @@ -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) } diff --git a/exceptions/CollectionOperations.go b/exceptions/CollectionOperations.go index fafa6c3..ce0c93c 100644 --- a/exceptions/CollectionOperations.go +++ b/exceptions/CollectionOperations.go @@ -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")) diff --git a/exceptions/ElementNotFound.go b/exceptions/ElementNotFound.go index 9ce0e90..0a6bb97 100644 --- a/exceptions/ElementNotFound.go +++ b/exceptions/ElementNotFound.go @@ -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")), } } diff --git a/exceptions/Exception.go b/exceptions/Exception.go index 5324c4d..36ee4cf 100644 --- a/exceptions/Exception.go +++ b/exceptions/Exception.go @@ -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 } diff --git a/exceptions/ExceptionConfig.go b/exceptions/ExceptionConfig.go index 27737cf..03904ff 100644 --- a/exceptions/ExceptionConfig.go +++ b/exceptions/ExceptionConfig.go @@ -7,6 +7,10 @@ type ExceptionConfig struct { ExceptionName string } +func Cfg() *ExceptionConfig { + return DefaultExceptionConfig() +} + func DefaultExceptionConfig() *ExceptionConfig { return &ExceptionConfig{ SkipStack: 0, diff --git a/exceptions/IllegalParameter.go b/exceptions/IllegalParameter.go index 3302545..ce98bd2 100644 --- a/exceptions/IllegalParameter.go +++ b/exceptions/IllegalParameter.go @@ -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")), } } diff --git a/exceptions/IndexOutOfBoundError.go b/exceptions/IndexOutOfBoundError.go index f33ad66..00c7c6e 100644 --- a/exceptions/IndexOutOfBoundError.go +++ b/exceptions/IndexOutOfBoundError.go @@ -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() diff --git a/exceptions/NPE.go b/exceptions/NPE.go index 728b5f0..182a572 100644 --- a/exceptions/NPE.go +++ b/exceptions/NPE.go @@ -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")), } } diff --git a/exceptions/OperationNotSupportedException.go b/exceptions/OperationNotSupportedException.go index 9cd86a2..874e7c5 100644 --- a/exceptions/OperationNotSupportedException.go +++ b/exceptions/OperationNotSupportedException.go @@ -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")), } } diff --git a/exceptions/PackageException.go b/exceptions/PackageException.go index dac9b45..39a3844 100644 --- a/exceptions/PackageException.go +++ b/exceptions/PackageException.go @@ -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, } } diff --git a/exceptions/RuntimeException.go b/exceptions/RuntimeException.go index 6ad0039..58e9df5 100644 --- a/exceptions/RuntimeException.go +++ b/exceptions/RuntimeException.go @@ -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:]) diff --git a/exceptions/RuntimeException_test.go b/exceptions/RuntimeException_test.go new file mode 100644 index 0000000..125ee6e --- /dev/null +++ b/exceptions/RuntimeException_test.go @@ -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() +} diff --git a/exceptions/StackTrace_test.go b/exceptions/StackTrace_test.go new file mode 100644 index 0000000..420c7e9 --- /dev/null +++ b/exceptions/StackTrace_test.go @@ -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) +} diff --git a/exceptions/TypeCastException.go b/exceptions/TypeCastException.go index 2256e33..ebe71df 100644 --- a/exceptions/TypeCastException.go +++ b/exceptions/TypeCastException.go @@ -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")), } } diff --git a/exceptions/WrongCallHostException.go b/exceptions/WrongCallHostException.go index 5c1602d..ed44bda 100644 --- a/exceptions/WrongCallHostException.go +++ b/exceptions/WrongCallHostException.go @@ -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")), } } diff --git a/go.mod b/go.mod index cf02b81..f0bd38a 100644 --- a/go.mod +++ b/go.mod @@ -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 +) diff --git a/lang/Lang.go b/lang/Lang.go index b37a3f4..7b090ef 100644 --- a/lang/Lang.go +++ b/lang/Lang.go @@ -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) } } diff --git a/lang/Object.go b/lang/Object.go index c25ce98..e0d8cea 100644 --- a/lang/Object.go +++ b/lang/Object.go @@ -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 + } +} diff --git a/lang/ThreadLocal.go b/lang/ThreadLocal.go new file mode 100644 index 0000000..40403b9 --- /dev/null +++ b/lang/ThreadLocal.go @@ -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() +} diff --git a/lang/ThreadLocal_test.go b/lang/ThreadLocal_test.go new file mode 100644 index 0000000..ca78505 --- /dev/null +++ b/lang/ThreadLocal_test.go @@ -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()) +} diff --git a/lang/atomic/Array.go b/lang/atomic/Array.go new file mode 100644 index 0000000..44cbeaf --- /dev/null +++ b/lang/atomic/Array.go @@ -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) +} diff --git a/lang/atomic/Atomic.go b/lang/atomic/Atomic.go index b924394..e33c640 100644 --- a/lang/atomic/Atomic.go +++ b/lang/atomic/Atomic.go @@ -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) } diff --git a/lang/atomic/Reference.go b/lang/atomic/Reference.go index dc9baee..49690d2 100644 --- a/lang/atomic/Reference.go +++ b/lang/atomic/Reference.go @@ -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)) } diff --git a/util/Arrays.go b/util/Arrays.go index 1efb176..ecc0ece 100644 --- a/util/Arrays.go +++ b/util/Arrays.go @@ -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)) } diff --git a/util/Context.go b/util/Context.go new file mode 100644 index 0000000..ddcc050 --- /dev/null +++ b/util/Context.go @@ -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) +} diff --git a/util/Context_test.go b/util/Context_test.go new file mode 100644 index 0000000..793b0d0 --- /dev/null +++ b/util/Context_test.go @@ -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)) +} diff --git a/util/arrayList.go b/util/arrayList.go index 9c753b9..4bca7c5 100644 --- a/util/arrayList.go +++ b/util/arrayList.go @@ -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 diff --git a/util/arrayList_test.go b/util/arrayList_test.go index 7b59d26..d0015e3 100644 --- a/util/arrayList_test.go +++ b/util/arrayList_test.go @@ -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 {