Browse Source

splicer: simplify logic and make it more parallel

Benton Edmondson 1 year ago
parent
commit
e17d36263c
1 changed files with 42 additions and 42 deletions
  1. 42 42
      splicer/splicer.go

+ 42 - 42
splicer/splicer.go

@@ -5,23 +5,20 @@ import (
 	"sync"
 )
 
-/*
-	TODO:
-	This can be optimized by storing more than 1 element from each container,
-	and then refilling elements of the containers in parallel.
-*/
-
 type Splicer []struct {
 	basepoint uint
 	page      pub.Container
-	element   pub.Tangible
+	elements   []pub.Tangible
 }
 
 func (s Splicer) Harvest(quantity uint, startingPoint uint) ([]pub.Tangible, pub.Container, uint) {
+	/* Make a clone so Splicer remains immutable and thus threadsafe */
 	clone := s.clone()
 
+	clone.replenish(int(quantity + startingPoint))
+
 	for i := 0; i < int(startingPoint); i++ {
-		clone.microharvest()
+		_ = clone.microharvest()
 	}
 
 	output := make([]pub.Tangible, 0, quantity)
@@ -40,25 +37,52 @@ func (s Splicer) Harvest(quantity uint, startingPoint uint) ([]pub.Tangible, pub
 func (s Splicer) clone() *Splicer {
 	newSplicer := make(Splicer, len(s))
 	copy(newSplicer, s)
+	for i := range newSplicer {
+		copy(newSplicer[i].elements, s[i].elements)
+	}
 	return &newSplicer
 }
 
+func (s Splicer) replenish(amount int) {
+	var wg sync.WaitGroup
+	for i, source := range s {
+		i := i
+		source := source
+		wg.Add(1)
+		go func() {
+			if len(source.elements) < amount && source.page != nil {
+				var newElements []pub.Tangible
+				newElements, s[i].page, s[i].basepoint = source.page.Harvest(uint(amount - len(source.elements)), source.basepoint)
+				s[i].elements = append(s[i].elements, newElements...)
+			}
+			wg.Done()
+		}()
+	}
+	wg.Wait()
+}
+
 func (s Splicer) microharvest() pub.Tangible {
 	var mostRecent pub.Tangible
 	var mostRecentIndex int
 	for i, candidate := range s {
+		if len(candidate.elements) == 0 {
+			continue
+		}
+
+		candidateElement := candidate.elements[0]
+
 		if mostRecent == nil {
-			mostRecent = candidate.element
+			mostRecent = candidateElement
 			mostRecentIndex = i
 			continue
 		}
 
-		if candidate.element == nil {
+		if candidateElement == nil {
 			continue
 		}
 
-		if candidate.element.Timestamp().After(mostRecent.Timestamp()) {
-			mostRecent = candidate.element
+		if candidateElement.Timestamp().After(mostRecent.Timestamp()) {
+			mostRecent = candidateElement
 			mostRecentIndex = i
 			continue
 		}
@@ -68,19 +92,8 @@ func (s Splicer) microharvest() pub.Tangible {
 		return nil
 	}
 
-	if s[mostRecentIndex].page != nil {
-		var elements []pub.Tangible
-		elements, s[mostRecentIndex].page, s[mostRecentIndex].basepoint = s[mostRecentIndex].page.Harvest(1, s[mostRecentIndex].basepoint)
-		if len(elements) > 1 {
-			panic("harvest returned more that one element when I only asked for one")
-		} else if len(elements) == 0 {
-			s[mostRecentIndex].element = nil
-		} else {
-			s[mostRecentIndex].element = elements[0]
-		}
-	} else {
-		s[mostRecentIndex].element = nil
-	}
+	/* Shift (pop from front) the element that was selected */
+	s[mostRecentIndex].elements = s[mostRecentIndex].elements[1:]
 
 	return mostRecent
 }
@@ -94,29 +107,16 @@ func NewSplicer(inputs []string) *Splicer {
 		wg.Add(1)
 		go func() {
 			fetched := pub.FetchUserInput(input)
-			var children pub.Container
 			switch narrowed := fetched.(type) {
 			case pub.Tangible:
-				children = narrowed.Children()
+				s[i].page = narrowed.Children()
 			case *pub.Collection:
-				children = narrowed
+				s[i].page = narrowed
 			default:
 				panic("cannot splice non-Tangible, non-Collection")
 			}
-
-			if children != nil {
-				var elements []pub.Tangible
-				elements, s[i].page, s[i].basepoint = children.Harvest(1, 0)
-				if len(elements) > 1 {
-					panic("harvest returned more that one element when I only asked for one")
-				} else if len(elements) == 0 {
-					s[i].element = nil
-				} else {
-					s[i].element = elements[0]
-				}
-			} else {
-				s[i].element = nil
-			}
+			s[i].basepoint = 0
+			s[i].elements = []pub.Tangible{}
 			wg.Done()
 		}()
 	}