use crate::{
platform::{EncodingError, EncodingId, PlatformId},
tables::{Table, TableDirectory, TableError},
types::{Offset16, Tag, tags, uint16},
util::custom_iterator,
};
use std::{borrow::Cow, bstr::ByteStr};
#[repr(C)]
pub struct NameV0 {
pub version: uint16,
pub count: uint16,
pub storage_offset: Offset16,
name_records: [NameRecordRaw; 0],
}
#[repr(C)]
pub struct NameV1 {
v0: NameV0,
}
const impl std::ops::Deref for NameV1 {
type Target = NameV0;
fn deref(&self) -> &Self::Target {
&self.v0
}
}
#[repr(C)]
pub struct NameRecordRaw {
pub platform_id: uint16,
pub encoding_id: uint16,
pub language_id: uint16,
pub name_id: uint16,
pub length: uint16,
pub string_offset: Offset16,
}
#[repr(C)]
pub struct LangTagRecordRaw {
pub length: uint16,
pub lang_tag_offset: Offset16,
}
impl<'a> Table<'a> for Name<'a> {
const TAG: Tag = tags::name;
fn new_in(dir: &'a TableDirectory) -> Result<Self, TableError> {
let rec = dir.table_record(Self::TAG).ok_or(TableError::NotFound)?;
let v0 = rec.raw_as::<NameV0>().ok_or(TableError::InvalidLen)?;
let mut required_len =
size_of::<NameV0>() + v0.count.get() as usize * size_of::<NameRecordRaw>();
if (rec.length.get() as usize) < required_len {
return Err(TableError::InvalidLen);
}
let storage_offset = v0.storage_offset.get() as u32;
for name in v0.name_records() {
let offset = (name.string_offset.get() as u32) + (name.length.get() as u32);
if rec.length.get() < storage_offset + offset {
return Err(TableError::InvalidLen);
}
}
if v0.version.get() >= 1 {
required_len += size_of::<uint16>(); if (rec.length.get() as usize) < required_len {
return Err(TableError::InvalidLen);
}
let v1 = rec.raw_as::<NameV1>().unwrap();
required_len += v1.lang_tag_count().get() as usize * size_of::<LangTagRecordRaw>();
if (rec.length.get() as usize) < required_len {
return Err(TableError::InvalidLen);
}
for tag in v1.lang_tag_records() {
let offset = (tag.lang_tag_offset.get() as u32) + (tag.length.get() as u32);
if rec.length.get() < storage_offset + offset {
return Err(TableError::InvalidLen);
}
}
}
Ok(Self { name: v0 })
}
}
#[derive(Copy)]
#[derive_const(Clone)]
pub struct Name<'a> {
name: &'a NameV0,
}
const impl<'a> std::ops::Deref for Name<'a> {
type Target = &'a NameV0;
fn deref(&self) -> &Self::Target {
&self.name
}
}
impl<'a> Name<'a> {
pub const fn version(&self) -> u16 {
self.version.get()
}
pub const fn v1(&self) -> Option<&'a NameV1> {
if self.version() >= 1 {
Some(unsafe { std::mem::transmute::<&NameV0, &NameV1>(self.name) })
} else {
None
}
}
pub const fn names(&self) -> NameRecordsIter<'a> {
NameRecordsIter::new(*self)
}
pub const fn lang_tags(&self) -> LangTagRecordsIter<'a> {
LangTagRecordsIter::new(*self)
}
pub const fn lang_tag_count(&self) -> Option<uint16> {
Some(self.v1()?.lang_tag_count())
}
pub const fn lang_tag_records(&self) -> Option<&'a [LangTagRecordRaw]> {
Some(self.v1()?.lang_tag_records())
}
}
impl NameV0 {
pub const fn name_records(&self) -> &[NameRecordRaw] {
unsafe { std::slice::from_raw_parts(self.name_records.as_ptr(), self.count.get() as _) }
}
pub const fn string_storage(&self) -> &StringStorage {
let offset = self.storage_offset.get() as usize;
unsafe { &*std::ptr::from_ref(self).byte_add(offset).cast() }
}
}
impl NameV1 {
pub const fn lang_tag_count(&self) -> uint16 {
unsafe { *self.name_records().as_ptr_range().end.cast::<uint16>() }
}
pub const fn lang_tag_records(&self) -> &[LangTagRecordRaw] {
let len_ptr = self.name_records().as_ptr_range().end.cast::<uint16>();
unsafe { std::slice::from_raw_parts(len_ptr.add(1).cast(), (*len_ptr).get() as _) }
}
}
pub struct StringStorage {
_dont_instantiate: (),
}
impl StringStorage {
pub const fn as_ptr(&self) -> *const u8 {
std::ptr::from_ref(self).cast()
}
pub(crate) const unsafe fn get(&self, offset: u16, length: u16) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.as_ptr().add(offset as _), length as _) }
}
}
#[derive(Copy)]
#[derive_const(Clone)]
pub struct NameRecord<'a>(Name<'a>, &'a NameRecordRaw);
#[derive(Copy)]
#[derive_const(Clone)]
pub struct LangTagRecord<'a>(Name<'a>, &'a LangTagRecordRaw);
const impl<'a> std::ops::Deref for NameRecord<'a> {
type Target = &'a NameRecordRaw;
fn deref(&self) -> &Self::Target {
&self.1
}
}
const impl<'a> std::ops::Deref for LangTagRecord<'a> {
type Target = &'a LangTagRecordRaw;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<'a> NameRecord<'a> {
pub const fn bytes(&self) -> &'a [u8] {
unsafe { self.0.string_storage().get(self.1.string_offset.get(), self.1.length.get()) }
}
pub fn string(&self) -> Result<String, EncodingError> {
let encoding = EncodingId::new(self.1.platform_id.get(), self.1.encoding_id.get())?;
encoding.decode_utf16be(self.bytes())
}
}
impl<'a> LangTagRecord<'a> {
pub const fn bytes(&self) -> &'a [u8] {
unsafe { self.0.string_storage().get(self.1.lang_tag_offset.get(), self.1.length.get()) }
}
pub fn string(&self) -> String {
String::from_utf16be_lossy(self.bytes())
}
}
#[derive(Clone)]
pub struct NameRecordsIter<'a> {
table: Name<'a>,
inner: std::slice::Iter<'a, NameRecordRaw>,
}
impl<'a> NameRecordsIter<'a> {
pub const fn new(table: Name<'a>) -> Self {
Self { table, inner: table.name_records().iter() }
}
pub fn as_records(&self) -> &'a [NameRecordRaw] {
self.inner.as_slice()
}
}
custom_iterator!(NameRecordsIter<'a> as this {
type Item = NameRecord<'a>;
map: |x| NameRecord(this.table, x);
});
#[derive(Clone)]
pub struct LangTagRecordsIter<'a> {
table: Name<'a>,
inner: std::slice::Iter<'a, LangTagRecordRaw>,
}
impl<'a> LangTagRecordsIter<'a> {
pub const fn new(table: Name<'a>) -> Self {
Self { table, inner: table.lang_tag_records().unwrap_or(&[]).iter() }
}
pub fn as_records(&self) -> &'a [LangTagRecordRaw] {
self.inner.as_slice()
}
}
custom_iterator!(LangTagRecordsIter<'a> as this {
type Item = LangTagRecord<'a>;
map: |x| LangTagRecord(this.table, x);
});
impl std::fmt::Debug for Name<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut f = f.debug_struct("NameTable");
f.field("version", &self.version.get())
.field("count", &self.count.get())
.field_with("storage_offset", |f| write!(f, "{:#06X}", self.storage_offset))
.field_with("name_records", |f| f.debug_list().entries(self.names()).finish());
if let Some(v1) = self.v1() {
f.field("lang_tag_count", &v1.lang_tag_records().len());
f.field_with("lang_tag_records", |f| f.debug_list().entries(self.lang_tags()).finish());
}
f.finish()
}
}
impl std::fmt::Debug for NameV0 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Name { name: self }.fmt(f)
}
}
impl std::fmt::Debug for NameV1 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Name { name: self }.fmt(f)
}
}
impl std::fmt::Debug for NameRecord<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let Self(name, rec) = *self;
let value = self.string().map_err(|err| (err, ByteStr::new(self.bytes())));
let plat_id = PlatformId::new(rec.platform_id.get());
let plat_name = plat_id.map_or("Unknown", |x| x.name());
let enc_id = plat_id.and_then(|x| x.encoding(rec.encoding_id.get()));
let enc_name = enc_id.map_or(Cow::Borrowed("Unknown"), |x| x.name());
let lang_id = plat_id.and_then(|x| x.language(rec.language_id.get()));
let lang_name = format!(
"{}: {}",
lang_id.and_then(|x| x.tag(Some(name))).unwrap_or(Cow::Borrowed("und")),
lang_id.and_then(|x| x.english_name(Some(name))).unwrap_or(Cow::Borrowed("Unknown")),
);
f.debug_struct("NameRecord")
.field_with("platform_id", |f| write!(f, "{} ({})", rec.platform_id, plat_name))
.field_with("encoding_id", |f| write!(f, "{} ({})", rec.encoding_id, enc_name))
.field_with("language_id", |f| write!(f, "{:#06X} ({})", rec.language_id, lang_name))
.field("name_id", &rec.name_id.get())
.field("length", &rec.length.get())
.field_with("string_offset", |f| write!(f, "{:#06X}", rec.string_offset))
.field_with("value", |f| {
let mut f = f.with_options(*f.options().alternate(false));
value.fmt(&mut f)
})
.finish()
}
}
impl std::fmt::Debug for LangTagRecord<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let Self(_, rec) = *self;
f.debug_struct("LangTagRecord")
.field("length", &rec.length.get())
.field_with("lang_tag_offset", |f| write!(f, "{:#06X}", rec.lang_tag_offset))
.field_with("value", |f| {
let mut f = f.with_options(*f.options().alternate(false));
self.string().fmt(&mut f)
})
.finish()
}
}