Skip to content

Commit

Permalink
Add experimental feature flagged no_std support
Browse files Browse the repository at this point in the history
The default use_std feature flag controls whether or
not to compile in #![no_std] mode.

The alloc feature flag must be specified and the
toolchain fixed to nightly-2018-03-07 for the no_std
experimental mode to operate, due to the core::io
handling.
  • Loading branch information
ZackPierce committed May 2, 2018
1 parent 16fd30f commit 4cfcb8e
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.*.swp
.idea
doc
tags
examples/ss10pusa.csv
Expand Down
13 changes: 11 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@ name = "aho_corasick"

[[bin]]
name = "aho-corasick-dot"
path = "src/main.rs"
path = "src/bin/aho-corasick-dot.rs"
test = false
doc = false
bench = false

[dependencies]
memchr = "2"
cfg-if = "0.1"
memchr = { version = "2", default-features = false }
# TODO - move to a non-specific-nightly-version-linked core io alternative
core_io = { version = "0.1.20180307", optional = true, default-features = false, features = ["collections"] }

[dev-dependencies]
csv = "1.0.0-beta.5"
Expand All @@ -34,6 +37,12 @@ quickcheck = { version = "0.6", default-features = false }
serde = "1"
serde_derive = "1"

[features]

default = ["use_std"]
use_std = ["memchr/use_std", "memchr/libc"]
alloc = ["core_io"]

[[bench]]
name = "bench"
path = "benches/bench.rs"
Expand Down
7 changes: 7 additions & 0 deletions examples/dict-search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ fn main() {
}
}
}
#[cfg(not(feature = "use_std"))]
fn run(_: &Args) -> Result<(), Box<Error>> {
Err("The use_std feature is mandatory for the dict-search example to work".into())
}

#[cfg(feature = "use_std")]
fn run(args: &Args) -> Result<(), Box<Error>> {
let aut = try!(build_automaton(&args.flag_dict, args.flag_min_len));
if args.flag_memory_usage {
Expand Down Expand Up @@ -110,6 +115,7 @@ fn run(args: &Args) -> Result<(), Box<Error>> {
Ok(())
}

#[cfg(feature = "use_std")]
fn write_matches<A, I>(aut: &A, it: I) -> Result<(), Box<Error>>
where A: Automaton<String>, I: Iterator<Item=io::Result<Match>> {
let mut wtr = csv::Writer::from_writer(io::stdout());
Expand All @@ -122,6 +128,7 @@ fn write_matches<A, I>(aut: &A, it: I) -> Result<(), Box<Error>>
Ok(())
}

#[cfg(feature = "use_std")]
fn build_automaton(
dict_path: &str,
min_len: usize,
Expand Down
37 changes: 23 additions & 14 deletions src/autiter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
use std::io::{self, BufRead};
use std::marker::PhantomData;
cfg_if! {

if #[cfg(feature = "use_std")] {
use std::marker::PhantomData;
use std::io::{BufRead, BufReader, Read, Result as IoResult};
} else {
use core::marker::PhantomData;
use io::{BufRead, BufReader, Read, Result as IoResult};
}
}


use memchr::{memchr, memchr2, memchr3};

Expand Down Expand Up @@ -77,29 +86,29 @@ pub trait Automaton<P> {
}

/// Returns an iterator of non-overlapping matches in the given reader.
fn stream_find<'a, R: io::Read>(
fn stream_find<'a, R: Read>(
&'a self,
rdr: R,
) -> StreamMatches<'a, R, P, Self>
where Self: Sized {
StreamMatches {
aut: self,
buf: io::BufReader::new(rdr),
buf: BufReader::new(rdr),
texti: 0,
si: ROOT_STATE,
_m: PhantomData,
}
}

/// Returns an iterator of overlapping matches in the given reader.
fn stream_find_overlapping<'a, R: io::Read>(
fn stream_find_overlapping<'a, R: Read>(
&'a self,
rdr: R,
) -> StreamMatchesOverlapping<'a, R, P, Self>
where Self: Sized {
StreamMatchesOverlapping {
aut: self,
buf: io::BufReader::new(rdr),
buf: BufReader::new(rdr),
texti: 0,
si: ROOT_STATE,
outi: 0,
Expand Down Expand Up @@ -364,17 +373,17 @@ impl<'a, 's, P, A: Automaton<P> + ?Sized> Iterator for Matches<'a, 's, P, A> {
#[derive(Debug)]
pub struct StreamMatches<'a, R, P, A: 'a + Automaton<P> + ?Sized> {
aut: &'a A,
buf: io::BufReader<R>,
buf: BufReader<R>,
texti: usize,
si: StateIdx,
_m: PhantomData<P>,
}

impl<'a, R: io::Read, P, A: Automaton<P>>
impl<'a, R: Read, P, A: Automaton<P>>
Iterator for StreamMatches<'a, R, P, A> {
type Item = io::Result<Match>;
type Item = IoResult<Match>;

fn next(&mut self) -> Option<io::Result<Match>> {
fn next(&mut self) -> Option<IoResult<Match>> {
let mut m = None;
let mut consumed = 0;
'LOOP: loop {
Expand Down Expand Up @@ -478,18 +487,18 @@ impl<'a, 's, P, A: Automaton<P> + ?Sized>
#[derive(Debug)]
pub struct StreamMatchesOverlapping<'a, R, P, A: 'a + Automaton<P> + ?Sized> {
aut: &'a A,
buf: io::BufReader<R>,
buf: BufReader<R>,
texti: usize,
si: StateIdx,
outi: usize,
_m: PhantomData<P>,
}

impl<'a, R: io::Read, P, A: Automaton<P> + ?Sized>
impl<'a, R: Read, P, A: Automaton<P> + ?Sized>
Iterator for StreamMatchesOverlapping<'a, R, P, A> {
type Item = io::Result<Match>;
type Item = IoResult<Match>;

fn next(&mut self) -> Option<io::Result<Match>> {
fn next(&mut self) -> Option<IoResult<Match>> {
if self.aut.has_match(self.si, self.outi) {
let m = self.aut.get_match(self.si, self.outi, self.texti);
self.outi += 1;
Expand Down
8 changes: 2 additions & 6 deletions src/main.rs → src/bin/aho-corasick-dot.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
extern crate memchr;

extern crate aho_corasick;
use std::env;

use lib::AcAutomaton;

#[allow(dead_code)]
mod lib;
use aho_corasick::AcAutomaton;

fn main() {
let aut = AcAutomaton::new(env::args().skip(1));
Expand Down
13 changes: 11 additions & 2 deletions src/full.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
use std::fmt;
use std::mem;
cfg_if! {
if #[cfg(feature = "use_std")] {
use std::vec::Vec;
use std::fmt;
use std::mem;
} else {
use alloc::vec::Vec;
use core::fmt;
use core::mem;
}
}

use super::{
FAIL_STATE,
Expand Down
76 changes: 65 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Finally, there are also methods for finding matches on *streams*. Namely, the
search text does not have to live in memory. It's useful to run this on files
that can't fit into memory:
```no_run
```ignore
use std::fs::File;
use aho_corasick::{Automaton, AcAutomaton};
Expand Down Expand Up @@ -119,15 +119,47 @@ assert_eq!(matches, vec![Match { pati: 1, start: 0, end: 1}]);
*/

#![deny(missing_docs)]
#![cfg_attr(not(feature = "use_std"), no_std)]
#![cfg_attr(not(feature = "use_std"), feature(alloc))]
#![cfg_attr(not(feature = "use_std"), feature(slice_concat_ext))]

#[macro_use]
extern crate cfg_if;

#[cfg(all(test, not(feature = "use_std")))]
#[macro_use]
extern crate std;

#[cfg(not(feature = "use_std"))]
#[macro_use]
extern crate alloc;

#[cfg(not(feature = "use_std"))]
extern crate core_io as io;

extern crate memchr;
#[cfg(test)]
extern crate quickcheck;

use std::collections::VecDeque;
use std::fmt;
use std::iter::FromIterator;
use std::mem;
cfg_if! {
if #[cfg(feature = "use_std")] {
use std::collections::VecDeque;
use std::string::String;
use std::vec::Vec;
use std::fmt;
use std::iter::FromIterator;
use std::mem;
} else {
use alloc::slice::SliceConcatExt;
use alloc::string::String;
use alloc::vec_deque::VecDeque;
use alloc::vec::Vec;
use core::fmt;
use core::iter::FromIterator;
use core::mem;
}
}


pub use self::autiter::{
Automaton, Match,
Expand Down Expand Up @@ -507,7 +539,10 @@ impl<S: AsRef<[u8]>> FromIterator<S> for AcAutomaton<S> {
impl<P: AsRef<[u8]> + fmt::Debug, T: Transitions>
fmt::Debug for AcAutomaton<P, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
#[cfg(feature = "use_std")]
use std::iter::repeat;
#[cfg(not(feature = "use_std"))]
use core::iter::repeat;

try!(writeln!(f, "{}", repeat('-').take(79).collect::<String>()));
try!(writeln!(f, "Patterns: {:?}", self.pats));
Expand All @@ -525,7 +560,10 @@ impl<T: Transitions> State<T> {
}

fn goto_string(&self, root: bool) -> String {
#[cfg(feature = "use_std")]
use std::char::from_u32;
#[cfg(not(feature = "use_std"))]
use core::char::from_u32;

let mut goto = vec![];
for b in (0..256).map(|b| b as u8) {
Expand All @@ -548,7 +586,10 @@ impl<T: Transitions> fmt::Debug for State<T> {
impl<T: Transitions> AcAutomaton<String, T> {
#[doc(hidden)]
pub fn dot(&self) -> String {
#[cfg(feature = "use_std")]
use std::fmt::Write;
#[cfg(not(feature = "use_std"))]
use core::fmt::Write;
let mut out = String::new();
macro_rules! w {
($w:expr, $($tt:tt)*) => { {write!($w, $($tt)*)}.unwrap() }
Expand Down Expand Up @@ -593,8 +634,20 @@ fn usize_bytes() -> usize {

#[cfg(test)]
mod tests {
use std::collections::HashSet;
use std::io;
cfg_if! {
if #[cfg(feature = "use_std")] {
use std::boxed::Box;
use std::collections::HashSet;
use std::string::String;
use std::vec::Vec;
use std::io::Cursor;
} else {
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use io::Cursor;
}
}

use quickcheck::{Arbitrary, Gen, quickcheck};

Expand All @@ -607,7 +660,7 @@ mod tests {

fn aut_finds<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
let cur = io::Cursor::new(haystack.as_bytes());
let cur = Cursor::new(haystack.as_bytes());
AcAutomaton::new(xs.to_vec())
.stream_find(cur).map(|r| r.unwrap()).collect()
}
Expand All @@ -619,7 +672,7 @@ mod tests {

fn aut_findfs<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
let cur = io::Cursor::new(haystack.as_bytes());
let cur = Cursor::new(haystack.as_bytes());
AcAutomaton::new(xs.to_vec())
.into_full()
.stream_find(cur).map(|r| r.unwrap()).collect()
Expand All @@ -632,7 +685,7 @@ mod tests {

fn aut_findos<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
let cur = io::Cursor::new(haystack.as_bytes());
let cur = Cursor::new(haystack.as_bytes());
AcAutomaton::new(xs.to_vec())
.stream_find_overlapping(cur).map(|r| r.unwrap()).collect()
}
Expand All @@ -645,7 +698,7 @@ mod tests {

fn aut_findfos<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
let cur = io::Cursor::new(haystack.as_bytes());
let cur = Cursor::new(haystack.as_bytes());
AcAutomaton::new(xs.to_vec())
.into_full()
.stream_find_overlapping(cur).map(|r| r.unwrap()).collect()
Expand Down Expand Up @@ -909,6 +962,7 @@ mod tests {
matches
}

#[cfg(feature = "use_std")]
#[test]
fn qc_ac_equals_naive() {
fn prop(needles: Vec<SmallAscii>, haystack: BiasAscii) -> bool {
Expand Down

0 comments on commit 4cfcb8e

Please sign in to comment.