1use std::collections::BTreeSet;
4use std::ops::Deref;
5
6use crate::error::{OpenError, PushError, TryPushError};
7use crate::store::{Op, Store};
8use crate::sync::{Arc, Condvar, Mutex};
9
10const META_KEY: [u8; 1] = [0x00];
14const ENTRY_PREFIX: u8 = 0x01;
15const ENTRY_LOW: [u8; 1] = [ENTRY_PREFIX];
16const ENTRY_HIGH: [u8; 9] = [ENTRY_PREFIX, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
17const FORMAT_VERSION: u8 = 1;
18
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
21pub enum Durability {
22 #[default]
25 Sync,
26 Group,
30 None,
33}
34
35impl Durability {
36 fn durable(self) -> bool {
37 !matches!(self, Durability::None)
38 }
39
40 fn group(self) -> bool {
41 matches!(self, Durability::Group)
42 }
43}
44
45struct Inner {
46 tail: u64,
47 head: u64,
52 acked_above: BTreeSet<u64>,
56 len: usize,
57 bytes: usize,
59 reserved: BTreeSet<u64>,
60 closed: bool,
61}
62
63struct Shared<S> {
64 store: S,
65 capacity: usize,
66 max_bytes: usize,
67 durable: bool,
68 ack_durable: bool,
69 group: bool,
70 inner: Mutex<Inner>,
71 room: Condvar,
72 group_state: Mutex<GroupState>,
73 group_ready: Condvar,
74}
75
76#[derive(Default)]
77struct GroupState {
78 pending: Vec<(u64, Vec<u8>)>,
79 flushing: bool,
80 done: std::collections::BTreeMap<u64, bool>,
81}
82
83impl<S: Store> Shared<S> {
84 fn admits(&self, len: usize, bytes: usize, v: usize) -> bool {
88 len < self.capacity && (len == 0 || bytes.saturating_add(v) <= self.max_bytes)
89 }
90
91 fn group_commit(&self, seq: u64, value: &[u8]) -> Result<(), S::Error> {
95 let mut group = self.group_state.lock().unwrap();
96 group.pending.push((seq, value.to_vec()));
97
98 if group.flushing {
99 while !group.done.contains_key(&seq) {
100 group = self.group_ready.wait(group).unwrap();
101 }
102 } else {
103 group.flushing = true;
104 loop {
105 let batch = std::mem::take(&mut group.pending);
106 if batch.is_empty() {
107 group.flushing = false;
108 break;
109 }
110 drop(group);
111
112 let keys: Vec<[u8; 9]> = batch.iter().map(|(s, _)| entry_key(*s)).collect();
113 let ops: Vec<Op<'_>> = batch
114 .iter()
115 .zip(&keys)
116 .map(|((_, value), key)| Op::Put(key, value))
117 .collect();
118 let ok = self.store.commit(&ops, true).is_ok();
119
120 group = self.group_state.lock().unwrap();
121 for (flushed, _) in &batch {
122 group.done.insert(*flushed, ok);
123 }
124 self.group_ready.notify_all();
125 if group.pending.is_empty() {
126 group.flushing = false;
127 break;
128 }
129 }
130 }
131
132 let outcome = group.done.remove(&seq);
133 drop(group);
134 match outcome {
135 Some(true) => Ok(()),
136 _ => self.store.commit(&[Op::Put(&entry_key(seq), value)], true),
139 }
140 }
141}
142
143pub type Ends<S> = (Producer<S>, Consumer<S>);
145
146pub struct Builder<S> {
148 store: S,
149 capacity: usize,
150 max_bytes: usize,
151 durability: Durability,
152 durable_acks: bool,
153}
154
155impl<S: Store> Builder<S> {
156 pub fn new(store: S) -> Self {
158 Self {
159 store,
160 capacity: 1024,
161 max_bytes: usize::MAX,
162 durability: Durability::Sync,
163 durable_acks: true,
164 }
165 }
166
167 pub fn capacity(mut self, capacity: usize) -> Self {
169 assert!(capacity > 0, "capacity must be greater than 0");
170 self.capacity = capacity;
171 self
172 }
173
174 pub fn max_bytes(mut self, max_bytes: usize) -> Self {
180 assert!(max_bytes > 0, "max_bytes must be greater than 0");
181 self.max_bytes = max_bytes;
182 self
183 }
184
185 pub fn durability(mut self, durability: Durability) -> Self {
187 self.durability = durability;
188 self
189 }
190
191 pub fn durable_acks(mut self, durable_acks: bool) -> Self {
200 self.durable_acks = durable_acks;
201 self
202 }
203
204 pub fn open(self) -> Result<Ends<S>, OpenError<S::Error>> {
206 let durable = self.durability.durable();
207 let group = self.durability.group();
208 let ack_durable = durable && self.durable_acks;
209
210 match self.store.get(&META_KEY).map_err(OpenError::Store)? {
211 Some(meta) => {
212 let version = meta.first().copied().unwrap_or_default();
213 if version != FORMAT_VERSION {
214 return Err(OpenError::UnsupportedVersion(version));
215 }
216 }
217 None => self
218 .store
219 .commit(&[Op::Put(&META_KEY, &[FORMAT_VERSION])], durable)
220 .map_err(OpenError::Store)?,
221 }
222
223 let tail = match self
224 .store
225 .seek_back(&ENTRY_HIGH)
226 .map_err(OpenError::Store)?
227 {
228 Some((key, _)) if is_entry(&key) => seq_of(&key) + 1,
229 _ => 0,
230 };
231
232 let mut len = 0usize;
233 let mut bytes = 0usize;
234 let mut head = tail;
235 let mut cursor = ENTRY_LOW.to_vec();
236 while let Some((key, value)) = self.store.seek(&cursor).map_err(OpenError::Store)? {
237 if !is_entry(&key) {
238 break;
239 }
240 if len == 0 {
241 head = seq_of(&key);
242 }
243 len += 1;
244 bytes += value.len();
245 cursor = entry_key(seq_of(&key) + 1).to_vec();
246 }
247
248 let shared = Arc::new(Shared {
249 store: self.store,
250 capacity: self.capacity,
251 max_bytes: self.max_bytes,
252 durable,
253 ack_durable,
254 group,
255 inner: Mutex::new(Inner {
256 tail,
257 head,
258 acked_above: BTreeSet::new(),
259 len,
260 bytes,
261 reserved: BTreeSet::new(),
262 closed: false,
263 }),
264 room: Condvar::new(),
265 group_state: Mutex::new(GroupState::default()),
266 group_ready: Condvar::new(),
267 });
268 Ok((
269 Producer {
270 shared: Arc::clone(&shared),
271 },
272 Consumer { shared },
273 ))
274 }
275}
276
277pub struct Producer<S> {
279 shared: Arc<Shared<S>>,
280}
281
282impl<S> Clone for Producer<S> {
283 fn clone(&self) -> Self {
284 Self {
285 shared: Arc::clone(&self.shared),
286 }
287 }
288}
289
290impl<S: Store> Producer<S> {
291 pub fn push(&self, value: &[u8]) -> Result<(), PushError<S::Error>> {
293 let seq = {
294 let mut inner = self.shared.inner.lock().unwrap();
295 loop {
296 if inner.closed {
297 return Err(PushError::Closed);
298 }
299 if self.shared.admits(inner.len, inner.bytes, value.len()) {
300 break;
301 }
302 inner = self.shared.room.wait(inner).unwrap();
303 }
304 let seq = inner.tail;
305 inner.tail += 1;
306 inner.len += 1;
307 inner.bytes += value.len();
308 seq
309 };
310 self.write(seq, value).map_err(PushError::Store)
311 }
312
313 pub fn try_push(&self, value: &[u8]) -> Result<(), TryPushError<S::Error>> {
315 let seq = {
316 let mut inner = self.shared.inner.lock().unwrap();
317 if inner.closed {
318 return Err(TryPushError::Closed);
319 }
320 if !self.shared.admits(inner.len, inner.bytes, value.len()) {
321 return Err(TryPushError::Full);
322 }
323 let seq = inner.tail;
324 inner.tail += 1;
325 inner.len += 1;
326 inner.bytes += value.len();
327 seq
328 };
329 self.write(seq, value).map_err(TryPushError::Store)
330 }
331
332 pub fn close(&self) {
334 {
335 let mut inner = self.shared.inner.lock().unwrap();
336 inner.closed = true;
337 }
338 self.shared.room.notify_all();
339 }
340
341 pub fn len(&self) -> usize {
343 self.shared.inner.lock().unwrap().len
344 }
345
346 pub fn is_empty(&self) -> bool {
348 self.len() == 0
349 }
350
351 fn write(&self, seq: u64, value: &[u8]) -> Result<(), S::Error> {
353 let result = if self.shared.group {
354 self.shared.group_commit(seq, value)
355 } else {
356 self.shared
357 .store
358 .commit(&[Op::Put(&entry_key(seq), value)], self.shared.durable)
359 };
360 match result {
361 Ok(()) => Ok(()),
362 Err(e) => {
363 {
364 let mut inner = self.shared.inner.lock().unwrap();
365 inner.len -= 1;
366 inner.bytes -= value.len();
367 }
368 self.shared.room.notify_one();
369 Err(e)
370 }
371 }
372 }
373}
374
375pub struct Consumer<S> {
377 shared: Arc<Shared<S>>,
378}
379
380impl<S: Store> Consumer<S> {
381 pub fn reserve(&self) -> Result<Option<Reserved<S>>, S::Error> {
384 let mut cursor = entry_key(self.shared.inner.lock().unwrap().head).to_vec();
385 loop {
386 match self.shared.store.seek(&cursor)? {
387 Some((key, value)) if is_entry(&key) => {
388 let seq = seq_of(&key);
389 let mut inner = self.shared.inner.lock().unwrap();
390 if inner.reserved.contains(&seq) {
391 drop(inner);
392 cursor = entry_key(seq + 1).to_vec();
393 continue;
394 }
395 inner.reserved.insert(seq);
396 drop(inner);
397 return Ok(Some(Reserved {
398 shared: Arc::clone(&self.shared),
399 seq,
400 value,
401 done: false,
402 }));
403 }
404 _ => return Ok(None),
405 }
406 }
407 }
408
409 #[cfg(feature = "tokio")]
411 pub(crate) fn is_drained(&self) -> bool {
412 let inner = self.shared.inner.lock().unwrap();
413 inner.closed && inner.len == 0
414 }
415}
416
417pub struct Reserved<S: Store> {
420 shared: Arc<Shared<S>>,
421 seq: u64,
422 value: Vec<u8>,
423 done: bool,
424}
425
426impl<S: Store> Reserved<S> {
427 pub fn seq(&self) -> u64 {
429 self.seq
430 }
431
432 pub fn ack(mut self) -> Result<(), S::Error> {
435 let key = entry_key(self.seq);
436 self.shared
437 .store
438 .commit(&[Op::Delete(&key)], self.shared.ack_durable)?;
439 {
440 let mut inner = self.shared.inner.lock().unwrap();
441 inner.reserved.remove(&self.seq);
442 inner.len -= 1;
443 inner.bytes -= self.value.len();
444 if self.seq == inner.head {
448 let mut next = inner.head + 1;
449 while inner.acked_above.remove(&next) {
450 next += 1;
451 }
452 inner.head = next;
453 } else {
454 inner.acked_above.insert(self.seq);
455 }
456 }
457 self.shared.room.notify_one();
458 self.done = true;
459 Ok(())
460 }
461
462 pub fn nack(mut self) {
464 self.release();
465 self.done = true;
466 }
467
468 fn release(&self) {
469 let mut inner = self.shared.inner.lock().unwrap();
470 inner.reserved.remove(&self.seq);
471 }
472}
473
474impl<S: Store> Deref for Reserved<S> {
475 type Target = [u8];
476
477 fn deref(&self) -> &Self::Target {
478 &self.value
479 }
480}
481
482impl<S: Store> Drop for Reserved<S> {
483 fn drop(&mut self) {
484 if !self.done {
485 self.release();
486 }
487 }
488}
489
490fn entry_key(seq: u64) -> [u8; 9] {
491 let mut key = [0u8; 9];
492 key[0] = ENTRY_PREFIX;
493 key[1..].copy_from_slice(&seq.to_be_bytes());
494 key
495}
496
497fn seq_of(key: &[u8]) -> u64 {
498 let mut bytes = [0u8; 8];
499 bytes.copy_from_slice(&key[1..9]);
500 u64::from_be_bytes(bytes)
501}
502
503fn is_entry(key: &[u8]) -> bool {
504 key.len() == 9 && key[0] == ENTRY_PREFIX
505}
506
507#[cfg(test)]
508mod tests {
509 use super::*;
510 use crate::store::MemStore;
511
512 #[test]
513 fn key_roundtrip() {
514 for seq in [0u64, 1, 255, 256, u32::MAX as u64, u64::MAX] {
515 let key = entry_key(seq);
516 assert!(is_entry(&key));
517 assert_eq!(seq_of(&key), seq);
518 }
519 }
520
521 #[test]
522 fn keys_sort_by_seq_after_meta() {
523 assert!(META_KEY[..] < ENTRY_LOW[..]);
524 assert!(ENTRY_LOW[..] < entry_key(0)[..]);
525 assert!(entry_key(1) < entry_key(2));
526 assert!(entry_key(255) < entry_key(256));
527 assert!(entry_key(u64::MAX)[..] <= ENTRY_HIGH[..]);
528 }
529
530 #[test]
533 fn store_orders_keys_by_numeric_seq() {
534 let store = MemStore::new();
535 for &seq in &[300u64, 1, 256, 255, 2, 65_536, 65_535] {
536 store
537 .commit(&[Op::Put(&entry_key(seq), b"x")], false)
538 .unwrap();
539 }
540 assert_eq!(
541 collect_seqs(&store),
542 vec![1, 2, 255, 256, 300, 65_535, 65_536]
543 );
544 }
545
546 #[test]
547 fn open_recovers_tail_len_and_skips_gaps() {
548 let store = MemStore::new();
549 store
550 .commit(
551 &[
552 Op::Put(&entry_key(5), b"five"),
553 Op::Put(&entry_key(7), b"seven"),
554 ],
555 false,
556 )
557 .unwrap();
558
559 let (tx, rx) = Builder::new(store).capacity(8).open().unwrap();
560 assert_eq!(tx.len(), 2);
561
562 tx.push(b"eight").unwrap(); let a = rx.reserve().unwrap().unwrap();
564 assert_eq!((a.seq(), &*a), (5, &b"five"[..]));
565 a.ack().unwrap();
566 let b = rx.reserve().unwrap().unwrap();
567 assert_eq!(b.seq(), 7); b.ack().unwrap();
569 assert_eq!(rx.reserve().unwrap().unwrap().seq(), 8);
570 }
571
572 #[test]
573 fn unsupported_version_is_rejected() {
574 let store = MemStore::new();
575 store.commit(&[Op::Put(&META_KEY, &[2])], false).unwrap();
576 match Builder::new(store).open() {
577 Err(OpenError::UnsupportedVersion(v)) => assert_eq!(v, 2),
578 _ => panic!("expected UnsupportedVersion"),
579 }
580 }
581
582 #[test]
583 fn try_push_is_full_at_capacity() {
584 let (tx, rx) = mem(1);
585 tx.push(b"a").unwrap();
586 assert!(matches!(tx.try_push(b"b"), Err(TryPushError::Full)));
587 rx.reserve().unwrap().unwrap().ack().unwrap();
588 tx.try_push(b"b").unwrap();
589 }
590
591 #[test]
592 fn close_rejects_further_push() {
593 let (tx, _rx) = mem(4);
594 tx.close();
595 assert!(matches!(tx.push(b"a"), Err(PushError::Closed)));
596 assert!(matches!(tx.try_push(b"a"), Err(TryPushError::Closed)));
597 }
598
599 #[test]
600 fn nack_returns_item_for_redelivery() {
601 let (tx, rx) = mem(4);
602 tx.push(b"a").unwrap();
603 rx.reserve().unwrap().unwrap().nack();
604 assert_eq!(rx.reserve().unwrap().unwrap().seq(), 0);
605 }
606
607 #[test]
608 fn drop_returns_item_for_redelivery() {
609 let (tx, rx) = mem(4);
610 tx.push(b"a").unwrap();
611 drop(rx.reserve().unwrap().unwrap());
612 assert_eq!(rx.reserve().unwrap().unwrap().seq(), 0);
613 }
614
615 #[test]
616 fn reserve_is_none_when_empty_or_all_reserved() {
617 let (tx, rx) = mem(4);
618 assert!(rx.reserve().unwrap().is_none());
619 tx.push(b"a").unwrap();
620 let _held = rx.reserve().unwrap().unwrap();
621 assert!(rx.reserve().unwrap().is_none());
622 }
623
624 #[test]
625 fn group_durability_delivers_in_order() {
626 let (tx, rx) = Builder::new(MemStore::new())
627 .capacity(8)
628 .durability(Durability::Group)
629 .open()
630 .unwrap();
631 for i in 0..4u8 {
632 tx.push(&[i]).unwrap();
633 }
634 for i in 0..4u8 {
635 let item = rx.reserve().unwrap().unwrap();
636 assert_eq!(&*item, &[i][..]);
637 item.ack().unwrap();
638 }
639 assert!(rx.reserve().unwrap().is_none());
640 }
641
642 #[test]
643 fn max_bytes_bounds_by_total_size() {
644 let (tx, rx) = Builder::new(MemStore::new())
645 .capacity(100)
646 .max_bytes(10)
647 .open()
648 .unwrap();
649 tx.push(b"aaaaa").unwrap();
650 tx.push(b"bbbbb").unwrap(); assert!(matches!(tx.try_push(b"c"), Err(TryPushError::Full)));
652 rx.reserve().unwrap().unwrap().ack().unwrap(); tx.try_push(b"c").unwrap();
654 }
655
656 #[test]
657 fn oversized_item_is_admitted_into_an_empty_queue() {
658 let (tx, rx) = Builder::new(MemStore::new())
659 .capacity(100)
660 .max_bytes(4)
661 .open()
662 .unwrap();
663 tx.push(b"way bigger than four").unwrap(); assert!(matches!(tx.try_push(b"x"), Err(TryPushError::Full)));
665 rx.reserve().unwrap().unwrap().ack().unwrap();
666 tx.try_push(b"x").unwrap();
667 }
668
669 #[test]
670 fn open_recovers_byte_accounting() {
671 let store = MemStore::new();
672 store
673 .commit(
674 &[
675 Op::Put(&entry_key(0), b"hello"),
676 Op::Put(&entry_key(1), b"world!!"),
677 ],
678 false,
679 )
680 .unwrap();
681
682 let (tx, rx) = Builder::new(store)
684 .capacity(100)
685 .max_bytes(12)
686 .open()
687 .unwrap();
688 assert_eq!(tx.len(), 2);
689 assert!(matches!(tx.try_push(b"x"), Err(TryPushError::Full)));
690
691 let a = rx.reserve().unwrap().unwrap();
692 assert_eq!(a.seq(), 0);
693 a.ack().unwrap(); tx.try_push(b"x").unwrap();
695 }
696
697 fn mem(capacity: usize) -> (Producer<MemStore>, Consumer<MemStore>) {
698 Builder::new(MemStore::new())
699 .capacity(capacity)
700 .open()
701 .unwrap()
702 }
703
704 fn collect_seqs(store: &MemStore) -> Vec<u64> {
705 let mut seqs = Vec::new();
706 let mut cursor = ENTRY_LOW.to_vec();
707 while let Some((key, _)) = store.seek(&cursor).unwrap() {
708 if !is_entry(&key) {
709 break;
710 }
711 seqs.push(seq_of(&key));
712 cursor = entry_key(seq_of(&key) + 1).to_vec();
713 }
714 seqs
715 }
716}