Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Congyu WANG authored and Congyu WANG committed Mar 21, 2024
1 parent c054aa4 commit c32a24c
Showing 1 changed file with 22 additions and 111 deletions.
133 changes: 22 additions & 111 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,126 +1,37 @@
# Intrusive Circular Doubly Linked List in Rust
# Intrusive Circular Linked List in Rust

Learning how to do cdlist in Rust:
Let the elements hold the ownership, instead of the collection.
## Introduction

## Usage
The `cdlist` crate implements a non-thread-safe, intrusive, doubly-linked list in Rust. Its primary characteristic is the inclusion of link pointers within the data structures themselves, rather than in separate node wrappers. This approach enhances memory and performance efficiency but requires careful handling of ownership and safety, which this crate has taken care of.

See tests as examples.
## Characteristics

```rust
use cdlist::LinkNode;
- **Intrusive Design**: Nodes contain links to their neighbors, reducing overhead.
- **Non-Thread-Safe**: Optimized for single-threaded environments, avoiding the complexity and overhead of synchronization.
- **Self-Ownership**: Nodes own their data and their position within the list. Dropping a data automatically delists it.
- **Memory Safety**: Utilizes pinning to maintain the integrity of self-references within nodes, ensuring safe usage of the data structure.

#[test]
fn deref_mut() {
let mut node0 = LinkNode::new(1);
let node1 = LinkNode::new(2);
*node0 += *node1;
assert_eq!(3, *node0);
}

#[test]
fn iter_single() {
let node0 = LinkNode::new(0);
assert_eq!(collect(&node0), vec![0]);
}
## Example Usage

#[test]
fn iter() {
let mut nodes = (0..10).map(LinkNode::new).collect::<Vec<_>>();
connect_all(&mut nodes, 0, 10);
assert_eq!(collect(&nodes[0]), (0..10).collect::<Vec<_>>());
assert_eq!(collect_rev(&nodes[9]), (0..10).rev().collect::<Vec<_>>());
}

#[test]
fn iter_mut() {
let mut nodes = (0..10).map(LinkNode::new).collect::<Vec<_>>();
connect_all(&mut nodes, 0, 10);
let mut j = 0;
nodes[5].for_each_mut(|i| {
*i += j;
j += 1;
});
j = 0;
assert_eq!(collect(&nodes[0]), vec![5, 7, 9, 11, 13, 5, 7, 9, 11, 13]);
nodes[9].for_each_mut_rev(|i| {
*i += j;
j += 1;
});
assert_eq!(
collect(&nodes[0]),
vec![14, 15, 16, 17, 18, 9, 10, 11, 12, 13]
);
}
```rust
use cdlist::LinkNode;

#[test]
fn pop_self() {
let mut node0 = LinkNode::new(0);
node0.take();
assert_eq!(collect(&node0), vec![0]);
}
fn main() {
let mut node1 = LinkNode::new(1);
let mut node2 = LinkNode::new(2);

#[test]
fn requeue() {
let mut n0 = LinkNode::new(0);
let mut n1 = LinkNode::new(1);
let mut n2 = LinkNode::new(2);
n0.add(&mut n1);
n1.add(&mut n2);
assert_eq!(collect(&n0), vec![0, 1, 2]);
n2.add(&mut n1);
assert_eq!(collect(&n0), vec![0, 2, 1]);
assert_eq!(collect(&n2), vec![2, 1, 0]);
}
node1.add(&mut node2); // Adds node2 after node1

#[test]
fn take() {
let mut nodes = (0..10).map(LinkNode::new).collect::<Vec<_>>();
connect_all(&mut nodes, 0, 10);
assert_eq!(collect(&nodes[0]), (0..10).collect::<Vec<_>>());
let to_take = [0, 2, 4, 6, 8];
for i in to_take {
nodes[i].take();
}
for i in to_take {
assert_eq!(collect(&nodes[i]), vec![i]);
}
assert_eq!(collect(&nodes[1]), vec![1, 3, 5, 7, 9]);
node1.for_each(|&data| println!("{}", data)); // Prints: 1 2
}
```

#[test]
fn add() {
let mut nodes = (0..10).map(LinkNode::new).collect::<Vec<_>>();
connect_all(&mut nodes, 0, 5);
connect_all(&mut nodes, 5, 10);
assert_eq!(collect(&nodes[0]), (0..5).collect::<Vec<_>>());
assert_eq!(collect(&nodes[5]), (5..10).collect::<Vec<_>>());
let (n0, n1) = nodes.split_at_mut(5);
n0[2].add(&mut n1[2]);
assert_eq!(collect(&nodes[0]), vec![0, 1, 2, 7, 3, 4]);
assert_eq!(collect_rev(&nodes[4]), vec![4, 3, 7, 2, 1, 0]);
assert_eq!(collect(&nodes[5]), vec![5, 6, 8, 9]);
assert_eq!(collect_rev(&nodes[9]), vec![9, 8, 6, 5]);
}
## Implementation Insights

// helper functions
- **Pinning**: Nodes are pinned (`Pin<Box<Inner<T>>>`) to prevent invalidation of references due to memory movement, crucial for the safety of self-referential structures.

fn collect<T: Copy>(node: &LinkNode<T>) -> Vec<T> {
let mut vec = vec![];
node.for_each(|&i| vec.push(i));
vec
}
## Next Steps

fn collect_rev<T: Copy>(node: &LinkNode<T>) -> Vec<T> {
let mut vec = vec![];
node.for_each_rev(|&i| vec.push(i));
vec
}
To really make this crate useful, it needs to allow multi-threading, which can be enabled behind a feature flag. Though, this would involves a lot of work to ensure racing-conditions are handled correctly.

fn connect_all<T>(nodes: &mut [LinkNode<T>], start: usize, end: usize) {
for i in start..(end - 1) {
let (ni, nj) = nodes[i..].split_at_mut(1);
ni[0].add(&mut nj[0])
}
}
```
This crate is mainly a learning exercise.

0 comments on commit c32a24c

Please sign in to comment.