test MutableMultiValue

This commit is contained in:
Edward Shen 2021-03-05 23:34:07 -05:00
parent 05eba761ee
commit 6167914277
Signed by: edward
GPG key ID: 19182661E818369F

View file

@ -2,9 +2,12 @@
use crate::parser::{parse_from_bytes, Error, Event, ParsedSectionHeader, Parser}; use crate::parser::{parse_from_bytes, Error, Event, ParsedSectionHeader, Parser};
use crate::values::{normalize_bytes, normalize_cow, normalize_vec}; use crate::values::{normalize_bytes, normalize_cow, normalize_vec};
use std::collections::{HashMap, VecDeque}; use std::{borrow::Borrow, convert::TryFrom, ops::Deref};
use std::{borrow::Borrow, convert::TryFrom};
use std::{borrow::Cow, fmt::Display}; use std::{borrow::Cow, fmt::Display};
use std::{
collections::{HashMap, VecDeque},
ops::DerefMut,
};
/// All possible error types that may occur from interacting with [`GitConfig`]. /// All possible error types that may occur from interacting with [`GitConfig`].
#[derive(PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord, Debug)] #[derive(PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord, Debug)]
@ -103,8 +106,6 @@ impl MutableValue<'_, '_, '_> {
} }
} }
dbg!(&latest_value);
dbg!(&partial_value);
latest_value latest_value
.map(normalize_cow) .map(normalize_cow)
.or_else(|| partial_value.map(normalize_vec)) .or_else(|| partial_value.map(normalize_vec))
@ -144,6 +145,44 @@ impl MutableValue<'_, '_, '_> {
} }
} }
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
struct EntryData {
section_id: SectionId,
offset_index: usize,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
enum Offset {
NonSignificant(usize),
Significant(usize),
}
impl Offset {
const fn len(&self) -> usize {
match self {
Self::NonSignificant(v) | Self::Significant(v) => *v,
}
}
}
impl Deref for Offset {
type Target = usize;
fn deref(&self) -> &Self::Target {
match self {
Self::NonSignificant(v) | Self::Significant(v) => v,
}
}
}
impl DerefMut for Offset {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::NonSignificant(v) | Self::Significant(v) => v,
}
}
}
/// An intermediate representation of a mutable multivar obtained from /// An intermediate representation of a mutable multivar obtained from
/// [`GitConfig`]. /// [`GitConfig`].
/// ///
@ -151,13 +190,21 @@ impl MutableValue<'_, '_, '_> {
/// [`GitConfig`], and thus guarantees through Rust's borrower checker that /// [`GitConfig`], and thus guarantees through Rust's borrower checker that
/// multiple mutable references to [`GitConfig`] cannot be owned at the same /// multiple mutable references to [`GitConfig`] cannot be owned at the same
/// time. /// time.
#[derive(Eq, PartialEq, Debug)]
pub struct MutableMultiValue<'borrow, 'lookup, 'event> { pub struct MutableMultiValue<'borrow, 'lookup, 'event> {
section: &'borrow mut HashMap<SectionId, Vec<Event<'event>>>, section: &'borrow mut HashMap<SectionId, Vec<Event<'event>>>,
key: &'lookup str, key: &'lookup str,
indices_and_sizes: Vec<(SectionId, usize, usize)>, /// 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.
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.
offsets: HashMap<SectionId, Vec<Offset>>,
} }
impl<'event> MutableMultiValue<'_, '_, 'event> { impl<'lookup, 'event> MutableMultiValue<'_, 'lookup, 'event> {
/// Returns the actual values. This is computed each time this is called. /// Returns the actual values. This is computed each time this is called.
/// ///
/// # Errors /// # Errors
@ -169,8 +216,14 @@ impl<'event> MutableMultiValue<'_, '_, 'event> {
let mut partial_value = None; let mut partial_value = None;
// section_id is guaranteed to exist in self.sections, else we have a // section_id is guaranteed to exist in self.sections, else we have a
// violated invariant. // violated invariant.
for (section_id, index, size) in &self.indices_and_sizes { for EntryData {
for event in &self.section.get(section_id).unwrap()[*index..=*index + *size] { section_id,
offset_index,
} in &self.indices_and_sizes
{
let (offset, size) =
MutableMultiValue::get_index_and_size(&self.offsets, *section_id, *offset_index);
for event in &self.section.get(section_id).unwrap()[offset..offset + size] {
match event { match event {
Event::Key(event_key) if *event_key == self.key => found_key = true, Event::Key(event_key) if *event_key == self.key => found_key = true,
Event::Value(v) if found_key => { Event::Value(v) if found_key => {
@ -238,14 +291,18 @@ impl<'event> MutableMultiValue<'_, '_, 'event> {
/// ///
/// This will panic if the index is out of range. /// This will panic if the index is out of range.
pub fn set_value<'a: 'event>(&mut self, index: usize, input: Cow<'a, [u8]>) { pub fn set_value<'a: 'event>(&mut self, index: usize, input: Cow<'a, [u8]>) {
let (section_id, index, size) = &mut self.indices_and_sizes[index]; let EntryData {
let section = self.section.get_mut(section_id).unwrap(); section_id,
dbg!(&section); offset_index,
section.drain(*index..=*index + *size); } = self.indices_and_sizes[index];
*size = 3; MutableMultiValue::set_value_inner(
section.insert(*index, Event::Value(input)); self.key,
section.insert(*index, Event::KeyValueSeparator); &mut self.offsets,
section.insert(*index, Event::Key(Cow::Owned(self.key.into()))); self.section.get_mut(&section_id).unwrap(),
section_id,
offset_index,
input,
);
} }
/// Sets all values to the provided values. Note that this follows [`zip`] /// Sets all values to the provided values. Note that this follows [`zip`]
@ -255,14 +312,24 @@ impl<'event> MutableMultiValue<'_, '_, 'event> {
/// remaining values are ignored. /// remaining values are ignored.
/// ///
/// [`zip`]: std::iter::Iterator::zip /// [`zip`]: std::iter::Iterator::zip
#[inline]
pub fn set_values<'a: 'event>(&mut self, input: impl Iterator<Item = Cow<'a, [u8]>>) { pub fn set_values<'a: 'event>(&mut self, input: impl Iterator<Item = Cow<'a, [u8]>>) {
for ((section_id, index, size), value) in self.indices_and_sizes.iter_mut().zip(input) { for (
let section = self.section.get_mut(section_id).unwrap(); EntryData {
section.drain(*index..=*index + *size); section_id,
*size = 3; offset_index,
section.insert(*index, Event::Value(value)); },
section.insert(*index, Event::KeyValueSeparator); value,
section.insert(*index, Event::Key(Cow::Owned(self.key.into()))); ) in self.indices_and_sizes.iter().zip(input)
{
Self::set_value_inner(
self.key,
&mut self.offsets,
self.section.get_mut(section_id).unwrap(),
*section_id,
*offset_index,
value,
);
} }
} }
@ -277,16 +344,19 @@ impl<'event> MutableMultiValue<'_, '_, 'event> {
/// input bytes for all values. /// input bytes for all values.
#[inline] #[inline]
pub fn set_owned_values_all(&mut self, input: &[u8]) { pub fn set_owned_values_all(&mut self, input: &[u8]) {
for (section_id, index, size) in &mut self.indices_and_sizes { for EntryData {
self.section section_id,
.get_mut(section_id) offset_index,
.unwrap() } in &self.indices_and_sizes
.drain(*index..*index + *size); {
*size = 1; Self::set_value_inner(
self.section self.key,
.get_mut(section_id) &mut self.offsets,
.unwrap() self.section.get_mut(section_id).unwrap(),
.insert(*index, Event::Value(Cow::Owned(input.to_vec()))); *section_id,
*offset_index,
Cow::Owned(input.to_vec()),
);
} }
} }
@ -295,20 +365,42 @@ impl<'event> MutableMultiValue<'_, '_, 'event> {
/// [`GitConfig`]. Consider using [`Self::set_owned_values_all`] or /// [`GitConfig`]. Consider using [`Self::set_owned_values_all`] or
/// [`Self::set_str_all`] unless you have a strict performance or memory /// [`Self::set_str_all`] unless you have a strict performance or memory
/// need for a more ergonomic interface. /// need for a more ergonomic interface.
#[inline]
pub fn set_values_all<'a: 'event>(&mut self, input: &'a [u8]) { pub fn set_values_all<'a: 'event>(&mut self, input: &'a [u8]) {
for (section_id, index, size) in &mut self.indices_and_sizes { for EntryData {
self.section section_id,
.get_mut(section_id) offset_index,
.unwrap() } in &self.indices_and_sizes
.drain(*index..*index + *size); {
*size = 1; Self::set_value_inner(
self.section self.key,
.get_mut(section_id) &mut self.offsets,
.unwrap() self.section.get_mut(section_id).unwrap(),
.insert(*index, Event::Value(Cow::Borrowed(input))); *section_id,
*offset_index,
Cow::Borrowed(input),
);
} }
} }
fn set_value_inner<'a: 'event>(
key: &'lookup str,
offsets: &mut HashMap<SectionId, Vec<Offset>>,
section: &mut Vec<Event<'event>>,
section_id: SectionId,
offset_index: usize,
input: Cow<'a, [u8]>,
) {
let (offset, size) =
MutableMultiValue::get_index_and_size(offsets, section_id, offset_index);
section.drain(offset..offset + size);
MutableMultiValue::set_offset(offsets, section_id, offset_index, 3);
section.insert(offset, Event::Value(input));
section.insert(offset, Event::KeyValueSeparator);
section.insert(offset, Event::Key(Cow::Owned(key.into())));
}
/// Removes the value at the given index. Does nothing when called multiple /// Removes the value at the given index. Does nothing when called multiple
/// times in succession. /// times in succession.
/// ///
@ -316,13 +408,19 @@ impl<'event> MutableMultiValue<'_, '_, 'event> {
/// ///
/// This will panic if the index is out of range. /// This will panic if the index is out of range.
pub fn delete(&mut self, index: usize) { pub fn delete(&mut self, index: usize) {
let (section_id, section_index, size) = &mut self.indices_and_sizes[index]; let EntryData {
if *size > 0 { section_id,
offset_index,
} = &self.indices_and_sizes[index];
let (offset, size) =
MutableMultiValue::get_index_and_size(&self.offsets, *section_id, *offset_index);
if size > 0 {
self.section self.section
.get_mut(section_id) .get_mut(section_id)
.unwrap() .unwrap()
.drain(*section_index..=*section_index + *size); .drain(offset..offset + size);
*size = 0;
Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
self.indices_and_sizes.remove(index); self.indices_and_sizes.remove(index);
} }
} }
@ -330,17 +428,57 @@ impl<'event> MutableMultiValue<'_, '_, 'event> {
/// Removes all values. Does nothing when called multiple times in /// Removes all values. Does nothing when called multiple times in
/// succession. /// succession.
pub fn delete_all(&mut self) { pub fn delete_all(&mut self) {
for (section_id, index, size) in &mut self.indices_and_sizes { for EntryData {
if *size > 0 { section_id,
offset_index,
} in &self.indices_and_sizes
{
let (offset, size) =
MutableMultiValue::get_index_and_size(&self.offsets, *section_id, *offset_index);
if size > 0 {
self.section self.section
.get_mut(section_id) .get_mut(section_id)
.unwrap() .unwrap()
.drain(*index..=*index + *size); .drain(offset..offset + size);
*size = 0; Self::set_offset(&mut self.offsets, *section_id, *offset_index, 0);
} }
} }
self.indices_and_sizes.clear(); self.indices_and_sizes.clear();
} }
// SectionId is the same size as a reference, which means it's just as
// efficient passing in a value instead of a reference.
fn get_index_and_size(
offsets: &'lookup HashMap<SectionId, Vec<Offset>>,
section_id: SectionId,
offset_index: usize,
) -> (usize, usize) {
offsets
.get(&section_id)
.unwrap()
.iter()
.take(offset_index + 1)
.fold((0, 0), |(old, new), offset| (old + new, offset.len()))
}
// This must be an associated function rather than a method to allow Rust
// to split mutable borrows.
//
// SectionId is the same size as a reference, which means it's just as
// efficient passing in a value instead of a reference.
fn set_offset(
offsets: &mut HashMap<SectionId, Vec<Offset>>,
section_id: SectionId,
offset_index: usize,
value: usize,
) {
*offsets
.get_mut(&section_id)
.unwrap()
.get_mut(offset_index)
.unwrap()
.deref_mut() = value;
}
} }
/// High level `git-config` reader and writer. /// High level `git-config` reader and writer.
@ -775,40 +913,47 @@ impl<'event> GitConfig<'event> {
.get_section_ids_by_name_and_subname(section_name, subsection_name)? .get_section_ids_by_name_and_subname(section_name, subsection_name)?
.to_vec(); .to_vec();
let mut indices = vec![]; let mut offsets = HashMap::new();
let mut entries = vec![];
for section_id in section_ids.iter().rev() { for section_id in section_ids.iter().rev() {
let mut size = 0; let mut last_boundary = 0;
let mut index = 0;
let mut found_key = false; let mut found_key = false;
let mut offset_list = vec![];
let mut offset_index = 0;
for (i, event) in self.sections.get(section_id).unwrap().iter().enumerate() { for (i, event) in self.sections.get(section_id).unwrap().iter().enumerate() {
match event { match event {
Event::Key(event_key) if *event_key == key => { Event::Key(event_key) if *event_key == key => {
index = i;
size = 1;
found_key = true; found_key = true;
} offset_list.push(Offset::NonSignificant(i - last_boundary));
Event::Newline(_) | Event::Whitespace(_) | Event::ValueNotDone(_) offset_index += 1;
if found_key => last_boundary = i;
{
size += 1;
} }
Event::Value(_) | Event::ValueDone(_) if found_key => { Event::Value(_) | Event::ValueDone(_) if found_key => {
found_key = false; found_key = false;
size += 1; entries.push(EntryData {
indices.push((*section_id, index, size)); section_id: *section_id,
offset_index,
});
offset_list.push(Offset::Significant(i - last_boundary + 1));
offset_index += 1;
last_boundary = i + 1;
} }
_ => (), _ => (),
} }
} }
offsets.insert(*section_id, offset_list);
} }
if indices.is_empty() { entries.sort();
if entries.is_empty() {
Err(GitConfigError::KeyDoesNotExist(key)) Err(GitConfigError::KeyDoesNotExist(key))
} else { } else {
Ok(MutableMultiValue { Ok(MutableMultiValue {
section: &mut self.sections, section: &mut self.sections,
key, key,
indices_and_sizes: indices, indices_and_sizes: entries,
offsets,
}) })
} }
} }
@ -1371,9 +1516,9 @@ mod mutable_multi_value {
assert_eq!( assert_eq!(
&*value.get().unwrap(), &*value.get().unwrap(),
vec![ vec![
Cow::<[u8]>::Owned(b"b100".to_vec()),
Cow::<[u8]>::Borrowed(b"d"), Cow::<[u8]>::Borrowed(b"d"),
Cow::<[u8]>::Borrowed(b"f"), Cow::<[u8]>::Borrowed(b"f"),
Cow::<[u8]>::Owned(b"b100".to_vec())
] ]
); );
} }
@ -1401,6 +1546,41 @@ mod mutable_multi_value {
.get_raw_multi_value_mut("core", None, "a") .get_raw_multi_value_mut("core", None, "a")
.unwrap(); .unwrap();
values.set_string(0, "Hello".to_string()); values.set_string(0, "Hello".to_string());
dbg!(values);
assert_eq!(
git_config.to_string(),
r#"[core]
a=Hello
[core]
a=d
a=f"#,
);
}
#[test]
fn set_value_at_end() {
let mut git_config = init_config();
let mut values = git_config
.get_raw_multi_value_mut("core", None, "a")
.unwrap();
values.set_string(2, "Hello".to_string());
assert_eq!(
git_config.to_string(),
r#"[core]
a=b"100"
[core]
a=d
a=Hello"#,
);
}
#[test]
fn set_values_all() {
let mut git_config = init_config();
let mut values = git_config
.get_raw_multi_value_mut("core", None, "a")
.unwrap();
values.set_owned_values_all(b"Hello");
assert_eq!( assert_eq!(
git_config.to_string(), git_config.to_string(),
r#"[core] r#"[core]
@ -1410,6 +1590,34 @@ mod mutable_multi_value {
a=Hello"#, a=Hello"#,
); );
} }
#[test]
fn delete() {
let mut git_config = init_config();
let mut values = git_config
.get_raw_multi_value_mut("core", None, "a")
.unwrap();
values.delete(0);
assert_eq!(
git_config.to_string(),
"[core]\n \n [core]
a=d
a=f",
);
}
#[test]
fn delete_all() {
let mut git_config = init_config();
let mut values = git_config
.get_raw_multi_value_mut("core", None, "a")
.unwrap();
values.delete_all();
assert_eq!(
git_config.to_string(),
"[core]\n \n [core]\n \n ",
);
}
} }
#[cfg(test)] #[cfg(test)]