Skip to content

Commit

Permalink
Add custom Drop implementation for Cons
Browse files Browse the repository at this point in the history
This avoids stack overflow when dropping long lists. Closes #104.
  • Loading branch information
Andreas Rottmann committed Jun 17, 2024
1 parent 7d54176 commit 4bd7a55
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 2 deletions.
6 changes: 6 additions & 0 deletions lexpr/NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ New features:
allow parsing files produced by recent KiCad versions, thus closing
#64.

Fixes:

- The `Cons` type now has a custom `Drop` implementation which avoids
recursion on the "cdr" field. This allows for dropping long lists
without overflowing the stack (#104).

Changes:

- The `sexp!` macro is now only included when specifying the
Expand Down
27 changes: 25 additions & 2 deletions lexpr/src/cons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,18 @@ impl Cons {
/// assert_eq!(car, "a");
/// assert_eq!(cdr, 42);
/// ```
pub fn into_pair(self) -> (Value, Value) {
(self.inner.0, self.inner.1)
pub fn into_pair(mut self) -> (Value, Value) {
(
std::mem::replace(&mut self.inner.0, Value::Nil),
std::mem::replace(&mut self.inner.1, Value::Nil),
)
}

fn take(&mut self) -> Cons {
Self::new(
std::mem::replace(&mut self.inner.0, Value::Nil),
std::mem::replace(&mut self.inner.1, Value::Nil),
)
}

/// Obtains an iterator yielding references to all the cons cells in this
Expand Down Expand Up @@ -214,6 +224,19 @@ impl IntoIterator for Cons {
}
}

impl Drop for Cons {
fn drop(&mut self) {
match self.cdr() {
Value::Cons(cell) if cell.cdr().is_cons() => {}
_ => return,
}
let mut cell = self.take();
while let Some(cdr) = cell.cdr_mut().as_cons_mut() {
cell = cdr.take();
}
}
}

impl<'a> IntoIterator for &'a Cons {
type Item = &'a Cons;
type IntoIter = Iter<'a>;
Expand Down
7 changes: 7 additions & 0 deletions lexpr/src/value/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![cfg_attr(tarpaulin, skip)]

use std::iter;

use crate::{Cons, Number, Value};

type Predicate = fn(&Value) -> bool;
Expand Down Expand Up @@ -136,3 +138,8 @@ fn test_vectors() {
assert_eq!(v.as_slice(), Some(elts.as_slice()));
}
}

#[test]
fn drop_long_list() {
let _long = Value::list(iter::repeat(Value::from(42)).take(1_000_000));
}

0 comments on commit 4bd7a55

Please sign in to comment.