viffy 0.1.5

SoA + SIMD automata generator
Documentation
use crate::{make_typed_id, ContainerIndex, Serializer, Serializable};

make_typed_id!(RawTextKey, u32);

/// Raw text pool
#[derive(Default, Debug)]
pub struct TextPool(Vec<String>);
impl TextPool {
	pub fn add_text(&mut self, text: &str) -> RawTextKey {
		self.0.push(text.to_string());
		RawTextKey::from(self.0.len() as u32)
	}
	pub fn get_text_from_key(&self, key: RawTextKey) -> &str {
		if key.is_valid() { self.0.get(key.index() as usize).unwrap().as_str() }
		else { &"NOTEXTKEY" }
	}
}
impl Serializable for TextPool {
	fn serialize(&self, s: &mut Serializer) {
		self.0.serialize(s);
	}
	fn deserialize(&mut self, s: &mut Serializer) {
		self.0.deserialize(s);
	}
	fn serialized_size(&self) -> usize {
		self.0.serialized_size()
	}
}

//
// Localised text
//
make_typed_id!(TextKey, u32);
make_typed_id!(LocaleKey, u8);

#[derive(Default, Debug)]
pub struct UnlocalisedTextPool {
	locale_keys: std::collections::BTreeMap<String, TextKey>,
}
impl UnlocalisedTextPool {
	pub fn get_key_as_string(&self, key: TextKey) -> String {
		if key.is_valid() {
			self.locale_keys.iter().filter(|x| *x.1 == key)
				.map(|x| x.0.clone())
				.next()
				.unwrap_or_default()
		} else {
			"NOTEXTKEY".to_string()
		}
	}
	pub fn find_or_add_key(&mut self, text: &str) -> TextKey {
		self.locale_keys.get(text)
			.map(|x| *x)
			.or_else(|| {
				let id = TextKey::from((self.locale_keys.len() + 1) as u32);
				self.locale_keys.insert(text.to_string(), id);
				Some(id)
			}).unwrap()
	}
	pub fn view_locale_keys(&self) -> &std::collections::BTreeMap<String, TextKey> {
		&self.locale_keys
	}
}
impl Serializable for UnlocalisedTextPool {
	fn serialize(&self, s: &mut Serializer) {
		self.locale_keys.serialize(s);
	}
	fn deserialize(&mut self, s: &mut Serializer) {
		self.locale_keys.deserialize(s);
	}
	fn serialized_size(&self) -> usize {
		self.locale_keys.serialized_size()
	}
}

#[derive(Default)]
pub struct LocalisedTextPool {
	raw_text: TextPool,
	per_locale_text: Vec<Vec<RawTextKey>>,
	locale_keys: std::collections::BTreeMap<String, TextKey>,
	locale_native_name: Vec<String>,
}
impl LocalisedTextPool {
	pub fn add_locale(&mut self, native_name: &str) -> LocaleKey {
		let plt = Vec::default();
		self.per_locale_text.push(plt);
		self.locale_native_name.push(native_name.to_string());
		LocaleKey::from(self.per_locale_text.len() as u8)
	}
	pub fn get_text_from_key(&self, locale: LocaleKey, key: TextKey) -> &str {
		if key.is_valid() {
			let rk = self.per_locale_text[locale.index() as usize][key.index() as usize];
			if rk.is_valid() {
				self.raw_text.get_text_from_key(rk)
			} else {
				&"NOLOCALEKEY"
			}
		} else {
			&"NOTEXTKEY"
		}
	}
	pub fn get_text_from_text_key(&self, locale: LocaleKey, key: &str) -> &str {
		self.get_text_from_key(locale, self.locale_keys.get(key)
			.map(|&v| v)
			.unwrap_or_default())
	}
	pub fn get_key_as_string(&self, key: TextKey) -> String {
		if key.is_valid() {
			self.locale_keys.iter()
				.filter(|&(_, &v)| v == key)
				.map(|(k, _)| k.clone())
				.next()
				.unwrap_or_default()
		} else {
			"NOTEXTKEY".to_string()
		}
	}
	pub fn set_locale_keys(&mut self, ks: &std::collections::BTreeMap<String, TextKey>) {
		self.locale_keys = ks.clone();
		for plt in self.per_locale_text.iter_mut() {
			plt.resize(self.locale_keys.len(), RawTextKey::default());
		}
	}
	pub fn add_locale_key(&mut self, locale: LocaleKey, text: &str, code: &str) -> TextKey {
		let nk = self.raw_text.add_text(text);
		if let Some(&fid) = self.locale_keys.get(&code.to_string()) {
			// default behaviour is to override
			// TODO: we're wasting space when overriding old stuff!?, it's kept on the raw text pool! (bad)
			unsafe {
				*(*self.per_locale_text.get_unchecked_mut(locale.index() as usize)).get_unchecked_mut(fid.index() as usize) = nk;
			}
			fid
		} else {
			// Add new entry (obviously after we took the scenario ones)
			let v = TextKey::from((self.locale_keys.len() + 1) as u16);
			self.locale_keys.insert(code.to_string(), v);
			if v.index() as usize >= self.per_locale_text[locale.index() as usize].len() {
				let prealloc = 8192; //factor to preallocate (so we dont constantly resize)
				for plt in self.per_locale_text.iter_mut() {
					plt.resize(v.index() as usize + prealloc, RawTextKey::default());
				}
			}
			unsafe {
				*(*self.per_locale_text.get_unchecked_mut(locale.index() as usize)).get_unchecked_mut(v.index() as usize) = nk;
			}
			v
		}
	}
	pub fn optimize(&mut self) {
		// TODO merge raw text keys with matching names
		// TODO merge duplicate keys?
	}
}
impl Serializable for LocalisedTextPool {
	fn serialize(&self, s: &mut Serializer) {
		self.raw_text.serialize(s);
		self.per_locale_text.serialize(s);
		self.locale_keys.serialize(s);
		self.locale_native_name.serialize(s);
	}
	fn deserialize(&mut self, s: &mut Serializer) {
		self.raw_text.deserialize(s);
		self.per_locale_text.deserialize(s);
		self.locale_keys.deserialize(s);
		self.locale_native_name.deserialize(s);
	}
	fn serialized_size(&self) -> usize {
		self.raw_text.serialized_size()
		+ self.per_locale_text.serialized_size()
		+ self.locale_keys.serialized_size()
		+ self.locale_native_name.serialized_size()
	}
}


//
// Mutable text key
//
make_typed_id!(MutTextKey, u16);
#[derive(Default)]
pub struct MutTextPool(Vec<Option<String>>);
impl MutTextPool {
	pub fn add_text(&mut self, text: &str) -> MutTextKey {
		for e in self.0.iter_mut().enumerate() {
			if let None = e.1 {
				*e.1 = Some(text.to_string());
				return MutTextKey::from(e.0 as u16);
			}
		}
		self.0.push(Some(text.to_string()));
		MutTextKey::from(self.0.len() as u16)
	}
	pub fn remove_text(&mut self, k: MutTextKey) {
		self.0[k.index() as usize] = None;
	}
	pub fn get_text<'a>(&'a self, k: MutTextKey) -> Option<&'a str> {
		if k.is_valid() {
			self.0[k.index() as usize].as_ref().map(|x| x.as_str())
		} else {
			None
		}
	}
}
impl Serializable for MutTextPool {
	fn serialize(&self, s: &mut Serializer) {
		self.0.serialize(s);
	}
	fn deserialize(&mut self, s: &mut Serializer) {
		self.0.deserialize(s);
	}
	fn serialized_size(&self) -> usize {
		self.0.serialized_size()
	}
}