make rustfmt.toml consistent with editorconfig because of course rustfmt refuses to use the editorconfig

This commit is contained in:
Elijah R
2024-08-04 15:59:39 -04:00
parent 64d4774d00
commit b00c662b95
6 changed files with 262 additions and 259 deletions

3
cvm-rs/rustfmt.toml Normal file
View File

@@ -0,0 +1,3 @@
tab_spaces = 4
newline_style = "Unix"
hard_tabs = true

View File

@@ -8,30 +8,30 @@ pub type Elements = Vec<String>;
/// Errors during decoding /// Errors during decoding
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum DecodeError { pub enum DecodeError {
/// Invalid guacamole instruction format /// Invalid guacamole instruction format
InvalidFormat, InvalidFormat,
/// Instruction is too long for the current decode policy. /// Instruction is too long for the current decode policy.
InstructionTooLong, InstructionTooLong,
/// Element is too long for the current decode policy. /// Element is too long for the current decode policy.
ElementTooLong, ElementTooLong,
/// Invalid element size. /// Invalid element size.
ElementSizeInvalid, ElementSizeInvalid,
} }
pub type DecodeResult<T> = std::result::Result<T, DecodeError>; pub type DecodeResult<T> = std::result::Result<T, DecodeError>;
impl fmt::Display for DecodeError { impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::InvalidFormat => write!(f, "Invalid Guacamole instruction while decoding"), Self::InvalidFormat => write!(f, "Invalid Guacamole instruction while decoding"),
Self::InstructionTooLong => write!(f, "Instruction too long for current decode policy"), Self::InstructionTooLong => write!(f, "Instruction too long for current decode policy"),
Self::ElementTooLong => write!(f, "Element too long for current decode policy"), Self::ElementTooLong => write!(f, "Element too long for current decode policy"),
Self::ElementSizeInvalid => write!(f, "Element size is invalid"), Self::ElementSizeInvalid => write!(f, "Element size is invalid"),
} }
} }
} }
// this decode policy abstraction would in theory be useful, // this decode policy abstraction would in theory be useful,
@@ -40,13 +40,13 @@ impl fmt::Display for DecodeError {
pub struct StaticDecodePolicy<const INST_SIZE: usize, const ELEM_SIZE: usize>(); pub struct StaticDecodePolicy<const INST_SIZE: usize, const ELEM_SIZE: usize>();
impl<const INST_SIZE: usize, const ELEM_SIZE: usize> StaticDecodePolicy<INST_SIZE, ELEM_SIZE> { impl<const INST_SIZE: usize, const ELEM_SIZE: usize> StaticDecodePolicy<INST_SIZE, ELEM_SIZE> {
fn max_instruction_size(&self) -> usize { fn max_instruction_size(&self) -> usize {
INST_SIZE INST_SIZE
} }
fn max_element_size(&self) -> usize { fn max_element_size(&self) -> usize {
ELEM_SIZE ELEM_SIZE
} }
} }
/// The default decode policy. /// The default decode policy.
@@ -54,139 +54,139 @@ pub type DefaultDecodePolicy = StaticDecodePolicy<12288, 4096>;
/// Encodes elements into a Guacamole instruction /// Encodes elements into a Guacamole instruction
pub fn encode_instruction(elements: &Elements) -> String { pub fn encode_instruction(elements: &Elements) -> String {
let mut str = String::new(); let mut str = String::new();
for elem in elements.iter() { for elem in elements.iter() {
str.push_str(&format!("{}.{},", elem.len(), elem)); str.push_str(&format!("{}.{},", elem.len(), elem));
} }
// hacky, but whatever // hacky, but whatever
str.pop(); str.pop();
str.push(';'); str.push(';');
str str
} }
/// Decodes a Guacamole instruction to individual elements /// Decodes a Guacamole instruction to individual elements
pub fn decode_instruction(element_string: &String) -> DecodeResult<Elements> { pub fn decode_instruction(element_string: &String) -> DecodeResult<Elements> {
let policy = DefaultDecodePolicy {}; let policy = DefaultDecodePolicy {};
let mut vec: Elements = Vec::new(); let mut vec: Elements = Vec::new();
let mut current_position: usize = 0; let mut current_position: usize = 0;
// Instruction is too long. Don't even bother // Instruction is too long. Don't even bother
if policy.max_instruction_size() < element_string.len() { if policy.max_instruction_size() < element_string.len() {
return Err(DecodeError::InstructionTooLong); return Err(DecodeError::InstructionTooLong);
} }
let chars = element_string.chars().collect::<Vec<_>>(); let chars = element_string.chars().collect::<Vec<_>>();
loop { loop {
let mut element_size: usize = 0; let mut element_size: usize = 0;
// Scan the integer value in by hand. This is mostly because // Scan the integer value in by hand. This is mostly because
// I'm stupid, and the Rust integer parsing routines (seemingly) // I'm stupid, and the Rust integer parsing routines (seemingly)
// require a substring (or a slice, but, if you can generate a slice, // require a substring (or a slice, but, if you can generate a slice,
// you can also just scan the value in by hand.) // you can also just scan the value in by hand.)
// //
// We bound this anyways and do quite the checks, so even though it's not great, // We bound this anyways and do quite the checks, so even though it's not great,
// it should be generally fine (TM). // it should be generally fine (TM).
loop { loop {
let c = chars[current_position]; let c = chars[current_position];
if c >= '0' && c <= '9' { if c >= '0' && c <= '9' {
element_size = element_size * 10 + (c as usize) - ('0' as usize); element_size = element_size * 10 + (c as usize) - ('0' as usize);
} else { } else {
if c == '.' { if c == '.' {
break; break;
} }
return Err(DecodeError::InvalidFormat); return Err(DecodeError::InvalidFormat);
} }
current_position += 1; current_position += 1;
} }
// Eat the '.' seperating the size and the element data; // Eat the '.' seperating the size and the element data;
// our integer scanning ensures we only get here in the case that this is actually the '.' // our integer scanning ensures we only get here in the case that this is actually the '.'
// character. // character.
current_position += 1; current_position += 1;
// Make sure the element size doesn't overflow the decode policy // Make sure the element size doesn't overflow the decode policy
// or the size of the whole instruction. // or the size of the whole instruction.
if element_size >= policy.max_element_size() { if element_size >= policy.max_element_size() {
return Err(DecodeError::ElementTooLong); return Err(DecodeError::ElementTooLong);
} }
if element_size >= element_string.len() { if element_size >= element_string.len() {
return Err(DecodeError::ElementSizeInvalid); return Err(DecodeError::ElementSizeInvalid);
} }
// cutoff elements or something // cutoff elements or something
if current_position + element_size > chars.len() - 1 { if current_position + element_size > chars.len() - 1 {
//println!("? {current_position} a {}", chars.len()); //println!("? {current_position} a {}", chars.len());
return Err(DecodeError::InvalidFormat); return Err(DecodeError::InvalidFormat);
} }
let element = chars let element = chars
.iter() .iter()
.skip(current_position) .skip(current_position)
.take(element_size) .take(element_size)
.collect::<String>(); .collect::<String>();
current_position += element_size; current_position += element_size;
vec.push(element); vec.push(element);
// make sure seperator is proper // make sure seperator is proper
match chars[current_position] { match chars[current_position] {
',' => {} ',' => {}
';' => break, ';' => break,
_ => return Err(DecodeError::InvalidFormat), _ => return Err(DecodeError::InvalidFormat),
} }
// eat the ',' // eat the ','
current_position += 1; current_position += 1;
} }
Ok(vec) Ok(vec)
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn decode_basic() { fn decode_basic() {
let test = String::from("7.connect,3.vm1;"); let test = String::from("7.connect,3.vm1;");
let res = decode_instruction(&test); let res = decode_instruction(&test);
assert!(res.is_ok()); assert!(res.is_ok());
assert_eq!(res.unwrap(), vec!["connect", "vm1"]); assert_eq!(res.unwrap(), vec!["connect", "vm1"]);
} }
#[test] #[test]
fn decode_errors() { fn decode_errors() {
let test = String::from("700.connect,3.vm1;"); let test = String::from("700.connect,3.vm1;");
let res = decode_instruction(&test); let res = decode_instruction(&test);
eprintln!("Error for: {}", res.clone().unwrap_err()); eprintln!("Error for: {}", res.clone().unwrap_err());
assert!(res.is_err()) assert!(res.is_err())
} }
// generally just test that the codec even works // generally just test that the codec even works
// (we can decode a instruction we created) // (we can decode a instruction we created)
#[test] #[test]
fn general_codec_works() { fn general_codec_works() {
let vec = vec![String::from("connect"), String::from("vm1")]; let vec = vec![String::from("connect"), String::from("vm1")];
let test = encode_instruction(&vec); let test = encode_instruction(&vec);
assert_eq!(test, "7.connect,3.vm1;"); assert_eq!(test, "7.connect,3.vm1;");
let res = decode_instruction(&test); let res = decode_instruction(&test);
assert!(res.is_ok()); assert!(res.is_ok());
assert_eq!(res.unwrap(), vec); assert_eq!(res.unwrap(), vec);
} }
} }

View File

@@ -2,46 +2,46 @@ use crate::guac;
use neon::prelude::*; use neon::prelude::*;
fn guac_decode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsArray> { fn guac_decode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsArray> {
let input = cx.argument::<JsString>(0)?.value(cx); let input = cx.argument::<JsString>(0)?.value(cx);
match guac::decode_instruction(&input) { match guac::decode_instruction(&input) {
Ok(data) => { Ok(data) => {
let array = JsArray::new(cx, data.len()); let array = JsArray::new(cx, data.len());
let conv = data let conv = data
.iter() .iter()
.map(|v| cx.string(v)) .map(|v| cx.string(v))
.collect::<Vec<Handle<JsString>>>(); .collect::<Vec<Handle<JsString>>>();
for (i, str) in conv.iter().enumerate() { for (i, str) in conv.iter().enumerate() {
array.set(cx, i as u32, *str)?; array.set(cx, i as u32, *str)?;
} }
return Ok(array); return Ok(array);
} }
Err(e) => { Err(e) => {
return cx.throw_error(format!("{}", e)); return cx.throw_error(format!("{}", e));
} }
} }
} }
fn guac_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsString> { fn guac_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsString> {
let mut elements: Vec<String> = Vec::with_capacity(cx.len()); let mut elements: Vec<String> = Vec::with_capacity(cx.len());
// Capture varadic arguments // Capture varadic arguments
for i in 0..cx.len() { for i in 0..cx.len() {
let input = cx.argument::<JsString>(i)?.value(cx); let input = cx.argument::<JsString>(i)?.value(cx);
elements.push(input); elements.push(input);
} }
Ok(cx.string(guac::encode_instruction(&elements))) Ok(cx.string(guac::encode_instruction(&elements)))
} }
pub fn guac_decode(mut cx: FunctionContext) -> JsResult<JsArray> { pub fn guac_decode(mut cx: FunctionContext) -> JsResult<JsArray> {
guac_decode_impl(&mut cx) guac_decode_impl(&mut cx)
} }
pub fn guac_encode(mut cx: FunctionContext) -> JsResult<JsString> { pub fn guac_encode(mut cx: FunctionContext) -> JsResult<JsString> {
guac_encode_impl(&mut cx) guac_encode_impl(&mut cx)
} }

View File

@@ -1,82 +1,82 @@
use turbojpeg_sys::*; use turbojpeg_sys::*;
pub struct Image<'a> { pub struct Image<'a> {
pub buffer: &'a [u8], pub buffer: &'a [u8],
pub width: u32, pub width: u32,
pub height: u32, pub height: u32,
pub stride: u32, pub stride: u32,
pub format: TJPF, pub format: TJPF,
} }
pub struct JpegCompressor { pub struct JpegCompressor {
handle: tjhandle, handle: tjhandle,
subsamp: TJSAMP, subsamp: TJSAMP,
quality: u32, quality: u32,
} }
unsafe impl Send for JpegCompressor {} unsafe impl Send for JpegCompressor {}
impl JpegCompressor { impl JpegCompressor {
pub fn new() -> Self { pub fn new() -> Self {
unsafe { unsafe {
let init = Self { let init = Self {
handle: tjInitCompress(), handle: tjInitCompress(),
subsamp: TJSAMP_TJSAMP_422, subsamp: TJSAMP_TJSAMP_422,
quality: 95, quality: 95,
}; };
return init; return init;
} }
} }
pub fn set_quality(&mut self, quality: u32) { pub fn set_quality(&mut self, quality: u32) {
self.quality = quality; self.quality = quality;
} }
pub fn set_subsamp(&mut self, samp: TJSAMP) { pub fn set_subsamp(&mut self, samp: TJSAMP) {
self.subsamp = samp; self.subsamp = samp;
} }
pub fn compress_buffer<'a>(&self, image: &Image<'a>) -> Vec<u8> { pub fn compress_buffer<'a>(&self, image: &Image<'a>) -> Vec<u8> {
unsafe { unsafe {
let size: usize = let size: usize =
tjBufSize(image.width as i32, image.height as i32, self.subsamp) as usize; tjBufSize(image.width as i32, image.height as i32, self.subsamp) as usize;
let mut vec = Vec::with_capacity(size); let mut vec = Vec::with_capacity(size);
vec.resize(size, 0); vec.resize(size, 0);
let mut ptr: *mut u8 = vec.as_mut_ptr(); let mut ptr: *mut u8 = vec.as_mut_ptr();
let mut size: libc::c_ulong = 0; let mut size: libc::c_ulong = 0;
let res = tjCompress2( let res = tjCompress2(
self.handle, self.handle,
image.buffer.as_ptr(), image.buffer.as_ptr(),
image.width as i32, image.width as i32,
image.stride as i32, image.stride as i32,
image.height as i32, image.height as i32,
image.format, image.format,
std::ptr::addr_of_mut!(ptr), std::ptr::addr_of_mut!(ptr),
std::ptr::addr_of_mut!(size), std::ptr::addr_of_mut!(size),
self.subsamp, self.subsamp,
self.quality as i32, self.quality as i32,
(TJFLAG_NOREALLOC) as i32, (TJFLAG_NOREALLOC) as i32,
); );
// TODO: Result sex so we can actually notify failure // TODO: Result sex so we can actually notify failure
if res == -1 { if res == -1 {
return Vec::new(); return Vec::new();
} }
// Truncate down to the size we're given back // Truncate down to the size we're given back
vec.truncate(size as usize); vec.truncate(size as usize);
return vec; return vec;
} }
} }
} }
impl Drop for JpegCompressor { impl Drop for JpegCompressor {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
tjDestroy(self.handle); tjDestroy(self.handle);
} }
} }
} }

View File

@@ -13,75 +13,75 @@ use crate::jpeg_compressor::*;
/// Gives a static Tokio runtime. We should replace this with /// Gives a static Tokio runtime. We should replace this with
/// rayon or something, but for now tokio works. /// rayon or something, but for now tokio works.
fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> { fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> {
static RUNTIME: OnceCell<Runtime> = OnceCell::new(); static RUNTIME: OnceCell<Runtime> = OnceCell::new();
RUNTIME RUNTIME
.get_or_try_init(Runtime::new) .get_or_try_init(Runtime::new)
.or_else(|err| cx.throw_error(&err.to_string())) .or_else(|err| cx.throw_error(&err.to_string()))
} }
thread_local! { thread_local! {
static COMPRESSOR: RefCell<JpegCompressor> = RefCell::new(JpegCompressor::new()); static COMPRESSOR: RefCell<JpegCompressor> = RefCell::new(JpegCompressor::new());
} }
fn jpeg_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsPromise> { fn jpeg_encode_impl<'a>(cx: &mut FunctionContext<'a>) -> JsResult<'a, JsPromise> {
let input = cx.argument::<JsObject>(0)?; let input = cx.argument::<JsObject>(0)?;
// Get our input arguments here // Get our input arguments here
let width: u64 = input.get::<JsNumber, _, _>(cx, "width")?.value(cx) as u64; let width: u64 = input.get::<JsNumber, _, _>(cx, "width")?.value(cx) as u64;
let height: u64 = input.get::<JsNumber, _, _>(cx, "height")?.value(cx) as u64; let height: u64 = input.get::<JsNumber, _, _>(cx, "height")?.value(cx) as u64;
let stride: u64 = input.get::<JsNumber, _, _>(cx, "stride")?.value(cx) as u64; let stride: u64 = input.get::<JsNumber, _, _>(cx, "stride")?.value(cx) as u64;
let buffer: Handle<JsBuffer> = input.get(cx, "buffer")?; let buffer: Handle<JsBuffer> = input.get(cx, "buffer")?;
let (deferred, promise) = cx.promise(); let (deferred, promise) = cx.promise();
let channel = cx.channel(); let channel = cx.channel();
let runtime = runtime(cx)?; let runtime = runtime(cx)?;
let buf = buffer.as_slice(cx); let buf = buffer.as_slice(cx);
let copy: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(Vec::with_capacity(buf.len()))); let copy: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(Vec::with_capacity(buf.len())));
// Copy from the node buffer to our temporary buffer // Copy from the node buffer to our temporary buffer
{ {
let mut locked = copy.lock().unwrap(); let mut locked = copy.lock().unwrap();
let cap = locked.capacity(); let cap = locked.capacity();
locked.resize(cap, 0); locked.resize(cap, 0);
locked.copy_from_slice(buf); locked.copy_from_slice(buf);
} }
// Spawn off a tokio blocking pool thread that will do the work for us // Spawn off a tokio blocking pool thread that will do the work for us
runtime.spawn_blocking(move || { runtime.spawn_blocking(move || {
let clone = Arc::clone(&copy); let clone = Arc::clone(&copy);
let locked = clone.lock().unwrap(); let locked = clone.lock().unwrap();
let image: Image = Image { let image: Image = Image {
buffer: locked.as_slice(), buffer: locked.as_slice(),
width: width as u32, width: width as u32,
height: height as u32, height: height as u32,
stride: (stride * 4u64) as u32, // I think? stride: (stride * 4u64) as u32, // I think?
format: turbojpeg_sys::TJPF_TJPF_RGBA, format: turbojpeg_sys::TJPF_TJPF_RGBA,
}; };
let vec = COMPRESSOR.with(|lazy| { let vec = COMPRESSOR.with(|lazy| {
let mut b = lazy.borrow_mut(); let mut b = lazy.borrow_mut();
b.set_quality(35); b.set_quality(35);
b.set_subsamp(turbojpeg_sys::TJSAMP_TJSAMP_420); b.set_subsamp(turbojpeg_sys::TJSAMP_TJSAMP_420);
b.compress_buffer(&image) b.compress_buffer(&image)
}); });
// Fulfill the Javascript promise with our encoded buffer // Fulfill the Javascript promise with our encoded buffer
deferred.settle_with(&channel, move |mut cx| { deferred.settle_with(&channel, move |mut cx| {
let mut buf = cx.buffer(vec.len())?; let mut buf = cx.buffer(vec.len())?;
let slice = buf.as_mut_slice(&mut cx); let slice = buf.as_mut_slice(&mut cx);
slice.copy_from_slice(vec.as_slice()); slice.copy_from_slice(vec.as_slice());
Ok(buf) Ok(buf)
}); });
}); });
Ok(promise) Ok(promise)
} }
pub fn jpeg_encode(mut cx: FunctionContext) -> JsResult<JsPromise> { pub fn jpeg_encode(mut cx: FunctionContext) -> JsResult<JsPromise> {
jpeg_encode_impl(&mut cx) jpeg_encode_impl(&mut cx)
} }

View File

@@ -8,10 +8,10 @@ use neon::prelude::*;
#[neon::main] #[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> { fn main(mut cx: ModuleContext) -> NeonResult<()> {
// Mostly transitionary, later on API should change // Mostly transitionary, later on API should change
cx.export_function("jpegEncode", jpeg_js::jpeg_encode)?; cx.export_function("jpegEncode", jpeg_js::jpeg_encode)?;
cx.export_function("guacDecode", guac_js::guac_decode)?; cx.export_function("guacDecode", guac_js::guac_decode)?;
cx.export_function("guacEncode", guac_js::guac_encode)?; cx.export_function("guacEncode", guac_js::guac_encode)?;
Ok(()) Ok(())
} }