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::{translate::*, GStr, GString, GStringPtr, IntoGStr, IntoOptionalGStr}; |
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<'a> PartialEq<[&'a str]> for StrV { |
67 | fn eq(&self, other: &[&'a str]) -> bool { |
68 | for (a: &GStringPtr, b: &&str) in Iterator::zip(self.iter(), other:other.iter()) { |
69 | if a != b { |
70 | return false; |
71 | } |
72 | } |
73 | |
74 | true |
75 | } |
76 | } |
77 | |
78 | impl<'a> PartialEq<StrV> for [&'a 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: &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<'a> From<Vec<&'a str>> for StrV { |
308 | #[inline ] |
309 | fn from(value: Vec<&'a 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<'a, const N: usize> From<[&'a str; N]> for StrV { |
347 | #[inline ] |
348 | fn from(value: [&'a str; N]) -> Self { |
349 | unsafe { |
350 | let mut s: StrV = Self::with_capacity(value.len()); |
351 | for (i: usize, item: &&str) in value.iter().enumerate() { |
352 | *s.ptr.as_ptr().add(count:i) = GString::from(*item).into_glib_ptr(); |
353 | } |
354 | s.len = value.len(); |
355 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
356 | s |
357 | } |
358 | } |
359 | } |
360 | |
361 | impl<'a, const N: usize> From<[&'a GStr; N]> for StrV { |
362 | #[inline ] |
363 | fn from(value: [&'a GStr; N]) -> Self { |
364 | unsafe { |
365 | let mut s: StrV = Self::with_capacity(value.len()); |
366 | for (i: usize, item: &&GStr) in value.iter().enumerate() { |
367 | *s.ptr.as_ptr().add(count:i) = GString::from(*item).into_glib_ptr(); |
368 | } |
369 | s.len = value.len(); |
370 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
371 | s |
372 | } |
373 | } |
374 | } |
375 | |
376 | impl<'a> From<&'a [&'a str]> for StrV { |
377 | #[inline ] |
378 | fn from(value: &'a [&'a str]) -> Self { |
379 | unsafe { |
380 | let mut s: StrV = Self::with_capacity(value.len()); |
381 | for (i: usize, item: &&str) in value.iter().enumerate() { |
382 | *s.ptr.as_ptr().add(count:i) = GString::from(*item).into_glib_ptr(); |
383 | } |
384 | s.len = value.len(); |
385 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
386 | s |
387 | } |
388 | } |
389 | } |
390 | |
391 | impl<'a> From<&'a [&'a GStr]> for StrV { |
392 | #[inline ] |
393 | fn from(value: &'a [&'a GStr]) -> Self { |
394 | unsafe { |
395 | let mut s: StrV = Self::with_capacity(value.len()); |
396 | for (i: usize, item: &&GStr) in value.iter().enumerate() { |
397 | *s.ptr.as_ptr().add(count:i) = GString::from(*item).into_glib_ptr(); |
398 | } |
399 | s.len = value.len(); |
400 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
401 | s |
402 | } |
403 | } |
404 | } |
405 | |
406 | impl Clone for StrV { |
407 | #[inline ] |
408 | fn clone(&self) -> Self { |
409 | unsafe { |
410 | let mut s: StrV = Self::with_capacity(self.len()); |
411 | for (i: usize, item: &GStringPtr) in self.iter().enumerate() { |
412 | *s.ptr.as_ptr().add(count:i) = GString::from(item.as_str()).into_glib_ptr(); |
413 | } |
414 | s.len = self.len(); |
415 | *s.ptr.as_ptr().add(count:s.len) = ptr::null_mut(); |
416 | s |
417 | } |
418 | } |
419 | } |
420 | |
421 | impl StrV { |
422 | // rustdoc-stripper-ignore-next |
423 | /// Borrows a C array. |
424 | #[inline ] |
425 | pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a [GStringPtr] { |
426 | let mut len = 0; |
427 | if !ptr.is_null() { |
428 | while !(*ptr.add(len)).is_null() { |
429 | len += 1; |
430 | } |
431 | } |
432 | Self::from_glib_borrow_num(ptr, len) |
433 | } |
434 | |
435 | // rustdoc-stripper-ignore-next |
436 | /// Borrows a C array. |
437 | #[inline ] |
438 | pub unsafe fn from_glib_borrow_num<'a>( |
439 | ptr: *const *const c_char, |
440 | len: usize, |
441 | ) -> &'a [GStringPtr] { |
442 | debug_assert!(!ptr.is_null() || len == 0); |
443 | |
444 | if len == 0 { |
445 | &[] |
446 | } else { |
447 | std::slice::from_raw_parts(ptr as *const GStringPtr, len) |
448 | } |
449 | } |
450 | |
451 | // rustdoc-stripper-ignore-next |
452 | /// Create a new `StrV` around a C array. |
453 | #[inline ] |
454 | pub unsafe fn from_glib_none_num( |
455 | ptr: *const *const c_char, |
456 | len: usize, |
457 | _null_terminated: bool, |
458 | ) -> Self { |
459 | debug_assert!(!ptr.is_null() || len == 0); |
460 | |
461 | if len == 0 { |
462 | StrV::default() |
463 | } else { |
464 | // Allocate space for len + 1 pointers, one pointer for each string and a trailing |
465 | // null pointer. |
466 | let new_ptr = |
467 | ffi::g_malloc(mem::size_of::<*mut c_char>() * (len + 1)) as *mut *mut c_char; |
468 | |
469 | // Need to clone every item because we don't own it here |
470 | for i in 0..len { |
471 | let p = ptr.add(i) as *mut *const c_char; |
472 | let q = new_ptr.add(i) as *mut *const c_char; |
473 | *q = ffi::g_strdup(*p); |
474 | } |
475 | |
476 | *new_ptr.add(len) = ptr::null_mut(); |
477 | |
478 | StrV { |
479 | ptr: ptr::NonNull::new_unchecked(new_ptr), |
480 | len, |
481 | capacity: len + 1, |
482 | } |
483 | } |
484 | } |
485 | |
486 | // rustdoc-stripper-ignore-next |
487 | /// Create a new `StrV` around a C array. |
488 | #[inline ] |
489 | pub unsafe fn from_glib_container_num( |
490 | ptr: *mut *const c_char, |
491 | len: usize, |
492 | null_terminated: bool, |
493 | ) -> Self { |
494 | debug_assert!(!ptr.is_null() || len == 0); |
495 | |
496 | if len == 0 { |
497 | ffi::g_free(ptr as ffi::gpointer); |
498 | StrV::default() |
499 | } else { |
500 | // Need to clone every item because we don't own it here |
501 | for i in 0..len { |
502 | let p = ptr.add(i); |
503 | *p = ffi::g_strdup(*p); |
504 | } |
505 | |
506 | // And now it can be handled exactly the same as `from_glib_full_num()`. |
507 | Self::from_glib_full_num(ptr as *mut *mut c_char, len, null_terminated) |
508 | } |
509 | } |
510 | |
511 | // rustdoc-stripper-ignore-next |
512 | /// Create a new `StrV` around a C array. |
513 | #[inline ] |
514 | pub unsafe fn from_glib_full_num( |
515 | ptr: *mut *mut c_char, |
516 | len: usize, |
517 | null_terminated: bool, |
518 | ) -> Self { |
519 | debug_assert!(!ptr.is_null() || len == 0); |
520 | |
521 | if len == 0 { |
522 | ffi::g_free(ptr as ffi::gpointer); |
523 | StrV::default() |
524 | } else { |
525 | if null_terminated { |
526 | return StrV { |
527 | ptr: ptr::NonNull::new_unchecked(ptr), |
528 | len, |
529 | capacity: len + 1, |
530 | }; |
531 | } |
532 | |
533 | // Need to re-allocate here for adding the NULL-terminator |
534 | let capacity = len + 1; |
535 | assert_ne!(capacity, 0); |
536 | let ptr = ffi::g_realloc( |
537 | ptr as *mut _, |
538 | mem::size_of::<*mut c_char>().checked_mul(capacity).unwrap(), |
539 | ) as *mut *mut c_char; |
540 | *ptr.add(len) = ptr::null_mut(); |
541 | |
542 | StrV { |
543 | ptr: ptr::NonNull::new_unchecked(ptr), |
544 | len, |
545 | capacity, |
546 | } |
547 | } |
548 | } |
549 | |
550 | // rustdoc-stripper-ignore-next |
551 | /// Create a new `StrV` around a `NULL`-terminated C array. |
552 | #[inline ] |
553 | pub unsafe fn from_glib_none(ptr: *const *const c_char) -> Self { |
554 | let mut len = 0; |
555 | if !ptr.is_null() { |
556 | while !(*ptr.add(len)).is_null() { |
557 | len += 1; |
558 | } |
559 | } |
560 | |
561 | StrV::from_glib_none_num(ptr, len, true) |
562 | } |
563 | |
564 | // rustdoc-stripper-ignore-next |
565 | /// Create a new `StrV` around a `NULL`-terminated C array. |
566 | #[inline ] |
567 | pub unsafe fn from_glib_container(ptr: *mut *const c_char) -> Self { |
568 | let mut len = 0; |
569 | if !ptr.is_null() { |
570 | while !(*ptr.add(len)).is_null() { |
571 | len += 1; |
572 | } |
573 | } |
574 | |
575 | StrV::from_glib_container_num(ptr, len, true) |
576 | } |
577 | |
578 | // rustdoc-stripper-ignore-next |
579 | /// Create a new `StrV` around a `NULL`-terminated C array. |
580 | #[inline ] |
581 | pub unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self { |
582 | let mut len = 0; |
583 | if !ptr.is_null() { |
584 | while !(*ptr.add(len)).is_null() { |
585 | len += 1; |
586 | } |
587 | } |
588 | |
589 | StrV::from_glib_full_num(ptr, len, true) |
590 | } |
591 | |
592 | // rustdoc-stripper-ignore-next |
593 | /// Creates a new empty slice. |
594 | #[inline ] |
595 | pub fn new() -> Self { |
596 | StrV { |
597 | ptr: ptr::NonNull::dangling(), |
598 | len: 0, |
599 | capacity: 0, |
600 | } |
601 | } |
602 | |
603 | // rustdoc-stripper-ignore-next |
604 | /// Creates a new empty slice with the given capacity. |
605 | #[inline ] |
606 | pub fn with_capacity(capacity: usize) -> Self { |
607 | let mut s = Self::new(); |
608 | s.reserve(capacity); |
609 | s |
610 | } |
611 | |
612 | // rustdoc-stripper-ignore-next |
613 | /// Returns the underlying pointer. |
614 | /// |
615 | /// This is guaranteed to be `NULL`-terminated. |
616 | #[inline ] |
617 | pub fn as_ptr(&self) -> *const *mut c_char { |
618 | if self.len == 0 { |
619 | static EMPTY: [usize; 1] = [0]; |
620 | |
621 | EMPTY.as_ptr() as *const _ |
622 | } else { |
623 | self.ptr.as_ptr() |
624 | } |
625 | } |
626 | |
627 | // rustdoc-stripper-ignore-next |
628 | /// Consumes the slice and returns the underlying pointer. |
629 | /// |
630 | /// This is guaranteed to be `NULL`-terminated. |
631 | #[inline ] |
632 | pub fn into_raw(mut self) -> *mut *mut c_char { |
633 | if self.len == 0 { |
634 | ptr::null_mut() |
635 | } else { |
636 | self.len = 0; |
637 | self.capacity = 0; |
638 | self.ptr.as_ptr() |
639 | } |
640 | } |
641 | |
642 | // rustdoc-stripper-ignore-next |
643 | /// Gets the length of the slice. |
644 | #[inline ] |
645 | pub fn len(&self) -> usize { |
646 | self.len |
647 | } |
648 | |
649 | // rustdoc-stripper-ignore-next |
650 | /// Returns `true` if the slice is empty. |
651 | #[inline ] |
652 | pub fn is_empty(&self) -> bool { |
653 | self.len == 0 |
654 | } |
655 | |
656 | // rustdoc-stripper-ignore-next |
657 | /// Returns the capacity of the slice. |
658 | /// |
659 | /// This includes the space that is reserved for the `NULL`-terminator. |
660 | #[inline ] |
661 | pub fn capacity(&self) -> usize { |
662 | self.capacity |
663 | } |
664 | |
665 | // rustdoc-stripper-ignore-next |
666 | /// Sets the length of the slice to `len`. |
667 | /// |
668 | /// # SAFETY |
669 | /// |
670 | /// There must be at least `len` valid items and a `NULL`-terminator after the last item. |
671 | pub unsafe fn set_len(&mut self, len: usize) { |
672 | self.len = len; |
673 | } |
674 | |
675 | // rustdoc-stripper-ignore-next |
676 | /// Reserves at least this much additional capacity. |
677 | #[allow (clippy::int_plus_one)] |
678 | pub fn reserve(&mut self, additional: usize) { |
679 | // Nothing new to reserve as there's still enough space |
680 | if self.len + additional + 1 <= self.capacity { |
681 | return; |
682 | } |
683 | |
684 | let new_capacity = |
685 | usize::next_power_of_two(std::cmp::max(self.len + additional, MIN_SIZE) + 1); |
686 | assert_ne!(new_capacity, 0); |
687 | assert!(new_capacity > self.capacity); |
688 | |
689 | unsafe { |
690 | let ptr = if self.capacity == 0 { |
691 | ptr::null_mut() |
692 | } else { |
693 | self.ptr.as_ptr() as *mut _ |
694 | }; |
695 | let new_ptr = ffi::g_realloc( |
696 | ptr, |
697 | mem::size_of::<*mut c_char>() |
698 | .checked_mul(new_capacity) |
699 | .unwrap(), |
700 | ) as *mut *mut c_char; |
701 | self.ptr = ptr::NonNull::new_unchecked(new_ptr); |
702 | self.capacity = new_capacity; |
703 | } |
704 | } |
705 | |
706 | // rustdoc-stripper-ignore-next |
707 | /// Borrows this slice as a `&[GStringPtr]`. |
708 | #[inline ] |
709 | pub const fn as_slice(&self) -> &[GStringPtr] { |
710 | unsafe { |
711 | if self.len == 0 { |
712 | &[] |
713 | } else { |
714 | std::slice::from_raw_parts(self.ptr.as_ptr() as *const GStringPtr, self.len) |
715 | } |
716 | } |
717 | } |
718 | |
719 | // rustdoc-stripper-ignore-next |
720 | /// Removes all items from the slice. |
721 | #[inline ] |
722 | pub fn clear(&mut self) { |
723 | unsafe { |
724 | for i in 0..self.len { |
725 | ffi::g_free(*self.ptr.as_ptr().add(i) as ffi::gpointer); |
726 | } |
727 | |
728 | self.len = 0; |
729 | } |
730 | } |
731 | |
732 | // rustdoc-stripper-ignore-next |
733 | /// Clones and appends all elements in `slice` to the slice. |
734 | #[inline ] |
735 | pub fn extend_from_slice<S: AsRef<str>>(&mut self, other: &[S]) { |
736 | // Nothing new to reserve as there's still enough space |
737 | if self.len + other.len() + 1 > self.capacity { |
738 | self.reserve(other.len()); |
739 | } |
740 | |
741 | unsafe { |
742 | for item in other { |
743 | *self.ptr.as_ptr().add(self.len) = GString::from(item.as_ref()).into_glib_ptr(); |
744 | self.len += 1; |
745 | } |
746 | |
747 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
748 | } |
749 | } |
750 | |
751 | // rustdoc-stripper-ignore-next |
752 | /// Inserts `item` at position `index` of the slice, shifting all elements after it to the |
753 | /// right. |
754 | #[inline ] |
755 | pub fn insert(&mut self, index: usize, item: GString) { |
756 | assert!(index <= self.len); |
757 | |
758 | // Nothing new to reserve as there's still enough space |
759 | if self.len + 1 + 1 > self.capacity { |
760 | self.reserve(1); |
761 | } |
762 | |
763 | unsafe { |
764 | if index == self.len { |
765 | *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr(); |
766 | } else { |
767 | let p = self.ptr.as_ptr().add(index); |
768 | ptr::copy(p, p.add(1), self.len - index); |
769 | *self.ptr.as_ptr().add(index) = item.into_glib_ptr(); |
770 | } |
771 | |
772 | self.len += 1; |
773 | |
774 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
775 | } |
776 | } |
777 | |
778 | // rustdoc-stripper-ignore-next |
779 | /// Pushes `item` to the end of the slice. |
780 | #[inline ] |
781 | pub fn push(&mut self, item: GString) { |
782 | // Nothing new to reserve as there's still enough space |
783 | if self.len + 1 + 1 > self.capacity { |
784 | self.reserve(1); |
785 | } |
786 | |
787 | unsafe { |
788 | *self.ptr.as_ptr().add(self.len) = item.into_glib_ptr(); |
789 | self.len += 1; |
790 | |
791 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
792 | } |
793 | } |
794 | |
795 | // rustdoc-stripper-ignore-next |
796 | /// Removes item from position `index` of the slice, shifting all elements after it to the |
797 | /// left. |
798 | #[inline ] |
799 | pub fn remove(&mut self, index: usize) -> GString { |
800 | assert!(index < self.len); |
801 | |
802 | unsafe { |
803 | let p = self.ptr.as_ptr().add(index); |
804 | let item = *p; |
805 | ptr::copy(p.add(1), p, self.len - index - 1); |
806 | |
807 | self.len -= 1; |
808 | |
809 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
810 | |
811 | GString::from_glib_full(item) |
812 | } |
813 | } |
814 | |
815 | // rustdoc-stripper-ignore-next |
816 | /// Swaps item from position `index` of the slice and returns it. |
817 | #[inline ] |
818 | pub fn swap(&mut self, index: usize, new_item: GString) -> GString { |
819 | assert!(index < self.len); |
820 | |
821 | unsafe { |
822 | let p = self.ptr.as_ptr().add(index); |
823 | let item = *p; |
824 | *p = new_item.into_glib_ptr(); |
825 | |
826 | GString::from_glib_full(item) |
827 | } |
828 | } |
829 | |
830 | // rustdoc-stripper-ignore-next |
831 | /// Removes the last item of the slice and returns it. |
832 | #[inline ] |
833 | pub fn pop(&mut self) -> Option<GString> { |
834 | if self.len == 0 { |
835 | return None; |
836 | } |
837 | |
838 | unsafe { |
839 | self.len -= 1; |
840 | let p = self.ptr.as_ptr().add(self.len); |
841 | let item = *p; |
842 | |
843 | *self.ptr.as_ptr().add(self.len) = ptr::null_mut(); |
844 | |
845 | Some(GString::from_glib_full(item)) |
846 | } |
847 | } |
848 | |
849 | // rustdoc-stripper-ignore-next |
850 | /// Shortens the slice by keeping the last `len` items. |
851 | /// |
852 | /// If there are fewer than `len` items then this has no effect. |
853 | #[inline ] |
854 | pub fn truncate(&mut self, len: usize) { |
855 | if self.len <= len { |
856 | return; |
857 | } |
858 | |
859 | unsafe { |
860 | while self.len > len { |
861 | self.len -= 1; |
862 | let p = self.ptr.as_ptr().add(self.len); |
863 | ffi::g_free(*p as ffi::gpointer); |
864 | *p = ptr::null_mut(); |
865 | } |
866 | } |
867 | } |
868 | |
869 | // rustdoc-stripper-ignore-next |
870 | /// Joins the strings into a longer string, with an optional separator |
871 | #[inline ] |
872 | #[doc (alias = "g_strjoinv" )] |
873 | pub fn join(&self, separator: Option<impl IntoGStr>) -> GString { |
874 | separator.run_with_gstr(|separator| unsafe { |
875 | from_glib_full(ffi::g_strjoinv( |
876 | separator.to_glib_none().0, |
877 | self.as_ptr() as *mut _, |
878 | )) |
879 | }) |
880 | } |
881 | |
882 | // rustdoc-stripper-ignore-next |
883 | /// Checks whether the `StrV` contains the specified string |
884 | #[inline ] |
885 | #[doc (alias = "g_strv_contains" )] |
886 | pub fn contains(&self, s: impl IntoGStr) -> bool { |
887 | s.run_with_gstr(|s| unsafe { |
888 | from_glib(ffi::g_strv_contains( |
889 | self.as_ptr() as *const _, |
890 | s.to_glib_none().0, |
891 | )) |
892 | }) |
893 | } |
894 | } |
895 | |
896 | impl FromGlibContainer<*mut c_char, *mut *mut c_char> for StrV { |
897 | #[inline ] |
898 | unsafe fn from_glib_none_num(ptr: *mut *mut c_char, num: usize) -> Self { |
899 | Self::from_glib_none_num(ptr as *const *const c_char, len:num, _null_terminated:false) |
900 | } |
901 | |
902 | #[inline ] |
903 | unsafe fn from_glib_container_num(ptr: *mut *mut c_char, num: usize) -> Self { |
904 | Self::from_glib_container_num(ptr as *mut *const c_char, len:num, null_terminated:false) |
905 | } |
906 | |
907 | #[inline ] |
908 | unsafe fn from_glib_full_num(ptr: *mut *mut c_char, num: usize) -> Self { |
909 | Self::from_glib_full_num(ptr, len:num, null_terminated:false) |
910 | } |
911 | } |
912 | |
913 | impl FromGlibContainer<*mut c_char, *const *mut c_char> for StrV { |
914 | unsafe fn from_glib_none_num(ptr: *const *mut c_char, num: usize) -> Self { |
915 | Self::from_glib_none_num(ptr as *const *const c_char, len:num, _null_terminated:false) |
916 | } |
917 | |
918 | unsafe fn from_glib_container_num(_ptr: *const *mut c_char, _num: usize) -> Self { |
919 | unimplemented!(); |
920 | } |
921 | |
922 | unsafe fn from_glib_full_num(_ptr: *const *mut c_char, _num: usize) -> Self { |
923 | unimplemented!(); |
924 | } |
925 | } |
926 | |
927 | impl FromGlibPtrContainer<*mut c_char, *mut *mut c_char> for StrV { |
928 | #[inline ] |
929 | unsafe fn from_glib_none(ptr: *mut *mut c_char) -> Self { |
930 | Self::from_glib_none(ptr as *const *const c_char) |
931 | } |
932 | |
933 | #[inline ] |
934 | unsafe fn from_glib_container(ptr: *mut *mut c_char) -> Self { |
935 | Self::from_glib_container(ptr as *mut *const c_char) |
936 | } |
937 | |
938 | #[inline ] |
939 | unsafe fn from_glib_full(ptr: *mut *mut c_char) -> Self { |
940 | Self::from_glib_full(ptr) |
941 | } |
942 | } |
943 | |
944 | impl FromGlibPtrContainer<*mut c_char, *const *mut c_char> for StrV { |
945 | #[inline ] |
946 | unsafe fn from_glib_none(ptr: *const *mut c_char) -> Self { |
947 | Self::from_glib_none(ptr as *const *const c_char) |
948 | } |
949 | |
950 | unsafe fn from_glib_container(_ptr: *const *mut c_char) -> Self { |
951 | unimplemented!(); |
952 | } |
953 | |
954 | unsafe fn from_glib_full(_ptr: *const *mut c_char) -> Self { |
955 | unimplemented!(); |
956 | } |
957 | } |
958 | |
959 | impl<'a> ToGlibPtr<'a, *mut *mut c_char> for StrV { |
960 | type Storage = PhantomData<&'a Self>; |
961 | |
962 | #[inline ] |
963 | fn to_glib_none(&'a self) -> Stash<'a, *mut *mut c_char, Self> { |
964 | Stash(self.as_ptr() as *mut _, PhantomData) |
965 | } |
966 | |
967 | #[inline ] |
968 | fn to_glib_container(&'a self) -> Stash<'a, *mut *mut c_char, Self> { |
969 | unsafe { |
970 | let ptr: *mut *mut i8 = |
971 | ffi::g_malloc(n_bytes:mem::size_of::<*mut c_char>() * (self.len() + 1)) as *mut *mut c_char; |
972 | ptr::copy_nonoverlapping(self.as_ptr(), dst:ptr, self.len() + 1); |
973 | Stash(ptr, PhantomData) |
974 | } |
975 | } |
976 | |
977 | #[inline ] |
978 | fn to_glib_full(&self) -> *mut *mut c_char { |
979 | self.clone().into_raw() |
980 | } |
981 | } |
982 | |
983 | impl<'a> ToGlibPtr<'a, *const *mut c_char> for StrV { |
984 | type Storage = PhantomData<&'a Self>; |
985 | |
986 | #[inline ] |
987 | fn to_glib_none(&'a self) -> Stash<'a, *const *mut c_char, Self> { |
988 | Stash(self.as_ptr(), PhantomData) |
989 | } |
990 | } |
991 | |
992 | impl IntoGlibPtr<*mut *mut c_char> for StrV { |
993 | #[inline ] |
994 | unsafe fn into_glib_ptr(self) -> *mut *mut c_char { |
995 | self.into_raw() |
996 | } |
997 | } |
998 | |
999 | impl crate::StaticType for StrV { |
1000 | #[inline ] |
1001 | fn static_type() -> crate::Type { |
1002 | <Vec<String>>::static_type() |
1003 | } |
1004 | } |
1005 | |
1006 | impl<'a> crate::StaticType for &'a [GStringPtr] { |
1007 | #[inline ] |
1008 | fn static_type() -> crate::Type { |
1009 | <Vec<String>>::static_type() |
1010 | } |
1011 | } |
1012 | |
1013 | impl crate::value::ValueType for StrV { |
1014 | type Type = Vec<String>; |
1015 | } |
1016 | |
1017 | unsafe impl<'a> crate::value::FromValue<'a> for StrV { |
1018 | type Checker = crate::value::GenericValueTypeChecker<Self>; |
1019 | |
1020 | unsafe fn from_value(value: &'a crate::value::Value) -> Self { |
1021 | let ptr: *mut *mut i8 = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0) as *mut *mut c_char; |
1022 | FromGlibPtrContainer::from_glib_full(ptr) |
1023 | } |
1024 | } |
1025 | |
1026 | unsafe impl<'a> crate::value::FromValue<'a> for &'a [GStringPtr] { |
1027 | type Checker = crate::value::GenericValueTypeChecker<Self>; |
1028 | |
1029 | unsafe fn from_value(value: &'a crate::value::Value) -> Self { |
1030 | let ptr: *const *const i8 = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char; |
1031 | StrV::from_glib_borrow(ptr) |
1032 | } |
1033 | } |
1034 | |
1035 | impl crate::value::ToValue for StrV { |
1036 | fn to_value(&self) -> crate::value::Value { |
1037 | unsafe { |
1038 | let mut value: Value = crate::value::Value::for_value_type::<Self>(); |
1039 | gobject_ffi::g_value_set_boxed( |
1040 | value:value.to_glib_none_mut().0, |
1041 | self.as_ptr() as ffi::gpointer, |
1042 | ); |
1043 | value |
1044 | } |
1045 | } |
1046 | |
1047 | fn value_type(&self) -> crate::Type { |
1048 | <StrV as crate::StaticType>::static_type() |
1049 | } |
1050 | } |
1051 | |
1052 | impl From<StrV> for crate::Value { |
1053 | #[inline ] |
1054 | fn from(s: StrV) -> Self { |
1055 | unsafe { |
1056 | let mut value: Value = crate::value::Value::for_value_type::<StrV>(); |
1057 | gobject_ffi::g_value_take_boxed( |
1058 | value:value.to_glib_none_mut().0, |
1059 | v_boxed:s.into_raw() as ffi::gpointer, |
1060 | ); |
1061 | value |
1062 | } |
1063 | } |
1064 | } |
1065 | |
1066 | // rustdoc-stripper-ignore-next |
1067 | /// A trait to accept both `&[T]` or `StrV` as an argument. |
1068 | pub trait IntoStrV { |
1069 | // rustdoc-stripper-ignore-next |
1070 | /// Runs the given closure with a `NULL`-terminated array. |
1071 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R; |
1072 | } |
1073 | |
1074 | impl IntoStrV for StrV { |
1075 | #[inline ] |
1076 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1077 | <&Self>::run_with_strv(&self, f) |
1078 | } |
1079 | } |
1080 | |
1081 | impl<'a> IntoStrV for &'a StrV { |
1082 | #[inline ] |
1083 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1084 | f(unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) }) |
1085 | } |
1086 | } |
1087 | |
1088 | // rustdoc-stripper-ignore-next |
1089 | /// Maximum number of pointers to stack-allocate before falling back to a heap allocation. |
1090 | /// |
1091 | /// The beginning will be used for the pointers, the remainder for the actual string content. |
1092 | const MAX_STACK_ALLOCATION: usize = 16; |
1093 | |
1094 | impl IntoStrV for Vec<GString> { |
1095 | #[inline ] |
1096 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1097 | self.as_slice().run_with_strv(f) |
1098 | } |
1099 | } |
1100 | |
1101 | impl<'a> IntoStrV for Vec<&'a GString> { |
1102 | #[inline ] |
1103 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1104 | self.as_slice().run_with_strv(f) |
1105 | } |
1106 | } |
1107 | |
1108 | impl<'a> IntoStrV for Vec<&'a GStr> { |
1109 | #[inline ] |
1110 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1111 | self.as_slice().run_with_strv(f) |
1112 | } |
1113 | } |
1114 | |
1115 | impl<'a> IntoStrV for Vec<&'a str> { |
1116 | #[inline ] |
1117 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1118 | self.as_slice().run_with_strv(f) |
1119 | } |
1120 | } |
1121 | |
1122 | impl IntoStrV for Vec<String> { |
1123 | #[inline ] |
1124 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1125 | self.as_slice().run_with_strv(f) |
1126 | } |
1127 | } |
1128 | |
1129 | impl<'a> IntoStrV for Vec<&'a String> { |
1130 | #[inline ] |
1131 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1132 | self.as_slice().run_with_strv(f) |
1133 | } |
1134 | } |
1135 | |
1136 | impl IntoStrV for &[GString] { |
1137 | #[inline ] |
1138 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1139 | let required_len: usize = (self.len() + 1) * mem::size_of::<*mut c_char>(); |
1140 | |
1141 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1142 | unsafe { |
1143 | let mut s: MaybeUninit<[*mut i8; 16]> = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1144 | let ptrs: *mut *mut i8 = s.as_mut_ptr() as *mut *mut c_char; |
1145 | |
1146 | for (i: usize, item: &GString) in self.iter().enumerate() { |
1147 | *ptrs.add(count:i) = item.as_ptr() as *mut _; |
1148 | } |
1149 | *ptrs.add(self.len()) = ptr::null_mut(); |
1150 | |
1151 | f(std::slice::from_raw_parts(data:ptrs, self.len())) |
1152 | } |
1153 | } else { |
1154 | let mut s: StrV = StrV::with_capacity(self.len()); |
1155 | s.extend_from_slice(self); |
1156 | s.run_with_strv(f) |
1157 | } |
1158 | } |
1159 | } |
1160 | |
1161 | impl<'a> IntoStrV for &[&'a GString] { |
1162 | #[inline ] |
1163 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1164 | let required_len: usize = (self.len() + 1) * mem::size_of::<*mut c_char>(); |
1165 | |
1166 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1167 | unsafe { |
1168 | let mut s: MaybeUninit<[*mut i8; 16]> = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1169 | let ptrs: *mut *mut i8 = s.as_mut_ptr() as *mut *mut c_char; |
1170 | |
1171 | for (i: usize, item: &&GString) in self.iter().enumerate() { |
1172 | *ptrs.add(count:i) = item.as_ptr() as *mut _; |
1173 | } |
1174 | *ptrs.add(self.len()) = ptr::null_mut(); |
1175 | |
1176 | f(std::slice::from_raw_parts(data:ptrs, self.len())) |
1177 | } |
1178 | } else { |
1179 | let mut s: StrV = StrV::with_capacity(self.len()); |
1180 | s.extend_from_slice(self); |
1181 | s.run_with_strv(f) |
1182 | } |
1183 | } |
1184 | } |
1185 | |
1186 | impl<'a> IntoStrV for &[&'a GStr] { |
1187 | #[inline ] |
1188 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1189 | let required_len: usize = (self.len() + 1) * mem::size_of::<*mut c_char>(); |
1190 | |
1191 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1192 | unsafe { |
1193 | let mut s: MaybeUninit<[*mut i8; 16]> = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1194 | let ptrs: *mut *mut i8 = s.as_mut_ptr() as *mut *mut c_char; |
1195 | |
1196 | for (i: usize, item: &&GStr) in self.iter().enumerate() { |
1197 | *ptrs.add(count:i) = item.as_ptr() as *mut _; |
1198 | } |
1199 | *ptrs.add(self.len()) = ptr::null_mut(); |
1200 | |
1201 | f(std::slice::from_raw_parts(data:ptrs, self.len())) |
1202 | } |
1203 | } else { |
1204 | let mut s: StrV = StrV::with_capacity(self.len()); |
1205 | s.extend_from_slice(self); |
1206 | s.run_with_strv(f) |
1207 | } |
1208 | } |
1209 | } |
1210 | |
1211 | impl<'a> IntoStrV for &[&'a str] { |
1212 | #[inline ] |
1213 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1214 | let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>() |
1215 | + self.iter().map(|s| s.len() + 1).sum::<usize>(); |
1216 | |
1217 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1218 | unsafe { |
1219 | let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1220 | let ptrs = s.as_mut_ptr() as *mut *mut c_char; |
1221 | let mut strs = ptrs.add(self.len() + 1) as *mut c_char; |
1222 | |
1223 | for (i, item) in self.iter().enumerate() { |
1224 | ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len()); |
1225 | *strs.add(item.len()) = 0; |
1226 | *ptrs.add(i) = strs; |
1227 | strs = strs.add(item.len() + 1); |
1228 | } |
1229 | *ptrs.add(self.len()) = ptr::null_mut(); |
1230 | |
1231 | f(std::slice::from_raw_parts(ptrs, self.len())) |
1232 | } |
1233 | } else { |
1234 | let mut s = StrV::with_capacity(self.len()); |
1235 | s.extend_from_slice(self); |
1236 | s.run_with_strv(f) |
1237 | } |
1238 | } |
1239 | } |
1240 | |
1241 | impl IntoStrV for &[String] { |
1242 | #[inline ] |
1243 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1244 | let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>() |
1245 | + self.iter().map(|s| s.len() + 1).sum::<usize>(); |
1246 | |
1247 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1248 | unsafe { |
1249 | let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1250 | let ptrs = s.as_mut_ptr() as *mut *mut c_char; |
1251 | let mut strs = ptrs.add(self.len() + 1) as *mut c_char; |
1252 | |
1253 | for (i, item) in self.iter().enumerate() { |
1254 | ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len()); |
1255 | *strs.add(item.len()) = 0; |
1256 | *ptrs.add(i) = strs; |
1257 | strs = strs.add(item.len() + 1); |
1258 | } |
1259 | *ptrs.add(self.len()) = ptr::null_mut(); |
1260 | |
1261 | f(std::slice::from_raw_parts(ptrs, self.len())) |
1262 | } |
1263 | } else { |
1264 | let mut s = StrV::with_capacity(self.len()); |
1265 | s.extend_from_slice(self); |
1266 | s.run_with_strv(f) |
1267 | } |
1268 | } |
1269 | } |
1270 | |
1271 | impl<'a> IntoStrV for &[&'a String] { |
1272 | #[inline ] |
1273 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1274 | let required_len = (self.len() + 1) * mem::size_of::<*mut c_char>() |
1275 | + self.iter().map(|s| s.len() + 1).sum::<usize>(); |
1276 | |
1277 | if required_len < MAX_STACK_ALLOCATION * mem::size_of::<*mut c_char>() { |
1278 | unsafe { |
1279 | let mut s = mem::MaybeUninit::<[*mut c_char; MAX_STACK_ALLOCATION]>::uninit(); |
1280 | let ptrs = s.as_mut_ptr() as *mut *mut c_char; |
1281 | let mut strs = ptrs.add(self.len() + 1) as *mut c_char; |
1282 | |
1283 | for (i, item) in self.iter().enumerate() { |
1284 | ptr::copy_nonoverlapping(item.as_ptr() as *const _, strs, item.len()); |
1285 | *strs.add(item.len()) = 0; |
1286 | *ptrs.add(i) = strs; |
1287 | strs = strs.add(item.len() + 1); |
1288 | } |
1289 | *ptrs.add(self.len()) = ptr::null_mut(); |
1290 | |
1291 | f(std::slice::from_raw_parts(ptrs, self.len())) |
1292 | } |
1293 | } else { |
1294 | let mut s = StrV::with_capacity(self.len()); |
1295 | s.extend_from_slice(self); |
1296 | s.run_with_strv(f) |
1297 | } |
1298 | } |
1299 | } |
1300 | |
1301 | impl<const N: usize> IntoStrV for [GString; N] { |
1302 | #[inline ] |
1303 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1304 | self.as_slice().run_with_strv(f) |
1305 | } |
1306 | } |
1307 | |
1308 | impl<'a, const N: usize> IntoStrV for [&'a GString; N] { |
1309 | #[inline ] |
1310 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1311 | self.as_slice().run_with_strv(f) |
1312 | } |
1313 | } |
1314 | |
1315 | impl<'a, const N: usize> IntoStrV for [&'a GStr; N] { |
1316 | #[inline ] |
1317 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1318 | self.as_slice().run_with_strv(f) |
1319 | } |
1320 | } |
1321 | |
1322 | impl<'a, const N: usize> IntoStrV for [&'a str; N] { |
1323 | #[inline ] |
1324 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1325 | self.as_slice().run_with_strv(f) |
1326 | } |
1327 | } |
1328 | |
1329 | impl<const N: usize> IntoStrV for [String; N] { |
1330 | #[inline ] |
1331 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1332 | self.as_slice().run_with_strv(f) |
1333 | } |
1334 | } |
1335 | |
1336 | impl<'a, const N: usize> IntoStrV for [&'a String; N] { |
1337 | #[inline ] |
1338 | fn run_with_strv<R, F: FnOnce(&[*mut c_char]) -> R>(self, f: F) -> R { |
1339 | self.as_slice().run_with_strv(f) |
1340 | } |
1341 | } |
1342 | |
1343 | #[cfg (test)] |
1344 | mod test { |
1345 | use super::*; |
1346 | |
1347 | #[test ] |
1348 | fn test_from_glib_full() { |
1349 | let items = ["str1" , "str2" , "str3" , "str4" ]; |
1350 | |
1351 | let slice = unsafe { |
1352 | let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *mut c_char; |
1353 | *ptr.add(0) = items[0].to_glib_full(); |
1354 | *ptr.add(1) = items[1].to_glib_full(); |
1355 | *ptr.add(2) = items[2].to_glib_full(); |
1356 | *ptr.add(3) = items[3].to_glib_full(); |
1357 | |
1358 | StrV::from_glib_full_num(ptr, 4, false) |
1359 | }; |
1360 | |
1361 | for (a, b) in Iterator::zip(items.iter(), slice.iter()) { |
1362 | assert_eq!(a, b); |
1363 | } |
1364 | } |
1365 | |
1366 | #[test ] |
1367 | fn test_from_glib_container() { |
1368 | let items = [ |
1369 | crate::gstr!("str1" ), |
1370 | crate::gstr!("str2" ), |
1371 | crate::gstr!("str3" ), |
1372 | crate::gstr!("str4" ), |
1373 | ]; |
1374 | |
1375 | let slice = unsafe { |
1376 | let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char; |
1377 | *ptr.add(0) = items[0].as_ptr(); |
1378 | *ptr.add(1) = items[1].as_ptr(); |
1379 | *ptr.add(2) = items[2].as_ptr(); |
1380 | *ptr.add(3) = items[3].as_ptr(); |
1381 | |
1382 | StrV::from_glib_container_num(ptr, 4, false) |
1383 | }; |
1384 | |
1385 | for (a, b) in Iterator::zip(items.iter(), slice.iter()) { |
1386 | assert_eq!(a, b); |
1387 | } |
1388 | } |
1389 | |
1390 | #[test ] |
1391 | fn test_from_glib_none() { |
1392 | let items = [ |
1393 | crate::gstr!("str1" ), |
1394 | crate::gstr!("str2" ), |
1395 | crate::gstr!("str3" ), |
1396 | crate::gstr!("str4" ), |
1397 | ]; |
1398 | |
1399 | let slice = unsafe { |
1400 | let ptr = ffi::g_malloc(mem::size_of::<*mut c_char>() * 4) as *mut *const c_char; |
1401 | *ptr.add(0) = items[0].as_ptr(); |
1402 | *ptr.add(1) = items[1].as_ptr(); |
1403 | *ptr.add(2) = items[2].as_ptr(); |
1404 | *ptr.add(3) = items[3].as_ptr(); |
1405 | |
1406 | let res = StrV::from_glib_none_num(ptr, 4, false); |
1407 | ffi::g_free(ptr as ffi::gpointer); |
1408 | res |
1409 | }; |
1410 | |
1411 | for (a, b) in Iterator::zip(items.iter(), slice.iter()) { |
1412 | assert_eq!(a, b); |
1413 | } |
1414 | } |
1415 | |
1416 | #[test ] |
1417 | fn test_from_slice() { |
1418 | let items = [ |
1419 | crate::gstr!("str1" ), |
1420 | crate::gstr!("str2" ), |
1421 | crate::gstr!("str3" ), |
1422 | ]; |
1423 | |
1424 | let slice1 = StrV::from(&items[..]); |
1425 | let slice2 = StrV::from(items); |
1426 | assert_eq!(slice1.len(), 3); |
1427 | assert_eq!(slice1, slice2); |
1428 | } |
1429 | |
1430 | #[test ] |
1431 | fn test_safe_api() { |
1432 | let items = [ |
1433 | crate::gstr!("str1" ), |
1434 | crate::gstr!("str2" ), |
1435 | crate::gstr!("str3" ), |
1436 | ]; |
1437 | |
1438 | let mut slice = StrV::from(&items[..]); |
1439 | assert_eq!(slice.len(), 3); |
1440 | slice.push(GString::from("str4" )); |
1441 | assert_eq!(slice.len(), 4); |
1442 | |
1443 | for (a, b) in Iterator::zip(items.iter(), slice.iter()) { |
1444 | assert_eq!(a, b); |
1445 | } |
1446 | assert_eq!(slice[3], "str4" ); |
1447 | |
1448 | let vec = Vec::from(slice); |
1449 | assert_eq!(vec.len(), 4); |
1450 | for (a, b) in Iterator::zip(items.iter(), vec.iter()) { |
1451 | assert_eq!(a, b); |
1452 | } |
1453 | assert_eq!(vec[3], "str4" ); |
1454 | |
1455 | let mut slice = StrV::from(vec); |
1456 | assert_eq!(slice.len(), 4); |
1457 | let e = slice.pop().unwrap(); |
1458 | assert_eq!(e, "str4" ); |
1459 | assert_eq!(slice.len(), 3); |
1460 | slice.insert(2, e); |
1461 | assert_eq!(slice.len(), 4); |
1462 | assert_eq!(slice[0], "str1" ); |
1463 | assert_eq!(slice[1], "str2" ); |
1464 | assert_eq!(slice[2], "str4" ); |
1465 | assert_eq!(slice[3], "str3" ); |
1466 | let e = slice.remove(2); |
1467 | assert_eq!(e, "str4" ); |
1468 | assert_eq!(slice.len(), 3); |
1469 | slice.push(e); |
1470 | assert_eq!(slice.len(), 4); |
1471 | |
1472 | for (a, b) in Iterator::zip(items.iter(), slice.into_iter()) { |
1473 | assert_eq!(*a, b); |
1474 | } |
1475 | } |
1476 | |
1477 | #[test ] |
1478 | fn test_into_strv() { |
1479 | let items = ["str1" , "str2" , "str3" , "str4" ]; |
1480 | |
1481 | items[..].run_with_strv(|s| unsafe { |
1482 | assert!(s.get_unchecked(4).is_null()); |
1483 | assert_eq!(s.len(), items.len()); |
1484 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1485 | assert_eq!(s, items); |
1486 | }); |
1487 | |
1488 | Vec::from(&items[..]).run_with_strv(|s| unsafe { |
1489 | assert!(s.get_unchecked(4).is_null()); |
1490 | assert_eq!(s.len(), items.len()); |
1491 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1492 | assert_eq!(s, items); |
1493 | }); |
1494 | |
1495 | StrV::from(&items[..]).run_with_strv(|s| unsafe { |
1496 | assert!(s.get_unchecked(4).is_null()); |
1497 | assert_eq!(s.len(), items.len()); |
1498 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1499 | assert_eq!(s, items); |
1500 | }); |
1501 | |
1502 | let v = items.iter().copied().map(String::from).collect::<Vec<_>>(); |
1503 | items.run_with_strv(|s| unsafe { |
1504 | assert!(s.get_unchecked(4).is_null()); |
1505 | assert_eq!(s.len(), v.len()); |
1506 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1507 | assert_eq!(s, items); |
1508 | }); |
1509 | |
1510 | let v = items.iter().copied().map(GString::from).collect::<Vec<_>>(); |
1511 | items.run_with_strv(|s| unsafe { |
1512 | assert!(s.get_unchecked(4).is_null()); |
1513 | assert_eq!(s.len(), v.len()); |
1514 | let s = StrV::from_glib_borrow(s.as_ptr() as *const *const c_char); |
1515 | assert_eq!(s, items); |
1516 | }); |
1517 | } |
1518 | |
1519 | #[test ] |
1520 | fn test_join() { |
1521 | let items = [ |
1522 | crate::gstr!("str1" ), |
1523 | crate::gstr!("str2" ), |
1524 | crate::gstr!("str3" ), |
1525 | ]; |
1526 | |
1527 | let strv = StrV::from(&items[..]); |
1528 | assert_eq!(strv.join(None::<&str>), "str1str2str3" ); |
1529 | assert_eq!(strv.join(Some("," )), "str1,str2,str3" ); |
1530 | } |
1531 | |
1532 | #[test ] |
1533 | fn test_contains() { |
1534 | let items = [ |
1535 | crate::gstr!("str1" ), |
1536 | crate::gstr!("str2" ), |
1537 | crate::gstr!("str3" ), |
1538 | ]; |
1539 | |
1540 | let strv = StrV::from(&items[..]); |
1541 | assert!(strv.contains("str2" )); |
1542 | assert!(!strv.contains("str4" )); |
1543 | } |
1544 | } |
1545 | |