use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use deser_core::de::DeserializeDriver;
use deser_core::ext::Timestamp;
use deser_core::{Error, ErrorKind, Event, State};
pub(crate) trait Out<'i> {
fn state_mut(&mut self) -> &mut State;
fn emit<'e, E: Into<Event<'e>>>(&mut self, event: E) -> Result<(), Error>;
fn emit_input(&mut self, event: Event<'i>) -> Result<(), Error>;
#[inline(always)]
fn emit_at<'e, E: Into<Event<'e>>>(
&mut self,
start: usize,
end: usize,
event: E,
) -> Result<(), Error> {
self.state_mut().set_input_range(start, end);
self.emit(event)
}
#[inline(always)]
fn emit_input_at(&mut self, start: usize, end: usize, event: Event<'i>) -> Result<(), Error> {
self.state_mut().set_input_range(start, end);
self.emit_input(event)
}
}
pub(crate) struct Borrowing<'a, 'd, 'i>(pub &'a mut DeserializeDriver<'d, 'i>);
impl<'i> Out<'i> for Borrowing<'_, '_, 'i> {
#[inline(always)]
fn state_mut(&mut self) -> &mut State {
self.0.state_mut()
}
#[inline(always)]
fn emit<'e, E: Into<Event<'e>>>(&mut self, event: E) -> Result<(), Error> {
self.0.emit(event)
}
#[inline(always)]
fn emit_input(&mut self, event: Event<'i>) -> Result<(), Error> {
self.0.emit_borrowed(event)
}
}
pub(crate) struct Copying<'a, 'd, 'de>(pub &'a mut DeserializeDriver<'d, 'de>);
impl<'i> Out<'i> for Copying<'_, '_, '_> {
#[inline(always)]
fn state_mut(&mut self) -> &mut State {
self.0.state_mut()
}
#[inline(always)]
fn emit<'e, E: Into<Event<'e>>>(&mut self, event: E) -> Result<(), Error> {
self.0.emit(event)
}
#[inline(always)]
fn emit_input(&mut self, event: Event<'i>) -> Result<(), Error> {
self.0.emit(event)
}
}
#[cold]
pub(crate) fn syntax_error(offset: usize, msg: &str) -> Error {
Error::with_offset(ErrorKind::Syntax, format!("syntax error: {}", msg), offset)
}
#[cold]
pub(crate) fn eof_error(offset: usize) -> Error {
Error::with_offset(ErrorKind::EndOfFile, "unexpected end of input", offset)
}
const PLIST_EPOCH: i64 = 978_307_200;
pub(crate) fn timestamp_from_plist(value: f64) -> Option<Timestamp> {
const LIMIT: f64 = 9_007_199_254_740_992.0; if !(value > -LIMIT && value < LIMIT) {
return None;
}
let mut seconds = value as i64;
if seconds as f64 > value {
seconds -= 1;
}
let micros = (value - seconds as f64) * 1e6;
let mut micros = (micros + 0.5) as u32;
if micros >= 1_000_000 {
seconds += 1;
micros -= 1_000_000;
}
Some(Timestamp {
seconds: seconds.checked_add(PLIST_EPOCH)?,
nanosecond: micros * 1000,
})
}
pub(crate) fn timestamp_to_plist(value: &Timestamp) -> f64 {
let seconds = i128::from(value.seconds) - i128::from(PLIST_EPOCH);
seconds as f64 + f64::from(value.nanosecond) / 1e9
}
pub(crate) fn format_xml_date(value: &Timestamp) -> String {
Timestamp {
seconds: value.seconds,
nanosecond: 0,
}
.to_string()
}
pub(crate) fn decode_base64(text: &str) -> Result<Vec<u8>, usize> {
let mut out = Vec::with_capacity(text.len() / 4 * 3);
let mut acc = 0u32;
let mut bits = 0;
for (idx, c) in text.bytes().enumerate() {
let value = match c {
b'A'..=b'Z' => c - b'A',
b'a'..=b'z' => c - b'a' + 26,
b'0'..=b'9' => c - b'0' + 52,
b'+' => 62,
b'/' => 63,
b'=' => {
return match text[idx..]
.bytes()
.position(|c| c != b'=' && !c.is_ascii_whitespace())
{
Some(pos) => Err(idx + pos),
None => Ok(out),
};
}
c if c.is_ascii_whitespace() => continue,
_ => return Err(idx),
};
acc = (acc << 6) | u32::from(value);
bits += 6;
if bits >= 8 {
bits -= 8;
out.push((acc >> bits) as u8);
}
}
Ok(out)
}
pub(crate) fn decode_utf16_text(input: &[u8]) -> Option<Result<String, usize>> {
let big_endian = if input.starts_with(b"\xfe\xff") {
true
} else if input.starts_with(b"\xff\xfe") {
false
} else {
return None;
};
let body = &input[2..];
let (units, rest) = body.as_chunks::<2>();
if !rest.is_empty() {
return Some(Err(input.len() - 1));
}
let units = units.iter().map(|&unit| {
if big_endian {
u16::from_be_bytes(unit)
} else {
u16::from_le_bytes(unit)
}
});
let mut out = String::with_capacity(body.len() / 2);
for (idx, c) in char::decode_utf16(units).enumerate() {
match c {
Ok(c) => out.push(c),
Err(_) => return Some(Err(2 + idx * 2)),
}
}
Some(Ok(out))
}
#[test]
fn test_base64() {
use deser_core::adapters::{Base64, BytesEncoding};
for len in 0..20 {
let data: Vec<u8> = (0..len as u8).map(|x| x.wrapping_mul(37)).collect();
let mut encoded = String::new();
Base64::encode(&data, &mut encoded);
assert_eq!(decode_base64(&encoded).unwrap(), data);
let spaced: String = encoded
.chars()
.flat_map(|c| [c, '\n'])
.filter(|&c| c != '=')
.collect();
assert_eq!(decode_base64(&spaced).unwrap(), data);
}
assert_eq!(decode_base64("AA=x"), Err(3));
assert_eq!(decode_base64("A*"), Err(1));
}
#[test]
fn test_timestamps() {
let ts = timestamp_from_plist(0.0).unwrap();
assert_eq!(ts.to_string(), "2001-01-01T00:00:00Z");
let ts = timestamp_from_plist(-0.5).unwrap();
assert_eq!(ts.to_string(), "2000-12-31T23:59:59.5Z");
let ts = timestamp_from_plist(1.1).unwrap();
assert_eq!(ts.nanosecond, 100_000_000);
assert_eq!(timestamp_to_plist(&ts), 1.1);
assert_eq!(timestamp_from_plist(f64::NAN), None);
assert_eq!(timestamp_from_plist(f64::INFINITY), None);
}