Skip to content

Commit f2fed22

Browse files
committed
fix: item db duplicate handling is stable over insertion order
1 parent 314677b commit f2fed22

2 files changed

Lines changed: 48 additions & 20 deletions

File tree

src/db.rs

Lines changed: 46 additions & 20 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,51 @@ 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());
78-
} else {
79-
dup_error(&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) =
85+
// Just compare the top loc, not the whole stack
86+
if new.key.loc.ptr > existing.key.loc.ptr {
87+
(&new, existing)
88+
} else {
89+
(existing, &new)
90+
};
91+
92+
if overwriten.key.loc.ptr.kind >= keep.key.loc.ptr.kind {
93+
if overwriten.block.equivalent(&keep.block) {
94+
exact_dup_reporter(&keep.key, &overwriten.key, &item.to_string());
95+
} else {
96+
dup_error(&keep.key, &overwriten.key, &item.to_string());
97+
}
8098
}
99+
// Update the db if the new entry is the one we're keeping
100+
if &raw const new == &raw const *keep {
101+
self.items_lc[item as usize]
102+
.insert(Lowercase::new(new.key.as_str()), new.key.as_str());
103+
occupied_entry.insert(new);
104+
}
105+
}
106+
Entry::Vacant(vacant_entry) => {
107+
self.items_lc[item as usize].insert(Lowercase::new(key.as_str()), key.as_str());
108+
vacant_entry.insert(DbEntry { key, block, kind });
81109
}
82110
}
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 });
85111
}
86112

87113
#[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)