iterator.go 1.4 KB

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