Use UnsignedLongSet instead of RangeSet in metadata
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / utils / UnsignedLongSet.java
index ac599a66240a3469323a6df3925ab227b323f9d4..4d4b99bbc9bd1fbc6d21a90e56aa41c2b4982382 100644 (file)
@@ -11,17 +11,23 @@ import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.BoundType;
 import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableRangeSet;
 import com.google.common.collect.Range;
 import com.google.common.collect.RangeSet;
 import com.google.common.primitives.UnsignedLong;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Collections;
 import java.util.Iterator;
+import java.util.NavigableSet;
 import java.util.TreeSet;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.concepts.Mutable;
+import org.opendaylight.yangtools.concepts.WritableObjects;
 
 /**
  * A class holding an equivalent of {@code Set<UnsignedLong>}. It is geared towards efficiently tracking ranges of
@@ -33,9 +39,10 @@ import org.opendaylight.yangtools.concepts.Mutable;
  *
  * @author Robert Varga
  */
-@Beta
-public final class UnsignedLongSet implements Mutable {
-    private static final class Entry implements Comparable<Entry>, Mutable {
+abstract class UnsignedLongSet {
+    @Beta
+    @VisibleForTesting
+    public static final class Entry implements Comparable<Entry>, Mutable {
         // Note: mutable to allow efficient merges.
         long lowerBits;
         long upperBits;
@@ -59,6 +66,16 @@ public final class UnsignedLongSet implements Mutable {
             return of(range.lowerEndpoint().longValue(), range.upperEndpoint().longValue() - 1);
         }
 
+        @VisibleForTesting
+        public UnsignedLong lower() {
+            return UnsignedLong.fromLongBits(lowerBits);
+        }
+
+        @VisibleForTesting
+        public UnsignedLong upper() {
+            return UnsignedLong.fromLongBits(upperBits);
+        }
+
         boolean contains(final long longBits) {
             return Long.compareUnsigned(lowerBits, longBits) <= 0 && Long.compareUnsigned(upperBits, longBits) >= 0;
         }
@@ -72,6 +89,24 @@ public final class UnsignedLongSet implements Mutable {
             return Range.closedOpen(UnsignedLong.fromLongBits(lowerBits), UnsignedLong.fromLongBits(upperBits + 1));
         }
 
+        // These two methods provide the same serialization format as the one we've used to serialize
+        // Range<UnsignedLong>
+        static @NonNull Entry readUnsigned(final DataInput in) throws IOException {
+            final byte hdr = WritableObjects.readLongHeader(in);
+            final long first = WritableObjects.readFirstLong(in, hdr);
+            final long second = WritableObjects.readSecondLong(in, hdr) - 1;
+            if (Long.compareUnsigned(first, second) > 0) {
+                throw new IOException("Lower endpoint " + Long.toUnsignedString(first) + " is greater than upper "
+                    + "endpoint " + Long.toUnsignedString(second));
+            }
+
+            return new Entry(first, second);
+        }
+
+        void writeUnsigned(final @NonNull DataOutput out) throws IOException {
+            WritableObjects.writeLongs(out, lowerBits, upperBits + 1);
+        }
+
         @Override
         @SuppressWarnings("checkstyle:parameterName")
         public int compareTo(final Entry o) {
@@ -105,28 +140,13 @@ public final class UnsignedLongSet implements Mutable {
     // for a contains() operation we just need the first headSet() entry. For insert operations we just update either
     // the lower bound or the upper bound of an existing entry. When we do, we also look at prev/next entry and if they
     // are contiguous with the updated entry, we adjust the entry once more and remove the prev/next entry.
-    private final TreeSet<Entry> ranges;
+    private final @NonNull TreeSet<Entry> ranges;
 
-    private UnsignedLongSet(final TreeSet<Entry> ranges) {
+    UnsignedLongSet(final TreeSet<Entry> ranges) {
         this.ranges = requireNonNull(ranges);
     }
 
-    private UnsignedLongSet(final RangeSet<UnsignedLong> rangeSet) {
-        ranges = new TreeSet<>();
-        for (var range : rangeSet.asRanges()) {
-            ranges.add(Entry.of(range));
-        }
-    }
-
-    public static @NonNull UnsignedLongSet of() {
-        return new UnsignedLongSet(new TreeSet<>());
-    }
-
-    public static @NonNull UnsignedLongSet of(final RangeSet<UnsignedLong> rangeSet) {
-        return new UnsignedLongSet(rangeSet);
-    }
-
-    public void add(final long longBits) {
+    final void addImpl(final long longBits) {
         final var range = Entry.of(longBits);
 
         final var headIt = headIter(range);
@@ -161,31 +181,49 @@ public final class UnsignedLongSet implements Mutable {
         ranges.add(range);
     }
 
-    public boolean contains(final long longBits) {
+    public final boolean contains(final long longBits) {
         final var headIt = headIter(Entry.of(longBits));
         return headIt.hasNext() && headIt.next().contains(longBits);
     }
 
-    public UnsignedLongSet copy() {
-        return new UnsignedLongSet(new TreeSet<>(Collections2.transform(ranges, Entry::copy)));
+    public final boolean isEmpty() {
+        return ranges.isEmpty();
+    }
+
+    public final int size() {
+        return ranges.size();
+    }
+
+    public abstract @NonNull ImmutableUnsignedLongSet immutableCopy();
+
+    public final @NonNull MutableUnsignedLongSet mutableCopy() {
+        return new MutableUnsignedLongSet(copyRanges());
+    }
+
+    final @NonNull TreeSet<Entry> copyRanges() {
+        return new TreeSet<>(Collections2.transform(ranges, Entry::copy));
+    }
+
+    public final @NonNull NavigableSet<Entry> ranges() {
+        return Collections.unmodifiableNavigableSet(ranges);
     }
 
-    public ImmutableRangeSet<UnsignedLong> toRangeSet() {
-        return ImmutableRangeSet.copyOf(Collections2.transform(ranges, Entry::toUnsigned));
+    final @NonNull NavigableSet<Entry> trustedRanges() {
+        return ranges;
     }
 
     @Override
-    public int hashCode() {
+    public final int hashCode() {
         return ranges.hashCode();
     }
 
     @Override
-    public boolean equals(final Object obj) {
+    public final boolean equals(final Object obj) {
         return obj == this || obj instanceof UnsignedLongSet && ranges.equals(((UnsignedLongSet) obj).ranges);
     }
 
     @Override
-    public String toString() {
+    public final String toString() {
         final var helper = MoreObjects.toStringHelper(this);
 
         final int size = ranges.size();