Discover the common prefix in a FileStateSet 09/104609/3
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 26 Feb 2023 17:55:35 +0000 (18:55 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 26 Feb 2023 18:24:18 +0000 (19:24 +0100)
Address a TODO that potentially improves persisted file size. This
makes FileState not a WritableObject, as the serialization format
requires further information.

JIRA: YANGTOOLS-1166
Change-Id: I35f7044186ac1e862a41e4f4a903fba2417cbb23
Signed-off-by: Ruslan Kashapov <ruslan.kashapov@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileState.java
plugin/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/FileStateSet.java

index 0be58262c94d2b0532475d8d753c1d2679f4c32e..e2357d8f74b3fa6302865a144b542e825e4b9499 100644 (file)
@@ -9,30 +9,14 @@ package org.opendaylight.yangtools.yang2sources.plugin;
 
 import static java.util.Objects.requireNonNull;
 
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
 import java.nio.file.attribute.BasicFileAttributes;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.concepts.WritableObject;
-import org.opendaylight.yangtools.concepts.WritableObjects;
 
 /**
  * Hash of a single file state. {@link #size()} corresponds to {@link BasicFileAttributes#size()}.
  */
-record FileState(@NonNull String path, long size, int crc32) implements WritableObject {
+record FileState(@NonNull String path, long size, int crc32) {
     FileState {
         requireNonNull(path);
     }
-
-    public static FileState read(final DataInput in) throws IOException {
-        return new FileState(in.readUTF(), WritableObjects.readLong(in), in.readInt());
-    }
-
-    @Override
-    public void writeTo(final DataOutput out) throws IOException {
-        out.writeUTF(path);
-        WritableObjects.writeLong(out, size);
-        out.writeInt(crc32);
-    }
 }
index c587cd6013e6b85fede8e3f13790ee58e68da168..42e9c57b7550e8a2141f4503c374ca4d326a8745 100644 (file)
@@ -9,27 +9,46 @@ package org.opendaylight.yangtools.yang2sources.plugin;
 
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
+import java.util.Set;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.concepts.WritableObject;
+import org.opendaylight.yangtools.concepts.WritableObjects;
 
 /**
  * A collection of {@link FileState} objects indexed by their {@link FileState#path()}.
  */
 record FileStateSet(@NonNull ImmutableMap<String, FileState> fileStates) implements WritableObject {
+    private static final @NonNull FileStateSet EMPTY = new FileStateSet(ImmutableMap.of());
+
     FileStateSet {
         requireNonNull(fileStates);
     }
 
+    static @NonNull FileStateSet empty() {
+        return EMPTY;
+    }
+
+    static @NonNull FileStateSet ofNullable(final @Nullable ImmutableMap<String, FileState> fileStates) {
+        return fileStates == null || fileStates.isEmpty() ? EMPTY : new FileStateSet(fileStates);
+    }
+
     static @NonNull FileStateSet readFrom(final DataInput in) throws IOException {
         final int size = in.readInt();
+        if (size == 0) {
+            return EMPTY;
+        }
+
+        final var prefix = in.readUTF();
         final var fileStates = ImmutableMap.<String, FileState>builderWithExpectedSize(size);
         for (int i = 0; i < size; ++i) {
-            final var fileState = FileState.read(in);
-            fileStates.put(fileState.path(), fileState);
+            final var path = prefix + in.readUTF();
+            fileStates.put(path, new FileState(path, WritableObjects.readLong(in), in.readInt()));
         }
         return new FileStateSet(fileStates.build());
     }
@@ -37,10 +56,36 @@ record FileStateSet(@NonNull ImmutableMap<String, FileState> fileStates) impleme
     @Override
     public void writeTo(final DataOutput out) throws IOException {
         out.writeInt(fileStates.size());
+        if (fileStates.isEmpty()) {
+            // Nothing else here
+            return;
+        }
+
+        final var prefix = findPathPrefix(fileStates.keySet());
+        out.writeUTF(prefix);
+
+        final int cutIndex = prefix.length();
         for (var fileState : fileStates.values()) {
-            // TODO: discover common prefix and serialize it just once -- but that will complicate things a log, as
-            //       we really maintain a hierarchy, which means we want the Map sorted in a certain way.
-            fileState.writeTo(out);
+            final var path = fileState.path();
+            out.writeUTF(cutIndex == 0 ? path : path.substring(cutIndex));
+            WritableObjects.writeLong(out, fileState.size());
+            out.writeInt(fileState.crc32());
+        }
+    }
+
+    private static String findPathPrefix(final Set<String> filePaths) {
+        if (filePaths.size() == 1) {
+            // Single item: do not use a prefix
+            return "";
         }
+
+        final var it = filePaths.iterator();
+        var prefix = it.next();
+
+        do {
+            prefix = Strings.commonPrefix(prefix, it.next());
+        } while (!prefix.isEmpty() && it.hasNext());
+
+        return prefix;
     }
 }