Compaction of discrete resources with range based representation

This resolves ONOS-4281

Change-Id: I0739ba94cc0b3ce617e2db44307fef396dcfb942
(cherry picked from commit 34e2c1c223ec62c85c2613c0d899a0cc06b63f33)
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java
index 549675b..dd78e50 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentResourceStore.java
@@ -23,6 +23,7 @@
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.util.Tools;
+import org.onlab.util.KryoNamespace;
 import org.onosproject.net.resource.ContinuousResource;
 import org.onosproject.net.resource.ContinuousResourceId;
 import org.onosproject.net.resource.DiscreteResource;
@@ -45,7 +46,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -70,11 +70,14 @@
         implements ResourceStore {
     private static final Logger log = LoggerFactory.getLogger(ConsistentResourceStore.class);
 
-    static final Serializer SERIALIZER = Serializer.using(
-            Arrays.asList(KryoNamespaces.API),
-            UnifiedDiscreteResources.class,
-            NonEncodableDiscreteResources.class,
-            ContinuousResourceAllocation.class);
+    static final Serializer SERIALIZER = Serializer.using(KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(UnifiedDiscreteResources.class)
+            .register(new EncodableDiscreteResourcesSerializer(), EncodableDiscreteResources.class)
+            .register(NonEncodableDiscreteResources.class)
+            .register(EmptyDiscreteResources.class)
+            .register(ContinuousResourceAllocation.class)
+            .build());
 
     // TODO: We should provide centralized values for this
     static final int MAX_RETRIES = 5;
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/DiscreteResources.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/DiscreteResources.java
index 6b7da8e..3b39120 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/DiscreteResources.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/DiscreteResources.java
@@ -32,7 +32,7 @@
      * @return a empty set.
      */
     static DiscreteResources empty() {
-        return UnifiedDiscreteResources.empty();
+        return EmptyDiscreteResources.INSTANCE;
     }
 
     /**
@@ -41,7 +41,7 @@
      * @param resources resources
      * @return instance
      */
-    static DiscreteResources of(List<DiscreteResource> resources) {
+    static DiscreteResources of(Set<DiscreteResource> resources) {
         return UnifiedDiscreteResources.of(resources);
     }
 
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResources.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResources.java
index 891f35f..a20ca52 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResources.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/EncodableDiscreteResources.java
@@ -43,6 +43,10 @@
     }
 
     static DiscreteResources of(Set<DiscreteResource> resources) {
+        if (resources.isEmpty()) {
+            return DiscreteResources.empty();
+        }
+
         DiscreteResource parent = resources.iterator().next().parent().get();
         return of(parent, resources);
     }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/NonEncodableDiscreteResources.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/NonEncodableDiscreteResources.java
index 7b991b4..333a40b 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/NonEncodableDiscreteResources.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/NonEncodableDiscreteResources.java
@@ -30,26 +30,23 @@
 final class NonEncodableDiscreteResources implements DiscreteResources {
     private final Set<DiscreteResource> values;
 
-    static NonEncodableDiscreteResources empty() {
-        return new NonEncodableDiscreteResources();
-    }
+    static DiscreteResources of(Set<DiscreteResource> resources) {
+        if (resources.isEmpty()) {
+            return DiscreteResources.empty();
+        }
 
-    static NonEncodableDiscreteResources of(List<DiscreteResource> resources) {
         return new NonEncodableDiscreteResources(resources);
     }
 
-    private NonEncodableDiscreteResources() {
-        this.values = new LinkedHashSet<>();
-    }
-
-    private NonEncodableDiscreteResources(List<DiscreteResource> values) {
-        this.values = new LinkedHashSet<>(values);
-    }
-
     private NonEncodableDiscreteResources(Set<DiscreteResource> values) {
         this.values = values;
     }
 
+    // for serializer
+    private NonEncodableDiscreteResources() {
+        this.values = null;
+    }
+
     @Override
     public Optional<DiscreteResource> lookup(DiscreteResourceId id) {
         DiscreteResource resource = Resources.discrete(id).resource();
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/TransactionalDiscreteResourceSubStore.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/TransactionalDiscreteResourceSubStore.java
index d4306a7..e98cb20 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/TransactionalDiscreteResourceSubStore.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/TransactionalDiscreteResourceSubStore.java
@@ -24,6 +24,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Optional;
 
@@ -59,7 +60,7 @@
             return true;
         }
 
-        DiscreteResources requested = DiscreteResources.of(values);
+        DiscreteResources requested = DiscreteResources.of(new LinkedHashSet<>(values));
         DiscreteResources oldValues = childMap.putIfAbsent(key, requested);
         if (oldValues == null) {
             return true;
diff --git a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/UnifiedDiscreteResources.java b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/UnifiedDiscreteResources.java
index ec121cd..1b4ded4 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/resource/impl/UnifiedDiscreteResources.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/resource/impl/UnifiedDiscreteResources.java
@@ -15,12 +15,18 @@
  */
 package org.onosproject.store.resource.impl;
 
+import com.google.common.collect.Sets;
 import org.onosproject.net.resource.DiscreteResource;
 import org.onosproject.net.resource.DiscreteResourceId;
+import org.onosproject.net.resource.Resources;
 
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * Represents a set of resources containing resources that can be encoded as integer
@@ -28,55 +34,66 @@
  */
 final class UnifiedDiscreteResources implements DiscreteResources {
     private final DiscreteResources nonEncodables;
+    private final DiscreteResources encodables;
+    private static final Codecs CODECS = Codecs.getInstance();
 
-    static DiscreteResources empty() {
-        return new UnifiedDiscreteResources();
+    static DiscreteResources of(Set<DiscreteResource> resources) {
+        if (resources.isEmpty()) {
+            return DiscreteResources.empty();
+        }
+
+        Map<Boolean, Set<DiscreteResource>> partitioned = resources.stream()
+                .collect(Collectors.partitioningBy(CODECS::isEncodable, Collectors.toCollection(LinkedHashSet::new)));
+        return new UnifiedDiscreteResources(
+                NonEncodableDiscreteResources.of(partitioned.get(false)),
+                EncodableDiscreteResources.of(partitioned.get(true))
+        );
     }
 
-    static DiscreteResources of(List<DiscreteResource> resources) {
-        return new UnifiedDiscreteResources(resources);
-    }
-
-    private UnifiedDiscreteResources() {
-        this.nonEncodables = NonEncodableDiscreteResources.empty();
-    }
-
-    private UnifiedDiscreteResources(List<DiscreteResource> resources) {
-        this.nonEncodables = NonEncodableDiscreteResources.of(resources);
+    private UnifiedDiscreteResources(DiscreteResources nonEncodables, DiscreteResources encodables) {
+        this.nonEncodables = nonEncodables;
+        this.encodables = encodables;
     }
 
     @Override
     public Optional<DiscreteResource> lookup(DiscreteResourceId id) {
+        if (CODECS.isEncodable(Resources.discrete(id).resource())) {
+            return encodables.lookup(id);
+        }
+
         return nonEncodables.lookup(id);
     }
 
     @Override
     public DiscreteResources difference(DiscreteResources other) {
-        return nonEncodables.difference(other);
+        return of(Sets.difference(values(), other.values()));
     }
 
     @Override
     public boolean isEmpty() {
-        return nonEncodables.isEmpty();
+        return nonEncodables.isEmpty() && encodables.isEmpty();
     }
 
     @Override
     public boolean containsAny(List<DiscreteResource> other) {
-        return nonEncodables.containsAny(other);
+        Map<Boolean, List<DiscreteResource>> partitioned = other.stream()
+                .collect(Collectors.partitioningBy(CODECS::isEncodable));
+        return nonEncodables.containsAny(partitioned.get(false)) || encodables.containsAny(partitioned.get(true));
     }
 
     @Override
     public DiscreteResources add(DiscreteResources other) {
-        return nonEncodables.add(other);
+        return of(Sets.union(this.values(), other.values()));
     }
 
     @Override
     public DiscreteResources remove(List<DiscreteResource> removed) {
-        return nonEncodables.remove(removed);
+        return of(Sets.difference(values(), new LinkedHashSet<>(removed)));
     }
 
     @Override
     public Set<DiscreteResource> values() {
-        return nonEncodables.values();
+        return Stream.concat(encodables.values().stream(), nonEncodables.values().stream())
+                .collect(Collectors.toCollection(LinkedHashSet::new));
     }
 }