Skip to content

Commit 38e9a79

Browse files
committed
Our of bounds exceptions for resizable arrays
1 parent 81717bf commit 38e9a79

File tree

5 files changed

+42
-19
lines changed

5 files changed

+42
-19
lines changed

examples/stdlib/heap/heap.effekt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import test
44
def main() = {
55
suite("HeapTests", false) {
66
test("simple heap sort on integers") {
7+
with on[OutOfBounds].default { assertTrue(false); <> };
78
val h = heap[Int](box { (x: Int, y: Int) =>
89
if (x < y) {
910
Less()
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
ResizableArrayTests
22
✓ usage as stack
33

4-
1 pass
4+
2 pass
55
0 fail
6-
1 tests total
6+
2 tests total

examples/stdlib/resizable_array/resizable_array.effekt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import test
44
def main() = {
55
suite("ResizableArrayTests", false) {
66
test("usage as stack") {
7+
with on[OutOfBounds].default { assertTrue(false, "out of bounds") }
78
val a = resizableArray()
89
a.add(1)
910
a.add(1)
@@ -26,6 +27,19 @@ def main() = {
2627
assert(a.popRight(), 1)
2728
assert(a.popRight(), 1)
2829
}
30+
test("out of bounds check") {
31+
def checkOutOfBounds(arr: ResizableArray[Int], index: Int) = {
32+
with on[OutOfBounds].default { () }
33+
arr.get(index)
34+
assertTrue(false, "not out of bounds")
35+
}
36+
with on[OutOfBounds].default { assertTrue(false, "out of bounds") }
37+
val a = resizableArray()
38+
a.checkOutOfBounds(-1)
39+
a.add(1)
40+
a.add(2)
41+
a.checkOutOfBounds(2)
42+
}
2943
};
3044
()
3145
}

libraries/common/heap.effekt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ namespace internal {
7373
///
7474
/// O(log n) worst case if capacity suffices, O(1) average
7575
def insert[T](heap: Heap[T], value: T): Unit = {
76+
with on[OutOfBounds].panic();
7677
val idx = heap.rawContents.add(value)
7778
internal::bubbleUp(heap, idx)
7879
}
@@ -81,15 +82,15 @@ def insert[T](heap: Heap[T], value: T): Unit = {
8182
/// panics when heap is empty
8283
///
8384
/// O(1)
84-
def findMin[T](heap: Heap[T]): T = {
85+
def findMin[T](heap: Heap[T]): T / Exception[OutOfBounds] = {
8586
heap.rawContents.get(0)
8687
}
8788

8889
/// find and remove the minimal element in this heap
8990
/// panics when heap is empty
9091
///
9192
/// O(log n)
92-
def deleteMin[T](heap: Heap[T]): T = {
93+
def deleteMin[T](heap: Heap[T]): T / Exception[OutOfBounds] = {
9394
val arr = heap.rawContents
9495
val res = arr.get(0)
9596
if (arr.size == 1) {

libraries/common/resizable_array.effekt

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,37 @@ def resizableArray[T](capacity: Int): ResizableArray[T] = {
2424
ResizableArray(ref(0), ref(unsafeAllocate(capacity)))
2525
}
2626

27-
/// Aallocatellocate a new, empty dynamic array
27+
/// Aallocate a new, empty dynamic array
2828
def resizableArray[T](): ResizableArray[T] = resizableArray(8)
2929

30-
/// Panics if index is not a valid index into arr
31-
def boundsCheck[T](arr: ResizableArray[T], index: Int): Unit = {
30+
31+
/// Throw an OutOfBounds exception if index is not a valid index into arr
32+
def boundsCheck[T](arr: ResizableArray[T], index: Int): Unit / Exception[OutOfBounds] =
3233
if (index < 0 || index >= arr.size) {
33-
panic("Array index out of bounds")
34+
do raise(OutOfBounds(), "Array index out of bounds: " ++ show(index))
3435
}
35-
}
3636

3737

3838
/// get the element at position index in the array
3939
///
4040
/// O(1)
41-
def get[T](arr: ResizableArray[T], index: Int): T = {
41+
def get[T](arr: ResizableArray[T], index: Int): T / Exception[OutOfBounds] = {
4242
arr.boundsCheck(index);
4343
arr.rawContentPtr.get.get(index)
4444
}
4545

4646
/// set the element at position index in the array
4747
///
4848
/// O(1)
49-
def set[T](arr: ResizableArray[T], index: Int, value: T): Unit = {
49+
def set[T](arr: ResizableArray[T], index: Int, value: T): Unit / Exception[OutOfBounds] = {
5050
arr.boundsCheck(index);
5151
arr.rawContentPtr.get.set(index, value)
5252
}
5353

5454
/// swap the elements at the given positions in the array
5555
///
5656
/// O(1)
57-
def swap[T](arr: ResizableArray[T], index1: Int, index2: Int): Unit = {
57+
def swap[T](arr: ResizableArray[T], index1: Int, index2: Int): Unit / Exception[OutOfBounds] = {
5858
arr.boundsCheck(max(index1, index2))
5959
val raw = arr.rawContentPtr.get
6060
val tmp = raw.get(index1)
@@ -66,9 +66,9 @@ def swap[T](arr: ResizableArray[T], index1: Int, index2: Int): Unit = {
6666
/// This only changes the size of the backing array, not the `size`.
6767
///
6868
/// O(n)
69-
def setCapacity[T](arr: ResizableArray[T], capacity: Int): Unit = {
69+
def setCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutOfBounds] = {
7070
if (arr.size > capacity) {
71-
panic("Cannot change capacity of ResizableArray.")
71+
do raise(OutOfBounds(), "Cannot change capacity of ResizableArray to " ++ capacity.show ++ " below size " ++ arr.size.show)
7272
}
7373
val oldRaw = arr.rawContentPtr.get
7474
if (oldRaw.size != capacity) {
@@ -81,7 +81,7 @@ def setCapacity[T](arr: ResizableArray[T], capacity: Int): Unit = {
8181
/// If the shrinkThreshold is reached, shrink by growFactor, otherwise do nothing
8282
///
8383
/// O(n)
84-
def maybeShrink[T](arr: ResizableArray[T]): Unit = {
84+
def maybeShrink[T](arr: ResizableArray[T]): Unit / Exception[OutOfBounds] = {
8585
if(arr.size.toDouble < arr.rawContentPtr.get.size.toDouble * shrinkThreshold) {
8686
val newCap = max(arr.size, (arr.rawContentPtr.get.size.toDouble / growFactor).ceil)
8787
arr.setCapacity(newCap)
@@ -91,7 +91,7 @@ def maybeShrink[T](arr: ResizableArray[T]): Unit = {
9191
/// makes sure capacity is at least the given one
9292
///
9393
/// O(given capacity - current capacity) amortized, O(n) worst case // TODO ?
94-
def ensureCapacity[T](arr: ResizableArray[T], capacity: Int): Unit = {
94+
def ensureCapacity[T](arr: ResizableArray[T], capacity: Int): Unit / Exception[OutOfBounds] = {
9595
if (arr.rawContentPtr.get.size < capacity) {
9696
val curCapd: Double = arr.rawContentPtr.get.size.toDouble
9797
val minGrowCapacity: Int = (curCapd * growFactor + 1.0).toInt
@@ -104,7 +104,13 @@ def ensureCapacity[T](arr: ResizableArray[T], capacity: Int): Unit = {
104104
/// Note: New elements might be uninitialized!!!
105105
///
106106
/// O(max(1,index - n)) amortized, O(n) worst case if index > capacity
107-
def setResizing[T](arr: ResizableArray[T], index: Int, value: T): Unit = {
107+
def setResizing[T](arr: ResizableArray[T], index: Int, value: T): Unit / Exception[OutOfBounds] = {
108+
if (index < 0) {
109+
do raise(OutOfBounds(), "Negative index " ++ index.show)
110+
}
111+
if (index < arr.size) {
112+
arr.rawContentPtr.get.set(index, value)
113+
}
108114
ensureCapacity(arr, index + 1)
109115
arr.rawContentPtr.get.set(index, value)
110116
if (index >= arr.size) arr.rawSizePtr.set(index + 1)
@@ -115,6 +121,7 @@ def setResizing[T](arr: ResizableArray[T], index: Int, value: T): Unit = {
115121
///
116122
/// O(1) amortized, O(n) worst case
117123
def add[T](arr: ResizableArray[T], value: T): Int = {
124+
with on[OutOfBounds].panic();
118125
val idx = arr.size
119126
arr.setResizing(idx, value)
120127
idx
@@ -123,7 +130,7 @@ def add[T](arr: ResizableArray[T], value: T): Int = {
123130
/// Remove and return the rightmost element in the resizable array.
124131
///
125132
/// O(1) amortized, O(n) worst case
126-
def popRight[T](arr: ResizableArray[T]): T = {
133+
def popRight[T](arr: ResizableArray[T]): T / Exception[OutOfBounds] = {
127134
val last = arr.size - 1
128135
arr.boundsCheck(last)
129136
val r = arr.rawContentPtr.get.get(last)
@@ -187,7 +194,7 @@ def foreachReversed[T](arr: ResizableArray[T]){ body: T => Unit }: Unit = {
187194
def toList[T](arr: ResizableArray[T]): List[T] = {
188195
def go(i: Int, acc: List[T]): List[T] = {
189196
if (i < 0) { acc } else {
190-
go(i - 1, Cons(arr.get(i), acc))
197+
go(i - 1, Cons(arr.rawContentPtr.get.get(i), acc))
191198
}
192199
}
193200
go(arr.size - 1, Nil())

0 commit comments

Comments
 (0)