use bbg::IntentRecord;
use tape::{sigil, Chunk, ReadResult, Reader};
use crate::{CyberlinkRecord, Signal, SELF_NETWORK};
pub const RENDER_BIN: u8 = b'b';
pub fn encode_signal_frame(signal: &Signal) -> Vec<u8> {
let payload = serialize_signal(signal);
Chunk::new(sigil::ZAP, RENDER_BIN, payload.into()).encode()
}
pub fn encode_intent_frame(intent: &IntentRecord) -> Vec<u8> {
let payload = serialize_intent(intent);
Chunk::new(sigil::KET, RENDER_BIN, payload.into()).encode()
}
pub fn decode_signal_frame(bytes: &[u8]) -> Option<Signal> {
let mut reader = Reader::new();
reader.feed(bytes);
match reader.next_chunk() {
ReadResult::Chunk(chunk) if chunk.sigil == sigil::ZAP => deserialize_signal(&chunk.payload),
_ => None,
}
}
pub fn decode_signals(bytes: &[u8]) -> Vec<Signal> {
let mut reader = Reader::new();
reader.feed(bytes);
let mut out = Vec::new();
loop {
match reader.next_chunk() {
ReadResult::Chunk(chunk) => {
if chunk.sigil == sigil::ZAP {
if let Some(s) = deserialize_signal(&chunk.payload) {
out.push(s);
}
}
}
ReadResult::Pending | ReadResult::Eof => break,
}
}
out
}
pub enum CyberFrame {
Signal(Signal),
Intent(IntentRecord),
}
pub fn decode_events(bytes: &[u8]) -> Vec<CyberFrame> {
let mut reader = Reader::new();
reader.feed(bytes);
let mut out = Vec::new();
loop {
match reader.next_chunk() {
ReadResult::Chunk(c) if c.sigil == sigil::ZAP => {
if let Some(s) = deserialize_signal(&c.payload) {
out.push(CyberFrame::Signal(s));
}
}
ReadResult::Chunk(c) if c.sigil == sigil::KET => {
if let Some(i) = deserialize_intent(&c.payload) {
out.push(CyberFrame::Intent(i));
}
}
ReadResult::Chunk(_) => {}
ReadResult::Pending | ReadResult::Eof => break,
}
}
out
}
fn serialize_signal(s: &Signal) -> Vec<u8> {
let mut buf = Vec::with_capacity(32 + 8 + 32 + 8 + 8);
buf.extend_from_slice(&s.neuron);
buf.extend_from_slice(&s.step.to_le_bytes());
buf.extend_from_slice(&s.prev);
buf.extend_from_slice(&s.height.to_le_bytes());
buf.extend_from_slice(&(s.links.len() as u32).to_le_bytes());
for l in &s.links {
buf.extend_from_slice(&l.neuron);
buf.extend_from_slice(&l.from);
buf.extend_from_slice(&l.to);
buf.extend_from_slice(&l.token);
buf.extend_from_slice(&l.amount.to_le_bytes());
buf.push(l.valence as u8);
buf.extend_from_slice(&l.height.to_le_bytes());
}
buf
}
fn deserialize_signal(buf: &[u8]) -> Option<Signal> {
let mut p = 0usize;
let neuron = take32(buf, &mut p)?;
let step = take_u64(buf, &mut p)?;
let prev = take32(buf, &mut p)?;
let height = take_u64(buf, &mut p)?;
let count = take_u32(buf, &mut p)? as usize;
let mut links = Vec::with_capacity(count);
for _ in 0..count {
links.push(CyberlinkRecord {
neuron: take32(buf, &mut p)?,
from: take32(buf, &mut p)?,
to: take32(buf, &mut p)?,
token: take32(buf, &mut p)?,
amount: take_u64(buf, &mut p)?,
valence: take_u8(buf, &mut p)? as i8,
height: take_u64(buf, &mut p)?,
});
}
Some(Signal {
neuron,
network: SELF_NETWORK,
links,
delta_pi: vec![],
prev,
step,
height,
proof: None,
})
}
fn take32(buf: &[u8], p: &mut usize) -> Option<[u8; 32]> {
let slice = buf.get(*p..*p + 32)?;
*p += 32;
slice.try_into().ok()
}
fn take_u64(buf: &[u8], p: &mut usize) -> Option<u64> {
let slice = buf.get(*p..*p + 8)?;
*p += 8;
Some(u64::from_le_bytes(slice.try_into().ok()?))
}
fn take_u32(buf: &[u8], p: &mut usize) -> Option<u32> {
let slice = buf.get(*p..*p + 4)?;
*p += 4;
Some(u32::from_le_bytes(slice.try_into().ok()?))
}
fn take_u8(buf: &[u8], p: &mut usize) -> Option<u8> {
let b = *buf.get(*p)?;
*p += 1;
Some(b)
}
fn serialize_intent(i: &IntentRecord) -> Vec<u8> {
let mut buf = Vec::with_capacity(32 + 8 + 32 + 64);
buf.extend_from_slice(&i.neuron);
buf.extend_from_slice(&i.h0.to_le_bytes());
buf.extend_from_slice(&i.scope_hash);
buf.extend_from_slice(&i.signature);
buf
}
fn deserialize_intent(buf: &[u8]) -> Option<IntentRecord> {
let mut p = 0usize;
let neuron = take32(buf, &mut p)?;
let h0 = take_u64(buf, &mut p)?;
let scope_hash = take32(buf, &mut p)?;
let signature: [u8; 64] = buf.get(p..p + 64)?.try_into().ok()?;
Some(IntentRecord { neuron, h0, scope_hash, signature })
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Signal;
fn empty_signal() -> Signal {
Signal {
neuron: [1u8; 32],
network: crate::SELF_NETWORK,
links: vec![],
delta_pi: vec![],
prev: [0u8; 32],
step: 0,
height: 0,
proof: None,
}
}
fn empty_intent() -> IntentRecord {
IntentRecord {
neuron: [1u8; 32],
h0: 0,
scope_hash: [2u8; 32],
signature: [0u8; 64],
}
}
#[test]
fn signal_frame_starts_with_tape_marker_and_zap_sigil() {
let frame = encode_signal_frame(&empty_signal());
assert_eq!(frame[0], 0x1F, "tape unit-separator marker");
assert_eq!(frame[1], sigil::ZAP, "signal sigil");
assert_eq!(frame[2], RENDER_BIN);
}
#[test]
fn intent_frame_starts_with_tape_marker_and_ket_sigil() {
let frame = encode_intent_frame(&empty_intent());
assert_eq!(frame[0], 0x1F);
assert_eq!(frame[1], sigil::KET);
assert_eq!(frame[2], RENDER_BIN);
}
#[test]
fn signal_payload_length_matches_serialization() {
let s = empty_signal();
let payload = serialize_signal(&s);
assert_eq!(payload.len(), 32 + 8 + 32 + 8 + 4);
}
#[test]
fn signal_frame_roundtrips_through_decode() {
let mut s = empty_signal();
s.step = 7;
s.height = 42;
s.prev = [9u8; 32];
s.links.push(CyberlinkRecord {
neuron: [1u8; 32],
from: [2u8; 32],
to: [3u8; 32],
token: [4u8; 32],
amount: 5,
valence: -1,
height: 42,
});
let frame = encode_signal_frame(&s);
let back = decode_signal_frame(&frame).expect("decodes");
assert_eq!(back.neuron, s.neuron);
assert_eq!(back.step, s.step);
assert_eq!(back.prev, s.prev);
assert_eq!(back.height, s.height);
assert_eq!(back.links.len(), 1);
assert_eq!(back.links[0].to, [3u8; 32]);
assert_eq!(back.links[0].valence, -1);
assert_eq!(back.hash(), s.hash());
}
#[test]
fn decode_rejects_non_signal_bytes() {
assert!(decode_signal_frame(b"not a tape frame").is_none());
assert!(decode_signal_frame(&[]).is_none());
}
#[test]
fn intent_payload_length_is_fixed() {
let i = empty_intent();
let payload = serialize_intent(&i);
assert_eq!(payload.len(), 136);
}
}