1use std::{
2 ffi::{c_char, CString},
3 marker::PhantomData,
4 ops::Deref,
5 os::unix::prelude::OsStrExt,
6 path::Path,
7 ptr::null_mut,
8 slice,
9};
10
11use crate::{sys, AllocationError};
12
13pub struct Blob<'a>(*mut sys::hb_blob_t, PhantomData<&'a [u8]>);
18
19impl Blob<'static> {
20 #[doc(alias = "hb_blob_create_from_file")]
22 #[doc(alias = "hb_blob_create_from_file_or_fail")]
23 pub fn from_file(path: impl AsRef<Path>) -> Result<Self, AllocationError> {
24 let path =
25 CString::new(path.as_ref().as_os_str().as_bytes()).map_err(|_| AllocationError)?;
26
27 let blob = unsafe { sys::hb_blob_create_from_file_or_fail(path.as_ptr()) };
28 if blob.is_null() {
29 return Err(AllocationError);
30 }
31 Ok(Self(blob, PhantomData))
32 }
33}
34
35impl<'a> Blob<'a> {
36 #[doc(alias = "hb_blob_create")]
38 #[doc(alias = "hb_blob_create_or_fail")]
39 pub fn from_bytes(buffer: &'a [u8]) -> Result<Self, AllocationError> {
40 let blob = unsafe {
41 sys::hb_blob_create_or_fail(
42 buffer.as_ptr() as *const c_char,
43 buffer.len().try_into().map_err(|_| AllocationError)?,
44 sys::hb_memory_mode_t_HB_MEMORY_MODE_READONLY,
45 null_mut(),
46 None,
47 )
48 };
49 if blob.is_null() {
50 return Err(AllocationError);
51 }
52 Ok(Self(blob, PhantomData))
53 }
54
55 pub fn is_empty(&self) -> bool {
57 self.len() == 0
58 }
59
60 #[doc(alias = "hb_blob_get_length")]
62 pub fn len(&self) -> usize {
63 (unsafe { sys::hb_blob_get_length(self.0) }) as usize
64 }
65}
66
67impl<'a> Blob<'a> {
68 pub fn into_raw(self) -> *mut sys::hb_blob_t {
73 let ptr = self.0;
74 std::mem::forget(self);
75 ptr
76 }
77
78 pub fn as_raw(&self) -> *mut sys::hb_blob_t {
82 self.0
83 }
84
85 pub unsafe fn from_raw(blob: *mut sys::hb_blob_t) -> Self {
91 Self(blob, PhantomData)
92 }
93}
94
95impl Deref for Blob<'_> {
96 type Target = [u8];
97
98 #[doc(alias = "hb_blob_get_data")]
99 fn deref(&self) -> &Self::Target {
100 let mut len = 0u32;
101 let data = unsafe { sys::hb_blob_get_data(self.0, &mut len as *mut u32) } as *const u8;
102 if data.is_null() {
103 return &[];
105 }
106 unsafe { slice::from_raw_parts(data, len as usize) }
107 }
108}
109
110impl<'a> Drop for Blob<'a> {
111 #[doc(alias = "hb_blob_destroy")]
112 fn drop(&mut self) {
113 unsafe { sys::hb_blob_destroy(self.0) }
114 }
115}
116
117impl<'a> Clone for Blob<'a> {
118 #[doc(alias = "hb_blob_reference")]
119 fn clone(&self) -> Self {
120 Self(unsafe { sys::hb_blob_reference(self.0) }, PhantomData)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use crate::tests::NOTO_SANS;
128
129 #[test]
130 fn empty_is_empty() {
131 assert!(Blob::from_bytes(&[]).unwrap().is_empty());
132 }
133
134 #[test]
135 fn non_empty_is_not_empty() {
136 assert!(!Blob::from_bytes(&[1, 2, 3]).unwrap().is_empty());
137 }
138
139 #[test]
140 fn len_works() {
141 assert_eq!(Blob::from_bytes(&[]).unwrap().len(), 0);
142 assert_eq!(Blob::from_bytes(&[1, 2, 3]).unwrap().len(), 3);
143 }
144
145 #[test]
146 fn content_is_correct() {
147 assert_eq!(&*Blob::from_bytes(&[1, 2, 3]).unwrap(), &[1, 2, 3]);
148 }
149
150 #[test]
151 fn from_file_loads_file() {
152 let correct = std::fs::read(NOTO_SANS).unwrap();
153 let blob = Blob::from_file(NOTO_SANS).unwrap();
154 assert_eq!(correct, &*blob);
155 }
156
157 #[test]
158 fn clone_refers_to_same_object() {
159 let b1 = Blob::from_bytes(&[1, 2, 3]).unwrap();
160 let b2 = b1.clone();
161 assert_eq!(&*b1, &[1, 2, 3]);
162 assert_eq!(&*b2, &[1, 2, 3]);
163 drop(b1);
164 assert_eq!(&*b2, &[1, 2, 3]);
165 }
166
167 #[test]
168 fn convert_into_raw_and_back() {
169 let blob = Blob::from_bytes(&[1, 2, 3]).unwrap();
170 let blob_ptr = blob.into_raw();
171 let blob = unsafe { Blob::from_raw(blob_ptr) };
172 drop(blob);
173 }
174}