use crate::ingress::buffer::decode_qwp_varint as decode_varint;
#[cfg(test)]
use std::sync::atomic::{AtomicBool, Ordering};
const QWP_HEADER_SIZE: usize = 12;
const QWP_MAGIC: [u8; 4] = *b"QWP1";
const HEADER_OFFSET_FLAGS: usize = 5;
const QWP_FLAG_DELTA_SYMBOL_DICT: u8 = 0x08;
const CATCH_UP_VARINT_HEADROOM: usize = 16;
#[cfg(test)]
const FAIL_CATCH_UP_ALLOCATION_SYMBOL: &[u8] = b"qdb-test-catch-up-allocation";
#[cfg(test)]
static FAIL_NEXT_MATCHING_CATCH_UP_ALLOCATION: AtomicBool = AtomicBool::new(false);
#[cfg(test)]
pub(crate) fn fail_next_catch_up_allocation_for_test() {
FAIL_NEXT_MATCHING_CATCH_UP_ALLOCATION.store(true, Ordering::Release);
}
#[cfg(test)]
fn should_fail_catch_up_allocation_for_test(entries: &[u8]) -> bool {
entries
.windows(FAIL_CATCH_UP_ALLOCATION_SYMBOL.len())
.any(|window| window == FAIL_CATCH_UP_ALLOCATION_SYMBOL)
&& FAIL_NEXT_MATCHING_CATCH_UP_ALLOCATION.swap(false, Ordering::AcqRel)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct CatchUpEntryTooLarge {
pub(crate) entry_bytes: usize,
pub(crate) budget: usize,
}
#[derive(Debug, Default)]
pub(crate) struct SentDictMirror {
bytes: Vec<u8>,
count: u32,
enabled: bool,
seek_hint: std::cell::Cell<(usize, usize)>,
}
impl SentDictMirror {
pub(crate) fn new(enabled: bool) -> Self {
Self {
bytes: Vec::new(),
count: 0,
enabled,
seek_hint: std::cell::Cell::new((0, 0)),
}
}
fn entry_offset(&self, n: usize) -> usize {
let (hint_idx, hint_off) = self.seek_hint.get();
let (start_idx, start_off) = if hint_idx <= n && hint_off <= self.bytes.len() {
(hint_idx, hint_off)
} else {
(0, 0)
};
match skip_entries_checked(&self.bytes[start_off..], n - start_idx) {
Some(rel) => {
let off = start_off + rel;
self.seek_hint.set((n, off));
off
}
None => skip_entries(&self.bytes, n),
}
}
pub(crate) fn is_enabled(&self) -> bool {
self.enabled
}
pub(crate) fn count(&self) -> u32 {
self.count
}
pub(crate) fn is_empty(&self) -> bool {
self.count == 0
}
pub(crate) fn into_entries(self) -> Vec<u8> {
self.bytes
}
#[must_use = "an allocation failure leaves the mirror disabled and empty; a \
caller folding recovered state must fail rather than continue"]
pub(crate) fn seed(&mut self, entries: &[u8], count: u32) -> bool {
if !self.enabled {
return false;
}
self.bytes.clear();
self.count = 0;
self.seek_hint.set((0, 0));
if count == 0 {
return true;
}
if self.bytes.try_reserve(entries.len()).is_err() {
self.enabled = false;
self.count = 0;
return false;
}
self.bytes.extend_from_slice(entries);
self.count = count;
true
}
pub(crate) fn seed_owned(&mut self, entries: Vec<u8>, count: u32) {
if !self.enabled || count == 0 {
return;
}
self.bytes = entries;
self.count = count;
self.seek_hint.set((0, 0));
}
#[must_use = "an allocation failure leaves the mirror disabled and empty; a \
caller folding recovered state must fail rather than continue"]
pub(crate) fn accumulate(&mut self, frame: &[u8]) -> bool {
if !self.enabled {
return true;
}
let Some(section) = parse_delta_section(frame) else {
return true;
};
if section.delta_count == 0 {
return true;
}
let tip = u64::from(self.count);
let frame_end = section
.delta_start
.saturating_add(u64::from(section.delta_count));
if section.delta_start > tip || frame_end <= tip {
return true;
}
let skip = (tip - section.delta_start) as usize;
let suffix_off = skip_entries(section.entries, skip);
let suffix = §ion.entries[suffix_off..];
if self.bytes.try_reserve(suffix.len()).is_err() {
self.enabled = false;
self.count = 0;
self.bytes = Vec::new();
self.seek_hint.set((0, 0));
return false;
}
self.bytes.extend_from_slice(suffix);
debug_assert!(
frame_end <= u64::from(u32::MAX),
"catch-up mirror count {frame_end} exceeds u32::MAX"
);
self.count = frame_end as u32;
true
}
pub(crate) fn conflicts_with(&self, frame: &[u8]) -> bool {
if !self.enabled {
return false;
}
let Some(section) = parse_delta_section(frame) else {
return false;
};
let tip = u64::from(self.count);
if section.delta_count == 0 || section.delta_start >= tip {
return false;
}
let frame_end = section
.delta_start
.saturating_add(u64::from(section.delta_count));
let overlap_end = frame_end.min(tip);
let overlap_entries = (overlap_end - section.delta_start) as usize;
let mirror_lo = self.entry_offset(section.delta_start as usize);
let mirror_hi = self.entry_offset(overlap_end as usize);
let frame_hi = skip_entries(section.entries, overlap_entries);
self.bytes[mirror_lo..mirror_hi] != section.entries[..frame_hi]
}
pub(crate) fn for_each_catch_up_frame<E>(
&self,
server_max_batch_size: usize,
version: u8,
mut emit: impl FnMut(&[u8]) -> Result<(), E>,
) -> Result<u64, CatchUpStreamError<E>> {
if self.count == 0 {
return Ok(0);
}
let budget = if server_max_batch_size > 0 {
server_max_batch_size
.saturating_sub(QWP_HEADER_SIZE + CATCH_UP_VARINT_HEADROOM)
.max(1)
} else {
(u32::MAX as usize).saturating_sub(QWP_HEADER_SIZE + CATCH_UP_VARINT_HEADROOM)
};
let mut emitted: u64 = 0;
let mut chunk_start_id: u32 = 0;
let mut chunk_start_off: usize = 0;
let mut chunk_symbols: u32 = 0;
let mut chunk_bytes: usize = 0;
let mut p = 0usize;
while p < self.bytes.len() {
let entry_start = p;
let Some((len, after_len)) = decode_varint(&self.bytes, p) else {
break;
};
let Some(entry_end) = after_len
.checked_add(len as usize)
.filter(|end| *end <= self.bytes.len())
else {
break;
};
p = entry_end;
let entry_bytes = p - entry_start;
if entry_bytes > budget {
return Err(CatchUpStreamError::EntryTooLarge(CatchUpEntryTooLarge {
entry_bytes,
budget,
}));
}
if chunk_symbols > 0 && chunk_bytes + entry_bytes > budget {
let frame = build_catch_up_frame(
chunk_start_id,
chunk_symbols,
&self.bytes[chunk_start_off..entry_start],
version,
)
.map_err(CatchUpStreamError::FrameBuild)?;
emit(&frame).map_err(CatchUpStreamError::Emit)?;
emitted += 1;
chunk_start_id += chunk_symbols;
chunk_start_off = entry_start;
chunk_symbols = 0;
chunk_bytes = 0;
}
chunk_symbols += 1;
chunk_bytes += entry_bytes;
}
if chunk_symbols > 0 {
let frame = build_catch_up_frame(
chunk_start_id,
chunk_symbols,
&self.bytes[chunk_start_off..p],
version,
)
.map_err(CatchUpStreamError::FrameBuild)?;
emit(&frame).map_err(CatchUpStreamError::Emit)?;
emitted += 1;
}
Ok(emitted)
}
#[cfg(test)]
pub(crate) fn build_catch_up_frames(
&self,
server_max_batch_size: usize,
version: u8,
) -> Result<Vec<Vec<u8>>, CatchUpEntryTooLarge> {
let mut frames = Vec::new();
match self.for_each_catch_up_frame::<std::convert::Infallible>(
server_max_batch_size,
version,
|frame| {
frames.push(frame.to_vec());
Ok(())
},
) {
Ok(_) => Ok(frames),
Err(CatchUpStreamError::EntryTooLarge(e)) => Err(e),
Err(CatchUpStreamError::FrameBuild(_)) => {
unreachable!("catch-up frame build cannot fail at test scale")
}
Err(CatchUpStreamError::Emit(never)) => match never {},
}
}
}
pub(crate) enum CatchUpStreamError<E> {
EntryTooLarge(CatchUpEntryTooLarge),
FrameBuild(CatchUpFrameBuildError),
Emit(E),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum CatchUpFrameBuildError {
AllocationFailed,
PayloadTooLarge,
}
pub(crate) fn frame_delta_start(frame: &[u8]) -> Option<u64> {
if !is_delta_frame(frame) {
return None;
}
decode_varint(frame, QWP_HEADER_SIZE).map(|(v, _)| v)
}
fn is_delta_frame(frame: &[u8]) -> bool {
frame.len() >= QWP_HEADER_SIZE
&& frame[0..4] == QWP_MAGIC
&& frame[HEADER_OFFSET_FLAGS] & QWP_FLAG_DELTA_SYMBOL_DICT != 0
}
struct DeltaSection<'a> {
delta_start: u64,
delta_count: u32,
entries: &'a [u8],
}
fn parse_delta_section(frame: &[u8]) -> Option<DeltaSection<'_>> {
if !is_delta_frame(frame) {
return None;
}
let (delta_start, p) = decode_varint(frame, QWP_HEADER_SIZE)?;
let (delta_count, mut p) = decode_varint(frame, p)?;
let delta_count = u32::try_from(delta_count).ok()?;
let region_start = p;
for _ in 0..delta_count {
let (len, after_len) = decode_varint(frame, p)?;
p = after_len.checked_add(len as usize)?;
if p > frame.len() {
return None;
}
}
Some(DeltaSection {
delta_start,
delta_count,
entries: &frame[region_start..p],
})
}
fn build_catch_up_frame(
delta_start: u32,
delta_count: u32,
entries: &[u8],
version: u8,
) -> Result<Vec<u8>, CatchUpFrameBuildError> {
#[cfg(test)]
if should_fail_catch_up_allocation_for_test(entries) {
return Err(CatchUpFrameBuildError::AllocationFailed);
}
let mut payload = Vec::new();
let payload_capacity = CATCH_UP_VARINT_HEADROOM
.checked_add(entries.len())
.ok_or(CatchUpFrameBuildError::PayloadTooLarge)?;
payload
.try_reserve(payload_capacity)
.map_err(|_| CatchUpFrameBuildError::AllocationFailed)?;
write_varint(&mut payload, u64::from(delta_start));
write_varint(&mut payload, u64::from(delta_count));
payload.extend_from_slice(entries);
let payload_len =
u32::try_from(payload.len()).map_err(|_| CatchUpFrameBuildError::PayloadTooLarge)?;
let mut frame = Vec::new();
let frame_capacity = QWP_HEADER_SIZE
.checked_add(payload.len())
.ok_or(CatchUpFrameBuildError::PayloadTooLarge)?;
frame
.try_reserve(frame_capacity)
.map_err(|_| CatchUpFrameBuildError::AllocationFailed)?;
frame.extend_from_slice(&QWP_MAGIC);
frame.push(version);
frame.push(QWP_FLAG_DELTA_SYMBOL_DICT);
frame.extend_from_slice(&0u16.to_le_bytes()); frame.extend_from_slice(&payload_len.to_le_bytes());
frame.extend_from_slice(&payload);
Ok(frame)
}
fn skip_entries_checked(entries: &[u8], n: usize) -> Option<usize> {
let mut p = 0usize;
for _ in 0..n {
let (len, after_len) = decode_varint(entries, p)?;
let end = after_len.checked_add(len as usize)?;
if end > entries.len() {
return None;
}
p = end;
}
Some(p)
}
fn skip_entries(entries: &[u8], n: usize) -> usize {
let mut p = 0usize;
for _ in 0..n {
let Some((len, after_len)) = decode_varint(entries, p) else {
return p;
};
match after_len.checked_add(len as usize) {
Some(end) if end <= entries.len() => p = end,
_ => return p,
}
}
p
}
fn write_varint(out: &mut Vec<u8>, mut value: u64) {
while value > 0x7F {
out.push(((value & 0x7F) as u8) | 0x80);
value >>= 7;
}
out.push(value as u8);
}
#[cfg(test)]
pub(crate) fn make_delta_frame(delta_start: u64, entries: &[&[u8]], table_junk: &[u8]) -> Vec<u8> {
let mut payload = Vec::new();
write_varint(&mut payload, delta_start);
write_varint(&mut payload, entries.len() as u64);
for e in entries {
write_varint(&mut payload, e.len() as u64);
payload.extend_from_slice(e);
}
payload.extend_from_slice(table_junk);
let mut frame = Vec::new();
frame.extend_from_slice(&QWP_MAGIC);
frame.push(1);
frame.push(QWP_FLAG_DELTA_SYMBOL_DICT);
frame.extend_from_slice(&1u16.to_le_bytes());
frame.extend_from_slice(&(payload.len() as u32).to_le_bytes());
frame.extend_from_slice(&payload);
frame
}
#[cfg(test)]
mod tests {
use super::*;
fn make_frame(delta_start: u64, entries: &[&[u8]], table_junk: &[u8]) -> Vec<u8> {
make_delta_frame(delta_start, entries, table_junk)
}
fn symbols_from_catch_up(frames: &[Vec<u8>]) -> Vec<Vec<u8>> {
let mut out = Vec::new();
let mut expected_start = 0u64;
for f in frames {
let s = parse_delta_section(f).expect("catch-up frame parses");
assert_eq!(
s.delta_start, expected_start,
"catch-up ranges are contiguous"
);
expected_start += u64::from(s.delta_count);
let mut p = 0usize;
for _ in 0..s.delta_count {
let (len, after) = decode_varint(s.entries, p).unwrap();
out.push(s.entries[after..after + len as usize].to_vec());
p = after + len as usize;
}
}
out
}
#[test]
fn accumulate_extends_on_contiguous_delta() {
let mut m = SentDictMirror::new(true);
assert!(
m.accumulate(&make_frame(0, &[b"AAPL", b"GOOG"], b"tabledata")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 2);
assert!(
m.accumulate(&make_frame(2, &[b"MSFT"], b"more")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 3);
let frames = m.build_catch_up_frames(0, 1).unwrap();
assert_eq!(
symbols_from_catch_up(&frames),
vec![b"AAPL".to_vec(), b"GOOG".to_vec(), b"MSFT".to_vec()]
);
}
#[test]
fn accumulate_skips_replay_overlap_and_empty() {
let mut m = SentDictMirror::new(true);
assert!(
m.accumulate(&make_frame(0, &[b"A", b"B"], b"")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 2);
assert!(
m.accumulate(&make_frame(0, &[b"A", b"B"], b"")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 2);
assert!(
m.accumulate(&make_frame(2, &[], b"")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 2);
assert!(
m.accumulate(&make_frame(5, &[b"X"], b"")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 2);
}
#[test]
fn accumulate_folds_an_overlapping_frame_that_extends_past_a_short_seed() {
let mut m = SentDictMirror::new(true);
assert!(m.seed(&[1, b'a'], 1), "seeding a small region cannot fail");
assert_eq!(m.count(), 1);
assert!(
m.accumulate(&make_frame(0, &[b"a", b"b", b"c"], b"table")),
"folding a small frame cannot fail"
);
assert_eq!(
m.count(),
3,
"an overlapping frame that reaches past the seed extends the mirror"
);
let frames = m.build_catch_up_frames(0, 1).unwrap();
assert_eq!(
symbols_from_catch_up(&frames),
vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]
);
}
#[test]
fn conflicts_with_flags_a_differing_redefinition_but_not_a_matching_one() {
let mut m = SentDictMirror::new(true);
assert!(m.seed(&[1, b'A'], 1), "seeding a small region cannot fail"); assert!(
m.accumulate(&make_frame(1, &[b"B"], b"")),
"folding a small frame cannot fail"
); assert_eq!(m.count(), 2);
assert!(m.conflicts_with(&make_frame(1, &[b"C"], b"")));
assert!(m.conflicts_with(&make_frame(1, &[b"C", b"D"], b"")));
assert!(!m.conflicts_with(&make_frame(1, &[b"B"], b"")));
assert!(!m.conflicts_with(&make_frame(0, &[b"A", b"B"], b"")));
assert!(!m.conflicts_with(&make_frame(1, &[b"B", b"D"], b"")));
assert!(!m.conflicts_with(&make_frame(2, &[b"C"], b"")));
assert!(!m.conflicts_with(&make_frame(5, &[b"X"], b"")));
assert!(!m.conflicts_with(&make_frame(2, &[], b"")));
let disabled = SentDictMirror::new(false);
assert!(!disabled.conflicts_with(&make_frame(1, &[b"C"], b"")));
}
#[test]
fn disabled_mirror_is_inert() {
let mut m = SentDictMirror::new(false);
assert!(
m.accumulate(&make_frame(0, &[b"A"], b"")),
"folding a small frame cannot fail"
);
assert!(
!m.seed(&[1, b'z'], 1),
"a disabled mirror reports `false`: it holds nothing, so a caller \
folding recovered state must not treat it as seeded"
);
assert_eq!(m.count(), 0);
assert!(m.build_catch_up_frames(0, 1).unwrap().is_empty());
}
#[test]
fn the_seek_hint_survives_a_dense_frame_that_bases_below_it() {
let mut m = SentDictMirror::new(true);
assert!(m.seed(&[1, b'a'], 1), "seeding a small region cannot fail");
assert!(
m.accumulate(&make_frame(1, &[b"bb", b"ccc", b"dddd"], b"")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 4);
assert!(!m.conflicts_with(&make_frame(3, &[b"dddd", b"e"], b"")));
assert!(
!m.conflicts_with(&make_frame(0, &[b"a", b"bb", b"ccc", b"dddd"], b"")),
"a dense frame that agrees with the mirror is a benign replay"
);
assert!(
m.conflicts_with(&make_frame(0, &[b"a", b"bb", b"XXX", b"dddd"], b"")),
"...and one that disagrees at id 2 must still be caught after the hint \
has advanced past it"
);
assert!(!m.conflicts_with(&make_frame(2, &[b"ccc", b"dddd"], b"")));
assert!(m.conflicts_with(&make_frame(1, &[b"ZZ"], b"")));
}
#[test]
fn seed_resets_the_seek_hint_and_the_region_even_when_given_nothing() {
let mut m = SentDictMirror::new(true);
assert!(m.seed(&[1, b'a', 2, b'b', b'b'], 2));
assert!(!m.conflicts_with(&make_frame(0, &[b"a", b"bb"], b"")));
assert!(m.seed(&[], 0), "an empty seed reports success");
assert_eq!(m.count(), 0);
assert!(
m.build_catch_up_frames(0, 1).unwrap().is_empty(),
"the previous region must be gone, not merely unreachable via `count`"
);
assert!(m.seed(&[3, b'x', b'y', b'z'], 1));
assert!(!m.conflicts_with(&make_frame(0, &[b"xyz"], b"")));
assert!(m.conflicts_with(&make_frame(0, &[b"a"], b"")));
}
#[test]
fn seed_from_persisted_then_extend() {
let mut m = SentDictMirror::new(true);
assert!(
m.seed(&[1, b'a', 1, b'b'], 2),
"seeding a small region cannot fail"
);
assert_eq!(m.count(), 2);
assert!(
m.accumulate(&make_frame(2, &[b"c"], b"")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 3);
let frames = m.build_catch_up_frames(0, 1).unwrap();
assert_eq!(
symbols_from_catch_up(&frames),
vec![b"a".to_vec(), b"b".to_vec(), b"c".to_vec()]
);
}
#[test]
fn recovered_side_file_rebuilds_the_catch_up_dictionary() {
use crate::ingress::sender::qwp_ws_sfa_symbol_dict::PersistedSymbolDict;
let dir = tempfile::tempdir().unwrap();
{
let mut pd = PersistedSymbolDict::open(dir.path()).unwrap();
pd.append_symbol(b"alpha").unwrap();
pd.append_symbol(b"bravo").unwrap();
}
let pd = PersistedSymbolDict::open(dir.path()).unwrap();
let mut mirror = SentDictMirror::new(true);
assert!(
mirror.seed(pd.loaded_entries(), pd.size()),
"seeding a small region cannot fail"
);
assert_eq!(mirror.count(), 2);
let frames = mirror.build_catch_up_frames(0, 1).unwrap();
assert_eq!(
symbols_from_catch_up(&frames),
vec![b"alpha".to_vec(), b"bravo".to_vec()]
);
}
#[test]
fn catch_up_single_frame_when_uncapped() {
let mut m = SentDictMirror::new(true);
assert!(
m.accumulate(&make_frame(0, &[b"AAPL", b"GOOG", b"MSFT"], b"")),
"folding a small frame cannot fail"
);
let frames = m.build_catch_up_frames(0, 7).unwrap();
assert_eq!(frames.len(), 1);
assert_eq!(frame_delta_start(&frames[0]), Some(0));
}
#[test]
fn catch_up_splits_by_cap_and_reassembles_gap_free() {
let mut m = SentDictMirror::new(true);
let syms: Vec<Vec<u8>> = (0..10).map(|i| format!("sy{i:02}").into_bytes()).collect();
let refs: Vec<&[u8]> = syms.iter().map(|s| s.as_slice()).collect();
assert!(
m.accumulate(&make_frame(0, &refs, b"")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 10);
let cap = 12 + 16 + 15;
let frames = m.build_catch_up_frames(cap, 3).unwrap();
assert!(
frames.len() > 1,
"expected a multi-frame split, got {}",
frames.len()
);
for f in &frames {
assert!(f.len() <= cap, "frame {} exceeds cap {}", f.len(), cap);
assert_eq!(f[4], 3, "catch-up frames carry the negotiated version");
}
assert_eq!(symbols_from_catch_up(&frames), syms);
}
#[test]
fn catch_up_errors_when_entry_exceeds_cap() {
let mut m = SentDictMirror::new(true);
assert!(
m.accumulate(&make_frame(0, &[b"a_very_long_symbol_value"], b"")),
"folding a small frame cannot fail"
);
let err = m.build_catch_up_frames(20, 1).unwrap_err();
assert!(err.entry_bytes > err.budget);
}
#[test]
fn catch_up_degrades_on_a_torn_mirror_instead_of_aborting() {
let mut m = SentDictMirror::new(true);
assert!(
m.accumulate(&make_frame(0, &[b"AAPL", b"GOOG"], b"")),
"folding a small frame cannot fail"
);
assert_eq!(m.count(), 2);
m.bytes.truncate(m.bytes.len() - 2);
let frames = m.build_catch_up_frames(0, 1).unwrap();
assert_eq!(
symbols_from_catch_up(&frames),
vec![b"AAPL".to_vec()],
"catch-up re-registers the valid prefix and stops at the tear"
);
}
#[test]
fn round_trip_accumulate_catch_up_reaccumulate() {
let mut sender = SentDictMirror::new(true);
assert!(
sender.accumulate(&make_frame(0, &[b"one", b"two"], b"t")),
"folding a small frame cannot fail"
);
assert!(
sender.accumulate(&make_frame(2, &[b"three"], b"t")),
"folding a small frame cannot fail"
);
assert!(
sender.accumulate(&make_frame(3, &[b"four", b"five"], b"t")),
"folding a small frame cannot fail"
);
let frames = sender.build_catch_up_frames(12 + 16 + 8, 1).unwrap();
let mut replayed = SentDictMirror::new(true);
for f in &frames {
assert!(replayed.accumulate(f), "folding a small frame cannot fail");
}
assert_eq!(replayed.count(), sender.count());
assert_eq!(
symbols_from_catch_up(&replayed.build_catch_up_frames(0, 1).unwrap()),
vec![
b"one".to_vec(),
b"two".to_vec(),
b"three".to_vec(),
b"four".to_vec(),
b"five".to_vec()
]
);
}
#[test]
fn frame_delta_start_ignores_non_delta_frames() {
let mut frame = make_frame(3, &[b"x"], b"");
frame[HEADER_OFFSET_FLAGS] = 0;
assert_eq!(frame_delta_start(&frame), None);
let junk = vec![0u8; 20];
assert_eq!(frame_delta_start(&junk), None);
assert_eq!(frame_delta_start(&make_frame(7, &[b"x"], b"")), Some(7));
}
}