-
Notifications
You must be signed in to change notification settings - Fork 25
/
Vectors.dfy
217 lines (197 loc) · 5.74 KB
/
Vectors.dfy
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
// RUN: %verify "%s"
include "../../BoundedInts.dfy"
include "../../Wrappers.dfy"
module {:options "-functionSyntax:4"} JSON.Utils.Vectors {
import opened BoundedInts
import opened Wrappers
datatype VectorError = OutOfMemory
const OOM_FAILURE := Fail(OutOfMemory)
class Vector<A> {
ghost var items : seq<A>
ghost var Repr : set<object>
const a: A
var size: uint32
var capacity: uint32
var data: array<A>
const MAX_CAPACITY: uint32 := UINT32_MAX
const MAX_CAPACITY_BEFORE_DOUBLING: uint32 := UINT32_MAX / 2
ghost predicate Valid?()
reads this, Repr
ensures Valid?() ==> this in Repr
{
&& Repr == {this, data}
&& capacity != 0
&& data.Length == capacity as int
&& size <= capacity
&& size as int == |items|
&& items == data[..size]
}
constructor(a0: A, initial_capacity: uint32 := 8)
requires initial_capacity > 0
ensures size == 0
ensures items == []
ensures fresh(Repr)
ensures Valid?()
{
a := a0;
items := [];
size := 0;
capacity := initial_capacity;
data := new A[initial_capacity](_ => a0);
Repr := {this, data};
}
function At(idx: uint32) : (a: A)
reads this, this.data
requires idx < size
requires Valid?()
ensures a == data[idx] == items[idx]
{
data[idx]
}
function Top() : (a: A)
reads this, this.data
requires 0 < size
requires Valid?()
ensures a == data[size - 1] == items[size - 1]
{
data[size - 1]
}
method Put(idx: uint32, a: A)
requires idx < size
requires Valid?()
modifies data, `items
ensures Valid?()
ensures items == old(items)[idx := a]
{
data[idx] := a;
items := items[idx := a];
}
method CopyFrom(new_data: array<A>, count: uint32)
requires count as int <= new_data.Length
requires count <= capacity
requires data.Length == capacity as int
modifies data
ensures data[..count] == new_data[..count]
ensures data[count..] == old(data[count..])
{
for idx: uint32 := 0 to count
invariant idx <= capacity
invariant data.Length == capacity as int
invariant data[..idx] == new_data[..idx]
invariant data[count..] == old(data[count..])
{
data[idx] := new_data[idx];
}
}
method Realloc(new_capacity: uint32)
requires Valid?()
requires new_capacity > capacity
modifies `capacity, `data, `Repr, data
ensures Valid?()
ensures capacity == new_capacity
ensures fresh(data)
{
var old_data, old_capacity := data, capacity;
data, capacity := new A[new_capacity](_ => a), new_capacity;
CopyFrom(old_data, old_capacity);
Repr := {this, data};
}
function DefaultNewCapacity(capacity: uint32) : uint32 {
if capacity < MAX_CAPACITY_BEFORE_DOUBLING
then 2 * capacity
else MAX_CAPACITY
}
method ReallocDefault() returns (o: Outcome<VectorError>)
requires Valid?()
modifies `capacity, `data, `Repr, data
ensures Valid?()
ensures o.Fail? <==> old(capacity) == MAX_CAPACITY
ensures o.Fail? ==>
&& unchanged(this)
&& unchanged(data)
ensures o.Pass? ==>
&& fresh(data)
&& old(capacity) < MAX_CAPACITY
&& capacity == old(if capacity < MAX_CAPACITY_BEFORE_DOUBLING
then 2 * capacity else MAX_CAPACITY)
{
if capacity == MAX_CAPACITY {
return Fail(OutOfMemory);
}
Realloc(DefaultNewCapacity(capacity));
return Pass;
}
method Ensure(reserved: uint32) returns (o: Outcome<VectorError>)
requires Valid?()
modifies this, `data
ensures Valid?()
modifies `capacity, `data, `Repr, data
ensures reserved <= capacity - size ==>
o.Pass?
ensures o.Pass? ==>
old(size as int + reserved as int) <= capacity as int
ensures o.Fail? ==>
reserved > MAX_CAPACITY - size
{
if reserved > MAX_CAPACITY - size {
return Fail(OutOfMemory);
}
if reserved <= capacity - size {
return Pass;
}
var new_capacity := capacity;
while reserved > new_capacity - size
decreases MAX_CAPACITY - new_capacity
invariant new_capacity >= capacity
{
new_capacity := DefaultNewCapacity(new_capacity);
}
Realloc(new_capacity);
return Pass;
}
method PopFast()
requires Valid?()
requires size > 0
modifies `size, `items
ensures Valid?()
ensures size == old(size) - 1
ensures items == old(items[..|items| - 1])
{
size := size - 1;
items := items[..|items| - 1];
}
method PushFast(a: A)
requires Valid?()
requires size < capacity
modifies `size, `items, data
ensures Valid?()
ensures size == old(size) + 1
ensures items == old(items) + [a]
{
data[size] := a;
size := size + 1;
items := items + [a];
}
method Push(a: A) returns (o: Outcome<VectorError>)
requires Valid?()
modifies this, data
ensures Valid?()
ensures o.Fail? ==>
&& unchanged(this)
&& unchanged(data)
ensures o.Pass? ==>
&& old(size) < MAX_CAPACITY
&& size == old(size) + 1
&& items == old(items) + [a]
&& capacity >= old(capacity)
&& if old(size == capacity) then fresh(data) else unchanged(`data)
{
if size == capacity {
var d := ReallocDefault();
if d.Fail? { return d; }
}
PushFast(a);
return Pass;
}
}
}