iterator.go 1.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package redis
  2. import (
  3. "sync"
  4. )
  5. // ScanIterator is used to incrementally iterate over a collection of elements.
  6. // It's safe for concurrent use by multiple goroutines.
  7. type ScanIterator struct {
  8. mu sync.Mutex // protects Scanner and pos
  9. cmd *ScanCmd
  10. pos int
  11. }
  12. // Err returns the last iterator error, if any.
  13. func (it *ScanIterator) Err() error {
  14. it.mu.Lock()
  15. err := it.cmd.Err()
  16. it.mu.Unlock()
  17. return err
  18. }
  19. // Next advances the cursor and returns true if more values can be read.
  20. func (it *ScanIterator) Next() bool {
  21. it.mu.Lock()
  22. defer it.mu.Unlock()
  23. // Instantly return on errors.
  24. if it.cmd.Err() != nil {
  25. return false
  26. }
  27. // Advance cursor, check if we are still within range.
  28. if it.pos < len(it.cmd.page) {
  29. it.pos++
  30. return true
  31. }
  32. for {
  33. // Return if there is no more data to fetch.
  34. if it.cmd.cursor == 0 {
  35. return false
  36. }
  37. // Fetch next page.
  38. if it.cmd.args[0] == "scan" {
  39. it.cmd.args[1] = it.cmd.cursor
  40. } else {
  41. it.cmd.args[2] = it.cmd.cursor
  42. }
  43. err := it.cmd.process(it.cmd)
  44. if err != nil {
  45. return false
  46. }
  47. it.pos = 1
  48. // Redis can occasionally return empty page.
  49. if len(it.cmd.page) > 0 {
  50. return true
  51. }
  52. }
  53. }
  54. // Val returns the key/field at the current cursor position.
  55. func (it *ScanIterator) Val() string {
  56. var v string
  57. it.mu.Lock()
  58. if it.cmd.Err() == nil && it.pos > 0 && it.pos <= len(it.cmd.page) {
  59. v = it.cmd.page[it.pos-1]
  60. }
  61. it.mu.Unlock()
  62. return v
  63. }