#![cfg_attr(not(test), no_std)]
/// Encoder iterator, converting bytes into unicode chars based on the contained encoding callable.
pub struct Encoder<I, F>
where
I: Iterator<Item = u8>,
F: Fn(u8) -> char,
{
iterator: I,
encode: F,
}
impl<I, F> Encoder<I, F>
where
I: Iterator<Item = u8>,
F: Fn(u8) -> char,
{
pub fn new(iterator: I, encode: F) -> Self {
Self { iterator, encode }
}
}
impl<I, F> Iterator for Encoder<I, F>
where
I: Iterator<Item = u8>,
F: Fn(u8) -> char,
{
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
self.iterator.next().map(|byte| (self.encode)(byte))
}
}
/// Encoder iterator, converting unicode chars into bytes based on the contained decoding callable.
/// Must be fallible because not all unicode codepoints are valid bytes.
pub struct Decoder<I, F>
where
I: Iterator<Item = char>,
F: Fn(char) -> Option<u8>,
{
iterator: I,
decode: F,
}
impl<I, F> Decoder<I, F>
where
I: Iterator<Item = char>,
F: Fn(char) -> Option<u8>,
{
pub fn new(iterator: I, decode: F) -> Self {
Self { iterator, decode }
}
}
impl<I, F> Iterator for Decoder<I, F>
where
I: Iterator<Item = char>,
F: Fn(char) -> Option<u8>,
{
type Item = Option<u8>;
fn next(&mut self) -> Option<Self::Item> {
self.iterator.next().map(|byte| (self.decode)(byte))
}
}
/// Encode function for encoding to printable-ascii-preserving Unicode.
/// `0x00..=0x1F` is mapped to the range starting at `U+B0` to map into the Latin-1 Supplement block in the first range that is 8-byte aligned and fully printable, skipping `NBSP` and `SHY`.
/// `0x20..=0x7E` are mapped to the same bytes as printable ASCII.
/// `0x7F` is arbitrarily mapped from ASCII ESC to §.
/// `0x80..=0xFF` mapped to the range starting at `U+100`, Latin Extended-A, with the exception of
/// `0xC9`, which is mapped arbitrarily to `¤` to avoid the deprecated character at that codepoint.
pub fn encode_papu(byte: u8) -> char {
match byte {
b @ 0x00..=0x1F => (b + 0xB0) as char,
b @ 0x20..=0x7E => b as char,
0x7F => '§',
// Unsafe is fine here because these ranges are known to be safe char values.
b @ (0x80..=0xC8 | 0xCA..) => unsafe { char::from_u32_unchecked(b as u32 + 0x80) },
0xC9 => '¤',
}
}
/// Decode function for printable-ascii-preserving Unicode.
/// All values are mapped as the inverse of encode_papu. All other input chars map to None.
pub const fn decode_papu(c: char) -> Option<u8> {
Some(match c as u32 {
b @ 0xB0..=0xCF => (b - 0xB0) as u8,
b @ 0x20..=0x7E => b as u8,
0xA7 => 0x7F,
b @ (0x100..=0x148 | 0x14A..=0x17F) => (b - 0x80) as u8,
0xA4 => 0xC9,
_ => return None,
})
}
/// Encode function for encoding to emoji.
/// `0x00..=0x4F` is mapped to the range starting at `U+1F370` For some plants and foods.
/// `0x50..=0x8F` is mapped to the range starting at `U+1F400` for animals.
/// `0x90..=0xDF` is mapped to the range starting at `U+1F600` for expressions and hand signs.
/// `0xE0..=0xFF` is mapped to the range starting at `U+1F910` for more expressions and hand signs.
pub fn encode_emoji(byte: u8) -> char {
match byte {
b @ 0x00..=0x4F => unsafe { char::from_u32_unchecked(b as u32 + 0x1F330) },
b @ 0x50..=0x8F => unsafe { char::from_u32_unchecked(b as u32 + (0x1F400 - 0x50)) },
b @ 0x90..=0xDF => unsafe { char::from_u32_unchecked(b as u32 + (0x1F600 - 0x90)) },
b @ 0xE0..=0xFF => unsafe { char::from_u32_unchecked(b as u32 + (0x1F910 - 0xE0)) },
}
}
/// Decode function for emoji.
/// All values are mapped as the inverse of encode_emoji. All other input chars map to None.
pub const fn decode_emoji(c: char) -> Option<u8> {
Some(match c as u32 {
b @ 0x1F330..=0x1F37F => (b - 0x1F330) as u8,
b @ 0x1F400..=0x1F43F => (b - (0x1F400 - 0x50)) as u8,
b @ 0x1F600..=0x1F64F => (b - (0x1F600 - 0x90)) as u8,
b @ 0x1F910..=0x1F92F => (b - (0x1F910 - 0xE0)) as u8,
_ => return None,
})
}
pub trait Encode: Iterator<Item = u8>
where
Self: Sized,
{
fn base256u<F>(self, function: F) -> Encoder<Self, F>
where
F: Fn(u8) -> char,
{
Encoder::new(self, function)
}
fn base256u_papu(self) -> Encoder<Self, fn(u8) -> char> {
self.base256u(encode_papu)
}
fn base256u_emoji(self) -> Encoder<Self, fn(u8) -> char> {
self.base256u(encode_emoji)
}
}
impl<T> Encode for T where T: Iterator<Item = u8> {}
pub trait Decode: Iterator<Item = char>
where
Self: Sized,
{
fn base256u<F>(self, function: F) -> Decoder<Self, F>
where
F: Fn(char) -> Option<u8>,
{
Decoder::new(self, function)
}
fn base256u_papu(self) -> Decoder<Self, fn(char) -> Option<u8>> {
self.base256u(decode_papu)
}
fn base256u_emoji(self) -> Decoder<Self, fn(char) -> Option<u8>> {
self.base256u(decode_emoji)
}
}
impl<T> Decode for T where T: Iterator<Item = char> {}
#[cfg(test)]
mod tests {
use crate::{Decode, Encode};
#[test]
fn encoding_papu() {
let encoded: String = (u8::MIN..=u8::MAX).base256u_papu().collect();
assert_eq!(encoded, "°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~§ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇň¤ŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ");
let encoded: String = b"Pack my box with five dozen liquor jugs."
.into_iter()
.copied()
.base256u_papu()
.collect();
assert_eq!(encoded, "Pack my box with five dozen liquor jugs.");
}
#[test]
fn decoding_papu() {
let decoded: Vec<Option<u8>> = "°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~§ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿŀŁłŃńŅņŇň¤ŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƝʼn".chars().base256u_papu().collect();
let mut matcher: Vec<Option<u8>> = (u8::MIN..=u8::MAX).map(|b| Some(b)).collect();
matcher.push(None);
matcher.push(None);
assert_eq!(decoded, matcher);
let decoded: Vec<u8> = "Pack my box with five dozen liquor jugs."
.chars()
.base256u_papu()
.map(|c| c.unwrap())
.collect();
assert_eq!(
decoded.as_slice(),
b"Pack my box with five dozen liquor jugs."
);
}
#[test]
fn encoding_emoji() {
let encoded: String = (u8::MIN..=u8::MAX).base256u_emoji().collect();
assert_eq!(encoded, "🌰🌱🌲🌳🌴🌵🌶🌷🌸🌹🌺🌻🌼🌽🌾🌿🍀🍁🍂🍃🍄🍅🍆🍇🍈🍉🍊🍋🍌🍍🍎🍏🍐🍑🍒🍓🍔🍕🍖🍗🍘🍙🍚🍛🍜🍝🍞🍟🍠🍡🍢🍣🍤🍥🍦🍧🍨🍩🍪🍫🍬🍭🍮🍯🍰🍱🍲🍳🍴🍵🍶🍷🍸🍹🍺🍻🍼🍽🍾🍿🐀🐁🐂🐃🐄🐅🐆🐇🐈🐉🐊🐋🐌🐍🐎🐏🐐🐑🐒🐓🐔🐕🐖🐗🐘🐙🐚🐛🐜🐝🐞🐟🐠🐡🐢🐣🐤🐥🐦🐧🐨🐩🐪🐫🐬🐭🐮🐯🐰🐱🐲🐳🐴🐵🐶🐷🐸🐹🐺🐻🐼🐽🐾🐿😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟😠😡😢😣😤😥😦😧😨😩😪😫😬😭😮😯😰😱😲😳😴😵😶😷😸😹😺😻😼😽😾😿🙀🙁🙂🙃🙄🙅🙆🙇🙈🙉🙊🙋🙌🙍🙎🙏🤐🤑🤒🤓🤔🤕🤖🤗🤘🤙🤚🤛🤜🤝🤞🤟🤠🤡🤢🤣🤤🤥🤦🤧🤨🤩🤪🤫🤬🤭🤮🤯");
let encoded: String = b"Pack my box with five dozen liquor jugs."
.into_iter()
.copied()
.base256u_emoji()
.collect();
assert_eq!(
encoded,
"🐀🐑🐓🐛🍐🐝🐩🍐🐒🐟🐨🍐🐧🐙🐤🐘🍐🐖🐙🐦🐕🍐🐔🐟🐪🐕🐞🍐🐜🐙🐡🐥🐟🐢🍐🐚🐥🐗🐣🍞"
);
}
#[test]
fn decoding_emoji() {
let decoded: Vec<Option<u8>> = "🌰🌱🌲🌳🌴🌵🌶🌷🌸🌹🌺🌻🌼🌽🌾🌿🍀🍁🍂🍃🍄🍅🍆🍇🍈🍉🍊🍋🍌🍍🍎🍏🍐🍑🍒🍓🍔🍕🍖🍗🍘🍙🍚🍛🍜🍝🍞🍟🍠🍡🍢🍣🍤🍥🍦🍧🍨🍩🍪🍫🍬🍭🍮🍯🍰🍱🍲🍳🍴🍵🍶🍷🍸🍹🍺🍻🍼🍽🍾🍿🐀🐁🐂🐃🐄🐅🐆🐇🐈🐉🐊🐋🐌🐍🐎🐏🐐🐑🐒🐓🐔🐕🐖🐗🐘🐙🐚🐛🐜🐝🐞🐟🐠🐡🐢🐣🐤🐥🐦🐧🐨🐩🐪🐫🐬🐭🐮🐯🐰🐱🐲🐳🐴🐵🐶🐷🐸🐹🐺🐻🐼🐽🐾🐿😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟😠😡😢😣😤😥😦😧😨😩😪😫😬😭😮😯😰😱😲😳😴😵😶😷😸😹😺😻😼😽😾😿🙀🙁🙂🙃🙄🙅🙆🙇🙈🙉🙊🙋🙌🙍🙎🙏🤐🤑🤒🤓🤔🤕🤖🤗🤘🤙🤚🤛🤜🤝🤞🤟🤠🤡🤢🤣🤤🤥🤦🤧🤨🤩🤪🤫🤬🤭🤮🤯Ɲʼn".chars().base256u_emoji().collect();
let mut matcher: Vec<Option<u8>> = (u8::MIN..=u8::MAX).map(|b| Some(b)).collect();
matcher.push(None);
matcher.push(None);
assert_eq!(decoded, matcher);
let decoded: Vec<u8> =
"🐀🐑🐓🐛🍐🐝🐩🍐🐒🐟🐨🍐🐧🐙🐤🐘🍐🐖🐙🐦🐕🍐🐔🐟🐪🐕🐞🍐🐜🐙🐡🐥🐟🐢🍐🐚🐥🐗🐣🍞"
.chars()
.base256u_emoji()
.map(|c| c.unwrap())
.collect();
assert_eq!(
decoded.as_slice(),
b"Pack my box with five dozen liquor jugs."
);
}
}