Remove hashmap based word lookup

This commit is contained in:
Steven Roose 2020-06-19 11:53:13 +01:00
parent a399d5c4a5
commit e6cb3dfa1f
No known key found for this signature in database
GPG Key ID: 2F2A88D7F8D68E87
3 changed files with 7 additions and 107 deletions

View File

@ -36,9 +36,6 @@ all-languages = [
"spanish"
]
# Don't use a map to find words, but iterate through the list.
low-memory = []
[dependencies]
bitcoin_hashes = "0.7.6"
unicode-normalization = "=0.1.9"

View File

@ -19,65 +19,6 @@ mod korean;
#[cfg(feature = "spanish")]
mod spanish;
#[cfg(not(feature = "low-memory"))]
mod lazy {
use std::cell::Cell;
use std::collections::HashMap;
use std::sync::Once;
/// Type used to load a word map in a lazy fashion.
pub(crate) struct LazyMap(Cell<Option<HashMap<&'static str, u16>>>, Once);
impl LazyMap {
#[allow(deprecated)]
const INIT: Self = LazyMap(Cell::new(None), ::std::sync::ONCE_INIT);
#[inline(always)]
pub fn get(&'static self, list: &'static [&'static str]) -> &HashMap<&'static str, u16> {
self.1.call_once(|| {
let mut map = HashMap::new();
for (idx, word) in list.iter().enumerate() {
map.insert(*word, idx as u16);
}
self.0.set(Some(map));
});
// `self.0` is guaranteed to be `Some` by this point
// The `Once` will catch and propagate panics
unsafe {
match *self.0.as_ptr() {
Some(ref x) => x,
None => panic!(),
}
}
}
}
// This marker impl is required for the Cell to work.
// The LazyMap is an implementation identical to lazy_static's.
// We assume lazy_static's exact same usage is considered safe.
#[cfg(not(feature = "low-memory"))]
unsafe impl Sync for LazyMap {}
pub(crate) static LAZY_MAP_ENGLISH: LazyMap = LazyMap::INIT;
#[cfg(feature = "chinese-simplified")]
pub(crate) static LAZY_MAP_CHINESE_SIMPLIFIED: LazyMap = LazyMap::INIT;
#[cfg(feature = "chinese-traditional")]
pub(crate) static LAZY_MAP_CHINESE_TRADITIONAL: LazyMap = LazyMap::INIT;
#[cfg(feature = "czech")]
pub(crate) static LAZY_MAP_CZECH: LazyMap = LazyMap::INIT;
#[cfg(feature = "french")]
pub(crate) static LAZY_MAP_FRENCH: LazyMap = LazyMap::INIT;
#[cfg(feature = "italian")]
pub(crate) static LAZY_MAP_ITALIAN: LazyMap = LazyMap::INIT;
#[cfg(feature = "japanese")]
pub(crate) static LAZY_MAP_JAPANESE: LazyMap = LazyMap::INIT;
#[cfg(feature = "korean")]
pub(crate) static LAZY_MAP_KOREAN: LazyMap = LazyMap::INIT;
#[cfg(feature = "spanish")]
pub(crate) static LAZY_MAP_SPANISH: LazyMap = LazyMap::INIT;
}
/// Language to be used for the mnemonic phrase.
///
/// The English language is always available, other languages are enabled using
@ -147,28 +88,10 @@ impl Language {
}
}
/// The word map that maps words to the index in the word list for this language.
#[cfg(not(feature = "low-memory"))]
pub(crate) fn word_map(self) -> &'static ::std::collections::HashMap<&'static str, u16> {
match self {
Language::English => lazy::LAZY_MAP_ENGLISH.get(self.word_list()),
#[cfg(feature = "chinese-simplified")]
Language::SimplifiedChinese => lazy::LAZY_MAP_CHINESE_SIMPLIFIED.get(self.word_list()),
#[cfg(feature = "chinese-traditional")]
Language::TraditionalChinese => lazy::LAZY_MAP_CHINESE_TRADITIONAL.get(self.word_list()),
#[cfg(feature = "czech")]
Language::Czech => lazy::LAZY_MAP_CZECH.get(self.word_list()),
#[cfg(feature = "french")]
Language::French => lazy::LAZY_MAP_FRENCH.get(self.word_list()),
#[cfg(feature = "italian")]
Language::Italian => lazy::LAZY_MAP_ITALIAN.get(self.word_list()),
#[cfg(feature = "japanese")]
Language::Japanese => lazy::LAZY_MAP_JAPANESE.get(self.word_list()),
#[cfg(feature = "korean")]
Language::Korean => lazy::LAZY_MAP_KOREAN.get(self.word_list()),
#[cfg(feature = "spanish")]
Language::Spanish => lazy::LAZY_MAP_SPANISH.get(self.word_list()),
}
/// Get the index of the word in the word list.
#[inline]
pub(crate) fn find_word(self, word: &str) -> Option<usize> {
self.word_list().iter().position(|w| *w == word)
}
}
@ -222,8 +145,6 @@ mod tests {
for (_idx, word) in lang.word_list().iter().enumerate() {
assert!(::unicode_normalization::is_nfkd(&word));
write!(&mut digest, "{}\n", word).unwrap();
#[cfg(not(feature = "low-memory"))]
assert_eq!(_idx, lang.word_map()[word] as usize);
}
assert_eq!(&sha256::Hash::from_engine(digest).to_string(), sum,
"word list for language {} failed checksum check", lang,

View File

@ -179,9 +179,6 @@ impl Mnemonic {
/// Static method to validate a mnemonic in a given language.
pub fn validate_in(language: Language, s: &str) -> Result<(), Error> {
#[cfg(not(feature = "low-memory"))]
let word_map = language.word_map();
let words: Vec<&str> = s.split_whitespace().collect();
if words.len() < 6 || words.len() % 6 != 0 || words.len() > 24 {
return Err(Error::BadWordCount(words.len()));
@ -189,12 +186,7 @@ impl Mnemonic {
let mut bits = vec![false; words.len() * 11];
for (i, word) in words.iter().enumerate() {
#[cfg(not(feature = "low-memory"))]
let found = word_map.get(word);
#[cfg(feature = "low-memory")]
let found = language.word_list().iter().position(|w| w == word);
if let Some(idx) = found {
if let Some(idx) = language.find_word(word) {
for j in 0..11 {
bits[i * 11 + j] = idx >> (10 - j) & 1 == 1;
}
@ -250,12 +242,7 @@ impl Mnemonic {
return Err(Error::BadWordCount(0));
}
for language in &languages {
#[cfg(not(feature = "low-memory"))]
let found = language.word_map().get(first_word).is_some();
#[cfg(feature = "low-memory")]
let found = language.word_list().iter().any(|w| *w == first_word);
if found {
if language.find_word(first_word).is_some() {
return Ok(*language);
}
}
@ -320,8 +307,6 @@ impl Mnemonic {
// values that were already previously validated.
let language = Mnemonic::guess_language(self.as_str()).unwrap();
#[cfg(not(feature = "low-memory"))]
let word_map = language.word_map();
// Preallocate enough space for the longest possible word list
let mut entropy = Vec::with_capacity(33);
@ -330,10 +315,7 @@ impl Mnemonic {
let words: Vec<&str> = self.as_str().split_whitespace().collect();
for word in &words {
#[cfg(not(feature = "low-memory"))]
let idx = *word_map.get(word).unwrap();
#[cfg(feature = "low-memory")]
let idx = language.word_list().iter().position(|w| w == word).unwrap();
let idx = language.find_word(word).unwrap();
remainder |= ((idx as u32) << (32 - 11)) >> offset;
offset += 11;