rusteerio 1.1.3 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Cargo.lock CHANGED
@@ -720,7 +720,7 @@ dependencies = [
720
720
 
721
721
  [[package]]
722
722
  name = "rusteerio"
723
- version = "1.1.3"
723
+ version = "1.1.4"
724
724
  dependencies = [
725
725
  "ego-tree",
726
726
  "html5ever",
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rusteerio"
3
- version = "1.1.3"
3
+ version = "1.1.4"
4
4
  edition = "2021"
5
5
  description = "Rust HTML5 parser and CSS selector engine for the rusteerio Node.js package"
6
6
  license = "MIT"
@@ -4,7 +4,7 @@ This file preserves third-party license and attribution notices for the Rust nat
4
4
 
5
5
  Generated by `node scripts/generate-notices.mjs` from `cargo metadata --locked`, crate license files, and the pinned supplemental sources recorded in `scripts/notices/sources.json`. It includes build dependencies and dependencies for all target platforms; a particular binary may use a subset. Identical notice texts are reproduced once and referenced below. The html5ever, markup5ever and html5gum crates contain local observer-hook and opt-in UTF-16 transport patches; their complete patched source and original notices are included in `native/vendor`. Other dependency source files are unmodified.
6
6
 
7
- Cargo.lock SHA-256: `b4e1749e9f6c31b8ecd8cb108285e93f365a4906f4ef04e8239959f549b7de1a`.
7
+ Cargo.lock SHA-256: `a2ff4645db9ecfd1110ed1ae3af80a43ffaf87ab3c192ce24bee23065684331c`.
8
8
 
9
9
  ## Source availability
10
10
 
@@ -49,41 +49,128 @@ const KINDS: [&str; 8] = [
49
49
  const SHORT_NAME_SLOTS: usize = 256;
50
50
 
51
51
  pub(crate) struct NodeSnapshot {
52
+ /// Consecutive ranges of nodes, encoded separately (in parallel for
53
+ /// large trees) with references local to each chunk.
54
+ chunks: Vec<Chunk>,
55
+ }
56
+
57
+ struct Chunk {
52
58
  is_encoded: bool,
53
59
  nodes: Vec<u32>,
54
60
  children: Vec<u32>,
55
61
  attributes: Vec<u32>,
56
62
  strings: Vec<u32>,
57
63
  text: String,
64
+ units: u32,
58
65
  }
59
66
 
60
67
  impl NodeSnapshot {
61
68
  pub(crate) fn into_js(self, env: &Env) -> Result<JsObject> {
62
- let parts = [&self.nodes, &self.children, &self.attributes, &self.strings];
63
- let lengths = parts.map(|part| part.len() as u32);
69
+ let total = |part: fn(&Chunk) -> usize| self.chunks.iter().map(part).sum::<usize>();
70
+ let lengths = [
71
+ total(|chunk| chunk.nodes.len()),
72
+ total(|chunk| chunk.children.len()),
73
+ total(|chunk| chunk.attributes.len()),
74
+ total(|chunk| chunk.strings.len()),
75
+ ];
64
76
  let header = [
65
- lengths[0],
66
- lengths[1],
67
- lengths[2],
68
- lengths[3],
69
- u32::from(self.is_encoded),
77
+ lengths[0] as u32,
78
+ lengths[1] as u32,
79
+ lengths[2] as u32,
80
+ lengths[3] as u32,
81
+ u32::from(self.chunks.iter().any(|chunk| chunk.is_encoded)),
70
82
  ];
71
- let length = header.len() + parts.iter().map(|part| part.len()).sum::<usize>();
83
+ let length = header.len() + lengths.iter().sum::<usize>();
72
84
  let data = uninitialized_words(env, length, |words| {
73
- let mut offset = 0;
74
- for part in std::iter::once(&header[..]).chain(parts.iter().map(|part| part.as_slice()))
75
- {
76
- words[offset..offset + part.len()].copy_from_slice(part);
77
- offset += part.len();
85
+ words[..header.len()].copy_from_slice(&header);
86
+ let (nodes, rest) = words[header.len()..].split_at_mut(lengths[0]);
87
+ let (children, rest) = rest.split_at_mut(lengths[1]);
88
+ let (attributes, strings) = rest.split_at_mut(lengths[2]);
89
+ let mut bases = Bases::default();
90
+ for chunk in &self.chunks {
91
+ bases.copy(chunk, nodes, children, attributes, strings);
78
92
  }
79
93
  })?;
94
+ let text = if self.chunks.len() == 1 {
95
+ crate::codec::js_string(env, &self.chunks[0].text)?
96
+ } else {
97
+ let mut text =
98
+ String::with_capacity(self.chunks.iter().map(|chunk| chunk.text.len()).sum());
99
+ for chunk in &self.chunks {
100
+ text.push_str(&chunk.text);
101
+ }
102
+ crate::codec::js_string(env, &text)?
103
+ };
80
104
  let mut object = env.create_object()?;
81
105
  object.set_named_property("data", data)?;
82
- object.set_named_property("text", crate::codec::js_string(env, &self.text)?)?;
106
+ object.set_named_property("text", text)?;
83
107
  Ok(object)
84
108
  }
85
109
  }
86
110
 
111
+ /// Where the next chunk's entries start in each combined list.
112
+ #[derive(Default)]
113
+ struct Bases {
114
+ nodes: usize,
115
+ children: usize,
116
+ attributes: usize,
117
+ strings: usize,
118
+ units: u32,
119
+ }
120
+
121
+ impl Bases {
122
+ /// Copies `chunk` after the chunks before it, shifting its references to
123
+ /// children, attribute pairs, strings and text by theirs.
124
+ fn copy(
125
+ &mut self,
126
+ chunk: &Chunk,
127
+ nodes: &mut [u32],
128
+ children: &mut [u32],
129
+ attributes: &mut [u32],
130
+ strings: &mut [u32],
131
+ ) {
132
+ let string_base = (self.strings / 2) as u32;
133
+ let child_base = self.children as u32;
134
+ let pair_base = (self.attributes / 2) as u32;
135
+ let target = &mut nodes[self.nodes..self.nodes + chunk.nodes.len()];
136
+ target.copy_from_slice(&chunk.nodes);
137
+ if self.nodes != 0 {
138
+ for fields in target.as_chunks_mut::<STRIDE>().0 {
139
+ let flags = fields[10];
140
+ if flags & RELEASED != 0 {
141
+ continue;
142
+ }
143
+ fields[0] += string_base;
144
+ fields[4] += child_base;
145
+ fields[8] += pair_base;
146
+ for field in [6, 7, 14] {
147
+ if fields[field] != 0 {
148
+ fields[field] += string_base;
149
+ }
150
+ }
151
+ if flags & HAS_NAMESPACE != 0 {
152
+ fields[13] += string_base;
153
+ }
154
+ }
155
+ }
156
+ children[self.children..self.children + chunk.children.len()]
157
+ .copy_from_slice(&chunk.children);
158
+ let target = &mut attributes[self.attributes..self.attributes + chunk.attributes.len()];
159
+ for (slot, reference) in target.iter_mut().zip(&chunk.attributes) {
160
+ *slot = reference + string_base;
161
+ }
162
+ let target = &mut strings[self.strings..self.strings + chunk.strings.len()];
163
+ for (slot, offset) in target.iter_mut().zip(&chunk.strings) {
164
+ *slot = offset + self.units;
165
+ }
166
+ self.nodes += chunk.nodes.len();
167
+ self.children += chunk.children.len();
168
+ self.attributes += chunk.attributes.len();
169
+ self.strings += chunk.strings.len();
170
+ self.units += chunk.units;
171
+ }
172
+ }
173
+
87
174
  /// A `Uint32Array` of `length` words that `fill` writes. A Node buffer is
88
175
  /// used for its storage because, unlike an `ArrayBuffer`, it is not
89
176
  /// zero-filled first; `fill` writes every word before JavaScript can read it.
@@ -167,43 +254,34 @@ struct Builder {
167
254
  is_encoded: bool,
168
255
  }
169
256
 
257
+ /// Nodes per chunk worth encoding on another thread.
258
+ const PARALLEL_CHUNK_NODES: usize = 8192;
259
+
170
260
  pub(crate) fn build(document: &NativeDocument) -> NodeSnapshot {
171
261
  let codec = document.options.get("__utf16Codec") == Some(&Value::Bool(true))
172
262
  || document.options.get("__losslessUnicode") == Some(&Value::Bool(true));
173
- let count = document.slot_count() as usize;
174
- let mut builder = Builder {
175
- nodes: Vec::with_capacity(count * STRIDE),
176
- children: Vec::with_capacity(count),
177
- attributes: Vec::new(),
178
- strings: Vec::with_capacity(count * 2),
179
- text: String::with_capacity(document.content_bytes.get()),
180
- units: 0,
181
- interned: IdMap::with_capacity_and_hasher(32, Default::default()),
182
- kinds: [None; KINDS.len()],
183
- short_names: Box::new([(0, 0, 0); SHORT_NAME_SLOTS]),
184
- local_names: IdMap::with_capacity_and_hasher(32, Default::default()),
185
- namespaces: IdMap::default(),
186
- codec,
187
- is_encoded: false,
188
- };
189
263
  if let Some(arena) = document.arena.borrow().as_ref() {
190
- builder.nodes.reserve(arena.nodes.len() * STRIDE);
191
- for id in 0..arena.nodes.len() {
192
- match arena.node(id as u32) {
193
- Some(node) => builder.encode(node),
194
- None => builder.released(),
195
- }
196
- }
197
- } else {
198
- builder.encode_immutable(document);
264
+ let chunks =
265
+ crate::runtime::chunked_map(arena.nodes.len(), PARALLEL_CHUNK_NODES, |range| {
266
+ let mut builder = Builder::new(range.len(), 0, codec);
267
+ for id in range {
268
+ match arena.node(id as u32) {
269
+ Some(node) => builder.encode(node),
270
+ None => builder.released(),
271
+ }
272
+ }
273
+ vec![builder.finish()]
274
+ });
275
+ return NodeSnapshot { chunks };
199
276
  }
277
+ let mut builder = Builder::new(
278
+ document.slot_count() as usize,
279
+ document.content_bytes.get(),
280
+ codec,
281
+ );
282
+ builder.encode_immutable(document);
200
283
  NodeSnapshot {
201
- is_encoded: builder.is_encoded,
202
- nodes: builder.nodes,
203
- children: builder.children,
204
- attributes: builder.attributes,
205
- strings: builder.strings,
206
- text: builder.text,
284
+ chunks: vec![builder.finish()],
207
285
  }
208
286
  }
209
287
 
@@ -232,6 +310,36 @@ fn pointer(value: Option<u32>) -> u32 {
232
310
  }
233
311
 
234
312
  impl Builder {
313
+ fn new(count: usize, text_bytes: usize, codec: bool) -> Self {
314
+ Self {
315
+ nodes: Vec::with_capacity(count * STRIDE),
316
+ children: Vec::with_capacity(count),
317
+ attributes: Vec::new(),
318
+ strings: Vec::with_capacity(count * 2),
319
+ text: String::with_capacity(text_bytes),
320
+ units: 0,
321
+ interned: IdMap::with_capacity_and_hasher(32, Default::default()),
322
+ kinds: [None; KINDS.len()],
323
+ short_names: Box::new([(0, 0, 0); SHORT_NAME_SLOTS]),
324
+ local_names: IdMap::with_capacity_and_hasher(32, Default::default()),
325
+ namespaces: IdMap::default(),
326
+ codec,
327
+ is_encoded: false,
328
+ }
329
+ }
330
+
331
+ fn finish(self) -> Chunk {
332
+ Chunk {
333
+ is_encoded: self.is_encoded,
334
+ nodes: self.nodes,
335
+ children: self.children,
336
+ attributes: self.attributes,
337
+ strings: self.strings,
338
+ text: self.text,
339
+ units: self.units,
340
+ }
341
+ }
342
+
235
343
  fn string(&mut self, value: &str) -> u32 {
236
344
  let start = self.units;
237
345
  self.text.push_str(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rusteerio",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Cheerio-compatible HTML processing with a Rust engine and parallel, non-blocking extraction",
5
5
  "license": "MIT",
6
6
  "repository": {
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file