1use std::fmt;
4use std::ops::{Deref, DerefMut};
5use std::ptr::{self, NonNull};
6
7use fontconfig_sys as sys;
8use sys::ffi_dispatch;
9
10#[cfg(feature = "dlopen")]
11use sys::statics::LIB;
12#[cfg(not(feature = "dlopen"))]
13use sys::*;
14
15use crate::FcTrue;
16
17pub struct OwnedCharSet {
19 pub(crate) fcset: NonNull<sys::FcCharSet>,
20}
21
22#[repr(transparent)]
24pub struct CharSet {
25 pub(crate) fcset: sys::FcCharSet,
26}
27
28impl CharSet {
29 pub fn len(&self) -> usize {
31 let size = unsafe { ffi_dispatch!(LIB, FcCharSetCount, self.as_ptr()) };
32 size as usize
33 }
34
35 pub fn is_empty(&self) -> bool {
37 self.len() == 0
38 }
39
40 pub fn has_char(&self, c: char) -> bool {
42 let res = unsafe { ffi_dispatch!(LIB, FcCharSetHasChar, self.as_ptr(), c as u32) };
43 res == FcTrue
44 }
45
46 pub fn is_subset(&self, other: &Self) -> bool {
48 let res = unsafe { ffi_dispatch!(LIB, FcCharSetIsSubset, self.as_ptr(), other.as_ptr()) };
49 res == FcTrue
50 }
51
52 pub fn intersect(&self, other: &Self) -> OwnedCharSet {
54 let fcset =
55 unsafe { ffi_dispatch!(LIB, FcCharSetIntersect, self.as_ptr(), other.as_ptr()) };
56 OwnedCharSet {
57 fcset: NonNull::new(fcset).expect("intersect failed"),
58 }
59 }
60
61 pub fn subtract(&self, other: &Self) -> OwnedCharSet {
63 let fcset = unsafe { ffi_dispatch!(LIB, FcCharSetSubtract, self.as_ptr(), other.as_ptr()) };
64 OwnedCharSet {
65 fcset: NonNull::new(fcset).expect("subtract failed"),
66 }
67 }
68
69 pub fn union(&self, other: &Self) -> OwnedCharSet {
71 let fcset = unsafe { ffi_dispatch!(LIB, FcCharSetUnion, self.as_ptr(), other.as_ptr()) };
72 OwnedCharSet {
73 fcset: NonNull::new(fcset).expect("union failed"),
74 }
75 }
76
77 pub fn iter(&self) -> Iter {
79 let mut map = [0; sys::constants::FC_CHARSET_MAP_SIZE as usize];
80 let mut next = 0;
81 let codepoint = unsafe {
82 ffi_dispatch!(
83 LIB,
84 FcCharSetFirstPage,
85 self.as_ptr(),
86 map.as_mut_ptr(),
87 &mut next
88 )
89 };
90 Iter {
91 cs: self,
92 codepoint,
93 next,
94 map,
95 i: 0,
96 bit: 0,
97 }
98 }
99
100 pub(crate) fn as_ptr(&self) -> *const sys::FcCharSet {
101 &self.fcset
102 }
103
104 pub(crate) fn as_mut_ptr(&mut self) -> *mut sys::FcCharSet {
105 &mut self.fcset
106 }
107}
108
109impl OwnedCharSet {
110 pub fn new() -> Self {
112 let fcset = unsafe { ffi_dispatch!(LIB, FcCharSetCreate,) };
113 OwnedCharSet {
114 fcset: NonNull::new(fcset).unwrap(),
115 }
116 }
117 pub fn add_char(&mut self, c: char) {
119 let res = unsafe { ffi_dispatch!(LIB, FcCharSetAddChar, self.as_mut_ptr(), c as u32) };
120 assert_eq!(res, FcTrue);
121 }
122
123 pub fn del_char(&mut self, c: char) {
125 let res = unsafe { ffi_dispatch!(LIB, FcCharSetDelChar, self.as_mut_ptr(), c as u32) };
126 assert_eq!(res, FcTrue);
127 }
128
129 pub fn merge(&mut self, other: &CharSet) {
131 let res = unsafe {
132 ffi_dispatch!(
133 LIB,
134 FcCharSetMerge,
135 self.as_mut_ptr(),
136 other.as_ptr(),
137 ptr::null_mut()
138 )
139 };
140 assert_eq!(res, FcTrue);
141 }
142}
143
144impl PartialEq for CharSet {
145 fn eq(&self, other: &Self) -> bool {
146 let res = unsafe { ffi_dispatch!(LIB, FcCharSetEqual, self.as_ptr(), other.as_ptr()) };
147 res == FcTrue
148 }
149}
150
151impl Drop for OwnedCharSet {
163 fn drop(&mut self) {
164 unsafe { ffi_dispatch!(LIB, FcCharSetDestroy, self.as_mut_ptr()) };
165 }
166}
167
168impl Default for OwnedCharSet {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174impl Deref for OwnedCharSet {
175 type Target = CharSet;
176 fn deref(&self) -> &CharSet {
177 unsafe { &*(self.fcset.as_ptr() as *const CharSet) }
178 }
179}
180
181impl DerefMut for OwnedCharSet {
182 fn deref_mut(&mut self) -> &mut CharSet {
183 unsafe { &mut *(self.fcset.as_ptr() as *mut CharSet) }
184 }
185}
186
187impl AsRef<CharSet> for OwnedCharSet {
188 fn as_ref(&self) -> &CharSet {
189 self
190 }
191}
192
193impl AsMut<CharSet> for OwnedCharSet {
194 fn as_mut(&mut self) -> &mut CharSet {
195 self
196 }
197}
198
199impl fmt::Debug for CharSet {
200 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201 f.debug_set().entries(self.iter()).finish()
202 }
203}
204
205impl fmt::Debug for OwnedCharSet {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 f.debug_set().entries(self.iter()).finish()
208 }
209}
210
211pub struct Iter<'a> {
213 cs: &'a CharSet,
214 codepoint: sys::FcChar32,
215 next: sys::FcChar32,
216 map: [sys::FcChar32; sys::constants::FC_CHARSET_MAP_SIZE as usize],
217 i: usize,
218 bit: usize,
219}
220
221impl Iterator for Iter<'_> {
222 type Item = char;
223 fn next(&mut self) -> Option<char> {
224 const MAP_SIZE: usize = sys::constants::FC_CHARSET_MAP_SIZE as usize;
225 loop {
226 if self.codepoint == sys::constants::FC_CHARSET_DONE {
227 return None;
228 }
229 if self.i >= MAP_SIZE {
231 if self.next == sys::constants::FC_CHARSET_DONE {
232 self.codepoint = sys::constants::FC_CHARSET_DONE;
234 } else {
235 self.codepoint = unsafe {
237 ffi_dispatch!(
238 LIB,
239 FcCharSetNextPage,
240 self.cs.as_ptr(),
241 self.map.as_mut_ptr(),
242 &mut self.next
243 )
244 };
245 self.i = 0;
246 }
247 continue;
248 }
249 let mut bits = self.map[self.i];
250 if bits == 0 {
251 self.i += 1;
252 self.bit = 0;
253 continue;
254 }
255
256 let mut ok = true;
257 let mut n = bits >> self.bit;
259 while n & 1 == 0 {
260 if n == 0 {
261 self.i += 1;
262 self.bit = 0;
263 ok = false;
264 break;
265 }
266 self.bit += 1;
267 if self.bit > 31 {
268 self.i += 1;
269 self.bit = 0;
270 ok = false;
271 break;
272 }
273 if self.i >= MAP_SIZE {
274 ok = false;
275 break;
276 }
277 bits = self.map[self.i];
278 n = bits >> self.bit;
280 }
281 if ok {
284 let codepoint =
285 self.codepoint + (32u32 * self.i as u32) + u32::try_from(self.bit).ok()?;
286 self.bit += 1;
288 if self.bit > 31 {
289 self.i += 1;
290 self.bit = 0;
291 }
292 return char::try_from(codepoint).ok();
293 }
294 }
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301
302 #[test]
303 fn charset_modify() {
304 let mut cs = OwnedCharSet::new();
305 assert!(!cs.has_char('a'));
306 cs.add_char('a');
307 assert!(cs.has_char('a'));
308 cs.del_char('a');
309 assert!(!cs.has_char('a'));
310 }
311
312 #[test]
313 fn charset_merge() {
314 let mut cs1 = OwnedCharSet::new();
315 let mut cs2 = OwnedCharSet::new();
316 cs1.add_char('a');
317 cs2.add_char('b');
318 cs1.merge(&cs2);
319 assert!(cs1.has_char('a'));
320 assert!(cs1.has_char('b'));
321 }
322
323 #[test]
324 fn charset_union() {
325 let mut cs1 = OwnedCharSet::new();
326 let mut cs2 = OwnedCharSet::new();
327 cs1.add_char('a');
328 cs2.add_char('b');
329 let cs3 = cs1.union(&cs2);
330 assert!(cs3.has_char('a'));
331 assert!(cs3.has_char('b'));
332 assert!(!cs1.has_char('b'));
333 assert!(!cs2.has_char('a'));
334 }
335
336 #[test]
337 fn charset_intersect() {
338 let mut cs1 = OwnedCharSet::new();
339 let mut cs2 = OwnedCharSet::new();
340 cs1.add_char('a');
341 cs1.add_char('c');
342 cs2.add_char('b');
343 cs2.add_char('c');
344 let cs3 = cs1.intersect(&cs2);
345 assert!(!cs3.has_char('a'));
346 assert!(!cs3.has_char('b'));
347 assert!(cs3.has_char('c'));
348 assert!(!cs1.has_char('b'));
349 assert!(!cs2.has_char('a'));
350 }
351
352 #[test]
353 fn charset_subtract() {
354 let mut cs1 = OwnedCharSet::new();
355 let mut cs2 = OwnedCharSet::new();
356 cs1.add_char('a');
357 cs1.add_char('c');
358 cs2.add_char('b');
359 cs2.add_char('c');
360 let cs3 = cs1.subtract(&cs2);
361 assert!(cs3.has_char('a'));
362 assert!(!cs3.has_char('b'));
363 assert!(!cs3.has_char('c'));
364 assert!(!cs1.has_char('b'));
365 assert!(!cs2.has_char('a'));
366 }
367
368 #[test]
369 fn charset_equal() {
370 let mut cs1 = OwnedCharSet::new();
371 let mut cs2 = OwnedCharSet::new();
372 cs1.add_char('a');
373 cs1.add_char('c');
374 cs2.add_char('b');
375 cs2.add_char('c');
376 assert_ne!(cs1.as_ref(), cs2.as_ref());
377 cs2.add_char('a');
378 cs2.del_char('b');
379 assert_eq!(cs1.as_ref(), cs2.as_ref());
380 }
381
382 #[test]
383 fn charset_iter() {
384 let mut cs = OwnedCharSet::new();
385 cs.add_char('a');
386 cs.add_char('b');
387 cs.add_char('c');
388 cs.add_char('汉');
389 cs.add_char('字');
390 cs.add_char('😁');
391 let mut iter = cs.iter();
392 assert_eq!(iter.next(), Some('a'));
393 assert_eq!(iter.next(), Some('b'));
394 assert_eq!(iter.next(), Some('c'));
395 assert_eq!(iter.next(), Some('字'));
396 assert_eq!(iter.next(), Some('汉'));
397 assert_eq!(iter.next(), Some('😁'));
398 assert_eq!(iter.next(), None);
399 }
400}