hashmap_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. package hashmap
  2. import (
  3. "fmt"
  4. "strconv"
  5. "sync"
  6. "sync/atomic"
  7. "testing"
  8. "time"
  9. )
  10. type Animal struct {
  11. name string
  12. }
  13. func uKey(i int) interface{} { return uintptr(i) }
  14. func iKey(i int) interface{} { return i }
  15. func sKey(i int) interface{} { return strconv.Itoa(i) }
  16. func bKey(i int) interface{} { return []byte(strconv.Itoa(i) + "bytes") }
  17. func s2sKey(s string) interface{} { return s }
  18. func s2bKey(s string) interface{} { return []byte(s) }
  19. func TestMapCreation(t *testing.T) {
  20. m := &HashMap{}
  21. if m.Len() != 0 {
  22. t.Errorf("new map should be empty but has %d items.", m.Len())
  23. }
  24. }
  25. func TestOverwrite(t *testing.T) {
  26. tests := []struct {
  27. name string
  28. key func(int) interface{}
  29. }{
  30. {name: "uintptr", key: uKey},
  31. {name: "int", key: iKey},
  32. {name: "string", key: sKey},
  33. {name: "[]byte", key: bKey},
  34. }
  35. for _, tt := range tests {
  36. t.Run(tt.name, func(t *testing.T) {
  37. m := &HashMap{}
  38. elephant := "elephant"
  39. monkey := "monkey"
  40. m.Set(tt.key(1), elephant)
  41. m.Set(tt.key(1), monkey)
  42. if m.Len() != 1 {
  43. t.Errorf("map should contain exactly one element but has %v items.", m.Len())
  44. }
  45. item, ok := m.Get(tt.key(1)) // Retrieve inserted element.
  46. if !ok {
  47. t.Error("ok should be true for item stored within the map.")
  48. }
  49. if item != monkey {
  50. t.Error("wrong item returned.")
  51. }
  52. })
  53. }
  54. }
  55. func TestInsert(t *testing.T) {
  56. tests := []struct {
  57. name string
  58. key func(int) interface{}
  59. }{
  60. {name: "uintptr", key: uKey},
  61. {name: "int", key: iKey},
  62. {name: "string", key: sKey},
  63. {name: "[]byte", key: bKey},
  64. }
  65. for _, tt := range tests {
  66. t.Run(tt.name, func(t *testing.T) {
  67. m := &HashMap{}
  68. _, ok := m.Get(tt.key(0))
  69. if ok {
  70. t.Error("empty map should not return an item.")
  71. }
  72. c := uintptr(16)
  73. ok = m.Insert(tt.key(0), c)
  74. if !ok {
  75. t.Error("insert did not succeed.")
  76. }
  77. ok = m.Insert(tt.key(128), c)
  78. if !ok {
  79. t.Error("insert did not succeed.")
  80. }
  81. ok = m.Insert(tt.key(128), c)
  82. if ok {
  83. t.Error("insert on existing item did succeed.")
  84. }
  85. _, ok = m.Get(tt.key(128))
  86. if !ok {
  87. t.Error("ok should be true for item stored within the map.")
  88. }
  89. _, ok = m.Get(tt.key(127))
  90. if ok {
  91. t.Error("item for key should not exist.")
  92. }
  93. if m.Len() != 2 {
  94. t.Errorf("map should contain exactly 2 elements but has %v items.", m.Len())
  95. }
  96. })
  97. }
  98. }
  99. func TestSet(t *testing.T) {
  100. tests := []struct {
  101. name string
  102. key func(int) interface{}
  103. }{
  104. {name: "int", key: iKey},
  105. {name: "string", key: sKey},
  106. {name: "[]byte", key: bKey},
  107. }
  108. for _, tt := range tests {
  109. t.Run(tt.name, func(t *testing.T) {
  110. m := New(4)
  111. elephant := "elephant"
  112. monkey := "monkey"
  113. m.Set(tt.key(4), elephant)
  114. m.Set(tt.key(3), elephant)
  115. m.Set(tt.key(2), monkey)
  116. m.Set(tt.key(1), monkey)
  117. if m.Len() != 4 {
  118. t.Error("map should contain exactly 4 elements.")
  119. }
  120. })
  121. }
  122. }
  123. func TestGet(t *testing.T) {
  124. tests := []struct {
  125. name string
  126. key func(string) interface{}
  127. }{
  128. {name: "string", key: s2sKey},
  129. {name: "[]byte", key: s2bKey},
  130. }
  131. for _, tt := range tests {
  132. t.Run(tt.name, func(t *testing.T) {
  133. m := &HashMap{}
  134. elephant := "elephant"
  135. val, ok := m.Get(tt.key("animal")) // Get a missing element.
  136. if ok {
  137. t.Error("ok should be false when item is missing from map.")
  138. }
  139. if val != nil {
  140. t.Error("Missing values should return as nil.")
  141. }
  142. m.Set(tt.key("animal"), elephant)
  143. _, ok = m.Get(tt.key("human")) // Get a missing element.
  144. if ok {
  145. t.Error("ok should be false when item is missing from map.")
  146. }
  147. value, ok := m.Get(tt.key("animal")) // Retrieve inserted element.
  148. if !ok {
  149. t.Error("ok should be true for item stored within the map.")
  150. }
  151. if value != elephant {
  152. t.Error("item was modified.")
  153. }
  154. })
  155. }
  156. }
  157. func TestGetUintKey(t *testing.T) {
  158. m := &HashMap{}
  159. _, ok := m.GetUintKey(0)
  160. if ok {
  161. t.Error("empty map should not return an item.")
  162. }
  163. c := uintptr(16)
  164. ok = m.Insert(uintptr(0), c)
  165. if !ok {
  166. t.Error("insert did not succeed.")
  167. }
  168. ok = m.Insert(uintptr(128), c)
  169. if !ok {
  170. t.Error("insert did not succeed.")
  171. }
  172. ok = m.Insert(uintptr(128), c)
  173. if ok {
  174. t.Error("insert on existing item did succeed.")
  175. }
  176. _, ok = m.GetUintKey(128)
  177. if !ok {
  178. t.Error("ok should be true for item stored within the map.")
  179. }
  180. _, ok = m.GetUintKey(127)
  181. if ok {
  182. t.Error("item for key should not exist.")
  183. }
  184. if m.Len() != 2 {
  185. t.Errorf("map should contain exactly 2 elements but has %v items.", m.Len())
  186. }
  187. }
  188. func TestGrow(t *testing.T) {
  189. m := &HashMap{}
  190. m.Grow(uintptr(63))
  191. for { // make sure to wait for resize operation to finish
  192. if atomic.LoadUintptr(&m.resizing) == 0 {
  193. break
  194. }
  195. time.Sleep(time.Microsecond * 50)
  196. }
  197. d := m.mapData()
  198. if d.keyshifts != 58 {
  199. t.Error("Grow operation did not result in correct internal map data structure.")
  200. }
  201. }
  202. func TestResize(t *testing.T) {
  203. m := New(2)
  204. itemCount := 50
  205. for i := 0; i < itemCount; i++ {
  206. m.Set(uintptr(i), &Animal{strconv.Itoa(i)})
  207. }
  208. if m.Len() != itemCount {
  209. t.Error("Expected element count did not match.")
  210. }
  211. for { // make sure to wait for resize operation to finish
  212. if atomic.LoadUintptr(&m.resizing) == 0 {
  213. break
  214. }
  215. time.Sleep(time.Microsecond * 50)
  216. }
  217. if m.Fillrate() != 34 {
  218. t.Error("Expecting 34 percent fillrate.")
  219. }
  220. for i := 0; i < itemCount; i++ {
  221. _, ok := m.Get(uintptr(i))
  222. if !ok {
  223. t.Error("Getting inserted item failed.")
  224. }
  225. }
  226. }
  227. func TestStringer(t *testing.T) {
  228. tests := []struct {
  229. name string
  230. key func(int) interface{}
  231. }{
  232. {name: "int", key: iKey},
  233. {name: "string", key: sKey},
  234. {name: "[]byte", key: bKey},
  235. }
  236. for _, tt := range tests {
  237. t.Run(tt.name, func(t *testing.T) {
  238. m := &HashMap{}
  239. elephant := &Animal{"elephant"}
  240. monkey := &Animal{"monkey"}
  241. s := m.String()
  242. if s != "[]" {
  243. t.Error("empty map as string does not match.")
  244. }
  245. m.Set(tt.key(0), elephant)
  246. s = m.String()
  247. hashedKey0 := getKeyHash(tt.key(0))
  248. if s != fmt.Sprintf("[%v]", hashedKey0) {
  249. t.Error("1 item map as string does not match:", s)
  250. }
  251. m.Set(tt.key(1), monkey)
  252. s = m.String()
  253. hashedKey1 := getKeyHash(tt.key(1))
  254. if s != fmt.Sprintf("[%v,%v]", hashedKey1, hashedKey0) {
  255. t.Error("2 item map as string does not match:", s)
  256. }
  257. })
  258. }
  259. }
  260. func TestDelete(t *testing.T) {
  261. tests := []struct {
  262. name string
  263. key func(int) interface{}
  264. }{
  265. {name: "int", key: func(i int) interface{} { return i }},
  266. {name: "string", key: func(i int) interface{} { return strconv.Itoa(i) }},
  267. {name: "[]byte", key: func(i int) interface{} { return []byte(strconv.Itoa(i) + "bytes") }},
  268. }
  269. for _, tt := range tests {
  270. t.Run(tt.name, func(t *testing.T) {
  271. m := &HashMap{}
  272. m.Del(tt.key(0))
  273. elephant := &Animal{"elephant"}
  274. monkey := &Animal{"monkey"}
  275. m.Set(tt.key(1), elephant)
  276. m.Set(tt.key(2), monkey)
  277. m.Del(tt.key(0))
  278. m.Del(tt.key(3))
  279. if m.Len() != 2 {
  280. t.Error("map should contain exactly two elements.")
  281. }
  282. m.Del(tt.key(1))
  283. m.Del(tt.key(1))
  284. m.Del(tt.key(2))
  285. if m.Len() != 0 {
  286. t.Error("map should be empty.")
  287. }
  288. for item := range m.Iter() {
  289. t.Errorf("map should be empty but got %v in the iterator.", item)
  290. }
  291. val, ok := m.Get(tt.key(1)) // Get a missing element.
  292. if ok {
  293. t.Error("ok should be false when item is missing from map.")
  294. }
  295. if val != nil {
  296. t.Error("Missing values should return as nil.")
  297. }
  298. m.Set(tt.key(1), elephant)
  299. })
  300. }
  301. }
  302. func TestIterator(t *testing.T) {
  303. tests := []struct {
  304. name string
  305. key func(int) interface{}
  306. }{
  307. {name: "uintptr", key: iKey},
  308. {name: "string", key: sKey},
  309. {name: "[]byte", key: bKey},
  310. }
  311. for _, tt := range tests {
  312. t.Run(tt.name, func(t *testing.T) {
  313. m := &HashMap{}
  314. for item := range m.Iter() {
  315. t.Errorf("Expected no object but got %v.", item)
  316. }
  317. itemCount := 16
  318. for i := itemCount; i > 0; i-- {
  319. m.Set(tt.key(i), &Animal{strconv.Itoa(i)})
  320. }
  321. counter := 0
  322. for item := range m.Iter() {
  323. val := item.Value
  324. if val == nil {
  325. t.Error("Expecting an object.")
  326. }
  327. counter++
  328. }
  329. if counter != itemCount {
  330. t.Error("Returned item count did not match.")
  331. }
  332. })
  333. }
  334. }
  335. func TestHashedKey(t *testing.T) {
  336. m := &HashMap{}
  337. _, ok := m.GetHashedKey(uintptr(0))
  338. if ok {
  339. t.Error("empty map should not return an item.")
  340. }
  341. m.DelHashedKey(uintptr(0))
  342. m.allocate(uintptr(64))
  343. m.DelHashedKey(uintptr(0))
  344. itemCount := 16
  345. log := log2(uintptr(itemCount))
  346. for i := 0; i < itemCount; i++ {
  347. m.SetHashedKey(uintptr(i)<<(strconv.IntSize-log), &Animal{strconv.Itoa(i)})
  348. }
  349. if m.Len() != itemCount {
  350. t.Error("Expected element count did not match.")
  351. }
  352. for i := 0; i < itemCount; i++ {
  353. _, ok = m.GetHashedKey(uintptr(i) << (strconv.IntSize - log))
  354. if !ok {
  355. t.Error("Getting inserted item failed.")
  356. }
  357. }
  358. for i := 0; i < itemCount; i++ {
  359. m.DelHashedKey(uintptr(i) << (strconv.IntSize - log))
  360. }
  361. _, ok = m.GetHashedKey(uintptr(0))
  362. if ok {
  363. t.Error("item for key should not exist.")
  364. }
  365. if m.Len() != 0 {
  366. t.Error("Map is not empty.")
  367. }
  368. }
  369. func TestCompareAndSwapHashedKey(t *testing.T) {
  370. m := &HashMap{}
  371. elephant := &Animal{"elephant"}
  372. monkey := &Animal{"monkey"}
  373. m.SetHashedKey(1<<(strconv.IntSize-2), elephant)
  374. if m.Len() != 1 {
  375. t.Error("map should contain exactly one element.")
  376. }
  377. if !m.CasHashedKey(1<<(strconv.IntSize-2), elephant, monkey) {
  378. t.Error("Cas should success if expectation met")
  379. }
  380. if m.Len() != 1 {
  381. t.Error("map should contain exactly one element.")
  382. }
  383. if m.CasHashedKey(1<<(strconv.IntSize-2), elephant, monkey) {
  384. t.Error("Cas should fail if expectation didn't meet")
  385. }
  386. if m.Len() != 1 {
  387. t.Error("map should contain exactly one element.")
  388. }
  389. item, ok := m.GetHashedKey(1 << (strconv.IntSize - 2))
  390. if !ok {
  391. t.Error("ok should be true for item stored within the map.")
  392. }
  393. if item != monkey {
  394. t.Error("wrong item returned.")
  395. }
  396. }
  397. func TestCompareAndSwap(t *testing.T) {
  398. tests := []struct {
  399. name string
  400. key interface{}
  401. }{
  402. {name: "string", key: "animal"},
  403. {name: "[]byte", key: []byte(`animal`)},
  404. }
  405. for _, tt := range tests {
  406. t.Run(tt.name, func(t *testing.T) {
  407. m := &HashMap{}
  408. ok := m.CasHashedKey(uintptr(0), nil, nil)
  409. if ok {
  410. t.Error("empty map should not return an item.")
  411. }
  412. elephant := &Animal{"elephant"}
  413. monkey := &Animal{"monkey"}
  414. m.Set(tt.key, elephant)
  415. if m.Len() != 1 {
  416. t.Error("map should contain exactly one element.")
  417. }
  418. if !m.Cas(tt.key, elephant, monkey) {
  419. t.Error("Cas should success if expectation met")
  420. }
  421. if m.Cas(tt.key, elephant, monkey) {
  422. t.Error("Cas should fail if expectation didn't meet")
  423. }
  424. item, ok := m.Get(tt.key)
  425. if !ok {
  426. t.Error("ok should be true for item stored within the map.")
  427. }
  428. if item != monkey {
  429. t.Error("wrong item returned.")
  430. }
  431. })
  432. }
  433. }
  434. // TestAPICounter shows how to use the hashmap to count REST server API calls
  435. func TestAPICounter(t *testing.T) {
  436. tests := []struct {
  437. name string
  438. key func(string) interface{}
  439. }{
  440. {name: "string", key: s2sKey},
  441. {name: "[]byte", key: s2bKey},
  442. }
  443. for _, tt := range tests {
  444. t.Run(tt.name, func(t *testing.T) {
  445. m := &HashMap{}
  446. for i := 0; i < 100; i++ {
  447. s := fmt.Sprintf("/api%d/", i%4)
  448. for {
  449. counter := int64(0)
  450. actual, _ := m.GetOrInsert(tt.key(s), &counter)
  451. c := actual.(*int64)
  452. atomic.AddInt64(c, 1)
  453. break
  454. }
  455. }
  456. s := fmt.Sprintf("/api%d/", 0)
  457. val, _ := m.Get(tt.key(s))
  458. c := val.(*int64)
  459. if *c != 25 {
  460. t.Error("wrong API call count.")
  461. }
  462. })
  463. }
  464. }
  465. func TestExample(t *testing.T) {
  466. m := &HashMap{}
  467. i := 123
  468. m.Set("amount", i)
  469. j, ok := m.Get("amount")
  470. if !ok {
  471. t.Fail()
  472. }
  473. if i != j {
  474. t.Fail()
  475. }
  476. }
  477. func TestByteSlice(t *testing.T) {
  478. m := &HashMap{}
  479. k := []byte(`Well this is a fine mess`)
  480. i := 123
  481. m.Set(k, i)
  482. j, ok := m.Get(k)
  483. if !ok {
  484. t.Fail()
  485. }
  486. if i != j {
  487. t.Fail()
  488. }
  489. }
  490. func TestHashMap_parallel(t *testing.T) {
  491. max := 10
  492. dur := 2 * time.Second
  493. m := &HashMap{}
  494. do := func(t *testing.T, max int, d time.Duration, fn func(*testing.T, int)) <-chan error {
  495. t.Helper()
  496. done := make(chan error)
  497. var times int64
  498. // This goroutines will terminate test in case if closure hangs.
  499. go func() {
  500. for {
  501. select {
  502. case <-time.After(d + 500*time.Millisecond):
  503. if atomic.LoadInt64(&times) == 0 {
  504. done <- fmt.Errorf("closure was not executed even once, something blocks it")
  505. }
  506. close(done)
  507. case <-done:
  508. }
  509. }
  510. }()
  511. go func() {
  512. timer := time.NewTimer(d)
  513. defer timer.Stop()
  514. InfLoop:
  515. for {
  516. for i := 0; i < max; i++ {
  517. select {
  518. case <-timer.C:
  519. break InfLoop
  520. default:
  521. }
  522. fn(t, i)
  523. atomic.AddInt64(&times, 1)
  524. }
  525. }
  526. close(done)
  527. }()
  528. return done
  529. }
  530. wait := func(t *testing.T, done <-chan error) {
  531. t.Helper()
  532. if err := <-done; err != nil {
  533. t.Error(err)
  534. }
  535. }
  536. // Initial fill.
  537. for i := 0; i < max; i++ {
  538. m.Set(i, i)
  539. m.Set(fmt.Sprintf("%d", i), i)
  540. m.SetHashedKey(uintptr(i), i)
  541. }
  542. t.Run("set_get", func(t *testing.T) {
  543. doneSet := do(t, max, dur, func(t *testing.T, i int) {
  544. m.Set(i, i)
  545. })
  546. doneGet := do(t, max, dur, func(t *testing.T, i int) {
  547. if _, ok := m.Get(i); !ok {
  548. t.Errorf("missing value for key: %d", i)
  549. }
  550. })
  551. doneGetStringKey := do(t, max, dur, func(t *testing.T, i int) {
  552. if _, ok := m.GetStringKey(fmt.Sprintf("%d", i)); !ok {
  553. t.Errorf("missing value for key: %d", i)
  554. }
  555. })
  556. doneGetHashedKey := do(t, max, dur, func(t *testing.T, i int) {
  557. if _, ok := m.GetHashedKey(uintptr(i)); !ok {
  558. t.Errorf("missing value for key: %d", i)
  559. }
  560. })
  561. wait(t, doneSet)
  562. wait(t, doneGet)
  563. wait(t, doneGetStringKey)
  564. wait(t, doneGetHashedKey)
  565. })
  566. t.Run("get-or-insert-and-delete", func(t *testing.T) {
  567. doneGetOrInsert := do(t, max, dur, func(t *testing.T, i int) {
  568. m.GetOrInsert(i, i)
  569. })
  570. doneDel := do(t, max, dur, func(t *testing.T, i int) {
  571. m.Del(i)
  572. })
  573. wait(t, doneGetOrInsert)
  574. wait(t, doneDel)
  575. })
  576. }
  577. func TestHashMap_SetConcurrent(t *testing.T) {
  578. blocks := &HashMap{}
  579. var wg sync.WaitGroup
  580. for i := 0; i < 100; i++ {
  581. wg.Add(1)
  582. go func(blocks *HashMap, i int) {
  583. defer wg.Done()
  584. blocks.Set(strconv.Itoa(i), struct{}{})
  585. wg.Add(1)
  586. go func(blocks *HashMap, i int) {
  587. defer wg.Done()
  588. blocks.Get(strconv.Itoa(i))
  589. }(blocks, i)
  590. }(blocks, i)
  591. }
  592. wg.Wait()
  593. }