#![allow(clippy::missing_safety_doc)]
use crate::GrayFrame;
use crate::output::Encoding;
use crate::segment::Segment;
use crate::traits::{Decode, Encode};
#[unsafe(no_mangle)]
pub extern "C" fn alloc(len: usize) -> *mut u8 {
let boxed = vec![0u8; len].into_boxed_slice();
Box::into_raw(boxed) as *mut u8
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dealloc(ptr: *mut u8, len: usize) {
if ptr.is_null() || len == 0 {
return;
}
let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
unsafe { drop(Box::from_raw(slice)) };
}
fn export(buf: Vec<u8>) -> u64 {
let boxed = buf.into_boxed_slice();
let len = boxed.len() as u64;
let ptr = Box::into_raw(boxed) as *const u8 as u64;
(ptr << 32) | len
}
unsafe fn input(ptr: *const u8, len: usize) -> &'static [u8] {
unsafe { core::slice::from_raw_parts(ptr, len) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn encode(
sym_ptr: *const u8,
sym_len: usize,
data_ptr: *const u8,
data_len: usize,
) -> u64 {
let sym = core::str::from_utf8(unsafe { input(sym_ptr, sym_len) }).unwrap_or("");
let data = core::str::from_utf8(unsafe { input(data_ptr, data_len) }).unwrap_or("");
match build(sym, data) {
Ok(enc) => export(serialize_encoding(&enc)),
Err(_) => 0,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn decode(w: usize, h: usize, luma_ptr: *const u8) -> u64 {
let luma = unsafe { input(luma_ptr, w * h) };
let frame = match GrayFrame::new(luma, w, h) {
Ok(f) => f,
Err(_) => return export(b"[]".to_vec()),
};
let mut out: Vec<(String, String)> = Vec::new();
let mut push = |sym: crate::Symbol| {
let name = sym.symbology.to_string();
let text = sym.text().unwrap_or_default();
if !out.iter().any(|(n, t)| *n == name && *t == text) {
out.push((name, text));
}
};
if let Ok(s) = crate::codes::qr::scan(&frame) {
push(s);
}
if let Ok(s) = crate::codes::datamatrix::scan(&frame) {
push(s);
}
if let Some(s) = crate::codes::pdf417::scan(&frame) {
push(s);
}
let candidates = crate::scan1d::scan_lines(&frame, &crate::scan1d::ScanOptions::default());
let linear: [Box<dyn Decode>; 6] = [
Box::new(crate::codes::code128::Code128Decoder::new()),
Box::new(crate::codes::ean::EanDecoder::new()),
Box::new(crate::codes::code93::Code93Decoder::new()),
Box::new(crate::codes::code39::Code39Decoder::new()),
Box::new(crate::codes::itf::ItfDecoder::new()),
Box::new(crate::codes::codabar::CodabarDecoder::new()),
];
for cand in &candidates {
for dec in &linear {
if let Some(s) = crate::scan1d::try_decode(cand, dec.as_ref()) {
push(s);
}
}
}
let mut json = String::from("[");
for (i, (name, text)) in out.iter().enumerate() {
if i > 0 {
json.push(',');
}
json.push_str("{\"symbology\":");
json_str(&mut json, name);
json.push_str(",\"text\":");
json_str(&mut json, text);
json.push('}');
}
json.push(']');
export(json.into_bytes())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn locate(w: usize, h: usize, luma_ptr: *const u8) -> u64 {
let luma = unsafe { input(luma_ptr, w * h) };
let frame = match GrayFrame::new(luma, w, h) {
Ok(f) => f,
Err(_) => return export(b"[]".to_vec()),
};
let candidates = crate::detect::locate(&frame, &crate::detect::LocateOptions::default());
let mut json = String::from("[");
for (i, c) in candidates.iter().enumerate() {
if i > 0 {
json.push(',');
}
let family = match c.symbology.map(|s| s.dimension()) {
Some(crate::Dimension::Matrix) => "matrix",
Some(crate::Dimension::Stacked) => "stacked",
Some(crate::Dimension::Postal) => "postal",
_ => "linear",
};
json.push_str("{\"family\":\"");
json.push_str(family);
json.push_str("\",\"corners\":[");
for (j, p) in c.location.outline.corners.iter().enumerate() {
if j > 0 {
json.push(',');
}
json.push('[');
push_num(&mut json, p.x);
json.push(',');
push_num(&mut json, p.y);
json.push(']');
}
json.push_str("]}");
}
json.push(']');
export(json.into_bytes())
}
fn push_num(s: &mut String, v: f32) {
let r = (v * 10.0).round() / 10.0;
s.push_str(&r.to_string());
}
fn json_str(out: &mut String, s: &str) {
out.push('"');
for c in s.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
c => out.push(c),
}
}
out.push('"');
}
fn serialize_encoding(enc: &Encoding) -> Vec<u8> {
let mut buf = Vec::new();
match enc {
Encoding::Matrix(m) => {
buf.extend_from_slice(&(m.width() as u32).to_le_bytes());
buf.extend_from_slice(&(m.height() as u32).to_le_bytes());
buf.extend_from_slice(&(m.quiet_zone as u32).to_le_bytes());
buf.push(0);
for y in 0..m.height() {
for x in 0..m.width() {
buf.push(m.get(x, y) as u8);
}
}
}
Encoding::Linear(p) => {
buf.extend_from_slice(&(p.modules.len() as u32).to_le_bytes());
buf.extend_from_slice(&1u32.to_le_bytes());
buf.extend_from_slice(&(p.quiet_zone as u32).to_le_bytes());
buf.push(1);
for &b in &p.modules {
buf.push(b as u8);
}
}
}
buf
}
fn build(sym: &str, data: &str) -> Result<Encoding, crate::Error> {
use crate::codes::*;
let b = data.as_bytes();
match sym {
"qr" => {
let e = qr::QrEncoder::new();
e.encode(&e.build_text(data, qr::EcLevel::M)?)
}
"microqr" => {
let e = microqr::MicroQrEncoder::new();
e.encode(&e.build(vec![Segment::byte(b.to_vec())], microqr::MicroEcLevel::M)?)
}
"aztec" => {
let e = aztec::AztecEncoder::new();
e.encode(&e.build_text(data)?)
}
"datamatrix" => {
let e = datamatrix::DataMatrixEncoder::new();
e.encode(&e.build_text(data)?)
}
"pdf417" => {
let e = pdf417::Pdf417Encoder::new();
e.encode(&e.build_text(data, pdf417::EcLevel::new(2).unwrap())?)
}
"maxicode" => {
let e = maxicode::MaxiCodeEncoder::new();
e.encode(&e.build_text(data)?)
}
"code128" => {
let e = code128::Code128Encoder::new();
e.encode(&e.build_text(data)?)
}
"code39" => {
let e = code39::Code39Encoder::new();
e.encode(&e.build(b, true, false)?)
}
"code93" => {
let e = code93::Code93Encoder::new();
e.encode(&e.build(b, true)?)
}
"ean13" => {
let e = ean::EanEncoder::new();
e.encode(&e.build_ean13(data)?)
}
"ean8" => {
let e = ean::EanEncoder::new();
e.encode(&e.build_ean8(data)?)
}
"upca" => {
let e = ean::EanEncoder::new();
e.encode(&e.build_upca(data)?)
}
"itf" => {
let e = itf::ItfEncoder::new();
e.encode(&e.build(b, false)?)
}
"codabar" => {
let e = codabar::CodabarEncoder::new();
e.encode(&e.build(b'A', b, b'A')?)
}
_ => Err(crate::Error::Unsupported {
what: "symbology not in the web demo set",
}),
}
}