selma 0.2.2 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -76,7 +76,7 @@ attributes: {
76
76
 
77
77
  # URL handling protocols to allow in specific attributes. By default, no
78
78
  # protocols are allowed. Use :relative in place of a protocol if you want
79
- # to allow relative URLs sans protocol.
79
+ # to allow relative URLs sans protocol. Set to `:all` to allow any protocol.
80
80
  protocols: {
81
81
  "a" => { "href" => ["http", "https", "mailto", :relative] },
82
82
  "img" => { "href" => ["http", "https"] },
@@ -103,7 +103,11 @@ Here's an example which rewrites the `href` attribute on `a` and the `src` attri
103
103
 
104
104
  ```ruby
105
105
  class MatchAttribute
106
- SELECTOR = Selma::Selector(match_element: %(a[href^="http:"], img[src^="http:"]"))
106
+ SELECTOR = Selma::Selector.new(match_element: %(a[href^="http:"], img[src^="http:"]"))
107
+
108
+ def selector
109
+ SELECTOR
110
+ end
107
111
 
108
112
  def handle_element(element)
109
113
  if element.tag_name == "a"
@@ -130,7 +134,6 @@ The `Selma::Selector` object has three possible kwargs:
130
134
  Here's an example for `handle_text_chunk` which changes strings in various elements which are _not_ `pre` or `code`:
131
135
 
132
136
  ```ruby
133
-
134
137
  class MatchText
135
138
  SELECTOR = Selma::Selector.new(match_text_within: "*", ignore_text_within: ["pre", "code"])
136
139
 
@@ -176,40 +179,145 @@ The `element` argument in `handle_element` has the following methods:
176
179
  - `after(content, as: content_type)`: Inserts `content` after the text. `content_type` is either `:text` or `:html` and determines how the content will be applied.
177
180
  - `replace(content, as: content_type)`: Replaces the text node with `content`. `content_type` is either `:text` or `:html` and determines how the content will be applied.
178
181
 
182
+ ## Security
183
+
184
+ Theoretically, a malicious user can provide a very large document for processing, which can exhaust the memory of the host machine. To set a limit on how much string content is processed at once, you can provide `memory` options:
185
+
186
+ ```ruby
187
+ Selma::Rewriter.new(options: { memory: { max_allowed_memory_usage: 1_000_000 } }) # ~1MB
188
+ ```
189
+
190
+ The structure of the `memory` options looks like this:
191
+
192
+ ```ruby
193
+ {
194
+ memory: {
195
+ max_allowed_memory_usage: 1000,
196
+ preallocated_parsing_buffer_size: 100,
197
+ }
198
+ }
199
+ ```
200
+
201
+ Note that `preallocated_parsing_buffer_size` must always be less than `max_allowed_memory_usage`. See [the`lol_html` project documentation](https://docs.rs/lol_html/1.2.1/lol_html/struct.MemorySettings.html) to learn more about the default values.
202
+
179
203
  ## Benchmarks
180
204
 
205
+ When `bundle exec rake benchmark`, two different benchmarks are calculated. Here are those results on my machine.
206
+
207
+ ### Benchmarks for just the sanitization process
208
+
209
+ Comparing Selma against popular Ruby sanitization gems:
210
+
211
+ <!-- prettier-ignore-start -->
212
+ <details>
213
+ <pre>
214
+ input size = 25309 bytes, 0.03 MB
215
+
216
+ ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
217
+ Warming up --------------------------------------
218
+ sanitize-sm 15.000 i/100ms
219
+ selma-sm 127.000 i/100ms
220
+ Calculating -------------------------------------
221
+ sanitize-sm 157.643 (± 1.9%) i/s - 4.740k in 30.077172s
222
+ selma-sm 1.278k (± 1.5%) i/s - 38.354k in 30.019722s
223
+
224
+ Comparison:
225
+ selma-sm: 1277.9 i/s
226
+ sanitize-sm: 157.6 i/s - 8.11x slower
227
+
228
+ input size = 86686 bytes, 0.09 MB
229
+
230
+ ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
231
+ Warming up --------------------------------------
232
+ sanitize-md 4.000 i/100ms
233
+ selma-md 33.000 i/100ms
234
+ Calculating -------------------------------------
235
+ sanitize-md 40.034 (± 5.0%) i/s - 1.200k in 30.043322s
236
+ selma-md 332.959 (± 2.1%) i/s - 9.999k in 30.045733s
237
+
238
+ Comparison:
239
+ selma-md: 333.0 i/s
240
+ sanitize-md: 40.0 i/s - 8.32x slower
241
+
242
+ input size = 7172510 bytes, 7.17 MB
243
+
244
+ ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
245
+ Warming up --------------------------------------
246
+ sanitize-lg 1.000 i/100ms
247
+ selma-lg 1.000 i/100ms
248
+ Calculating -------------------------------------
249
+ sanitize-lg 0.141 (± 0.0%) i/s - 5.000 in 35.426127s
250
+ selma-lg 3.963 (± 0.0%) i/s - 119.000 in 30.037386s
251
+
252
+ Comparison:
253
+ selma-lg: 4.0 i/s
254
+ sanitize-lg: 0.1 i/s - 28.03x slower
255
+
256
+ </pre>
257
+ </details>
258
+ <!-- prettier-ignore-end -->
259
+
260
+ ### Benchmarks for just the rewriting process
261
+
262
+ Comparing Selma against popular Ruby HTML parsing gems:
263
+
264
+ <!-- prettier-ignore-start -->
181
265
  <details>
182
266
  <pre>
183
- ruby test/benchmark.rb
184
- ruby test/benchmark.rb
267
+ input size = 25309 bytes, 0.03 MB
268
+
269
+ ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
185
270
  Warming up --------------------------------------
186
- sanitize-document-huge
187
- 1.000 i/100ms
188
- selma-document-huge 1.000 i/100ms
271
+ nokogiri-sm 79.000 i/100ms
272
+ nokolexbor-sm 295.000 i/100ms
273
+ selma-sm 237.000 i/100ms
189
274
  Calculating -------------------------------------
190
- sanitize-document-huge
191
- 0.257 0.0%) i/s - 2.000 in 7.783398s
192
- selma-document-huge 4.602 0.0%) i/s - 23.000 in 5.002870s
275
+ nokogiri-sm 800.531 (± 2.2%) i/s - 24.016k in 30.016056s
276
+ nokolexbor-sm 3.033k 3.6%) i/s - 91.155k in 30.094884s
277
+ selma-sm 2.386k 1.6%) i/s - 71.574k in 30.001701s
278
+
279
+ Comparison:
280
+ nokolexbor-sm: 3033.1 i/s
281
+ selma-sm: 2386.3 i/s - 1.27x slower
282
+ nokogiri-sm: 800.5 i/s - 3.79x slower
283
+
284
+ input size = 86686 bytes, 0.09 MB
285
+
286
+ ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
193
287
  Warming up --------------------------------------
194
- sanitize-document-medium
195
- 2.000 i/100ms
196
- selma-document-medium
197
- 22.000 i/100ms
288
+ nokogiri-md 8.000 i/100ms
289
+ nokolexbor-md 43.000 i/100ms
290
+ selma-md 38.000 i/100ms
198
291
  Calculating -------------------------------------
199
- sanitize-document-medium
200
- 28.676 3.5%) i/s - 144.000 in 5.024669s
201
- selma-document-medium
202
- 121.500 (±22.2%) i/s - 594.000 in 5.135410s
292
+ nokogiri-md 85.013 (± 8.2%) i/s - 2.024k in 52.257472s
293
+ nokolexbor-md 416.074 11.1%) i/s - 12.341k in 30.111613s
294
+ selma-md 361.471 (± 4.7%) i/s - 10.830k in 30.033997s
295
+
296
+ Comparison:
297
+ nokolexbor-md: 416.1 i/s
298
+ selma-md: 361.5 i/s - same-ish: difference falls within error
299
+ nokogiri-md: 85.0 i/s - 4.89x slower
300
+
301
+ input size = 7172510 bytes, 7.17 MB
302
+
303
+ ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
203
304
  Warming up --------------------------------------
204
- sanitize-document-small
205
- 10.000 i/100ms
206
- selma-document-small 20.000 i/100ms
305
+ nokogiri-lg 1.000 i/100ms
306
+ nokolexbor-lg 1.000 i/100ms
307
+ selma-lg 1.000 i/100ms
207
308
  Calculating -------------------------------------
208
- sanitize-document-small
209
- 107.280 (± 0.9%) i/s - 540.000 in 5.033850s
210
- selma-document-small 118.867 31.1%) i/s - 540.000 in 5.080726s
309
+ nokogiri-lg 0.805 (± 0.0%) i/s - 25.000 in 31.148730s
310
+ nokolexbor-lg 2.194 (± 0.0%) i/s - 66.000 in 30.278108s
311
+ selma-lg 5.541 0.0%) i/s - 166.000 in 30.037197s
312
+
313
+ Comparison:
314
+ selma-lg: 5.5 i/s
315
+ nokolexbor-lg: 2.2 i/s - 2.53x slower
316
+ nokogiri-lg: 0.8 i/s - 6.88x slower
317
+
211
318
  </pre>
212
319
  </details>
320
+ <!-- prettier-ignore-end -->
213
321
 
214
322
  ## Contributing
215
323
 
data/ext/selma/Cargo.toml CHANGED
@@ -6,10 +6,13 @@ rust-version = "1.75.0"
6
6
  publish = false
7
7
 
8
8
  [dependencies]
9
- enum-iterator = "1.4"
9
+ enum-iterator = "2.1"
10
10
  escapist = "0.0.2"
11
- magnus = "0.6"
12
- lol_html = "1.2"
11
+ magnus = { version = "0.7", features = ["rb-sys"] }
12
+ rb-sys = { version = "*", default-features = false, features = [
13
+ "stable-api-compiled-fallback",
14
+ ] }
15
+ lol_html = "2.0"
13
16
 
14
17
  [lib]
15
18
  name = "selma"
@@ -1,3 +1,5 @@
1
+ use std::cell::RefCell;
2
+
1
3
  use crate::native_ref_wrap::NativeRefWrap;
2
4
  use lol_html::html_content::Element;
3
5
  use magnus::{exception, method, Error, Module, RArray, RClass, RHash, RString, Value};
@@ -8,16 +10,14 @@ struct HTMLElement {
8
10
  }
9
11
 
10
12
  #[magnus::wrap(class = "Selma::HTML::Element")]
11
- pub struct SelmaHTMLElement(std::cell::RefCell<HTMLElement>);
13
+ pub struct SelmaHTMLElement(RefCell<HTMLElement>);
12
14
 
13
15
  /// SAFETY: This is safe because we only access this data when the GVL is held.
14
16
  unsafe impl Send for SelmaHTMLElement {}
15
17
 
16
18
  impl SelmaHTMLElement {
17
- pub fn new(element: &mut Element, ancestors: &[String]) -> Self {
18
- let (ref_wrap, _anchor) = NativeRefWrap::wrap_mut(element);
19
-
20
- Self(std::cell::RefCell::new(HTMLElement {
19
+ pub fn new(ref_wrap: NativeRefWrap<Element<'static, 'static>>, ancestors: &[String]) -> Self {
20
+ Self(RefCell::new(HTMLElement {
21
21
  element: ref_wrap,
22
22
  ancestors: ancestors.to_owned(),
23
23
  }))
@@ -26,13 +26,12 @@ impl SelmaHTMLElement {
26
26
  fn tag_name(&self) -> Result<String, Error> {
27
27
  let binding = self.0.borrow();
28
28
 
29
- if let Ok(e) = binding.element.get() {
30
- Ok(e.tag_name())
31
- } else {
32
- Err(Error::new(
29
+ match binding.element.get() {
30
+ Ok(e) => Ok(e.tag_name().to_string()),
31
+ Err(_) => Err(Error::new(
33
32
  exception::runtime_error(),
34
33
  "`tag_name` is not available",
35
- ))
34
+ )),
36
35
  }
37
36
  }
38
37
 
@@ -119,11 +118,13 @@ impl SelmaHTMLElement {
119
118
  .iter()
120
119
  .for_each(|attr| match hash.aset(attr.name(), attr.value()) {
121
120
  Ok(_) => {}
122
- Err(err) => Err(Error::new(
123
- exception::runtime_error(),
124
- format!("AttributeNameError: {err:?}"),
125
- ))
126
- .unwrap(),
121
+ Err(err) => panic!(
122
+ "{:?}",
123
+ Error::new(
124
+ exception::runtime_error(),
125
+ format!("AttributeNameError: {err:?}"),
126
+ )
127
+ ),
127
128
  });
128
129
  }
129
130
  Ok(hash)
@@ -139,7 +140,10 @@ impl SelmaHTMLElement {
139
140
  .for_each(|ancestor| match array.push(RString::new(ancestor)) {
140
141
  Ok(_) => {}
141
142
  Err(err) => {
142
- Err(Error::new(exception::runtime_error(), format!("{err:?}"))).unwrap()
143
+ panic!(
144
+ "{:?}",
145
+ Error::new(exception::runtime_error(), format!("{err:?}"))
146
+ )
143
147
  }
144
148
  });
145
149
 
@@ -224,24 +228,25 @@ impl SelmaHTMLElement {
224
228
  }
225
229
  }
226
230
 
227
- fn remove_and_keep_content(&self) {
228
- let mut binding = self.0.borrow_mut();
229
-
230
- if let Ok(e) = binding.element.get_mut() {
231
- e.remove_and_keep_content()
232
- }
231
+ fn remove_and_keep_content(&self) -> Result<(), Error> {
232
+ self.0
233
+ .borrow_mut()
234
+ .element
235
+ .get_mut()
236
+ .unwrap()
237
+ .remove_and_keep_content();
238
+ Ok(())
233
239
  }
234
240
 
235
241
  fn is_removed(&self) -> Result<bool, Error> {
236
242
  let binding = self.0.borrow();
237
243
 
238
- if let Ok(e) = binding.element.get() {
239
- Ok(e.removed())
240
- } else {
241
- Err(Error::new(
244
+ match binding.element.get() {
245
+ Ok(e) => Ok(e.removed()),
246
+ Err(_) => Err(Error::new(
242
247
  exception::runtime_error(),
243
248
  "`is_removed` is not available",
244
- ))
249
+ )),
245
250
  }
246
251
  }
247
252
  }
@@ -1,3 +1,5 @@
1
+ use std::cell::RefCell;
2
+
1
3
  use crate::native_ref_wrap::NativeRefWrap;
2
4
  use lol_html::html_content::EndTag;
3
5
  use magnus::{method, Error, Module, RClass};
@@ -7,16 +9,14 @@ struct HTMLEndTag {
7
9
  }
8
10
 
9
11
  #[magnus::wrap(class = "Selma::HTML::EndTag")]
10
- pub struct SelmaHTMLEndTag(std::cell::RefCell<HTMLEndTag>);
12
+ pub struct SelmaHTMLEndTag(RefCell<HTMLEndTag>);
11
13
 
12
14
  /// SAFETY: This is safe because we only access this data when the GVL is held.
13
15
  unsafe impl Send for SelmaHTMLEndTag {}
14
16
 
15
17
  impl SelmaHTMLEndTag {
16
- pub fn new(end_tag: &mut EndTag) -> Self {
17
- let (ref_wrap, _anchor) = NativeRefWrap::wrap(end_tag);
18
-
19
- Self(std::cell::RefCell::new(HTMLEndTag { end_tag: ref_wrap }))
18
+ pub fn new(ref_wrap: NativeRefWrap<EndTag<'static>>) -> Self {
19
+ Self(RefCell::new(HTMLEndTag { end_tag: ref_wrap }))
20
20
  }
21
21
 
22
22
  fn tag_name(&self) -> String {
@@ -1,23 +1,44 @@
1
+ use std::cell::RefCell;
2
+
1
3
  use crate::native_ref_wrap::NativeRefWrap;
2
4
  use lol_html::html_content::{TextChunk, TextType};
3
5
  use magnus::{exception, method, Error, Module, RClass, Symbol, Value};
4
6
 
5
7
  struct HTMLTextChunk {
6
8
  text_chunk: NativeRefWrap<TextChunk<'static>>,
9
+ buffer: String,
10
+ }
11
+
12
+ macro_rules! clone_buffer_if_not_empty {
13
+ ($binding:expr, $buffer:expr) => {
14
+ if !$binding.buffer.is_empty() {
15
+ $buffer.clone_from(&$binding.buffer);
16
+ }
17
+ };
18
+ }
19
+
20
+ // if this is the first time we're processing this text chunk (buffer is empty),
21
+ // we carry on. Otherwise, we need to use the buffer text, not the text chunk,
22
+ // because lol-html is not designed in such a way to keep track of text chunks.
23
+ macro_rules! set_text_chunk_to_buffer {
24
+ ($text_chunk:expr, $buffer:expr) => {
25
+ if !$buffer.is_empty() {
26
+ $text_chunk.set_str($buffer);
27
+ }
28
+ };
7
29
  }
8
30
 
9
31
  #[magnus::wrap(class = "Selma::HTML::TextChunk")]
10
- pub struct SelmaHTMLTextChunk(std::cell::RefCell<HTMLTextChunk>);
32
+ pub struct SelmaHTMLTextChunk(RefCell<HTMLTextChunk>);
11
33
 
12
34
  /// SAFETY: This is safe because we only access this data when the GVL is held.
13
35
  unsafe impl Send for SelmaHTMLTextChunk {}
14
36
 
15
37
  impl SelmaHTMLTextChunk {
16
- pub fn new(text_chunk: &mut TextChunk) -> Self {
17
- let (ref_wrap, _anchor) = NativeRefWrap::wrap_mut(text_chunk);
18
-
19
- Self(std::cell::RefCell::new(HTMLTextChunk {
38
+ pub fn new(ref_wrap: NativeRefWrap<TextChunk<'static>>) -> Self {
39
+ Self(RefCell::new(HTMLTextChunk {
20
40
  text_chunk: ref_wrap,
41
+ buffer: String::new(),
21
42
  }))
22
43
  }
23
44
 
@@ -54,7 +75,19 @@ impl SelmaHTMLTextChunk {
54
75
  }
55
76
  }
56
77
 
57
- fn before(&self, args: &[Value]) -> Result<(), Error> {
78
+ fn is_removed(&self) -> Result<bool, Error> {
79
+ let binding = self.0.borrow();
80
+
81
+ match binding.text_chunk.get() {
82
+ Ok(tc) => Ok(tc.removed()),
83
+ Err(_) => Err(Error::new(
84
+ exception::runtime_error(),
85
+ "`is_removed` is not available",
86
+ )),
87
+ }
88
+ }
89
+
90
+ fn before(&self, args: &[Value]) -> Result<String, Error> {
58
91
  let mut binding = self.0.borrow_mut();
59
92
  let text_chunk = binding.text_chunk.get_mut().unwrap();
60
93
 
@@ -65,10 +98,10 @@ impl SelmaHTMLTextChunk {
65
98
 
66
99
  text_chunk.before(&text_str, content_type);
67
100
 
68
- Ok(())
101
+ Ok(text_chunk.as_str().to_string())
69
102
  }
70
103
 
71
- fn after(&self, args: &[Value]) -> Result<(), Error> {
104
+ fn after(&self, args: &[Value]) -> Result<String, Error> {
72
105
  let mut binding = self.0.borrow_mut();
73
106
  let text_chunk = binding.text_chunk.get_mut().unwrap();
74
107
 
@@ -79,21 +112,30 @@ impl SelmaHTMLTextChunk {
79
112
 
80
113
  text_chunk.after(&text_str, content_type);
81
114
 
82
- Ok(())
115
+ Ok(text_chunk.as_str().to_string())
83
116
  }
84
117
 
85
- fn replace(&self, args: &[Value]) -> Result<(), Error> {
118
+ fn replace(&self, args: &[Value]) -> Result<String, Error> {
86
119
  let mut binding = self.0.borrow_mut();
120
+ let mut buffer = String::new();
121
+
122
+ clone_buffer_if_not_empty!(binding, buffer);
123
+
87
124
  let text_chunk = binding.text_chunk.get_mut().unwrap();
88
125
 
126
+ set_text_chunk_to_buffer!(text_chunk, buffer);
127
+
89
128
  let (text_str, content_type) = match crate::scan_text_args(args) {
90
129
  Ok((text_str, content_type)) => (text_str, content_type),
91
130
  Err(err) => return Err(err),
92
131
  };
93
-
94
132
  text_chunk.replace(&text_str, content_type);
95
133
 
96
- Ok(())
134
+ text_chunk.set_str(text_str.clone());
135
+
136
+ binding.buffer = text_chunk.as_str().to_string();
137
+
138
+ Ok(text_str)
97
139
  }
98
140
  }
99
141
 
@@ -108,6 +150,7 @@ pub fn init(c_html: RClass) -> Result<(), Error> {
108
150
  c_text_chunk.define_method("before", method!(SelmaHTMLTextChunk::before, -1))?;
109
151
  c_text_chunk.define_method("after", method!(SelmaHTMLTextChunk::after, -1))?;
110
152
  c_text_chunk.define_method("replace", method!(SelmaHTMLTextChunk::replace, -1))?;
153
+ c_text_chunk.define_method("removed?", method!(SelmaHTMLTextChunk::is_removed, 0))?;
111
154
 
112
155
  Ok(())
113
156
  }
@@ -1,15 +1,18 @@
1
- use std::{cell::Cell, marker::PhantomData, rc::Rc};
1
+ use std::{
2
+ marker::PhantomData,
3
+ sync::{Arc, Mutex},
4
+ };
2
5
 
3
- // NOTE: My Rust isn't good enough to know what any of this does,
4
- // but it was taken from https://github.com/cloudflare/lol-html/blob/1a1ab2e2bf896f815fe8888ed78ccdf46d7c6b85/js-api/src/lib.rs#LL38
6
+ // NOTE: this was inspired from
7
+ // https://github.com/worker-tools/html-rewriter-wasm/blob/92bafdfa34c809c37036f57cb282184cada3bbc9/src/handlers.rs
5
8
 
6
9
  pub struct Anchor<'r> {
7
- poisoned: Rc<Cell<bool>>,
10
+ poisoned: Arc<Mutex<bool>>,
8
11
  lifetime: PhantomData<&'r mut ()>,
9
12
  }
10
13
 
11
14
  impl<'r> Anchor<'r> {
12
- pub fn new(poisoned: Rc<Cell<bool>>) -> Self {
15
+ pub fn new(poisoned: Arc<Mutex<bool>>) -> Self {
13
16
  Anchor {
14
17
  poisoned,
15
18
  lifetime: PhantomData,
@@ -17,44 +20,46 @@ impl<'r> Anchor<'r> {
17
20
  }
18
21
  }
19
22
 
20
- // impl Drop for Anchor<'_> {
21
- // fn drop(&mut self) {
22
- // self.poisoned.replace(true);
23
- // }
24
- // }
23
+ impl Drop for Anchor<'_> {
24
+ fn drop(&mut self) {
25
+ *self.poisoned.lock().unwrap() = true;
26
+ }
27
+ }
25
28
 
26
- // NOTE: wasm_bindgen doesn't allow structures with lifetimes. To workaround that
27
- // we create a wrapper that erases all the lifetime information from the inner reference
29
+ // NOTE: So far as I understand it, there's no great way to work between lol_html's lifetimes and FFI.
30
+ // To work around that, we create a wrapper that erases all the lifetime information from the inner reference
28
31
  // and provides an anchor object that keeps track of the lifetime in the runtime.
29
32
  //
30
33
  // When anchor goes out of scope, wrapper becomes poisoned and any attempt to get inner
31
34
  // object results in exception.
35
+ #[derive(Clone)]
32
36
  pub struct NativeRefWrap<R> {
33
37
  inner_ptr: *mut R,
34
- poisoned: Rc<Cell<bool>>,
38
+ poisoned: Arc<Mutex<bool>>,
35
39
  }
36
40
 
37
41
  impl<R> NativeRefWrap<R> {
38
- pub fn wrap<I>(inner: &I) -> (Self, Anchor) {
42
+ pub fn wrap<I>(inner: &mut I) -> (Self, Anchor) {
39
43
  let wrap = NativeRefWrap {
40
- inner_ptr: inner as *const I as *mut R,
41
- poisoned: Rc::new(Cell::new(false)),
44
+ inner_ptr: inner as *mut I as *mut R,
45
+ poisoned: Arc::new(Mutex::new(false)),
42
46
  };
43
47
 
44
- let anchor = Anchor::new(Rc::clone(&wrap.poisoned));
48
+ let anchor = Anchor::new(Arc::clone(&wrap.poisoned));
45
49
 
46
50
  (wrap, anchor)
47
51
  }
48
52
 
49
- pub fn wrap_mut<I>(inner: &mut I) -> (Self, Anchor) {
50
- let wrap = NativeRefWrap {
51
- inner_ptr: inner as *mut I as *mut R,
52
- poisoned: Rc::new(Cell::new(false)),
53
- };
54
-
55
- let anchor = Anchor::new(Rc::clone(&wrap.poisoned));
53
+ fn assert_not_poisoned(&self) -> Result<(), &'static str> {
54
+ if self.is_poisoned() {
55
+ Err("The object has been freed and can't be used anymore.")
56
+ } else {
57
+ Ok(())
58
+ }
59
+ }
56
60
 
57
- (wrap, anchor)
61
+ pub fn is_poisoned(&self) -> bool {
62
+ *self.poisoned.lock().unwrap()
58
63
  }
59
64
 
60
65
  pub fn get(&self) -> Result<&R, &'static str> {
@@ -68,12 +73,4 @@ impl<R> NativeRefWrap<R> {
68
73
 
69
74
  Ok(unsafe { self.inner_ptr.as_mut() }.unwrap())
70
75
  }
71
-
72
- fn assert_not_poisoned(&self) -> Result<(), &'static str> {
73
- if self.poisoned.get() {
74
- Err("The object has been freed and can't be used anymore.")
75
- } else {
76
- Ok(())
77
- }
78
- }
79
76
  }