Tidy up node + context comparators a bit

This commit is contained in:
Luck 2021-01-14 20:45:51 +00:00
parent be1b9d45fa
commit 73230bc9b6
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
6 changed files with 88 additions and 80 deletions

View File

@ -25,14 +25,14 @@
package me.lucko.luckperms.common.context;
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
import net.luckperms.api.context.Context;
import net.luckperms.api.context.DefaultContextKeys;
import net.luckperms.api.context.ImmutableContextSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
public class ContextSetComparator implements Comparator<ImmutableContextSet> {
@ -53,40 +53,41 @@ public class ContextSetComparator implements Comparator<ImmutableContextSet> {
return 0;
}
// compare presence of a server context
int result = Boolean.compare(o1.containsKey(DefaultContextKeys.SERVER_KEY), o2.containsKey(DefaultContextKeys.SERVER_KEY));
if (result != 0) {
return result;
}
// compare presence of a world context
result = Boolean.compare(o1.containsKey(DefaultContextKeys.WORLD_KEY), o2.containsKey(DefaultContextKeys.WORLD_KEY));
if (result != 0) {
return result;
}
// compare overall size
result = Integer.compare(o1.size(), o2.size());
if (result != 0) {
return result;
}
// we *have* to maintain transitivity in this comparator. this may be expensive, but it's necessary, as this
// comparator is used in the PermissionHolder nodes treemap
// At this point, we don't really care about the order between the two sets.
// However, we *have* to maintain transitivity in this comparator (despite how
// expensive/complex it may be) as it is used in the PermissionHolder nodes treemap.
// in order to have consistent ordering, we have to compare the content of the context sets by ordering the
// elements and then comparing which set is greater.
List<Context> o1Entries = new ArrayList<>(o1.toSet());
List<Context> o2Entries = new ArrayList<>(o2.toSet());
o1Entries.sort(CONTEXT_COMPARATOR);
o2Entries.sort(CONTEXT_COMPARATOR);
// in order to have consistent ordering, we have to compare the content of the context sets
// by sorting the contents and then comparing which set is greater.
Context[] o1Array = o1 instanceof ImmutableContextSetImpl ? ((ImmutableContextSetImpl) o1).toArray() : o1.toSet().toArray(new Context[0]);
Context[] o2Array = o2 instanceof ImmutableContextSetImpl ? ((ImmutableContextSetImpl) o2).toArray() : o2.toSet().toArray(new Context[0]);
// size is definitely the same
Iterator<Context> it1 = o1Entries.iterator();
Iterator<Context> it2 = o2Entries.iterator();
Arrays.sort(o1Array, CONTEXT_COMPARATOR);
Arrays.sort(o2Array, CONTEXT_COMPARATOR);
while (it1.hasNext()) {
Context ent1 = it1.next();
Context ent2 = it2.next();
for (int i = 0; i < o1Array.length; i++) {
Context ent1 = o1Array[i];
Context ent2 = o2Array[i];
result = CONTEXT_COMPARATOR.compare(ent1, ent2);
result = compareContexts(ent1, ent2);
if (result != 0) {
return result;
}
@ -95,19 +96,23 @@ public class ContextSetComparator implements Comparator<ImmutableContextSet> {
throw new AssertionError("sets are equal? " + o1 + " - " + o2);
}
@SuppressWarnings("StringEquality")
private static final Comparator<String> FAST_STRING_COMPARATOR = (o1, o2) -> o1 == o2 ? 0 : o1.compareTo(o2);
private static final Comparator<Context> CONTEXT_COMPARATOR = ContextSetComparator::compareContexts;
private static final Comparator<Context> CONTEXT_COMPARATOR = (o1, o2) -> {
private static int compareContexts(Context o1, Context o2) {
if (o1 == o2) {
return 0;
}
int i = FAST_STRING_COMPARATOR.compare(o1.getKey(), o2.getKey());
int i = compareStringsFast(o1.getKey(), o2.getKey());
if (i != 0) {
return i;
}
return FAST_STRING_COMPARATOR.compare(o1.getValue(), o2.getValue());
};
return compareStringsFast(o1.getValue(), o2.getValue());
}
@SuppressWarnings("StringEquality")
private static int compareStringsFast(String o1, String o2) {
return o1 == o2 ? 0 : o1.compareTo(o2);
}
}

View File

@ -26,6 +26,7 @@
package me.lucko.luckperms.common.context.contextset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import com.google.common.collect.SetMultimap;
import net.luckperms.api.context.Context;
@ -35,9 +36,12 @@ import net.luckperms.api.context.DefaultContextKeys;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
public abstract class AbstractContextSet implements ContextSet {
@ -91,6 +95,18 @@ public abstract class AbstractContextSet implements ContextSet {
protected abstract boolean otherContainsAll(ContextSet other, ContextSatisfyMode mode);
public abstract Context[] toArray();
@Override
public @NonNull Iterator<Context> iterator() {
return Iterators.forArray(toArray());
}
@Override
public Spliterator<Context> spliterator() {
return Arrays.spliterator(toArray());
}
@Override
public boolean isEmpty() {
return backing().isEmpty();

View File

@ -25,7 +25,6 @@
package me.lucko.luckperms.common.context.contextset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
@ -42,11 +41,9 @@ import net.luckperms.api.context.MutableContextSet;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
public final class ImmutableContextSetImpl extends AbstractContextSet implements ImmutableContextSet {
public static final ImmutableContextSetImpl EMPTY = new ImmutableContextSetImpl(ImmutableSetMultimap.of());
@ -64,11 +61,19 @@ public final class ImmutableContextSetImpl extends AbstractContextSet implements
}
private final ImmutableSetMultimap<String, String> map;
private final Context[] array;
private final int hashCode;
ImmutableContextSetImpl(ImmutableSetMultimap<String, String> contexts) {
this.map = contexts;
this.hashCode = this.map.hashCode();
Set<Map.Entry<String, String>> entries = this.map.entries();
this.array = new Context[entries.size()];
int i = 0;
for (Map.Entry<String, String> e : entries) {
this.array[i++] = new ContextImpl(e.getKey(), e.getValue());
}
}
@Override
@ -99,12 +104,7 @@ public final class ImmutableContextSetImpl extends AbstractContextSet implements
@Override
public @NonNull Set<Context> toSet() {
ImmutableSet.Builder<Context> builder = ImmutableSet.builder();
Set<Map.Entry<String, String>> entries = this.map.entries();
for (Map.Entry<String, String> e : entries) {
builder.add(new ContextImpl(e.getKey(), e.getValue()));
}
return builder.build();
return ImmutableSet.copyOf(this.array);
}
@Override
@ -122,24 +122,9 @@ public final class ImmutableContextSetImpl extends AbstractContextSet implements
return m.build();
}
private ImmutableList<Context> toList() {
Set<Map.Entry<String, String>> entries = this.map.entries();
Context[] array = new Context[entries.size()];
int i = 0;
for (Map.Entry<String, String> e : entries) {
array[i++] = new ContextImpl(e.getKey(), e.getValue());
}
return ImmutableList.copyOf(array);
}
@Override
public @NonNull Iterator<Context> iterator() {
return toList().iterator();
}
@Override
public Spliterator<Context> spliterator() {
return toList().spliterator();
public Context[] toArray() {
return this.array;
}
@Override

View File

@ -26,7 +26,6 @@
package me.lucko.luckperms.common.context.contextset;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
@ -43,11 +42,9 @@ import net.luckperms.api.context.MutableContextSet;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
public final class MutableContextSetImpl extends AbstractContextSet implements MutableContextSet {
private final SetMultimap<String, String> map;
@ -132,7 +129,8 @@ public final class MutableContextSetImpl extends AbstractContextSet implements M
return builder.build();
}
private ImmutableList<Context> toList() {
@Override
public Context[] toArray() {
Set<Map.Entry<String, String>> entries = this.map.entries();
Context[] array;
synchronized (this.map) {
@ -142,17 +140,7 @@ public final class MutableContextSetImpl extends AbstractContextSet implements M
array[i++] = new ContextImpl(e.getKey(), e.getValue());
}
}
return ImmutableList.copyOf(array);
}
@Override
public @NonNull Iterator<Context> iterator() {
return toList().iterator();
}
@Override
public Spliterator<Context> spliterator() {
return toList().spliterator();
return array;
}
@Override

View File

@ -31,7 +31,6 @@ import net.luckperms.api.node.types.PermissionNode;
import java.util.Comparator;
public class NodeComparator implements Comparator<Node> {
private static final Comparator<? super Node> INSTANCE = new NodeComparator();
private static final Comparator<? super Node> REVERSE = INSTANCE.reversed();
@ -43,17 +42,20 @@ public class NodeComparator implements Comparator<Node> {
return REVERSE;
}
@SuppressWarnings({"ConstantConditions", "OptionalGetWithoutIsPresent"})
@Override
public int compare(Node o1, Node o2) {
if (o1.equals(o2)) {
return 0;
}
// compare whether nodes are temporary
int result = Boolean.compare(o1.hasExpiry(), o2.hasExpiry());
if (result != 0) {
return result;
}
// compare whether nodes are wildcard nodes
result = Boolean.compare(
o1 instanceof PermissionNode && ((PermissionNode) o1).isWildcard(),
o2 instanceof PermissionNode && ((PermissionNode) o2).isWildcard()
@ -62,6 +64,8 @@ public class NodeComparator implements Comparator<Node> {
return result;
}
// compare expiry times if both nodes are temporary
// due to the comparison earlier, either both nodes are temporary or neither are.
if (o1.hasExpiry()) {
result = o1.getExpiry().compareTo(o2.getExpiry());
if (result != 0) {
@ -69,20 +73,27 @@ public class NodeComparator implements Comparator<Node> {
}
}
if (o1 instanceof PermissionNode && ((PermissionNode) o1).isWildcard()) {
result = Integer.compare(((PermissionNode) o1).getWildcardLevel().getAsInt(), ((PermissionNode) o2).getWildcardLevel().getAsInt());
// compare wildcard level if both nodes are wildcards
// due to the comparison earlier, either both nodes are wildcards or neither are
if (o1 instanceof PermissionNode && ((PermissionNode) o1).isWildcard()) { // implies o2.isWildcard too
int o1Level = ((PermissionNode) o1).getWildcardLevel().getAsInt();
int o2Level = ((PermissionNode) o2).getWildcardLevel().getAsInt();
result = Integer.compare(o1Level, o2Level);
if (result != 0) {
return result;
}
}
// note vvv
// compare node keys lexicographically
// note that the order is reversed - A comes before Z
result = -o1.getKey().compareTo(o2.getKey());
if (result != 0) {
return result;
}
// note vvv - we want false to have priority
// compare the boolean node values
// note that the order is reversed, false comes before true
result = -Boolean.compare(o1.getValue(), o2.getValue());
if (result != 0) {
return result;

View File

@ -27,6 +27,7 @@ package me.lucko.luckperms.common.node.comparator;
import me.lucko.luckperms.common.context.ContextSetComparator;
import net.luckperms.api.context.ImmutableContextSet;
import net.luckperms.api.node.Node;
import java.util.Comparator;
@ -35,10 +36,8 @@ import java.util.Comparator;
* Compares permission nodes based upon their supposed "priority".
*/
public class NodeWithContextComparator implements Comparator<Node> {
private NodeWithContextComparator() {}
private static final Comparator<? super Node> INSTANCE = new NodeWithContextComparator();
private static final Comparator<? super Node> REVERSE = INSTANCE.reversed();
private static final Comparator<? super Node> INSTANCE = new NodeWithContextComparator(ContextSetComparator.normal(), NodeComparator.normal());
private static final Comparator<? super Node> REVERSE = new NodeWithContextComparator(ContextSetComparator.reverse(), NodeComparator.reverse());
public static Comparator<? super Node> normal() {
return INSTANCE;
@ -48,22 +47,26 @@ public class NodeWithContextComparator implements Comparator<Node> {
return REVERSE;
}
private final Comparator<? super ImmutableContextSet> contextSetComparator;
private final Comparator<? super Node> nodeComparator;
NodeWithContextComparator(Comparator<? super ImmutableContextSet> contextSetComparator, Comparator<? super Node> nodeComparator) {
this.contextSetComparator = contextSetComparator;
this.nodeComparator = nodeComparator;
}
@Override
public int compare(Node o1, Node o2) {
if (o1.equals(o2)) {
return 0;
}
int result = ContextSetComparator.normal().compare(
o1.getContexts(),
o2.getContexts()
);
int result = this.contextSetComparator.compare(o1.getContexts(), o2.getContexts());
if (result != 0) {
return result;
}
return NodeComparator.normal().compare(o1, o2);
return this.nodeComparator.compare(o1, o2);
}
}