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

WIP: Implement base64 encode/decode #136

Merged
merged 3 commits into from Feb 12, 2017
Jump to file or symbol
Failed to load files and symbols.
+175 −265
Diff settings

Always

Just for now

Copy path View file
@@ -4,6 +4,13 @@ Remacs should generally behave identically to GNU Emacs. If you find a
difference, please
[file a bug](https://github.com/Wilfred/remacs/issues/new).
## Differences
- `base64-encode-string` and `base64-decode-string` does not fail
on multibyte strings.
- `base64-encode-region` and `base64-decode-region` does not fail
on multibyte regions.
## Detecting Remacs
You can detect if your current Emacs instance is Remacs by running the
@@ -14,4 +21,3 @@ following code:
```
This will return `t` in a Remacs instance.
Copy path View file

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
Copy path View file
@@ -11,6 +11,7 @@ build = "build.rs"
[dependencies]
lazy_static = "0.2.2"
libc = "0.2.17"
rustc-serialize = "0.3"
# Only want this local crate as dependency on Mac OS X
[target.'cfg(target_os = "macos")'.dependencies]
Copy path View file
@@ -0,0 +1,149 @@
extern crate libc;
extern crate rustc_serialize;
use std::ffi::CStr;
use std::ffi::CString;
use std::ptr;
use std::slice;
use strings::{MIME_LINE_LENGTH};
use self::rustc_serialize::base64::{FromBase64, ToBase64, STANDARD};
// We don't use the length arg as CStr::from_ptr handles checking the
// length for us. We don't use the multibyte arg either as we are able
// to encode multibyte strings. They need to still be there because of
// the C API.
#[no_mangle]
#[allow(unused_variables)]
pub extern "C" fn base64_encode_1(from: *const libc::c_char,
to: *mut libc::c_char,
length: libc::ptrdiff_t,
line_break: bool,
multibyte: bool)
-> libc::ptrdiff_t {
let bytes = unsafe { slice::from_raw_parts(from as *const u8, length as usize) };
let coded = bytes.to_base64(STANDARD);
let size = coded.len() as libc::ptrdiff_t;
let string = CString::new(coded).unwrap();
if line_break && MIME_LINE_LENGTH < size {
// If we want to break lines at every MIME_LINE_LENGTH, and
// the line is longer, then
let bytes = string.into_bytes();
let mut n = 0;
let mut sink = to;
// split the byte slice into chunks the size of
// MIME_LINE_LENGTH.
for chunk in bytes.chunks(MIME_LINE_LENGTH as usize) {
let l = chunk.len();
let c = chunk.as_ptr();
unsafe {
ptr::copy(c as *const libc::c_char, sink, l);
sink = sink.offset(l as isize);
// Add a newline after the current chunk.
*sink = b'\n' as libc::c_char;
sink = sink.offset(1);
}
n = n + 1;
}
size + n
} else {
unsafe {
ptr::copy(string.into_raw(), to, size as usize);
}
size
}
}
fn decode(encoded: &CStr) -> Result<Vec<u8>, String> {
encoded.to_str()
.map_err(|err| err.to_string())
.and_then(|encoded| encoded.from_base64()
.map_err(|err| err.to_string()))
}
/// Base64-decode the data at FROM of LENGTH bytes into TO. If
/// MULTIBYTE, the decoded result should be in multibyte form. If
/// NCHARS_RETURN is not NULL, store the number of produced
/// characters in *NCHARS_RETURN.
// We don't use the length arg as CStr::from_ptr handles the length
// for us. It still needs to be there to conform to the C API.
#[no_mangle]
#[allow(unused_variables)]
pub extern "C" fn base64_decode_1(from: *const libc::c_char,
to: *mut libc::c_char,
length: libc::ptrdiff_t,
multibyte: bool,
nchars_return: *mut libc::ptrdiff_t)
-> libc::ptrdiff_t {
let encoded = unsafe { CStr::from_ptr(from) };
match decode(encoded) {
Ok(decoded) => {
let size = decoded.len() as libc::ptrdiff_t;
let d = decoded.as_ptr() as *const libc::c_char;
unsafe {
if !multibyte && !nchars_return.is_null() {
*nchars_return = size
} else if !nchars_return.is_null() {
match String::from_utf8(decoded) {
Ok(s) => {
*nchars_return = s.chars().count() as libc::ptrdiff_t;
},
Err(_) => { *nchars_return = size },
}
}
ptr::copy(d, to, size as usize);
size
}
},
Err(_) => -1,
}
}
#[test]
fn test_base64_encode_1() {
let input = CString::new("hello world").unwrap();
let uncoded: *const libc::c_char = input.as_ptr();
let mut encoded = [0; 17];
let length = base64_encode_1(uncoded,
encoded.as_mut_ptr(),
input.as_bytes().len() as libc::ptrdiff_t,
false,
false);
assert!(length != -1);
let answer = unsafe { CStr::from_ptr(encoded.as_ptr()).to_str().unwrap() };
assert_eq!(answer.len(), length as usize);
assert_eq!("aGVsbG8gd29ybGQ=", answer);
}
#[test]
fn test_base64_decode_1() {
let input = CString::new("aGVsbG8gd29ybGQ=").unwrap();
let encoded: *const libc::c_char = input.as_ptr();
let mut decoded = [0; 20];
let mut n: isize = 0;
let nchars: *mut isize = &mut n;
let length = base64_decode_1(encoded,
decoded.as_mut_ptr(),
input.as_bytes().len() as libc::ptrdiff_t,
true,
nchars);
assert!(length != -1);
let answer = unsafe { CStr::from_ptr(decoded.as_ptr()).to_str().unwrap() };
assert_eq!(n, length);
assert_eq!("hello world", answer);
}
Copy path View file
@@ -23,9 +23,13 @@ mod strings;
mod symbols;
mod globals;
mod character;
mod base64;
use lisp::LispSubr;
pub use base64::base64_encode_1;
pub use base64::base64_decode_1;
// These need to be exported as bytecode.c depends upon them.
pub use math::Fplus;
pub use math::Fminus;
Copy path View file
@@ -7,7 +7,7 @@ use lisp::{LispObject, LispSubr, Qnil, SBYTES, SSDATA, STRING_MULTIBYTE, STRINGP
use lists::NILP;
extern "C" {
fn make_unibyte_string(s: *const libc::c_char, length: libc::ptrdiff_t) -> LispObject;
fn make_string(s: *const libc::c_char, length: libc::ptrdiff_t) -> LispObject;
fn base64_encode_1(from: *const libc::c_char,
to: *mut libc::c_char,
length: libc::ptrdiff_t,
@@ -23,7 +23,7 @@ extern "C" {
fn error(m: *const u8, ...);
}
static MIME_LINE_LENGTH: isize = 76;
pub static MIME_LINE_LENGTH: isize = 76;
fn Fstringp(object: LispObject) -> LispObject {
LispObject::from_bool(object.is_string())
@@ -97,7 +97,7 @@ fn Fbase64_encode_string(string: LispObject, noLineBreak: LispObject) -> LispObj
error("Multibyte character in data for base64 encoding\0".as_ptr());
}
make_unibyte_string(encoded, encodedLength)
make_string(encoded, encodedLength)
}
}
@@ -122,16 +122,13 @@ fn Fbase64_decode_string(string: LispObject) -> LispObject {
unsafe {
let decoded = buffer.as_mut_ptr();
let decoded_length = base64_decode_1(SSDATA(string),
decoded,
length,
false,
ptr::null_mut());
let decoded_length =
base64_decode_1(SSDATA(string), decoded, length, false, ptr::null_mut());
if decoded_length > length {
panic!("Decoded length is above length");
} else if decoded_length >= 0 {
decoded_string = make_unibyte_string(decoded, decoded_length);
decoded_string = make_string(decoded, decoded_length);
}
if !STRINGP(decoded_string) {
Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.