From 1eb4c78a2e75b198a71f6362476be41ba2b0d52a Mon Sep 17 00:00:00 2001
From: Gene Gleyzer <gene@xqiz.it>
Date: Wed, 22 Jun 2022 14:14:44 -0400
Subject: [PATCH] Implement persistent part of the Map API for ListMap

---
 doc/todo-list.txt                             |  4 +-
 .../src/main/x/ecstasy/collections/ListMap.x  | 72 ++++++++++++++++---
 .../main/x/ecstasy/collections/ListMapIndex.x | 11 +--
 manualTests/src/main/x/TestSimple.x           | 14 ----
 4 files changed, 72 insertions(+), 29 deletions(-)

diff --git a/doc/todo-list.txt b/doc/todo-list.txt
index 893a5cf597..b3b6bab8b7 100644
--- a/doc/todo-list.txt
+++ b/doc/todo-list.txt
@@ -3,7 +3,9 @@
     const Test(HashMap<Int, String> map) - json serialization DOESN'T WORK
 
 - need to make _native module non-discoverable (and services for container -1)
-    see TestSimple.x on the shelve
+    see TestSimple.x on the shelf
+
+- need to implement "persistent" mutators for HasherMap
 
 - the XTC compiler disregards "resources" changes if nothing else changed and "-force" option
   is not specified
diff --git a/lib_ecstasy/src/main/x/ecstasy/collections/ListMap.x b/lib_ecstasy/src/main/x/ecstasy/collections/ListMap.x
index c4113dc26f..3172666755 100644
--- a/lib_ecstasy/src/main/x/ecstasy/collections/ListMap.x
+++ b/lib_ecstasy/src/main/x/ecstasy/collections/ListMap.x
@@ -43,8 +43,10 @@ class ListMap<Key, Value>
      */
     construct(Key[] keys, Value[] vals)
         {
-        this.keyArray = keys;
-        this.valArray = vals;
+        this.keyArray = keys.mutability == Persistent || keys.mutability == Constant
+                            ? keys : keys.freeze(inPlace=False);
+        this.valArray = vals.mutability == Persistent || vals.mutability == Constant
+                            ? vals : vals.freeze(inPlace=False);
         this.inPlace  = False;
 
         // TODO various checks, and do we need to copy the array(s) if they aren't immutable?
@@ -208,14 +210,39 @@ class ListMap<Key, Value>
         {
         if (Int index := indexOf(key))
             {
+            verifyInPlace();
             valArray[index] = value;
+            return this;
             }
-        else
+
+        if (inPlace)
             {
             appendEntry(key, value);
+            return this;
             }
 
-        return this;
+        // persistent
+        Key[]   keys = keyArray.add(key);
+        Value[] vals = valArray.add(value);
+        return new ListMap(keys, vals);
+        }
+
+    @Override
+    ListMap putAll(Map<Key, Value> that)
+        {
+        if (inPlace)
+            {
+            for ((Key key, Value value) : that)
+                {
+                put(key, value);
+                }
+            return this;
+            }
+
+        // persistent
+        Key[]   keys = keyArray.addAll(that.keys.toArray());
+        Value[] vals = valArray.addAll(that.values.toArray());
+        return new ListMap(keys, vals);
         }
 
     @Override
@@ -223,7 +250,16 @@ class ListMap<Key, Value>
         {
         if (Int index := indexOf(key))
             {
-            deleteEntryAt(index);
+            if (inPlace)
+                {
+                deleteEntryAt(index);
+                }
+            else // persistent
+                {
+                Key[]   keys = keyArray.delete(index);
+                Value[] vals = valArray.delete(index);
+                return new ListMap(keys, vals);
+                }
             }
 
         return this;
@@ -236,8 +272,17 @@ class ListMap<Key, Value>
             {
             if (valArray[index] == value)
                 {
-                deleteEntryAt(index);
-                return True, this;
+                if (inPlace)
+                    {
+                    deleteEntryAt(index);
+                    return True, this;
+                    }
+                else // persistent
+                    {
+                    Key[]   keys = keyArray.delete(index);
+                    Value[] vals = valArray.delete(index);
+                    return True, new ListMap(keys, vals);
+                    }
                 }
             }
 
@@ -250,9 +295,16 @@ class ListMap<Key, Value>
         Int count = size;
         if (count > 0)
             {
-            keyArray.clear();
-            valArray.clear();
-            deletes += count;
+            if (inPlace)
+                {
+                keyArray.clear();
+                valArray.clear();
+                deletes += count;
+                }
+            else // persistent
+                {
+                return new ListMap([], []);
+                }
             }
 
         return this;
diff --git a/lib_ecstasy/src/main/x/ecstasy/collections/ListMapIndex.x b/lib_ecstasy/src/main/x/ecstasy/collections/ListMapIndex.x
index e1c88ffe18..a24ff6510c 100644
--- a/lib_ecstasy/src/main/x/ecstasy/collections/ListMapIndex.x
+++ b/lib_ecstasy/src/main/x/ecstasy/collections/ListMapIndex.x
@@ -48,7 +48,7 @@ mixin ListMapIndex<Key extends Hashable, Value>
         Bucket[]? buckets = this.buckets;
         if (buckets == Null)
             {
-            if (size > MINSIZE)
+            if (size > MINSIZE && inPlace)
                 {
                 buildIndex();
                 buckets = this.buckets;
@@ -171,14 +171,17 @@ mixin ListMapIndex<Key extends Hashable, Value>
     @Override
     ListMapIndex clear()
         {
-        buckets = Null;
+        if (inPlace)
+            {
+            buckets = Null;
+            }
         return super();
         }
 
     @Override
     immutable ListMapIndex makeImmutable()
         {
-        if (buckets == Null && size > MINSIZE)
+        if (buckets == Null && size > MINSIZE && inPlace)
             {
             buildIndex();
             }
@@ -459,4 +462,4 @@ mixin ListMapIndex<Key extends Hashable, Value>
             }
         return indexes - 1;
         }
-    }
+    }
\ No newline at end of file
diff --git a/manualTests/src/main/x/TestSimple.x b/manualTests/src/main/x/TestSimple.x
index 7ce4c031c3..10138ebe64 100644
--- a/manualTests/src/main/x/TestSimple.x
+++ b/manualTests/src/main/x/TestSimple.x
@@ -21,18 +21,4 @@ module TestSimple
 //        map = map.put(2, "b");
 //        testSer(schema, "map", new Test(map));
         }
-
-    private <Ser> void testSer(Schema schema, String name, Ser val)
-        {
-        StringBuffer buf = new StringBuffer();
-        schema.createObjectOutput(buf).write(val);
-
-        String s = buf.toString();
-        console.println($"JSON {name} written out={s}");
-
-        Ser val2 = schema.createObjectInput(new CharArrayReader(s)).read<Ser>();
-        console.println($"read {name} back in={val2}");
-        }
-
-    const Test(Map<Int, String> map);
     }
\ No newline at end of file