use std::{collections::HashMap, ops::DerefMut}; use bstr::{BStr, BString, ByteVec}; use crate::{ file::{ self, SectionData, SectionId, mutable::{Whitespace, escape_value}, }, lookup, parse::{Event, section}, value::normalize,}; /// Internal data structure for [`MutableMultiValue`]#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]pub(crate) struct EntryData { pub(crate) section_id: SectionId, pub(crate) offset_index: usize,} /// An intermediate representation of a mutable multivar obtained from a [`File`][crate::File].#[derive(Debug)]pub struct MultiValueMut<'borrow> { pub(crate) section: &'borrow mut HashMap<SectionId, SectionData>, pub(crate) backing: &'borrow mut Vec<u8>, pub(crate) key: section::ValueName, /// Each entry data struct provides sufficient information to index into /// [`Self::offsets`]. This layer of indirection is used for users to index /// into the offsets rather than leaking the internal data structures. pub(crate) indices_and_sizes: Vec<EntryData>, /// Each offset represents the size of a event slice and whether or not the /// event slice is significant or not. This is used to index into the /// actual section. pub(crate) offsets: HashMap<SectionId, Vec<usize>>,} impl MultiValueMut<'_> { /// Returns the actual values. pub fn get(&self) -> Result<Vec<BString>, lookup::existing::Error> { let mut expect_value = false; let mut values = Vec::new(); let mut concatenated_value = BString::default(); for EntryData { section_id, offset_index, } in &self.indices_and_sizes { let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index); for event in &self.section.get(section_id).expect("known section id").as_ref()[offset..offset + size] { match event { Event::SectionValueName(section_key) if section_key .as_bstr_in(self.backing) .eq_ignore_ascii_case(self.key.0.as_slice()) => { expect_value = true; } Event::Value(v) if expect_value => { expect_value = false; values.push(normalize(v.as_slice_in(self.backing)).into_owned()); } Event::ValueNotDone(v) if expect_value => concatenated_value.push_str(v.as_slice_in(self.backing)), Event::ValueDone(v) if expect_value => { expect_value = false; concatenated_value.push_str(v.as_slice_in(self.backing)); let concatenated_value = std::mem::take(&mut concatenated_value); values.push(normalize(&concatenated_value).into_owned()); } _ => (), } } } if values.is_empty() { return Err(lookup::existing::Error::KeyMissing); } Ok(values) } /// Returns the amount of values within this multivar. #[must_use] pub fn len(&self) -> usize { self.indices_and_sizes.len() } /// Returns true if the multivar does not have any values. /// This might occur if the value was deleted but wasn't yet set with a new value. #[must_use] pub fn is_empty(&self) -> bool { self.indices_and_sizes.is_empty() } /// Sets the value at the given index. /// /// # Safety /// /// This will panic if the index is out of range. pub fn set_string_at(&mut self, index: usize, value: impl AsRef<str>) -> Result<(), crate::parse::span::Error> { self.set_at(index, value.as_ref()) } /// Sets the value at the given index. /// /// # Safety /// /// This will panic if the index is out of range. pub fn set_at(&mut self, index: usize, value: impl crate::AsBStr) -> Result<(), crate::parse::span::Error> { let EntryData { section_id, offset_index, } = self.indices_and_sizes[index]; MultiValueMut::set_value_inner( &self.key, &mut self.offsets, &mut self.section.get_mut(§ion_id).expect("known section id").body, self.backing, section_id, offset_index, value.as_bstr(), ) } /// Sets all values to the provided ones. Note that this follows [`zip`] /// logic: if the number of values in the input is less than the number of /// values currently existing, then only the first `n` values are modified. /// If more values are provided than there currently are, then the /// remaining values are ignored. /// /// [`zip`]: std::iter::Iterator::zip pub fn set_values<Iter, Item>(&mut self, values: Iter) -> Result<(), crate::parse::span::Error> where Iter: IntoIterator<Item = Item>, Item: crate::AsBStr, { for ( EntryData { section_id, offset_index, }, value, ) in self.indices_and_sizes.iter().zip(values) { Self::set_value_inner( &self.key, &mut self.offsets, &mut self.section.get_mut(section_id).expect("known section id").body, self.backing, *section_id, *offset_index, value.as_bstr(), )?; } Ok(()) } /// Sets all values in this multivar to the provided one without owning the /// provided input. pub fn set_all(&mut self, input: impl crate::AsBStr) -> Result<(), crate::parse::span::Error> { let input = input.as_bstr(); for EntryData { section_id, offset_index, } in &self.indices_and_sizes { Self::set_value_inner( &self.key, &mut self.offsets, &mut self.section.get_mut(section_id).expect("known section id").body, self.backing, *section_id, *offset_index, input, )?; } Ok(()) } fn set_value_inner( value_name: §ion::ValueName, offsets: &mut HashMap<SectionId, Vec<usize>>, section: &mut file::section::BodyData, backing: &mut Vec<u8>, section_id: SectionId, offset_index: usize, value: &BStr, ) -> Result<(), crate::parse::span::Error> { let (offset, size) = MultiValueMut::index_and_size(offsets, section_id, offset_index); let whitespace = Whitespace::from_body(section, backing); let value = crate::parse::Span::append(backing, &escape_value(value))?; let key_sep_events = whitespace.key_value_separators(backing)?; let key = crate::parse::Span::append(backing, value_name.0.as_slice())?; let section = section.as_mut(); section.drain(offset..offset + size); MultiValueMut::set_offset(offsets, section_id, offset_index, 2 + key_sep_events.len()); section.insert(offset, Event::Value(value)); section .splice(offset..offset, key_sep_events.into_iter().rev()) .for_each(|_| {}); section.insert(offset, Event::SectionValueName(key)); Ok(()) } /// Removes the value at the given index. Does nothing when called multiple /// times in succession. /// /// # Safety /// /// This will panic if the index is out of range. pub fn delete(&mut self, index: usize) { let EntryData { section_id, offset_index, } = &self.indices_and_sizes[index]; let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index); if size == 0 { return; } self.section .get_mut(section_id) .expect("known section id") .body .as_mut() .drain(offset..offset + size); Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0); self.indices_and_sizes.remove(index); } /// Removes all values. Does nothing when called multiple times in /// succession. pub fn delete_all(&mut self) { for EntryData { section_id, offset_index, } in &self.indices_and_sizes { let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index); if size == 0 { continue; } self.section .get_mut(section_id) .expect("known section id") .body .as_mut() .drain(offset..offset + size); Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0); } self.indices_and_sizes.clear(); } fn index_and_size( offsets: &HashMap<SectionId, Vec<usize>>, section_id: SectionId, offset_index: usize, ) -> (usize, usize) { offsets .get(§ion_id) .expect("known section id") .iter() .take(offset_index + 1) .fold((0, 0), |(total_ofs, ofs), size| (total_ofs + ofs, *size)) } // This must be an associated function rather than a method to allow Rust // to split mutable borrows. fn set_offset( offsets: &mut HashMap<SectionId, Vec<usize>>, section_id: SectionId, offset_index: usize, value: usize, ) { *offsets .get_mut(§ion_id) .expect("known section id") .get_mut(offset_index) .unwrap() .deref_mut() = value; }}
use std::{collections::HashMap, ops::DerefMut};
use bstr::{BStr, BString, ByteVec};
use crate::{
file::{
self, SectionData, SectionId,
mutable::{Whitespace, escape_value},
},
lookup,
parse::{Event, section},
value::normalize,
};
/// Internal data structure for [`MutableMultiValue`]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub(crate) struct EntryData {
pub(crate) section_id: SectionId,
pub(crate) offset_index: usize,
}
/// An intermediate representation of a mutable multivar obtained from a [`File`][crate::File].
#[derive(Debug)]
pub struct MultiValueMut<'borrow> {
pub(crate) section: &'borrow mut HashMap<SectionId, SectionData>,
pub(crate) backing: &'borrow mut Vec<u8>,
pub(crate) key: section::ValueName,
/// Each entry data struct provides sufficient information to index into
/// [`Self::offsets`]. This layer of indirection is used for users to index
/// into the offsets rather than leaking the internal data structures.
pub(crate) indices_and_sizes: Vec<EntryData>,
/// Each offset represents the size of a event slice and whether or not the
/// event slice is significant or not. This is used to index into the
/// actual section.
pub(crate) offsets: HashMap<SectionId, Vec<usize>>,
}
impl MultiValueMut<'_> {
/// Returns the actual values.
pub fn get(&self) -> Result<Vec<BString>, lookup::existing::Error> {
let mut expect_value = false;
let mut values = Vec::new();
let mut concatenated_value = BString::default();
for EntryData {
section_id,
offset_index,
} in &self.indices_and_sizes
{
let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index);
for event in &self.section.get(section_id).expect("known section id").as_ref()[offset..offset + size] {
match event {
Event::SectionValueName(section_key)
if section_key
.as_bstr_in(self.backing)
.eq_ignore_ascii_case(self.key.0.as_slice()) =>
{
expect_value = true;
}
Event::Value(v) if expect_value => {
expect_value = false;
values.push(normalize(v.as_slice_in(self.backing)).into_owned());
}
Event::ValueNotDone(v) if expect_value => concatenated_value.push_str(v.as_slice_in(self.backing)),
Event::ValueDone(v) if expect_value => {
expect_value = false;
concatenated_value.push_str(v.as_slice_in(self.backing));
let concatenated_value = std::mem::take(&mut concatenated_value);
values.push(normalize(&concatenated_value).into_owned());
}
_ => (),
}
}
}
if values.is_empty() {
return Err(lookup::existing::Error::KeyMissing);
}
Ok(values)
}
/// Returns the amount of values within this multivar.
#[must_use]
pub fn len(&self) -> usize {
self.indices_and_sizes.len()
}
/// Returns true if the multivar does not have any values.
/// This might occur if the value was deleted but wasn't yet set with a new value.
#[must_use]
pub fn is_empty(&self) -> bool {
self.indices_and_sizes.is_empty()
}
/// Sets the value at the given index.
///
/// # Safety
///
/// This will panic if the index is out of range.
pub fn set_string_at(&mut self, index: usize, value: impl AsRef<str>) -> Result<(), crate::parse::span::Error> {
self.set_at(index, value.as_ref())
}
/// Sets the value at the given index.
///
/// # Safety
///
/// This will panic if the index is out of range.
pub fn set_at(&mut self, index: usize, value: impl crate::AsBStr) -> Result<(), crate::parse::span::Error> {
let EntryData {
section_id,
offset_index,
} = self.indices_and_sizes[index];
MultiValueMut::set_value_inner(
&self.key,
&mut self.offsets,
&mut self.section.get_mut(§ion_id).expect("known section id").body,
self.backing,
section_id,
offset_index,
value.as_bstr(),
)
}
/// Sets all values to the provided ones. Note that this follows [`zip`]
/// logic: if the number of values in the input is less than the number of
/// values currently existing, then only the first `n` values are modified.
/// If more values are provided than there currently are, then the
/// remaining values are ignored.
///
/// [`zip`]: std::iter::Iterator::zip
pub fn set_values<Iter, Item>(&mut self, values: Iter) -> Result<(), crate::parse::span::Error>
where
Iter: IntoIterator<Item = Item>,
Item: crate::AsBStr,
{
for (
EntryData {
section_id,
offset_index,
},
value,
) in self.indices_and_sizes.iter().zip(values)
{
Self::set_value_inner(
&self.key,
&mut self.offsets,
&mut self.section.get_mut(section_id).expect("known section id").body,
self.backing,
*section_id,
*offset_index,
value.as_bstr(),
)?;
}
Ok(())
}
/// Sets all values in this multivar to the provided one without owning the
/// provided input.
pub fn set_all(&mut self, input: impl crate::AsBStr) -> Result<(), crate::parse::span::Error> {
let input = input.as_bstr();
for EntryData {
section_id,
offset_index,
} in &self.indices_and_sizes
{
Self::set_value_inner(
&self.key,
&mut self.offsets,
&mut self.section.get_mut(section_id).expect("known section id").body,
self.backing,
*section_id,
*offset_index,
input,
)?;
}
Ok(())
}
fn set_value_inner(
value_name: §ion::ValueName,
offsets: &mut HashMap<SectionId, Vec<usize>>,
section: &mut file::section::BodyData,
backing: &mut Vec<u8>,
section_id: SectionId,
offset_index: usize,
value: &BStr,
) -> Result<(), crate::parse::span::Error> {
let (offset, size) = MultiValueMut::index_and_size(offsets, section_id, offset_index);
let whitespace = Whitespace::from_body(section, backing);
let value = crate::parse::Span::append(backing, &escape_value(value))?;
let key_sep_events = whitespace.key_value_separators(backing)?;
let key = crate::parse::Span::append(backing, value_name.0.as_slice())?;
let section = section.as_mut();
section.drain(offset..offset + size);
MultiValueMut::set_offset(offsets, section_id, offset_index, 2 + key_sep_events.len());
section.insert(offset, Event::Value(value));
section
.splice(offset..offset, key_sep_events.into_iter().rev())
.for_each(|_| {});
section.insert(offset, Event::SectionValueName(key));
Ok(())
}
/// Removes the value at the given index. Does nothing when called multiple
/// times in succession.
///
/// # Safety
///
/// This will panic if the index is out of range.
pub fn delete(&mut self, index: usize) {
let EntryData {
section_id,
offset_index,
} = &self.indices_and_sizes[index];
let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index);
if size == 0 {
return;
}
self.section
.get_mut(section_id)
.expect("known section id")
.body
.as_mut()
.drain(offset..offset + size);
Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
self.indices_and_sizes.remove(index);
}
/// Removes all values. Does nothing when called multiple times in
/// succession.
pub fn delete_all(&mut self) {
for EntryData {
section_id,
offset_index,
} in &self.indices_and_sizes
{
let (offset, size) = MultiValueMut::index_and_size(&self.offsets, *section_id, *offset_index);
if size == 0 {
continue;
}
self.section
.get_mut(section_id)
.expect("known section id")
.body
.as_mut()
.drain(offset..offset + size);
Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
}
self.indices_and_sizes.clear();
}
fn index_and_size(
offsets: &HashMap<SectionId, Vec<usize>>,
section_id: SectionId,
offset_index: usize,
) -> (usize, usize) {
offsets
.get(§ion_id)
.expect("known section id")
.iter()
.take(offset_index + 1)
.fold((0, 0), |(total_ofs, ofs), size| (total_ofs + ofs, *size))
}
// This must be an associated function rather than a method to allow Rust
// to split mutable borrows.
fn set_offset(
offsets: &mut HashMap<SectionId, Vec<usize>>,
section_id: SectionId,
offset_index: usize,
value: usize,
) {
*offsets
.get_mut(§ion_id)
.expect("known section id")
.get_mut(offset_index)
.unwrap()
.deref_mut() = value;
}
}