use qubit_io::{
BigEndian,
BinaryCodec,
ByteOrder,
LittleEndian,
};
use crate::{
Charset,
CharsetDecodeError,
CharsetDecodeErrorKind,
CharsetDecodeResult,
CharsetEncodeError,
CharsetEncodeErrorKind,
CharsetEncodeResult,
DecodeStatus,
Unicode,
Utf16,
};
pub(crate) fn decode_units_prefix(input: &[u16], index: usize) -> CharsetDecodeResult<DecodeStatus> {
if index > input.len() {
let kind = CharsetDecodeErrorKind::MalformedSequence { value: None };
return Err(CharsetDecodeError::new(Charset::UTF_16, kind, index));
}
if index == input.len() {
return Ok(DecodeStatus::NeedMore {
required: index + 1,
available: 0,
});
}
let first = input[index];
if Utf16::is_high_surrogate(first) {
if input.len() < index + 2 {
return Ok(DecodeStatus::NeedMore {
required: index + 2,
available: input.len() - index,
});
}
let second = input[index + 1];
match Utf16::compose_pair(first, second).and_then(Unicode::to_char) {
Some(ch) => Ok(DecodeStatus::Complete { value: ch, consumed: 2 }),
None => {
let kind = CharsetDecodeErrorKind::MalformedSequence {
value: Some(second as u32),
};
Err(CharsetDecodeError::new(Charset::UTF_16, kind, index + 1))
}
}
} else if Utf16::is_low_surrogate(first) {
let kind = CharsetDecodeErrorKind::MalformedSequence {
value: Some(first as u32),
};
Err(CharsetDecodeError::new(Charset::UTF_16, kind, index))
} else {
let ch = char::from_u32(first as u32).expect("non-surrogate UTF-16 unit is a scalar value");
Ok(DecodeStatus::Complete { value: ch, consumed: 1 })
}
}
pub(crate) fn encode_units_char(ch: char, output: &mut [u16], index: usize) -> CharsetEncodeResult<usize> {
if index > output.len() {
let kind = CharsetEncodeErrorKind::BufferTooSmall {
required: index + 1,
available: 0,
};
return Err(CharsetEncodeError::new(Charset::UTF_16, kind, index));
}
let length = Utf16::unit_len(ch);
let available = output.len() - index;
if available < length {
let kind = CharsetEncodeErrorKind::BufferTooSmall {
required: index + length,
available,
};
return Err(CharsetEncodeError::new(Charset::UTF_16, kind, index));
}
let code_point = ch as u32;
if length == 1 {
output[index] = code_point as u16;
} else {
output[index] = Utf16::high_surrogate(code_point).expect("supplementary scalar has high surrogate");
output[index + 1] = Utf16::low_surrogate(code_point).expect("supplementary scalar has low surrogate");
}
Ok(length)
}
pub(crate) fn decode_bytes_prefix(
input: &[u8],
index: usize,
byte_order: ByteOrder,
) -> CharsetDecodeResult<DecodeStatus> {
let charset = Charset::from_utf16_byte_order(byte_order);
if index > input.len() {
let kind = CharsetDecodeErrorKind::MalformedSequence { value: None };
return Err(CharsetDecodeError::new(charset, kind, index));
}
let available = input.len() - index;
if available < 2 {
return Ok(DecodeStatus::NeedMore {
required: index.saturating_add(2),
available,
});
}
let first = match byte_order {
ByteOrder::BigEndian => unsafe { BinaryCodec::<u16, BigEndian>::read_unchecked(input, index) },
ByteOrder::LittleEndian => unsafe { BinaryCodec::<u16, LittleEndian>::read_unchecked(input, index) },
};
if Utf16::is_high_surrogate(first) {
if available < 4 {
return Ok(DecodeStatus::NeedMore {
required: index.saturating_add(4),
available,
});
}
let second = match byte_order {
ByteOrder::BigEndian => unsafe { BinaryCodec::<u16, BigEndian>::read_unchecked(input, index + 2) },
ByteOrder::LittleEndian => unsafe { BinaryCodec::<u16, LittleEndian>::read_unchecked(input, index + 2) },
};
match Utf16::compose_pair(first, second).and_then(Unicode::to_char) {
Some(ch) => Ok(DecodeStatus::Complete { value: ch, consumed: 4 }),
None => {
let kind = CharsetDecodeErrorKind::MalformedSequence {
value: Some(second as u32),
};
Err(CharsetDecodeError::new(charset, kind, index + 2))
}
}
} else if Utf16::is_low_surrogate(first) {
let kind = CharsetDecodeErrorKind::MalformedSequence {
value: Some(first as u32),
};
Err(CharsetDecodeError::new(charset, kind, index))
} else {
let ch = char::from_u32(first as u32).expect("non-surrogate UTF-16 unit is a scalar value");
Ok(DecodeStatus::Complete { value: ch, consumed: 2 })
}
}
pub(crate) fn encode_bytes_char(
ch: char,
output: &mut [u8],
byte_order: ByteOrder,
index: usize,
) -> CharsetEncodeResult<usize> {
let charset = Charset::from_utf16_byte_order(byte_order);
if index > output.len() {
let kind = CharsetEncodeErrorKind::BufferTooSmall {
required: index + 2,
available: 0,
};
return Err(CharsetEncodeError::new(charset, kind, index));
}
let required = Utf16::unit_len(ch) * 2;
let available = output.len() - index;
if available < required {
let kind = CharsetEncodeErrorKind::BufferTooSmall {
required: index + required,
available,
};
return Err(CharsetEncodeError::new(charset, kind, index));
}
let mut units = [0_u16; Utf16::MAX_UNITS_PER_CHAR];
let unit_count = encode_units_char(ch, &mut units, 0)?;
for (unit_index, unit) in units.iter().take(unit_count).enumerate() {
let offset = index + unit_index * 2;
match byte_order {
ByteOrder::BigEndian => unsafe { BinaryCodec::<u16, BigEndian>::write_unchecked(output, offset, *unit) },
ByteOrder::LittleEndian => unsafe {
BinaryCodec::<u16, LittleEndian>::write_unchecked(output, offset, *unit)
},
}
}
Ok(required)
}