/
Buffer.mo
166 lines (149 loc) · 4.53 KB
/
Buffer.mo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/// Generic, extensible buffers
///
/// Generic, mutable sequences that grow to accommodate arbitrary numbers of elements.
///
/// Class `Buffer<X>` provides extensible, mutable sequences of elements of type `X`.
/// that can be efficiently produced and consumed with imperative code.
/// A buffer object can be extended by a single element or the contents of another buffer object.
///
/// When required, the current state of a buffer object can be converted to a fixed-size array of its elements.
///
/// Buffers complement Motoko's non-extensible array types
/// (arrays do not support efficient extension, because the size of an array is
/// determined at construction and cannot be changed).
import Prim "mo:⛔";
module {
/// Create a stateful buffer class encapsulating a mutable array.
///
/// The argument `initCapacity` determines its initial capacity.
/// The underlying mutable array grows by doubling when its current
/// capacity is exceeded.
public class Buffer<X>(initCapacity : Nat) {
var count : Nat = 0;
var elems : [var X] = [var]; // initially empty; allocated upon first `add`
/// Adds a single element to the buffer.
public func add(elem : X) {
if (count == elems.size()) {
let size =
if (count == 0) {
if (initCapacity > 0) { initCapacity } else { 1 }
} else {
2 * elems.size()
};
let elems2 = Prim.Array_init<X>(size, elem);
var i = 0;
label l loop {
if (i >= count) break l;
elems2[i] := elems[i];
i += 1;
};
elems := elems2;
};
elems[count] := elem;
count += 1;
};
/// Removes the item that was inserted last and returns it or `null` if no
/// elements had been added to the Buffer.
public func removeLast() : ?X {
if (count == 0) {
null
} else {
count -= 1;
?elems[count]
};
};
/// Adds all elements in buffer `b` to this buffer.
public func append(b : Buffer<X>) {
let i = b.vals();
loop {
switch (i.next()) {
case null return;
case (?x) { add(x) };
};
};
};
/// Returns the current number of elements.
public func size() : Nat =
count;
/// Resets the buffer.
public func clear() =
count := 0;
/// Returns a copy of this buffer.
public func clone() : Buffer<X> {
let c = Buffer<X>(elems.size());
var i = 0;
label l loop {
if (i >= count) break l;
c.add(elems[i]);
i += 1;
};
c
};
/// Returns an `Iter` over the elements of this buffer.
public func vals() : { next : () -> ?X } = object {
var pos = 0;
public func next() : ?X {
if (pos == count) { null } else {
let elem = ?elems[pos];
pos += 1;
elem
}
}
};
/// Creates a new array containing this buffer's elements.
public func toArray() : [X] =
// immutable clone of array
Prim.Array_tabulate<X>(
count,
func(x : Nat) : X { elems[x] }
);
/// Creates a mutable array containing this buffer's elements.
public func toVarArray() : [var X] {
if (count == 0) { [var] } else {
let a = Prim.Array_init<X>(count, elems[0]);
var i = 0;
label l loop {
if (i >= count) break l;
a[i] := elems[i];
i += 1;
};
a
}
};
/// Gets the `i`-th element of this buffer. Traps if `i >= count`. Indexing is zero-based.
public func get(i : Nat) : X {
assert(i < count);
elems[i]
};
/// Gets the `i`-th element of the buffer as an option. Returns `null` when `i >= count`. Indexing is zero-based.
public func getOpt(i : Nat) : ?X {
if (i < count) {
?elems[i]
}
else {
null
}
};
/// Overwrites the current value of the `i`-entry of this buffer with `elem`. Traps if the
/// index is out of bounds. Indexing is zero-based.
public func put(i : Nat, elem : X) {
elems[i] := elem;
};
};
/// Creates a buffer from immutable array elements.
public func fromArray<X>(elems : [X]) : Buffer<X> {
let buff = Buffer<X>(elems.size());
for (elem in elems.vals()) {
buff.add(elem)
};
buff
};
/// Creates a buffer from the elements of a mutable array.
public func fromVarArray<X>(elems : [var X]) : Buffer<X> {
let buff = Buffer<X>(elems.size());
for (elem in elems.vals()) {
buff.add(elem)
};
buff
};
}