use std::error::Error;
use std::fmt;
use std::iter::{Enumerate, FusedIterator};
use std::slice;
use crate::item;
#[derive(Clone, Debug, Eq)]
pub struct Storage {
base: Vec<u32>,
total: u64,
}
impl Storage {
pub const fn new() -> Self {
Self {
base: Vec::new(),
total: 0,
}
}
pub fn is_empty(&self) -> bool {
self.total == 0
}
pub fn get_total(&self) -> u64 {
self.total
}
pub fn get(&self, ty: item::Type) -> u32 {
match self.base.get(u16::from(ty) as usize) {
None => 0,
Some(cnt) => *cnt,
}
}
pub fn set(&mut self, ty: item::Type, count: u32) -> u32 {
let idx = u16::from(ty) as usize;
match self.base.get_mut(idx) {
None => {
self.base.resize(idx + 1, 0);
self.base[idx] = count;
self.total += count as u64;
0
}
Some(curr) => {
let prev = *curr;
self.total = self.total - prev as u64 + count as u64;
*curr = count;
prev
}
}
}
pub fn add(&mut self, ty: item::Type, add: u32, max: u32) -> (u32, u32) {
let idx = u16::from(ty) as usize;
match self.base.get_mut(idx) {
None => {
let actual = add.min(max);
self.base.resize(idx + 1, 0);
self.base[idx] = actual;
self.total += add as u64;
(actual, actual)
}
Some(curr) => {
if *curr < max {
let actual = add.min(max - *curr);
*curr += actual;
self.total += actual as u64;
(actual, *curr)
} else {
(0, *curr)
}
}
}
}
pub fn try_add(
&mut self,
ty: item::Type,
add: u32,
max: u32,
) -> Result<(u32, u32), TryAddError> {
let idx = u16::from(ty) as usize;
match self.base.get_mut(idx) {
None => {
if add <= max {
self.base.resize(idx + 1, 0);
self.base[idx] = add;
self.total += add as u64;
Ok((add, add))
} else {
Err(TryAddError {
ty,
have: 0,
add,
max,
})
}
}
Some(curr) => {
if *curr <= max && max - *curr <= add {
*curr += add;
self.total += add as u64;
Ok((add, *curr))
} else {
Err(TryAddError {
ty,
have: *curr,
add,
max,
})
}
}
}
}
pub fn sub(&mut self, ty: item::Type, sub: u32, min: u32) -> (u32, u32) {
match self.base.get_mut(u16::from(ty) as usize) {
None => (0, 0),
Some(curr) => {
if *curr > min {
let actual = sub.min(*curr - min);
*curr -= actual;
self.total -= actual as u64;
(actual, *curr)
} else {
(0, *curr)
}
}
}
}
pub fn try_sub(
&mut self,
ty: item::Type,
sub: u32,
min: u32,
) -> Result<(u32, u32), TrySubError> {
let idx = u16::from(ty) as usize;
match self.base.get_mut(idx) {
None => Err(TrySubError {
ty,
have: 0,
sub,
min,
}),
Some(curr) => {
if *curr >= min && *curr - min >= sub {
*curr -= sub;
self.total -= sub as u64;
Ok((sub, *curr))
} else {
Err(TrySubError {
ty,
have: *curr,
sub,
min,
})
}
}
}
}
pub fn add_all(&mut self, other: &Storage, max_each: u32) -> (u64, u64) {
let mut added = 0u64;
if max_each > 0 && other.get_total() > 0 {
let mut iter = other.base.iter().enumerate();
// resize our vector only once and if necessary
let (last, add_last) = iter.rfind(|(_, n)| **n != 0).unwrap();
if self.base.len() <= last {
self.base.resize(last + 1, 0);
}
// process items by increasing ID
for (idx, add) in iter {
let curr = self.base[idx];
if curr < max_each {
let actual = (*add).min(max_each - curr);
self.base[idx] += actual;
added += actual as u64;
}
}
// process the final element (which we've retrieved first)
let curr = self.base[last];
if curr < max_each {
let actual = (*add_last).min(max_each - curr);
self.base[last] += actual;
added += actual as u64;
}
// update total
self.total += added;
}
(added, self.total)
}
pub fn pull_all(&mut self, other: &mut Storage, max_each: u32) -> (u64, u64, u64) {
let mut added = 0u64;
if max_each > 0 && other.get_total() > 0 {
let mut iter = other.base.iter_mut().enumerate();
// resize our vector only once and if necessary
let (last, add_last) = iter.rfind(|(_, n)| **n != 0).unwrap();
if self.base.len() <= last {
self.base.resize(last + 1, 0);
}
// process items by increasing ID
for (idx, add) in iter {
let curr = self.base[idx];
if curr < max_each {
let actual = (*add).min(max_each - curr);
self.base[idx] += actual;
*add -= actual;
added += actual as u64;
}
}
// process the final element (which we've retrieved first)
let curr = self.base[last];
if curr < max_each {
let actual = (*add_last).min(max_each - curr);
self.base[last] += actual;
*add_last -= actual;
added += actual as u64;
}
// update totals
self.total += added;
other.total -= added;
}
(added, self.total, other.total)
}
pub fn sub_all(&mut self, other: &Storage, min_each: u32) -> (u64, u64) {
let mut subbed = 0u64;
if self.get_total() > 0 && other.get_total() > 0 {
// no need for resizing, we only remove
// process items by increasing ID
for (idx, sub) in other.base.iter().enumerate() {
if let Some(curr) = self.base.get(idx) {
if *curr > min_each {
let actual = (*sub).min(*curr - min_each);
self.base[idx] -= actual;
subbed += actual as u64;
}
} else {
break;
}
}
// update total
self.total -= subbed;
}
(subbed, self.total)
}
pub fn diff_all(&mut self, other: &mut Storage, min_each: u32) -> (u64, u64, u64) {
let mut subbed = 0u64;
if self.get_total() > 0 && other.get_total() > 0 {
// no need for resizing, we only remove
// consider only indexes present in both
let end = self.base.len().min(other.base.len());
let lhs = &mut self.base[..end];
let rhs = &mut other.base[..end];
// process items by increasing ID
for (l, r) in lhs.into_iter().zip(rhs) {
if *l > min_each && *r > min_each {
let actual = (*l - min_each).min(*r - min_each);
*l -= actual;
*r -= actual;
subbed -= actual as u64;
}
}
// update totals
self.total -= subbed;
other.total -= subbed;
}
(subbed, self.total, other.total)
}
pub fn iter<'s>(&'s self) -> Iter<'s> {
Iter {
base: self.base.iter().enumerate(),
all: true,
}
}
pub fn iter_nonzero<'s>(&'s self) -> Iter<'s> {
Iter {
base: self.base.iter().enumerate(),
all: false,
}
}
pub fn clear(&mut self) {
self.base.clear()
}
}
// manual because padding with zeros doesn't affect equality
impl PartialEq for Storage {
fn eq(&self, other: &Self) -> bool {
let mut li = self.base.iter().fuse();
let mut ri = other.base.iter().fuse();
loop {
match (li.next(), ri.next()) {
(None, None) => return true,
(l, r) => {
if l.unwrap_or(&0) != r.unwrap_or(&0) {
return false;
}
}
}
}
}
}
impl fmt::Display for Storage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
for (ty, cnt) in self.iter_nonzero() {
if first {
first = false;
} else {
f.write_str(", ")?;
}
write!(f, "{cnt} {ty}")?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TryAddError {
pub ty: item::Type,
pub have: u32,
pub add: u32,
pub max: u32,
}
impl fmt::Display for TryAddError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"adding {} {:?} to current {} would exceed {}",
self.add, self.ty, self.have, self.max
)
}
}
impl Error for TryAddError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TrySubError {
pub ty: item::Type,
pub have: u32,
pub sub: u32,
pub min: u32,
}
impl fmt::Display for TrySubError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"removing {} {:?} from current {} would drop below {}",
self.sub, self.ty, self.have, self.min
)
}
}
impl Error for TrySubError {}
#[derive(Clone, Debug)]
pub struct Iter<'l> {
base: Enumerate<slice::Iter<'l, u32>>,
all: bool,
}
impl<'l> Iterator for Iter<'l> {
type Item = (item::Type, u32);
fn next(&mut self) -> Option<Self::Item> {
while let Some((idx, cnt)) = self.base.next() {
if *cnt > 0 || self.all {
if let Ok(ty) = item::Type::try_from(idx as u16) {
return Some((ty, *cnt));
}
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.base.size_hint().1)
}
}
impl<'l> FusedIterator for Iter<'l> where Enumerate<slice::Iter<'l, u32>>: FusedIterator {}