mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-02 12:38:43 +00:00
198 lines
5.3 KiB
Rust
198 lines
5.3 KiB
Rust
use heapless::String;
|
|
|
|
#[cfg(feature = "micropython")]
|
|
use crate::error::Error;
|
|
|
|
#[cfg(feature = "micropython")]
|
|
use crate::micropython::{buffer::StrBuffer, obj::Obj};
|
|
|
|
#[cfg(feature = "translations")]
|
|
use crate::translations::TR;
|
|
|
|
/// Trait for internal representation of strings. This is a legacy crutch before
|
|
/// we fully transition to `TString`. For now, it allows some manner of
|
|
/// compatibility between `&str` and `StrBuffer`. Implies the following
|
|
/// operations:
|
|
/// - dereference into a short-lived `&str` reference (AsRef<str>) (probably not
|
|
/// strictly necessary anymore)
|
|
/// - create a new string from a string literal (From<&'static str>)
|
|
/// - infallibly convert into a `TString` (Into<TString<'static>>), which is
|
|
/// then used for other operations.
|
|
pub trait StringType: AsRef<str> + From<&'static str> + Into<TString<'static>> {}
|
|
|
|
impl<T> StringType for T where T: AsRef<str> + From<&'static str> + Into<TString<'static>> {}
|
|
|
|
/// Unified-length String type, long enough for most simple use-cases.
|
|
pub type ShortString = String<50>;
|
|
|
|
pub fn hexlify(data: &[u8], buffer: &mut [u8]) {
|
|
const HEX_LOWER: [u8; 16] = *b"0123456789abcdef";
|
|
let mut i: usize = 0;
|
|
for b in data.iter().take(buffer.len() / 2) {
|
|
let hi: usize = ((b & 0xf0) >> 4).into();
|
|
let lo: usize = (b & 0x0f).into();
|
|
buffer[i] = HEX_LOWER[hi];
|
|
buffer[i + 1] = HEX_LOWER[lo];
|
|
i += 2;
|
|
}
|
|
}
|
|
|
|
pub fn format_i64(num: i64, buffer: &mut [u8]) -> Option<&str> {
|
|
let mut i = 0;
|
|
let mut num = num;
|
|
let negative = if num < 0 {
|
|
num = -num;
|
|
true
|
|
} else {
|
|
false
|
|
};
|
|
|
|
while num > 0 && i < buffer.len() {
|
|
buffer[i] = b'0' + ((num % 10) as u8);
|
|
num /= 10;
|
|
i += 1;
|
|
}
|
|
match i {
|
|
0 => Some("0"),
|
|
_ if num > 0 => None,
|
|
_ if negative && i == buffer.len() => None,
|
|
_ => {
|
|
if negative {
|
|
buffer[i] = b'-';
|
|
i += 1;
|
|
}
|
|
let result = &mut buffer[..i];
|
|
result.reverse();
|
|
Some(unsafe { core::str::from_utf8_unchecked(result) })
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub enum TString<'a> {
|
|
#[cfg(feature = "micropython")]
|
|
Allocated(StrBuffer),
|
|
#[cfg(feature = "translations")]
|
|
Translation {
|
|
tr: TR,
|
|
offset: u16,
|
|
},
|
|
Str(&'a str),
|
|
}
|
|
|
|
impl TString<'_> {
|
|
pub fn len(&self) -> usize {
|
|
self.map(|s| s.len())
|
|
}
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
self.len() == 0
|
|
}
|
|
|
|
/// Maps the string to a value using a closure.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// The properties of this function are bounded by the properties of
|
|
/// `TranslatedString::map_translated`. The reference to the string is
|
|
/// guaranteed to be valid throughout the closure, but must not escape
|
|
/// it. The `for<'a>` bound on the closure's argument ensures this.
|
|
pub fn map<F, T>(&self, fun: F) -> T
|
|
where
|
|
F: for<'a> FnOnce(&'a str) -> T,
|
|
{
|
|
match self {
|
|
#[cfg(feature = "micropython")]
|
|
Self::Allocated(buf) => fun(buf.as_ref()),
|
|
#[cfg(feature = "translations")]
|
|
Self::Translation { tr, offset } => tr.map_translated(|s| fun(&s[*offset as usize..])),
|
|
Self::Str(s) => fun(s),
|
|
}
|
|
}
|
|
|
|
pub fn skip_prefix(&self, skip_bytes: usize) -> Self {
|
|
self.map(|s| {
|
|
assert!(skip_bytes <= s.len());
|
|
assert!(s.is_char_boundary(skip_bytes));
|
|
});
|
|
match self {
|
|
#[cfg(feature = "micropython")]
|
|
Self::Allocated(s) => Self::Allocated(s.skip_prefix(skip_bytes)),
|
|
#[cfg(feature = "translations")]
|
|
Self::Translation { tr, offset } => Self::Translation {
|
|
tr: *tr,
|
|
offset: offset + skip_bytes as u16,
|
|
},
|
|
Self::Str(s) => Self::Str(&s[skip_bytes..]),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TString<'static> {
|
|
#[cfg(feature = "translations")]
|
|
pub const fn from_translation(tr: TR) -> Self {
|
|
Self::Translation { tr, offset: 0 }
|
|
}
|
|
|
|
#[cfg(feature = "micropython")]
|
|
pub const fn from_strbuffer(buf: StrBuffer) -> Self {
|
|
Self::Allocated(buf)
|
|
}
|
|
|
|
pub const fn empty() -> Self {
|
|
Self::Str("")
|
|
}
|
|
}
|
|
|
|
impl<'a> TString<'a> {
|
|
pub const fn from_str(s: &'a str) -> Self {
|
|
Self::Str(s)
|
|
}
|
|
}
|
|
|
|
impl<'a> From<&'a str> for TString<'a> {
|
|
fn from(s: &'a str) -> Self {
|
|
Self::Str(s)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "translations")]
|
|
impl From<TR> for TString<'static> {
|
|
fn from(tr: TR) -> Self {
|
|
Self::from_translation(tr)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "micropython")]
|
|
impl From<StrBuffer> for TString<'static> {
|
|
fn from(buf: StrBuffer) -> Self {
|
|
Self::from_strbuffer(buf)
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "micropython")]
|
|
impl TryFrom<Obj> for TString<'static> {
|
|
type Error = Error;
|
|
|
|
fn try_from(obj: Obj) -> Result<Self, Self::Error> {
|
|
Ok(StrBuffer::try_from(obj)?.into())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "micropython")]
|
|
impl<'a> TryFrom<TString<'a>> for Obj {
|
|
type Error = Error;
|
|
|
|
fn try_from(s: TString<'a>) -> Result<Self, Self::Error> {
|
|
s.map(|t| t.try_into())
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b> PartialEq<TString<'a>> for TString<'b> {
|
|
fn eq(&self, other: &TString<'a>) -> bool {
|
|
self.map(|s| other.map(|o| s == o))
|
|
}
|
|
}
|
|
|
|
impl Eq for TString<'_> {}
|