splicer.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package splicer
  2. import (
  3. "servitor/pub"
  4. "sync"
  5. )
  6. type Splicer []struct {
  7. basepoint uint
  8. page pub.Container
  9. elements []pub.Tangible
  10. }
  11. func (s Splicer) Harvest(quantity uint, startingPoint uint) ([]pub.Tangible, pub.Container, uint) {
  12. /* Make a clone so Splicer remains immutable and thus threadsafe */
  13. clone := s.clone()
  14. clone.replenish(int(quantity + startingPoint))
  15. for i := 0; i < int(startingPoint); i++ {
  16. _ = clone.microharvest()
  17. }
  18. output := make([]pub.Tangible, 0, quantity)
  19. for i := 0; i < int(quantity); i++ {
  20. harvested := clone.microharvest()
  21. if harvested == nil {
  22. clone = nil
  23. break
  24. }
  25. output = append(output, harvested)
  26. }
  27. return output, clone, 0
  28. }
  29. func (s Splicer) clone() *Splicer {
  30. newSplicer := make(Splicer, len(s))
  31. copy(newSplicer, s)
  32. for i := range newSplicer {
  33. copy(newSplicer[i].elements, s[i].elements)
  34. }
  35. return &newSplicer
  36. }
  37. func (s Splicer) replenish(amount int) {
  38. var wg sync.WaitGroup
  39. for i, source := range s {
  40. i := i
  41. source := source
  42. wg.Add(1)
  43. go func() {
  44. if len(source.elements) < amount && source.page != nil {
  45. var newElements []pub.Tangible
  46. newElements, s[i].page, s[i].basepoint = source.page.Harvest(uint(amount - len(source.elements)), source.basepoint)
  47. s[i].elements = append(s[i].elements, newElements...)
  48. }
  49. wg.Done()
  50. }()
  51. }
  52. wg.Wait()
  53. }
  54. func (s Splicer) microharvest() pub.Tangible {
  55. var mostRecent pub.Tangible
  56. var mostRecentIndex int
  57. for i, candidate := range s {
  58. if len(candidate.elements) == 0 {
  59. continue
  60. }
  61. candidateElement := candidate.elements[0]
  62. if mostRecent == nil {
  63. mostRecent = candidateElement
  64. mostRecentIndex = i
  65. continue
  66. }
  67. if candidateElement == nil {
  68. continue
  69. }
  70. if candidateElement.Timestamp().After(mostRecent.Timestamp()) {
  71. mostRecent = candidateElement
  72. mostRecentIndex = i
  73. continue
  74. }
  75. }
  76. if mostRecent == nil {
  77. return nil
  78. }
  79. /* Shift (pop from front) the element that was selected */
  80. s[mostRecentIndex].elements = s[mostRecentIndex].elements[1:]
  81. return mostRecent
  82. }
  83. func NewSplicer(inputs []string) *Splicer {
  84. s := make(Splicer, len(inputs))
  85. var wg sync.WaitGroup
  86. for i, input := range inputs {
  87. i := i
  88. input := input
  89. wg.Add(1)
  90. go func() {
  91. fetched := pub.FetchUserInput(input)
  92. switch narrowed := fetched.(type) {
  93. case pub.Tangible:
  94. s[i].page = narrowed.Children()
  95. case *pub.Collection:
  96. s[i].page = narrowed
  97. default:
  98. panic("cannot splice non-Tangible, non-Collection")
  99. }
  100. s[i].basepoint = 0
  101. s[i].elements = []pub.Tangible{}
  102. wg.Done()
  103. }()
  104. }
  105. wg.Wait()
  106. return &s
  107. }