github.com/tursom/GoCollections@v0.3.10/util/Context.go (about) 1 /* 2 * Copyright (c) 2022 tursom. All rights reserved. 3 * Use of this source code is governed by a GPL-3 4 * license that can be found in the LICENSE file. 5 */ 6 7 package util 8 9 import ( 10 "sync" 11 "unsafe" 12 13 "github.com/tursom/GoCollections/lang" 14 "github.com/tursom/GoCollections/lang/atomic" 15 ) 16 17 var ( 18 contextId atomic.Int32 19 ) 20 21 type ( 22 Context struct { 23 lang.BaseObject 24 contextId int32 25 keyId atomic.Int32 26 } 27 ContextMap interface { 28 Get(key *ContextKey[any]) (any, bool) 29 Set(key *ContextKey[any], value any) 30 } 31 ContextKey[T any] struct { 32 lang.BaseObject 33 contextId int32 34 id int32 35 supplier func() any 36 } 37 contextMapImpl struct { 38 lang.BaseObject 39 contextId int32 40 array []any 41 } 42 concurrentContextMap struct { 43 contextMapImpl 44 lock sync.Mutex 45 } 46 ) 47 48 func NewContext() *Context { 49 return &Context{ 50 contextId: contextId.Add(1), 51 } 52 } 53 54 func AllocateContextKey[T any](ctx *Context) *ContextKey[T] { 55 return AllocateContextKeyWithDefault[T](ctx, nil) 56 } 57 58 func AllocateContextKeyWithDefault[T any](ctx *Context, supplier func() T) *ContextKey[T] { 59 var s func() any 60 if supplier != nil { 61 s = func() any { 62 return supplier() 63 } 64 } 65 return &ContextKey[T]{ 66 contextId: ctx.contextId, 67 id: ctx.keyId.Add(1) - 1, 68 supplier: s, 69 } 70 } 71 72 func (c *Context) NewMap() ContextMap { 73 return &contextMapImpl{ 74 contextId: c.contextId, 75 //array: make([]any, c.keyId.Load()), 76 } 77 } 78 79 func (c *Context) NewConcurrentMap() ContextMap { 80 return &concurrentContextMap{ 81 contextMapImpl: contextMapImpl{ 82 contextId: c.contextId, 83 //array: make([]any, c.keyId.Load()), 84 }, 85 } 86 } 87 88 func (k *ContextKey[T]) Get(m ContextMap) T { 89 value, _ := k.TryGet(m) 90 return value 91 } 92 93 func (k *ContextKey[T]) TryGet(m ContextMap) (T, bool) { 94 value, ok := m.Get(k.asNormalKey()) 95 return lang.Cast[T](value), ok 96 } 97 98 func (k *ContextKey[T]) Set(m ContextMap, value T) { 99 m.Set(k.asNormalKey(), value) 100 } 101 102 func (m *contextMapImpl) Get(key *ContextKey[any]) (any, bool) { 103 if len(m.array) <= int(key.id) { 104 if key.supplier == nil { 105 return nil, false 106 } else { 107 supplier := key.supplier() 108 m.Set(key, supplier) 109 return supplier, true 110 } 111 } else { 112 return m.array[key.id], true 113 } 114 } 115 116 func (m *contextMapImpl) Set(key *ContextKey[any], value any) { 117 if len(m.array) <= int(key.id) { 118 newArray := make([]any, key.id+1) 119 copy(newArray, m.array) 120 m.array = newArray 121 } 122 m.array[key.id] = value 123 } 124 125 func (m *concurrentContextMap) Get(key *ContextKey[any]) (any, bool) { 126 if len(m.array) <= int(key.id) && key.supplier != nil { 127 m.Set(key, key.supplier()) 128 } 129 return m.contextMapImpl.Get(key) 130 } 131 132 func (m *concurrentContextMap) Set(key *ContextKey[any], value any) { 133 m.lock.Lock() 134 defer m.lock.Unlock() 135 m.contextMapImpl.Set(key, value) 136 } 137 138 //goland:noinspection GoRedundantConversion 139 func (k *ContextKey[T]) asNormalKey() *ContextKey[any] { 140 return (*ContextKey[any])(unsafe.Pointer(k)) 141 }