Skip to content

Commit

Permalink
Refactored read_into and write_into; test examples in documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
TheVeryDarkness committed Sep 3, 2024
1 parent 5895885 commit 1c727bf
Show file tree
Hide file tree
Showing 43 changed files with 1,490 additions and 1,034 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

This library is mainly for some cases that need simple and fast I/O, such as online judge.

This library has an interface for inputs that is a bit similar to input streams in C++, and some default format for output.
This library has an interface for inputs that is a bit similar to input streams in C++ (thus is helpful if you're finding an I/O framework in Rust for online judge), and some default format for output.

See the [documentation](https://docs.rs/iof) for more details.

Expand Down
15 changes: 15 additions & 0 deletions examples/doc_get_line.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use iof::{get_line, read};

fn main() {
// Read a single string from input.
let s: String = read!();
assert_eq!(s, "42");

// Read a line of string from input.
let s: String = get_line();
assert_eq!(s, "");

// Read a line of string from input.
let s: String = get_line();
assert_eq!(s, "abc");
}
2 changes: 2 additions & 0 deletions examples/doc_get_line.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
42
abc
11 changes: 11 additions & 0 deletions examples/doc_get_line_some.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use iof::{get_line_some, read};

fn main() {
// Read a single string from input.
let s: String = read!();
assert_eq!(s, "42");

// Read a non-empty line of string from input.
let s: String = get_line_some();
assert_eq!(s, "abc");
}
2 changes: 2 additions & 0 deletions examples/doc_get_line_some.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
42
abc
44 changes: 44 additions & 0 deletions examples/doc_read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use iof::{read, Mat};

fn main() {
// Read a single integer from input.
let n: u32 = read!();
assert_eq!(n, 42);

// Read a single string from input.
let n: String = read!();
assert_eq!(n, "abc");

// Read a vector of characters from input.
let v: Vec<char> = read!();
assert_eq!(v, ['d', 'e', 'f']);

// Read a tuple from input.
// Equivalent to:
// let l: u32 = read!();
// let m: f64 = read!();
// let n: String = read!();
let (l, m, n): (u32, f64, String) = read!();
assert_eq!(l, 0);
assert_eq!(m, 0.3);
assert_eq!(n, "lmn");

// Read a vector of integers from input.
let v: Vec<u32> = read!(3);
assert_eq!(v, [1, 2, 3]);

// Read a matrix of integers from input.
let m: Mat<u32> = read!(2, 3);
assert_eq!(m, [[1, 2, 3], [4, 5, 6]]);

// Read a matrix of characters from input.
let m: Mat<char> = read!(3, 5);
assert_eq!(
m,
[
['.', '@', '/', '#', '$'],
['!', '@', '#', '!', '@'],
['*', '&', '@', ':', ',']
]
);
}
8 changes: 8 additions & 0 deletions examples/doc_read.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
42 abc def
0 0.3 lmn
1 2 3
1 2 3
4 5 6
.@/#$
!@#!@
*&@:,
4 changes: 2 additions & 2 deletions src/array/guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::mem::MaybeUninit;

/// Helper for initializing an array of `MaybeUninit<T>` elements.
///
/// Once dopped, initialized elements in the array are also dropped.
/// Once dropped, initialized elements in the array are also dropped.
/// Call [std::mem::forget] to drop the array without dropping the elements.
///
/// Borrow from the underlying implementation of [std::array::from_fn].
Expand Down Expand Up @@ -33,7 +33,7 @@ impl<'a, T, const N: usize> ArrayGuard<'a, T, N> {
Self { array, len: 0 }
}

/// Use [usize::unchecked_add] if it's stablized.
/// Use [usize::unchecked_add] if it's stabilized.
pub(crate) unsafe fn push_unchecked(&mut self, value: T) {
let _ = self.array.get_unchecked_mut(self.len).write(value);
// Safety: We just wrote to the array.
Expand Down
26 changes: 0 additions & 26 deletions src/array.rs → src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,6 @@ mod guard;
#[cfg(test)]
mod tests;

/// Create an array of `T` elements from a function that produces each element.
///
/// It's safe to pass a function that may panic, but the array will be dropped.
///
/// # Examples
///
/// ```rust,ignore
/// use iof::array_from_fn;
/// let array: [String; 3] = array_from_fn(|| "hello".to_string());
/// assert_eq!(array[0], "hello");
/// assert_eq!(array[1], "hello");
/// assert_eq!(array[2], "hello");
/// ```
pub fn array_from_fn<T, const N: usize>(mut f: impl FnMut() -> T) -> [T; N] {
let mut array: [MaybeUninit<T>; N] = from_fn(|_| MaybeUninit::uninit());
let mut guard = ArrayGuard::new(&mut array);
for _ in 0..N {
unsafe {
guard.push_unchecked(f());
}
}
forget(guard);
// Hope this is optimized well.
array.map(|x| unsafe { x.assume_init() })
}

/// Create an array of `T` elements from a function that produces each element with a possible error.
///
/// It's safe to pass a function that may panic, but the array will be dropped.
Expand Down
102 changes: 48 additions & 54 deletions src/array/tests.rs
Original file line number Diff line number Diff line change
@@ -1,77 +1,43 @@
mod tracked {
use std::sync::atomic::{AtomicUsize, Ordering};
mod tracked;

static COUNT: AtomicUsize = AtomicUsize::new(0);
pub struct Tracked(Box<usize>);
impl Tracked {
pub(super) fn new() -> Self {
loop {
let n = COUNT.load(Ordering::Relaxed);
if COUNT
.compare_exchange(n, n + 1, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
// Well, we shouldn't use println! without a lock in real code.
println!("Creating Tracked({})", n);
return Tracked(Box::new(n));
}
}
}
}
impl Drop for Tracked {
fn drop(&mut self) {
loop {
let n = COUNT.load(Ordering::Relaxed);
assert!(n > 0);
if COUNT
.compare_exchange(n, n - 1, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
break;
}
}
// Well, we shouldn't use println! without a lock in real code.
println!("Dropping Tracked({})", self.0);
}
}

/// Ensure all elements are dropped.
pub(super) fn check() {
assert_eq!(COUNT.load(Ordering::Relaxed), 0);
}
}

use super::array_from_fn;
use crate::array::array_try_from_fn;
use std::convert::Infallible;
use std::{panic::catch_unwind, thread::spawn};
use tracked::{check, Tracked};

#[test]
fn array_string() {
let mut i = 0;
let array: [String; 3] = array_from_fn(|| {
let array: [String; 3] = array_try_from_fn(|| -> Result<String, Infallible> {
i += 1;
i.to_string()
});
Ok(i.to_string())
})
.unwrap();
assert_eq!(array[0], "1");
assert_eq!(array[1], "2");
assert_eq!(array[2], "3");
}

#[test]
fn consecutive() {
array_tracked_caught_panic();
array_tracked()
}

fn array_tracked_caught_panic() {
let threads: Vec<_> = (0..16)
.map(|_| {
spawn(|| {
.map(|i| {
spawn(move || {
let res = catch_unwind(|| {
let mut i = 0;
let array: [Tracked; 64] = array_from_fn(|| {
if i >= 63 {
let mut j = 0;
let array: Result<[Tracked; 64], Infallible> = array_try_from_fn(|| {
if j >= 63 {
panic!("Sorry, something is wrong with the array.");
}
i += 1;
Tracked::new()
j += 1;
Ok(Tracked::new(i, j))
});
array
array.unwrap()
});
assert!(res.is_err());
})
Expand All @@ -84,3 +50,31 @@ fn array_tracked_caught_panic() {

check();
}

fn array_tracked() {
let threads: Vec<_> = (0..16)
.map(|i| {
spawn(move || {
let res = catch_unwind(|| {
let mut j = 0;
let array: Result<[Tracked; 64], &'static str> = array_try_from_fn(|| {
if j >= 63 {
Err("Sorry, something is wrong with the array.")?
}
j += 1;
Ok(Tracked::new(i, j))
});
array
})
.unwrap();
assert!(res.is_err());
})
})
.collect();

for t in threads {
t.join().unwrap();
}

check();
}
41 changes: 41 additions & 0 deletions src/array/tests/tracked.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use std::sync::atomic::{AtomicUsize, Ordering};

static COUNT: AtomicUsize = AtomicUsize::new(0);
pub struct Tracked(Box<(usize, usize, usize)>);
impl Tracked {
pub(super) fn new(i: usize, j: usize) -> Self {
loop {
let n = COUNT.load(Ordering::Relaxed);
if COUNT
.compare_exchange(n, n + 1, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
// Well, we shouldn't use println! without a lock in real code.
println!("Creating Tracked({n}) for ({i}, {j})");
return Tracked(Box::new((n, i, j)));
}
}
}
}
impl Drop for Tracked {
fn drop(&mut self) {
loop {
let n = COUNT.load(Ordering::Relaxed);
assert!(n > 0);
if COUNT
.compare_exchange(n, n - 1, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
break;
}
}
let (n, i, j) = *self.0;
// Well, we shouldn't use println! without a lock in real code.
println!("Dropping Tracked({n}) for ({i}, {j})");
}
}

/// Ensure all elements are dropped.
pub(super) fn check() {
assert_eq!(COUNT.load(Ordering::Relaxed), 0);
}
Loading

0 comments on commit 1c727bf

Please sign in to comment.