use std::char;
use std::fmt;
use std::fmt::Display;
use std::fmt::Write;
use std::iter::IntoIterator;
use std::ops::Index;
const NONE: char = char::REPLACEMENT_CHARACTER;
const PETSCII_NONE: u8 = 0x7F;
#[rustfmt::skip]
static PETSCII_TO_CHAR_MAP: [char; 256] = [
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, '\n', NONE, NONE,
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'[', '\u{00A3}', ']', '\u{2191}', '\u{2190}', '\u{2501}',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'\u{254b}', NONE, '\u{2503}', '\u{2592}', NONE,
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
'\u{00a0}', '\u{258c}', '\u{2584}', '\u{2594}',
'\u{2581}', '\u{258e}', '\u{2592}', '\u{2595}',
NONE, NONE, '\u{2595}', '\u{2523}',
'\u{2597}', '\u{2517}', '\u{2513}', '\u{2582}',
'\u{250f}', '\u{253b}', '\u{2533}', '\u{252b}',
'\u{258e}', '\u{258d}', '\u{2595}', '\u{2594}',
'\u{2594}', '\u{2583}', '\u{2713}', '\u{2596}',
'\u{259d}', '\u{2518}', '\u{2598}', '\u{259a}',
'\u{2501}',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'\u{254b}', NONE, '\u{2503}', '\u{2592}', NONE,
'\u{00a0}', '\u{258c}', '\u{2584}', '\u{2594}',
'\u{2581}', '\u{258e}', '\u{2592}', '\u{2595}',
NONE, NONE, '\u{2595}', '\u{2523}',
'\u{2597}', '\u{2517}', '\u{2513}', '\u{2582}',
'\u{250f}', '\u{253b}', '\u{2533}', '\u{252b}',
'\u{258e}', '\u{258d}', '\u{2595}', '\u{2594}',
'\u{2594}', '\u{2583}', '\u{2713}', '\u{2596}',
'\u{259d}', '\u{2518}', '\u{2598}', '\u{2592}',
];
const fn petscii_to_unicode_char(byte: u8) -> char {
PETSCII_TO_CHAR_MAP[byte as usize]
}
#[derive(Debug)]
pub enum PetsciiError {
BufferExceeded,
}
impl Display for PetsciiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use PetsciiError::*;
match self {
BufferExceeded => f.write_str("buffer exceeded"),
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Petscii(Vec<u8>);
impl Petscii {
pub fn from_bytes(bytes: &[u8]) -> Petscii {
Petscii(bytes.to_owned())
}
pub fn from_padded_bytes(bytes: &[u8], pad_byte: u8) -> Petscii {
let mut end = bytes.len();
for offset in (0..end).rev() {
if bytes[offset] == pad_byte {
end -= 1;
} else {
break;
}
}
Petscii((bytes[..end]).to_owned())
}
pub fn from_str_lossy(string: &str) -> Petscii {
let mut petscii_bytes = Vec::with_capacity(string.len());
'outer: for c in string.chars() {
for (p, mapping) in PETSCII_TO_CHAR_MAP.iter().enumerate() {
if *mapping == c {
petscii_bytes.push(p as u8);
continue 'outer;
}
}
petscii_bytes.push(PETSCII_NONE);
}
Petscii(petscii_bytes.to_owned())
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub const fn is_empty(&self) -> bool {
self.0.len() == 0
}
pub fn write_bytes_with_padding(
&self,
bytes: &mut [u8],
pad_byte: u8,
) -> Result<(), PetsciiError> {
let src_len = self.0.len();
let dst_len = bytes.len();
if src_len > dst_len {
return Err(PetsciiError::BufferExceeded);
} else {
let _ = &bytes[..src_len].copy_from_slice(&self.0);
for byte_ref in bytes.iter_mut().take(dst_len).skip(src_len) {
*byte_ref = pad_byte;
}
}
Ok(())
}
}
impl From<Petscii> for String {
fn from(petscii: Petscii) -> Self {
petscii.to_string()
}
}
impl From<String> for Petscii {
fn from(string: String) -> Petscii {
Self::from_str_lossy(&string)
}
}
impl From<&String> for Petscii {
fn from(string: &String) -> Petscii {
Self::from_str_lossy(string)
}
}
impl From<&str> for Petscii {
fn from(string: &str) -> Petscii {
Self::from_str_lossy(string)
}
}
impl AsRef<Petscii> for Petscii {
fn as_ref(&self) -> &Petscii {
self
}
}
impl AsRef<[u8]> for Petscii {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl fmt::Display for Petscii {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
for petscii_char in self.0.iter() {
let c = petscii_to_unicode_char(*petscii_char);
f.write_char(c)?;
}
Ok(())
}
}
impl fmt::Debug for Petscii {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(f, "\"")?;
for petscii_char in self.0.iter() {
let c = petscii_to_unicode_char(*petscii_char);
if c == '"' {
f.write_str("\\\"")?;
} else {
f.write_char(c)?;
}
}
write!(f, "\"")
}
}
impl Index<usize> for Petscii {
type Output = u8;
fn index(&self, index: usize) -> &u8 {
&self.0[index]
}
}
impl IntoIterator for Petscii {
type Item = u8;
type IntoIter = ::std::vec::IntoIter<u8>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a Petscii {
type Item = &'a u8;
type IntoIter = ::std::slice::Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
(self.0).iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[ignore]
#[test]
fn dump_petscii_chart() {
for a in 0..16u8 {
for b in 0..16u8 {
let c = a * 16 + b;
print!("{} ", petscii_to_unicode_char(c));
}
println!();
}
}
#[test]
fn test_petscii_chars() {
assert_eq!(petscii_to_unicode_char(0x00), char::REPLACEMENT_CHARACTER);
assert_eq!(petscii_to_unicode_char(0x0d), '\n');
assert_eq!(petscii_to_unicode_char(0x20), ' ');
assert_eq!(petscii_to_unicode_char(0x21), '!');
assert_eq!(petscii_to_unicode_char(0x2f), '/');
assert_eq!(petscii_to_unicode_char(0x30), '0');
assert_eq!(petscii_to_unicode_char(0x31), '1');
assert_eq!(petscii_to_unicode_char(0x3f), '?');
assert_eq!(petscii_to_unicode_char(0x40), '@');
assert_eq!(petscii_to_unicode_char(0x41), 'a');
assert_eq!(petscii_to_unicode_char(0x5a), 'z');
assert_eq!(petscii_to_unicode_char(0x61), 'A');
assert_eq!(petscii_to_unicode_char(0x7a), 'Z');
assert_eq!(
petscii_to_unicode_char(0x7b),
char::from_u32(0x254b).unwrap()
);
assert_eq!(petscii_to_unicode_char(0x80), char::REPLACEMENT_CHARACTER);
assert_eq!(petscii_to_unicode_char(0x9f), char::REPLACEMENT_CHARACTER);
assert_eq!(
petscii_to_unicode_char(0xa0),
char::from_u32(0x00a0).unwrap()
);
assert_eq!(
petscii_to_unicode_char(0xbf),
char::from_u32(0x259a).unwrap()
);
assert_eq!(
petscii_to_unicode_char(0xc0),
char::from_u32(0x2501).unwrap()
);
assert_eq!(petscii_to_unicode_char(0xc1), 'A');
assert_eq!(petscii_to_unicode_char(0xda), 'Z');
assert_eq!(
petscii_to_unicode_char(0xff),
char::from_u32(0x2592).unwrap()
);
}
#[test]
fn test_petscii() {
let bytes = &[0x41, 0x42, 0x43, 0x61, 0x62, 0x63, 0xc1, 0xc2, 0xc3, 0x00];
let petscii = Petscii::from_bytes(bytes);
assert_eq!(petscii[0], 0x41);
assert_eq!(petscii[8], 0xc3);
assert_eq!(petscii[9], 0x00);
let mut bytes_expected = bytes[..].to_vec();
for byte in petscii.clone() {
assert!(!bytes_expected.is_empty());
let byte_expected = bytes_expected.remove(0);
assert_eq!(byte_expected, byte);
}
let mut bytes_expected = bytes[..].to_vec();
for byte in &petscii {
assert!(!bytes_expected.is_empty());
let byte_expected = bytes_expected.remove(0);
assert_eq!(byte_expected, *byte);
}
let string: String = petscii.into();
assert_eq!(string, "abcABCABC\u{FFFD}");
}
#[test]
fn test_padding() {
const LENGTH: usize = 8;
const PAD: u8 = 0xa0;
const BASIC_PADDED: [u8; LENGTH] = [0x41, 0x42, 0x43, PAD, PAD, PAD, PAD, PAD];
const BASIC_UNPADDED: [u8; 3] = [0x41, 0x42, 0x43];
let petscii = Petscii::from_bytes(&BASIC_PADDED);
assert_eq!(petscii.len(), BASIC_PADDED.len());
let petscii = Petscii::from_padded_bytes(&BASIC_PADDED, PAD);
assert_eq!(petscii.len(), BASIC_UNPADDED.len());
let mut bytes = [b'\0'; LENGTH];
petscii.write_bytes_with_padding(&mut bytes, PAD).unwrap();
assert_eq!(bytes, BASIC_PADDED);
const FULL: [u8; LENGTH] = [0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48];
let petscii = Petscii::from_padded_bytes(&FULL, PAD);
assert_eq!(petscii.len(), FULL.len());
let mut bytes = [b'\0'; LENGTH];
petscii.write_bytes_with_padding(&mut bytes, PAD).unwrap();
assert_eq!(bytes, FULL);
let mut bytes = [b'\0'; LENGTH - 1];
assert!(petscii.write_bytes_with_padding(&mut bytes, PAD).is_err());
const ZERO_PADDED: [u8; LENGTH] = [PAD, PAD, PAD, PAD, PAD, PAD, PAD, PAD];
const ZERO_UNPADDED: [u8; 0] = [];
let petscii = Petscii::from_bytes(&ZERO_PADDED);
assert_eq!(petscii.len(), ZERO_PADDED.len());
let petscii = Petscii::from_padded_bytes(&ZERO_PADDED, PAD);
assert_eq!(petscii.len(), ZERO_UNPADDED.len());
let mut bytes = [b'\0'; LENGTH];
petscii.write_bytes_with_padding(&mut bytes, PAD).unwrap();
assert_eq!(bytes, ZERO_PADDED);
}
#[test]
fn test_conversions() {
fn process_move<P: Into<Petscii>>(petscii: P) -> String {
let petscii: Petscii = petscii.into();
petscii.into()
}
fn process_ref<P: AsRef<Petscii> + ?Sized>(petscii: &P) -> String {
let petscii: &Petscii = petscii.as_ref();
petscii.to_string()
}
let str_slice_storage: String = String::from("&str (string slice) test");
let str_slice: &str = &str_slice_storage[..];
let string: String = String::from("String test.");
let string_ref: &String = &string;
let petscii: Petscii = Petscii::from_str_lossy("Petscii test.");
assert_eq!(process_move(str_slice), str_slice_storage);
assert_eq!(process_move(string.clone()), string);
assert_eq!(process_move(string_ref), string);
assert_eq!(process_move(petscii.clone()), petscii.to_string());
static INPUT_BUFFER: [u8; 3] = [b'a', b'b', b'c'];
let petscii: Petscii = Petscii::from_bytes(&INPUT_BUFFER);
process_ref(&petscii);
let output_buffer: &[u8] = petscii.as_ref();
assert_eq!(output_buffer, INPUT_BUFFER);
}
}