package com.github.davidmoten.rtree.fbs; import static com.github.davidmoten.rtree.fbs.FlatBuffersHelper.createBox; import static com.github.davidmoten.rtree.fbs.FlatBuffersHelper.parseObject; import static com.github.davidmoten.rtree.fbs.FlatBuffersHelper.toGeometry; import java.util.ArrayList; import java.util.List; import com.github.davidmoten.guavamini.Preconditions; import com.github.davidmoten.rtree.Context; import com.github.davidmoten.rtree.Entries; import com.github.davidmoten.rtree.Entry; import com.github.davidmoten.rtree.Node; import com.github.davidmoten.rtree.NonLeaf; import com.github.davidmoten.rtree.fbs.generated.Box_; import com.github.davidmoten.rtree.fbs.generated.Entry_; import com.github.davidmoten.rtree.fbs.generated.Geometry_; import com.github.davidmoten.rtree.fbs.generated.Node_; import com.github.davidmoten.rtree.geometry.Geometries; import com.github.davidmoten.rtree.geometry.Geometry; import com.github.davidmoten.rtree.internal.NodeAndEntries; import com.github.davidmoten.rtree.internal.NonLeafHelper; import rx.Subscriber; import rx.functions.Func1; final class NonLeafFlatBuffers<T, S extends Geometry> implements NonLeaf<T, S> { private final Node_ node; private final Context<T, S> context; private final Func1<byte[], ? extends T> deserializer; NonLeafFlatBuffers(Node_ node, Context<T, S> context, Func1<byte[], ? extends T> deserializer) { Preconditions.checkNotNull(node); // remove precondition because reduces performance // Preconditions.checkArgument(node.childrenLength() > 0); this.node = node; this.context = context; this.deserializer = deserializer; } @Override public List<Node<T, S>> add(Entry<? extends T, ? extends S> entry) { return NonLeafHelper.add(entry, this); } @Override public NodeAndEntries<T, S> delete(Entry<? extends T, ? extends S> entry, boolean all) { return NonLeafHelper.delete(entry, all, this); } @Override public void searchWithoutBackpressure(Func1<? super Geometry, Boolean> criterion, Subscriber<? super Entry<T, S>> subscriber) { // pass through entry and geometry and box instances to be reused for // flatbuffers extraction this reduces allocation/gc costs (but of // course introduces some mutable ugliness into the codebase) searchWithoutBackpressure(node, criterion, subscriber, deserializer, new Entry_(), new Geometry_(), new Box_()); } @SuppressWarnings("unchecked") private static <T, S extends Geometry> void searchWithoutBackpressure(Node_ node, Func1<? super Geometry, Boolean> criterion, Subscriber<? super Entry<T, S>> subscriber, Func1<byte[], ? extends T> deserializer, Entry_ entry, Geometry_ geometry, Box_ box) { { node.mbb(box); if (!criterion .call(Geometries.rectangle(box.minX(), box.minY(), box.maxX(), box.maxY()))) return; } int numChildren = node.childrenLength(); // reduce allocations by reusing objects Node_ child = new Node_(); if (numChildren > 0) { for (int i = 0; i < numChildren; i++) { if (subscriber.isUnsubscribed()) return; node.children(child, i); searchWithoutBackpressure(child, criterion, subscriber, deserializer, entry, geometry, box); } } else { int numEntries = node.entriesLength(); // reduce allocations by reusing objects // check all entries for (int i = 0; i < numEntries; i++) { if (subscriber.isUnsubscribed()) return; // set entry node.entries(entry, i); // set geometry entry.geometry(geometry); final Geometry g = toGeometry(geometry); if (criterion.call(g)) { T t = parseObject(deserializer, entry); Entry<T, S> ent = Entries.entry(t, (S) g); subscriber.onNext(ent); } } } } private List<Node<T, S>> createChildren() { // reduce allocations by resusing objects int numChildren = node.childrenLength(); List<Node<T, S>> children = new ArrayList<Node<T, S>>(numChildren); for (int i = 0; i < numChildren; i++) { Node_ child = node.children(i); if (child.childrenLength() > 0) { children.add(new NonLeafFlatBuffers<T, S>(child, context, deserializer)); } else { children.add(new LeafFlatBuffers<T, S>(child, context, deserializer)); } } return children; } @Override public int count() { return node.childrenLength(); } @Override public Context<T, S> context() { return context; } @Override public Geometry geometry() { return FlatBuffersHelper.createBox(node.mbb()); } @Override public Node<T, S> child(int i) { Node_ child = node.children(i); if (child.childrenLength() > 0) return new NonLeafFlatBuffers<T, S>(child, context, deserializer); else return new LeafFlatBuffers<T, S>(child, context, deserializer); } @Override public List<Node<T, S>> children() { return createChildren(); } @Override public String toString() { return "Node [" + (node.childrenLength() > 0 ? "NonLeaf" : "Leaf") + "," + createBox(node.mbb()).toString() + "]"; } }