1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{ffi::c_char, fmt, marker::PhantomData, mem, ptr}; |
4 | |
5 | use crate::{ffi, gobject_ffi, prelude::*, translate::*, GStr, GString, GStringPtr}; |
6 | |
7 | // rustdoc-stripper-ignore-next |
8 | /// Minimum size of the `StrV` allocation. |
9 | const MIN_SIZE: usize = 16; |
10 | |
11 | // rustdoc-stripper-ignore-next |
12 | /// `NULL`-terminated array of `NULL`-terminated strings. |
13 | /// |
14 | /// The underlying memory is always `NULL`-terminated. |
15 | /// |
16 | /// This can be used like a `&[&str]`, `&mut [&str]` and `Vec<&str>`. |
17 | pub struct StrV { |
18 | ptr: ptr::NonNull<*mut c_char>, |
19 | // rustdoc-stripper-ignore-next |
20 | /// Length without the `NULL`-terminator. |
21 | len: usize, |
22 | // rustdoc-stripper-ignore-next |
23 | /// Capacity **with** the `NULL`-terminator, i.e. the actual allocation size. |
24 | capacity: usize, |
25 | } |
26 | |
27 | impl fmt::Debug for StrV { |
28 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
29 | self.as_slice().fmt(f) |
30 | } |
31 | } |
32 | |
33 | unsafe impl Send for StrV {} |
34 | |
35 | unsafe impl Sync for StrV {} |
36 | |
37 | impl PartialEq for StrV { |
38 | #[inline ] |
39 | fn eq(&self, other: &Self) -> bool { |
40 | self.as_slice() == other.as_slice() |
41 | } |
42 | } |
43 | |
44 | impl Eq for StrV {} |
45 | |
46 | impl PartialOrd for StrV { |
47 | #[inline ] |
48 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { |
49 | Some(self.cmp(other)) |
50 | } |
51 | } |
52 | |
53 | impl Ord for StrV { |
54 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { |
55 | self.as_slice().cmp(other.as_slice()) |
56 | } |
57 | } |
58 | |
59 | impl std::hash::Hash for StrV { |
60 | #[inline ] |
61 | fn hash<H: std::hash::Hasher>(&self, state: &mut H) { |
62 | self.as_slice().hash(state) |
63 | } |
64 | } |
65 | |
66 | impl PartialEq<[&'_ str]> for StrV { |
67 | fn eq(&self, other: &[&'_ str]) -> bool { |
68 | for (a: &GStringPtr, b: &&str) in Iterator::zip(self.iter(), other.iter()) { |
69 | if a != b { |
70 | return false; |
71 | } |
72 | } |
73 | |
74 | true |
75 | } |
76 | } |
77 | |
78 | impl PartialEq<StrV> for [&'_ str] { |
79 | #[inline ] |
80 | fn eq(&self, other: &StrV) -> bool { |
81 | other.eq(self) |
82 | } |
83 | } |
84 | |
85 | impl Drop for StrV { |
86 | #[inline ] |
87 | fn drop(&mut self) { |
88 | unsafe { |
89 | if self.capacity != 0 { |
90 | ffi::g_strfreev(self.ptr.as_ptr()); |
91 | } |
92 | } |
93 | } |
94 | } |
95 | |
96 | impl Default for StrV { |
97 | #[inline ] |
98 | fn default() -> Self { |
99 | Self::new() |
100 | } |
101 | } |
102 | |
103 | impl AsRef<[GStringPtr]> for StrV { |
104 | #[inline ] |
105 | fn as_ref(&self) -> &[GStringPtr] { |
106 | self.as_slice() |
107 | } |
108 | } |
109 | |
110 | impl std::borrow::Borrow<[GStringPtr]> for StrV { |
111 | #[inline ] |
112 | fn borrow(&self) -> &[GStringPtr] { |
113 | self.as_slice() |
114 | } |
115 | } |
116 | |
117 | impl std::ops::Deref for StrV { |
118 | type Target = [GStringPtr]; |
119 | |
120 | #[inline ] |
121 | fn deref(&self) -> &[GStringPtr] { |
122 | self.as_slice() |
123 | } |
124 | } |
125 | |
126 | impl std::iter::Extend<GString> for StrV { |
127 | #[inline ] |
128 | fn extend<I: IntoIterator<Item = GString>>(&mut self, iter: I) { |
129 | let iter: ::IntoIter = iter.into_iter(); |
130 | self.reserve(additional:iter.size_hint().0); |
131 | |
132 | for item: GString in iter { |
133 | self.push(item); |
134 | } |
135 | } |
136 | } |
137 | |
138 | impl<'a> std::iter::Extend<&'a str> for StrV { |
139 | #[inline ] |
140 | fn extend<I: IntoIterator<Item = &'a str>>(&mut self, iter: I) { |
141 | let iter: ::IntoIter = iter.into_iter(); |
142 | self.reserve(additional:iter.size_hint().0); |
143 | |
144 | for item: &'a str in iter { |
145 | self.push(item:GString::from(item)); |
146 | } |
147 | } |
148 | } |
149 | |
150 | impl std::iter::FromIterator<GString> for StrV { |
151 | #[inline ] |
152 | fn from_iter<I: IntoIterator<Item = GString>>(iter: I) -> Self { |
153 | let iter: ::IntoIter = iter.into_iter(); |
154 | let mut s: StrV = Self::with_capacity(iter.size_hint().0); |
155 | for item: GString in iter { |
156 | s.push(item); |
157 | } |
158 | s |
159 | } |
160 | } |
161 | |
162 | impl<'a> std::iter::IntoIterator for &'a StrV { |
163 | type Item = &'a GStringPtr; |
164 | type IntoIter = std::slice::Iter<'a, GStringPtr>; |
165 | |
166 | #[inline ] |
167 | fn into_iter(self) -> Self::IntoIter { |
168 | self.as_slice().iter() |
169 | } |
170 | } |
171 | |
172 | impl std::iter::IntoIterator for StrV { |
173 | type Item = GString; |
174 | type IntoIter = IntoIter; |
175 | |
176 | #[inline ] |
177 | fn into_iter(self) -> Self::IntoIter { |
178 | IntoIter::new(self) |
179 | } |
180 | } |
181 | |
182 | pub struct IntoIter { |
183 | ptr: ptr::NonNull<*mut c_char>, |
184 | idx: ptr::NonNull<*mut c_char>, |
185 | len: usize, |
186 | empty: bool, |
187 | } |
188 | |
189 | impl IntoIter { |
190 | #[inline ] |
191 | fn new(slice: StrV) -> Self { |
192 | let slice = mem::ManuallyDrop::new(slice); |
193 | IntoIter { |
194 | ptr: slice.ptr, |
195 | idx: slice.ptr, |
196 | len: slice.len, |
197 | empty: slice.capacity == 0, |
198 | } |
199 | } |
200 | |
201 | // rustdoc-stripper-ignore-next |
202 | /// Returns the remaining items as slice. |
203 | #[inline ] |
204 | pub const fn as_slice(&self) -> &[GStringPtr] { |
205 | unsafe { |
206 | if self.len == 0 { |
207 | &[] |
208 | } else { |
209 | std::slice::from_raw_parts(self.idx.as_ptr() as *const GStringPtr, self.len) |
210 | } |
211 | } |
212 | } |
213 | } |
214 | |
215 | impl Drop for IntoIter { |
216 | #[inline ] |
217 | fn drop(&mut self) { |
218 | unsafe { |
219 | for i: usize in 0..self.len { |
220 | ffi::g_free(*self.idx.as_ptr().add(count:i) as ffi::gpointer); |
221 | } |
222 | |
223 | if !self.empty { |
224 | ffi::g_free(self.ptr.as_ptr() as ffi::gpointer); |
225 | } |
226 | } |
227 | } |
228 | } |
229 | |
230 | impl Iterator for IntoIter { |
231 | type Item = GString; |
232 | |
233 | #[inline ] |
234 | fn next(&mut self) -> Option<Self::Item> { |
235 | if self.len == 0 { |
236 | return None; |
237 | } |
238 | |
239 | unsafe { |
240 | let p = self.idx.as_ptr(); |
241 | self.len -= 1; |
242 | self.idx = ptr::NonNull::new_unchecked(p.add(1)); |
243 | Some(GString::from_glib_full(*p)) |
244 | } |
245 | } |
246 | |
247 | #[inline ] |
248 | fn size_hint(&self) -> (usize, Option<usize>) { |
249 | (self.len, Some(self.len)) |
250 | } |
251 | |
252 | #[inline ] |
253 | fn count(self) -> usize { |
254 | self.len |
255 | } |
256 | |
257 | #[inline ] |
258 | fn last(mut self) -> Option<GString> { |
259 | if self.len == 0 { |
260 | None |
261 | } else { |
262 | self.len -= 1; |
263 | Some(unsafe { GString::from_glib_full(*self.idx.as_ptr().add(self.len)) }) |
264 | } |
265 | } |
266 | } |
267 | |
268 | impl DoubleEndedIterator for IntoIter { |
269 | #[inline ] |
270 | fn next_back(&mut self) -> Option<GString> { |
271 | if self.len == 0 { |
272 | None |
273 | } else { |
274 | self.len -= 1; |
275 | Some(unsafe { GString::from_glib_full(*self.idx.as_ptr().add(self.len)) }) |
276 | } |
277 | } |
278 | } |
279 | |
280 | impl ExactSizeIterator for IntoIter {} |
281 | |
282 | impl std::iter::FusedIterator for IntoIter {} |
283 | |
284 | impl From<StrV> for Vec<GString> { |
285 | #[inline ] |
286 | fn from(value: StrV) -> Self { |
287 | value.into_iter().collect() |
288 | } |
289 | } |
290 | |
291 | impl From<Vec<String>> for StrV { |
292 | #[inline ] |
293 | fn from(value: Vec<String>) -> Self { |
294 | unsafe { |
295 | let len: usize = value.len(); |
296 | let mut s: StrV = Self::with_capacity(len); |
297 | for (i: usize, item: String) in value.into_iter().enumerate() { |
298 | *s.ptr.as_ptr().add(count:i) = GString::from(item).into_glib_ptr(); |
299 | } |
300 | s.len = len; |
301 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
302 | s |
303 | } |
304 | } |
305 | } |
306 | |
307 | impl From<Vec<&'_ str>> for StrV { |
308 | #[inline ] |
309 | fn from(value: Vec<&'_ str>) -> Self { |
310 | value.as_slice().into() |
311 | } |
312 | } |
313 | |
314 | impl From<Vec<GString>> for StrV { |
315 | #[inline ] |
316 | fn from(value: Vec<GString>) -> Self { |
317 | unsafe { |
318 | let len: usize = value.len(); |
319 | let mut s: StrV = Self::with_capacity(len); |
320 | for (i: usize, v: GString) in value.into_iter().enumerate() { |
321 | *s.ptr.as_ptr().add(count:i) = v.into_glib_ptr(); |
322 | } |
323 | s.len = len; |
324 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
325 | s |
326 | } |
327 | } |
328 | } |
329 | |
330 | impl<const N: usize> From<[GString; N]> for StrV { |
331 | #[inline ] |
332 | fn from(value: [GString; N]) -> Self { |
333 | unsafe { |
334 | let len: usize = value.len(); |
335 | let mut s: StrV = Self::with_capacity(len); |
336 | for (i: usize, v: GString) in value.into_iter().enumerate() { |
337 | *s.ptr.as_ptr().add(count:i) = v.into_glib_ptr(); |
338 | } |
339 | s.len = len; |
340 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
341 | s |
342 | } |
343 | } |
344 | } |
345 | |
346 | impl<const N: usize> From<[String; N]> for StrV { |
347 | #[inline ] |
348 | fn from(value: [String; N]) -> Self { |
349 | unsafe { |
350 | let len: usize = value.len(); |
351 | let mut s: StrV = Self::with_capacity(len); |
352 | for (i: usize, v: String) in value.into_iter().enumerate() { |
353 | *s.ptr.as_ptr().add(count:i) = GString::from(v).into_glib_ptr(); |
354 | } |
355 | s.len = len; |
356 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
357 | s |
358 | } |
359 | } |
360 | } |
361 | |
362 | impl<const N: usize> From<[&'_ str; N]> for StrV { |
363 | #[inline ] |
364 | fn from(value: [&'_ str; N]) -> Self { |
365 | unsafe { |
366 | let mut s: StrV = Self::with_capacity(value.len()); |
367 | for (i: usize, item: &&str) in value.iter().enumerate() { |
368 | *s.ptr.as_ptr().add(count:i) = GString::from(*item).into_glib_ptr(); |
369 | } |
370 | s.len = value.len(); |
371 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
372 | s |
373 | } |
374 | } |
375 | } |
376 | |
377 | impl<const N: usize> From<[&'_ GStr; N]> for StrV { |
378 | #[inline ] |
379 | fn from(value: [&'_ GStr; N]) -> Self { |
380 | unsafe { |
381 | let mut s: StrV = Self::with_capacity(value.len()); |
382 | for (i: usize, item: &&GStr) in value.iter().enumerate() { |
383 | *s.ptr.as_ptr().add(count:i) = GString::from(*item).into_glib_ptr(); |
384 | } |
385 | s.len = value.len(); |
386 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
387 | s |
388 | } |
389 | } |
390 | } |
391 | |
392 | impl From<&'_ [&'_ str]> for StrV { |
393 | #[inline ] |
394 | fn from(value: &'_ [&'_ str]) -> Self { |
395 | unsafe { |
396 | let mut s: StrV = Self::with_capacity(value.len()); |
397 | for (i: usize, item: &&str) in value.iter().enumerate() { |
398 | *s.ptr.as_ptr().add(count:i) = GString::from(*item).into_glib_ptr(); |
399 | } |
400 | s.len = value.len(); |
401 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
402 | s |
403 | } |
404 | } |
405 | } |
406 | |
407 | impl From<&'_ [&'_ GStr]> for StrV { |
408 | #[inline ] |
409 | fn from(value: &'_ [&'_ GStr]) -> Self { |
410 | unsafe { |
411 | let mut s: StrV = Self::with_capacity(value.len()); |
412 | for (i: usize, item: &&GStr) in value.iter().enumerate() { |
413 | *s.ptr.as_ptr().add(count:i) = GString::from(*item).into_glib_ptr(); |
414 | } |
415 | s.len = value.len(); |
416 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
417 | s |
418 | } |
419 | } |
420 | } |
421 | |
422 | impl Clone for StrV { |
423 | #[inline ] |
424 | fn clone(&self) -> Self { |
425 | unsafe { |
426 | let mut s: StrV = Self::with_capacity(self.len()); |
427 | for (i: usize, item: &GStringPtr) in self.iter().enumerate() { |
428 | *s.ptr.as_ptr().add(count:i) = GString::from(item.as_str()).into_glib_ptr(); |
429 | } |
430 | s.len = self.len(); |
431 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
432 | s |
433 | } |
434 | } |
435 | } |
436 | |
437 | impl StrV { |
438 | // rustdoc-stripper-ignore-next |
439 | /// Borrows a C array. |
440 | #[inline ] |
441 | pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a [GStringPtr] { |
442 | let mut len = 0; |
443 | if !ptr.is_null() { |
444 | while !(*ptr.add(len)).is_null() { |
445 | len += 1; |
446 | } |
447 | } |
448 | Self::from_glib_borrow_num(ptr, len) |
449 | } |
450 | |
451 | // rustdoc-stripper-ignore-next |
452 | /// Borrows a C array. |
453 | #[inline ] |
454 | pub unsafe fn from_glib_borrow_num<'a>( |
455 | ptr: *const *const c_char, |
456 | len: usize, |
457 | ) -> &'a [GStringPtr] { |
458 | debug_assert!(!ptr.is_null() || len == 0); |
459 | |
460 | if len == 0 { |
461 | &[] |
462 | } else { |
463 | std::slice::from_raw_parts(ptr as *const GStringPtr, len) |
464 | } |
465 | } |
466 | |
467 | // rustdoc-stripper-ignore-next |
468 | /// Create a new `StrV` around a C array. |
469 | #[inline ] |
470 | pub unsafe fn from_glib_none_num( |
471 | ptr: *const *const c_char, |
472 | len: usize, |
473 | _null_terminated: bool, |
474 | ) -> Self { |
475 | debug_assert!(!ptr.is_null() || len == 0); |
476 | |
477 | if len == 0 { |
478 | StrV::default() |
479 | } else { |
480 | // Allocate space for len + 1 pointers, one pointer for each string and a trailing |
481 | // null pointer. |
482 | let new_ptr = |
483 | ffi::g_malloc(mem::size_of::<*mut c_char>() * (len + 1)) as *mut *mut c_char; |
484 | |
485 | // Need to clone every item because we don't own it here |
486 | for i in 0..len { |
487 | let p = ptr.add(i) as *mut *const c_char; |
488 | let q = new_ptr.add(i) as *mut *const c_char; |
489 | *q = ffi::g_strdup(*p); |
490 | } |
491 | |
492 | *new_ptr.add(len) = ptr::null_mut(); |
493 | |
494 | StrV { |
495 | ptr: ptr::NonNull::new_unchecked(new_ptr), |
496 | len, |
497 | capacity: len + 1, |
498 | } |
499 | } |
500 | } |
501 | |
502 | // rustdoc-stripper-ignore-next |
503 | /// Create a new `StrV` around a C array. |
504 | #[inline ] |
505 | pub unsafe fn from_glib_container_num( |
506 | ptr: *mut *const c_char, |
507 | len: usize, |
508 | null_terminated: bool, |
509 | ) -> Self { |
510 | debug_assert!(!ptr.is_null() || len == 0); |
511 | |
512 | if len == 0 { |
513 | ffi::g_free(ptr as ffi::gpointer); |
514 | StrV::default() |
515 | } else { |
516 | // Need to clone every item because we don't own it here |
517 | for i in 0..len { |
518 | let p = ptr.add(i); |
519 | *p = ffi::g_strdup(*p); |
520 | } |
521 | |
522 | // And now it can be handled exactly the same as `from_glib_full_num()`. |
523 | Self::from_glib_full_num(ptr as *mut *mut c_char, len, null_terminated) |
524 | } |
525 | } |
526 | |
527 | // rustdoc-stripper-ignore-next |
528 | /// Create a new `StrV` around a C array. |
529 | #[inline ] |
530 | pub unsafe fn from_glib_full_num( |
531 | ptr: *mut *mut c_char, |
532 | len: usize, |
533 | null_terminated: bool, |
534 | ) -> Self { |
535 | debug_assert!(!ptr.is_null() || len == 0); |
536 | |
537 | if len == 0 { |
538 | ffi::g_free(ptr as ffi::gpointer); |
539 | StrV::default() |
540 | } else { |
541 | if null_terminated { |
542 | return StrV { |
543 | ptr: ptr::NonNull::new_unchecked(ptr), |
544 | len, |
545 | capacity: len + 1, |
546 | }; |
547 | } |
548 | |
549 | // Need to re-allocate here for adding the NULL-terminator |
550 | let capacity = len + 1; |
551 | assert_ne!(capacity, 0); |
552 | let ptr = ffi::g_realloc( |
553 | ptr as *mut _, |
554 | mem::size_of::<*mut c_char>().checked_mul(capacity).unwrap(), |
555 | ) as *mut *mut c_char; |
556 | *ptr.add(len) = ptr::null_mut(); |
557 | |
558 | StrV { |
559 | ptr: ptr::NonNull::new_unchecked(ptr), |
560 | len, |
561 | capacity, |
562 | } |
563 | } |
564 | } |
565 | |
566 | // rustdoc-stripper-ignore-next |
567 | /// Create a new `StrV` around a `NULL`-terminated C array. |
568 | #[inline ] |
569 | pub unsafe fn from_glib_none(ptr: *const *const c_char) -> Self { |
570 | let mut len = 0; |
571 | if !ptr.is_null() { |
572 | while !(*ptr.add(len)).is_null() { |
573 | len += 1; |
574 | } |
575 | } |
576 | |
577 | StrV::from_glib_none_num(ptr, len, true) |
578 | } |
579 | |
580 | // rustdoc-stripper-ignore-next |
581 | /// Create a new `StrV` around a `NULL`-terminated C array. |
582 | #[inline ] |
583 | pub unsafe fn from_glib_container(ptr: *mut *const c_char) -> Self { |
584 | let mut len = 0; |
585 | if !ptr.is_null() { |
586 | while !(*ptr.add(len)).is_null() { |
587 | len += 1; |
588 | } |
589 | } |
590 | |
591 | StrV::from_glib_container_num(ptr, len, true) |
592 | } |
593 | |
594 | // rustdoc-stripper-ignore-next |
595 | /// Create a new `StrV` around a `NULL`-terminated C array. |
596 | #[inline ] |
597 | pub unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self { |
598 | let mut len = 0; |
599 | if !ptr.is_null() { |
600 | while !(*ptr.add(len)).is_null() { |
601 | len += 1; |
602 | } |
603 | } |
604 | |
605 | StrV::from_glib_full_num(ptr, len, true) |
606 | } |
607 | |
608 | // rustdoc-stripper-ignore-next |
609 | /// Creates a new empty slice. |
610 | #[inline ] |
611 | pub fn new() -> Self { |
612 | StrV { |
613 | ptr: ptr::NonNull::dangling(), |
614 | len: 0, |
615 | capacity: 0, |
616 | } |
617 | } |
618 | |
619 | // rustdoc-stripper-ignore-next |
620 | /// Creates a new empty slice with the given capacity. |
621 | #[inline ] |
622 | pub fn with_capacity(capacity: usize) -> Self { |
623 | let mut s = Self::new(); |
624 | s.reserve(capacity); |
625 | s |
626 | } |
627 | |
628 | // rustdoc-stripper-ignore-next |
629 | /// Returns the underlying pointer. |
630 | /// |
631 | /// This is guaranteed to be `NULL`-terminated. |
632 | #[inline ] |
633 | pub fn as_ptr(&self) -> *const *mut c_char { |
634 | if self.len == 0 { |
635 | static EMPTY: [usize; 1] = [0]; |
636 | |
637 | EMPTY.as_ptr() as *const _ |
638 | } else { |
639 | self.ptr.as_ptr() |
640 | } |
641 | } |
642 | |
643 | // rustdoc-stripper-ignore-next |
644 | /// Consumes the slice and returns the underlying pointer. |
645 | /// |
646 | /// This is guaranteed to be `NULL`-terminated. |
647 | #[inline ] |
648 | pub fn into_raw(mut self) -> *mut *mut c_char { |
649 | if self.len == 0 { |
650 | ptr::null_mut() |
651 | } else { |
652 | self.len = 0; |
653 | self.capacity = 0; |
654 | self.ptr.as_ptr() |
655 | } |
656 | } |
657 | |
658 | // rustdoc-stripper-ignore-next |
659 | /// Gets the length of the slice. |
660 | #[inline ] |
661 | pub fn len(&self) -> usize { |
662 | self.len |
663 | } |
664 | |
665 | // rustdoc-stripper-ignore-next |
666 | /// Returns `true` if the slice is empty. |
667 | #[inline ] |
668 | pub fn is_empty(&self) -> bool { |
669 | self.len == 0 |
670 | } |
671 | |
672 | // rustdoc-stripper-ignore-next |
673 | /// Returns the capacity of the slice. |
674 | /// |
675 | /// This includes the space that is reserved for the `NULL`-terminator. |
676 | #[inline ] |
677 | pub fn capacity(&self) -> usize { |
678 | self.capacity |
679 | } |
680 | |
681 | // rustdoc-stripper-ignore-next |
682 | /// Sets the length of the slice to `len`. |
683 | /// |
684 | /// # SAFETY |
685 | /// |
686 | /// There must be at least `len` valid items and a `NULL`-terminator after the last item. |
687 | pub unsafe fn set_len(&mut self, len: usize) { |
688 | self.len = len; |
689 | } |
690 | |
691 | // rustdoc-stripper-ignore-next |
692 | /// Reserves at least this much additional capacity. |
693 | #[allow (clippy::int_plus_one)] |
694 | pub fn reserve(&mut self, additional: usize) { |
695 | // Nothing new to reserve as there's still enough space |
696 | if self.len + additional + 1 <= self.capacity { |
697 | return; |
698 | } |
699 | |
700 | let new_capacity = |
701 | usize::next_power_of_two(std::cmp::max(self.len + additional, MIN_SIZE) + 1); |
702 | assert_ne!(new_capacity, 0); |
703 | assert!(new_capacity > self.capacity); |
704 | |
705 | unsafe { |
706 | let ptr = if self.capacity == 0 { |
707 | ptr::null_mut() |
708 | } else { |
709 | self.ptr.as_ptr() as *mut _ |
710 | }; |
711 | let new_ptr = ffi::g_realloc( |
712 | ptr, |
713 | mem::size_of::<*mut c_char>() |
714 | .checked_mul(new_capacity) |
715 | .unwrap(), |
716 | ) as *mut *mut c_char; |
717 | if self.capacity == 0 { |
718 | *new_ptr = ptr::null_mut(); |
719 | } |
720 | self.ptr = ptr::NonNull::new_unchecked(new_ptr); |
721 | self.capacity = new_capacity; |
722 | } |
723 | } |
724 | |
725 | // rustdoc-stripper-ignore-next |
726 | /// Borrows this slice as a `&[GStringPtr]`. |
727 | #[inline ] |
728 | pub const fn as_slice(&self) -> &[GStringPtr] { |
729 | unsafe { |
730 | if self.len == 0 { |
731 | &[] |
732 | } else { |
733 | std::slice::from_raw_parts(self.ptr.as_ptr() as *const GStringPtr, self.len) |
734 | } |
735 | } |
736 | } |
737 | |
738 | // rustdoc-stripper-ignore-next |
739 | /// Removes all items from the slice. |
740 | #[inline ] |
741 | pub fn clear(&mut self) { |
742 | unsafe { |
743 | for i in 0..self.len { |
744 | ffi::g_free(*self.ptr.as_ptr().add(i) as ffi::gpointer); |
745 | } |
746 | |
747 | self.len = 0; |
748 | } |
749 | } |
750 | |
751 | // rustdoc-stripper-ignore-next |
752 | /// Clones and appends all elements in `slice` to the slice. |
753 | #[inline ] |
754 | pub fn extend_from_slice<S: AsRef<str>>(&mut self, other: &[S]) { |
755 | // Nothing new to reserve as there's still enough space |
756 | if self.len + other.len() + 1 > self.capacity { |
757 | self.reserve(other.len()); |
758 | } |
759 | |
760 | unsafe { |
761 | for item in other { |
762 | *self.ptr.as_ptr().add(self.len) = GString::from(item.as_ref()).into_glib_ptr(); |
763 | self.len += 1; |
764 | } |
765 | |
766 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
767 | } |
768 | } |
769 | |
770 | // rustdoc-stripper-ignore-next |
771 | /// Inserts `item` at position `index` of the slice, shifting all elements after it to the |
772 | /// right. |
773 | #[inline ] |
774 | pub fn insert(&mut self, index: usize, item: GString) { |
775 | assert!(index <= self.len); |
776 | |
777 | // Nothing new to reserve as there's still enough space |
778 | if self.len + 1 + 1 > self.capacity { |
779 | self.reserve(1); |
780 | } |
781 | |
782 | unsafe { |
783 | if index == self.len { |
784 | *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr(); |
785 | } else { |
786 | let p = self.ptr.as_ptr().add(index); |
787 | ptr::copy(p, p.add(1), self.len - index); |
788 | *self.ptr.as_ptr().add(index) = item.into_glib_ptr(); |
789 | } |
790 | |
791 | self.len += 1; |
792 | |
793 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
794 | } |
795 | } |
796 | |
797 | // rustdoc-stripper-ignore-next |
798 | /// Pushes `item` to the end of the slice. |
799 | #[inline ] |
800 | pub fn push(&mut self, item: GString) { |
801 | // Nothing new to reserve as there's still enough space |
802 | if self.len + 1 + 1 > self.capacity { |
803 | self.reserve(1); |
804 | } |
805 | |
806 | unsafe { |
807 | *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr(); |
808 | self.len += 1; |
809 | |
810 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
811 | } |
812 | } |
813 | |
814 | // rustdoc-stripper-ignore-next |
815 | /// Removes item from position `index` of the slice, shifting all elements after it to the |
816 | /// left. |
817 | #[inline ] |
818 | pub fn remove(&mut self, index: usize) -> GString { |
819 | assert!(index < self.len); |
820 | |
821 | unsafe { |
822 | let p = self.ptr.as_ptr().add(index); |
823 | let item = *p; |
824 | ptr::copy(p.add(1), p, self.len - index - 1); |
825 | |
826 | self.len -= 1; |
827 | |
828 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
829 | |
830 | GString::from_glib_full(item) |
831 | } |
832 | } |
833 | |
834 | // rustdoc-stripper-ignore-next |
835 | /// Swaps item from position `index` of the slice and returns it. |
836 | #[inline ] |
837 | pub fn swap(&mut self, index: usize, new_item: GString) -> GString { |
838 | assert!(index < self.len); |
839 | |
840 | unsafe { |
841 | let p = self.ptr.as_ptr().add(index); |
842 | let item = *p; |
843 | *p = new_item.into_glib_ptr(); |
844 | |
845 | GString::from_glib_full(item) |
846 | } |
847 | } |
848 | |
849 | // rustdoc-stripper-ignore-next |
850 | /// Removes the last item of the slice and returns it. |
851 | #[inline ] |
852 | pub fn pop(&mut self) -> Option<GString> { |
853 | if self.len == 0 { |
854 | return None; |
855 | } |
856 | |
857 | unsafe { |
858 | self.len -= 1; |
859 | let p = self.ptr.as_ptr().add(self.len); |
860 | let item = *p; |
861 | |
862 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
863 | |
864 | Some(GString::from_glib_full(item)) |
865 | } |
866 | } |
867 | |
868 | // rustdoc-stripper-ignore-next |
869 | /// Shortens the slice by keeping the last `len` items. |
870 | /// |
871 | /// If there are fewer than `len` items then this has no effect. |
872 | #[inline ] |
873 | pub fn truncate(&mut self, len: usize) { |
874 | if self.len <= len { |
875 | return; |
876 | } |
877 | |
878 | unsafe { |
879 | while self.len > len { |
880 | self.len -= 1; |
881 | let p = self.ptr.as_ptr().add(self.len); |
882 | ffi::g_free(*p as ffi::gpointer); |
883 | *p = ptr::null_mut(); |
884 | } |
885 | } |
886 | } |
887 | |
888 | // rustdoc-stripper-ignore-next |
889 | /// Joins the strings into a longer string, with an optional separator |
890 | #[inline ] |
891 | #[doc (alias = "g_strjoinv" )] |
892 | pub fn join(&self, separator: Option<impl IntoGStr>) -> GString { |
893 | separator.run_with_gstr(|separator| unsafe { |
894 | from_glib_full(ffi::g_strjoinv( |
895 | separator.to_glib_none().0, |
896 | self.as_ptr() as *mut _, |
897 | )) |
898 | }) |
899 | } |
900 | |
901 | // rustdoc-stripper-ignore-next |
902 | /// Checks whether the `StrV` contains the specified string |
903 | #[inline ] |
904 | #[doc (alias = "g_strv_contains" )] |
905 | pub fn contains(&self, s: impl IntoGStr) -> bool { |
906 | s.run_with_gstr(|s| unsafe { |
907 | from_glib(ffi::g_strv_contains( |
908 | self.as_ptr() as *const _, |
909 | s.to_glib_none().0, |
910 | )) |
911 | }) |
912 | } |
913 | } |
914 | |
915 | impl FromGlibContainer<*mut c_char, *mut *mut c_char> for StrV { |
916 | #[inline ] |
917 | unsafe fn from_glib_none_num(ptr: *mut *mut c_char, num: usize) -> Self { |
918 | Self::from_glib_none_num(ptr as *const *const c_char, len:num, _null_terminated:false) |
919 | } |
920 | |
921 | #[inline ] |
922 | unsafe fn from_glib_container_num(ptr: *mut *mut c_char, num: usize) -> Self { |
923 | Self::from_glib_container_num(ptr as *mut *const c_char, len:num, null_terminated:false) |
924 | } |
925 | |
926 | #[inline ] |
927 | unsafe fn from_glib_full_num(ptr: *mut *mut c_char, num: usize) -> Self { |
928 | Self::from_glib_full_num(ptr, len:num, null_terminated:false) |
929 | } |
930 | } |
931 | |
932 | impl FromGlibContainer<*mut c_char, *const *mut c_char> for StrV { |
933 | unsafe fn from_glib_none_num(ptr: *const *mut c_char, num: usize) -> Self { |
934 | Self::from_glib_none_num(ptr as *const *const c_char, len:num, _null_terminated:false) |
935 | } |
936 | |
937 | unsafe fn from_glib_container_num(_ptr: *const *mut c_char, _num: usize) -> Self { |
938 | unimplemented!(); |
939 | } |
940 | |
941 | unsafe fn from_glib_full_num(_ptr: *const *mut c_char, _num: usize) -> Self { |
942 | unimplemented!(); |
943 | } |
944 | } |
945 | |
946 | impl FromGlibPtrContainer<*mut c_char, *mut *mut c_char> for StrV { |
947 | #[inline ] |
948 | unsafe fn from_glib_none(ptr: *mut *mut c_char) -> Self { |
949 | Self::from_glib_none(ptr as *const *const c_char) |
950 | } |
951 | |
952 | #[inline ] |
953 | unsafe fn from_glib_container(ptr: *mut *mut c_char) -> Self { |
954 | Self::from_glib_container(ptr as *mut *const c_char) |
955 | } |
956 | |
957 | #[inline ] |
958 | unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self { |
959 | Self::from_glib_full(ptr) |
960 | } |
961 | } |
962 | |
963 | impl FromGlibPtrContainer<*mut c_char, *const *mut c_char> for StrV { |
964 | #[inline ] |
965 | unsafe fn from_glib_none(ptr: *const *mut c_char) -> Self { |
966 | Self::from_glib_none(ptr as *const *const c_char) |
967 | } |
968 | |
969 | unsafe fn from_glib_container(_ptr: *const *mut c_char) -> Self { |
970 | unimplemented!(); |
971 | } |
972 | |
973 | unsafe fn from_glib_full(_ptr: *const *mut c_char) -> Self { |
974 | unimplemented!(); |
975 | } |
976 | } |
977 | |
978 | impl<'a> ToGlibPtr<'a, *mut *mut c_char> for StrV { |
979 | type Storage = PhantomData<&'a Self>; |
980 | |
981 | #[inline ] |
982 | fn to_glib_none(&'a self) -> Stash<'a, *mut *mut c_char, Self> { |
983 | Stash(self.as_ptr() as *mut _, PhantomData) |
984 | } |
985 | |
986 | #[inline ] |
987 | fn to_glib_container(&'a self) -> Stash<'a, *mut *mut c_char, Self> { |
988 | unsafe { |
989 | let ptr: *mut *mut {unknown} = |
990 | ffi::g_malloc(n_bytes:mem::size_of::<*mut c_char>() * (self.len() + 1)) as *mut *mut c_char; |
991 | ptr::copy_nonoverlapping(self.as_ptr(), dst:ptr, self.len() + 1); |
992 | Stash(ptr, PhantomData) |
993 | } |
994 | } |
995 | |
996 | #[inline ] |
997 | fn to_glib_full(&self) -> *mut *mut c_char { |
998 | self.clone().into_raw() |
999 | } |
1000 | } |
1001 | |
1002 | impl<'a> ToGlibPtr<'a, *const *mut c_char> for StrV { |
1003 | type Storage = PhantomData<&'a Self>; |
1004 | |
1005 | #[inline ] |
1006 | fn to_glib_none(&'a self) -> Stash<'a, *const *mut c_char, Self> { |
1007 | Stash(self.as_ptr(), PhantomData) |
1008 | } |
1009 | } |
1010 | |
1011 | impl IntoGlibPtr<*mut *mut c_char> for StrV { |
1012 | #[inline ] |
1013 | unsafe fn into_glib_ptr(self) -> *mut *mut c_char { |
1014 | self.into_raw() |
1015 | } |
1016 | } |
1017 | |
1018 | impl StaticType for StrV { |
1019 | #[inline ] |
1020 | fn static_type() -> crate::Type { |
1021 | <Vec<String>>::static_type() |
1022 | } |
1023 | } |
1024 | |
1025 | impl StaticType for &'_ [GStringPtr] { |
1026 | #[inline ] |
1027 | fn static_type() -> crate::Type { |
1028 | <Vec<String>>::static_type() |
1029 | } |
1030 | } |
1031 | |
1032 | impl crate::value::ValueType for StrV { |
1033 | type Type = Vec<String>; |
1034 | } |
1035 | |
1036 | unsafe impl<'a> crate::value::FromValue<'a> for StrV { |
1037 | type Checker = crate::value::GenericValueTypeChecker<Self>; |
1038 | |
1039 | unsafe fn from_value(value: &'a crate::value::Value) -> Self { |
1040 | let ptr: *mut *mut {unknown} = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut *mut c_char; |
1041 | FromGlibPtrContainer::from_glib_full(ptr) |
1042 | } |
1043 | } |
1044 | |
1045 | unsafe impl<'a> crate::value::FromValue<'a> for &'a [GStringPtr] { |
1046 | type Checker = crate::value::GenericValueTypeChecker<Self>; |
1047 | |
1048 | unsafe fn from_value(value: &'a crate::value::Value) -> Self { |
1049 | let ptr: *const *const {unknown} = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char; |
1050 | StrV::from_glib_borrow(ptr) |
1051 | } |
1052 | } |
1053 | |
1054 | impl crate::value::ToValue for StrV { |
1055 | fn to_value(&self) -> crate::value::Value { |
1056 | unsafe { |
1057 | let mut value: Value = crate::value::Value::for_value_type::<Self>(); |
1058 | gobject_ffi::g_value_set_boxed( |
1059 | value.to_glib_none_mut().0, |
1060 | self.as_ptr() as ffi::gpointer, |
1061 | ); |
1062 | value |
1063 | } |
1064 | } |
1065 | |
1066 | fn value_type(&self) -> crate::Type { |
1067 | <StrV as StaticType>::static_type() |
1068 | } |
1069 | } |
1070 | |
1071 | impl From<StrV> for crate::Value { |
1072 | #[inline ] |
1073 | fn from(s: StrV) -> Self { |
1074 | unsafe { |
1075 | let mut value: Value = crate::value::Value::for_value_type::<StrV>(); |
1076 | gobject_ffi::g_value_take_boxed( |
1077 | value.to_glib_none_mut().0, |
1078 | v_boxed:s.into_raw() as ffi::gpointer, |
1079 | ); |
1080 | value |
1081 | } |
1082 | } |
1083 | } |
1084 | |
1085 | // rustdoc-stripper-ignore-next |
1086 | /// A trait to accept both `&[T]` or `StrV` as an argument. |
1087 | pub trait IntoStrV { |
1088 | // rustdoc-stripper-ignore-next |
1089 | /// Runs the given closure with a `NULL`-terminated array. |
1090 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R; |
1091 | } |
1092 | |
1093 | impl IntoStrV for StrV { |
1094 | #[inline ] |
1095 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1096 | <&Self>::run_with_strv(&self, f) |
1097 | } |
1098 | } |
1099 | |
1100 | impl IntoStrV for &'_ StrV { |
1101 | #[inline ] |
1102 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1103 | f(unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) }) |
1104 | } |
1105 | } |
1106 | |
1107 | // rustdoc-stripper-ignore-next |
1108 | /// Maximum number of pointers to stack-allocate before falling back to a heap allocation. |
1109 | /// |
1110 | /// The beginning will be used for the pointers, the remainder for the actual string content. |
1111 | const MAX_STACK_ALLOCATION: usize = 16; |
1112 | |
1113 | impl IntoStrV for Vec<GString> { |
1114 | #[inline ] |
1115 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1116 | self.as_slice().run_with_strv(f) |
1117 | } |
1118 | } |
1119 | |
1120 | impl IntoStrV for Vec<&'_ GString> { |
1121 | #[inline ] |
1122 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1123 | self.as_slice().run_with_strv(f) |
1124 | } |
1125 | } |
1126 | |
1127 | impl IntoStrV for Vec<&'_ GStr> { |
1128 | #[inline ] |
1129 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1130 | self.as_slice().run_with_strv(f) |
1131 | } |
1132 | } |
1133 | |
1134 | impl IntoStrV for Vec<&'_ str> { |
1135 | #[inline ] |
1136 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1137 | self.as_slice().run_with_strv(f) |
1138 | } |
1139 | } |
1140 | |
1141 | impl IntoStrV for Vec<String> { |
1142 | #[inline ] |
1143 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1144 | self.as_slice().run_with_strv(f) |
1145 | } |
1146 | } |
1147 | |
1148 | impl IntoStrV for Vec<&'_ String> { |
1149 | #[inline ] |
1150 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1151 | self.as_slice().run_with_strv(f) |
1152 | } |
1153 | } |
1154 | |
1155 | impl IntoStrV for &[GString] { |
1156 | #[inline ] |
1157 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1158 | let required_len: usize = (self.len() + 1) * mem::size_of::<*mut c_char>(); |
1159 | |
1160 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1161 | unsafe { |
1162 | let mut s: MaybeUninit<[*mut {unknown}; 16]> = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1163 | let ptrs: *mut *mut {unknown} = s.as_mut_ptr() as *mut *mut c_char; |
1164 | |
1165 | for (i: usize, item: &GString) in self.iter().enumerate() { |
1166 | *ptrs.add(count:i) = item.as_ptr() as *mut _; |
1167 | } |
1168 | *ptrs.add(self.len()) = ptr::null_mut(); |
1169 | |
1170 | f(std::slice::from_raw_parts(data:ptrs, self.len())) |
1171 | } |
1172 | } else { |
1173 | let mut s: StrV = StrV::with_capacity(self.len()); |
1174 | s.extend_from_slice(self); |
1175 | s.run_with_strv(f) |
1176 | } |
1177 | } |
1178 | } |
1179 | |
1180 | impl IntoStrV for &[&GString] { |
1181 | #[inline ] |
1182 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1183 | let required_len: usize = (self.len() + 1) * mem::size_of::<*mut c_char>(); |
1184 | |
1185 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1186 | unsafe { |
1187 | let mut s: MaybeUninit<[*mut {unknown}; 16]> = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1188 | let ptrs: *mut *mut {unknown} = s.as_mut_ptr() as *mut *mut c_char; |
1189 | |
1190 | for (i: usize, item: &&GString) in self.iter().enumerate() { |
1191 | *ptrs.add(count:i) = item.as_ptr() as *mut _; |
1192 | } |
1193 | *ptrs.add(self.len()) = ptr::null_mut(); |
1194 | |
1195 | f(std::slice::from_raw_parts(data:ptrs, self.len())) |
1196 | } |
1197 | } else { |
1198 | let mut s: StrV = StrV::with_capacity(self.len()); |
1199 | s.extend_from_slice(self); |
1200 | s.run_with_strv(f) |
1201 | } |
1202 | } |
1203 | } |
1204 | |
1205 | impl IntoStrV for &[&GStr] { |
1206 | #[inline ] |
1207 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1208 | let required_len: usize = (self.len() + 1) * mem::size_of::<*mut c_char>(); |
1209 | |
1210 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1211 | unsafe { |
1212 | let mut s: MaybeUninit<[*mut {unknown}; 16]> = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1213 | let ptrs: *mut *mut {unknown} = s.as_mut_ptr() as *mut *mut c_char; |
1214 | |
1215 | for (i: usize, item: &&GStr) in self.iter().enumerate() { |
1216 | *ptrs.add(count:i) = item.as_ptr() as *mut _; |
1217 | } |
1218 | *ptrs.add(self.len()) = ptr::null_mut(); |
1219 | |
1220 | f(std::slice::from_raw_parts(data:ptrs, self.len())) |
1221 | } |
1222 | } else { |
1223 | let mut s: StrV = StrV::with_capacity(self.len()); |
1224 | s.extend_from_slice(self); |
1225 | s.run_with_strv(f) |
1226 | } |
1227 | } |
1228 | } |
1229 | |
1230 | impl IntoStrV for &[&str] { |
1231 | #[inline ] |
1232 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1233 | let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>() |
1234 | + self.iter().map(|s| s.len() + 1).sum::<usize>(); |
1235 | |
1236 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1237 | unsafe { |
1238 | let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1239 | let ptrs = s.as_mut_ptr() as *mut *mut c_char; |
1240 | let mut strs = ptrs.add(self.len() + 1) as *mut c_char; |
1241 | |
1242 | for (i, item) in self.iter().enumerate() { |
1243 | ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len()); |
1244 | *strs.add(item.len()) = 0; |
1245 | *ptrs.add(i) = strs; |
1246 | strs = strs.add(item.len() + 1); |
1247 | } |
1248 | *ptrs.add(self.len()) = ptr::null_mut(); |
1249 | |
1250 | f(std::slice::from_raw_parts(ptrs, self.len())) |
1251 | } |
1252 | } else { |
1253 | let mut s = StrV::with_capacity(self.len()); |
1254 | s.extend_from_slice(self); |
1255 | s.run_with_strv(f) |
1256 | } |
1257 | } |
1258 | } |
1259 | |
1260 | impl IntoStrV for &[String] { |
1261 | #[inline ] |
1262 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1263 | let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>() |
1264 | + self.iter().map(|s| s.len() + 1).sum::<usize>(); |
1265 | |
1266 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1267 | unsafe { |
1268 | let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1269 | let ptrs = s.as_mut_ptr() as *mut *mut c_char; |
1270 | let mut strs = ptrs.add(self.len() + 1) as *mut c_char; |
1271 | |
1272 | for (i, item) in self.iter().enumerate() { |
1273 | ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len()); |
1274 | *strs.add(item.len()) = 0; |
1275 | *ptrs.add(i) = strs; |
1276 | strs = strs.add(item.len() + 1); |
1277 | } |
1278 | *ptrs.add(self.len()) = ptr::null_mut(); |
1279 | |
1280 | f(std::slice::from_raw_parts(ptrs, self.len())) |
1281 | } |
1282 | } else { |
1283 | let mut s = StrV::with_capacity(self.len()); |
1284 | s.extend_from_slice(self); |
1285 | s.run_with_strv(f) |
1286 | } |
1287 | } |
1288 | } |
1289 | |
1290 | impl IntoStrV for &[&String] { |
1291 | #[inline ] |
1292 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1293 | let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>() |
1294 | + self.iter().map(|s| s.len() + 1).sum::<usize>(); |
1295 | |
1296 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1297 | unsafe { |
1298 | let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1299 | let ptrs = s.as_mut_ptr() as *mut *mut c_char; |
1300 | let mut strs = ptrs.add(self.len() + 1) as *mut c_char; |
1301 | |
1302 | for (i, item) in self.iter().enumerate() { |
1303 | ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len()); |
1304 | *strs.add(item.len()) = 0; |
1305 | *ptrs.add(i) = strs; |
1306 | strs = strs.add(item.len() + 1); |
1307 | } |
1308 | *ptrs.add(self.len()) = ptr::null_mut(); |
1309 | |
1310 | f(std::slice::from_raw_parts(ptrs, self.len())) |
1311 | } |
1312 | } else { |
1313 | let mut s = StrV::with_capacity(self.len()); |
1314 | s.extend_from_slice(self); |
1315 | s.run_with_strv(f) |
1316 | } |
1317 | } |
1318 | } |
1319 | |
1320 | impl<const N: usize> IntoStrV for [GString; N] { |
1321 | #[inline ] |
1322 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1323 | self.as_slice().run_with_strv(f) |
1324 | } |
1325 | } |
1326 | |
1327 | impl<const N: usize> IntoStrV for [&'_ GString; N] { |
1328 | #[inline ] |
1329 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1330 | self.as_slice().run_with_strv(f) |
1331 | } |
1332 | } |
1333 | |
1334 | impl<const N: usize> IntoStrV for [&'_ GStr; N] { |
1335 | #[inline ] |
1336 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1337 | self.as_slice().run_with_strv(f) |
1338 | } |
1339 | } |
1340 | |
1341 | impl<const N: usize> IntoStrV for [&'_ str; N] { |
1342 | #[inline ] |
1343 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1344 | self.as_slice().run_with_strv(f) |
1345 | } |
1346 | } |
1347 | |
1348 | impl<const N: usize> IntoStrV for [String; N] { |
1349 | #[inline ] |
1350 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1351 | self.as_slice().run_with_strv(f) |
1352 | } |
1353 | } |
1354 | |
1355 | impl<const N: usize> IntoStrV for [&'_ String; N] { |
1356 | #[inline ] |
1357 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1358 | self.as_slice().run_with_strv(f) |
1359 | } |
1360 | } |
1361 | |
1362 | #[cfg (test)] |
1363 | mod test { |
1364 | use super::*; |
1365 | |
1366 | #[test ] |
1367 | fn test_from_glib_full() { |
1368 | let items = ["str1" , "str2" , "str3" , "str4" ]; |
1369 | |
1370 | let slice = unsafe { |
1371 | let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *mut c_char; |
1372 | *ptr.add(0) = items[0].to_glib_full(); |
1373 | *ptr.add(1) = items[1].to_glib_full(); |
1374 | *ptr.add(2) = items[2].to_glib_full(); |
1375 | *ptr.add(3) = items[3].to_glib_full(); |
1376 | |
1377 | StrV::from_glib_full_num(ptr, 4, false) |
1378 | }; |
1379 | |
1380 | for (a, b) in Iterator::zip(items.iter(), slice.iter()) { |
1381 | assert_eq!(a, b); |
1382 | } |
1383 | } |
1384 | |
1385 | #[test ] |
1386 | fn test_from_glib_container() { |
1387 | let items = [ |
1388 | crate::gstr!("str1" ), |
1389 | crate::gstr!("str2" ), |
1390 | crate::gstr!("str3" ), |
1391 | crate::gstr!("str4" ), |
1392 | ]; |
1393 | |
1394 | let slice = unsafe { |
1395 | let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char; |
1396 | *ptr.add(0) = items[0].as_ptr(); |
1397 | *ptr.add(1) = items[1].as_ptr(); |
1398 | *ptr.add(2) = items[2].as_ptr(); |
1399 | *ptr.add(3) = items[3].as_ptr(); |
1400 | |
1401 | StrV::from_glib_container_num(ptr, 4, false) |
1402 | }; |
1403 | |
1404 | for (a, b) in Iterator::zip(items.iter(), slice.iter()) { |
1405 | assert_eq!(a, b); |
1406 | } |
1407 | } |
1408 | |
1409 | #[test ] |
1410 | fn test_from_glib_none() { |
1411 | let items = [ |
1412 | crate::gstr!("str1" ), |
1413 | crate::gstr!("str2" ), |
1414 | crate::gstr!("str3" ), |
1415 | crate::gstr!("str4" ), |
1416 | ]; |
1417 | |
1418 | let slice = unsafe { |
1419 | let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char; |
1420 | *ptr.add(0) = items[0].as_ptr(); |
1421 | *ptr.add(1) = items[1].as_ptr(); |
1422 | *ptr.add(2) = items[2].as_ptr(); |
1423 | *ptr.add(3) = items[3].as_ptr(); |
1424 | |
1425 | let res = StrV::from_glib_none_num(ptr, 4, false); |
1426 | ffi::g_free(ptr as ffi::gpointer); |
1427 | res |
1428 | }; |
1429 | |
1430 | for (a, b) in Iterator::zip(items.iter(), slice.iter()) { |
1431 | assert_eq!(a, b); |
1432 | } |
1433 | } |
1434 | |
1435 | #[test ] |
1436 | fn test_from_slice() { |
1437 | let items = [ |
1438 | crate::gstr!("str1" ), |
1439 | crate::gstr!("str2" ), |
1440 | crate::gstr!("str3" ), |
1441 | ]; |
1442 | |
1443 | let slice1 = StrV::from(&items[..]); |
1444 | let slice2 = StrV::from(items); |
1445 | assert_eq!(slice1.len(), 3); |
1446 | assert_eq!(slice1, slice2); |
1447 | } |
1448 | |
1449 | #[test ] |
1450 | fn test_safe_api() { |
1451 | let items = [ |
1452 | crate::gstr!("str1" ), |
1453 | crate::gstr!("str2" ), |
1454 | crate::gstr!("str3" ), |
1455 | ]; |
1456 | |
1457 | let mut slice = StrV::from(&items[..]); |
1458 | assert_eq!(slice.len(), 3); |
1459 | slice.push(GString::from("str4" )); |
1460 | assert_eq!(slice.len(), 4); |
1461 | |
1462 | for (a, b) in Iterator::zip(items.iter(), slice.iter()) { |
1463 | assert_eq!(a, b); |
1464 | } |
1465 | assert_eq!(slice[3], "str4" ); |
1466 | |
1467 | let vec = Vec::from(slice); |
1468 | assert_eq!(vec.len(), 4); |
1469 | for (a, b) in Iterator::zip(items.iter(), vec.iter()) { |
1470 | assert_eq!(a, b); |
1471 | } |
1472 | assert_eq!(vec[3], "str4" ); |
1473 | |
1474 | let mut slice = StrV::from(vec); |
1475 | assert_eq!(slice.len(), 4); |
1476 | let e = slice.pop().unwrap(); |
1477 | assert_eq!(e, "str4" ); |
1478 | assert_eq!(slice.len(), 3); |
1479 | slice.insert(2, e); |
1480 | assert_eq!(slice.len(), 4); |
1481 | assert_eq!(slice[0], "str1" ); |
1482 | assert_eq!(slice[1], "str2" ); |
1483 | assert_eq!(slice[2], "str4" ); |
1484 | assert_eq!(slice[3], "str3" ); |
1485 | let e = slice.remove(2); |
1486 | assert_eq!(e, "str4" ); |
1487 | assert_eq!(slice.len(), 3); |
1488 | slice.push(e); |
1489 | assert_eq!(slice.len(), 4); |
1490 | |
1491 | for (a, b) in Iterator::zip(items.iter(), slice.into_iter()) { |
1492 | assert_eq!(*a, b); |
1493 | } |
1494 | } |
1495 | |
1496 | #[test ] |
1497 | fn test_into_strv() { |
1498 | let items = ["str1" , "str2" , "str3" , "str4" ]; |
1499 | |
1500 | items[..].run_with_strv(|s| unsafe { |
1501 | assert!((*s.as_ptr().add(4)).is_null()); |
1502 | assert_eq!(s.len(), items.len()); |
1503 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1504 | assert_eq!(s, items); |
1505 | }); |
1506 | |
1507 | Vec::from(&items[..]).run_with_strv(|s| unsafe { |
1508 | assert!((*s.as_ptr().add(4)).is_null()); |
1509 | assert_eq!(s.len(), items.len()); |
1510 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1511 | assert_eq!(s, items); |
1512 | }); |
1513 | |
1514 | StrV::from(&items[..]).run_with_strv(|s| unsafe { |
1515 | assert!((*s.as_ptr().add(4)).is_null()); |
1516 | assert_eq!(s.len(), items.len()); |
1517 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1518 | assert_eq!(s, items); |
1519 | }); |
1520 | |
1521 | let v = items.iter().copied().map(String::from).collect::<Vec<_>>(); |
1522 | items.run_with_strv(|s| unsafe { |
1523 | assert!((*s.as_ptr().add(4)).is_null()); |
1524 | assert_eq!(s.len(), v.len()); |
1525 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1526 | assert_eq!(s, items); |
1527 | }); |
1528 | |
1529 | let v = items.iter().copied().map(GString::from).collect::<Vec<_>>(); |
1530 | items.run_with_strv(|s| unsafe { |
1531 | assert!((*s.as_ptr().add(4)).is_null()); |
1532 | assert_eq!(s.len(), v.len()); |
1533 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1534 | assert_eq!(s, items); |
1535 | }); |
1536 | } |
1537 | |
1538 | #[test ] |
1539 | fn test_join() { |
1540 | let items = [ |
1541 | crate::gstr!("str1" ), |
1542 | crate::gstr!("str2" ), |
1543 | crate::gstr!("str3" ), |
1544 | ]; |
1545 | |
1546 | let strv = StrV::from(&items[..]); |
1547 | assert_eq!(strv.join(None::<&str>), "str1str2str3" ); |
1548 | assert_eq!(strv.join(Some("," )), "str1,str2,str3" ); |
1549 | } |
1550 | |
1551 | #[test ] |
1552 | fn test_contains() { |
1553 | let items = [ |
1554 | crate::gstr!("str1" ), |
1555 | crate::gstr!("str2" ), |
1556 | crate::gstr!("str3" ), |
1557 | ]; |
1558 | |
1559 | let strv = StrV::from(&items[..]); |
1560 | assert!(strv.contains("str2" )); |
1561 | assert!(!strv.contains("str4" )); |
1562 | } |
1563 | } |
1564 | |