summaryrefslogtreecommitdiffstats
path: root/toolkit/components/xulstore/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/xulstore/src/lib.rs')
-rw-r--r--toolkit/components/xulstore/src/lib.rs223
1 files changed, 223 insertions, 0 deletions
diff --git a/toolkit/components/xulstore/src/lib.rs b/toolkit/components/xulstore/src/lib.rs
new file mode 100644
index 0000000000..fdeec0f6c8
--- /dev/null
+++ b/toolkit/components/xulstore/src/lib.rs
@@ -0,0 +1,223 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+extern crate crossbeam_utils;
+#[macro_use]
+extern crate cstr;
+extern crate libc;
+#[macro_use]
+extern crate log;
+extern crate moz_task;
+extern crate nserror;
+extern crate nsstring;
+extern crate once_cell;
+extern crate rkv;
+extern crate serde_json;
+extern crate tempfile;
+extern crate thiserror;
+#[macro_use]
+extern crate xpcom;
+
+mod error;
+mod ffi;
+mod iter;
+mod persist;
+mod statics;
+
+use crate::{
+ error::{XULStoreError, XULStoreResult},
+ iter::XULStoreIterator,
+ persist::{flush_writes, persist},
+ statics::DATA_CACHE,
+};
+use nsstring::nsAString;
+use std::collections::btree_map::Entry;
+use std::fmt::Display;
+
+const SEPARATOR: char = '\u{0009}';
+
+pub(crate) fn make_key(doc: &impl Display, id: &impl Display, attr: &impl Display) -> String {
+ format!("{}{}{}{}{}", doc, SEPARATOR, id, SEPARATOR, attr)
+}
+
+pub(crate) fn set_value(
+ doc: &nsAString,
+ id: &nsAString,
+ attr: &nsAString,
+ value: &nsAString,
+) -> XULStoreResult<()> {
+ debug!("XULStore set value: {} {} {} {}", doc, id, attr, value);
+
+ // bug 319846 -- don't save really long attributes or values.
+ if id.len() > 512 || attr.len() > 512 {
+ return Err(XULStoreError::IdAttrNameTooLong);
+ }
+
+ let value = if value.len() > 4096 {
+ warn!("XULStore: truncating long attribute value");
+ String::from_utf16(&value[0..4096])?
+ } else {
+ String::from_utf16(value)?
+ };
+
+ let mut cache_guard = DATA_CACHE.lock()?;
+ let data = match cache_guard.as_mut() {
+ Some(data) => data,
+ None => return Ok(()),
+ };
+ data.entry(doc.to_string())
+ .or_default()
+ .entry(id.to_string())
+ .or_default()
+ .insert(attr.to_string(), value.clone());
+
+ persist(make_key(doc, id, attr), Some(value))?;
+
+ Ok(())
+}
+
+pub(crate) fn has_value(doc: &nsAString, id: &nsAString, attr: &nsAString) -> XULStoreResult<bool> {
+ debug!("XULStore has value: {} {} {}", doc, id, attr);
+
+ let cache_guard = DATA_CACHE.lock()?;
+ let data = match cache_guard.as_ref() {
+ Some(data) => data,
+ None => return Ok(false),
+ };
+
+ match data.get(&doc.to_string()) {
+ Some(ids) => match ids.get(&id.to_string()) {
+ Some(attrs) => Ok(attrs.contains_key(&attr.to_string())),
+ None => Ok(false),
+ },
+ None => Ok(false),
+ }
+}
+
+pub(crate) fn get_value(
+ doc: &nsAString,
+ id: &nsAString,
+ attr: &nsAString,
+) -> XULStoreResult<String> {
+ debug!("XULStore get value {} {} {}", doc, id, attr);
+
+ let cache_guard = DATA_CACHE.lock()?;
+ let data = match cache_guard.as_ref() {
+ Some(data) => data,
+ None => return Ok(String::new()),
+ };
+
+ match data.get(&doc.to_string()) {
+ Some(ids) => match ids.get(&id.to_string()) {
+ Some(attrs) => match attrs.get(&attr.to_string()) {
+ Some(value) => Ok(value.clone()),
+ None => Ok(String::new()),
+ },
+ None => Ok(String::new()),
+ },
+ None => Ok(String::new()),
+ }
+}
+
+pub(crate) fn remove_value(
+ doc: &nsAString,
+ id: &nsAString,
+ attr: &nsAString,
+) -> XULStoreResult<()> {
+ debug!("XULStore remove value {} {} {}", doc, id, attr);
+
+ let mut cache_guard = DATA_CACHE.lock()?;
+ let data = match cache_guard.as_mut() {
+ Some(data) => data,
+ None => return Ok(()),
+ };
+
+ let mut ids_empty = false;
+ if let Some(ids) = data.get_mut(&doc.to_string()) {
+ let mut attrs_empty = false;
+ if let Some(attrs) = ids.get_mut(&id.to_string()) {
+ attrs.remove(&attr.to_string());
+ if attrs.is_empty() {
+ attrs_empty = true;
+ }
+ }
+ if attrs_empty {
+ ids.remove(&id.to_string());
+ if ids.is_empty() {
+ ids_empty = true;
+ }
+ }
+ };
+ if ids_empty {
+ data.remove(&doc.to_string());
+ }
+
+ persist(make_key(doc, id, attr), None)?;
+
+ Ok(())
+}
+
+pub(crate) fn remove_document(doc: &nsAString) -> XULStoreResult<()> {
+ debug!("XULStore remove document {}", doc);
+
+ let mut cache_guard = DATA_CACHE.lock()?;
+ let data = match cache_guard.as_mut() {
+ Some(data) => data,
+ None => return Ok(()),
+ };
+
+ if let Entry::Occupied(entry) = data.entry(doc.to_string()) {
+ for (id, attrs) in entry.get() {
+ for attr in attrs.keys() {
+ persist(make_key(entry.key(), id, attr), None)?;
+ }
+ }
+ entry.remove_entry();
+ }
+
+ Ok(())
+}
+
+pub(crate) fn get_ids(doc: &nsAString) -> XULStoreResult<XULStoreIterator> {
+ debug!("XULStore get IDs for {}", doc);
+
+ let cache_guard = DATA_CACHE.lock()?;
+ let data = match cache_guard.as_ref() {
+ Some(data) => data,
+ None => return Ok(XULStoreIterator::new(vec![].into_iter())),
+ };
+
+ match data.get(&doc.to_string()) {
+ Some(ids) => {
+ let ids: Vec<String> = ids.keys().cloned().collect();
+ Ok(XULStoreIterator::new(ids.into_iter()))
+ }
+ None => Ok(XULStoreIterator::new(vec![].into_iter())),
+ }
+}
+
+pub(crate) fn get_attrs(doc: &nsAString, id: &nsAString) -> XULStoreResult<XULStoreIterator> {
+ debug!("XULStore get attrs for doc, ID: {} {}", doc, id);
+
+ let cache_guard = DATA_CACHE.lock()?;
+ let data = match cache_guard.as_ref() {
+ Some(data) => data,
+ None => return Ok(XULStoreIterator::new(vec![].into_iter())),
+ };
+
+ match data.get(&doc.to_string()) {
+ Some(ids) => match ids.get(&id.to_string()) {
+ Some(attrs) => {
+ let attrs: Vec<String> = attrs.keys().cloned().collect();
+ Ok(XULStoreIterator::new(attrs.into_iter()))
+ }
+ None => Ok(XULStoreIterator::new(vec![].into_iter())),
+ },
+ None => Ok(XULStoreIterator::new(vec![].into_iter())),
+ }
+}
+
+pub(crate) fn shutdown() -> XULStoreResult<()> {
+ flush_writes()
+}