Skip to content

Commit 241e33b

Browse files
committed
fix: item db duplicate handling is stable over insertion order
1 parent f7914e1 commit 241e33b

2 files changed

Lines changed: 45 additions & 19 deletions

File tree

src/db.rs

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use strum::EnumCount;
1313
use crate::block::Block;
1414
use crate::context::ScopeContext;
1515
use crate::everything::Everything;
16-
use crate::helpers::{dup_error, exact_dup_advice, exact_dup_error, TigerHashMap, TigerHashSet};
16+
use crate::helpers::{
17+
dup_error, exact_dup_advice, exact_dup_error, DupReporter, TigerHashMap, TigerHashSet,
18+
};
1719
use crate::item::Item;
1820
use crate::lowercase::Lowercase;
1921
use crate::token::Token;
@@ -50,17 +52,7 @@ impl Default for Db {
5052

5153
impl Db {
5254
pub fn add(&mut self, item: Item, key: Token, block: Block, kind: Box<dyn DbKind>) {
53-
if let Some(other) = self.database[item as usize].get(key.as_str()) {
54-
if other.key.loc.ptr.kind >= key.loc.ptr.kind {
55-
if other.block.equivalent(&block) {
56-
exact_dup_error(&key, &other.key, &item.to_string());
57-
} else {
58-
dup_error(&key, &other.key, &item.to_string());
59-
}
60-
}
61-
}
62-
self.items_lc[item as usize].insert(Lowercase::new(key.as_str()), key.as_str());
63-
self.database[item as usize].insert(key.as_str(), DbEntry { key, block, kind });
55+
self.add_with_reporter(item, key, block, kind, exact_dup_error);
6456
}
6557

6658
#[allow(dead_code)]
@@ -71,17 +63,49 @@ impl Db {
7163
block: Block,
7264
kind: Box<dyn DbKind>,
7365
) {
74-
if let Some(other) = self.database[item as usize].get(key.as_str()) {
75-
if other.key.loc.ptr.kind >= key.loc.ptr.kind {
76-
if other.block.equivalent(&block) {
77-
exact_dup_advice(&key, &other.key, &item.to_string());
66+
self.add_with_reporter(item, key, block, kind, exact_dup_advice);
67+
}
68+
69+
fn add_with_reporter(
70+
&mut self,
71+
item: Item,
72+
key: Token,
73+
block: Block,
74+
kind: Box<dyn DbKind>,
75+
exact_dup_reporter: DupReporter,
76+
) {
77+
use std::collections::hash_map::Entry;
78+
79+
match self.database[item as usize].entry(key.as_str()) {
80+
Entry::Occupied(mut occupied_entry) => {
81+
let new = DbEntry { key, block, kind };
82+
let existing = occupied_entry.get();
83+
84+
let (keep, overwriten) = if new.key.loc.ptr > existing.key.loc.ptr {
85+
(&new, existing)
7886
} else {
79-
dup_error(&key, &other.key, &item.to_string());
87+
(existing, &new)
88+
};
89+
90+
if overwriten.key.loc.ptr.kind >= keep.key.loc.ptr.kind {
91+
if overwriten.block.equivalent(&keep.block) {
92+
exact_dup_reporter(&keep.key, &overwriten.key, &item.to_string());
93+
} else {
94+
dup_error(&keep.key, &overwriten.key, &item.to_string());
95+
}
96+
}
97+
// Update the db if the new entry is the one we're keeping
98+
if &raw const new == &raw const *keep {
99+
self.items_lc[item as usize]
100+
.insert(Lowercase::new(new.key.as_str()), new.key.as_str());
101+
occupied_entry.insert(new);
80102
}
81103
}
104+
Entry::Vacant(vacant_entry) => {
105+
self.items_lc[item as usize].insert(Lowercase::new(key.as_str()), key.as_str());
106+
vacant_entry.insert(DbEntry { key, block, kind });
107+
}
82108
}
83-
self.items_lc[item as usize].insert(Lowercase::new(key.as_str()), key.as_str());
84-
self.database[item as usize].insert(key.as_str(), DbEntry { key, block, kind });
85109
}
86110

87111
#[cfg(feature = "hoi4")]

src/helpers.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ macro_rules! set {
2222
};
2323
}
2424

25+
pub type DupReporter = fn(&Token, &Token, &str) -> ();
26+
2527
/// Warns about a redefinition of a database item
2628
pub fn dup_error(key: &Token, other: &Token, id: &str) {
2729
warn(ErrorKey::DuplicateItem)

0 commit comments

Comments
 (0)