#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum SyncType {
Full = 0,
Incremental = 1,
}
impl From<u8> for SyncType {
fn from(value: u8) -> Self {
match value {
0 => SyncType::Full,
1 => SyncType::Incremental,
_ => SyncType::Full,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct SyncRequest {
pub slave_id: u8,
pub sync_type: SyncType,
pub last_log_index: u32,
}
impl SyncRequest {
pub fn new_full(slave_id: u8) -> Self {
Self {
slave_id,
sync_type: SyncType::Full,
last_log_index: 0,
}
}
pub fn new_incremental(slave_id: u8, last_log_index: u32) -> Self {
Self {
slave_id,
sync_type: SyncType::Incremental,
last_log_index,
}
}
pub fn encode(&self) -> alloc::vec::Vec<u8> {
let mut data = alloc::vec::Vec::with_capacity(6);
data.push(self.slave_id);
data.push(self.sync_type as u8);
if self.sync_type == SyncType::Incremental {
data.extend_from_slice(&self.last_log_index.to_le_bytes());
}
data
}
pub fn decode(data: &[u8]) -> Option<Self> {
if data.len() < 2 {
return None;
}
let slave_id = data[0];
let sync_type = SyncType::from(data[1]);
let last_log_index = if sync_type == SyncType::Incremental {
if data.len() < 6 {
return None;
}
u32::from_le_bytes([data[2], data[3], data[4], data[5]])
} else {
0
};
Some(Self {
slave_id,
sync_type,
last_log_index,
})
}
}
#[derive(Clone, Copy, Debug)]
pub struct SyncDataBegin {
pub sync_type: SyncType,
pub total_size: u64,
pub chunk_count: u32,
pub table_count: u8,
pub log_count: u32,
}
impl SyncDataBegin {
pub fn new_snapshot(total_size: u64, chunk_count: u32, table_count: u8) -> Self {
Self {
sync_type: SyncType::Full,
total_size,
chunk_count,
table_count,
log_count: 0,
}
}
pub fn new_wal(total_size: u64, chunk_count: u32, log_count: u32) -> Self {
Self {
sync_type: SyncType::Incremental,
total_size,
chunk_count,
table_count: 0,
log_count,
}
}
pub fn encode(&self) -> [u8; 18] {
let mut data = [0u8; 18];
data[0] = self.sync_type as u8;
data[1..9].copy_from_slice(&self.total_size.to_le_bytes());
data[9..13].copy_from_slice(&self.chunk_count.to_le_bytes());
data[13] = self.table_count;
data[14..18].copy_from_slice(&self.log_count.to_le_bytes());
data
}
pub fn decode(data: &[u8]) -> Option<Self> {
if data.len() < 18 {
return None;
}
Some(Self {
sync_type: SyncType::from(data[0]),
total_size: u64::from_le_bytes([
data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8],
]),
chunk_count: u32::from_le_bytes([data[9], data[10], data[11], data[12]]),
table_count: data[13],
log_count: u32::from_le_bytes([data[14], data[15], data[16], data[17]]),
})
}
}
pub const MAX_CHUNK_DATA_SIZE: usize = 60000;
#[derive(Clone, Debug)]
pub struct SyncDataChunk {
pub chunk_index: u32,
pub data_size: u16,
pub data: alloc::vec::Vec<u8>,
}
impl SyncDataChunk {
pub fn new(chunk_index: u32, data: &[u8]) -> Self {
Self {
chunk_index,
data_size: data.len() as u16,
data: data.to_vec(),
}
}
pub fn encode(&self) -> alloc::vec::Vec<u8> {
let mut data = alloc::vec::Vec::with_capacity(6 + self.data.len());
data.extend_from_slice(&self.chunk_index.to_le_bytes());
data.extend_from_slice(&self.data_size.to_le_bytes());
data.extend_from_slice(&self.data);
data
}
pub fn decode(data: &[u8]) -> Option<Self> {
if data.len() < 6 {
return None;
}
let chunk_index = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
let data_size = u16::from_le_bytes([data[4], data[5]]) as usize;
if data.len() < 6 + data_size {
return None;
}
Some(Self {
chunk_index,
data_size: data_size as u16,
data: data[6..6 + data_size].to_vec(),
})
}
}
#[derive(Clone, Copy, Debug)]
pub struct SyncDataEnd {
pub total_chunks: u32,
pub checksum: u32,
}
impl SyncDataEnd {
pub fn new(total_chunks: u32, checksum: u32) -> Self {
Self {
total_chunks,
checksum,
}
}
pub fn encode(&self) -> [u8; 8] {
let mut data = [0u8; 8];
data[0..4].copy_from_slice(&self.total_chunks.to_le_bytes());
data[4..8].copy_from_slice(&self.checksum.to_le_bytes());
data
}
pub fn decode(data: &[u8]) -> Option<Self> {
if data.len() < 8 {
return None;
}
Some(Self {
total_chunks: u32::from_le_bytes([data[0], data[1], data[2], data[3]]),
checksum: u32::from_le_bytes([data[4], data[5], data[6], data[7]]),
})
}
}
#[derive(Clone, Copy, Debug)]
pub struct SyncAck {
pub slave_id: u8,
pub success: bool,
pub chunks_received: u32,
}
impl SyncAck {
pub fn new(slave_id: u8, success: bool, chunks_received: u32) -> Self {
Self {
slave_id,
success,
chunks_received,
}
}
pub fn encode(&self) -> [u8; 6] {
let mut data = [0u8; 6];
data[0] = self.slave_id;
data[1] = if self.success { 1 } else { 0 };
data[2..6].copy_from_slice(&self.chunks_received.to_le_bytes());
data
}
pub fn decode(data: &[u8]) -> Option<Self> {
if data.len() < 6 {
return None;
}
Some(Self {
slave_id: data[0],
success: data[1] != 0,
chunks_received: u32::from_le_bytes([data[2], data[3], data[4], data[5]]),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sync_request_full() {
let req = SyncRequest::new_full(5);
let encoded = req.encode();
let decoded = SyncRequest::decode(&encoded).unwrap();
assert_eq!(decoded.slave_id, 5);
assert_eq!(decoded.sync_type, SyncType::Full);
assert_eq!(decoded.last_log_index, 0);
}
#[test]
fn test_sync_request_incremental() {
let req = SyncRequest::new_incremental(3, 12345);
let encoded = req.encode();
let decoded = SyncRequest::decode(&encoded).unwrap();
assert_eq!(decoded.slave_id, 3);
assert_eq!(decoded.sync_type, SyncType::Incremental);
assert_eq!(decoded.last_log_index, 12345);
}
#[test]
fn test_sync_data_begin() {
let begin = SyncDataBegin::new_snapshot(1024 * 1024, 20, 5);
let encoded = begin.encode();
let decoded = SyncDataBegin::decode(&encoded).unwrap();
assert_eq!(decoded.sync_type, SyncType::Full);
assert_eq!(decoded.total_size, 1024 * 1024);
assert_eq!(decoded.chunk_count, 20);
assert_eq!(decoded.table_count, 5);
}
#[test]
fn test_sync_data_chunk() {
let chunk_data = vec![1, 2, 3, 4, 5];
let chunk = SyncDataChunk::new(10, &chunk_data);
let encoded = chunk.encode();
let decoded = SyncDataChunk::decode(&encoded).unwrap();
assert_eq!(decoded.chunk_index, 10);
assert_eq!(decoded.data_size, 5);
assert_eq!(decoded.data, chunk_data);
}
#[test]
fn test_sync_data_end() {
let end = SyncDataEnd::new(20, 0xDEADBEEF);
let encoded = end.encode();
let decoded = SyncDataEnd::decode(&encoded).unwrap();
assert_eq!(decoded.total_chunks, 20);
assert_eq!(decoded.checksum, 0xDEADBEEF);
}
#[test]
fn test_sync_ack() {
let ack = SyncAck::new(5, true, 20);
let encoded = ack.encode();
let decoded = SyncAck::decode(&encoded).unwrap();
assert_eq!(decoded.slave_id, 5);
assert!(decoded.success);
assert_eq!(decoded.chunks_received, 20);
}
}