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

Port 'string-lessp' to Rust #217

Merged
merged 15 commits into from Jul 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions rust_src/remacs-sys/lib.rs
Expand Up @@ -665,6 +665,27 @@ pub struct Lisp_String {
pub data: *mut libc::c_char,
}

#[repr(C)]
pub union SymbolUnion {
pub value: Lisp_Object,
pub alias: *mut Lisp_Symbol,
pub blv: *mut libc::c_void, // @TODO implement Lisp_Buffer_Local_Value
pub fwd: *mut libc::c_void, // @TODO implement Lisp_Fwd
}

/// This struct has 4 bytes of padding, representing the bitfield that
/// lives at the top of a Lisp_Symbol. The first 10 bits of this field are
/// used
#[repr(C)]
pub struct Lisp_Symbol {
pub symbol_bitfield: u32,
pub name: Lisp_Object,
pub val: SymbolUnion,
pub function: Lisp_Object,
pub plist: Lisp_Object,
pub next: *mut Lisp_Symbol,
}

extern "C" {
pub static mut globals: emacs_globals;
pub static Qt: Lisp_Object;
Expand All @@ -675,6 +696,7 @@ extern "C" {
pub static Qintegerp: Lisp_Object;
pub static Qfloatp: Lisp_Object;
pub static Qstringp: Lisp_Object;
pub static Qsymbolp: Lisp_Object;
pub static Qlistp: Lisp_Object;
pub static Qmarkerp: Lisp_Object;
pub static Qwholenump: Lisp_Object;
Expand Down Expand Up @@ -707,6 +729,7 @@ extern "C" {
pub static Qfont_spec: Lisp_Object;
pub static Qfont_entity: Lisp_Object;
pub static Qfont_object: Lisp_Object;
pub static lispsym: Lisp_Symbol;

pub fn Fcons(car: Lisp_Object, cdr: Lisp_Object) -> Lisp_Object;
pub fn Fcurrent_buffer() -> Lisp_Object;
Expand Down
2 changes: 2 additions & 0 deletions rust_src/src/lib.rs
Expand Up @@ -84,6 +84,7 @@ pub use strings::Fstring_as_multibyte;
pub use strings::Fstring_to_multibyte;
pub use strings::Fstring_to_unibyte;
pub use strings::Fmultibyte_string_p;
pub use strings::Fstring_lessp;
pub use vectors::Flength;
pub use vectors::Fsort;
pub use lists::merge;
Expand Down Expand Up @@ -180,6 +181,7 @@ pub extern "C" fn rust_init_syms() {
defsubr(&*strings::Sstring_as_multibyte);
defsubr(&*strings::Sstring_to_multibyte);
defsubr(&*strings::Sstring_to_unibyte);
defsubr(&*strings::Sstring_lessp);
defsubr(&*character::Smax_char);
defsubr(&*character::Scharacterp);
defsubr(&*character::Schar_or_string_p);
Expand Down
53 changes: 49 additions & 4 deletions rust_src/src/lisp.rs
Expand Up @@ -14,14 +14,15 @@ use libc::{c_void, intptr_t};

use marker::{LispMarker, marker_position};
use multibyte::{LispStringRef, MAX_CHAR};
use symbols::LispSymbolRef;
use vectors::LispVectorlikeRef;
use buffers::LispBufferRef;

use remacs_sys::{EmacsInt, EmacsUint, EmacsDouble, EMACS_INT_MAX, EMACS_INT_SIZE,
EMACS_FLOAT_SIZE, USE_LSB_TAG, GCTYPEBITS, wrong_type_argument, Qstringp,
Qnumber_or_marker_p, Qt, make_float, Qlistp, Qintegerp, Qconsp, circular_list,
internal_equal, Fcons, CHECK_IMPURE, Qnumberp, Qfloatp, Qwholenump, Qvectorp,
SYMBOL_NAME, PseudovecType};
Qsymbolp, Qnumber_or_marker_p, Qt, make_float, Qlistp, Qintegerp, Qconsp,
circular_list, internal_equal, Fcons, CHECK_IMPURE, Qnumberp, Qfloatp,
Qwholenump, Qvectorp, SYMBOL_NAME, PseudovecType, lispsym};
use remacs_sys::Lisp_Object as CLisp_Object;

// TODO: tweak Makefile to rebuild C files if this changes.
Expand Down Expand Up @@ -150,11 +151,55 @@ impl LispObject {
}

// Symbol support (LispType == Lisp_Symbol == 0)

impl LispObject {
#[inline]
pub fn is_symbol(self) -> bool {
self.get_type() == LispType::Lisp_Symbol
}

#[inline]
pub fn as_symbol(&self) -> Option<LispSymbolRef> {
if self.is_symbol() {
Some(LispSymbolRef::new(
unsafe { mem::transmute(self.symbol_ptr_value()) },
))
} else {
None
}
}

#[inline]
pub fn as_symbol_or_error(&self) -> LispSymbolRef {
if self.is_symbol() {
LispSymbolRef::new(unsafe { mem::transmute(self.symbol_ptr_value()) })
} else {
unsafe { wrong_type_argument(Qsymbolp, self.to_raw()) }
}
}

#[inline]
pub fn symbol_or_string_as_string(string: LispObject) -> LispStringRef {
match string.as_symbol() {
Some(sym) => {
sym.symbol_name().as_string().expect(
"Expected a symbol name?",
)
}
None => string.as_string_or_error(),
}
}

#[inline]
fn symbol_ptr_value(&self) -> EmacsInt {
let ptr_value = if USE_LSB_TAG {
self.to_raw() as EmacsInt
} else {
self.get_untaggedptr() as EmacsInt
};

let lispsym_offset = unsafe { &lispsym as *const _ as EmacsInt };
ptr_value + lispsym_offset
}
}

// Misc support (LispType == Lisp_Misc == 1)
Expand Down
55 changes: 54 additions & 1 deletion rust_src/src/multibyte.rs
Expand Up @@ -43,7 +43,7 @@ use remacs_sys::{CHAR_MODIFIER_MASK, CHAR_SHIFT, CHAR_CTL, emacs_abort, CHARACTE
pub type LispStringRef = ExternalPtr<Lisp_String>;

// cannot use `char`, it takes values out of its range
type Codepoint = u32;
pub type Codepoint = u32;

/// Maximum character code
pub const MAX_CHAR: Codepoint = (1 << CHARACTERBITS) - 1;
Expand Down Expand Up @@ -106,6 +106,59 @@ impl LispStringRef {
}
}

pub struct LispStringRefIterator<'a> {
string_ref: &'a LispStringRef,
cur: usize,
}

pub struct LispStringRefCharIterator<'a>(LispStringRefIterator<'a>);

// Substitute for FETCH_STRING_CHAR_ADVANCE
impl<'a> Iterator for LispStringRefIterator<'a> {
type Item = (usize, Codepoint);

fn next(&mut self) -> Option<(usize, Codepoint)> {
if self.cur < self.string_ref.len_bytes() as usize {
let codepoint: Codepoint;
let old_index = self.cur;
let ref_slice = self.string_ref.as_slice();
if self.string_ref.is_multibyte() {
let (cp, advance) = multibyte_char_at(&ref_slice[self.cur..]);
codepoint = cp;
self.cur += advance;
} else {
codepoint = ref_slice[self.cur] as Codepoint;
self.cur += 1;
}

Some((old_index, codepoint))
} else {
None
}
}
}

impl<'a> Iterator for LispStringRefCharIterator<'a> {
type Item = Codepoint;

fn next(&mut self) -> Option<Codepoint> {
self.0.next().map(|result| result.1)
}
}

impl LispStringRef {
pub fn char_indices(&self) -> LispStringRefIterator {
LispStringRefIterator {
string_ref: self,
cur: 0,
}
}

pub fn chars(&self) -> LispStringRefCharIterator {
LispStringRefCharIterator(self.char_indices())
}
}

fn string_overflow() -> ! {
unsafe { error("Maximum string size exceeded\0".as_ptr()) }
}
Expand Down
17 changes: 17 additions & 0 deletions rust_src/src/strings.rs
Expand Up @@ -202,6 +202,23 @@ fn string_to_unibyte(string: LispObject) -> LispObject {
}
}

/// Return non-nil if STRING1 is less than STRING2 in lexicographic order.
/// Case is significant.
#[lisp_fn]
fn string_lessp(string1: LispObject, string2: LispObject) -> LispObject {
let lispstr1 = LispObject::symbol_or_string_as_string(string1);
let lispstr2 = LispObject::symbol_or_string_as_string(string2);

let zip = lispstr1.chars().zip(lispstr2.chars());
for (codept1, codept2) in zip {
if codept1 != codept2 {
return LispObject::from_bool(codept1 < codept2);
}
}

LispObject::from_bool(lispstr1.len_chars() < lispstr2.len_chars())
}

/// Return t if OBJECT is a multibyte string.
/// Return nil if OBJECT is either a unibyte string, or not a string.
#[lisp_fn]
Expand Down
11 changes: 10 additions & 1 deletion rust_src/src/symbols.rs
@@ -1,5 +1,14 @@
use remacs_macros::lisp_fn;
use lisp::LispObject;
use lisp::{LispObject, ExternalPtr};
use remacs_sys::Lisp_Symbol;

pub type LispSymbolRef = ExternalPtr<Lisp_Symbol>;

impl LispSymbolRef {
pub fn symbol_name(&self) -> LispObject {
LispObject::from_raw(self.name)
}
}

/// Return t if OBJECT is a symbol.
#[lisp_fn]
Expand Down
38 changes: 0 additions & 38 deletions src/fns.c
Expand Up @@ -167,43 +167,6 @@ If string STR1 is greater, the value is a positive number N;
return Qt;
}

DEFUN ("string-lessp", Fstring_lessp, Sstring_lessp, 2, 2, 0,
doc: /* Return non-nil if STRING1 is less than STRING2 in lexicographic order.
Case is significant.
Symbols are also allowed; their print names are used instead. */)
(register Lisp_Object string1, Lisp_Object string2)
{
register ptrdiff_t end;
register ptrdiff_t i1, i1_byte, i2, i2_byte;

if (SYMBOLP (string1))
string1 = SYMBOL_NAME (string1);
if (SYMBOLP (string2))
string2 = SYMBOL_NAME (string2);
CHECK_STRING (string1);
CHECK_STRING (string2);

i1 = i1_byte = i2 = i2_byte = 0;

end = SCHARS (string1);
if (end > SCHARS (string2))
end = SCHARS (string2);

while (i1 < end)
{
/* When we find a mismatch, we must compare the
characters, not just the bytes. */
int c1, c2;

FETCH_STRING_CHAR_ADVANCE (c1, string1, i1, i1_byte);
FETCH_STRING_CHAR_ADVANCE (c2, string2, i2, i2_byte);

if (c1 != c2)
return c1 < c2 ? Qt : Qnil;
}
return i1 < SCHARS (string2) ? Qt : Qnil;
}

DEFUN ("string-version-lessp", Fstring_version_lessp,
Sstring_version_lessp, 2, 2, 0,
doc: /* Return non-nil if S1 is less than S2, as version strings.
Expand Down Expand Up @@ -4009,7 +3972,6 @@ this variable. */);
defsubr (&Sidentity);
defsubr (&Srandom);
defsubr (&Scompare_strings);
defsubr (&Sstring_lessp);
defsubr (&Sstring_version_lessp);
defsubr (&Sstring_collate_lessp);
defsubr (&Sstring_collate_equalp);
Expand Down