use crate::hb;
use std::borrow::Borrow;
use std::ops::{Deref, DerefMut};
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
#[repr(transparent)]
pub struct Tag(pub hb::hb_tag_t);
impl Tag {
pub const fn new(a: char, b: char, c: char, d: char) -> Self {
Tag(((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32))
}
fn tag_to_string(self) -> String {
let mut buf: [u8; 4] = [0; 4];
unsafe { hb::hb_tag_to_string(self.0, buf.as_mut_ptr() as *mut _) };
String::from_utf8_lossy(&buf).into()
}
pub const fn to_bytes(self) -> [u8; 4] {
#[allow(clippy::identity_op)]
[
(self.0 >> 24 & 0xff) as u8,
(self.0 >> 16 & 0xff) as u8,
(self.0 >> 8 & 0xff) as u8,
(self.0 >> 0 & 0xff) as u8,
]
}
}
use std::fmt;
use std::fmt::{Debug, Display, Formatter};
impl Debug for Tag {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let string = self.tag_to_string();
let mut chars = string.chars().chain(std::iter::repeat('\u{FFFD}'));
write!(
f,
"Tag({:?}, {:?}, {:?}, {:?})",
chars.next().unwrap(),
chars.next().unwrap(),
chars.next().unwrap(),
chars.next().unwrap()
)
}
}
impl<'a> From<&'a [u8; 4]> for Tag {
fn from(byte_array: &'a [u8; 4]) -> Tag {
Tag::new(
byte_array[0] as char,
byte_array[1] as char,
byte_array[2] as char,
byte_array[3] as char,
)
}
}
impl From<Tag> for [u8; 4] {
fn from(tag: Tag) -> [u8; 4] {
tag.to_bytes()
}
}
impl Display for Tag {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.tag_to_string())
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TagFromStrErr {
NonAscii,
ZeroLengthString,
}
use std;
use std::str::FromStr;
impl FromStr for Tag {
type Err = TagFromStrErr;
fn from_str(s: &str) -> Result<Tag, TagFromStrErr> {
if !s.is_ascii() {
return Err(TagFromStrErr::NonAscii);
}
if s.is_empty() {
return Err(TagFromStrErr::ZeroLengthString);
}
let len = std::cmp::max(s.len(), 4) as i32;
unsafe { Ok(Tag(hb::hb_tag_from_string(s.as_ptr() as *mut _, len))) }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Direction {
Invalid,
Ltr,
Rtl,
Ttb,
Btt,
}
impl Direction {
pub fn to_raw(self) -> hb::hb_direction_t {
match self {
Direction::Invalid => hb::HB_DIRECTION_INVALID,
Direction::Ltr => hb::HB_DIRECTION_LTR,
Direction::Rtl => hb::HB_DIRECTION_RTL,
Direction::Ttb => hb::HB_DIRECTION_TTB,
Direction::Btt => hb::HB_DIRECTION_BTT,
}
}
pub fn from_raw(dir: hb::hb_direction_t) -> Self {
match dir {
hb::HB_DIRECTION_LTR => Direction::Ltr,
hb::HB_DIRECTION_RTL => Direction::Rtl,
hb::HB_DIRECTION_TTB => Direction::Ttb,
hb::HB_DIRECTION_BTT => Direction::Btt,
_ => Direction::Invalid,
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Language(pub hb::hb_language_t);
impl Default for Language {
fn default() -> Language {
Language(unsafe { hb::hb_language_get_default() })
}
}
impl Debug for Language {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Language(\"{}\")", self)
}
}
use std::ffi::CStr;
impl Display for Language {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let string = unsafe {
let char_ptr = hb::hb_language_to_string(self.0);
if char_ptr.is_null() {
return Err(fmt::Error);
}
CStr::from_ptr(char_ptr)
.to_str()
.expect("String representation of language is not valid utf8.")
};
write!(f, "{}", string)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct InvalidLanguage;
impl FromStr for Language {
type Err = InvalidLanguage;
fn from_str(s: &str) -> Result<Language, InvalidLanguage> {
let len = std::cmp::min(s.len(), std::i32::MAX as _) as i32;
let lang = unsafe { hb::hb_language_from_string(s.as_ptr() as *mut _, len) };
if lang.is_null() {
Err(InvalidLanguage {})
} else {
Ok(Language(lang))
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Script(pub hb::hb_script_t);
impl Script {
pub fn from_iso15924_tag(tag: Tag) -> Self {
Script(unsafe { hb::hb_script_from_iso15924_tag(tag.0) })
}
pub fn to_iso15924_tag(self) -> Tag {
Tag(unsafe { hb::hb_script_to_iso15924_tag(self.0) })
}
pub fn horizontal_direction(self) -> Direction {
Direction::from_raw(unsafe { hb::hb_script_get_horizontal_direction(self.0) })
}
}
pub unsafe trait HarfbuzzObject: Sized {
type Raw;
#[doc(hidden)]
unsafe fn from_raw(val: *const Self::Raw) -> Self;
fn as_raw(&self) -> *mut Self::Raw;
fn as_raw_mut(&mut self) -> *mut Self::Raw {
self.as_raw()
}
unsafe fn reference(&self);
unsafe fn dereference(&self);
}
#[derive(Debug, PartialEq, Eq)]
pub struct Shared<T: HarfbuzzObject> {
object: T,
}
impl<T: HarfbuzzObject> Shared<T> {
pub unsafe fn from_raw_owned(raw: *mut T::Raw) -> Self {
let object = T::from_raw(raw);
Shared { object }
}
pub fn into_raw(shared: Shared<T>) -> *mut T::Raw {
let result = shared.object.as_raw();
std::mem::forget(shared);
result
}
pub unsafe fn from_raw_ref(raw: *mut T::Raw) -> Self {
let object = T::from_raw(raw);
object.reference();
Shared { object }
}
}
impl<T: HarfbuzzObject> Clone for Shared<T> {
fn clone(&self) -> Self {
unsafe { Self::from_raw_ref(self.object.as_raw()) }
}
}
impl<T: HarfbuzzObject> Deref for Shared<T> {
type Target = T;
fn deref(&self) -> &T {
&self.object
}
}
impl<T: HarfbuzzObject> Borrow<T> for Shared<T> {
fn borrow(&self) -> &T {
self
}
}
impl<T: HarfbuzzObject> From<Owned<T>> for Shared<T> {
fn from(t: Owned<T>) -> Self {
let ptr = t.object.as_raw();
std::mem::forget(t);
unsafe { Shared::from_raw_owned(ptr) }
}
}
impl<T: HarfbuzzObject> Drop for Shared<T> {
fn drop(&mut self) {
unsafe { self.dereference() }
}
}
unsafe impl<T: HarfbuzzObject + Sync + Send> Send for Shared<T> {}
unsafe impl<T: HarfbuzzObject + Sync + Send> Sync for Shared<T> {}
#[derive(Debug, PartialEq, Eq)]
pub struct Owned<T: HarfbuzzObject> {
object: T,
}
impl<T: HarfbuzzObject> Owned<T> {
pub unsafe fn from_raw(raw: *mut T::Raw) -> Self {
Owned {
object: T::from_raw(raw),
}
}
pub fn into_raw(owned: Owned<T>) -> *mut T::Raw {
let result = owned.object.as_raw();
std::mem::forget(owned);
result
}
#[allow(clippy::wrong_self_convention)] pub fn to_shared(self) -> Shared<T> {
self.into()
}
}
impl<T: HarfbuzzObject> Drop for Owned<T> {
fn drop(&mut self) {
unsafe { self.dereference() }
}
}
impl<T: HarfbuzzObject> Deref for Owned<T> {
type Target = T;
fn deref(&self) -> &T {
&self.object
}
}
impl<T: HarfbuzzObject> DerefMut for Owned<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.object
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
use std::mem;
use std::rc::Rc;
use std::str::FromStr;
#[test]
fn test_tag_debugging() {
let tag = Tag::from_str("ABCD").unwrap();
assert_eq!("ABCD", format!("{}", tag));
assert_eq!("Tag('A', 'B', 'C', 'D')", format!("{:?}", tag));
}
#[test]
fn test_tag_creation() {
assert!(Tag::from_str("∞BCD").is_err());
assert!(Tag::from_str("").is_err());
assert_eq!(Tag::from_str("ABCDE"), Tag::from_str("ABCD"));
assert_eq!(Tag::from_str("abWd").unwrap(), Tag::new('a', 'b', 'W', 'd'));
}
#[test]
fn test_language() {
assert_eq!(Language::default().to_string(), "c");
assert_eq!(Language::from_str("ger").unwrap().to_string(), "ger");
assert_eq!(Language::from_str("ge!").unwrap().to_string(), "ge");
assert_eq!(Language::from_str("German").unwrap().to_string(), "german");
}
#[derive(Debug, Clone)]
struct ReferenceCounter {
share_count: Rc<Cell<isize>>,
}
unsafe impl HarfbuzzObject for ReferenceCounter {
type Raw = Cell<isize>;
unsafe fn from_raw(raw: *const Cell<isize>) -> Self {
ReferenceCounter {
share_count: Rc::from_raw(raw as *mut _),
}
}
fn as_raw(&self) -> *mut Cell<isize> {
Rc::into_raw(self.share_count.clone()) as *mut _
}
unsafe fn reference(&self) {
println!("referencing {:?}", self);
let rc = self.share_count.get();
self.share_count.set(rc + 1);
}
unsafe fn dereference(&self) {
println!("dereferencing {:?}", self);
let rc = self.share_count.get();
self.share_count.set(rc - 1);
}
}
#[test]
fn reference_counting_shared() {
let object = ReferenceCounter {
share_count: Rc::new(Cell::new(1)),
};
let raw = object.as_raw();
assert_eq!(Rc::strong_count(&object.share_count), 2);
let shared: Shared<ReferenceCounter> = unsafe { Shared::from_raw_owned(raw) };
assert_eq!(shared.share_count.get(), 1);
{
let shared2 = Shared::clone(&shared);
assert_eq!(shared.share_count.get(), 2);
mem::drop(shared2);
}
assert_eq!(shared.share_count.get(), 1);
mem::drop(shared);
assert_eq!(object.share_count.get(), 0);
assert_eq!(Rc::strong_count(&object.share_count), 1);
}
}