Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move libfsm/start.c to Rust #344

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8d24c4f
Initial import for Cargo.
katef Apr 20, 2021
a643036
Special cases for tests with their own build stuff.
katef Apr 20, 2021
00c33df
An attempt to bring in Cargo-generated dependencies.
katef Apr 20, 2021
9772089
Run cargo test.
katef Apr 20, 2021
6e05435
Switch to `staticlib` rather than symlinking for an archive.
katef Apr 20, 2021
34e8b49
Remove support for building shared libraries.
katef Apr 20, 2021
b572d09
Add `-undefined dynamic_lookup` for symbols provided by Cargo-built l…
katef Apr 20, 2021
bb83fd4
Switch to ld -r linking the cargo-generated .a archive when producing…
katef Apr 20, 2021
d0be59a
Replace bitfields in struct fsm_state and struct fsm with bool
federicomenaquintero Apr 20, 2021
8fb687b
Start sketching types for State and Fsm
federicomenaquintero Apr 20, 2021
b4a98c2
Move all of start.c to Rust
federicomenaquintero Apr 20, 2021
4277bb4
Remove example test from lib.rs
federicomenaquintero Apr 20, 2021
918798e
Hack in -lpthread -ldl everywhere needed
federicomenaquintero Apr 20, 2021
84d60c7
Move end.c to Rust
federicomenaquintero Apr 20, 2021
701f0fc
cargo fmt
federicomenaquintero Apr 20, 2021
d2aab46
fsm_addstate(): initialize all fields in the new fsm_state
federicomenaquintero Apr 23, 2021
036d1eb
fsm_addstate_bulk(): Initialize all the fields in the new states
federicomenaquintero Apr 23, 2021
ce58305
state.c: Extract function to initialize a state
federicomenaquintero Apr 23, 2021
73cdd10
fsm_addstate(): no need to initialize has_capture_actions in the real…
federicomenaquintero Apr 23, 2021
8a5468c
fsm_addstate(): Remove non-working test for statecount == -1
federicomenaquintero Apr 23, 2021
840b847
fsm_capture_init(): Initialize up to fsm->statecount fields, not the …
federicomenaquintero Apr 23, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
build/
/build
/target
Cargo.lock
14 changes: 14 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "libfsm-rs"
version = "0.1.0"
authors = ["Kate F <kate@elide.org>"]
edition = "2018"

[lib]
crate-type = ["staticlib"]
path = "src/libfsm/lib.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libc = "0.2"
22 changes: 21 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ INCDIR += include
.include <obj.mk>
.include <dep.mk>
.include <ar.mk>
.include <so.mk>
.include <part.mk>
.include <prog.mk>
.include <mkdir.mk>
Expand All @@ -110,3 +109,24 @@ STAGE_BUILD := ${STAGE_BUILD:Nbin/cvtpcre}
grep FAIL ${BUILD}/tests/*/res*; [ $$? -ne 0 ]
.endif

.if !${CC:T:Memcc*}

./target/debug/liblibfsm_rs.a:
cargo build

test::
cargo test

./target/debug/liblibfsm_rs.d: ./target/debug/liblibfsm_rs.a
.if exists(./target/debug/liblibfsm_rs.d)
.include "./target/debug/liblibfsm_rs.d"
.endif

.for part in ${PART}
${BUILD}/lib/${part}.o: ./target/debug/liblibfsm_rs.a
# hijacking LDRFLAGS here because the target only expects .o sources
LDRFLAGS.${part} += ./target/debug/liblibfsm_rs.a
.endfor

.endif

2 changes: 2 additions & 0 deletions src/fsm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ LDD_VER!= \
LFLAGS.fsm += -lrt
.endif

LFLAGS.fsm += -lpthread -ldl

.endif

.endif
Expand Down
2 changes: 0 additions & 2 deletions src/libfsm/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ SRC += src/libfsm/clone.c
SRC += src/libfsm/closure.c
SRC += src/libfsm/edge.c
SRC += src/libfsm/empty.c
SRC += src/libfsm/end.c
SRC += src/libfsm/endids.c
SRC += src/libfsm/equal.c
SRC += src/libfsm/exec.c
SRC += src/libfsm/fsm.c
SRC += src/libfsm/mode.c
SRC += src/libfsm/start.c
SRC += src/libfsm/state.c
SRC += src/libfsm/trim.c
SRC += src/libfsm/example.c
Expand Down
2 changes: 1 addition & 1 deletion src/libfsm/capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fsm_capture_init(struct fsm *fsm)
}
fsm->capture_info = ci;

for (i = 0; i < fsm->statealloc; i++) {
for (i = 0; i < fsm->statecount; i++) {
fsm->states[i].has_capture_actions = 0;
}

Expand Down
42 changes: 0 additions & 42 deletions src/libfsm/end.c

This file was deleted.

4 changes: 4 additions & 0 deletions src/libfsm/fsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "internal.h"
#include "capture.h"
#include "endids.h"
#include "libfsm_rs.h"

void
free_contents(struct fsm *fsm)
Expand All @@ -47,6 +48,9 @@ fsm_new(const struct fsm_options *opt)
static const struct fsm_options defaults;
struct fsm *new, f;

/* just to prove linking works */
fsm_noop();

if (opt == NULL) {
opt = &defaults;
}
Expand Down
243 changes: 243 additions & 0 deletions src/libfsm/fsm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
//! Representation of a Finite State Machine.

use libc::c_void;
use std::slice;

// Keep in sync with include/fsm.h:fsm_state_t
type StateId = u32;

// Opaque pointer to struct edget_set
type EdgeSet = *mut c_void;

// Opaque pointer to struct state_set
type StateSet = *mut c_void;

// Opaque pointer to struct fsm_capture_info
type CaptureInfo = *mut c_void;

// Opaque pointer to struct endid_info
type EndIdInfo = *mut c_void;

// Opaque pointer to struct fsm_options
type Options = *const c_void;

const ENDCOUNT_MAX: usize = usize::MAX;

/// One state in a `Fsm`'s array of states.
// Keep in sync with interanl.h:struct fsm_state
#[repr(C)]
struct State {
end: bool,

/// If false, then this state has no need for checking the fsm->capture_info struct.
has_capture_actions: bool,

/// Meaningful within one particular transformation only.
visited: bool,

edges: EdgeSet,
epsilons: StateSet,
}

/// Finite State Machine.
// Keep in sync with interanl.h:struct fsm
#[repr(C)]
pub struct Fsm {
/// Array.
states: *mut State,

/// Number of elements allocated.
statealloc: usize,

/// Number of elements populated.
statecount: usize,

endcount: usize,

start: StateId,
hasstart: bool,

capture_info: CaptureInfo,
endid_info: EndIdInfo,
opt: Options,
}

impl Fsm {
pub fn clear_start(&mut self) {
self.hasstart = false;
}

pub fn set_start(&mut self, state: StateId) {
assert!((state as usize) < self.statecount);

self.start = state;
self.hasstart = true;
}

pub fn get_start(&self) -> Option<StateId> {
if self.hasstart {
assert!((self.start as usize) < self.statecount);
Some(self.start)
} else {
None
}
}

fn states_as_mut_slice(&self) -> &mut [State] {
unsafe { slice::from_raw_parts_mut(self.states, self.statecount) }
}

pub fn set_end(&mut self, state: StateId, end: bool) {
assert!((state as usize) < self.statecount);

{
// Temporary scope so the `s` mutable borrow terminates before
// we frob `self.endcount` below.

let states = self.states_as_mut_slice();
let s = &mut states[state as usize];

if s.end == end {
return;
} else {
s.end = end;
}
}

if end {
assert!(self.endcount < ENDCOUNT_MAX);
self.endcount += 1;
} else {
assert!(self.endcount > 0);
self.endcount -= 1;
}
}
}

#[no_mangle]
pub unsafe fn fsm_clearstart(fsm: *mut Fsm) {
assert!(!fsm.is_null());
let fsm = &mut *fsm;

fsm.clear_start();
}

#[no_mangle]
pub unsafe fn fsm_setstart(fsm: *mut Fsm, state: StateId) {
assert!(!fsm.is_null());
let fsm = &mut *fsm;

fsm.set_start(state);
}

#[no_mangle]
pub unsafe fn fsm_getstart(fsm: *const Fsm, out_start: *mut StateId) -> i32 {
assert!(!fsm.is_null());
assert!(!out_start.is_null());

let fsm = &*fsm;
match fsm.get_start() {
Some(id) => {
*out_start = id;
1
}

None => 0,
}
}

#[no_mangle]
pub unsafe fn fsm_setend(fsm: *mut Fsm, state: StateId, end: i32) {
assert!(!fsm.is_null());
let fsm = &mut *fsm;

let end = if end != 0 { true } else { false };

fsm.set_end(state, end)
}

#[cfg(test)]
mod tests {
use super::*;
use std::ptr;

// Just here until we can construct Fsm in Rust
fn dummy_fsm_new() -> Fsm {
Fsm {
states: ptr::null_mut(),
statealloc: 0,
statecount: 0,
endcount: 0,
start: 0,
hasstart: false,
capture_info: ptr::null_mut(),
endid_info: ptr::null_mut(),
opt: ptr::null(),
}
}

#[test]
fn sets_and_clears_start() {
let mut fsm = dummy_fsm_new();

// FIXME: this sets up an inconsistent state, but we need it to
// test the assertion inside fsm.set_start().
fsm.statecount = 1;

fsm.set_start(0);
assert_eq!(fsm.get_start(), Some(0));

fsm.clear_start();
assert!(fsm.get_start().is_none())
}

#[test]
fn sets_and_clears_start_from_c() {
let mut fsm = dummy_fsm_new();
let fsm_ptr = &mut fsm as *mut _;

// FIXME: this sets up an inconsistent state, but we need it to
// test the assertion inside fsm_setstart().
fsm.statecount = 1;

unsafe {
let mut n = 0;

fsm_setstart(fsm_ptr, 0);
assert_eq!(fsm_getstart(fsm_ptr, &mut n), 1);
assert_eq!(n, 0);

fsm_clearstart(fsm_ptr);
assert_eq!(fsm_getstart(fsm_ptr, &mut n), 0);
}
}

#[test]
fn set_end_works() {
let mut fsm = dummy_fsm_new();

fn make_state() -> State {
State {
end: false,
has_capture_actions: false,
visited: false,
edges: ptr::null_mut(),
epsilons: ptr::null_mut(),
}
}

let mut states = vec![make_state(), make_state(), make_state()];

fsm.states = states.as_mut_ptr();
fsm.statecount = 3;
fsm.statealloc = 3;

fsm.set_end(1, true); // yay aliased mutability
assert_eq!(fsm.endcount, 1);
assert!(states[1].end);

fsm.set_end(1, false);
assert_eq!(fsm.endcount, 0);
assert!(!states[1].end);
}
}
Loading