const TYPE_INT: u8 = 3;
const TYPE_FLOAT: u8 = 5;
const TYPE_TEXTRAW: u8 = 10;
const TYPE_ARRAY: u8 = 11;
const TYPE_OBJECT: u8 = 12;
fn write_header(out: &mut Vec<u8>, jsonb_type: u8, payload_len: usize) {
if payload_len <= 11 {
out.push(((payload_len as u8) << 4) | jsonb_type);
} else if payload_len <= 0xFF {
out.push((12u8 << 4) | jsonb_type);
out.push(payload_len as u8);
} else if payload_len <= 0xFFFF {
out.push((13u8 << 4) | jsonb_type);
out.extend_from_slice(&(payload_len as u16).to_be_bytes());
} else if payload_len <= 0xFFFF_FFFF {
out.push((14u8 << 4) | jsonb_type);
out.extend_from_slice(&(payload_len as u32).to_be_bytes());
} else {
out.push((15u8 << 4) | jsonb_type);
out.extend_from_slice(&(payload_len as u64).to_be_bytes());
}
}
pub(crate) fn write_int(out: &mut Vec<u8>, n: i64) {
let s = n.to_string();
write_header(out, TYPE_INT, s.len());
out.extend_from_slice(s.as_bytes());
}
pub(crate) fn write_float(out: &mut Vec<u8>, f: f64) {
let s = format!("{f}");
write_header(out, TYPE_FLOAT, s.len());
out.extend_from_slice(s.as_bytes());
}
pub(crate) fn write_text(out: &mut Vec<u8>, s: &str) {
let bytes = s.as_bytes();
write_header(out, TYPE_TEXTRAW, bytes.len());
out.extend_from_slice(bytes);
}
pub struct encoder {
scratches: Vec<Vec<u8>>,
}
impl encoder {
pub fn new() -> Self {
Self {
scratches: Vec::with_capacity(4),
}
}
fn alloc(&mut self) -> Vec<u8> {
self
.scratches
.pop()
.unwrap_or_else(|| Vec::with_capacity(256))
}
fn free(&mut self, mut buf: Vec<u8>) {
buf.clear();
self.scratches.push(buf);
}
pub(crate) fn write_object<F>(&mut self, out: &mut Vec<u8>, f: F)
where
F: FnOnce(&mut encoder, &mut Vec<u8>),
{
let mut scratch = self.alloc();
f(self, &mut scratch);
write_header(out, TYPE_OBJECT, scratch.len());
out.extend_from_slice(&scratch);
self.free(scratch);
}
pub(crate) fn write_array<F>(&mut self, out: &mut Vec<u8>, f: F)
where
F: FnOnce(&mut encoder, &mut Vec<u8>),
{
let mut scratch = self.alloc();
f(self, &mut scratch);
write_header(out, TYPE_ARRAY, scratch.len());
out.extend_from_slice(&scratch);
self.free(scratch);
}
}