#![allow(dead_code)]
use std::{
fmt::Debug,
hash::{Hash, Hasher},
marker::PhantomData,
mem::{self, MaybeUninit},
ops::{Deref, DerefMut, Index, IndexMut},
ptr::{self, NonNull},
slice,
};
use skia_bindings::{
sk_sp, C_SkRefCntBase_ref, C_SkRefCntBase_unique, C_SkRefCntBase_unref, SkRefCnt, SkRefCntBase,
};
pub(crate) unsafe fn transmute_ref<FromT, ToT>(from: &FromT) -> &ToT {
debug_assert_eq!(mem::size_of::<FromT>(), mem::size_of::<ToT>());
debug_assert_eq!(mem::align_of::<FromT>(), mem::align_of::<ToT>());
&*(from as *const FromT as *const ToT)
}
pub(crate) unsafe fn transmute_ref_mut<FromT, ToT>(from: &mut FromT) -> &mut ToT {
debug_assert_eq!(mem::size_of::<FromT>(), mem::size_of::<ToT>());
debug_assert_eq!(mem::align_of::<FromT>(), mem::align_of::<ToT>());
&mut *(from as *mut FromT as *mut ToT)
}
pub(crate) trait IntoOption {
type Target;
fn into_option(self) -> Option<Self::Target>;
}
impl<T> IntoOption for *const T {
type Target = *const T;
fn into_option(self) -> Option<Self::Target> {
if !self.is_null() {
Some(self)
} else {
None
}
}
}
impl<T> IntoOption for *mut T {
type Target = ptr::NonNull<T>;
fn into_option(self) -> Option<Self::Target> {
ptr::NonNull::new(self)
}
}
impl IntoOption for bool {
type Target = ();
fn into_option(self) -> Option<Self::Target> {
if self {
Some(())
} else {
None
}
}
}
pub(crate) trait IfBoolSome {
fn if_true_some<V>(self, v: V) -> Option<V>;
fn if_false_some<V>(self, v: V) -> Option<V>;
fn if_true_then_some<V>(self, f: impl FnOnce() -> V) -> Option<V>;
fn if_false_then_some<V>(self, f: impl FnOnce() -> V) -> Option<V>;
}
impl IfBoolSome for bool {
fn if_true_some<V>(self, v: V) -> Option<V> {
self.into_option().and(Some(v))
}
fn if_false_some<V>(self, v: V) -> Option<V> {
(!self).if_true_some(v)
}
fn if_true_then_some<V>(self, f: impl FnOnce() -> V) -> Option<V> {
self.into_option().map(|()| f())
}
fn if_false_then_some<V>(self, f: impl FnOnce() -> V) -> Option<V> {
(!self).into_option().map(|()| f())
}
}
#[cfg(test)]
pub(crate) trait RefCount {
fn ref_cnt(&self) -> usize;
}
#[cfg(test)]
impl RefCount for SkRefCntBase {
#[allow(clippy::cast_ptr_alignment)]
fn ref_cnt(&self) -> usize {
unsafe {
let ptr: *const i32 = &self.fRefCnt as *const _ as *const i32;
(*ptr).try_into().unwrap()
}
}
}
impl NativeBase<SkRefCntBase> for SkRefCnt {}
#[cfg(test)]
impl RefCount for SkRefCnt {
fn ref_cnt(&self) -> usize {
self.base().ref_cnt()
}
}
#[cfg(test)]
impl RefCount for skia_bindings::SkNVRefCnt {
#[allow(clippy::cast_ptr_alignment)]
fn ref_cnt(&self) -> usize {
unsafe {
let ptr: *const i32 = &self.fRefCnt as *const _ as *const i32;
(*ptr).try_into().unwrap()
}
}
}
pub trait NativeRefCounted: Sized {
fn _ref(&self);
fn _unref(&self);
fn unique(&self) -> bool;
fn _ref_cnt(&self) -> usize {
unimplemented!();
}
}
impl NativeRefCounted for SkRefCntBase {
fn _ref(&self) {
unsafe { C_SkRefCntBase_ref(self) }
}
fn _unref(&self) {
unsafe { C_SkRefCntBase_unref(self) }
}
fn unique(&self) -> bool {
unsafe { C_SkRefCntBase_unique(self) }
}
#[allow(clippy::cast_ptr_alignment)]
fn _ref_cnt(&self) -> usize {
unsafe {
let ptr: *const i32 = &self.fRefCnt as *const _ as *const i32;
(*ptr).try_into().unwrap()
}
}
}
pub trait NativeRefCountedBase {
type Base: NativeRefCounted;
fn ref_counted_base(&self) -> &Self::Base {
unsafe { &*(self as *const _ as *const Self::Base) }
}
}
impl<Native, Base: NativeRefCounted> NativeRefCounted for Native
where
Native: NativeRefCountedBase<Base = Base>,
{
fn _ref(&self) {
self.ref_counted_base()._ref();
}
fn _unref(&self) {
self.ref_counted_base()._unref();
}
fn unique(&self) -> bool {
self.ref_counted_base().unique()
}
fn _ref_cnt(&self) -> usize {
self.ref_counted_base()._ref_cnt()
}
}
pub trait NativeAccess {
type Native;
fn native(&self) -> &Self::Native;
fn native_mut(&mut self) -> &mut Self::Native;
unsafe fn native_mut_force(&self) -> *mut Self::Native {
self.native() as *const Self::Native as *mut Self::Native
}
}
pub trait NativeDrop {
fn drop(&mut self);
}
pub trait NativeClone {
fn clone(&self) -> Self;
}
pub trait NativePartialEq {
fn eq(&self, rhs: &Self) -> bool;
}
pub trait NativeHash {
fn hash<H: Hasher>(&self, state: &mut H);
}
#[repr(transparent)]
pub struct Handle<N: NativeDrop>(
N,
PhantomData<*const ()>,
);
impl<N: NativeDrop> AsRef<Handle<N>> for Handle<N> {
fn as_ref(&self) -> &Self {
self
}
}
impl<N: NativeDrop> Handle<N> {
#[must_use]
pub(crate) fn from_native_c(n: N) -> Self {
Handle(n, PhantomData)
}
#[must_use]
pub(crate) fn from_native_ref(n: &N) -> &Self {
unsafe { transmute_ref(n) }
}
#[must_use]
pub(crate) fn from_native_ref_mut(n: &mut N) -> &mut Self {
unsafe { transmute_ref_mut(n) }
}
#[must_use]
pub(crate) fn from_native_ptr(np: *const N) -> *const Self {
np as _
}
#[allow(unused)]
#[must_use]
pub(crate) fn from_native_ptr_mut(np: *mut N) -> *mut Self {
np as _
}
#[must_use]
pub(crate) fn construct(construct: impl FnOnce(*mut N)) -> Self {
Self::try_construct(|i| {
construct(i);
true
})
.unwrap()
}
#[must_use]
pub(crate) fn try_construct(construct: impl FnOnce(*mut N) -> bool) -> Option<Self> {
self::try_construct(construct).map(Self::from_native_c)
}
#[must_use]
pub(crate) fn replace_native(mut self, native: &mut N) -> Self {
mem::swap(&mut self.0, native);
self
}
#[must_use]
pub(crate) fn into_native(mut self) -> N {
let r = mem::replace(&mut self.0, unsafe { mem::zeroed() });
mem::forget(self);
r
}
}
pub(crate) trait ReplaceWith<Other> {
fn replace_with(&mut self, other: Other) -> Other;
}
impl<N: NativeDrop> ReplaceWith<Handle<N>> for N {
fn replace_with(&mut self, other: Handle<N>) -> Handle<N> {
other.replace_native(self)
}
}
#[must_use]
pub(crate) fn construct<N>(construct: impl FnOnce(*mut N)) -> N {
try_construct(|i| {
construct(i);
true
})
.unwrap()
}
#[must_use]
pub(crate) fn try_construct<N>(construct: impl FnOnce(*mut N) -> bool) -> Option<N> {
let mut instance = MaybeUninit::uninit();
construct(instance.as_mut_ptr()).if_true_then_some(|| unsafe { instance.assume_init() })
}
impl<N: NativeDrop> Drop for Handle<N> {
fn drop(&mut self) {
self.0.drop()
}
}
impl<N: NativeDrop> NativeAccess for Handle<N> {
type Native = N;
fn native(&self) -> &N {
&self.0
}
fn native_mut(&mut self) -> &mut N {
&mut self.0
}
}
impl<N: NativeDrop + NativeClone> Clone for Handle<N> {
fn clone(&self) -> Self {
Self::from_native_c(self.0.clone())
}
}
impl<N: NativeDrop + NativePartialEq> PartialEq for Handle<N> {
fn eq(&self, rhs: &Self) -> bool {
self.native().eq(rhs.native())
}
}
impl<N: NativeDrop + NativeHash> Hash for Handle<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.native().hash(state);
}
}
pub(crate) trait NativeSliceAccess<N: NativeDrop> {
fn native(&self) -> &[N];
fn native_mut(&mut self) -> &mut [N];
}
impl<N: NativeDrop> NativeSliceAccess<N> for [Handle<N>] {
fn native(&self) -> &[N] {
let ptr = self
.first()
.map(|f| f.native() as *const N)
.unwrap_or(ptr::null());
unsafe { slice::from_raw_parts(ptr, self.len()) }
}
fn native_mut(&mut self) -> &mut [N] {
let ptr = self
.first_mut()
.map(|f| f.native_mut() as *mut N)
.unwrap_or(ptr::null_mut());
unsafe { slice::from_raw_parts_mut(ptr, self.len()) }
}
}
pub(crate) trait NativePointerOrNull {
type Native;
fn native_ptr_or_null(&self) -> *const Self::Native;
unsafe fn native_ptr_or_null_mut_force(&self) -> *mut Self::Native;
}
pub(crate) trait NativePointerOrNullMut {
type Native;
fn native_ptr_or_null_mut(&mut self) -> *mut Self::Native;
}
impl<H, N> NativePointerOrNull for Option<&H>
where
H: NativeAccess<Native = N>,
{
type Native = N;
fn native_ptr_or_null(&self) -> *const N {
match self {
Some(handle) => handle.native(),
None => ptr::null(),
}
}
unsafe fn native_ptr_or_null_mut_force(&self) -> *mut N {
match self {
Some(handle) => handle.native_mut_force(),
None => ptr::null_mut(),
}
}
}
impl<H, N> NativePointerOrNullMut for Option<&mut H>
where
H: NativeAccess<Native = N>,
{
type Native = N;
fn native_ptr_or_null_mut(&mut self) -> *mut N {
match self {
Some(handle) => handle.native_mut(),
None => ptr::null_mut(),
}
}
}
pub(crate) trait NativePointerOrNullMut2<N> {
fn native_ptr_or_null_mut(&mut self) -> *mut N;
}
pub(crate) trait NativePointerOrNull2<N> {
fn native_ptr_or_null(&self) -> *const N;
}
impl<H, N> NativePointerOrNull2<N> for Option<&H>
where
H: NativeTransmutable<N>,
{
fn native_ptr_or_null(&self) -> *const N {
match self {
Some(handle) => handle.native(),
None => ptr::null(),
}
}
}
impl<H, N> NativePointerOrNullMut2<N> for Option<&mut H>
where
H: NativeTransmutable<N>,
{
fn native_ptr_or_null_mut(&mut self) -> *mut N {
match self {
Some(handle) => handle.native_mut(),
None => ptr::null_mut(),
}
}
}
#[repr(transparent)]
pub struct RefHandle<N: NativeDrop>(ptr::NonNull<N>);
impl<N: NativeDrop> Drop for RefHandle<N> {
fn drop(&mut self) {
self.native_mut().drop()
}
}
impl<N: NativeDrop + NativePartialEq> PartialEq for RefHandle<N> {
fn eq(&self, rhs: &Self) -> bool {
self.native().eq(rhs.native())
}
}
impl<N: NativeDrop> NativeAccess for RefHandle<N> {
type Native = N;
fn native(&self) -> &N {
unsafe { self.0.as_ref() }
}
fn native_mut(&mut self) -> &mut N {
unsafe { self.0.as_mut() }
}
}
impl<N: NativeDrop> RefHandle<N> {
pub(crate) fn from_ptr(ptr: *mut N) -> Option<Self> {
ptr::NonNull::new(ptr).map(Self)
}
pub(crate) fn into_ptr(self) -> *mut N {
let p = self.0.as_ptr();
mem::forget(self);
p
}
}
#[repr(transparent)]
pub struct RCHandle<Native: NativeRefCounted>(ptr::NonNull<Native>);
impl<N: NativeRefCounted> From<&RCHandle<N>> for RCHandle<N> {
fn from(rch: &RCHandle<N>) -> Self {
rch.clone()
}
}
impl<N: NativeRefCounted> AsRef<RCHandle<N>> for RCHandle<N> {
fn as_ref(&self) -> &Self {
self
}
}
impl<N: NativeRefCounted> RCHandle<N> {
#[inline]
pub(crate) fn from_ptr(ptr: *mut N) -> Option<Self> {
ptr::NonNull::new(ptr).map(Self)
}
#[inline]
pub(crate) fn from_unshared_ptr(ptr: *mut N) -> Option<Self> {
ptr::NonNull::new(ptr).map(|ptr| {
unsafe { ptr.as_ref()._ref() };
Self(ptr)
})
}
pub(crate) fn from_unshared_ptr_ref(n: &*mut N) -> &Option<Self> {
unsafe { transmute_ref(n) }
}
pub(crate) fn from_non_null_sp_slice(sp_slice: &[sk_sp<N>]) -> &[Self] {
debug_assert!(sp_slice.iter().all(|v| !v.fPtr.is_null()));
unsafe { mem::transmute(sp_slice) }
}
#[allow(unused)]
pub(crate) fn as_ptr(&self) -> &NonNull<N> {
&self.0
}
}
#[cfg(test)]
mod rc_handle_tests {
use std::ptr;
use skia_bindings::{SkFontMgr, SkTypeface};
use crate::{prelude::NativeAccess, FontMgr, Typeface};
#[test]
fn rc_native_ref_null() {
let f: *mut SkTypeface = ptr::null_mut();
let r = Typeface::from_unshared_ptr(f);
assert!(r.is_none())
}
#[test]
fn rc_native_ref_non_null() {
let mut font_mgr = FontMgr::new();
let f: *mut SkFontMgr = font_mgr.native_mut();
let r = FontMgr::from_unshared_ptr(f);
assert!(r.is_some())
}
}
impl<N: NativeRefCounted> NativeAccess for RCHandle<N> {
type Native = N;
fn native(&self) -> &N {
unsafe { self.0.as_ref() }
}
fn native_mut(&mut self) -> &mut N {
unsafe { self.0.as_mut() }
}
}
impl<N: NativeRefCounted> Clone for RCHandle<N> {
fn clone(&self) -> Self {
let ptr = self.0;
unsafe { ptr.as_ref()._ref() };
Self(ptr)
}
}
impl<N: NativeRefCounted> Drop for RCHandle<N> {
#[inline]
fn drop(&mut self) {
unsafe { self.0.as_ref()._unref() };
}
}
impl<N: NativeRefCounted + NativePartialEq> PartialEq for RCHandle<N> {
fn eq(&self, rhs: &Self) -> bool {
self.native().eq(rhs.native())
}
}
pub(crate) trait IntoPtr<N> {
fn into_ptr(self) -> *mut N;
}
impl<N: NativeRefCounted> IntoPtr<N> for RCHandle<N> {
fn into_ptr(self) -> *mut N {
let ptr = self.0.as_ptr();
mem::forget(self);
ptr
}
}
pub(crate) trait IntoPtrOrNull {
type Native;
fn into_ptr_or_null(self) -> *mut Self::Native;
}
impl<N: NativeRefCounted> IntoPtrOrNull for Option<RCHandle<N>> {
type Native = N;
fn into_ptr_or_null(self) -> *mut N {
self.map(|rc| rc.into_ptr()).unwrap_or(ptr::null_mut())
}
}
pub trait IndexGet {}
pub trait IndexSet {}
pub trait IndexGetter<I, O: Copy> {
#[allow(unused)]
fn get(&self, index: I) -> O;
}
impl<T, I, O: Copy> IndexGetter<I, O> for T
where
T: Index<I, Output = O> + IndexGet,
{
fn get(&self, index: I) -> O {
self[index]
}
}
pub trait IndexSetter<I, O: Copy> {
fn set(&mut self, index: I, value: O) -> &mut Self;
}
impl<T, I, O: Copy> IndexSetter<I, O> for T
where
T: IndexMut<I, Output = O> + IndexSet,
{
fn set(&mut self, index: I, value: O) -> &mut Self {
self[index] = value;
self
}
}
pub trait NativeTransmutable<NT: Sized>: Sized
where
Self: Sized,
{
fn native(&self) -> &NT {
unsafe { transmute_ref(self) }
}
fn native_mut(&mut self) -> &mut NT {
unsafe { transmute_ref_mut(self) }
}
fn from_native_c(nt: NT) -> Self {
let r = unsafe { mem::transmute_copy::<NT, Self>(&nt) };
mem::forget(nt);
r
}
fn into_native(self) -> NT {
let r = unsafe { mem::transmute_copy::<Self, NT>(&self) };
mem::forget(self);
r
}
fn from_native_ref(nt: &NT) -> &Self {
unsafe { transmute_ref(nt) }
}
fn from_native_array_ref<const N: usize>(nt: &[NT; N]) -> &[Self; N] {
unsafe { transmute_ref(nt) }
}
fn from_native_ref_mut(nt: &mut NT) -> &mut Self {
unsafe { transmute_ref_mut(nt) }
}
fn from_native_ptr(np: *const NT) -> *const Self {
np as _
}
fn from_native_ptr_mut(np: *mut NT) -> *mut Self {
np as _
}
fn test_layout() {
assert_eq!(mem::size_of::<Self>(), mem::size_of::<NT>());
assert_eq!(mem::align_of::<Self>(), mem::align_of::<NT>());
}
fn construct(construct: impl FnOnce(*mut NT)) -> Self {
Self::try_construct(|i| {
construct(i);
true
})
.unwrap()
}
fn try_construct(construct: impl FnOnce(*mut NT) -> bool) -> Option<Self> {
self::try_construct(construct).map(Self::from_native_c)
}
}
pub(crate) trait NativeTransmutableSliceAccess<NT: Sized> {
fn native(&self) -> &[NT];
fn native_mut(&mut self) -> &mut [NT];
}
impl<NT, ElementT> NativeTransmutableSliceAccess<NT> for [ElementT]
where
ElementT: NativeTransmutable<NT>,
{
fn native(&self) -> &[NT] {
unsafe { &*(self as *const [ElementT] as *const [NT]) }
}
fn native_mut(&mut self) -> &mut [NT] {
unsafe { &mut *(self as *mut [ElementT] as *mut [NT]) }
}
}
impl<NT, RustT> NativeTransmutable<Option<NT>> for Option<RustT> where RustT: NativeTransmutable<NT> {}
impl<NT, RustT> NativeTransmutable<Option<&[NT]>> for Option<&[RustT]> where
RustT: NativeTransmutable<NT>
{
}
pub(crate) trait NativeTransmutableOptionSliceAccessMut<NT: Sized> {
fn native_mut(&mut self) -> &mut Option<&mut [NT]>;
}
impl<NT, RustT> NativeTransmutableOptionSliceAccessMut<NT> for Option<&mut [RustT]>
where
RustT: NativeTransmutable<NT>,
{
fn native_mut(&mut self) -> &mut Option<&mut [NT]> {
unsafe { transmute_ref_mut(self) }
}
}
pub(crate) trait AsPointerOrNull<PointerT> {
fn as_ptr_or_null(&self) -> *const PointerT;
}
pub(crate) trait AsPointerOrNullMut<PointerT>: AsPointerOrNull<PointerT> {
fn as_ptr_or_null_mut(&mut self) -> *mut PointerT;
}
impl<E> AsPointerOrNull<E> for Option<E> {
fn as_ptr_or_null(&self) -> *const E {
match self {
Some(e) => e,
None => ptr::null(),
}
}
}
impl<E> AsPointerOrNullMut<E> for Option<E> {
fn as_ptr_or_null_mut(&mut self) -> *mut E {
match self {
Some(e) => e,
None => ptr::null_mut(),
}
}
}
impl<E> AsPointerOrNull<E> for Option<&[E]> {
fn as_ptr_or_null(&self) -> *const E {
match self {
Some(slice) => slice.as_ptr(),
None => ptr::null(),
}
}
}
impl<E> AsPointerOrNull<E> for Option<&mut [E]> {
fn as_ptr_or_null(&self) -> *const E {
match self {
Some(slice) => slice.as_ptr(),
None => ptr::null(),
}
}
}
impl<E> AsPointerOrNullMut<E> for Option<&mut [E]> {
fn as_ptr_or_null_mut(&mut self) -> *mut E {
match self {
Some(slice) => slice.as_mut_ptr(),
None => ptr::null_mut(),
}
}
}
impl<E> AsPointerOrNull<E> for Option<&Vec<E>> {
fn as_ptr_or_null(&self) -> *const E {
match self {
Some(v) => v.as_ptr(),
None => ptr::null(),
}
}
}
impl<E> AsPointerOrNull<E> for Option<Vec<E>> {
fn as_ptr_or_null(&self) -> *const E {
match self {
Some(v) => v.as_ptr(),
None => ptr::null(),
}
}
}
impl<E> AsPointerOrNullMut<E> for Option<Vec<E>> {
fn as_ptr_or_null_mut(&mut self) -> *mut E {
match self {
Some(v) => v.as_mut_ptr(),
None => ptr::null_mut(),
}
}
}
#[repr(transparent)]
pub struct Borrows<'a, H>(H, PhantomData<&'a ()>);
impl<H> Deref for Borrows<'_, H> {
type Target = H;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<H> DerefMut for Borrows<'_, H> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<H> Borrows<'_, H> {
pub unsafe fn release(self) -> H {
self.0
}
}
pub(crate) trait BorrowsFrom: Sized {
fn borrows<D: ?Sized>(self, _dep: &D) -> Borrows<Self>;
}
impl<T: Sized> BorrowsFrom for T {
fn borrows<D: ?Sized>(self, _dep: &D) -> Borrows<Self> {
Borrows(self, PhantomData)
}
}
impl<H> Borrows<'_, H> {
pub(crate) unsafe fn unchecked_new(h: H) -> Self {
Self(h, PhantomData)
}
}
pub trait NativeBase<Base> {
fn base(&self) -> &Base {
unsafe { &*(self as *const Self as *const Base) }
}
fn base_mut(&mut self) -> &mut Base {
unsafe { &mut *(self as *mut Self as *mut Base) }
}
}
pub struct Sendable<H: ConditionallySend>(H);
unsafe impl<H: ConditionallySend> Send for Sendable<H> {}
impl<H: ConditionallySend> Sendable<H> {
#[deprecated(note = "Use Sendable::into_inner() instead")]
pub fn unwrap(self) -> H {
self.0
}
pub fn into_inner(self) -> H {
self.0
}
}
impl<H> Debug for Sendable<H>
where
H: Debug + ConditionallySend,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Sendable").field(&self.0).finish()
}
}
pub trait ConditionallySend: Sized {
fn can_send(&self) -> bool;
fn wrap_send(self) -> Result<Sendable<Self>, Self>;
}
impl<H: NativeRefCountedBase> ConditionallySend for RCHandle<H> {
fn can_send(&self) -> bool {
self.native().unique()
}
fn wrap_send(self) -> Result<Sendable<Self>, Self> {
if self.can_send() {
Ok(Sendable(self))
} else {
Err(self)
}
}
}
pub(crate) mod safer {
use core::slice;
use std::ptr;
pub unsafe fn from_raw_parts<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
let ptr = if len == 0 {
ptr::NonNull::dangling().as_ptr()
} else {
assert!(!ptr.is_null());
ptr
};
slice::from_raw_parts(ptr, len)
}
pub unsafe fn from_raw_parts_mut<'a, T>(ptr: *mut T, len: usize) -> &'a mut [T] {
let ptr = if len == 0 {
ptr::NonNull::dangling().as_ptr() as *mut _
} else {
assert!(!ptr.is_null());
ptr
};
slice::from_raw_parts_mut(ptr, len)
}
}
#[cfg(test)]
mod tests {
use skia_bindings::{sk_sp, SkFontMgr};
use crate::RCHandle;
#[test]
fn sp_equals_size_and_alignment_of_rc_handle() {
assert_eq!(
size_of::<sk_sp<SkFontMgr>>(),
size_of::<RCHandle<SkFontMgr>>()
);
assert_eq!(
align_of::<sk_sp<SkFontMgr>>(),
align_of::<RCHandle<SkFontMgr>>()
);
}
}