mod input;
#[cfg(test)]
mod tests;
use crate::error::{Error, Result};
use crate::varint::{put_u32, put_u64};
use input::{Input, put_key, set_once};
const VERSION: u8 = 2;
const VERSION_1: u8 = 1;
const FLUSHED_SEQUENCE: u8 = 1;
const NEXT_FILE: u8 = 2;
const ADDED_TABLE: u8 = 3;
const REMOVED_TABLE: u8 = 4;
const ADDED_KEYSPACE: u8 = 5;
const DROPPED_KEYSPACE: u8 = 6;
const NEXT_KEYSPACE: u8 = 7;
const STORE_FORMAT: u8 = 8;
const SKIPPABLE: u8 = 0x80;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct KeyspaceRecord {
pub id: u32,
pub name: String,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TableMeta {
pub keyspace: u32,
pub level: u8,
pub id: u64,
pub size: u64,
pub smallest: Vec<u8>,
pub largest: Vec<u8>,
pub min_sequence: u64,
pub max_sequence: u64,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct TableRef {
pub keyspace: u32,
pub level: u8,
pub id: u64,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ManifestEdit {
pub store_format: Option<u32>,
pub flushed_sequence: Option<u64>,
pub next_file: Option<u64>,
pub added: Vec<TableMeta>,
pub removed: Vec<TableRef>,
pub added_keyspaces: Vec<KeyspaceRecord>,
pub dropped_keyspaces: Vec<u32>,
pub next_keyspace: Option<u32>,
}
impl ManifestEdit {
pub fn encode(&self, out: &mut Vec<u8>) {
out.push(VERSION);
if let Some(format) = self.store_format {
out.push(STORE_FORMAT);
put_u32(out, format);
}
if let Some(sequence) = self.flushed_sequence {
out.push(FLUSHED_SEQUENCE);
put_u64(out, sequence);
}
if let Some(file) = self.next_file {
out.push(NEXT_FILE);
put_u64(out, file);
}
for table in &self.added {
out.push(ADDED_TABLE);
put_u32(out, table.keyspace);
out.push(table.level);
for value in [table.id, table.size, table.min_sequence, table.max_sequence] {
put_u64(out, value);
}
put_key(out, &table.smallest);
put_key(out, &table.largest);
}
for table in &self.removed {
out.push(REMOVED_TABLE);
put_u32(out, table.keyspace);
out.push(table.level);
put_u64(out, table.id);
}
for keyspace in &self.added_keyspaces {
out.push(ADDED_KEYSPACE);
put_u32(out, keyspace.id);
put_key(out, keyspace.name.as_bytes());
}
for id in &self.dropped_keyspaces {
out.push(DROPPED_KEYSPACE);
put_u32(out, *id);
}
if let Some(id) = self.next_keyspace {
out.push(NEXT_KEYSPACE);
put_u32(out, id);
}
}
pub fn decode(bytes: &[u8]) -> Result<Self> {
let mut input = Input { bytes };
let version = input.byte("manifest edit version")?;
if version != VERSION && version != VERSION_1 {
return Err(Error::UnsupportedVersion {
what: "manifest edit",
version,
});
}
let later = version == VERSION;
let mut edit = Self::default();
while !input.is_empty() {
match input.byte("manifest edit field")? {
STORE_FORMAT if later => set_once(&mut edit.store_format, input.u32()?)?,
tag if later && tag & SKIPPABLE != 0 => input.skip()?,
FLUSHED_SEQUENCE => set_once(&mut edit.flushed_sequence, input.u64()?)?,
NEXT_FILE => set_once(&mut edit.next_file, input.u64()?)?,
ADDED_TABLE => edit.added.push(TableMeta {
keyspace: input.u32()?,
level: input.byte("manifest edit level")?,
id: input.u64()?,
size: input.u64()?,
min_sequence: input.u64()?,
max_sequence: input.u64()?,
smallest: input.key()?,
largest: input.key()?,
}),
REMOVED_TABLE => edit.removed.push(TableRef {
keyspace: input.u32()?,
level: input.byte("manifest edit level")?,
id: input.u64()?,
}),
ADDED_KEYSPACE => edit.added_keyspaces.push(KeyspaceRecord {
id: input.u32()?,
name: String::from_utf8(input.key()?).map_err(|_| Error::Malformed {
what: "manifest edit",
reason: "a keyspace name is not UTF-8",
})?,
}),
DROPPED_KEYSPACE => edit.dropped_keyspaces.push(input.u32()?),
NEXT_KEYSPACE => set_once(&mut edit.next_keyspace, input.u32()?)?,
_ => {
return Err(Error::Malformed {
what: "manifest edit",
reason: "unknown field tag",
});
}
}
}
Ok(edit)
}
}
pub fn peek_store_format(bytes: &[u8]) -> Result<Option<u32>> {
let mut input = Input { bytes };
match input.byte("manifest edit version")? {
0 => Err(Error::UnsupportedVersion {
what: "manifest edit",
version: 0,
}),
VERSION_1 => Ok(None),
_ => {
if input.byte("manifest edit field")? != STORE_FORMAT {
return Err(Error::Malformed {
what: "manifest edit",
reason: "the first field is not the store format",
});
}
input.u32().map(Some)
}
}
}