Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions matching/src/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ impl AsRef<str> for CssTendril {
}
}

unsafe impl Sync for CssTendril {}

/// A [`treedom::markup5ever::LocalName`] which implemented [`cssparser::ToCss`]
#[derive(Clone, PartialEq, Eq)]
pub struct CssLocalName(pub treedom::markup5ever::LocalName);
Expand Down
2 changes: 1 addition & 1 deletion matching/src/selectable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ impl<'a> CssNodeRef<'a> {

/// # Safety
/// `node` value must be element
pub unsafe fn new_unchecked(node: treedom::NodeRef<'a>) -> Self {
pub fn new_unchecked(node: treedom::NodeRef<'a>) -> Self {
Self(node)
}

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![forbid(unsafe_code)]

use pyo3::prelude::*;

extern crate matching;
Expand Down
5 changes: 1 addition & 4 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ enum Input {
///
/// This is very easy to use and allows you to stream input using `.process()` method; By this way
/// you are don't worry about memory usages of huge inputs.
#[pyo3::pyclass(name = "Parser", module = "markupever._rustlib", frozen)]
#[pyo3::pyclass(name = "Parser", module = "markupever._rustlib", frozen, unsendable)]
pub struct PyParser {
state: parking_lot::Mutex<ParserState>,
}
Expand Down Expand Up @@ -405,9 +405,6 @@ impl PyParser {
}
}

unsafe impl Send for PyParser {}
unsafe impl Sync for PyParser {}

#[pyo3::pyfunction]
#[pyo3(signature=(node, indent=4, include_self=true, is_html=None))]
pub fn serialize(
Expand Down
19 changes: 7 additions & 12 deletions src/select.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::sync::Arc;

struct PySelectInner {
traverse: crate::iter::PyTraverse,
expr: ::matching::ExpressionGroup,
Expand Down Expand Up @@ -45,7 +43,7 @@ impl Iterator for PySelectInner {
let node = tree.get(guard.id).unwrap();

if self.expr.matches(
unsafe { ::matching::CssNodeRef::new_unchecked(node) },
::matching::CssNodeRef::new_unchecked(node),
None,
&mut Default::default(),
) {
Expand All @@ -58,9 +56,9 @@ impl Iterator for PySelectInner {
}
}

#[pyo3::pyclass(name = "Select", module = "markupever._rustlib", frozen)]
#[pyo3::pyclass(name = "Select", module = "markupever._rustlib", unsendable)]
pub struct PySelect {
inner: Arc<parking_lot::Mutex<PySelectInner>>,
inner: PySelectInner,
}

#[pyo3::pymethods]
Expand All @@ -70,20 +68,17 @@ impl PySelect {
let node = node.as_node_guard().clone();

Ok(Self {
inner: Arc::new(parking_lot::Mutex::new(PySelectInner::new(
node, expression,
)?)),
inner: PySelectInner::new(node, expression)?,
})
}

fn __iter__(self_: pyo3::PyRef<'_, Self>) -> pyo3::PyRef<'_, Self> {
self_
}

pub fn __next__(&self) -> pyo3::PyResult<crate::nodes::NodeGuard> {
let mut lock = self.inner.lock();

lock.next()
pub fn __next__(&mut self) -> pyo3::PyResult<crate::nodes::NodeGuard> {
self.inner
.next()
.ok_or_else(|| pyo3::PyErr::new::<pyo3::exceptions::PyStopIteration, _>(()))
}
}
92 changes: 0 additions & 92 deletions treedom/src/atomic.rs
Original file line number Diff line number Diff line change
@@ -1,95 +1,3 @@
/// A synchronization primitive which can nominally be written to only once.
///
/// Uses [`parking_lot::Once`] instead of [`std::sync::Once`]:
/// - Lower memory usage.
/// - Not required to be 'static.
/// - Relaxed memory barriers in the fast path, which can significantly improve performance on some architectures.
/// - Efficient handling of micro-contention using adaptive spinning.
pub struct OnceLock<T> {
once: ::parking_lot::Once,
value: ::std::cell::UnsafeCell<::std::mem::MaybeUninit<T>>,
phantom: ::std::marker::PhantomData<T>,
}

impl<T> OnceLock<T> {
pub const fn new() -> OnceLock<T> {
OnceLock {
once: ::parking_lot::Once::new(),
value: ::std::cell::UnsafeCell::new(::std::mem::MaybeUninit::uninit()),
phantom: ::std::marker::PhantomData,
}
}

pub fn get(&self) -> Option<&T> {
if self.once.state().done() {
Some(unsafe { (*self.value.get()).assume_init_ref() })
} else {
None
}
}

#[cold]
fn initialize<F>(&self, f: F)
where
F: FnOnce() -> T,
{
let slot = &self.value;

self.once.call_once_force(|_| {
unsafe { (*slot.get()).write(f()) };
});
}

pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
{
if let Some(value) = self.get() {
return value;
}

self.initialize(f);

debug_assert!(self.once.state().done());

// SAFETY: The inner value has been initialized
unsafe { (*self.value.get()).assume_init_ref() }
}

pub fn take(&mut self) -> Option<T> {
if self.once.state().done() {
self.once = ::parking_lot::Once::new();
// SAFETY: `self.value` is initialized and contains a valid `T`.
// `self.once` is reset, so `is_initialized()` will be false again
// which prevents the value from being read twice.
unsafe { Some((*self.value.get()).assume_init_read()) }
} else {
None
}
}
}

impl<T: std::fmt::Debug> std::fmt::Debug for OnceLock<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.get() {
Some(val) => write!(f, "OnceLock<{:?}>", val),
None => write!(f, "OnceLock<uninialized>"),
}
}
}

impl<T> Clone for OnceLock<T> {
fn clone(&self) -> Self {
Self::new()
}
}

impl<T> Default for OnceLock<T> {
fn default() -> Self {
Self::new()
}
}

pub type AtomicTendril = tendril::Tendril<tendril::fmt::UTF8, tendril::Atomic>;

/// Makes a [`AtomicTendril`] from a non-atomic tendril
Expand Down
2 changes: 1 addition & 1 deletion treedom/src/dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ impl markup5ever::serialize::Serialize for Serializer<'_> {
where
S: markup5ever::serialize::Serializer,
{
let mut traverse = unsafe { self.dom.tree.get_unchecked(self.id).traverse() };
let mut traverse = self.dom.tree.get(self.id).unwrap().traverse();

if let markup5ever::serialize::TraversalScope::ChildrenOnly(_) = traversal_scope {
traverse.next();
Expand Down
11 changes: 6 additions & 5 deletions treedom/src/interface.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::atomic::{make_atomic_tendril, AtomicTendril, OnceLock};
use crate::atomic::{make_atomic_tendril, AtomicTendril};
use std::cell::OnceCell;
use tendril::StrTendril;

/// The root of a document
Expand Down Expand Up @@ -133,16 +134,16 @@ impl From<markup5ever::QualName> for AttributeKey {
#[derive(Clone)]
pub struct ElementAttributes {
list: Vec<(AttributeKey, AtomicTendril)>,
id: OnceLock<Option<AtomicTendril>>,
class: OnceLock<Vec<markup5ever::LocalName>>,
id: OnceCell<Option<AtomicTendril>>,
class: OnceCell<Vec<markup5ever::LocalName>>,
}

impl ElementAttributes {
pub fn new(list: Vec<(AttributeKey, AtomicTendril)>) -> Self {
Self {
list,
id: OnceLock::new(),
class: OnceLock::new(),
id: OnceCell::new(),
class: OnceCell::new(),
}
}

Expand Down
Loading