Skip to content

Commit

Permalink
api: add Arbitrary impl for CString
Browse files Browse the repository at this point in the history
We add a little sophistication here for whether the CString is
completely valid UTF-8 or whether it's just an arbitrary mix of bytes.
(Excluding NUL of course.)

Fixes #165, Closes #257
  • Loading branch information
ThomasdenH authored and BurntSushi committed Dec 27, 2020
1 parent 481c0c1 commit b2a6ba6
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 7 deletions.
50 changes: 43 additions & 7 deletions src/arbitrary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::{
BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque,
};
use std::env;
use std::ffi::OsString;
use std::ffi::{CString, OsString};
use std::hash::{BuildHasher, Hash};
use std::iter::{empty, once};
use std::net::{
Expand Down Expand Up @@ -277,7 +277,7 @@ impl<A: Arbitrary> Arbitrary for Vec<A> {
let s = g.size();
g.gen_range(0, s)
};
(0..size).map(|_| Arbitrary::arbitrary(g)).collect()
(0..size).map(|_| A::arbitrary(g)).collect()
}

fn shrink(&self) -> Box<dyn Iterator<Item = Vec<A>>> {
Expand Down Expand Up @@ -598,11 +598,7 @@ impl Arbitrary for String {
let s = g.size();
g.gen_range(0, s)
};
let mut s = String::with_capacity(size);
for _ in 0..size {
s.push(char::arbitrary(g));
}
s
(0..size).map(|_| char::arbitrary(g)).collect()
}

fn shrink(&self) -> Box<dyn Iterator<Item = String>> {
Expand All @@ -612,6 +608,46 @@ impl Arbitrary for String {
}
}

impl Arbitrary for CString {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let size = {
let s = g.size();
g.gen_range(0, s)
};
// Use either random bytes or random UTF-8 encoded codepoints.
let utf8: bool = g.gen();
if utf8 {
CString::new(
(0..)
.map(|_| char::arbitrary(g))
.filter(|&c| c != '\0')
.take(size)
.collect::<String>(),
)
} else {
CString::new(
(0..)
.map(|_| u8::arbitrary(g))
.filter(|&c| c != b'\0')
.take(size)
.collect::<Vec<u8>>(),
)
}
.expect("null characters should have been filtered out")
}

fn shrink(&self) -> Box<dyn Iterator<Item = CString>> {
// Use the implementation for a vec here, but make sure null characters
// are filtered out.
Box::new(VecShrinker::new(self.as_bytes().to_vec()).map(|bytes| {
CString::new(
bytes.into_iter().filter(|&c| c != 0).collect::<Vec<u8>>(),
)
.expect("null characters should have been filtered out")
}))
}
}

impl Arbitrary for char {
fn arbitrary<G: Gen>(g: &mut G) -> char {
let mode = g.gen_range(0, 100);
Expand Down
5 changes: 5 additions & 0 deletions src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::cmp::Ord;
use std::collections::hash_map::DefaultHasher;
use std::collections::{HashMap, HashSet};
use std::ffi::CString;
use std::hash::BuildHasherDefault;
use std::path::PathBuf;

Expand Down Expand Up @@ -286,4 +287,8 @@ quickcheck! {
) -> bool {
true
}

fn cstring(_p: CString) -> bool {
true
}
}

0 comments on commit b2a6ba6

Please sign in to comment.