hashmap.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. package hashmap
  2. import (
  3. "bytes"
  4. "fmt"
  5. "reflect"
  6. "strconv"
  7. "sync/atomic"
  8. "unsafe"
  9. )
  10. // DefaultSize is the default size for a zero allocated map
  11. const DefaultSize = 8
  12. // MaxFillRate is the maximum fill rate for the slice before a resize will happen.
  13. const MaxFillRate = 50
  14. type (
  15. hashMapData struct {
  16. keyshifts uintptr // Pointer size - log2 of array size, to be used as index in the data array
  17. count uintptr // count of filled elements in the slice
  18. data unsafe.Pointer // pointer to slice data array
  19. index []*ListElement // storage for the slice for the garbage collector to not clean it up
  20. }
  21. // HashMap implements a read optimized hash map.
  22. HashMap struct {
  23. datamap unsafe.Pointer // pointer to a map instance that gets replaced if the map resizes
  24. linkedlist unsafe.Pointer // key sorted linked list of elements
  25. resizing uintptr // flag that marks a resizing operation in progress
  26. }
  27. // KeyValue represents a key/value that is returned by the iterator.
  28. KeyValue struct {
  29. Key interface{}
  30. Value interface{}
  31. }
  32. )
  33. // New returns a new HashMap instance with a specific initialization size.
  34. func New(size uintptr) *HashMap {
  35. m := &HashMap{}
  36. m.allocate(size)
  37. return m
  38. }
  39. // Len returns the number of elements within the map.
  40. func (m *HashMap) Len() int {
  41. list := m.list()
  42. return list.Len()
  43. }
  44. func (m *HashMap) mapData() *hashMapData {
  45. return (*hashMapData)(atomic.LoadPointer(&m.datamap))
  46. }
  47. func (m *HashMap) Listing() []*KeyValue {
  48. mm := []*KeyValue{}
  49. list := m.list()
  50. if list == nil {
  51. return mm
  52. }
  53. item := list.First()
  54. for item != nil {
  55. mm = append(mm, &KeyValue{Key:item.key,Value:item.Value()})
  56. item = item.Next()
  57. }
  58. return mm
  59. }
  60. func (m *HashMap) list() *List {
  61. return (*List)(atomic.LoadPointer(&m.linkedlist))
  62. }
  63. func (m *HashMap) allocate(newSize uintptr) {
  64. list := NewList()
  65. // atomic swap in case of another allocation happening concurrently
  66. if atomic.CompareAndSwapPointer(&m.linkedlist, nil, unsafe.Pointer(list)) {
  67. if atomic.CompareAndSwapUintptr(&m.resizing, uintptr(0), uintptr(1)) {
  68. m.grow(newSize, false)
  69. }
  70. }
  71. }
  72. // Fillrate returns the fill rate of the map as an percentage integer.
  73. func (m *HashMap) Fillrate() uintptr {
  74. data := m.mapData()
  75. count := atomic.LoadUintptr(&data.count)
  76. l := uintptr(len(data.index))
  77. return (count * 100) / l
  78. }
  79. func (m *HashMap) resizeNeeded(data *hashMapData, count uintptr) bool {
  80. l := uintptr(len(data.index))
  81. if l == 0 {
  82. return false
  83. }
  84. fillRate := (count * 100) / l
  85. return fillRate > MaxFillRate
  86. }
  87. func (m *HashMap) indexElement(hashedKey uintptr) (data *hashMapData, item *ListElement) {
  88. data = m.mapData()
  89. if data == nil {
  90. return nil, nil
  91. }
  92. index := hashedKey >> data.keyshifts
  93. ptr := (*unsafe.Pointer)(unsafe.Pointer(uintptr(data.data) + index*intSizeBytes))
  94. item = (*ListElement)(atomic.LoadPointer(ptr))
  95. return data, item
  96. }
  97. /* The Golang 1.10.1 compiler dons not inline this function well
  98. func (m *HashMap) searchItem(item *ListElement, key interface{}, keyHash uintptr) (value interface{}, ok bool) {
  99. for item != nil {
  100. if item.keyHash == keyHash && item.key == key {
  101. return item.Value(), true
  102. }
  103. if item.keyHash > keyHash {
  104. return nil, false
  105. }
  106. item = item.Next()
  107. }
  108. return nil, false
  109. }
  110. */
  111. // Del deletes the key from the map.
  112. func (m *HashMap) Del(key interface{}) {
  113. list := m.list()
  114. if list == nil {
  115. return
  116. }
  117. h := getKeyHash(key)
  118. var element *ListElement
  119. ElementLoop:
  120. for _, element = m.indexElement(h); element != nil; element = element.Next() {
  121. if element.keyHash == h {
  122. switch key.(type) {
  123. case []byte:
  124. if bytes.Compare(element.key.([]byte), key.([]byte)) == 0 {
  125. break ElementLoop
  126. }
  127. default:
  128. if element.key == key {
  129. break ElementLoop
  130. }
  131. }
  132. }
  133. if element.keyHash > h {
  134. return
  135. }
  136. }
  137. if element == nil {
  138. return
  139. }
  140. m.deleteElement(element)
  141. list.Delete(element)
  142. }
  143. // DelHashedKey deletes the hashed key from the map.
  144. func (m *HashMap) DelHashedKey(hashedKey uintptr) {
  145. list := m.list()
  146. if list == nil {
  147. return
  148. }
  149. // inline HashMap.searchItem()
  150. var element *ListElement
  151. ElementLoop:
  152. for _, element = m.indexElement(hashedKey); element != nil; element = element.Next() {
  153. if element.keyHash == hashedKey {
  154. break ElementLoop
  155. }
  156. if element.keyHash > hashedKey {
  157. return
  158. }
  159. }
  160. if element == nil {
  161. return
  162. }
  163. m.deleteElement(element)
  164. list.Delete(element)
  165. }
  166. // deleteElement deletes an element from index
  167. func (m *HashMap) deleteElement(element *ListElement) {
  168. for {
  169. data := m.mapData()
  170. index := element.keyHash >> data.keyshifts
  171. ptr := (*unsafe.Pointer)(unsafe.Pointer(uintptr(data.data) + index*intSizeBytes))
  172. next := element.Next()
  173. if next != nil && element.keyHash>>data.keyshifts != index {
  174. next = nil // do not set index to next item if it's not the same slice index
  175. }
  176. atomic.CompareAndSwapPointer(ptr, unsafe.Pointer(element), unsafe.Pointer(next))
  177. currentdata := m.mapData()
  178. if data == currentdata { // check that no resize happened
  179. break
  180. }
  181. }
  182. }
  183. // Insert sets the value under the specified key to the map if it does not exist yet.
  184. // If a resizing operation is happening concurrently while calling Set, the item might show up in the map only after the resize operation is finished.
  185. // Returns true if the item was inserted or false if it existed.
  186. func (m *HashMap) Insert(key interface{}, value interface{}) bool {
  187. h := getKeyHash(key)
  188. element := &ListElement{
  189. key: key,
  190. keyHash: h,
  191. value: unsafe.Pointer(&value),
  192. }
  193. return m.insertListElement(element, false)
  194. }
  195. // Set sets the value under the specified key to the map. An existing item for this key will be overwritten.
  196. // If a resizing operation is happening concurrently while calling Set, the item might show up in the map only after the resize operation is finished.
  197. func (m *HashMap) Set(key interface{}, value interface{}) {
  198. h := getKeyHash(key)
  199. element := &ListElement{
  200. key: key,
  201. keyHash: h,
  202. value: unsafe.Pointer(&value),
  203. }
  204. m.insertListElement(element, true)
  205. }
  206. // SetHashedKey sets the value under the specified hash key to the map. An existing item for this key will be overwritten.
  207. // You can use this function if your keys are already hashes and you want to avoid another hashing of the key.
  208. // Do not use non hashes as keys for this function, the performance would decrease!
  209. // If a resizing operation is happening concurrently while calling Set, the item might show up in the map only after the resize operation is finished.
  210. func (m *HashMap) SetHashedKey(hashedKey uintptr, value interface{}) {
  211. element := &ListElement{
  212. key: hashedKey,
  213. keyHash: hashedKey,
  214. value: unsafe.Pointer(&value),
  215. }
  216. m.insertListElement(element, true)
  217. }
  218. func (m *HashMap) insertListElement(element *ListElement, update bool) bool {
  219. for {
  220. data, existing := m.indexElement(element.keyHash)
  221. if data == nil {
  222. m.allocate(DefaultSize)
  223. continue // read mapdata and slice item again
  224. }
  225. list := m.list()
  226. if update {
  227. if !list.AddOrUpdate(element, existing) {
  228. continue // a concurrent add did interfere, try again
  229. }
  230. } else {
  231. existed, inserted := list.Add(element, existing)
  232. if existed {
  233. return false
  234. }
  235. if !inserted {
  236. continue
  237. }
  238. }
  239. count := data.addItemToIndex(element)
  240. if m.resizeNeeded(data, count) {
  241. if atomic.CompareAndSwapUintptr(&m.resizing, uintptr(0), uintptr(1)) {
  242. go m.grow(0, true)
  243. }
  244. }
  245. return true
  246. }
  247. }
  248. // CasHashedKey performs a compare and swap operation sets the value under the specified hash key to the map. An existing item for this key will be overwritten.
  249. func (m *HashMap) CasHashedKey(hashedKey uintptr, from, to interface{}) bool {
  250. data, existing := m.indexElement(hashedKey)
  251. if data == nil {
  252. return false
  253. }
  254. list := m.list()
  255. if list == nil {
  256. return false
  257. }
  258. element := &ListElement{
  259. key: hashedKey,
  260. keyHash: hashedKey,
  261. value: unsafe.Pointer(&to),
  262. }
  263. return list.Cas(element, from, existing)
  264. }
  265. // Cas performs a compare and swap operation sets the value under the specified hash key to the map. An existing item for this key will be overwritten.
  266. func (m *HashMap) Cas(key, from, to interface{}) bool {
  267. h := getKeyHash(key)
  268. return m.CasHashedKey(h, from, to)
  269. }
  270. // adds an item to the index if needed and returns the new item counter if it changed, otherwise 0
  271. func (mapData *hashMapData) addItemToIndex(item *ListElement) uintptr {
  272. index := item.keyHash >> mapData.keyshifts
  273. ptr := (*unsafe.Pointer)(unsafe.Pointer(uintptr(mapData.data) + index*intSizeBytes))
  274. for { // loop until the smallest key hash is in the index
  275. element := (*ListElement)(atomic.LoadPointer(ptr)) // get the current item in the index
  276. if element == nil { // no item yet at this index
  277. if atomic.CompareAndSwapPointer(ptr, nil, unsafe.Pointer(item)) {
  278. return atomic.AddUintptr(&mapData.count, 1)
  279. }
  280. continue // a new item was inserted concurrently, retry
  281. }
  282. if item.keyHash < element.keyHash {
  283. // the new item is the smallest for this index?
  284. if !atomic.CompareAndSwapPointer(ptr, unsafe.Pointer(element), unsafe.Pointer(item)) {
  285. continue // a new item was inserted concurrently, retry
  286. }
  287. }
  288. return 0
  289. }
  290. }
  291. // Grow resizes the hashmap to a new size, gets rounded up to next power of 2.
  292. // To double the size of the hashmap use newSize 0.
  293. // This function returns immediately, the resize operation is done in a goroutine.
  294. // No resizing is done in case of another resize operation already being in progress.
  295. func (m *HashMap) Grow(newSize uintptr) {
  296. if atomic.CompareAndSwapUintptr(&m.resizing, uintptr(0), uintptr(1)) {
  297. go m.grow(newSize, true)
  298. }
  299. }
  300. func (m *HashMap) grow(newSize uintptr, loop bool) {
  301. defer atomic.CompareAndSwapUintptr(&m.resizing, uintptr(1), uintptr(0))
  302. for {
  303. data := m.mapData()
  304. if newSize == 0 {
  305. newSize = uintptr(len(data.index)) << 1
  306. } else {
  307. newSize = roundUpPower2(newSize)
  308. }
  309. index := make([]*ListElement, newSize)
  310. header := (*reflect.SliceHeader)(unsafe.Pointer(&index))
  311. newdata := &hashMapData{
  312. keyshifts: strconv.IntSize - log2(newSize),
  313. data: unsafe.Pointer(header.Data), // use address of slice data storage
  314. index: index,
  315. }
  316. m.fillIndexItems(newdata) // initialize new index slice with longer keys
  317. atomic.StorePointer(&m.datamap, unsafe.Pointer(newdata))
  318. m.fillIndexItems(newdata) // make sure that the new index is up to date with the current state of the linked list
  319. if !loop {
  320. break
  321. }
  322. // check if a new resize needs to be done already
  323. count := uintptr(m.Len())
  324. if !m.resizeNeeded(newdata, count) {
  325. break
  326. }
  327. newSize = 0 // 0 means double the current size
  328. }
  329. }
  330. func (m *HashMap) fillIndexItems(mapData *hashMapData) {
  331. list := m.list()
  332. if list == nil {
  333. return
  334. }
  335. first := list.First()
  336. item := first
  337. lastIndex := uintptr(0)
  338. for item != nil {
  339. index := item.keyHash >> mapData.keyshifts
  340. if item == first || index != lastIndex { // store item with smallest hash key for every index
  341. mapData.addItemToIndex(item)
  342. lastIndex = index
  343. }
  344. item = item.Next()
  345. }
  346. }
  347. // String returns the map as a string, only hashed keys are printed.
  348. func (m *HashMap) String() string {
  349. list := m.list()
  350. if list == nil {
  351. return "[]"
  352. }
  353. buffer := bytes.NewBufferString("")
  354. buffer.WriteRune('[')
  355. first := list.First()
  356. item := first
  357. for item != nil {
  358. if item != first {
  359. buffer.WriteRune(',')
  360. }
  361. fmt.Fprint(buffer, item.keyHash)
  362. item = item.Next()
  363. }
  364. buffer.WriteRune(']')
  365. return buffer.String()
  366. }
  367. // Iter returns an iterator which could be used in a for range loop.
  368. // The order of the items is sorted by hash keys.
  369. func (m *HashMap) Iter() <-chan KeyValue {
  370. ch := make(chan KeyValue) // do not use a size here since items can get added during iteration
  371. go func() {
  372. list := m.list()
  373. if list == nil {
  374. close(ch)
  375. return
  376. }
  377. item := list.First()
  378. for item != nil {
  379. value := item.Value()
  380. ch <- KeyValue{item.key, value}
  381. item = item.Next()
  382. }
  383. close(ch)
  384. }()
  385. return ch
  386. }