|
| 1 | +package cmap |
| 2 | + |
| 3 | +import ( |
| 4 | + "reflect" |
| 5 | + "runtime" |
| 6 | + "sync" |
| 7 | + "unsafe" |
| 8 | + |
| 9 | + "github.com/antlabs/gstl/api" |
| 10 | + "github.com/antlabs/gstl/mapex" |
| 11 | + xxhash "github.com/cespare/xxhash/v2" |
| 12 | +) |
| 13 | + |
| 14 | +var _ api.CMaper[int, int] = (*CMap[int, int])(nil) |
| 15 | + |
| 16 | +type Pair[K comparable, V any] struct { |
| 17 | + Key K |
| 18 | + Val V |
| 19 | +} |
| 20 | + |
| 21 | +type CMap[K comparable, V any] struct { |
| 22 | + bucket []Item[K, V] |
| 23 | + keySize int |
| 24 | + isKeyStr bool |
| 25 | +} |
| 26 | + |
| 27 | +type Item[K comparable, V any] struct { |
| 28 | + rw sync.RWMutex |
| 29 | + m map[K]V |
| 30 | +} |
| 31 | + |
| 32 | +func New[K comparable, V any]() (c *CMap[K, V]) { |
| 33 | + np := runtime.GOMAXPROCS(0) |
| 34 | + if np <= 0 { |
| 35 | + np = 8 |
| 36 | + } |
| 37 | + |
| 38 | + c = &CMap[K, V]{ |
| 39 | + bucket: make([]Item[K, V], np), |
| 40 | + } |
| 41 | + |
| 42 | + for i := range c.bucket { |
| 43 | + c.bucket[i].m = make(map[K]V) |
| 44 | + } |
| 45 | + return c |
| 46 | +} |
| 47 | + |
| 48 | +// 计算hash值 |
| 49 | +func (c *CMap[K, V]) calHash(k K) uint64 { |
| 50 | + var key string |
| 51 | + |
| 52 | + if c.isKeyStr { |
| 53 | + // 直接赋值会报错, 使用unsafe绕过编译器检查 |
| 54 | + key = *(*string)(unsafe.Pointer(&k)) |
| 55 | + } else { |
| 56 | + // 因为xxhash.Sum64String 接收string, 所以要把非string类型变量当成string类型来处理 |
| 57 | + key = *(*string)(unsafe.Pointer(&reflect.StringHeader{ |
| 58 | + Data: uintptr(unsafe.Pointer(&k)), |
| 59 | + Len: c.keySize, |
| 60 | + })) |
| 61 | + } |
| 62 | + |
| 63 | + return xxhash.Sum64String(key) |
| 64 | +} |
| 65 | + |
| 66 | +// 保存key的类型和key的长度 |
| 67 | +func (h *CMap[K, V]) keyTypeAndKeySize() { |
| 68 | + var k K |
| 69 | + switch (interface{})(k).(type) { |
| 70 | + case string: |
| 71 | + h.isKeyStr = true |
| 72 | + default: |
| 73 | + h.keySize = int(unsafe.Sizeof(k)) |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +// 找到索引 |
| 78 | +func (c *CMap[K, V]) findIndex(key K) *Item[K, V] { |
| 79 | + index := c.calHash(key) % uint64(len(c.bucket)) |
| 80 | + return &c.bucket[index] |
| 81 | +} |
| 82 | + |
| 83 | +// 删除 |
| 84 | +func (c *CMap[K, V]) Delete(key K) { |
| 85 | + item := c.findIndex(key) |
| 86 | + item.rw.Lock() |
| 87 | + delete(item.m, key) |
| 88 | + item.rw.Unlock() |
| 89 | +} |
| 90 | + |
| 91 | +func (c *CMap[K, V]) Load(key K) (value V, ok bool) { |
| 92 | + item := c.findIndex(key) |
| 93 | + item.rw.RLock() |
| 94 | + value, ok = item.m[key] |
| 95 | + item.rw.RUnlock() |
| 96 | + return |
| 97 | +} |
| 98 | + |
| 99 | +func (c *CMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) { |
| 100 | + item := c.findIndex(key) |
| 101 | + item.rw.Lock() |
| 102 | + value, loaded = item.m[key] |
| 103 | + if !loaded { |
| 104 | + item.rw.Unlock() |
| 105 | + return |
| 106 | + } |
| 107 | + delete(item.m, key) |
| 108 | + item.rw.Unlock() |
| 109 | + return |
| 110 | +} |
| 111 | + |
| 112 | +func (c *CMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { |
| 113 | + item := c.findIndex(key) |
| 114 | + item.rw.Lock() |
| 115 | + actual, loaded = item.m[key] |
| 116 | + if !loaded { |
| 117 | + actual = value |
| 118 | + item.m[key] = actual |
| 119 | + item.rw.Unlock() |
| 120 | + return |
| 121 | + } |
| 122 | + actual, loaded = item.m[key] |
| 123 | + item.rw.Unlock() |
| 124 | + return |
| 125 | +} |
| 126 | + |
| 127 | +func (c *CMap[K, V]) Range(f func(key K, value V) bool) { |
| 128 | + for i := 0; i < len(c.bucket); i++ { |
| 129 | + item := &c.bucket[i] |
| 130 | + item.rw.RLock() |
| 131 | + for k, v := range item.m { |
| 132 | + if !f(k, v) { |
| 133 | + item.rw.RUnlock() |
| 134 | + return |
| 135 | + } |
| 136 | + } |
| 137 | + item.rw.RUnlock() |
| 138 | + } |
| 139 | +} |
| 140 | + |
| 141 | +func (c *CMap[K, V]) Iter() (rv chan Pair[K, V]) { |
| 142 | + |
| 143 | + rv = make(chan Pair[K, V]) |
| 144 | + var wg sync.WaitGroup |
| 145 | + |
| 146 | + wg.Add(len(c.bucket)) |
| 147 | + |
| 148 | + go func() { |
| 149 | + wg.Wait() |
| 150 | + close(rv) |
| 151 | + }() |
| 152 | + |
| 153 | + for i := 0; i < len(c.bucket); i++ { |
| 154 | + item := &c.bucket[i] |
| 155 | + go func(item *Item[K, V]) { |
| 156 | + |
| 157 | + defer wg.Done() |
| 158 | + item.rw.RLock() |
| 159 | + for k, v := range item.m { |
| 160 | + rv <- Pair[K, V]{Key: k, Val: v} |
| 161 | + } |
| 162 | + item.rw.RUnlock() |
| 163 | + |
| 164 | + }(item) |
| 165 | + } |
| 166 | + return rv |
| 167 | + |
| 168 | +} |
| 169 | + |
| 170 | +func (c *CMap[K, V]) Store(key K, value V) { |
| 171 | + item := c.findIndex(key) |
| 172 | + item.rw.Lock() |
| 173 | + item.m[key] = value |
| 174 | + item.rw.Unlock() |
| 175 | + return |
| 176 | +} |
| 177 | + |
| 178 | +// TODO 优化 |
| 179 | +func (c *CMap[K, V]) Keys() []K { |
| 180 | + l := c.Len() |
| 181 | + all := make([]K, 0, l) |
| 182 | + if l == 0 { |
| 183 | + return nil |
| 184 | + } |
| 185 | + |
| 186 | + for i := 0; i < len(c.bucket); i++ { |
| 187 | + |
| 188 | + item := &c.bucket[i] |
| 189 | + item.rw.RLock() |
| 190 | + all = append(all, mapex.Keys(item.m)...) |
| 191 | + item.rw.RUnlock() |
| 192 | + } |
| 193 | + return all |
| 194 | +} |
| 195 | + |
| 196 | +func (c *CMap[K, V]) Values() []V { |
| 197 | + l := c.Len() |
| 198 | + all := make([]V, 0, l) |
| 199 | + if l == 0 { |
| 200 | + return nil |
| 201 | + } |
| 202 | + |
| 203 | + for i := 0; i < len(c.bucket); i++ { |
| 204 | + |
| 205 | + item := &c.bucket[i] |
| 206 | + item.rw.RLock() |
| 207 | + all = append(all, mapex.Values(item.m)...) |
| 208 | + item.rw.RUnlock() |
| 209 | + } |
| 210 | + return all |
| 211 | +} |
| 212 | + |
| 213 | +func (c *CMap[K, V]) Len() int { |
| 214 | + l := 0 |
| 215 | + for i := 0; i < len(c.bucket); i++ { |
| 216 | + item := &c.bucket[i] |
| 217 | + item.rw.RLock() |
| 218 | + l += len(item.m) |
| 219 | + item.rw.RUnlock() |
| 220 | + } |
| 221 | + return l |
| 222 | +} |
0 commit comments