make rustfmt.toml consistent with editorconfig because of course rustfmt refuses to use the editorconfig
This commit is contained in:
3
cvm-rs/rustfmt.toml
Normal file
3
cvm-rs/rustfmt.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
tab_spaces = 4
|
||||||
|
newline_style = "Unix"
|
||||||
|
hard_tabs = true
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(©);
|
let clone = Arc::clone(©);
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user