use super::{Char16, Char24, Char32, Char7, Char8, Chars, NonMaxU8, NonSurrogateU16, Strings};
use crate::error::{TextosError, TextosResult as Result};
use devela::codegen::paste;
macro_rules! impls {
($name:ident: $( $bits:literal ),+ ) => {
$( impls![@$name: $bits]; )+
};
(@$name:ident: $bits:literal) => { paste! {
impl Strings for [<$name $bits>] {}
impl Chars for [<$name $bits>] {
const MAX: Self = Self::MAX;
#[inline]
fn byte_len(self) -> usize { self.byte_len() }
#[inline]
fn len_utf8(self) -> usize { self.len_utf8() }
#[inline]
fn len_utf16(self) -> usize { self.len_utf16() }
#[inline]
fn encode_utf8(self, dst: &mut [u8]) -> &mut str {
self.to_char().encode_utf8(dst)
}
#[inline]
fn to_utf8_bytes(self) -> [u8; 4] {
let dyn_array = self.to_utf8_bytes();
let mut array = [0u8; 4];
for i in 0..dyn_array.len() {
array[i] = dyn_array[i];
}
array
}
#[inline]
fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] {
self.to_char().encode_utf16(dst)
}
#[inline]
fn to_digit(self, radix: u32) -> Option<u32> { self.to_digit(radix) }
#[inline]
fn to_ascii_uppercase(self) -> Self { self.to_ascii_uppercase() }
#[inline]
fn to_ascii_lowercase(self) -> Self { self.to_ascii_lowercase() }
#[inline]
fn is_noncharacter(self) -> bool { self.is_noncharacter() }
#[inline]
fn is_digit(self, radix: u32) -> bool { self.is_digit(radix) }
#[inline]
fn is_control(self) -> bool { self.to_char().is_control() }
#[inline]
fn is_nul(self) -> bool { self.is_nul() }
#[inline]
fn is_alphabetic(self) -> bool { self.to_char().is_alphabetic() }
#[inline]
fn is_numeric(self) -> bool { self.to_char().is_numeric() }
#[inline]
fn is_alphanumeric(self) -> bool { self.to_char().is_alphanumeric() }
#[inline]
fn is_lowercase(self) -> bool { self.to_char().is_lowercase() }
#[inline]
fn is_uppercase(self) -> bool { self.to_char().is_uppercase() }
#[inline]
fn is_whitespace(self) -> bool { self.to_char().is_whitespace() }
#[inline]
fn is_ascii(self) -> bool { self.is_ascii() }
}
impl [<$name $bits>] {
pub const fn byte_len(self) -> usize { byte_len(self.to_u32()) }
#[inline]
pub const fn len_utf8(self) -> usize { self.to_char().len_utf8() }
#[inline]
pub const fn len_utf16(self) -> usize { self.to_char().len_utf16() }
#[inline]
pub const fn to_digit(self, radix: u32) -> Option<u32> {
self.to_char().to_digit(radix)
}
#[inline]
pub const fn is_nul(self) -> bool { self.to_u32() == 0 }
#[inline]
pub const fn is_digit(self, radix: u32) -> bool {
if let Some(_) = self.to_digit(radix) { true } else { false }
}
}
}};
}
impls![Char: 7, 8, 16, 24, 32];
impl Char7 {
pub const MAX: Char7 = Char7::new_unchecked(0x7F);
#[inline]
pub const fn try_from_char8(c: Char8) -> Result<Char7> {
if is_7bit(c.to_u32()) {
Ok(Char7::new_unchecked(c.to_u32() as u8))
} else {
Err(TextosError::OutOfBounds)
}
}
#[inline]
pub const fn try_from_char16(c: Char16) -> Result<Char7> {
if is_7bit(c.to_u32()) {
Ok(Char7::new_unchecked(c.to_u32() as u8))
} else {
Err(TextosError::OutOfBounds)
}
}
#[inline]
pub const fn try_from_char24(c: Char24) -> Result<Char7> {
let c = c.to_u32();
if is_7bit(c) {
Ok(Char7::new_unchecked(c as u8))
} else {
Err(TextosError::OutOfBounds)
}
}
#[inline]
pub const fn try_from_char32(c: Char32) -> Result<Char7> {
if is_7bit(c.to_u32()) {
Ok(Char7::new_unchecked(c.to_u32() as u8))
} else {
Err(TextosError::OutOfBounds)
}
}
#[inline]
pub const fn try_from_char(c: char) -> Result<Char7> {
if is_7bit(c as u32) {
Ok(Char7::new_unchecked(c as u32 as u8))
} else {
Err(TextosError::OutOfBounds)
}
}
const fn from_char_unchecked(c: char) -> Char7 {
Char7::new_unchecked(c as u32 as u8)
}
const fn new_unchecked(value: u8) -> Char7 {
#[cfg(not(feature = "unsafe"))]
if let Some(c) = NonMaxU8::new(value) {
Char7(c)
} else {
unreachable![]
}
#[cfg(feature = "unsafe")]
unsafe {
Char7(NonMaxU8::new_unchecked(value))
}
}
#[inline]
pub const fn to_char8(self) -> Char8 {
Char8::from_char7(self)
}
#[inline]
pub const fn to_char16(self) -> Char16 {
Char16::from_char7(self)
}
#[inline]
pub const fn to_char24(self) -> Char24 {
Char24::from_char7(self)
}
#[inline]
pub const fn to_char32(self) -> Char32 {
Char32::from_char7(self)
}
#[inline]
#[rustfmt::skip]
pub const fn to_char(self) -> char {
self.0.get() as char
}
#[inline]
pub const fn to_u32(self) -> u32 {
self.0.get() as u32
}
#[inline]
#[allow(clippy::unusual_byte_groupings)]
pub const fn to_utf8_bytes(self) -> [u8; 1] {
[self.0.get()]
}
#[inline]
pub const fn is_noncharacter(self) -> bool {
false
}
#[inline]
pub const fn is_character(self) -> bool {
true
}
#[inline]
pub const fn is_ascii(self) -> bool {
true
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_uppercase(self) -> Char7 {
Self::from_char_unchecked(char::to_ascii_uppercase(&self.to_char()))
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_lowercase(self) -> Char7 {
Self::from_char_unchecked(char::to_ascii_lowercase(&self.to_char()))
}
}
impl Char8 {
pub const MAX: Char8 = Char8(0xFF);
#[inline]
pub const fn from_char7(c: Char7) -> Char8 {
Self(c.0.get())
}
#[inline]
pub const fn try_from_char16(c: Char16) -> Result<Char8> {
if byte_len(c.to_u32()) == 1 {
Ok(Char8(c.to_u32() as u8))
} else {
Err(TextosError::OutOfBounds)
}
}
#[inline]
pub const fn try_from_char24(c: Char24) -> Result<Char8> {
let c = c.to_u32();
if byte_len(c) == 1 {
Ok(Char8(c as u8))
} else {
Err(TextosError::OutOfBounds)
}
}
#[inline]
pub const fn try_from_char32(c: Char32) -> Result<Char8> {
if byte_len(c.to_u32()) == 1 {
Ok(Char8(c.to_u32() as u8))
} else {
Err(TextosError::OutOfBounds)
}
}
#[inline]
pub const fn try_from_char(c: char) -> Result<Char8> {
if byte_len(c as u32) == 1 {
Ok(Char8(c as u32 as u8))
} else {
Err(TextosError::OutOfBounds)
}
}
const fn from_char_unchecked(c: char) -> Char8 {
Char8(c as u32 as u8)
}
#[inline]
pub const fn try_to_char7(self) -> Result<Char7> {
Char7::try_from_char8(self)
}
#[inline]
pub const fn to_char16(self) -> Char16 {
Char16::from_char8(self)
}
#[inline]
pub const fn to_char24(self) -> Char24 {
Char24::from_char8(self)
}
#[inline]
pub const fn to_char32(self) -> Char32 {
Char32::from_char8(self)
}
#[inline]
#[rustfmt::skip]
pub const fn to_char(self) -> char {
self.0 as char
}
#[inline]
pub const fn to_u32(self) -> u32 {
self.0 as u32
}
#[inline]
#[allow(clippy::unusual_byte_groupings)]
pub const fn to_utf8_bytes(self) -> [u8; 2] {
let c = self.0;
match c {
0x0000..=0x007F => [c, 0],
0x0080.. => {
let y = 0b10_000000 | (0b0011_1111 & c);
let x = 0b110_00000 | (c >> 6);
[x, y]
}
}
}
#[inline]
pub const fn is_noncharacter(self) -> bool {
is_noncharacter(self.0 as u32)
}
#[inline]
pub const fn is_character(self) -> bool {
!self.is_noncharacter()
}
#[inline]
pub const fn is_ascii(self) -> bool {
self.0 <= 0x7F
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_uppercase(self) -> Char8 {
Self::from_char_unchecked(char::to_ascii_uppercase(&self.to_char()))
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_lowercase(self) -> Char8 {
Self::from_char_unchecked(char::to_ascii_lowercase(&self.to_char()))
}
}
impl Char16 {
pub const MAX: Char16 = Char16::new_unchecked(0xFFFF);
pub const REPLACEMENT_CHARACTER: Char16 =
Char16::new_unchecked(char::REPLACEMENT_CHARACTER as u32 as u16);
#[inline]
pub const fn from_char7(c: Char7) -> Char16 {
Char16::new_unchecked(c.0.get() as u16)
}
#[inline]
pub const fn from_char8(c: Char8) -> Char16 {
Char16::new_unchecked(c.0 as u16)
}
#[inline]
pub const fn try_from_char24(c: Char24) -> Result<Char16> {
let c = c.to_u32();
if byte_len(c) == 1 {
Ok(Char16::new_unchecked(c as u16))
} else {
Err(TextosError::OutOfBounds)
}
}
#[inline]
pub const fn try_from_char32(c: Char32) -> Result<Char16> {
Self::try_from_char(c.to_char())
}
#[inline]
pub const fn try_from_char(c: char) -> Result<Char16> {
if byte_len(c as u32) <= 2 {
Ok(Char16::new_unchecked(c as u32 as u16))
} else {
Err(TextosError::OutOfBounds)
}
}
const fn from_char_unchecked(c: char) -> Char16 {
Char16::new_unchecked(c as u32 as u16)
}
const fn new_unchecked(value: u16) -> Char16 {
#[cfg(not(feature = "unsafe"))]
if let Some(c) = NonSurrogateU16::new(value) {
Char16(c)
} else {
unreachable![]
}
#[cfg(feature = "unsafe")]
unsafe {
Char16(NonSurrogateU16::new_unchecked(value))
}
}
#[inline]
pub const fn try_to_char7(self) -> Result<Char7> {
Char7::try_from_char16(self)
}
#[inline]
pub const fn try_to_char8(self) -> Result<Char8> {
Char8::try_from_char16(self)
}
#[inline]
pub const fn to_char24(self) -> Char24 {
Char24::from_char16(self)
}
#[inline]
pub const fn to_char32(self) -> Char32 {
Char32::from_char16(self)
}
#[inline]
#[rustfmt::skip]
pub const fn to_char(self) -> char {
if let Some(c) = char::from_u32(self.0.get() as u32) { c } else { unreachable![] }
}
#[inline]
pub const fn to_u32(self) -> u32 {
self.0.get() as u32
}
#[inline]
#[allow(clippy::unusual_byte_groupings)]
pub const fn to_utf8_bytes(self) -> [u8; 3] {
let c = self.0.get();
match c {
0x0000..=0x007F => [c as u8, 0, 0],
0x0080..=0x07FF => {
let y = 0b10_000000 | (0b0011_1111 & (c as u8));
let x = 0b110_00000 | ((c >> 6) as u8);
[x, y, 0]
}
0x0800..=0xFFFF => {
let z = 0b10_000000 | (0b0011_1111 & (c as u8));
let y = 0b10_000000 | ((c >> 6) & 0b0011_1111) as u8;
let x = 0b1110_0000 | ((c >> 12) as u8);
[x, y, z]
}
}
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_uppercase(self) -> Char16 {
Self::from_char_unchecked(char::to_ascii_uppercase(&self.to_char()))
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_lowercase(self) -> Char16 {
Self::from_char_unchecked(char::to_ascii_lowercase(&self.to_char()))
}
#[inline]
pub const fn is_noncharacter(self) -> bool {
is_noncharacter(self.0.get() as u32)
}
#[inline]
pub const fn is_character(self) -> bool {
!self.is_noncharacter()
}
#[inline]
pub const fn is_ascii(self) -> bool {
self.0.get() <= 0x7F
}
}
impl Char24 {
pub const MAX: Char24 = Char24::from_char('\u{10ffff}');
pub const REPLACEMENT_CHARACTER: Char24 = Char24::from_char(char::REPLACEMENT_CHARACTER);
#[inline]
pub const fn from_char7(c: Char7) -> Char24 {
Char24 {
hi: Self::new_unchecked_hi(0),
mi: 0,
lo: c.0.get(),
}
}
#[inline]
pub const fn from_char8(c: Char8) -> Char24 {
Char24 {
hi: Self::new_unchecked_hi(0),
mi: 0,
lo: c.0,
}
}
#[inline]
pub const fn from_char16(c: Char16) -> Char24 {
let mi = ((c.0.get() & 0xFF00) >> 8) as u8;
let lo = (c.0.get() & 0x00FF) as u8;
Char24 {
hi: Self::new_unchecked_hi(0),
mi,
lo,
}
}
#[inline]
pub const fn from_char32(c: Char32) -> Char24 {
Char24::from_char(c.0)
}
#[inline]
pub const fn from_char(c: char) -> Char24 {
let hi = ((c as u32 & 0x001F0000) >> 16) as u8;
let mi = ((c as u32 & 0x0000FF00) >> 8) as u8;
let lo = (c as u32 & 0x000000FF) as u8;
Char24 {
hi: Self::new_unchecked_hi(hi),
mi,
lo,
}
}
const fn new_unchecked_hi(value: u8) -> NonMaxU8 {
#[cfg(not(feature = "unsafe"))]
if let Some(c) = NonMaxU8::new(value) {
c
} else {
unreachable![]
}
#[cfg(feature = "unsafe")]
unsafe {
NonMaxU8::new_unchecked(value)
}
}
#[inline]
pub const fn try_to_char7(self) -> Result<Char7> {
Char7::try_from_char24(self)
}
#[inline]
pub const fn try_to_char8(self) -> Result<Char8> {
Char8::try_from_char24(self)
}
#[inline]
pub const fn try_to_char16(self) -> Result<Char16> {
Char16::try_from_char24(self)
}
#[inline]
pub const fn to_char32(self) -> Char32 {
Char32(self.to_char())
}
#[inline]
pub const fn to_u32(self) -> u32 {
(self.hi.get() as u32) << 16 | (self.mi as u32) << 8 | (self.lo as u32)
}
#[inline]
#[rustfmt::skip]
pub const fn to_char(self) -> char {
if let Some(c) = char::from_u32(self.to_u32()) { c } else { unreachable![] }
}
#[inline]
#[allow(clippy::unusual_byte_groupings)]
pub const fn to_utf8_bytes(self) -> [u8; 4] {
let c = self.to_u32();
match c {
0x0000..=0x007F => [c as u8, 0, 0, 0],
0x0080..=0x07FF => {
let y = 0b10_000000 | (0b0011_1111 & (c as u8));
let x = 0b110_00000 | ((c >> 6) as u8);
[x, y, 0, 0]
}
0x0800..=0xFFFF => {
let z = 0b10_000000 | (0b0011_1111 & (c as u8));
let y = 0b10_000000 | ((c >> 6) & 0b0011_1111) as u8;
let x = 0b1110_0000 | ((c >> 12) as u8);
[x, y, z, 0]
}
_ => {
let w = 0b10_000000 | (0b0011_1111 & (c as u8));
let z = 0b10_000000 | ((c >> 6) & 0b0011_1111) as u8;
let y = 0b10_000000 | ((c >> 12) & 0b0011_1111) as u8;
let x = 0b11110_000 | ((c >> 18) as u8);
[x, y, z, w]
}
}
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_uppercase(self) -> Char24 {
Self::from_char(char::to_ascii_uppercase(&self.to_char()))
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_lowercase(self) -> Char24 {
Self::from_char(char::to_ascii_lowercase(&self.to_char()))
}
#[inline]
pub const fn is_noncharacter(self) -> bool {
is_noncharacter(self.to_u32())
}
#[inline]
pub const fn is_character(self) -> bool {
!self.is_noncharacter()
}
#[inline]
pub const fn is_ascii(self) -> bool {
self.to_u32() <= 0x7F
}
}
impl Char32 {
pub const MAX: Char32 = Char32(char::MAX);
pub const REPLACEMENT_CHARACTER: Char32 = Char32(char::REPLACEMENT_CHARACTER);
#[inline]
pub const fn from_char7(c: Char7) -> Char32 {
Char32(c.to_char())
}
#[inline]
pub const fn from_char8(c: Char8) -> Char32 {
Char32(c.to_char())
}
#[inline]
pub const fn from_char16(c: Char16) -> Char32 {
Char32(c.to_char())
}
#[inline]
pub const fn from_char24(c: Char24) -> Char32 {
Char32(c.to_char())
}
#[inline]
pub const fn from_char(c: char) -> Char32 {
Char32(c)
}
#[inline]
pub const fn try_to_char7(self) -> Result<Char7> {
Char7::try_from_char32(self)
}
#[inline]
pub const fn try_to_char8(self) -> Result<Char8> {
Char8::try_from_char32(self)
}
#[inline]
pub const fn try_to_char16(self) -> Result<Char16> {
Char16::try_from_char32(self)
}
#[inline]
pub const fn to_char24(self) -> Char24 {
Char24::from_char32(self)
}
#[inline]
pub const fn to_char(self) -> char {
self.0
}
#[inline]
pub const fn to_u32(self) -> u32 {
self.0 as u32
}
#[inline]
#[allow(clippy::unusual_byte_groupings)]
pub const fn to_utf8_bytes(self) -> [u8; 4] {
let c = self.0 as u32;
match c {
0x0000..=0x007F => [c as u8, 0, 0, 0],
0x0080..=0x07FF => {
let y = 0b10_000000 | (0b0011_1111 & (c as u8));
let x = 0b110_00000 | ((c >> 6) as u8);
[x, y, 0, 0]
}
0x0800..=0xFFFF => {
let z = 0b10_000000 | (0b0011_1111 & (c as u8));
let y = 0b10_000000 | ((c >> 6) & 0b0011_1111) as u8;
let x = 0b1110_0000 | ((c >> 12) as u8);
[x, y, z, 0]
}
_ => {
let w = 0b10_000000 | (0b0011_1111 & (c as u8));
let z = 0b10_000000 | ((c >> 6) & 0b0011_1111) as u8;
let y = 0b10_000000 | ((c >> 12) & 0b0011_1111) as u8;
let x = 0b11110_000 | ((c >> 18) as u8);
[x, y, z, w]
}
}
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_uppercase(self) -> Char32 {
Char32(char::to_ascii_uppercase(&self.0))
}
#[inline]
#[rustfmt::skip]
pub const fn to_ascii_lowercase(self) -> Char32 {
Char32(char::to_ascii_lowercase(&self.0))
}
#[inline]
pub const fn is_noncharacter(self) -> bool {
is_noncharacter(self.0 as u32)
}
#[inline]
pub const fn is_character(self) -> bool {
!self.is_noncharacter()
}
#[inline]
pub const fn is_ascii(self) -> bool {
char::is_ascii(&self.0)
}
}
impl Strings for char {}
impl Chars for char {
const MAX: Self = Self::MAX;
#[inline]
fn byte_len(self) -> usize {
byte_len(self as u32)
}
#[inline]
fn len_utf8(self) -> usize {
self.len_utf8()
}
#[inline]
fn len_utf16(self) -> usize {
self.len_utf16()
}
#[inline]
fn encode_utf8(self, dst: &mut [u8]) -> &mut str {
self.encode_utf8(dst)
}
#[inline]
fn to_utf8_bytes(self) -> [u8; 4] {
Char32(self).to_utf8_bytes()
}
#[inline]
fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] {
self.encode_utf16(dst)
}
#[inline]
fn to_digit(self, radix: u32) -> Option<u32> {
self.to_digit(radix)
}
#[inline]
fn to_ascii_uppercase(self) -> char {
char::to_ascii_uppercase(&self)
}
#[inline]
fn to_ascii_lowercase(self) -> char {
char::to_ascii_lowercase(&self)
}
#[inline]
fn is_noncharacter(self) -> bool {
is_noncharacter(self as u32)
}
#[inline]
fn is_digit(self, radix: u32) -> bool {
self.is_digit(radix)
}
#[inline]
fn is_control(self) -> bool {
self.is_control()
}
#[inline]
fn is_nul(self) -> bool {
self as u32 == 0
}
#[inline]
fn is_alphabetic(self) -> bool {
self.is_alphabetic()
}
#[inline]
fn is_numeric(self) -> bool {
self.is_numeric()
}
#[inline]
fn is_alphanumeric(self) -> bool {
self.is_alphanumeric()
}
#[inline]
fn is_lowercase(self) -> bool {
self.is_lowercase()
}
#[inline]
fn is_uppercase(self) -> bool {
self.is_uppercase()
}
#[inline]
fn is_whitespace(self) -> bool {
self.is_whitespace()
}
#[inline]
fn is_ascii(self) -> bool {
(self as u32) <= 0x7F
}
}
#[inline]
const fn is_noncharacter(code: u32) -> bool {
(code >= 0xFDD0 && code <= 0xFDEF)
|| (code >= 0xFFFE && (code & 0xFF) == 0xFE)
|| (code >= 0xFFFE && (code & 0xFF) == 0xFF)
|| (code >= 0x2FE0 && code <= 0x2FEF)
}
#[inline]
const fn is_7bit(code: u32) -> bool {
code <= 0x7F
}
#[inline]
const fn byte_len(code: u32) -> usize {
match code {
0..=0xFF => 1,
0x100..=0xFFFF => 2,
_ => 3,
}
}
#[inline]
pub(crate) const fn char_utf8_2bytes_len(bytes: [u8; 2]) -> u8 {
1 + ((bytes[1] > 0) & (bytes[1] & 0b1100_0000 != 0b1000_0000)) as u8
}
#[inline]
pub(crate) const fn char_utf8_3bytes_len(bytes: [u8; 3]) -> u8 {
1 + ((bytes[1] > 0) & (bytes[1] & 0b1100_0000 != 0b1000_0000)) as u8
+ ((bytes[2] > 0) & (bytes[2] & 0b1100_0000 != 0b1000_0000)) as u8
}
#[inline]
pub(crate) const fn char_utf8_4bytes_len(bytes: [u8; 4]) -> u8 {
1 + ((bytes[1] > 0) & (bytes[1] & 0b1100_0000 != 0b1000_0000)) as u8
+ ((bytes[2] > 0) & (bytes[2] & 0b1100_0000 != 0b1000_0000)) as u8
+ ((bytes[3] > 0) & (bytes[3] & 0b1100_0000 != 0b1000_0000)) as u8
}