(
&mut self,
obj_len: usize,
chunks: impl Copy + FnOnce(usize) -> Cs,
)
| 673 | /// before the granule's data is read from / assumed to be initialized. |
| 674 | #[expect(clippy::doc_overindented_list_items)] |
| 675 | fn alloc_for_obj_common<'chunk, Cs: Iterator<Item = (&'chunk [u8], usize)>>( |
| 676 | &mut self, |
| 677 | obj_len: usize, |
| 678 | chunks: impl Copy + FnOnce(usize) -> Cs, |
| 679 | ) -> Result<(VarLenRef, bool), Error> { |
| 680 | // Check that we have sufficient space to allocate `obj_len` bytes in var-len data. |
| 681 | let (req_granules, enough_space, in_blob) = self.has_enough_space_for(obj_len); |
| 682 | if !enough_space { |
| 683 | return Err(Error::InsufficientVarLenSpace { |
| 684 | need: req_granules.try_into().unwrap_or(u16::MAX), |
| 685 | have: self.num_granules_available().try_into().unwrap_or(u16::MAX), |
| 686 | }); |
| 687 | } |
| 688 | |
| 689 | // For large blob objects, only reserve a granule. |
| 690 | // The caller promised that they will initialize it with a blob hash. |
| 691 | if in_blob { |
| 692 | let vlr = self.alloc_blob_hash()?; |
| 693 | return Ok((vlr, true)); |
| 694 | }; |
| 695 | |
| 696 | // Write each `chunk` to var-len storage. |
| 697 | // To do this, we allocate granules for and store the chunks in reverse, |
| 698 | // starting with the end first. |
| 699 | // The offset to the previous granule in the iteration is kept to |
| 700 | // link it in as the next pointer in the current iteration. |
| 701 | let mut next = PageOffset::VAR_LEN_NULL; |
| 702 | debug_assert_eq!(obj_len, chunks(req_granules).map(|(_, len)| len).sum::<usize>()); |
| 703 | for (chunk, len) in chunks(req_granules) { |
| 704 | // This should never error, since we already checked for available space. |
| 705 | let granule = self.alloc_granule()?; |
| 706 | // SAFETY: |
| 707 | // 1. `granule` is properly aligned as it came from `alloc_granule` |
| 708 | // and so is `next` as it's either NULL or was the previous `granule`. |
| 709 | // This also ensures that both are in bounds |
| 710 | // of the page for `granule + granule + VarLenGranule::SIZE`. |
| 711 | // |
| 712 | // 2. `next` is either NULL or was initialized in the previous loop iteration. |
| 713 | // |
| 714 | // 3. `granule` points to an unused slot as the space was just allocated. |
| 715 | unsafe { self.write_chunk_to_granule(chunk, len, granule, next) }; |
| 716 | next = granule; |
| 717 | } |
| 718 | |
| 719 | Ok(( |
| 720 | VarLenRef { |
| 721 | first_granule: next, |
| 722 | length_in_bytes: obj_len as u16, |
| 723 | }, |
| 724 | false, |
| 725 | )) |
| 726 | } |
| 727 | |
| 728 | /// Allocates a granule for a large blob object |
| 729 | /// and returns a [`VarLenRef`] pointing to that granule. |
no test coverage detected