You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trezor-firmware/core/embed/rust/src/trezorhal/qr.rs

76 lines
2.2 KiB

use crate::error::Error;
use cstr_core::CStr;
extern "C" {
fn display_qrcode(
x: cty::c_int,
y: cty::c_int,
data: *const cty::uint16_t,
scale: cty::uint8_t,
);
}
const NVERSIONS: usize = 10; // range of versions (=capacities) that we support
const QR_WIDTHS: [u32; NVERSIONS] = [21, 25, 29, 33, 37, 41, 45, 49, 53, 57];
const THRESHOLDS_BINARY: [usize; NVERSIONS] = [14, 26, 42, 62, 84, 106, 122, 152, 180, 213];
const THRESHOLDS_ALPHANUM: [usize; NVERSIONS] = [20, 38, 61, 90, 122, 154, 178, 221, 262, 311];
const ALPHANUM: &str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $*+-./:";
const MAX_DATA: usize = THRESHOLDS_ALPHANUM[THRESHOLDS_ALPHANUM.len() - 1] + 1; //FIXME
fn is_alphanum_only(data: &str) -> bool {
data.chars().all(|c| ALPHANUM.contains(c))
}
fn qr_version_index(data: &str, thresholds: &[usize]) -> Option<usize> {
for (i, threshold) in thresholds.iter().enumerate() {
if data.len() <= *threshold {
return Some(i);
}
}
None
}
pub fn render_qrcode(
x: i32,
y: i32,
data: &str,
max_size: u32,
case_sensitive: bool,
) -> Result<(), Error> {
let data_len = data.len();
let version_idx;
let mut buffer = [0u8; MAX_DATA];
assert!(data_len < buffer.len());
buffer.as_mut_slice()[..data_len].copy_from_slice(data.as_bytes());
if case_sensitive && !is_alphanum_only(data) {
version_idx = match qr_version_index(data, &THRESHOLDS_BINARY) {
Some(idx) => idx,
_ => return Err(Error::OutOfRange),
}
} else if let Some(idx) = qr_version_index(data, &THRESHOLDS_ALPHANUM) {
version_idx = idx;
if data_len > THRESHOLDS_BINARY[idx] {
for c in buffer.iter_mut() {
c.make_ascii_uppercase()
}
};
} else {
return Err(Error::OutOfRange);
}
let size = QR_WIDTHS[version_idx];
let scale = max_size / size;
assert!((1..=10).contains(&scale));
unsafe {
buffer[data_len] = 0u8;
let cstr = CStr::from_bytes_with_nul_unchecked(&buffer[..data_len + 1]);
display_qrcode(x, y, cstr.as_ptr() as _, scale as u8);
Ok(())
}
}