Ver código fonte

Add vector, remove some dependency on Gee

Billy Barrow 1 ano atrás
pai
commit
d581451416

+ 170 - 0
src/lib/Collections/Vector.vala

@@ -0,0 +1,170 @@
+namespace Invercargill {
+
+    public class Vector<T> : Enumerable<T> {
+
+        private T[] array;
+        private int n_items = 0;
+
+        private const int INITIAL_SIZE = 2;
+
+        public Vector() {
+            array = new T[INITIAL_SIZE];
+        }
+
+        public override Tracker<T> get_tracker() {
+            var i = 0;
+            return new Generator<T>(() => {
+                lock(array) {
+                    if(i < n_items) {
+                        var item = safely_read_array<T>(array, i);
+                        i++;
+                        return GeneratorResult<T>.item(item);
+                    }
+                    return GeneratorResult<T>.end();
+                }
+            }).get_tracker();
+        }
+
+        public override T[] to_array () {
+            lock(array) {
+                var a2 = new T[n_items];
+                for(var i = 0; i < n_items; i++) {
+                    safely_assign_to_array(a2, i, safely_read_array<T>(array, i));
+                }
+                return a2;
+            }
+        }
+
+        public void add(T item) {
+            lock(array) {
+                ensure_room(1);
+                safely_assign_to_array<T>(array, n_items, item);
+                n_items++;
+            }
+        }
+
+        public new T get(int index) throws IndexError {
+            IndexError? e = null;
+            lock(array) {
+                if(index < 0) {
+                    e = new IndexError.INDEX_EXCEEDS_LOWER_BOUNDS("Index is less than 0");
+                }
+                else if(index >= n_items) {
+                    e = new IndexError.INDEX_EXCEEDS_UPPER_BOUNDS(@"Tried to access index $(index) on a vector with $(n_items) item(s)");
+                }
+                else {
+                    return safely_read_array<T>(array, index);
+                }
+            }
+            throw e;
+        }
+
+        public T? get_or_default(int index) {
+            lock(array) {
+                if(index < 0 || index >= n_items) {
+                    return null;
+                }
+                return safely_read_array<T>(array, index);
+            }
+        }
+
+        public new void set(int index, T value) throws IndexError {
+            IndexError? e = null;
+            lock(array) {
+                if(index < 0) {
+                    e = new IndexError.INDEX_EXCEEDS_LOWER_BOUNDS("Index is less than 0");
+                }
+                else if(index >= n_items) {
+                    e = new IndexError.INDEX_EXCEEDS_UPPER_BOUNDS(@"Tried to set index $(index) on a vector with $(n_items) item(s)");
+                }
+                else {
+                    safely_assign_to_array<T>(array, index, value);
+                    return;
+                }
+            }
+            throw e;
+        }
+
+        public void add_all(Enumerable<T> items) {
+            items.iterate(i => add(i));
+        }
+
+        private void ensure_room(int items) {
+            if(array.length <= n_items + items) {
+                array.resize(array.length * 2);
+            }
+        }
+
+        public override int count() {
+            return n_items;
+        }
+
+        public void remove(int index) throws IndexError {
+            var e = remove_internal(index);
+            if(e != null) {
+                throw e;
+            }
+        }
+
+        public bool try_remove(int index) {
+            var e = remove_internal(index);
+            return e == null;
+        }
+
+        private IndexError? remove_internal(int index) {
+            IndexError? e = null;
+            lock(array) {
+                if(index < 0) {
+                    e = new IndexError.INDEX_EXCEEDS_LOWER_BOUNDS("Index is less than 0");
+                }
+                else if(index >= n_items) {
+                    e = new IndexError.INDEX_EXCEEDS_UPPER_BOUNDS(@"Tried to set index $(index) on a vector with $(n_items) item(s)");
+                }
+                else {
+                    for(int i = index; i < n_items; i++) {
+                        if(i+1 < n_items) {
+                            safely_assign_to_array<T>(array, i, safely_read_array<T>(array, i+1));
+                            continue;
+                        }
+                        array[i] = null;
+                    }
+                    n_items --;
+                }
+            }
+            return e;
+        }
+
+        public override T last(owned PredicateDelegate<T>? predicate = null) throws SequenceError {
+            if(predicate != null) {
+                return base.last((owned)predicate);
+            }
+
+            SequenceError? e = null;
+            lock(array) {
+                if(n_items == 0) {
+                    e = new SequenceError.NO_ELEMENTS("The sequence contains no elements");
+                }
+                else {
+                    return safely_read_array<T>(array, n_items -1);
+                }
+            }
+            throw e;
+        }
+
+        public override T? last_or_default(owned PredicateDelegate<T>? predicate = null) {
+            if(predicate != null) {
+                return base.last_or_default((owned)predicate);
+            }
+
+            lock(array) {
+                if(n_items == 0) {
+                    return null;
+                }
+                else {
+                    return safely_read_array<T>(array, n_items -1);
+                }
+            }
+        }
+    }
+
+}

+ 2 - 2
src/lib/Concrete/ArrayEnumerable.vala

@@ -6,7 +6,7 @@ namespace Invercargill {
         private T[] array;
 
         public ArrayEnumerable(T[] input) {
-            assert_false (typeof(T).is_value_type ());
+            //  assert_false (typeof(T).is_value_type ());
             array = input;
         }
 
@@ -26,7 +26,7 @@ namespace Invercargill {
                     return i < array.length;
                 },
                 () => {
-                    var res = array[i];
+                    var res = safely_read_array(array, i);
                     i++;
                     return res;
                 });

+ 6 - 6
src/lib/Convert.vala

@@ -1,12 +1,12 @@
 namespace Invercargill.Convert {
 
     public static Enumerable<T> ate<T>(T[] input) {
-        if(typeof(T).is_value_type()) {
-            // LibGee has already done the hard work around
-            // Vala's funkyness with arrays of type arguments
-            var gee = new Gee.ArrayList<T>.wrap(input);
-            return new GeeEnumerable<T>(gee);
-        }
+        //  if(typeof(T).is_value_type()) {
+        //      // LibGee has already done the hard work around
+        //      // Vala's funkyness with arrays of type arguments
+        //      var gee = new Gee.ArrayList<T>.wrap(input);
+        //      return new GeeEnumerable<T>(gee);
+        //  }
 
         return new ArrayEnumerable<T>(input);
     }

+ 18 - 2
src/lib/Enumerable.vala

@@ -30,7 +30,7 @@ namespace Invercargill {
             return series;
         }
 
-        public virtual Gee.Collection<T> to_collection() {
+        public virtual Gee.Collection<T> to_gee_collection() {
             var collection = new Gee.LinkedList<T>();
             iterate(i => collection.add(i));
             return collection;
@@ -41,7 +41,17 @@ namespace Invercargill {
         }
 
         public virtual T[] to_array() {
-            return to_collection().to_array();
+            var array = new T[1024];
+            var index = 0;
+            foreach (var item in this) {
+                if(index >= array.length) {
+                    array.resize(array.length*2);
+                }
+                safely_assign_to_array<T>(array, index, item);
+                index++;
+            }
+            array.resize(index);
+            return array;
         }
 
         public virtual int count() {
@@ -249,6 +259,12 @@ namespace Invercargill {
             return select<Object>(i => (Object)i).to_array();
         }
 
+        public virtual Vector<T> to_vector() {
+            var vector = new Vector<T>();
+            vector.add_all(this);
+            return vector;
+        }
+
     }
 
 }

+ 6 - 0
src/lib/Errors.vala

@@ -9,4 +9,10 @@ namespace Invercargill {
         EMPTY_READ,
         BUFFER_OVERFLOW,
     }
+
+    public errordomain IndexError {
+        INDEX_EXCEEDS_UPPER_BOUNDS,
+        INDEX_EXCEEDS_LOWER_BOUNDS,
+        KEY_NOT_FOUND
+    }
 }

+ 1 - 1
src/lib/Queries/Sort.vala

@@ -11,7 +11,7 @@ namespace Invercargill {
 
         public override Tracker<T> get_tracker() {
             // Hack for now
-            var list = input.to_collection();
+            var list = input.to_gee_collection();
             return new GeeTracker<T>(list, list.order_by ((a, b) => compare_func(a, b)));
         }
     }

+ 84 - 0
src/lib/Safety.vala

@@ -0,0 +1,84 @@
+
+namespace Invercargill {
+
+    internal T safely_read_array<T> (T[] array, int index) {
+        var t = typeof (T);
+        if (t == typeof (bool)) {
+            return ((bool[])array)[index];
+        }
+        else if (t == typeof (char)) {
+            return ((char[])array)[index];
+        }
+        else if (t == typeof (uchar)) {
+            return ((uchar[])array)[index];
+        }
+        else if (t == typeof (int) || t.is_enum () || t.is_flags ()) {
+            return ((int[])array)[index];
+        }
+        else if (t == typeof (uint)) {
+            return ((uint[])array)[index];
+        }
+        else if (t == typeof (int64)) {
+            return ((int64[])array)[index];
+        }
+        else if (t == typeof (uint64)) {
+            return ((uint64[])array)[index];
+        }
+        else if (t == typeof (long)) {
+            return ((long[])array)[index];
+        }
+        else if (t == typeof (ulong)) {
+            return ((ulong[])array)[index];
+        }
+        else if (t == typeof (float)) {
+            return ((float?[])array)[index];
+        }
+        else if (t == typeof (double)) {
+            return ((double?[])array)[index];
+        }
+        else {
+            return array[index];
+        }
+    }
+
+    internal void safely_assign_to_array<T> (T[] array, int index, T value) {
+        var t = typeof (T);
+        if (t == typeof (bool)) {
+            ((bool[])array)[index] = (bool)value;
+        }
+        else if (t == typeof (char)) {
+            ((char[])array)[index] = (char) value;
+        }
+        else if (t == typeof (uchar)) {
+            ((uchar[])array)[index] = (uchar)value;
+        }
+        else if (t == typeof (int) || t.is_enum () || t.is_flags ()) {
+            ((int[])array)[index] = (int)value;
+        }
+        else if (t == typeof (uint)) {
+            ((uint[])array)[index] = (uint)value;
+        }
+        else if (t == typeof (int64)) {
+            ((int64[])array)[index] = (int64)value;
+        }
+        else if (t == typeof (uint64)) {
+            ((uint64[])array)[index] = (uint64)value;
+        }
+        else if (t == typeof (long)) {
+            ((long[])array)[index] = (long)value;
+        }
+        else if (t == typeof (ulong)) {
+            ((ulong[])array)[index] = (ulong)value;
+        }
+        else if (t == typeof (float)) {
+            ((float?[])array)[index] = (float?)value;
+        }
+        else if (t == typeof (double)) {
+            ((double?[])array)[index] = (double?)value;
+        }
+        else {
+            array[index] = value;
+        }
+    }
+
+}

+ 2 - 0
src/lib/meson.build

@@ -14,6 +14,7 @@ sources += files('Tracker.vala')
 sources += files('Errors.vala')
 sources += files('SelectionContext.vala')
 sources += files('Convert.vala')
+sources += files('Safety.vala')
 
 sources += files('Queries/Query.vala')
 sources += files('Queries/Transform.vala')
@@ -39,6 +40,7 @@ sources += files('Concrete/Generator.vala')
 sources += files('Collections/Series.vala')
 sources += files('Collections/Fifo.vala')
 sources += files('Collections/BinaryData.vala')
+sources += files('Collections/Vector.vala')
 
 invercargill = shared_library('invercargill', sources,
     dependencies: dependencies,

+ 23 - 0
src/tests/Integration/Arrays.vala

@@ -0,0 +1,23 @@
+using Invercargill;
+using Invercargill.Convert;
+
+void array_tests() {
+
+    Test.add_func("/invercargill/function/to_array/simple", () => {
+        var input = new int[] { 1, 8, 2, 4, 6, 5, 3, 10, 3, 7 };
+        var array = ate(input).to_array();
+
+        for(int i = 0; i < input.length; i++) {
+            assert(array[i] == input[i]);
+        }
+    });
+
+    Test.add_func("/invercargill/function/to_array/many", () => {
+        var num = 10000000;
+        var array = range(0, num).to_array();
+
+        for(int i = 0; i < num; i++) {
+            assert(array[i] == i);
+        }
+    });
+}

+ 1 - 1
src/tests/Integration/Gee.vala

@@ -5,7 +5,7 @@ void gee_tests() {
 
     Test.add_func("/invercargill/conversion/gee/collection", () => {
         var items = ate(new int[] { 1, 2, 3, 4, 5, 6});
-        var result = items.to_collection();
+        var result = items.to_gee_collection();
 
         var i = 1;
         foreach (var item in result) {

+ 34 - 0
src/tests/Integration/Series.vala

@@ -0,0 +1,34 @@
+using Invercargill;
+using Invercargill.Convert;
+
+void series_tests() {
+
+    Test.add_func("/invercargill/structure/series/add_item", () => {
+        var series = new Series<int>();
+        series.add(8);
+
+        assert(series.first_or_default() == 8);
+        assert(series.count() == 1);
+    });
+
+    Test.add_func("/invercargill/structure/series/many_items", () => {
+
+        var items = range(0, 10000000);
+        var series = items.to_series();
+        assert(series.count() == 10000000);
+
+        series.matches(items, (a, b) => a == b);
+    });
+
+    Test.add_func("/invercargill/structure/series/to_array", () => {
+
+        var items = new int[] { 1, 8, 2, 4, 6, 5, 3, 10, 3, 7 };
+        var vector = Convert.ate(items).to_vector();
+        var array = vector.to_array();
+        assert(array.length == 10);
+
+        for(int i = 0; i < items.length; i++) {
+            assert(array[i] == items[i]);
+        }
+    });
+}

+ 202 - 0
src/tests/Integration/Vector.vala

@@ -0,0 +1,202 @@
+using Invercargill;
+using Invercargill.Convert;
+
+void vector_tests() {
+
+    Test.add_func("/invercargill/structure/vector/add_item", () => {
+
+        var vector = new Vector<int>();
+        vector.add(8);
+
+        assert(vector.first_or_default() == 8);
+        assert(vector.get_or_default(0) == 8);
+        assert(vector.count() == 1);
+    });
+
+    Test.add_func("/invercargill/structure/vector/expand", () => {
+
+        var vector = new Vector<int>();
+        vector.add(8);
+        vector.add(10);
+        vector.add(64);
+        vector.add(35);
+        vector.add(129);
+
+        assert(vector.first_or_default() == 8);
+        assert(vector.get_or_default(0) == 8);
+        assert(vector.get_or_default(1) == 10);
+        assert(vector.get_or_default(2) == 64);
+        assert(vector.get_or_default(3) == 35);
+        assert(vector.get_or_default(4) == 129);
+        assert(vector.last_or_default() == 129);
+        assert(vector.count() == 5);
+    });
+
+    Test.add_func("/invercargill/structure/vector/to_vector", () => {
+
+        var items = new int[] { 1, 8, 2, 4, 6, 5, 3, 10, 3, 7 };
+        var vector = Convert.ate(items).to_vector();
+        assert(vector.count() == 10);
+
+        for(int i = 0; i < items.length; i++) {
+            assert(vector.get_or_default(i) == items[i]);
+        }
+    });
+
+    Test.add_func("/invercargill/structure/vector/to_array", () => {
+
+        var items = new int[] { 1, 8, 2, 4, 6, 5, 3, 10, 3, 7 };
+        var vector = Convert.ate(items).to_vector();
+        var array = vector.to_array();
+        assert(array.length == 10);
+
+        for(int i = 0; i < items.length; i++) {
+            assert(array[i] == items[i]);
+        }
+    });
+
+    Test.add_func("/invercargill/structure/vector/many_items", () => {
+
+        var items = range(0, 10000000);
+        var vector = items.to_vector();
+        assert(vector.count() == 10000000);
+
+        vector.matches(items, (a, b) => a == b);
+    });
+
+    Test.add_func("/invercargill/structure/vector/remove", () => {
+
+        var items = new int[] { 1, 8, 2, 4, 6, 5, 3, 10, 3, 7 };
+        var expected = new int[] { 1, 8, 2, 4, 6, 3, 10, 3, 7 };
+        var vector = Convert.ate(items).to_vector();
+
+        assert(vector.try_remove(5));
+        assert(vector.count() == 9);
+
+        assert(vector.matches(Convert.ate(expected), (a, b) => a == b));
+    });
+
+    Test.add_func("/invercargill/structure/vector/last/no-condition", () => {
+        var items = ate(new int[] { 1, 2, 3, 4, 5, 6}).to_vector();
+
+        try{
+            var last = items.last();
+            assert_true(last == 6);
+        }
+        catch (Error e) {
+            assert_no_error(e);
+        }
+        
+    });
+
+    Test.add_func("/invercargill/structure/vector/last/condition", () => {
+        var items = ate(new int[] { 1, 2, 3, 4, 5, 6}).to_vector();
+
+        try {
+            var last = items.last(i => i < 4);
+            assert_true(last == 3);
+        }
+        catch (Error e) {
+            assert_no_error(e);
+        }
+    });
+
+    Test.add_func("/invercargill/structure/vector/last/empty", () => {
+        var items = new Vector<int>();
+
+        try {
+            items.last();
+            assert_not_reached();
+        }
+        catch (Error e) {
+
+        }
+    });
+
+    Test.add_func("/invercargill/structure/vector/get/empty", () => {
+        var items = new Vector<int>();
+
+        try {
+            items.get(0);
+            assert_not_reached();
+        }
+        catch (Error e) {
+
+        }
+    });
+
+
+    Test.add_func("/invercargill/structure/vector/get/negative-index", () => {
+        var items = new Vector<int>();
+
+        try {
+            items.get(-5);
+            assert_not_reached();
+        }
+        catch (Error e) {
+
+        }
+    });
+
+    Test.add_func("/invercargill/structure/vector/get/out-of-bounds", () => {
+        var items = ate(new int[] { 1, 2, 3, 4, 5, 6}).to_vector();
+
+        try {
+            items.get(6);
+            assert_not_reached();
+        }
+        catch (Error e) {
+
+        }
+    });
+
+    Test.add_func("/invercargill/structure/vector/set/empty", () => {
+        var items = new Vector<int>();
+
+        try {
+            items.set(0, 5);
+            assert_not_reached();
+        }
+        catch (Error e) {
+
+        }
+    });
+
+
+    Test.add_func("/invercargill/structure/vector/set/negative-index", () => {
+        var items = new Vector<int>();
+
+        try {
+            items.set(-5, 73);
+            assert_not_reached();
+        }
+        catch (Error e) {
+
+        }
+    });
+
+    Test.add_func("/invercargill/structure/vector/set/out-of-bounds", () => {
+        var items = ate(new int[] { 1, 2, 3, 4, 5, 6}).to_vector();
+
+        try {
+            items.set(6, 7);
+            assert_not_reached();
+        }
+        catch (Error e) {
+
+        }
+    });
+
+    Test.add_func("/invercargill/structure/vector/set/valid", () => {
+        var items = ate(new int[] { 1, 2, 3, 4, 5, 6}).to_vector();
+
+        try {
+            items.set(1, 8);
+        }
+        catch (Error e) {
+            assert_not_reached();
+        }
+        
+        assert(items.get_or_default(1) == 8);
+    });
+}

+ 3 - 0
src/tests/TestRunner.vala

@@ -12,6 +12,9 @@ public static int main(string[] args) {
     first_tests();
     binary_data_tests();
     sort_tests();
+    vector_tests();
+    series_tests();
+    array_tests();
 
     Test.run();
 

+ 4 - 1
src/tests/meson.build

@@ -11,5 +11,8 @@ sources += files('Integration/Tracker.vala')
 sources += files('Integration/Parallel.vala')
 sources += files('Integration/Firsts.vala')
 sources += files('Integration/BinaryData.vala')
+sources += files('Integration/Vector.vala')
+sources += files('Integration/Series.vala')
+sources += files('Integration/Arrays.vala')
 
-executable('invercargill-test-suite', sources, dependencies: dependencies, install: true)
+executable('invercargill-test-suite', sources, dependencies: dependencies, install: true)