mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 17:02:25 -05:00 
			
		
		
		
	* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
		
			
				
	
	
		
			498 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			498 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2020 Google Inc. All rights reserved.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License");
 | |
| // you may not use this file except in compliance with the License.
 | |
| // You may obtain a copy of the License at
 | |
| //
 | |
| //     http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| // Unless required by applicable law or agreed to in writing, software
 | |
| // distributed under the License is distributed on an "AS IS" BASIS,
 | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| // See the License for the specific language governing permissions and
 | |
| // limitations under the License.
 | |
| 
 | |
| package s2
 | |
| 
 | |
| import (
 | |
| 	"sort"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	// A special label indicating that the ContentsIterator done is true.
 | |
| 	cellIndexDoneContents = -1
 | |
| )
 | |
| 
 | |
| // cellIndexNode represents a node in the CellIndex. Cells are organized in a
 | |
| // tree such that the ancestors of a given node contain that node.
 | |
| type cellIndexNode struct {
 | |
| 	cellID CellID
 | |
| 	label  int32
 | |
| 	parent int32
 | |
| }
 | |
| 
 | |
| // newCellIndexNode returns a node with the appropriate default values.
 | |
| func newCellIndexNode() cellIndexNode {
 | |
| 	return cellIndexNode{
 | |
| 		cellID: 0,
 | |
| 		label:  cellIndexDoneContents,
 | |
| 		parent: -1,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // A rangeNode represents a range of leaf CellIDs. The range starts at
 | |
| // startID (a leaf cell) and ends at the startID field of the next
 | |
| // rangeNode. contents points to the node of the CellIndex cellTree
 | |
| // representing the cells that overlap this range.
 | |
| type rangeNode struct {
 | |
| 	startID  CellID // First leaf cell contained by this range.
 | |
| 	contents int32  // Contents of this node (an index within the cell tree).
 | |
| }
 | |
| 
 | |
| // CellIndexIterator is an iterator that visits the entire set of indexed
 | |
| // (CellID, label) pairs in an unspecified order.
 | |
| type CellIndexIterator struct {
 | |
| 	// TODO(roberts): Implement
 | |
| }
 | |
| 
 | |
| // NewCellIndexIterator creates an iterator for the given CellIndex.
 | |
| func NewCellIndexIterator(index *CellIndex) *CellIndexIterator {
 | |
| 	return &CellIndexIterator{}
 | |
| }
 | |
| 
 | |
| // CellIndexRangeIterator is an iterator that seeks and iterates over a set of
 | |
| // non-overlapping leaf cell ranges that cover the entire sphere. The indexed
 | |
| // (CellID, label) pairs that intersect the current leaf cell range can be
 | |
| // visited using CellIndexContentsIterator (see below).
 | |
| type CellIndexRangeIterator struct {
 | |
| 	rangeNodes []rangeNode
 | |
| 	pos        int
 | |
| 	nonEmpty   bool
 | |
| }
 | |
| 
 | |
| // NewCellIndexRangeIterator creates an iterator for the given CellIndex.
 | |
| // The iterator is initially *unpositioned*; you must call a positioning method
 | |
| // such as Begin() or Seek() before accessing its contents.
 | |
| func NewCellIndexRangeIterator(index *CellIndex) *CellIndexRangeIterator {
 | |
| 	return &CellIndexRangeIterator{
 | |
| 		rangeNodes: index.rangeNodes,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewCellIndexNonEmptyRangeIterator creates an iterator for the given CellIndex.
 | |
| // The iterator is initially *unpositioned*; you must call a positioning method such as
 | |
| // Begin() or Seek() before accessing its contents.
 | |
| func NewCellIndexNonEmptyRangeIterator(index *CellIndex) *CellIndexRangeIterator {
 | |
| 	return &CellIndexRangeIterator{
 | |
| 		rangeNodes: index.rangeNodes,
 | |
| 		nonEmpty:   true,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // StartID reports the CellID of the start of the current range of leaf CellIDs.
 | |
| //
 | |
| // If done is true, this returns the last possible CellID. This property means
 | |
| // that most loops do not need to test done explicitly.
 | |
| func (c *CellIndexRangeIterator) StartID() CellID {
 | |
| 	return c.rangeNodes[c.pos].startID
 | |
| }
 | |
| 
 | |
| // LimitID reports the non-inclusive end of the current range of leaf CellIDs.
 | |
| //
 | |
| // This assumes the iterator is not done.
 | |
| func (c *CellIndexRangeIterator) LimitID() CellID {
 | |
| 	return c.rangeNodes[c.pos+1].startID
 | |
| }
 | |
| 
 | |
| // IsEmpty reports if no (CellID, label) pairs intersect this range.
 | |
| // Also returns true if done() is true.
 | |
| func (c *CellIndexRangeIterator) IsEmpty() bool {
 | |
| 	return c.rangeNodes[c.pos].contents == cellIndexDoneContents
 | |
| }
 | |
| 
 | |
| // Begin positions the iterator at the first range of leaf cells (if any).
 | |
| func (c *CellIndexRangeIterator) Begin() {
 | |
| 	c.pos = 0
 | |
| 	for c.nonEmpty && c.IsEmpty() && !c.Done() {
 | |
| 		c.pos++
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Prev positions the iterator at the previous entry and reports whether it was not
 | |
| // already positioned at the beginning.
 | |
| func (c *CellIndexRangeIterator) Prev() bool {
 | |
| 	if c.nonEmpty {
 | |
| 		return c.nonEmptyPrev()
 | |
| 	}
 | |
| 	return c.prev()
 | |
| }
 | |
| 
 | |
| // prev is used to position the iterator at the previous entry without checking
 | |
| // if nonEmpty is true to prevent unwanted recursion.
 | |
| func (c *CellIndexRangeIterator) prev() bool {
 | |
| 	if c.pos == 0 {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	c.pos--
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // Prev positions the iterator at the previous entry, and reports whether it was
 | |
| // already positioned at the beginning.
 | |
| func (c *CellIndexRangeIterator) nonEmptyPrev() bool {
 | |
| 	for c.prev() {
 | |
| 		if !c.IsEmpty() {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Return the iterator to its original position.
 | |
| 	if c.IsEmpty() && !c.Done() {
 | |
| 		c.Next()
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| // Next advances the iterator to the next range of leaf cells.
 | |
| //
 | |
| // This assumes the iterator is not done.
 | |
| func (c *CellIndexRangeIterator) Next() {
 | |
| 	c.pos++
 | |
| 	for c.nonEmpty && c.IsEmpty() && !c.Done() {
 | |
| 		c.pos++
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Advance reports if advancing would leave it positioned on a valid range. If
 | |
| // the value would not be valid, the positioning is not changed.
 | |
| func (c *CellIndexRangeIterator) Advance(n int) bool {
 | |
| 	// Note that the last element of rangeNodes is a sentinel value.
 | |
| 	if n >= len(c.rangeNodes)-1-c.pos {
 | |
| 		return false
 | |
| 	}
 | |
| 	c.pos += n
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // Finish positions the iterator so that done is true.
 | |
| func (c *CellIndexRangeIterator) Finish() {
 | |
| 	// Note that the last element of rangeNodes is a sentinel value.
 | |
| 	c.pos = len(c.rangeNodes) - 1
 | |
| }
 | |
| 
 | |
| // Done reports if the iterator is positioned beyond the last valid range.
 | |
| func (c *CellIndexRangeIterator) Done() bool {
 | |
| 	return c.pos >= len(c.rangeNodes)-1
 | |
| }
 | |
| 
 | |
| // Seek positions the iterator at the first range with startID >= target.
 | |
| // Such an entry always exists as long as "target" is a valid leaf cell.
 | |
| //
 | |
| // Note that it is valid to access startID even when done is true.
 | |
| func (c *CellIndexRangeIterator) Seek(target CellID) {
 | |
| 	c.pos = sort.Search(len(c.rangeNodes), func(i int) bool {
 | |
| 		return c.rangeNodes[i].startID > target
 | |
| 	}) - 1
 | |
| 
 | |
| 	// Ensure we don't go beyond the beginning.
 | |
| 	if c.pos < 0 {
 | |
| 		c.pos = 0
 | |
| 	}
 | |
| 
 | |
| 	// Nonempty needs to find the next non-empty entry.
 | |
| 	for c.nonEmpty && c.IsEmpty() && !c.Done() {
 | |
| 		// c.Next()
 | |
| 		c.pos++
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // CellIndexContentsIterator is an iterator that visits the (CellID, label) pairs
 | |
| // that cover a set of leaf cell ranges (see CellIndexRangeIterator). Note that
 | |
| // when multiple leaf cell ranges are visited, this iterator only guarantees that
 | |
| // each result will be reported at least once, i.e. duplicate values may be
 | |
| // suppressed. If you want duplicate values to be reported again, be sure to call
 | |
| // Clear first.
 | |
| //
 | |
| // In particular, the implementation guarantees that when multiple leaf
 | |
| // cell ranges are visited in monotonically increasing order, then each
 | |
| // (CellID, label) pair is reported exactly once.
 | |
| type CellIndexContentsIterator struct {
 | |
| 	// The maximum index within the cellTree slice visited during the
 | |
| 	// previous call to StartUnion. This is used to eliminate duplicate
 | |
| 	// values when StartUnion is called multiple times.
 | |
| 	nodeCutoff int32
 | |
| 
 | |
| 	// The maximum index within the cellTree visited during the
 | |
| 	// current call to StartUnion. This is used to update nodeCutoff.
 | |
| 	nextNodeCutoff int32
 | |
| 
 | |
| 	// The value of startID from the previous call to StartUnion.
 | |
| 	// This is used to check whether these values are monotonically
 | |
| 	// increasing.
 | |
| 	prevStartID CellID
 | |
| 
 | |
| 	// The cell tree from CellIndex
 | |
| 	cellTree []cellIndexNode
 | |
| 
 | |
| 	// A copy of the current node in the cell tree.
 | |
| 	node cellIndexNode
 | |
| }
 | |
| 
 | |
| // NewCellIndexContentsIterator returns a new contents iterator.
 | |
| //
 | |
| // Note that the iterator needs to be positioned using StartUnion before
 | |
| // it can be safely used.
 | |
| func NewCellIndexContentsIterator(index *CellIndex) *CellIndexContentsIterator {
 | |
| 	it := &CellIndexContentsIterator{
 | |
| 		cellTree:       index.cellTree,
 | |
| 		prevStartID:    0,
 | |
| 		nodeCutoff:     -1,
 | |
| 		nextNodeCutoff: -1,
 | |
| 		node:           cellIndexNode{label: cellIndexDoneContents},
 | |
| 	}
 | |
| 	return it
 | |
| }
 | |
| 
 | |
| // Clear clears all state with respect to which range(s) have been visited.
 | |
| func (c *CellIndexContentsIterator) Clear() {
 | |
| 	c.prevStartID = 0
 | |
| 	c.nodeCutoff = -1
 | |
| 	c.nextNodeCutoff = -1
 | |
| 	c.node.label = cellIndexDoneContents
 | |
| }
 | |
| 
 | |
| // CellID returns the current CellID.
 | |
| func (c *CellIndexContentsIterator) CellID() CellID {
 | |
| 	return c.node.cellID
 | |
| }
 | |
| 
 | |
| // Label returns the current Label.
 | |
| func (c *CellIndexContentsIterator) Label() int32 {
 | |
| 	return c.node.label
 | |
| }
 | |
| 
 | |
| // Next advances the iterator to the next (CellID, label) pair covered by the
 | |
| // current leaf cell range.
 | |
| //
 | |
| // This requires the iterator to not be done.
 | |
| func (c *CellIndexContentsIterator) Next() {
 | |
| 	if c.node.parent <= c.nodeCutoff {
 | |
| 		// We have already processed this node and its ancestors.
 | |
| 		c.nodeCutoff = c.nextNodeCutoff
 | |
| 		c.node.label = cellIndexDoneContents
 | |
| 	} else {
 | |
| 		c.node = c.cellTree[c.node.parent]
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Done reports if all (CellID, label) pairs have been visited.
 | |
| func (c *CellIndexContentsIterator) Done() bool {
 | |
| 	return c.node.label == cellIndexDoneContents
 | |
| }
 | |
| 
 | |
| // StartUnion positions the ContentsIterator at the first (cell_id, label) pair
 | |
| // that covers the given leaf cell range. Note that when multiple leaf cell
 | |
| // ranges are visited using the same ContentsIterator, duplicate values
 | |
| // may be suppressed. If you don't want this behavior, call Reset() first.
 | |
| func (c *CellIndexContentsIterator) StartUnion(r *CellIndexRangeIterator) {
 | |
| 	if r.StartID() < c.prevStartID {
 | |
| 		c.nodeCutoff = -1 // Can't automatically eliminate duplicates.
 | |
| 	}
 | |
| 	c.prevStartID = r.StartID()
 | |
| 
 | |
| 	contents := r.rangeNodes[r.pos].contents
 | |
| 	if contents <= c.nodeCutoff {
 | |
| 		c.node.label = cellIndexDoneContents
 | |
| 	} else {
 | |
| 		c.node = c.cellTree[contents]
 | |
| 	}
 | |
| 
 | |
| 	// When visiting ancestors, we can stop as soon as the node index is smaller
 | |
| 	// than any previously visited node index. Because indexes are assigned
 | |
| 	// using a preorder traversal, such nodes are guaranteed to have already
 | |
| 	// been reported.
 | |
| 	c.nextNodeCutoff = contents
 | |
| }
 | |
| 
 | |
| // CellIndex stores a collection of (CellID, label) pairs.
 | |
| //
 | |
| // The CellIDs may be overlapping or contain duplicate values. For example, a
 | |
| // CellIndex could store a collection of CellUnions, where each CellUnion
 | |
| // gets its own non-negative int32 label.
 | |
| //
 | |
| // Similar to ShapeIndex and PointIndex which map each stored element to an
 | |
| // identifier, CellIndex stores a label that is typically used to map the
 | |
| // results of queries back to client's specific data.
 | |
| //
 | |
| // The zero value for a CellIndex is sufficient when constructing a CellIndex.
 | |
| //
 | |
| // To build a CellIndex where each Cell has a distinct label, call Add for each
 | |
| // (CellID, label) pair, and then Build the index. For example:
 | |
| //
 | |
| //	// contents is a mapping of an identifier in my system (restaurantID,
 | |
| //	// vehicleID, etc) to a CellID
 | |
| //	var contents = map[int32]CellID{...}
 | |
| //
 | |
| //	for key, val := range contents {
 | |
| //		index.Add(val, key)
 | |
| //	}
 | |
| //
 | |
| //	index.Build()
 | |
| //
 | |
| // There is also a helper method that adds all elements of CellUnion with the
 | |
| // same label:
 | |
| //
 | |
| //     index.AddCellUnion(cellUnion, label)
 | |
| //
 | |
| // Note that the index is not dynamic; the contents of the index cannot be
 | |
| // changed once it has been built. Adding more after calling Build results in
 | |
| // undefined behavior of the index.
 | |
| //
 | |
| // There are several options for retrieving data from the index. The simplest
 | |
| // is to use a built-in method such as IntersectingLabels (which returns
 | |
| // the labels of all cells that intersect a given target CellUnion):
 | |
| //
 | |
| //   labels := index.IntersectingLabels(targetUnion);
 | |
| //
 | |
| // Alternatively, you can use a ClosestCellQuery which computes the cell(s)
 | |
| // that are closest to a given target geometry.
 | |
| //
 | |
| // For example, here is how to find all cells that are closer than
 | |
| // distanceLimit to a given target point:
 | |
| //
 | |
| //	query := NewClosestCellQuery(cellIndex, opts)
 | |
| //	target := NewMinDistanceToPointTarget(targetPoint);
 | |
| //	for result := range query.FindCells(target) {
 | |
| //		// result.Distance() is the distance to the target.
 | |
| //		// result.CellID() is the indexed CellID.
 | |
| //		// result.Label() is the label associated with the CellID.
 | |
| //		DoSomething(targetPoint, result);
 | |
| //	}
 | |
| //
 | |
| // Internally, the index consists of a set of non-overlapping leaf cell ranges
 | |
| // that subdivide the sphere and such that each range intersects a particular
 | |
| // set of (cellID, label) pairs.
 | |
| //
 | |
| // Most clients should use either the methods such as VisitIntersectingCells
 | |
| // and IntersectingLabels, or a helper such as ClosestCellQuery.
 | |
| type CellIndex struct {
 | |
| 	// A tree of (cellID, label) pairs such that if X is an ancestor of Y, then
 | |
| 	// X.cellID contains Y.cellID. The contents of a given range of leaf
 | |
| 	// cells can be represented by pointing to a node of this tree.
 | |
| 	cellTree []cellIndexNode
 | |
| 
 | |
| 	// The last element of rangeNodes is a sentinel value, which is necessary
 | |
| 	// in order to represent the range covered by the previous element.
 | |
| 	rangeNodes []rangeNode
 | |
| }
 | |
| 
 | |
| // Add adds the given CellID and Label to the index.
 | |
| func (c *CellIndex) Add(id CellID, label int32) {
 | |
| 	if label < 0 {
 | |
| 		panic("labels must be non-negative")
 | |
| 	}
 | |
| 	c.cellTree = append(c.cellTree, cellIndexNode{cellID: id, label: label, parent: -1})
 | |
| }
 | |
| 
 | |
| // AddCellUnion adds all of the elements of the given CellUnion to the index with the same label.
 | |
| func (c *CellIndex) AddCellUnion(cu CellUnion, label int32) {
 | |
| 	if label < 0 {
 | |
| 		panic("labels must be non-negative")
 | |
| 	}
 | |
| 	for _, cell := range cu {
 | |
| 		c.Add(cell, label)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Build builds the index for use. This method should only be called once.
 | |
| func (c *CellIndex) Build() {
 | |
| 	// To build the cell tree and leaf cell ranges, we maintain a stack of
 | |
| 	// (CellID, label) pairs that contain the current leaf cell. This struct
 | |
| 	// represents an instruction to push or pop a (cellID, label) pair.
 | |
| 	//
 | |
| 	// If label >= 0, the (cellID, label) pair is pushed on the stack.
 | |
| 	// If CellID == SentinelCellID, a pair is popped from the stack.
 | |
| 	// Otherwise the stack is unchanged but a rangeNode is still emitted.
 | |
| 
 | |
| 	// delta represents an entry in a stack of (CellID, label) pairs used in the
 | |
| 	// construction of the CellIndex structure.
 | |
| 	type delta struct {
 | |
| 		startID CellID
 | |
| 		cellID  CellID
 | |
| 		label   int32
 | |
| 	}
 | |
| 
 | |
| 	deltas := make([]delta, 0, 2*len(c.cellTree)+2)
 | |
| 
 | |
| 	// Create two deltas for each (cellID, label) pair: one to add the pair to
 | |
| 	// the stack (at the start of its leaf cell range), and one to remove it from
 | |
| 	// the stack (at the end of its leaf cell range).
 | |
| 	for _, node := range c.cellTree {
 | |
| 		deltas = append(deltas, delta{
 | |
| 			startID: node.cellID.RangeMin(),
 | |
| 			cellID:  node.cellID,
 | |
| 			label:   node.label,
 | |
| 		})
 | |
| 		deltas = append(deltas, delta{
 | |
| 			startID: node.cellID.RangeMax().Next(),
 | |
| 			cellID:  SentinelCellID,
 | |
| 			label:   -1,
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	// We also create two special deltas to ensure that a RangeNode is emitted at
 | |
| 	// the beginning and end of the CellID range.
 | |
| 	deltas = append(deltas, delta{
 | |
| 		startID: CellIDFromFace(0).ChildBeginAtLevel(maxLevel),
 | |
| 		cellID:  CellID(0),
 | |
| 		label:   -1,
 | |
| 	})
 | |
| 	deltas = append(deltas, delta{
 | |
| 		startID: CellIDFromFace(5).ChildEndAtLevel(maxLevel),
 | |
| 		cellID:  CellID(0),
 | |
| 		label:   -1,
 | |
| 	})
 | |
| 
 | |
| 	sort.Slice(deltas, func(i, j int) bool {
 | |
| 		// deltas are sorted first by startID, then in reverse order by cellID,
 | |
| 		// and then by label. This is necessary to ensure that (1) larger cells
 | |
| 		// are pushed on the stack before smaller cells, and (2) cells are popped
 | |
| 		// off the stack before any new cells are added.
 | |
| 
 | |
| 		if si, sj := deltas[i].startID, deltas[j].startID; si != sj {
 | |
| 			return si < sj
 | |
| 		}
 | |
| 		if si, sj := deltas[i].cellID, deltas[j].cellID; si != sj {
 | |
| 			return si > sj
 | |
| 		}
 | |
| 		return deltas[i].label < deltas[j].label
 | |
| 	})
 | |
| 
 | |
| 	// Now walk through the deltas to build the leaf cell ranges and cell tree
 | |
| 	// (which is essentially a permanent form of the "stack" described above).
 | |
| 	c.cellTree = nil
 | |
| 	c.rangeNodes = nil
 | |
| 	contents := int32(-1)
 | |
| 	for i := 0; i < len(deltas); {
 | |
| 		startID := deltas[i].startID
 | |
| 		// Process all the deltas associated with the current startID.
 | |
| 		for ; i < len(deltas) && deltas[i].startID == startID; i++ {
 | |
| 			if deltas[i].label >= 0 {
 | |
| 				c.cellTree = append(c.cellTree, cellIndexNode{
 | |
| 					cellID: deltas[i].cellID,
 | |
| 					label:  deltas[i].label,
 | |
| 					parent: contents})
 | |
| 				contents = int32(len(c.cellTree) - 1)
 | |
| 			} else if deltas[i].cellID == SentinelCellID {
 | |
| 				contents = c.cellTree[contents].parent
 | |
| 			}
 | |
| 		}
 | |
| 		c.rangeNodes = append(c.rangeNodes, rangeNode{startID, contents})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // TODO(roberts): Differences from C++
 | |
| // IntersectingLabels
 | |
| // VisitIntersectingCells
 | |
| // CellIndexIterator
 |