use crate::{channel::MaybeId, ElementBuilder, IntoAttribue, IntoElement, NodeId, WritableText};
pub(crate) enum Op {
FirstChild = 0,
NextSibling = 1,
ParentNode = 2,
StoreWithId = 3,
SetLastNode = 4,
Stop = 5,
BuildFullElement = 6,
AppendChildren = 7,
ReplaceWith = 8,
InsertAfter = 9,
InsertBefore = 10,
Remove = 11,
CreateTextNode = 12,
CreateElement = 13,
SetText = 14,
SetAttribute = 15,
RemoveAttribute = 16,
SetStyle = 17,
RemoveStyle = 18,
CloneNode = 19,
NoOp = 20,
}
pub trait PreparedBatch {
fn msg(&self) -> &[u8];
fn str(&self) -> &[u8];
}
pub struct FinalizedBatch {
pub msg: Vec<u8>,
pub str: Vec<u8>,
}
impl PreparedBatch for FinalizedBatch {
fn msg(&self) -> &[u8] {
&self.msg
}
fn str(&self) -> &[u8] {
&self.str
}
}
impl<'a> PreparedBatch for &'a FinalizedBatch {
fn msg(&self) -> &[u8] {
&self.msg
}
fn str(&self) -> &[u8] {
&self.str
}
}
pub struct StaticBatch {
pub msg: &'static [u8],
pub str: &'static [u8],
}
impl PreparedBatch for StaticBatch {
fn msg(&self) -> &[u8] {
self.msg
}
fn str(&self) -> &[u8] {
self.str
}
}
impl<'a> PreparedBatch for &'a StaticBatch {
fn msg(&self) -> &[u8] {
self.msg
}
fn str(&self) -> &[u8] {
self.str
}
}
pub struct Batch {
pub(crate) msg: Vec<u8>,
pub(crate) str_buf: Vec<u8>,
pub(crate) current_op_batch_idx: usize,
pub(crate) current_op_byte_idx: usize,
pub(crate) current_op_bit_pack_index: u8,
}
impl Default for Batch {
fn default() -> Self {
Self {
msg: Vec::new(),
str_buf: Vec::new(),
current_op_byte_idx: 3,
current_op_bit_pack_index: 0,
current_op_batch_idx: 0,
}
}
}
impl Batch {
pub fn finalize(mut self) -> FinalizedBatch {
self.encode_op(Op::Stop);
FinalizedBatch {
msg: self.msg,
str: self.str_buf,
}
}
pub fn append_child(&mut self, root: MaybeId, child: MaybeId) {
self.encode_op(Op::AppendChildren);
let size = root.encoded_size() + child.encoded_size();
self.msg.reserve(size as usize);
unsafe {
self.encode_maybe_id_prealloc(root);
self.encode_maybe_id_prealloc(child);
}
}
pub fn replace_with(&mut self, root: MaybeId, node: MaybeId) {
self.encode_op(Op::ReplaceWith);
let size = root.encoded_size() + node.encoded_size();
self.msg.reserve(size as usize);
unsafe {
self.encode_maybe_id_prealloc(root);
self.encode_maybe_id_prealloc(node);
}
}
pub fn insert_after(&mut self, root: MaybeId, node: MaybeId) {
self.encode_op(Op::InsertAfter);
let size = root.encoded_size() + node.encoded_size();
self.msg.reserve(size as usize);
unsafe {
self.encode_maybe_id_prealloc(root);
self.encode_maybe_id_prealloc(node);
}
}
pub fn insert_before(&mut self, root: MaybeId, node: MaybeId) {
self.encode_op(Op::InsertBefore);
let size = root.encoded_size() + node.encoded_size();
self.msg.reserve(size as usize);
unsafe {
self.encode_maybe_id_prealloc(root);
self.encode_maybe_id_prealloc(node);
}
}
pub fn remove(&mut self, id: MaybeId) {
self.encode_op(Op::Remove);
self.encode_maybe_id(id);
}
pub fn create_text_node(&mut self, text: impl WritableText, id: MaybeId) {
self.encode_op(Op::CreateTextNode);
let size = id.encoded_size() + 2;
self.msg.reserve(size as usize);
unsafe {
self.encode_str_prealloc(text);
self.encode_maybe_id_prealloc(id);
}
}
pub fn create_element<'a, 'b, E>(&mut self, tag: E, id: Option<NodeId>)
where
E: IntoElement<'a, 'b>,
{
self.encode_op(Op::CreateElement);
self.msg
.reserve((E::SINGLE_BYTE as u8 + (id.is_some() as u8) * 4) as usize);
unsafe {
tag.encode_prealloc(self);
self.encode_optional_id_prealloc(id);
}
}
pub fn set_text(&mut self, text: impl WritableText, root: MaybeId) {
self.encode_op(Op::SetText);
let size = root.encoded_size() + 2;
self.msg.reserve(size as usize);
unsafe {
self.encode_maybe_id_prealloc(root);
self.encode_str_prealloc(text);
}
}
#[inline(never)]
pub fn set_attribute<'a, 'b, A>(&mut self, attr: A, value: impl WritableText, root: MaybeId)
where
A: IntoAttribue<'a, 'b>,
{
self.encode_op(Op::SetAttribute);
self.msg
.reserve((A::SINGLE_BYTE as u8 + root.encoded_size() + 2) as usize);
unsafe {
self.encode_maybe_id_prealloc(root);
attr.encode_prealloc(self);
self.encode_str_prealloc(value);
}
}
pub fn remove_attribute<'a, 'b, A>(&mut self, attr: A, root: MaybeId)
where
A: IntoAttribue<'a, 'b>,
{
self.encode_op(Op::RemoveAttribute);
let size = A::SINGLE_BYTE as u8 + root.encoded_size();
self.msg.reserve(size as usize);
unsafe {
self.encode_maybe_id_prealloc(root);
attr.encode_prealloc(self);
}
}
pub fn clone_node(&mut self, id: MaybeId, new_id: MaybeId) {
self.encode_op(Op::CloneNode);
let size = id.encoded_size() + new_id.encoded_size();
self.msg.reserve(size as usize);
self.encode_maybe_id(id);
self.encode_maybe_id(new_id);
}
pub fn first_child(&mut self) {
self.encode_op(Op::FirstChild);
}
pub fn next_sibling(&mut self) {
self.encode_op(Op::NextSibling);
}
pub fn parent_node(&mut self) {
self.encode_op(Op::ParentNode);
}
pub fn store_with_id(&mut self, id: NodeId) {
self.encode_op(Op::StoreWithId);
self.encode_id(id);
}
pub fn set_last_node(&mut self, id: NodeId) {
self.encode_op(Op::SetLastNode);
self.encode_id(id);
}
pub fn build_full_element(&mut self, el: ElementBuilder) {
self.encode_op(Op::BuildFullElement);
el.encode(self);
}
pub fn set_style(&mut self, style: &str, value: &str, id: MaybeId) {
self.encode_op(Op::SetStyle);
let size = id.encoded_size() + 2 + 2;
self.msg.reserve(size as usize);
self.encode_maybe_id(id);
unsafe {
self.encode_str_prealloc(style);
self.encode_str_prealloc(value);
}
}
pub fn remove_style(&mut self, style: &str, id: MaybeId) {
self.encode_op(Op::RemoveStyle);
let size = id.encoded_size() + 2;
self.msg.reserve(size as usize);
unsafe {
self.encode_maybe_id_prealloc(id);
self.encode_str_prealloc(style);
}
}
#[inline]
pub(crate) unsafe fn encode_optional_id_prealloc(&mut self, id: Option<NodeId>) {
match id {
Some(id) => {
self.encode_bool(true);
self.encode_id_prealloc(id);
}
None => {
self.encode_bool(false);
}
}
}
#[inline]
pub(crate) unsafe fn encode_maybe_id_prealloc(&mut self, id: MaybeId) {
match id {
MaybeId::Node(id) => {
self.encode_bool(true);
self.encode_id_prealloc(id);
}
MaybeId::LastNode => {
self.encode_bool(false);
}
}
}
#[inline]
pub(crate) fn encode_maybe_id(&mut self, id: MaybeId) {
match id {
MaybeId::Node(id) => {
self.encode_bool(true);
self.encode_id(id);
}
MaybeId::LastNode => {
self.encode_bool(false);
}
}
}
#[inline(always)]
pub(crate) unsafe fn encode_id_prealloc(&mut self, id: NodeId) {
self.encode_u32_prealloc(id.0);
}
#[inline(always)]
pub(crate) fn encode_id(&mut self, id: NodeId) {
self.encode_u32(id.0);
}
#[inline(always)]
pub(crate) fn encode_u32(&mut self, val: u32) {
self.msg.reserve(4);
unsafe {
self.encode_u32_prealloc(val);
}
}
#[inline(always)]
pub(crate) unsafe fn encode_u32_prealloc(&mut self, val: u32) {
let le = val.to_le();
unsafe {
let len = self.msg.len();
self.msg.as_mut_ptr().add(len).cast::<u32>().write(le);
self.msg.set_len(len + 4);
}
}
#[inline(always)]
pub(crate) fn encode_u16(&mut self, val: u16) {
self.msg.reserve(2);
unsafe {
self.encode_u16_prealloc(val);
}
}
#[inline(always)]
pub(crate) unsafe fn encode_u16_prealloc(&mut self, val: u16) {
let le = val.to_le();
#[allow(clippy::uninit_vec)]
unsafe {
let len = self.msg.len();
self.msg.as_mut_ptr().add(len).cast::<u16>().write(le);
self.msg.set_len(len + 2);
}
}
#[inline]
pub(crate) fn encode_str(&mut self, string: impl WritableText) {
let prev_len = self.str_buf.len();
string.write_as_text(&mut self.str_buf);
let len = self.str_buf.len() - prev_len;
self.encode_u16(len as u16);
}
#[inline]
pub(crate) unsafe fn encode_str_prealloc(&mut self, string: impl WritableText) {
let prev_len = self.str_buf.len();
string.write_as_text(&mut self.str_buf);
let len = self.str_buf.len() - prev_len;
self.encode_u16_prealloc(len as u16);
}
#[inline]
pub(crate) fn encode_cachable_str(&mut self, string: impl WritableText) {
let prev_len = self.str_buf.len();
string.write_as_text(&mut self.str_buf);
let len = self.str_buf.len() - prev_len;
self.encode_u16(len as u16);
}
#[inline]
pub(crate) fn encode_op(&mut self, op: Op) {
let u8_op = op as u8;
self.current_op_byte_idx += 1;
if self.current_op_byte_idx - self.current_op_batch_idx < 4 {
unsafe {
*self.msg.get_unchecked_mut(self.current_op_byte_idx) = u8_op;
}
} else {
self.current_op_batch_idx = self.msg.len();
self.current_op_byte_idx = self.current_op_batch_idx;
#[allow(clippy::uninit_vec)]
unsafe {
let len = self.msg.len();
self.msg.reserve(4);
self.msg.set_len(len + 4);
*self.msg.get_unchecked_mut(self.current_op_batch_idx) = u8_op;
}
}
self.current_op_bit_pack_index = 0;
}
#[inline]
pub(crate) fn encode_bool(&mut self, value: bool) {
if self.current_op_bit_pack_index < 3 {
if value {
unsafe {
*self.msg.get_unchecked_mut(self.current_op_byte_idx) |=
1 << (self.current_op_bit_pack_index + 5);
}
}
self.current_op_bit_pack_index += 1;
} else {
todo!("handle more than 3 bools in a op");
}
}
pub(crate) fn append(&mut self, mut batch: Self) {
let operations_left = 3 - (self.current_op_byte_idx - self.current_op_batch_idx);
for _ in 0..operations_left {
self.encode_op(Op::NoOp);
}
self.current_op_byte_idx = self.msg.len() + batch.current_op_byte_idx;
self.current_op_batch_idx = self.msg.len() + batch.current_op_batch_idx;
self.current_op_bit_pack_index = batch.current_op_bit_pack_index;
self.str_buf.extend_from_slice(&batch.str_buf);
self.msg.append(&mut batch.msg);
}
}