Skip to content

Commit b78c7be

Browse files
refactor(deque): consolidate index operation proofs
1 parent 5636c96 commit b78c7be

4 files changed

Lines changed: 421 additions & 427 deletions

File tree

deque/deque.mbt

Lines changed: 78 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -15,73 +15,16 @@
1515
///|
1616
fn[T] set_null(buffer : UninitializedArray[T], index : Int) = "%fixedarray.set_null"
1717

18-
///|
19-
/// The deque representation invariant guarantees these indices are in bounds.
20-
/// Keep these private so callers cannot bypass bounds checks.
21-
#inline
22-
fn[T] unsafe_buffer_get(buffer : UninitializedArray[T], index : Int) -> T = "%fixedarray.unsafe_get"
23-
24-
///|
25-
#inline
26-
fn[T] unsafe_buffer_set(
27-
buffer : UninitializedArray[T],
28-
index : Int,
29-
value : T,
30-
) -> Unit = "%fixedarray.unsafe_set"
31-
3218
///|
3319
fn[A] new_deque(capacity : Int) -> Deque[A] {
3420
{ buf: UninitializedArray::make(capacity), len: 0, head: 0 }
3521
}
3622

37-
///|
38-
/// Wraps an in-buffer offset without division. Comparing the offset with the
39-
/// contiguous space first also avoids overflowing `head + offset`.
40-
#inline
41-
fn wrap_index(
42-
head : Int,
43-
offset : Int,
44-
capacity : Int,
45-
) -> Int where {
46-
proof_require: wrap_index_pre(head, offset, capacity),
47-
proof_ensure: result => wrap_index_post(head, offset, capacity, result),
48-
proof_ensure: result => wrap_index_mod_equiv(head, offset, capacity, result),
49-
} {
50-
let contiguous = capacity - head
51-
if offset < contiguous {
52-
proof_assert (head + offset) % capacity == head + offset
53-
head + offset
54-
} else {
55-
proof_assert (head + offset) % capacity == offset - contiguous
56-
offset - contiguous
57-
}
58-
}
59-
60-
///|
61-
/// Moves an in-buffer index back by one without division.
62-
#inline
63-
fn decrement_index(
64-
index : Int,
65-
capacity : Int,
66-
) -> Int where {
67-
proof_require: decrement_index_pre(index, capacity),
68-
proof_ensure: result => decrement_index_post(index, capacity, result),
69-
proof_ensure: result => decrement_index_mod_equiv(index, capacity, result),
70-
} {
71-
if index == 0 {
72-
proof_assert (index + capacity - 1) % capacity == capacity - 1
73-
capacity - 1
74-
} else {
75-
proof_assert (index + capacity - 1) % capacity == index - 1
76-
index - 1
77-
}
78-
}
79-
8023
///|
8124
/// Computes the tail index (index of last element) on demand.
8225
/// Only valid when len > 0.
8326
fn[A] Deque::tail_index(self : Deque[A]) -> Int {
84-
wrap_index(self.head, self.len - 1, self.buf.length())
27+
deque_tail_index(self.head, self.len, self.buf.length())
8528
}
8629

8730
///|
@@ -343,15 +286,15 @@ pub fn[A] Deque::blit_to(
343286
}
344287
// Copy in reverse order
345288
for i in len>..0 {
346-
let dst_idx = wrap_index(dst.head, dst_offset + i, dst.buf.length())
347-
let src_idx = wrap_index(self.head, src_offset + i, self.buf.length())
348-
unsafe_buffer_set(dst.buf, dst_idx, unsafe_buffer_get(self.buf, src_idx))
289+
let dst_idx = (dst.head + dst_offset + i) % dst.buf.length()
290+
let src_idx = (self.head + src_offset + i) % self.buf.length()
291+
dst.buf[dst_idx] = self.buf[src_idx]
349292
}
350293
} else {
351294
for i in 0..<len {
352-
let dst_idx = wrap_index(dst.head, dst_offset + i, dst.buf.length())
353-
let src_idx = wrap_index(self.head, src_offset + i, self.buf.length())
354-
unsafe_buffer_set(dst.buf, dst_idx, unsafe_buffer_get(self.buf, src_idx))
295+
let dst_idx = (dst.head + dst_offset + i) % dst.buf.length()
296+
let src_idx = (self.head + src_offset + i) % self.buf.length()
297+
dst.buf[dst_idx] = self.buf[src_idx]
355298
if dst_offset + i >= dst_len {
356299
dst.len += 1
357300
}
@@ -416,13 +359,9 @@ pub fn[A] Deque::append(self : Deque[A], other : Deque[A]) -> Unit {
416359
let cap = self.buf.length()
417360
// Use captured state to read from other, avoiding aliasing issues
418361
for i in 0..<other_len {
419-
let read_idx = wrap_index(other_head, i, other_buf_len)
420-
let write_idx = wrap_index(self.head, self.len, cap)
421-
unsafe_buffer_set(
422-
self.buf,
423-
write_idx,
424-
unsafe_buffer_get(other_buf, read_idx),
425-
)
362+
let read_idx = (other_head + i) % other_buf_len
363+
let write_idx = (self.head + self.len) % cap
364+
self.buf[write_idx] = other_buf[read_idx]
426365
self.len += 1
427366
}
428367
}
@@ -483,22 +422,22 @@ pub fn[A] Deque::insert(self : Deque[A], index : Int, value : A) -> Unit {
483422
let cap = self.buf.length()
484423
if index < self.len / 2 {
485424
// Shift front elements left
486-
let new_head = decrement_index(self.head, cap)
425+
let new_head = (self.head - 1 + cap) % cap
487426
for i in 0..<index {
488-
let to = wrap_index(new_head, i, cap)
489-
let from = wrap_index(self.head, i, cap)
490-
unsafe_buffer_set(self.buf, to, unsafe_buffer_get(self.buf, from))
427+
let to = (new_head + i) % cap
428+
let from = (self.head + i) % cap
429+
self.buf[to] = self.buf[from]
491430
}
492431
self.head = new_head
493432
} else {
494433
// Shift back elements right
495434
for i = self.len; i > index; i = i - 1 {
496-
let from = wrap_index(self.head, i - 1, cap)
497-
let to = wrap_index(self.head, i, cap)
498-
unsafe_buffer_set(self.buf, to, unsafe_buffer_get(self.buf, from))
435+
let from = (self.head + i - 1) % cap
436+
let to = (self.head + i) % cap
437+
self.buf[to] = self.buf[from]
499438
}
500439
}
501-
unsafe_buffer_set(self.buf, wrap_index(self.head, index, cap), value)
440+
self.buf[(self.head + index) % cap] = value
502441
self.len += 1
503442
}
504443

@@ -559,21 +498,21 @@ pub fn[A] Deque::remove(self : Deque[A], index : Int) -> A {
559498
let cap = self.buf.length()
560499
if index < self.len / 2 {
561500
// Shift front elements right
562-
let new_head = wrap_index(self.head, 1, cap)
501+
let new_head = (self.head + 1) % cap
563502
for i in index>..0 {
564-
let to = wrap_index(self.head, i + 1, cap)
565-
let from = wrap_index(self.head, i, cap)
566-
unsafe_buffer_set(self.buf, to, unsafe_buffer_get(self.buf, from))
503+
let to = (self.head + i + 1) % cap
504+
let from = (self.head + i) % cap
505+
self.buf[to] = self.buf[from]
567506
}
568507
set_null(self.buf, self.head)
569508
self.head = new_head
570509
} else {
571510
// Shift back elements left
572-
let tail_idx = wrap_index(self.head, self.len - 1, cap)
511+
let tail_idx = (self.head + self.len - 1) % cap
573512
for i in (index + 1)..<self.len {
574-
let to = wrap_index(self.head, i - 1, cap)
575-
let from = wrap_index(self.head, i, cap)
576-
unsafe_buffer_set(self.buf, to, unsafe_buffer_get(self.buf, from))
513+
let to = (self.head + i - 1) % cap
514+
let from = (self.head + i) % cap
515+
self.buf[to] = self.buf[from]
577516
}
578517
set_null(self.buf, tail_idx)
579518
}
@@ -637,7 +576,9 @@ pub fn[A] Deque::capacity(self : Deque[A]) -> Int {
637576
/// Reallocate the deque with a new capacity.
638577
fn[A] Deque::realloc(self : Deque[A]) -> Unit {
639578
let old_cap = self.buf.length()
640-
let new_cap = if old_cap == 0 { 8 } else { old_cap * 2 }
579+
// Doubling a larger capacity would overflow `Int`.
580+
guard old_cap <= 0x3fff_ffff else { abort("Deque capacity overflow") }
581+
let new_cap = deque_realloc_capacity(old_cap, self.len)
641582
let new_buf = self.unsafe_make_and_blit_to(new_cap, 0)
642583
self.head = 0
643584
self.buf = new_buf
@@ -657,7 +598,8 @@ pub fn[A] Deque::front(self : Deque[A]) -> A? {
657598
if self.len == 0 {
658599
None
659600
} else {
660-
Some(unsafe_buffer_get(self.buf, self.head))
601+
let index = deque_element_index(self.head, self.len, self.buf.length(), 0)
602+
Some(self.buf.unsafe_get(index))
661603
}
662604
}
663605

@@ -675,7 +617,13 @@ pub fn[A] Deque::back(self : Deque[A]) -> A? {
675617
if self.len == 0 {
676618
None
677619
} else {
678-
Some(unsafe_buffer_get(self.buf, self.tail_index()))
620+
let index = deque_element_index(
621+
self.head,
622+
self.len,
623+
self.buf.length(),
624+
self.len - 1,
625+
)
626+
Some(self.buf.unsafe_get(index))
679627
}
680628
}
681629

@@ -696,9 +644,9 @@ pub fn[A] Deque::push_front(self : Deque[A], value : A) -> Unit {
696644
if self.len == self.buf.length() {
697645
self.realloc()
698646
}
699-
let cap = self.buf.length()
700-
self.head = decrement_index(self.head, cap)
701-
unsafe_buffer_set(self.buf, self.head, value)
647+
let new_head = deque_push_front_core(self.head, self.len, self.buf.length())
648+
self.buf.unsafe_set(new_head, value)
649+
self.head = new_head
702650
self.len += 1
703651
}
704652

@@ -719,9 +667,8 @@ pub fn[A] Deque::push_back(self : Deque[A], value : A) -> Unit {
719667
if self.len == self.buf.length() {
720668
self.realloc()
721669
}
722-
let cap = self.buf.length()
723-
let write_idx = wrap_index(self.head, self.len, cap)
724-
unsafe_buffer_set(self.buf, write_idx, value)
670+
let write_idx = deque_push_back_core(self.head, self.len, self.buf.length())
671+
self.buf.unsafe_set(write_idx, value)
725672
self.len += 1
726673
}
727674

@@ -741,9 +688,9 @@ pub fn[A] Deque::push_back(self : Deque[A], value : A) -> Unit {
741688
#alias(pop_front_exn, deprecated)
742689
pub fn[A] Deque::unsafe_pop_front(self : Deque[A]) -> Unit {
743690
guard self.len > 0 else { abort("The deque is empty!") }
691+
let new_head = deque_pop_front_core(self.head, self.len, self.buf.length())
744692
set_null(self.buf, self.head)
745-
let cap = self.buf.length()
746-
self.head = wrap_index(self.head, 1, cap)
693+
self.head = new_head
747694
self.len -= 1
748695
}
749696

@@ -794,7 +741,7 @@ test "unsafe_pop_front after many push_front" {
794741
#alias(pop_back_exn, deprecated)
795742
pub fn[A] Deque::unsafe_pop_back(self : Deque[A]) -> Unit {
796743
guard self.len > 0 else { abort("The deque is empty!") }
797-
let tail_idx = self.tail_index()
744+
let tail_idx = deque_pop_back_core(self.head, self.len, self.buf.length())
798745
set_null(self.buf, tail_idx)
799746
self.len -= 1
800747
}
@@ -832,10 +779,10 @@ pub fn[A] Deque::unsafe_pop_back(self : Deque[A]) -> Unit {
832779
/// ```
833780
pub fn[A] Deque::pop_front(self : Deque[A]) -> A? {
834781
guard self.len > 0 else { return None }
835-
let value = unsafe_buffer_get(self.buf, self.head)
782+
let new_head = deque_pop_front_core(self.head, self.len, self.buf.length())
783+
let value = self.buf.unsafe_get(self.head)
836784
set_null(self.buf, self.head)
837-
let cap = self.buf.length()
838-
self.head = wrap_index(self.head, 1, cap)
785+
self.head = new_head
839786
self.len -= 1
840787
Some(value)
841788
}
@@ -852,8 +799,8 @@ pub fn[A] Deque::pop_front(self : Deque[A]) -> A? {
852799
/// ```
853800
pub fn[A] Deque::pop_back(self : Deque[A]) -> A? {
854801
guard self.len > 0 else { return None }
855-
let tail_idx = self.tail_index()
856-
let value = unsafe_buffer_get(self.buf, tail_idx)
802+
let tail_idx = deque_pop_back_core(self.head, self.len, self.buf.length())
803+
let value = self.buf.unsafe_get(tail_idx)
857804
set_null(self.buf, tail_idx)
858805
self.len -= 1
859806
Some(value)
@@ -876,8 +823,13 @@ pub fn[A] Deque::at(self : Deque[A], index : Int) -> A {
876823
if index < 0 || index >= self.len {
877824
index_out_of_bounds(self.len, index)
878825
}
879-
let physical_index = wrap_index(self.head, index, self.buf.length())
880-
unsafe_buffer_get(self.buf, physical_index)
826+
let physical_index = deque_element_index(
827+
self.head,
828+
self.len,
829+
self.buf.length(),
830+
index,
831+
)
832+
self.buf.unsafe_get(physical_index)
881833
}
882834

883835
///|
@@ -898,8 +850,13 @@ pub fn[A] Deque::set(self : Deque[A], index : Int, value : A) -> Unit {
898850
if index < 0 || index >= self.len {
899851
index_out_of_bounds(self.len, index)
900852
}
901-
let physical_index = wrap_index(self.head, index, self.buf.length())
902-
unsafe_buffer_set(self.buf, physical_index, value)
853+
let physical_index = deque_element_index(
854+
self.head,
855+
self.len,
856+
self.buf.length(),
857+
index,
858+
)
859+
self.buf.unsafe_set(physical_index, value)
903860
}
904861

905862
///|
@@ -2367,8 +2324,13 @@ pub fn[A] Deque::binary_search_by(
23672324
/// Safe element access with bounds checking
23682325
pub fn[A] Deque::get(self : Deque[A], index : Int) -> A? {
23692326
if index >= 0 && index < self.len {
2370-
let physical_index = wrap_index(self.head, index, self.buf.length())
2371-
Some(unsafe_buffer_get(self.buf, physical_index))
2327+
let physical_index = deque_element_index(
2328+
self.head,
2329+
self.len,
2330+
self.buf.length(),
2331+
index,
2332+
)
2333+
Some(self.buf.unsafe_get(physical_index))
23722334
} else {
23732335
None
23742336
}
@@ -2490,12 +2452,10 @@ pub fn[A] Deque::rev_in_place(self : Deque[A]) -> Unit {
24902452
guard self.len > 0 else { return }
24912453
let cap = self.buf.length()
24922454
for _ in 0..<(self.len / 2); left = self.head, right = self.tail_index() {
2493-
let temp = unsafe_buffer_get(self.buf, left)
2494-
unsafe_buffer_set(self.buf, left, unsafe_buffer_get(self.buf, right))
2495-
unsafe_buffer_set(self.buf, right, temp)
2496-
let left = wrap_index(left, 1, cap)
2497-
let right = decrement_index(right, cap)
2498-
continue left, right
2455+
let temp = self.buf[left]
2456+
self.buf[left] = self.buf[right]
2457+
self.buf[right] = temp
2458+
continue (left + 1) % cap, (right - 1 + cap) % cap
24992459
}
25002460
}
25012461

@@ -2533,8 +2493,8 @@ pub fn[A] Deque::rev(self : Deque[A]) -> Deque[A] {
25332493
let new_buf = UninitializedArray::make(len)
25342494
// Copy elements in reverse order
25352495
for i in 0..<len {
2536-
let src_idx = wrap_index(self.head, len - i - 1, self.buf.length())
2537-
unsafe_buffer_set(new_buf, i, unsafe_buffer_get(self.buf, src_idx))
2496+
let src_idx = (self.head + len - i - 1) % self.buf.length()
2497+
new_buf[i] = self.buf[src_idx]
25382498
}
25392499
// Create new deque with reversed elements
25402500
{ buf: new_buf, len, head: 0 }

0 commit comments

Comments
 (0)