sledgehammer/channel.rs
1//! This module contains the [`MsgChannel`] type which is used to send batched dom manipulation operations.
2//! The main way to add operations to a [`MsgChannel`] is by dirrectly calling the methods on it, and then calling [`MsgChannel::flush`] to send the operations to the browser.
3//!
4//!
5
6use std::{fmt::Arguments, io::Write};
7
8use web_sys::Node;
9
10use crate::{
11 batch::{Batch, Op, PreparedBatch},
12 last_needs_memory, update_last_memory, work_last_created, ElementBuilder, IntoAttribue,
13 IntoElement, JsInterpreter, MSG_METADATA_PTR, MSG_PTR_PTR, STR_LEN_PTR, STR_PTR_PTR,
14};
15
16/// Tracks if a interpreter has been created. Used to prevent multiple interpreters from being created.
17static mut INTERPRETER_EXISTS: bool = false;
18
19/// An id that may be either the last node or a node with an assigned id.
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
21pub enum MaybeId {
22 /// The last node that was created or navigated to.
23 LastNode,
24 /// A node that was created and stored with an id
25 Node(NodeId),
26}
27
28impl MaybeId {
29 #[inline(always)]
30 pub(crate) const fn encoded_size(&self) -> u8 {
31 match self {
32 MaybeId::LastNode => 0,
33 MaybeId::Node(_) => 4,
34 }
35 }
36}
37
38/// A node that was created and stored with an id
39/// It is recommended to create and store ids with a slab allocator with an exposed slab index for example the excellent [slab](https://docs.rs/slab) crate.
40#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub struct NodeId(pub u32);
42
43/// The [`MsgChannel`] handles communication with the dom. It allows you to send batched operations to the dom.
44/// All of the functions that are not marked otherwise are qued and not exicuted imidately. When you want to exicute the que you have to call [`MsgChannel::flush`].
45/// There should only be one [`MsgChannel`] per application.
46pub struct MsgChannel {
47 pub(crate) js_interpreter: JsInterpreter,
48 batch: Batch,
49}
50
51impl Default for MsgChannel {
52 fn default() -> Self {
53 unsafe {
54 assert!(
55 !INTERPRETER_EXISTS,
56 "Found another MsgChannel. Only one MsgChannel can be created"
57 );
58 INTERPRETER_EXISTS = true;
59 }
60 debug_assert!(0x1F > Op::NoOp as u8);
61 format!(
62 "init: {:?}, {:?}, {:?}",
63 unsafe { MSG_PTR_PTR as usize },
64 unsafe { STR_PTR_PTR as usize },
65 unsafe { STR_LEN_PTR as usize }
66 );
67 let js_interpreter = unsafe {
68 JsInterpreter::new(
69 wasm_bindgen::memory(),
70 MSG_METADATA_PTR as usize,
71 MSG_PTR_PTR as usize,
72 STR_PTR_PTR as usize,
73 STR_LEN_PTR as usize,
74 )
75 };
76
77 Self {
78 js_interpreter,
79 batch: Batch::default(),
80 }
81 }
82}
83
84impl MsgChannel {
85 /// IMPORTANT: This method is exicuted immediatly and does not wait for the next flush
86 ///
87 /// Example:
88 /// ```no_run
89 /// let window = web_sys::window().unwrap();
90 /// let document = window.document().unwrap();
91 /// let body = document.body().unwrap();
92 /// let mut channel = MsgChannel::default();
93 /// // assign the NodeId(0) to the body element from web-sys
94 /// channel.set_node(NodeId(0), JsCast::dyn_into(body).unwrap());
95 /// // no need to call flush here because set_node is exicuted immediatly
96 /// ```
97 pub fn set_node(&mut self, id: NodeId, node: Node) {
98 self.js_interpreter.SetNode(id.0, node);
99 }
100
101 /// IMPORTANT: This method is exicuted immediatly and does not wait for the next flush
102 ///
103 /// Example:
104 /// ```no_run
105 /// let mut channel = MsgChannel::default();
106 /// channel.create_element("div", Some(NodeId(0)));
107 /// channel.flush();
108 /// let element = channel.get_node(NodeId(0));
109 /// let text = element.text_content().map(|t| t + " + web-sys");
110 /// element.set_text_content(text.as_deref());
111 /// // no need to call flush here because get_node is exicuted immediatly
112 /// ```
113 pub fn get_node(&mut self, id: NodeId) -> Node {
114 self.js_interpreter.GetNode(id.0)
115 }
116
117 /// Exicutes any queued operations in the order they were added
118 ///
119 /// Example:
120 ///
121 /// ```no_run
122 /// let mut channel = MsgChannel::default();
123 /// // this does not immediatly create a <div> or <p>
124 /// channel.create_element("div", None);
125 /// channel.create_element("p", None);
126 /// // this creates the <div> and <p> elements
127 /// channel.flush();
128 /// ```
129 pub fn flush(&mut self) {
130 self.batch.encode_op(Op::Stop);
131 run_batch(&self.batch.msg, &self.batch.str_buf);
132 self.batch.msg.clear();
133 self.batch.current_op_batch_idx = 0;
134 self.batch.current_op_byte_idx = 3;
135 self.batch.str_buf.clear();
136 }
137
138 /// Appends a number of nodes as children of the given node.
139 ///
140 /// Example:
141 ///
142 /// ```no_run
143 /// let mut channel = MsgChannel::default();
144 /// channel.create_element("div", Some(NodeId(0)));
145 /// channel.create_element("p", None);
146 /// // append the <p> element to the <div> element
147 /// channel.append_child(MaybeId::Node(NodeId(0)), MaybeId::LastNode);
148 /// channel.flush();
149 /// ```
150 pub fn append_child(&mut self, root: MaybeId, child: MaybeId) {
151 self.batch.append_child(root, child)
152 }
153
154 /// Replace a node with another node
155 ///
156 /// Example:
157 /// ```no_run
158 /// let mut channel = MsgChannel::default();
159 /// channel.create_element("div", Some(NodeId(0)));
160 /// channel.create_element("p", None);
161 /// // replace the <p> element with the <div> element
162 /// channel.replace_with(MaybeId::Node(NodeId(0)), MaybeId::LastNode);
163 /// channel.flush();
164 /// ```
165 pub fn replace_with(&mut self, root: MaybeId, node: MaybeId) {
166 self.batch.replace_with(root, node)
167 }
168
169 /// Insert a single node after a given node.
170 ///
171 /// Example:
172 /// ```no_run
173 /// let mut channel = MsgChannel::default();
174 /// channel.create_element("div", Some(NodeId(0)));
175 /// channel.create_element("p", None);
176 /// // insert the <p> element after the <div> element
177 /// channel.insert_after(MaybeId::Node(NodeId(0)), MaybeId::LastNode);
178 /// channel.flush();
179 /// ```
180 pub fn insert_after(&mut self, root: MaybeId, node: MaybeId) {
181 self.batch.insert_after(root, node)
182 }
183
184 /// Insert a single node before a given node.
185 ///
186 /// Example:
187 /// ```no_run
188 /// let mut channel = MsgChannel::default();
189 /// channel.create_element("div", Some(NodeId(0)));
190 /// channel.create_element("p", None);
191 /// // insert the <p> element before the <div> element
192 /// channel.insert_before(MaybeId::Node(NodeId(0)), MaybeId::LastNode);
193 /// channel.flush();
194 /// ```
195 pub fn insert_before(&mut self, root: MaybeId, node: MaybeId) {
196 self.batch.insert_before(root, node)
197 }
198
199 /// Remove a node from the DOM.
200 ///
201 /// Example:
202 /// ```no_run
203 /// let mut channel = MsgChannel::default();
204 /// channel.create_element("p", None);
205 /// // remove the <p> element
206 /// channel.remove(MaybeId::LastNode);
207 /// channel.flush();
208 /// ```
209 pub fn remove(&mut self, id: MaybeId) {
210 self.batch.remove(id)
211 }
212
213 /// Create a new text node
214 ///
215 /// Example:
216 /// ```no_run
217 /// let mut channel = MsgChannel::default();
218 /// // create a text node with the text "Hello World"
219 /// channel.create_text_node("Hello World", None);
220 /// channel.flush();
221 pub fn create_text_node(&mut self, text: impl WritableText, id: MaybeId) {
222 self.batch.create_text_node(text, id)
223 }
224
225 /// Create a new element node
226 ///
227 /// Example:
228 /// ```no_run
229 /// let mut channel = MsgChannel::default();
230 /// // create a <div> element
231 /// channel.create_element("div", None);
232 /// channel.flush();
233 /// ```
234 pub fn create_element<'a, 'b>(&mut self, tag: impl IntoElement<'a, 'b>, id: Option<NodeId>) {
235 self.batch.create_element(tag, id)
236 }
237
238 /// Set the textcontent of a node.
239 ///
240 /// Example:
241 /// ```no_run
242 /// let mut channel = MsgChannel::default();
243 /// // create a text node with the text "Hello World"
244 /// channel.create_text_node("Hello ", None);
245 /// // set the text content of the text node to "Hello World!!!"
246 /// channel.set_text_content("World!!!", MaybeId::LastNode);
247 /// channel.flush();
248 /// ```
249 pub fn set_text(&mut self, text: impl WritableText, root: MaybeId) {
250 self.batch.set_text(text, root)
251 }
252
253 /// Set the value of a node's attribute.
254 ///
255 /// Example:
256 /// ```no_run
257 /// let mut channel = MsgChannel::default();
258 /// // create a <div> element
259 /// channel.create_element("div", None);
260 /// // set the attribute "id" to "my-div" on the <div> element
261 /// channel.set_attribute(Attribute::id, "my-div", MaybeId::LastNode);
262 /// channel.flush();
263 /// ```
264 pub fn set_attribute<'a, 'b>(
265 &mut self,
266 attr: impl IntoAttribue<'a, 'b>,
267 value: impl WritableText,
268 root: MaybeId,
269 ) {
270 self.batch.set_attribute(attr, value, root)
271 }
272
273 /// Remove an attribute from a node.
274 ///
275 /// Example:
276 /// ```no_run
277 /// let mut channel = MsgChannel::default();
278 /// // create a <div> element
279 /// channel.create_element("div", None);
280 /// channel.set_attribute(Attribute::id, "my-div", MaybeId::LastNode);
281 /// // remove the attribute "id" from the <div> element
282 /// channel.remove_attribute(Attribute::id, MaybeId::LastNode);
283 /// channel.flush();
284 /// ```
285 pub fn remove_attribute<'a, 'b>(&mut self, attr: impl IntoAttribue<'a, 'b>, root: MaybeId) {
286 self.batch.remove_attribute(attr, root)
287 }
288
289 /// Clone a node and store it with a new id.
290 ///
291 /// Example:
292 /// ```no_run
293 /// let mut channel = MsgChannel::default();
294 /// // create a <div> element
295 /// channel.create_element("div", None);
296 /// // clone the <div> element and store it with the id 1
297 /// channel.clone_node(MaybeId::LastNode, Some(NodeId(1)));
298 /// channel.flush();
299 /// ```
300 pub fn clone_node(&mut self, id: MaybeId, new_id: MaybeId) {
301 self.batch.clone_node(id, new_id)
302 }
303
304 /// Move the last node to the first child
305 ///
306 /// Example:
307 /// ```no_run
308 /// let mut channel = MsgChannel::default();
309 /// // create a element: <div><p></p></div>
310 /// channel.build_full_element(
311 /// ElementBuilder::new("div".into())
312 /// .children(&[
313 /// ElementBuilder::new(Element::p.into())
314 /// .into(),
315 /// ]),
316 /// );
317 /// // move from the <div> to the <p>
318 /// channel.first_child();
319 /// // operatons modifing the <p> element...
320 /// channel.flush();
321 /// ```
322 pub fn first_child(&mut self) {
323 self.batch.first_child()
324 }
325
326 /// Move the last node to the next sibling
327 ///
328 /// Example:
329 /// ```no_run
330 /// let mut channel = MsgChannel::default();
331 /// // create a element: <div><h1></h1><p></p></div>
332 /// channel.build_full_element(
333 /// ElementBuilder::new("div".into())
334 /// .children(&[
335 /// ElementBuilder::new(Element::h1.into())
336 /// .into(),
337 /// ElementBuilder::new(Element::p.into())
338 /// ]),
339 /// );
340 /// // move from the <div> to the <h1>
341 /// channel.first_child();
342 /// // move from the <h1> to the <p>
343 /// channel.next_sibling();
344 /// // operatons modifing the <p> element...
345 /// channel.flush();
346 /// ```
347 pub fn next_sibling(&mut self) {
348 self.batch.next_sibling()
349 }
350
351 /// Move the last node to the parent node
352 ///
353 /// Example:
354 /// ```no_run
355 /// let mut channel = MsgChannel::default();
356 /// // create a element: <div><p></p></div>
357 /// channel.build_full_element(
358 /// ElementBuilder::new("div".into())
359 /// .children(&[
360 /// ElementBuilder::new(Element::p.into())
361 /// .id(NodeId(0))
362 /// .into(),
363 /// ]),
364 /// );
365 /// // move to the <p> element
366 /// channel.set_last_node(NodeId(0));
367 /// // move from the <p> to the <div>
368 /// channel.parent_node();
369 /// // operatons modifing the <p> element...
370 /// channel.flush();
371 /// ```
372 pub fn parent_node(&mut self) {
373 self.batch.parent_node()
374 }
375
376 /// Store the last node with the given id. This is useful when traversing the document tree.
377 ///
378 /// Example:
379 /// ```no_run
380 /// let mut channel = MsgChannel::default();
381 /// // create a element without an id
382 /// channel.create_element("div", None);
383 /// // store the <div> element with the id 0
384 /// channel.set_last_node(NodeId(0));
385 /// channel.flush();
386 /// ```
387 pub fn store_with_id(&mut self, id: NodeId) {
388 self.batch.store_with_id(id)
389 }
390
391 /// Set the last node to the given id. The last node can be used to traverse the document tree without passing objects between wasm and js every time.
392 ///
393 /// Example:
394 /// ```no_run
395 /// let mut channel = MsgChannel::default();
396 /// // create a element: <div><h1><h2></h2></h1><p></p></div>
397 /// channel.build_full_element(
398 /// ElementBuilder::new("div".into())
399 /// .children(&[
400 /// ElementBuilder::new(Element::h1.into())
401 /// .children(&[
402 /// ElementBuilder::new(Element::h2.into())
403 /// .into(),
404 /// ]).into(),
405 /// ElementBuilder::new(Element::p.into())
406 /// .into(),
407 /// ]),
408 /// );
409 /// // move from the <div> to the <h1>
410 /// channel.first_child();
411 /// // store the <h1> element with the id 0
412 /// channel.store_with_id(NodeId(0));
413 /// // move from the <h1> to the <h2>
414 /// channel.first_child();
415 /// // update something in the <h2> element...
416 /// // restore the <h1> element
417 /// channel.set_last_node(NodeId(0));
418 /// // move from the <h1> to the <p>
419 /// channel.next_sibling();
420 /// // operatons modifing the <p> element...
421 /// channel.flush();
422 /// ```
423 pub fn set_last_node(&mut self, id: NodeId) {
424 self.batch.set_last_node(id)
425 }
426
427 /// Build a full element, slightly more efficent than creating the element creating the element with `create_element` and then setting the attributes.
428 ///
429 /// Example:
430 /// ```rust
431 /// let mut channel = MsgChannel::default();
432 /// // create an element using sledgehammer
433 /// channel.build_full_element(
434 /// ElementBuilder::new("div".into())
435 /// .id(NodeId(0))
436 /// .attrs(&[(Attribute::style.into(), "color: blue")])
437 /// .children(&[
438 /// ElementBuilder::new(Element::p.into())
439 /// .into(),
440 /// TextBuilder::new("Hello from sledgehammer!").into(),
441 /// ]),
442 /// );
443 /// channel.flush();
444 /// ```
445 pub fn build_full_element(&mut self, el: ElementBuilder) {
446 self.batch.build_full_element(el)
447 }
448
449 /// Set a style property on a node.
450 ///
451 /// Example:
452 /// ```rust
453 /// let mut channel = MsgChannel::default();
454 /// channel.create_element("div", None);
455 /// // set the style property "color" to "blue"
456 /// channel.set_style("color", "blue", MaybeId::LastNode);
457 /// channel.flush();
458 /// ```
459 pub fn set_style(&mut self, style: &str, value: &str, id: MaybeId) {
460 self.batch.set_style(style, value, id)
461 }
462
463 /// Remove a style property from a node.
464 ///
465 /// Example:
466 /// ```rust
467 /// let mut channel = MsgChannel::default();
468 /// channel.create_element("div", None);
469 /// channel.set_style("color", "blue", MaybeId::LastNode);
470 /// // remove the color style
471 /// channel.remove_style("color", MaybeId::LastNode);
472 /// channel.flush();
473 /// ```
474 pub fn remove_style(&mut self, style: &str, id: MaybeId) {
475 self.batch.remove_style(style, id)
476 }
477
478 /// Adds a batch of operations to the current batch.
479 ///
480 /// Example:
481 /// ```rust
482 /// let mut channel = MsgChannel::default();
483 /// let mut batch = Batch::default();
484 /// batch.create_element("div", None);
485 /// // add the batch to the channel
486 /// channel.append(batch);
487 /// channel.flush();
488 /// ```
489 pub fn append(&mut self, batch: Batch) {
490 self.batch.append(batch);
491 }
492
493 /// IMPORTANT: This method is exicuted immediatly and does not wait for the next flush
494 ///
495 /// Run a batch of operations on the DOM immediately. This only runs the operations that are in the batch, not the operations that are queued in the [`MsgChannel`].
496 ///
497 /// Example:
498 /// ```rust
499 /// let mut channel = MsgChannel::default();
500 /// let mut batch = Batch::default();
501 /// batch.create_element("div", None);
502 /// // add the batch to the channel
503 /// channel.run_batch(&batch.finalize());
504 /// ```
505 pub fn run_batch(&mut self, batch: impl PreparedBatch) {
506 run_batch(batch.msg(), batch.str());
507 }
508}
509
510fn run_batch(msg: &[u8], str_buf: &[u8]) {
511 debug_assert_eq!(0usize.to_le_bytes().len(), 32 / 8);
512 let msg_ptr = msg.as_ptr() as usize;
513 let str_ptr = str_buf.as_ptr() as usize;
514 // the pointer will only be updated when the message vec is resized, so we have a flag to check if the pointer has changed to avoid unnecessary decoding
515 if unsafe { *MSG_METADATA_PTR } == 255 {
516 // this is the first message, so we need to encode all the metadata
517 unsafe {
518 let mut_ptr_ptr: *mut usize = std::mem::transmute(MSG_PTR_PTR);
519 *mut_ptr_ptr = msg_ptr;
520 let mut_metadata_ptr: *mut u8 = std::mem::transmute(MSG_METADATA_PTR);
521 // the first bit encodes if the msg pointer has changed
522 *mut_metadata_ptr = 1;
523 let mut_str_ptr_ptr: *mut usize = std::mem::transmute(STR_PTR_PTR);
524 *mut_str_ptr_ptr = str_ptr as usize;
525 // the second bit encodes if the str pointer has changed
526 *mut_metadata_ptr |= 2;
527 }
528 } else {
529 if unsafe { *MSG_PTR_PTR } != msg_ptr {
530 unsafe {
531 let mut_ptr_ptr: *mut usize = std::mem::transmute(MSG_PTR_PTR);
532 *mut_ptr_ptr = msg_ptr;
533 let mut_ptr_ptr: *mut u8 = std::mem::transmute(MSG_METADATA_PTR);
534 // the first bit encodes if the msg pointer has changed
535 *mut_ptr_ptr = 1;
536 }
537 } else {
538 unsafe {
539 let mut_ptr_ptr: *mut u8 = std::mem::transmute(MSG_METADATA_PTR);
540 // the first bit encodes if the msg pointer has changed
541 *mut_ptr_ptr = 0;
542 }
543 }
544 if unsafe { *STR_PTR_PTR } != str_ptr {
545 unsafe {
546 let mut_str_ptr_ptr: *mut usize = std::mem::transmute(STR_PTR_PTR);
547 *mut_str_ptr_ptr = str_ptr as usize;
548 let mut_metadata_ptr: *mut u8 = std::mem::transmute(MSG_METADATA_PTR);
549 // the second bit encodes if the str pointer has changed
550 *mut_metadata_ptr |= 1 << 1;
551 }
552 }
553 }
554 unsafe {
555 let mut_metadata_ptr: *mut u8 = std::mem::transmute(MSG_METADATA_PTR);
556 if !str_buf.is_empty() {
557 // the third bit encodes if there is any strings
558 *mut_metadata_ptr |= 1 << 2;
559 let mut_str_len_ptr: *mut usize = std::mem::transmute(STR_LEN_PTR);
560 *mut_str_len_ptr = str_buf.len() as usize;
561 if *mut_str_len_ptr < 100 {
562 // the fourth bit encodes if the strings are entirely ascii and small
563 *mut_metadata_ptr |= (str_buf.is_ascii() as u8) << 3;
564 }
565 }
566 }
567 if last_needs_memory() {
568 update_last_memory(wasm_bindgen::memory());
569 }
570 work_last_created();
571}
572
573/// Something that can be written as a utf-8 string to a buffer
574pub trait WritableText {
575 fn write_as_text(self, to: &mut Vec<u8>);
576}
577
578impl WritableText for char {
579 fn write_as_text(self, to: &mut Vec<u8>) {
580 to.push(self as u8);
581 }
582}
583
584impl<'a> WritableText for &'a str {
585 #[inline(always)]
586 fn write_as_text(self, to: &mut Vec<u8>) {
587 let len = self.len();
588 to.reserve(len);
589 let old_len = to.len();
590 #[allow(clippy::uninit_vec)]
591 unsafe {
592 let ptr = to.as_mut_ptr();
593 let bytes = self.as_bytes();
594 let str_ptr = bytes.as_ptr();
595 for o in 0..len {
596 *ptr.add(old_len + o) = *str_ptr.add(o);
597 }
598 to.set_len(old_len + len);
599 }
600 // let _ = to.write(self.as_bytes());
601 }
602}
603
604impl WritableText for Arguments<'_> {
605 fn write_as_text(self, to: &mut Vec<u8>) {
606 let _ = to.write_fmt(self);
607 }
608}
609
610impl<F> WritableText for F
611where
612 F: FnOnce(&mut Vec<u8>),
613{
614 fn write_as_text(self, to: &mut Vec<u8>) {
615 self(to);
616 }
617}
618
619macro_rules! write_unsized {
620 ($t: ty) => {
621 impl WritableText for $t {
622 fn write_as_text(self, to: &mut Vec<u8>) {
623 let mut n = self;
624 let mut n2 = n;
625 let mut num_digits = 0;
626 while n2 > 0 {
627 n2 /= 10;
628 num_digits += 1;
629 }
630 let len = num_digits;
631 to.reserve(len);
632 let ptr = to.as_mut_ptr().cast::<u8>();
633 let old_len = to.len();
634 let mut i = len - 1;
635 loop {
636 unsafe { ptr.add(old_len + i).write((n % 10) as u8 + b'0') }
637 n /= 10;
638
639 if n == 0 {
640 break;
641 } else {
642 i -= 1;
643 }
644 }
645
646 #[allow(clippy::uninit_vec)]
647 unsafe {
648 to.set_len(old_len + (len - i));
649 }
650 }
651 }
652 };
653}
654
655macro_rules! write_sized {
656 ($t: ty) => {
657 impl WritableText for $t {
658 fn write_as_text(self, to: &mut Vec<u8>) {
659 let neg = self < 0;
660 let mut n = if neg {
661 match self.checked_abs() {
662 Some(n) => n,
663 None => <$t>::MAX / 2 + 1,
664 }
665 } else {
666 self
667 };
668 let mut n2 = n;
669 let mut num_digits = 0;
670 while n2 > 0 {
671 n2 /= 10;
672 num_digits += 1;
673 }
674 let len = if neg { num_digits + 1 } else { num_digits };
675 to.reserve(len);
676 let ptr = to.as_mut_ptr().cast::<u8>();
677 let old_len = to.len();
678 let mut i = len - 1;
679 loop {
680 unsafe { ptr.add(old_len + i).write((n % 10) as u8 + b'0') }
681 n /= 10;
682
683 if n == 0 {
684 break;
685 } else {
686 i -= 1;
687 }
688 }
689
690 if neg {
691 i -= 1;
692 unsafe { ptr.add(old_len + i).write(b'-') }
693 }
694
695 #[allow(clippy::uninit_vec)]
696 unsafe {
697 to.set_len(old_len + (len - i));
698 }
699 }
700 }
701 };
702}
703
704write_unsized!(u8);
705write_unsized!(u16);
706write_unsized!(u32);
707write_unsized!(u64);
708write_unsized!(u128);
709write_unsized!(usize);
710
711write_sized!(i8);
712write_sized!(i16);
713write_sized!(i32);
714write_sized!(i64);
715write_sized!(i128);
716write_sized!(isize);