github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/f/cmap.go (about)

     1  package f
     2  
     3  import "sync"
     4  
     5  // CMapShardCount Map Shard Count
     6  var CMapShardCount = 32
     7  
     8  // CMap thread safe map of type string:Anything.
     9  // To avoid lock bottlenecks this map is dived to several (SHARD_COUNT) map shards.
    10  type CMap []*CMapShared
    11  
    12  // CMapShared thread safe string to anything map.
    13  type CMapShared struct {
    14  	items        map[string]interface{}
    15  	sync.RWMutex // Read Write mutex, guards access to internal map.
    16  }
    17  
    18  // NewConcurrentMap Creates a new concurrent map.
    19  func NewConcurrentMap() CMap {
    20  	m := make(CMap, CMapShardCount)
    21  	for i := 0; i < CMapShardCount; i++ {
    22  		m[i] = &CMapShared{items: make(map[string]interface{})}
    23  	}
    24  	return m
    25  }
    26  
    27  // NewConcurrentMapFromJSON Creates a new concurrent map.
    28  func NewConcurrentMapFromJSON(json []byte) (m CMap, err error) {
    29  	tmp := make(map[string]interface{})
    30  	err = DecodeJson(json, &tmp)
    31  	if err == nil {
    32  		m = NewConcurrentMap()
    33  		m.MSet(tmp)
    34  	}
    35  	return
    36  }
    37  
    38  // GetShard returns shard under given key.
    39  func (m CMap) GetShard(key string) *CMapShared {
    40  	return m[uint(fnv32(key))%uint(CMapShardCount)]
    41  }
    42  
    43  // MSet sets values.
    44  func (m CMap) MSet(data map[string]interface{}) {
    45  	for key, value := range data {
    46  		shard := m.GetShard(key)
    47  		shard.Lock()
    48  		shard.items[key] = value
    49  		shard.Unlock()
    50  	}
    51  }
    52  
    53  // Set the given value under the specified key.
    54  func (m CMap) Set(key string, value interface{}) {
    55  	// GetHeader map shard.
    56  	shard := m.GetShard(key)
    57  	shard.Lock()
    58  	shard.items[key] = value
    59  	shard.Unlock()
    60  }
    61  
    62  // CMapUpCb Callback to return new element to be inserted into the map
    63  // It is called while lock is held, therefore it MUST NOT
    64  // try to access other keys in same map, as it can lead to deadlock since
    65  // Go sync.RWLock is not reentrant.
    66  type CMapUpCb func(exist bool, valueInMap interface{}, newValue interface{}) interface{}
    67  
    68  // Up Insert or Update - updates existing element or inserts a new one using CMapUpCb
    69  func (m CMap) Up(key string, value interface{}, cb CMapUpCb) (res interface{}) {
    70  	shard := m.GetShard(key)
    71  	shard.Lock()
    72  	v, ok := shard.items[key]
    73  	res = cb(ok, v, value)
    74  	shard.items[key] = res
    75  	shard.Unlock()
    76  	return res
    77  }
    78  
    79  // SetIfAbsent Sets the given value under the specified key if no value was associated with it.
    80  func (m CMap) SetIfAbsent(key string, value interface{}) bool {
    81  	// GetHeader map shard.
    82  	shard := m.GetShard(key)
    83  	shard.Lock()
    84  	_, ok := shard.items[key]
    85  	if !ok {
    86  		shard.items[key] = value
    87  	}
    88  	shard.Unlock()
    89  	return !ok
    90  }
    91  
    92  // Get retrieves an element from map under given key.
    93  func (m CMap) Get(key string) (interface{}, bool) {
    94  	// GetHeader shard
    95  	shard := m.GetShard(key)
    96  	shard.RLock()
    97  	// GetHeader item from shard.
    98  	val, ok := shard.items[key]
    99  	shard.RUnlock()
   100  	return val, ok
   101  }
   102  
   103  // Count returns the number of elements within the map.
   104  func (m CMap) Count() int {
   105  	count := 0
   106  	for i := 0; i < CMapShardCount; i++ {
   107  		shard := m[i]
   108  		shard.RLock()
   109  		count += len(shard.items)
   110  		shard.RUnlock()
   111  	}
   112  	return count
   113  }
   114  
   115  // Has Looks up an item under specified key
   116  func (m CMap) Has(key string) bool {
   117  	// GetHeader shard
   118  	shard := m.GetShard(key)
   119  	shard.RLock()
   120  	// See if element is within shard.
   121  	_, ok := shard.items[key]
   122  	shard.RUnlock()
   123  	return ok
   124  }
   125  
   126  // Remove removes an element from the map.
   127  func (m CMap) Remove(key string) {
   128  	// Try to get shard.
   129  	shard := m.GetShard(key)
   130  	shard.Lock()
   131  	delete(shard.items, key)
   132  	shard.Unlock()
   133  }
   134  
   135  // RemoveCb is a callback executed in a map.RemoveCb() call, while Lock is held
   136  // If returns true, the element will be removed from the map
   137  type RemoveCb func(key string, v interface{}, exists bool) bool
   138  
   139  // RemoveCb locks the shard containing the key, retrieves its current value and calls the callback with those params
   140  // If callback returns true and element exists, it will remove it from the map
   141  // Returns the value returned by the callback (even if element was not present in the map)
   142  func (m CMap) RemoveCb(key string, cb RemoveCb) bool {
   143  	// Try to get shard.
   144  	shard := m.GetShard(key)
   145  	shard.Lock()
   146  	v, ok := shard.items[key]
   147  	remove := cb(key, v, ok)
   148  	if remove && ok {
   149  		delete(shard.items, key)
   150  	}
   151  	shard.Unlock()
   152  	return remove
   153  }
   154  
   155  // Pop removes an element from the map and returns it
   156  func (m CMap) Pop(key string) (v interface{}, exists bool) {
   157  	// Try to get shard.
   158  	shard := m.GetShard(key)
   159  	shard.Lock()
   160  	v, exists = shard.items[key]
   161  	delete(shard.items, key)
   162  	shard.Unlock()
   163  	return v, exists
   164  }
   165  
   166  // IsEmpty checks if map is empty.
   167  func (m CMap) IsEmpty() bool {
   168  	return m.Count() == 0
   169  }
   170  
   171  // Tuple Used by the Iter & IterBuffered functions to wrap two variables together over a channel
   172  type Tuple struct {
   173  	Key string
   174  	Val interface{}
   175  }
   176  
   177  // Iter returns an iterator which could be used in a for range loop.
   178  //
   179  // Deprecated: using IterBuffered() will get a better performence
   180  func (m CMap) Iter() <-chan Tuple {
   181  	chans := snapshot(m)
   182  	ch := make(chan Tuple)
   183  	go fanIn(chans, ch)
   184  	return ch
   185  }
   186  
   187  // IterBuffered returns a buffered iterator which could be used in a for range loop.
   188  func (m CMap) IterBuffered() <-chan Tuple {
   189  	chans := snapshot(m)
   190  	total := 0
   191  	for _, c := range chans {
   192  		total += cap(c)
   193  	}
   194  	ch := make(chan Tuple, total)
   195  	go fanIn(chans, ch)
   196  	return ch
   197  }
   198  
   199  // Returns a array of channels that contains elements in each shard,
   200  // which likely takes a snapshot of `m`.
   201  // It returns once the size of each buffered channel is determined,
   202  // before all the channels are populated using goroutines.
   203  func snapshot(m CMap) (chans []chan Tuple) {
   204  	chans = make([]chan Tuple, CMapShardCount)
   205  	wg := sync.WaitGroup{}
   206  	wg.Add(CMapShardCount)
   207  	// Foreach shard.
   208  	for index, shard := range m {
   209  		go func(index int, shard *CMapShared) {
   210  			// Foreach key, value pair.
   211  			shard.RLock()
   212  			chans[index] = make(chan Tuple, len(shard.items))
   213  			wg.Done()
   214  			for key, val := range shard.items {
   215  				chans[index] <- Tuple{key, val}
   216  			}
   217  			shard.RUnlock()
   218  			close(chans[index])
   219  		}(index, shard)
   220  	}
   221  	wg.Wait()
   222  	return chans
   223  }
   224  
   225  // fanIn reads elements from channels `chans` into channel `out`
   226  func fanIn(chans []chan Tuple, out chan Tuple) {
   227  	wg := sync.WaitGroup{}
   228  	wg.Add(len(chans))
   229  	for _, ch := range chans {
   230  		go func(ch chan Tuple) {
   231  			for t := range ch {
   232  				out <- t
   233  			}
   234  			wg.Done()
   235  		}(ch)
   236  	}
   237  	wg.Wait()
   238  	close(out)
   239  }
   240  
   241  // Items returns all items as map[string]interface{}
   242  func (m CMap) Items() map[string]interface{} {
   243  	tmp := make(map[string]interface{})
   244  
   245  	// Insert items to temporary map.
   246  	for item := range m.IterBuffered() {
   247  		tmp[item.Key] = item.Val
   248  	}
   249  
   250  	return tmp
   251  }
   252  
   253  // IterCb Iterator callback,called for every key,value found in
   254  // maps. RLock is held for all calls for a given shard
   255  // therefore callback sess consistent view of a shard,
   256  // but not across the shards
   257  type IterCb func(key string, v interface{})
   258  
   259  // IterCb Callback based iterator, cheapest way to read
   260  // all elements in a map.
   261  func (m CMap) IterCb(fn IterCb) {
   262  	for idx := range m {
   263  		shard := (m)[idx]
   264  		shard.RLock()
   265  		for key, value := range shard.items {
   266  			fn(key, value)
   267  		}
   268  		shard.RUnlock()
   269  	}
   270  }
   271  
   272  // Keys returns all keys as []string
   273  func (m CMap) Keys() []string {
   274  	count := m.Count()
   275  	ch := make(chan string, count)
   276  	go func() {
   277  		// Foreach shard.
   278  		wg := sync.WaitGroup{}
   279  		wg.Add(CMapShardCount)
   280  		for _, shard := range m {
   281  			go func(shard *CMapShared) {
   282  				// Foreach key, value pair.
   283  				shard.RLock()
   284  				for key := range shard.items {
   285  					ch <- key
   286  				}
   287  				shard.RUnlock()
   288  				wg.Done()
   289  			}(shard)
   290  		}
   291  		wg.Wait()
   292  		close(ch)
   293  	}()
   294  
   295  	// Generate keys
   296  	keys := make([]string, 0, count)
   297  	for k := range ch {
   298  		keys = append(keys, k)
   299  	}
   300  	return keys
   301  }
   302  
   303  // JSON Reviles CMap "private" variables to json marshal.
   304  func (m CMap) JSON() ([]byte, error) {
   305  	tmp := make(map[string]interface{})
   306  
   307  	// Insert items to temporary map.
   308  	for item := range m.IterBuffered() {
   309  		tmp[item.Key] = item.Val
   310  	}
   311  	return EncodeJson(tmp)
   312  }
   313  
   314  func fnv32(key string) uint32 {
   315  	hash := uint32(2166136261)
   316  	const prime32 = uint32(16777619)
   317  	for i := 0; i < len(key); i++ {
   318  		hash *= prime32
   319  		hash ^= uint32(key[i])
   320  	}
   321  	return hash
   322  }