• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 19903754435

03 Dec 2025 05:59PM UTC coverage: 59.12% (+0.1%) from 59.013%
19903754435

push

github

fasterthanlime
fix: use strict provenance APIs in facet-value

For inline values (null, booleans, short strings), we pack data directly
into pointer bits - there's no actual memory being pointed to. Previously
we used `bits as *mut u8` which is an integer-to-pointer cast that
violates strict provenance.

Now we use:
- `ptr::without_provenance_mut()` for creating fake pointers from integers
- `.addr()` instead of `as usize` for getting the address

This makes miri happy with `-Zmiri-strict-provenance`.

4 of 7 new or added lines in 1 file covered. (57.14%)

420 existing lines in 9 files now uncovered.

20944 of 35426 relevant lines covered (59.12%)

537.13 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.28
/facet-value/src/array.rs
1
//! Array value type.
2

3
#[cfg(feature = "alloc")]
4
use alloc::alloc::{Layout, alloc, dealloc, realloc};
5
#[cfg(feature = "alloc")]
6
use alloc::vec::Vec;
7
use core::borrow::{Borrow, BorrowMut};
8
use core::cmp::Ordering;
9
use core::fmt::{self, Debug, Formatter};
10
use core::hash::{Hash, Hasher};
11
use core::iter::FromIterator;
12
use core::ops::{Deref, DerefMut, Index, IndexMut};
13
use core::slice::SliceIndex;
14
use core::{cmp, ptr};
15

16
use crate::value::{TypeTag, Value};
17

18
/// Header for heap-allocated arrays.
19
#[repr(C, align(8))]
20
struct ArrayHeader {
21
    /// Number of elements
22
    len: usize,
23
    /// Capacity (number of elements that can be stored)
24
    cap: usize,
25
    // Array of Value follows immediately after
26
}
27

28
/// An array value.
29
///
30
/// `VArray` is a dynamic array of `Value`s, similar to `Vec<Value>`.
31
/// The length and capacity are stored in a heap-allocated header.
32
#[repr(transparent)]
33
#[derive(Clone)]
34
pub struct VArray(pub(crate) Value);
35

36
impl VArray {
37
    fn layout(cap: usize) -> Layout {
332✔
38
        Layout::new::<ArrayHeader>()
332✔
39
            .extend(Layout::array::<Value>(cap).unwrap())
332✔
40
            .unwrap()
332✔
41
            .0
332✔
42
            .pad_to_align()
332✔
43
    }
332✔
44

45
    #[cfg(feature = "alloc")]
46
    fn alloc(cap: usize) -> *mut ArrayHeader {
86✔
47
        unsafe {
48
            let layout = Self::layout(cap);
86✔
49
            let ptr = alloc(layout).cast::<ArrayHeader>();
86✔
50
            (*ptr).len = 0;
86✔
51
            (*ptr).cap = cap;
86✔
52
            ptr
86✔
53
        }
54
    }
86✔
55

56
    #[cfg(feature = "alloc")]
57
    fn realloc_ptr(ptr: *mut ArrayHeader, new_cap: usize) -> *mut ArrayHeader {
80✔
58
        unsafe {
59
            let old_cap = (*ptr).cap;
80✔
60
            let old_layout = Self::layout(old_cap);
80✔
61
            let new_layout = Self::layout(new_cap);
80✔
62
            let new_ptr =
80✔
63
                realloc(ptr.cast::<u8>(), old_layout, new_layout.size()).cast::<ArrayHeader>();
80✔
64
            (*new_ptr).cap = new_cap;
80✔
65
            new_ptr
80✔
66
        }
67
    }
80✔
68

69
    #[cfg(feature = "alloc")]
70
    fn dealloc_ptr(ptr: *mut ArrayHeader) {
86✔
71
        unsafe {
86✔
72
            let cap = (*ptr).cap;
86✔
73
            let layout = Self::layout(cap);
86✔
74
            dealloc(ptr.cast::<u8>(), layout);
86✔
75
        }
86✔
76
    }
86✔
77

78
    fn header(&self) -> &ArrayHeader {
1,420✔
79
        unsafe { &*(self.0.heap_ptr() as *const ArrayHeader) }
1,420✔
80
    }
1,420✔
81

82
    fn header_mut(&mut self) -> &mut ArrayHeader {
488✔
83
        unsafe { &mut *(self.0.heap_ptr_mut() as *mut ArrayHeader) }
488✔
84
    }
488✔
85

86
    fn items_ptr(&self) -> *const Value {
255✔
87
        // Go through heap_ptr directly to avoid creating intermediate reference
88
        // that would limit provenance to just the header
89
        unsafe { (self.0.heap_ptr() as *const ArrayHeader).add(1).cast() }
255✔
90
    }
255✔
91

92
    fn items_ptr_mut(&mut self) -> *mut Value {
488✔
93
        // Use heap_ptr_mut directly to preserve mutable provenance
94
        unsafe { (self.0.heap_ptr_mut() as *mut ArrayHeader).add(1).cast() }
488✔
95
    }
488✔
96

97
    /// Creates a new empty array.
98
    #[cfg(feature = "alloc")]
99
    #[must_use]
100
    pub fn new() -> Self {
80✔
101
        Self::with_capacity(0)
80✔
102
    }
80✔
103

104
    /// Creates a new array with the specified capacity.
105
    #[cfg(feature = "alloc")]
106
    #[must_use]
107
    pub fn with_capacity(cap: usize) -> Self {
86✔
108
        unsafe {
109
            let ptr = Self::alloc(cap);
86✔
110
            VArray(Value::new_ptr(ptr.cast(), TypeTag::ArrayOrTrue))
86✔
111
        }
112
    }
86✔
113

114
    /// Returns the number of elements.
115
    #[must_use]
116
    pub fn len(&self) -> usize {
933✔
117
        self.header().len
933✔
118
    }
933✔
119

120
    /// Returns `true` if the array is empty.
121
    #[must_use]
122
    pub fn is_empty(&self) -> bool {
6✔
123
        self.len() == 0
6✔
124
    }
6✔
125

126
    /// Returns the capacity.
127
    #[must_use]
128
    pub fn capacity(&self) -> usize {
244✔
129
        self.header().cap
244✔
130
    }
244✔
131

132
    /// Returns a slice of the elements.
133
    #[must_use]
134
    pub fn as_slice(&self) -> &[Value] {
255✔
135
        unsafe { core::slice::from_raw_parts(self.items_ptr(), self.len()) }
255✔
136
    }
255✔
137

138
    /// Returns a mutable slice of the elements.
139
    pub fn as_mut_slice(&mut self) -> &mut [Value] {
×
140
        unsafe { core::slice::from_raw_parts_mut(self.items_ptr_mut(), self.len()) }
×
141
    }
×
142

143
    /// Reserves capacity for at least `additional` more elements.
144
    #[cfg(feature = "alloc")]
145
    pub fn reserve(&mut self, additional: usize) {
244✔
146
        let current_cap = self.capacity();
244✔
147
        let desired_cap = self
244✔
148
            .len()
244✔
149
            .checked_add(additional)
244✔
150
            .expect("capacity overflow");
244✔
151

152
        if current_cap >= desired_cap {
244✔
153
            return;
164✔
154
        }
80✔
155

156
        let new_cap = cmp::max(current_cap * 2, desired_cap.max(4));
80✔
157

158
        unsafe {
80✔
159
            let new_ptr = Self::realloc_ptr(self.0.heap_ptr_mut().cast(), new_cap);
80✔
160
            self.0.set_ptr(new_ptr.cast());
80✔
161
        }
80✔
162
    }
244✔
163

164
    /// Pushes an element onto the back.
165
    #[cfg(feature = "alloc")]
166
    pub fn push(&mut self, value: impl Into<Value>) {
243✔
167
        self.reserve(1);
243✔
168
        unsafe {
243✔
169
            let len = self.header().len;
243✔
170
            let ptr = self.items_ptr_mut().add(len);
243✔
171
            ptr.write(value.into());
243✔
172
            self.header_mut().len = len + 1;
243✔
173
        }
243✔
174
    }
243✔
175

176
    /// Pops an element from the back.
177
    pub fn pop(&mut self) -> Option<Value> {
330✔
178
        let len = self.len();
330✔
179
        if len == 0 {
330✔
180
            return None;
87✔
181
        }
243✔
182
        unsafe {
183
            self.header_mut().len = len - 1;
243✔
184
            let ptr = self.items_ptr_mut().add(len - 1);
243✔
185
            Some(ptr.read())
243✔
186
        }
187
    }
330✔
188

189
    /// Inserts an element at the specified index.
190
    #[cfg(feature = "alloc")]
191
    pub fn insert(&mut self, index: usize, value: impl Into<Value>) {
1✔
192
        let len = self.len();
1✔
193
        assert!(index <= len, "index out of bounds");
1✔
194

195
        self.reserve(1);
1✔
196

197
        unsafe {
198
            let ptr = self.items_ptr_mut().add(index);
1✔
199
            // Shift elements to the right
200
            if index < len {
1✔
201
                ptr::copy(ptr, ptr.add(1), len - index);
1✔
202
            }
1✔
203
            ptr.write(value.into());
1✔
204
            self.header_mut().len = len + 1;
1✔
205
        }
206
    }
1✔
207

208
    /// Removes and returns the element at the specified index.
209
    pub fn remove(&mut self, index: usize) -> Option<Value> {
1✔
210
        let len = self.len();
1✔
211
        if index >= len {
1✔
212
            return None;
×
213
        }
1✔
214

215
        unsafe {
216
            let ptr = self.items_ptr_mut().add(index);
1✔
217
            let value = ptr.read();
1✔
218
            // Shift elements to the left
219
            if index < len - 1 {
1✔
220
                ptr::copy(ptr.add(1), ptr, len - index - 1);
1✔
221
            }
1✔
222
            self.header_mut().len = len - 1;
1✔
223
            Some(value)
1✔
224
        }
225
    }
1✔
226

227
    /// Removes an element by swapping it with the last element.
228
    /// More efficient than `remove` but doesn't preserve order.
229
    pub fn swap_remove(&mut self, index: usize) -> Option<Value> {
×
230
        let len = self.len();
×
231
        if index >= len {
×
232
            return None;
×
233
        }
×
234

235
        self.as_mut_slice().swap(index, len - 1);
×
236
        self.pop()
×
237
    }
×
238

239
    /// Clears the array.
240
    pub fn clear(&mut self) {
86✔
241
        while self.pop().is_some() {}
326✔
242
    }
86✔
243

244
    /// Truncates the array to the specified length.
245
    pub fn truncate(&mut self, len: usize) {
×
246
        while self.len() > len {
×
247
            self.pop();
×
248
        }
×
249
    }
×
250

251
    /// Gets an element by index.
252
    #[must_use]
253
    pub fn get(&self, index: usize) -> Option<&Value> {
186✔
254
        self.as_slice().get(index)
186✔
255
    }
186✔
256

257
    /// Gets a mutable element by index.
258
    pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> {
×
259
        self.as_mut_slice().get_mut(index)
×
260
    }
×
261

262
    /// Shrinks the capacity to match the length.
263
    #[cfg(feature = "alloc")]
264
    pub fn shrink_to_fit(&mut self) {
×
265
        let len = self.len();
×
266
        let cap = self.capacity();
×
267

268
        if len < cap {
×
269
            unsafe {
×
270
                let new_ptr = Self::realloc_ptr(self.0.heap_ptr_mut().cast(), len);
×
271
                self.0.set_ptr(new_ptr.cast());
×
272
            }
×
273
        }
×
274
    }
×
275

276
    pub(crate) fn clone_impl(&self) -> Value {
4✔
277
        let mut new = VArray::with_capacity(self.len());
4✔
278
        for v in self.as_slice() {
15✔
279
            new.push(v.clone());
15✔
280
        }
15✔
281
        new.0
4✔
282
    }
4✔
283

284
    pub(crate) fn drop_impl(&mut self) {
86✔
285
        self.clear();
86✔
286
        unsafe {
86✔
287
            Self::dealloc_ptr(self.0.heap_ptr_mut().cast());
86✔
288
        }
86✔
289
    }
86✔
290
}
291

292
// === Iterator ===
293

294
/// Iterator over owned `Value`s from a `VArray`.
295
pub struct ArrayIntoIter {
296
    array: VArray,
297
}
298

299
impl Iterator for ArrayIntoIter {
300
    type Item = Value;
301

302
    fn next(&mut self) -> Option<Self::Item> {
×
303
        if self.array.is_empty() {
×
304
            None
×
305
        } else {
306
            self.array.remove(0)
×
307
        }
308
    }
×
309

310
    fn size_hint(&self) -> (usize, Option<usize>) {
×
311
        let len = self.array.len();
×
312
        (len, Some(len))
×
313
    }
×
314
}
315

316
impl ExactSizeIterator for ArrayIntoIter {}
317

318
impl IntoIterator for VArray {
319
    type Item = Value;
320
    type IntoIter = ArrayIntoIter;
321

322
    fn into_iter(self) -> Self::IntoIter {
×
323
        ArrayIntoIter { array: self }
×
324
    }
×
325
}
326

327
impl<'a> IntoIterator for &'a VArray {
328
    type Item = &'a Value;
329
    type IntoIter = core::slice::Iter<'a, Value>;
330

331
    fn into_iter(self) -> Self::IntoIter {
×
332
        self.as_slice().iter()
×
333
    }
×
334
}
335

336
impl<'a> IntoIterator for &'a mut VArray {
337
    type Item = &'a mut Value;
338
    type IntoIter = core::slice::IterMut<'a, Value>;
339

340
    fn into_iter(self) -> Self::IntoIter {
×
341
        self.as_mut_slice().iter_mut()
×
342
    }
×
343
}
344

345
// === Deref ===
346

347
impl Deref for VArray {
348
    type Target = [Value];
349

350
    fn deref(&self) -> &[Value] {
12✔
351
        self.as_slice()
12✔
352
    }
12✔
353
}
354

355
impl DerefMut for VArray {
356
    fn deref_mut(&mut self) -> &mut [Value] {
×
357
        self.as_mut_slice()
×
358
    }
×
359
}
360

361
impl Borrow<[Value]> for VArray {
362
    fn borrow(&self) -> &[Value] {
×
363
        self.as_slice()
×
364
    }
×
365
}
366

367
impl BorrowMut<[Value]> for VArray {
368
    fn borrow_mut(&mut self) -> &mut [Value] {
×
369
        self.as_mut_slice()
×
370
    }
×
371
}
372

373
impl AsRef<[Value]> for VArray {
374
    fn as_ref(&self) -> &[Value] {
×
375
        self.as_slice()
×
376
    }
×
377
}
378

379
// === Index ===
380

381
impl<I: SliceIndex<[Value]>> Index<I> for VArray {
382
    type Output = I::Output;
383

384
    fn index(&self, index: I) -> &Self::Output {
25✔
385
        &self.as_slice()[index]
25✔
386
    }
25✔
387
}
388

389
impl<I: SliceIndex<[Value]>> IndexMut<I> for VArray {
390
    fn index_mut(&mut self, index: I) -> &mut Self::Output {
×
391
        &mut self.as_mut_slice()[index]
×
392
    }
×
393
}
394

395
// === Comparison ===
396

397
impl PartialEq for VArray {
398
    fn eq(&self, other: &Self) -> bool {
14✔
399
        self.as_slice() == other.as_slice()
14✔
400
    }
14✔
401
}
402

403
impl Eq for VArray {}
404

405
impl PartialOrd for VArray {
406
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
×
407
        // Element-wise comparison
408
        let mut iter1 = self.iter();
×
409
        let mut iter2 = other.iter();
×
410
        loop {
411
            match (iter1.next(), iter2.next()) {
×
412
                (Some(a), Some(b)) => match a.partial_cmp(b) {
×
413
                    Some(Ordering::Equal) => continue,
×
414
                    other => return other,
×
415
                },
416
                (None, None) => return Some(Ordering::Equal),
×
417
                (Some(_), None) => return Some(Ordering::Greater),
×
418
                (None, Some(_)) => return Some(Ordering::Less),
×
419
            }
420
        }
421
    }
×
422
}
423

424
impl Hash for VArray {
425
    fn hash<H: Hasher>(&self, state: &mut H) {
×
426
        self.as_slice().hash(state);
×
427
    }
×
428
}
429

430
impl Debug for VArray {
431
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
×
432
        Debug::fmt(self.as_slice(), f)
×
433
    }
×
434
}
435

436
impl Default for VArray {
437
    fn default() -> Self {
×
438
        Self::new()
×
439
    }
×
440
}
441

442
// === FromIterator / Extend ===
443

444
#[cfg(feature = "alloc")]
445
impl<T: Into<Value>> FromIterator<T> for VArray {
446
    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
2✔
447
        let iter = iter.into_iter();
2✔
448
        let (lower, _) = iter.size_hint();
2✔
449
        let mut array = VArray::with_capacity(lower);
2✔
450
        for v in iter {
6✔
451
            array.push(v);
6✔
452
        }
6✔
453
        array
2✔
454
    }
2✔
455
}
456

457
#[cfg(feature = "alloc")]
458
impl<T: Into<Value>> Extend<T> for VArray {
459
    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
×
460
        let iter = iter.into_iter();
×
461
        let (lower, _) = iter.size_hint();
×
462
        self.reserve(lower);
×
463
        for v in iter {
×
464
            self.push(v);
×
465
        }
×
466
    }
×
467
}
468

469
// === From implementations ===
470

471
#[cfg(feature = "alloc")]
472
impl<T: Into<Value>> From<Vec<T>> for VArray {
473
    fn from(vec: Vec<T>) -> Self {
×
474
        vec.into_iter().collect()
×
475
    }
×
476
}
477

478
#[cfg(feature = "alloc")]
479
impl<T: Into<Value> + Clone> From<&[T]> for VArray {
480
    fn from(slice: &[T]) -> Self {
×
481
        slice.iter().cloned().collect()
×
482
    }
×
483
}
484

485
// === Value conversions ===
486

487
impl AsRef<Value> for VArray {
488
    fn as_ref(&self) -> &Value {
×
489
        &self.0
×
490
    }
×
491
}
492

493
impl AsMut<Value> for VArray {
494
    fn as_mut(&mut self) -> &mut Value {
×
495
        &mut self.0
×
496
    }
×
497
}
498

499
impl From<VArray> for Value {
500
    fn from(arr: VArray) -> Self {
65✔
501
        arr.0
65✔
502
    }
65✔
503
}
504

505
impl VArray {
506
    /// Converts this VArray into a Value, consuming self.
507
    #[inline]
508
    pub fn into_value(self) -> Value {
10✔
509
        self.0
10✔
510
    }
10✔
511
}
512

513
#[cfg(test)]
514
mod tests {
515
    use super::*;
516
    use crate::ValueType;
517

518
    #[test]
519
    fn test_new() {
1✔
520
        let arr = VArray::new();
1✔
521
        assert!(arr.is_empty());
1✔
522
        assert_eq!(arr.len(), 0);
1✔
523
    }
1✔
524

525
    #[test]
526
    fn test_push_pop() {
1✔
527
        let mut arr = VArray::new();
1✔
528
        arr.push(Value::from(1));
1✔
529
        arr.push(Value::from(2));
1✔
530
        arr.push(Value::from(3));
1✔
531

532
        assert_eq!(arr.len(), 3);
1✔
533
        assert_eq!(arr.pop().unwrap().as_number().unwrap().to_i64(), Some(3));
1✔
534
        assert_eq!(arr.pop().unwrap().as_number().unwrap().to_i64(), Some(2));
1✔
535
        assert_eq!(arr.pop().unwrap().as_number().unwrap().to_i64(), Some(1));
1✔
536
        assert!(arr.pop().is_none());
1✔
537
    }
1✔
538

539
    #[test]
540
    fn test_insert_remove() {
1✔
541
        let mut arr = VArray::new();
1✔
542
        arr.push(Value::from(1));
1✔
543
        arr.push(Value::from(3));
1✔
544
        arr.insert(1, Value::from(2));
1✔
545

546
        assert_eq!(arr.len(), 3);
1✔
547
        assert_eq!(arr[0].as_number().unwrap().to_i64(), Some(1));
1✔
548
        assert_eq!(arr[1].as_number().unwrap().to_i64(), Some(2));
1✔
549
        assert_eq!(arr[2].as_number().unwrap().to_i64(), Some(3));
1✔
550

551
        let removed = arr.remove(1).unwrap();
1✔
552
        assert_eq!(removed.as_number().unwrap().to_i64(), Some(2));
1✔
553
        assert_eq!(arr.len(), 2);
1✔
554
    }
1✔
555

556
    #[test]
557
    fn test_clone() {
1✔
558
        let mut arr = VArray::new();
1✔
559
        arr.push(Value::from("hello"));
1✔
560
        arr.push(Value::from(42));
1✔
561

562
        let arr2 = arr.clone();
1✔
563
        assert_eq!(arr, arr2);
1✔
564
    }
1✔
565

566
    #[test]
567
    fn inline_strings_in_array_remain_inline() {
1✔
568
        let mut arr = VArray::new();
1✔
569
        for len in 0..=crate::string::VString::INLINE_LEN_MAX.min(6) {
7✔
570
            let s = "a".repeat(len);
7✔
571
            arr.push(Value::from(s.as_str()));
7✔
572
        }
7✔
573

574
        for value in arr.iter() {
7✔
575
            if value.value_type() == ValueType::String {
7✔
576
                assert!(
7✔
577
                    value.is_inline_string(),
7✔
578
                    "array element lost inline representation"
579
                );
UNCOV
580
            }
×
581
        }
582

583
        let cloned = arr.clone();
1✔
584
        for value in cloned.iter() {
7✔
585
            if value.value_type() == ValueType::String {
7✔
586
                assert!(
7✔
587
                    value.is_inline_string(),
7✔
588
                    "clone should preserve inline storage"
589
                );
UNCOV
590
            }
×
591
        }
592
    }
1✔
593

594
    #[test]
595
    fn test_iter() {
1✔
596
        let mut arr = VArray::new();
1✔
597
        arr.push(Value::from(1));
1✔
598
        arr.push(Value::from(2));
1✔
599

600
        let sum: i64 = arr
1✔
601
            .iter()
1✔
602
            .map(|v| v.as_number().unwrap().to_i64().unwrap())
2✔
603
            .sum();
1✔
604
        assert_eq!(sum, 3);
1✔
605
    }
1✔
606

607
    #[test]
608
    fn test_collect() {
1✔
609
        let arr: VArray = vec![1i64, 2, 3].into_iter().map(Value::from).collect();
1✔
610
        assert_eq!(arr.len(), 3);
1✔
611
    }
1✔
612
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc