1 /**
2 Copyright: Copyright (c) 2020, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 A RAII vector that uses GC memory. It is not meant to be performant but rather
7 convenient. The intention is to support put/pop for front and back and
8 convenient range operations.
9 */
10 module my.container.vector;
11 
12 import std.array : array;
13 import std.range : isForwardRange;
14 
15 auto vector(T)(T[] data) {
16     return Vector!T(data);
17 }
18 
19 struct Vector(T) {
20     T[] data;
21 
22     void putFront(T a) {
23         data = [a] ~ data;
24     }
25 
26     void put(T a) {
27         data ~= a;
28     }
29 
30     void put(T[] a) {
31         data ~= a;
32     }
33 
34     void popBack() {
35         data = data[0 .. $ - 1];
36     }
37 
38     T back() {
39         return data[$ - 1];
40     }
41 
42     T front() {
43         assert(!empty, "Can't get front of an empty range");
44         return data[0];
45     }
46 
47     void popFront() {
48         assert(!empty, "Can't pop front of an empty range");
49         data = data[1 .. $];
50     }
51 
52     void clear() {
53         data = null;
54     }
55 
56     bool empty() const {
57         return data.length == 0;
58     }
59 
60     size_t length() const {
61         return data.length;
62     }
63 
64     Vector!T range() {
65         return Vector!T(data);
66     }
67 
68     ref inout(T) opIndex(long index) scope return inout {
69         return data[index];
70     }
71 
72     /// Returns a new vector after appending to the given vector.
73     Vector opBinary(string s, T)(auto ref T other) const 
74             if (s == "~" && is(Unqual!T == Vector)) {
75         return vector(data ~ other.data);
76     }
77 
78     /// Assigns from a range.
79     void opAssign(R)(R range) scope if (isForwardRange!(R)) {
80         data = range.array;
81     }
82 
83     void opOpAssign(string op)(T other) scope if (op == "~") {
84         put(other);
85     }
86 
87     /// Append to the vector from a range
88     void opOpAssign(string op, R)(scope R range) scope 
89             if (op == "~" && isForwardRange!(R)) {
90         data ~= range.array;
91     }
92 
93     size_t opDollar() const {
94         return length;
95     }
96 
97     T[] opSlice() {
98         return data;
99     }
100 
101     /**
102        Returns a slice.
103        @system because the pointer in the slice might dangle.
104      */
105     T[] opSlice(size_t start, size_t end) {
106         return data[start .. end];
107     }
108 
109     void opSliceAssign(T value) {
110         data[] = value;
111     }
112 
113     /// Assign all elements in the given range to the given value
114     void opSliceAssign(T value, size_t start, size_t end) {
115         data[start .. end] = value;
116     }
117 
118     /// Assign all elements using the given operation and the given value
119     void opSliceOpAssign(string op)(E value) scope {
120         foreach (ref elt; data)
121             mixin(`elt ` ~ op ~ `= value;`);
122     }
123 
124     /// Assign all elements in the given range  using the given operation and the given value
125     void opSliceOpAssign(string op)(E value, long start, long end) scope {
126         foreach (ref elt; data[start .. end])
127             mixin(`elt ` ~ op ~ `= value;`);
128     }
129 
130     bool opCast(U)() const scope if (is(U == bool)) {
131         return data.length > 0;
132     }
133 
134     bool opEquals(ref scope const(Vector!(T)) other) const {
135         return data == other.data;
136     }
137 }
138 
139 @("shall put/pop")
140 unittest {
141     Vector!int v;
142     v.put(1);
143     v.put(2);
144 
145     assert(v.front == 1);
146     assert(v.back == 2);
147     v.popBack;
148     assert(v.front == 1);
149     assert(v.back == 1);
150 }
151 
152 @("shall put/pop")
153 unittest {
154     Vector!int v;
155     v.put(1);
156     v.put(2);
157 
158     assert(v.front == 1);
159     assert(v.back == 2);
160     v.popFront;
161     assert(v.front == 2);
162     assert(v.back == 2);
163 }