1use std::cmp::Ordering;
108use std::collections::VecDeque;
109
110use yo_common::num::{DIGITS_MAX, i64_digits, push_u64, u64_digits};
111
112use crate::frozen::{self, Broken};
113use crate::listpack::{self, Entry, Listpack};
114
115pub mod groups;
116
117pub use groups::{Consumer, Filter, Group, Nack, Retry};
118
119pub const NODE_BYTES: usize = 4096;
126
127pub const NODE_ENTRIES: usize = 100;
131
132const LIVE: i64 = 0;
134
135const DELETED: i64 = 1;
137
138const SAME_FIELDS: i64 = 2;
140
141const MASTER_FIELDS: usize = 3;
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
152pub struct Id {
153 pub ms: u64,
155 pub seq: u64,
157}
158
159impl Id {
160 pub const MIN: Id = Id { ms: 0, seq: 0 };
162
163 pub const MAX: Id = Id {
165 ms: u64::MAX,
166 seq: u64::MAX,
167 };
168
169 #[must_use]
171 #[inline]
172 pub const fn new(ms: u64, seq: u64) -> Id {
173 Id { ms, seq }
174 }
175
176 #[must_use]
181 pub const fn next(self) -> Option<Id> {
182 if self.seq != u64::MAX {
183 Some(Id {
184 ms: self.ms,
185 seq: self.seq + 1,
186 })
187 } else if self.ms != u64::MAX {
188 Some(Id {
189 ms: self.ms + 1,
190 seq: 0,
191 })
192 } else {
193 None
194 }
195 }
196
197 #[must_use]
199 pub const fn prev(self) -> Option<Id> {
200 if self.seq != 0 {
201 Some(Id {
202 ms: self.ms,
203 seq: self.seq - 1,
204 })
205 } else if self.ms != 0 {
206 Some(Id {
207 ms: self.ms - 1,
208 seq: u64::MAX,
209 })
210 } else {
211 None
212 }
213 }
214
215 #[must_use]
220 pub fn to_bytes(self) -> [u8; 16] {
221 let mut out = [0u8; 16];
222 out[..8].copy_from_slice(&self.ms.to_be_bytes());
223 out[8..].copy_from_slice(&self.seq.to_be_bytes());
224 out
225 }
226
227 #[must_use]
229 pub fn from_bytes(bytes: [u8; 16]) -> Id {
230 let mut ms = [0u8; 8];
231 let mut seq = [0u8; 8];
232 ms.copy_from_slice(&bytes[..8]);
233 seq.copy_from_slice(&bytes[8..]);
234 Id {
235 ms: u64::from_be_bytes(ms),
236 seq: u64::from_be_bytes(seq),
237 }
238 }
239
240 pub fn write_to(self, out: &mut Vec<u8>) {
242 push_u64(out, self.ms);
243 out.push(b'-');
244 push_u64(out, self.seq);
245 }
246
247 #[must_use]
249 pub fn to_vec(self) -> Vec<u8> {
250 let mut out = Vec::with_capacity(41);
251 self.write_to(&mut out);
252 out
253 }
254
255 #[must_use]
262 pub fn parse(s: &[u8], default: u64) -> Option<Id> {
263 let (ms, seq) = match s.iter().position(|c| *c == b'-') {
264 Some(at) => (&s[..at], Some(&s[at + 1..])),
265 None => (s, None),
266 };
267 Some(Id {
268 ms: digits(ms)?,
269 seq: match seq {
270 Some(seq) => digits(seq)?,
271 None => default,
272 },
273 })
274 }
275}
276
277fn digits(s: &[u8]) -> Option<u64> {
282 if s.is_empty() || s.len() > 20 {
283 return None;
284 }
285 let mut n = 0u64;
286 for c in s {
287 let d = c.wrapping_sub(b'0');
288 if d > 9 {
289 return None;
290 }
291 n = n.checked_mul(10)?.checked_add(u64::from(d))?;
292 }
293 Some(n)
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298pub enum Refused {
299 NotGreater,
301 Zero,
303 Full,
305}
306
307#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309pub struct Limits {
310 pub max_node_bytes: usize,
312 pub max_node_entries: usize,
314}
315
316impl Default for Limits {
317 fn default() -> Limits {
319 Limits {
320 max_node_bytes: NODE_BYTES,
321 max_node_entries: NODE_ENTRIES,
322 }
323 }
324}
325
326#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
333pub enum Refs {
334 #[default]
337 Keep,
338 Drop,
340 Acked,
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq)]
350pub enum Fate {
351 Missing,
353 Gone,
355 Held,
357}
358
359impl Fate {
360 #[must_use]
362 #[inline]
363 pub fn code(self) -> i64 {
364 match self {
365 Fate::Missing => -1,
366 Fate::Gone => 1,
367 Fate::Held => 2,
368 }
369 }
370}
371
372#[derive(Debug, Clone, PartialEq, Eq)]
374struct Node {
375 master: Id,
380 lp: Listpack,
381}
382
383#[derive(Debug, Clone, Copy)]
391struct Edges {
392 added: u64,
393 length: u64,
394 first: Option<Id>,
395 last: Id,
396 max_deleted: Id,
397}
398
399impl Edges {
400 fn estimate(&self, id: Id) -> Option<u64> {
412 if self.added == 0 {
413 return Some(0);
414 }
415 if id >= self.last {
416 return Some(self.added);
417 }
418 let first = self.first?;
419 if self.max_deleted != Id::MIN && self.max_deleted >= first {
423 return None;
424 }
425 let behind = self.added - self.length;
426 match id.cmp(&first) {
427 Ordering::Less => Some(behind),
428 Ordering::Equal => Some(behind + 1),
429 Ordering::Greater => None,
430 }
431 }
432
433 fn holed_from(&self, id: Id) -> bool {
439 if self.length == 0 || self.max_deleted == Id::MIN {
440 return false;
441 }
442 if self.first.is_some_and(|first| first > self.max_deleted) {
443 return false;
444 }
445 id <= self.max_deleted
446 }
447
448 fn lag(&self, group: &Group) -> Option<u64> {
450 if let Some(read) = self.estimate(group.last_id()) {
451 return Some(self.added.saturating_sub(read));
452 }
453 let read = group.entries_read()?;
454 if self.holed_from(group.last_id()) {
455 return None;
456 }
457 Some(self.added.saturating_sub(read))
458 }
459
460 fn on_deliver(&self, group: &Group, id: Id) -> Option<u64> {
467 match group.entries_read() {
468 Some(read) if !self.holed_from(id) => Some(read + 1),
469 _ => self.estimate(id),
470 }
471 }
472}
473
474const FORM_NODES: u8 = 1;
480
481#[derive(Debug, Clone, Default, PartialEq, Eq)]
483pub struct Stream {
484 nodes: VecDeque<Node>,
485 length: u64,
487 last: Id,
489 max_deleted: Id,
491 added: u64,
496 groups: Vec<(Vec<u8>, Group)>,
502}
503
504impl Stream {
505 #[must_use]
507 pub fn new() -> Stream {
508 Stream::default()
509 }
510
511 pub fn freeze(&self, out: &mut Vec<u8>) {
520 out.push(FORM_NODES);
521 frozen::put_uint(out, self.length);
522 frozen::put_uint(out, self.last.ms);
523 frozen::put_uint(out, self.last.seq);
524 frozen::put_uint(out, self.max_deleted.ms);
525 frozen::put_uint(out, self.max_deleted.seq);
526 frozen::put_uint(out, self.added);
527
528 frozen::put_uint(out, self.nodes.len() as u64);
529 for node in &self.nodes {
530 frozen::put_uint(out, node.master.ms);
531 frozen::put_uint(out, node.master.seq);
532 frozen::put_bytes(out, node.lp.as_bytes());
533 }
534
535 frozen::put_uint(out, self.groups.len() as u64);
536 for (name, group) in &self.groups {
537 frozen::put_bytes(out, name);
538 group.freeze(out);
539 }
540 }
541
542 pub fn thaw(bytes: &[u8]) -> Result<Stream, Broken> {
552 let mut cut = frozen::Cut::new(bytes);
553 if cut.byte()? != FORM_NODES {
554 return Err(Broken::Form);
555 }
556 let length = cut.uint()?;
557 let last = Id::new(cut.uint()?, cut.uint()?);
558 let max_deleted = Id::new(cut.uint()?, cut.uint()?);
559 let added = cut.uint()?;
560 if length > added || max_deleted > last {
564 return Err(Broken::Body);
565 }
566
567 let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
568 if n > cut.rest().len() {
571 return Err(Broken::Short);
572 }
573 let mut nodes = VecDeque::with_capacity(n);
574 let mut prev: Option<Id> = None;
575 for _ in 0..n {
576 let master = Id::new(cut.uint()?, cut.uint()?);
577 if prev.is_some_and(|p| p >= master) {
581 return Err(Broken::Body);
582 }
583 prev = Some(master);
584 let lp = Listpack::from_bytes(cut.bytes()?).map_err(|_| Broken::Body)?;
585 nodes.push_back(Node { master, lp });
586 }
587 if nodes.is_empty() && length != 0 {
588 return Err(Broken::Body);
589 }
590
591 let n = usize::try_from(cut.uint()?).map_err(|_| Broken::Short)?;
592 if n > cut.rest().len() {
593 return Err(Broken::Short);
594 }
595 let mut groups: Vec<(Vec<u8>, Group)> = Vec::with_capacity(n);
596 for _ in 0..n {
597 let name = cut.bytes()?;
598 if groups.iter().any(|(had, _)| had == name) {
601 return Err(Broken::Body);
602 }
603 groups.push((name.to_vec(), Group::thaw(&mut cut)?));
604 }
605
606 Ok(Stream {
607 nodes,
608 length,
609 last,
610 max_deleted,
611 added,
612 groups,
613 })
614 }
615
616 #[must_use]
618 #[inline]
619 pub fn len(&self) -> u64 {
620 self.length
621 }
622
623 #[must_use]
629 #[inline]
630 pub fn is_empty(&self) -> bool {
631 self.length == 0
632 }
633
634 #[must_use]
636 #[inline]
637 pub fn last_id(&self) -> Id {
638 self.last
639 }
640
641 #[must_use]
643 #[inline]
644 pub fn max_deleted_id(&self) -> Id {
645 self.max_deleted
646 }
647
648 #[must_use]
650 #[inline]
651 pub fn added(&self) -> u64 {
652 self.added
653 }
654
655 #[must_use]
661 pub fn first_id(&self) -> Option<Id> {
662 let mut found = None;
663 self.walk(Id::MIN, Id::MAX, Some(1), &mut |id, _| {
664 found = Some(id);
665 false
666 });
667 found
668 }
669
670 #[must_use]
678 pub fn top_id(&self) -> Option<Id> {
679 let mut found = None;
680 self.rev_range(Id::MIN, Id::MAX, Some(1), |id, _| {
681 found = Some(id);
682 false
683 });
684 found
685 }
686
687 pub fn set_id(
701 &mut self,
702 last: Id,
703 added: Option<u64>,
704 max_deleted: Option<Id>,
705 ) -> Result<(), Refused> {
706 if self.top_id().is_some_and(|top| last < top) {
707 return Err(Refused::NotGreater);
708 }
709 self.last = last;
710 if let Some(added) = added {
711 self.added = added;
712 }
713 if let Some(id) = max_deleted {
714 self.max_deleted = id;
715 }
716 Ok(())
717 }
718
719 #[must_use]
725 pub fn auto_id(&self, now: u64) -> Option<Id> {
726 if now > self.last.ms {
727 Some(Id { ms: now, seq: 0 })
728 } else {
729 self.last.next()
730 }
731 }
732
733 #[must_use]
735 pub fn auto_seq(&self, ms: u64) -> Option<Id> {
736 if ms > self.last.ms {
737 Some(Id { ms, seq: 0 })
738 } else if ms == self.last.ms {
739 self.last.next().filter(|id| id.ms == ms)
740 } else {
741 None
742 }
743 }
744
745 pub fn append(
753 &mut self,
754 id: Id,
755 fields: &[(&[u8], &[u8])],
756 limits: Limits,
757 ) -> Result<(), Refused> {
758 if id == Id::MIN {
759 return Err(Refused::Zero);
760 }
761 if id <= self.last {
762 return Err(Refused::NotGreater);
763 }
764
765 let size: usize = fields
769 .iter()
770 .map(|(f, v)| f.len() + v.len() + 11)
771 .sum::<usize>()
772 + 32;
773
774 let fits = match self.nodes.back() {
775 Some(node) => {
776 let (count, deleted) = counts(&node.lp);
777 node.lp.byte_len() + size < limits.max_node_bytes
778 && (count + deleted) < limits.max_node_entries as u64
779 }
780 None => false,
781 };
782
783 if !fits {
784 self.nodes.push_back(Node {
785 master: id,
786 lp: master_of(fields),
787 });
788 }
789 let node = self.nodes.back_mut().expect("a node was just made sure of");
790 let same = same_fields(&node.lp, fields);
791 write_entry(&mut node.lp, node.master, id, fields, same);
792 bump(&mut node.lp, 1, 0);
793
794 self.length += 1;
795 self.added += 1;
796 self.last = id;
797 Ok(())
798 }
799
800 pub fn delete(&mut self, id: Id) -> bool {
805 if !self.remove(id) {
806 return false;
807 }
808 self.max_deleted = self.max_deleted.max(id);
809 true
810 }
811
812 pub fn delete_ref(&mut self, id: Id, refs: Refs) -> Fate {
821 if refs == Refs::Acked && self.still_wanted(id) {
822 return Fate::Held;
823 }
824 if !self.delete(id) {
825 return Fate::Missing;
826 }
827 if refs == Refs::Drop {
828 self.drop_refs(id);
829 }
830 Fate::Gone
831 }
832
833 pub fn ack_delete(&mut self, group: &[u8], id: Id, refs: Refs) -> Fate {
840 let Some(g) = self.group_mut(group) else {
841 return Fate::Missing;
842 };
843 if !g.ack(id) {
844 return Fate::Missing;
845 }
846 if refs == Refs::Acked && self.still_wanted(id) {
847 return Fate::Held;
848 }
849 self.delete(id);
850 if refs == Refs::Drop {
851 self.drop_refs(id);
852 }
853 Fate::Gone
854 }
855
856 pub fn nack(&mut self, group: &[u8], id: Id, retry: Retry, force: bool) -> Option<bool> {
863 let here = self.contains(id);
864 let g = self.group_mut(group)?;
865 if g.release(id, retry) {
866 return Some(true);
867 }
868 if force && here {
869 g.force_release(id, retry);
870 return Some(true);
871 }
872 Some(false)
873 }
874
875 fn still_wanted(&self, id: Id) -> bool {
877 self.groups
878 .iter()
879 .any(|(_, g)| g.nack(id).is_some() || id > g.last_id())
880 }
881
882 fn drop_refs(&mut self, id: Id) {
884 for (_, g) in &mut self.groups {
885 g.forget(id);
886 }
887 }
888
889 fn remove(&mut self, id: Id) -> bool {
896 let Some(at) = self.node_of(id) else {
897 return false;
898 };
899 let node = &self.nodes[at];
900 let Some((offset, flags)) = find(&node.lp, node.master, id) else {
901 return false;
902 };
903 if flags & DELETED != 0 {
904 return false;
905 }
906
907 let (count, _) = counts(&node.lp);
908 if count == 1 {
909 self.nodes.remove(at);
910 } else {
911 let node = &mut self.nodes[at];
912 set_int(&mut node.lp, offset, flags | DELETED);
913 bump(&mut node.lp, -1, 1);
914 }
915 self.length -= 1;
916 true
917 }
918
919 fn edges(&self) -> Edges {
925 Edges {
926 added: self.added,
927 length: self.length,
928 first: self.first_id(),
929 last: self.last,
930 max_deleted: self.max_deleted,
931 }
932 }
933
934 #[must_use]
950 pub fn lag(&self, group: &Group) -> Option<u64> {
951 self.edges().lag(group)
952 }
953
954 pub fn trim_maxlen(&mut self, len: u64, exact: bool, limit: Option<u64>) -> u64 {
967 let mut gone = 0;
968 while self.length > len && !limit.is_some_and(|cap| gone >= cap) {
969 let Some(node) = self.nodes.front() else {
970 break;
971 };
972 let (count, _) = counts(&node.lp);
973 if self.length - count >= len {
974 self.length -= count;
975 gone += count;
976 self.nodes.pop_front();
977 continue;
978 }
979 if !exact {
980 break;
981 }
982 let Some(id) = self.first_id() else { break };
983 self.remove(id);
984 gone += 1;
985 }
986 gone
987 }
988
989 pub fn trim_minid(&mut self, id: Id, exact: bool, limit: Option<u64>) -> u64 {
994 let mut gone = 0;
995 while let Some(node) = self.nodes.front() {
996 if limit.is_some_and(|cap| gone >= cap) {
997 break;
998 }
999 let (count, _) = counts(&node.lp);
1000 if last_of(node) < id {
1001 self.length -= count;
1002 gone += count;
1003 self.nodes.pop_front();
1004 continue;
1005 }
1006 if !exact {
1007 break;
1008 }
1009 let Some(first) = self.first_id() else { break };
1010 if first >= id {
1011 break;
1012 }
1013 self.remove(first);
1014 gone += 1;
1015 }
1016 gone
1017 }
1018
1019 pub fn range<F>(&self, start: Id, end: Id, count: Option<usize>, mut f: F) -> usize
1026 where
1027 F: FnMut(Id, Fields<'_>) -> bool,
1028 {
1029 self.walk(start, end, count, &mut f)
1030 }
1031
1032 pub fn rev_range<'s, F>(&'s self, start: Id, end: Id, count: Option<usize>, mut f: F) -> usize
1038 where
1039 F: FnMut(Id, Fields<'_>) -> bool,
1040 {
1041 let mut seen = 0;
1042 let mut buf: Vec<(Id, Fields<'s>)> = Vec::new();
1047 let last = self.node_from(end);
1053 for node in self.nodes.iter().take(last + 1).rev() {
1054 if node.master > end {
1057 continue;
1058 }
1059 if last_of(node) < start {
1060 break;
1061 }
1062 buf.clear();
1063 each(&node.lp, node.master, &mut |id, fields| {
1064 if id >= start && id <= end {
1065 buf.push((id, fields));
1066 }
1067 id <= end
1068 });
1069 for (id, fields) in buf.drain(..).rev() {
1070 if count.is_some_and(|want| seen >= want) {
1071 return seen;
1072 }
1073 seen += 1;
1074 if !f(id, fields) {
1075 return seen;
1076 }
1077 }
1078 }
1079 seen
1080 }
1081
1082 #[must_use]
1087 pub fn contains(&self, id: Id) -> bool {
1088 let Some(at) = self.node_of(id) else {
1089 return false;
1090 };
1091 let node = &self.nodes[at];
1092 find(&node.lp, node.master, id).is_some_and(|(_, flags)| flags & DELETED == 0)
1093 }
1094
1095 pub fn create_group(&mut self, name: &[u8], last: Id, read: Option<u64>) -> bool {
1100 if self.group(name).is_some() {
1101 return false;
1102 }
1103 self.groups.push((name.to_vec(), Group::new(last, read)));
1104 true
1105 }
1106
1107 pub fn destroy_group(&mut self, name: &[u8]) -> bool {
1109 let Some(at) = self.groups.iter().position(|(n, _)| n == name) else {
1110 return false;
1111 };
1112 self.groups.remove(at);
1113 true
1114 }
1115
1116 #[must_use]
1118 pub fn group(&self, name: &[u8]) -> Option<&Group> {
1119 self.groups
1120 .iter()
1121 .find(|(n, _)| n.as_slice() == name)
1122 .map(|(_, g)| g)
1123 }
1124
1125 pub fn group_mut(&mut self, name: &[u8]) -> Option<&mut Group> {
1127 self.groups
1128 .iter_mut()
1129 .find(|(n, _)| n.as_slice() == name)
1130 .map(|(_, g)| g)
1131 }
1132
1133 pub fn groups(&self) -> impl Iterator<Item = (&[u8], &Group)> + '_ {
1135 self.groups.iter().map(|(n, g)| (n.as_slice(), g))
1136 }
1137
1138 pub fn read_group<F>(
1152 &mut self,
1153 group: &[u8],
1154 consumer: &[u8],
1155 count: Option<usize>,
1156 noack: bool,
1157 now: u64,
1158 mut f: F,
1159 ) -> Option<usize>
1160 where
1161 F: FnMut(Id, Fields<'_>) -> bool,
1162 {
1163 let edges = self.edges();
1166 let Stream { nodes, groups, .. } = self;
1169 let (_, g) = groups.iter_mut().find(|(n, _)| n.as_slice() == group)?;
1170 let slot = g.consumer_or_create(consumer, now);
1171 let Some(from) = g.last_id().next() else {
1172 g.touch(slot, now, false);
1175 return Some(0);
1176 };
1177 let mut seen = 0;
1178 walk_nodes(nodes, from, Id::MAX, count, &mut |id, fields| {
1179 let read = edges.on_deliver(g, id);
1182 if noack {
1183 g.skip(id);
1184 } else {
1185 g.deliver(slot, id, now);
1186 }
1187 g.set_read(read);
1188 seen += 1;
1189 f(id, fields)
1190 });
1191 g.touch(slot, now, seen > 0);
1192 Some(seen)
1193 }
1194
1195 pub fn read_group_pending<F>(
1210 &mut self,
1211 group: &[u8],
1212 consumer: &[u8],
1213 after: Id,
1214 count: Option<usize>,
1215 now: u64,
1216 mut f: F,
1217 ) -> Option<usize>
1218 where
1219 F: FnMut(Id, Option<Fields<'_>>) -> bool,
1220 {
1221 let g = self.group_mut(group)?;
1231 let slot = g.consumer_or_create(consumer, now);
1232 let ids: Vec<Id> = g
1233 .consumer(slot)
1234 .expect("the slot that was just made")
1235 .pending()
1236 .filter(|&id| id > after)
1237 .take(count.unwrap_or(usize::MAX))
1238 .collect();
1239 for &id in &ids {
1240 g.redeliver(id, now);
1241 }
1242 g.touch(slot, now, !ids.is_empty());
1243
1244 let mut seen = 0;
1245 for &id in &ids {
1246 seen += 1;
1247 let mut go = true;
1248 let mut found = false;
1249 self.walk(id, id, Some(1), &mut |got, fields| {
1250 found = true;
1251 go = f(got, Some(fields));
1252 false
1253 });
1254 if !found {
1255 go = f(id, None);
1256 }
1257 if !go {
1258 break;
1259 }
1260 }
1261 Some(seen)
1262 }
1263
1264 #[allow(clippy::too_many_arguments)]
1277 pub fn claim(
1278 &mut self,
1279 group: &[u8],
1280 consumer: &[u8],
1281 ids: &[Id],
1282 min_idle: u64,
1283 time: u64,
1284 retry: Option<u64>,
1285 bump: bool,
1286 force: bool,
1287 now: u64,
1288 gone: &mut Vec<Id>,
1289 ) -> Option<Vec<Id>> {
1290 self.group_mut(group)?.consumer_or_create(consumer, now);
1296 let mut took = Vec::new();
1297 for &id in ids {
1298 let here = self.contains(id);
1299 let g = self.group_mut(group)?;
1300 let slot = g.consumer_or_create(consumer, now);
1301 match g.nack(id) {
1302 Some(nack) => {
1303 if !here {
1304 g.forget(id);
1305 gone.push(id);
1306 continue;
1307 }
1308 if nack.idle(now) < min_idle {
1309 continue;
1310 }
1311 if g.claim(id, slot, time, retry, bump) {
1312 took.push(id);
1313 }
1314 }
1315 None => {
1316 if force && here && g.force(id, slot, time, retry.unwrap_or(1)) {
1319 took.push(id);
1320 }
1321 }
1322 }
1323 }
1324 if !took.is_empty() {
1328 let g = self.group_mut(group).expect("the group found a moment ago");
1329 let slot = g.consumer_or_create(consumer, now);
1330 g.touch(slot, now, true);
1331 }
1332 Some(took)
1333 }
1334
1335 #[allow(clippy::too_many_arguments)]
1343 pub fn autoclaim(
1344 &mut self,
1345 group: &[u8],
1346 consumer: &[u8],
1347 start: Id,
1348 min_idle: u64,
1349 count: usize,
1350 bump: bool,
1351 now: u64,
1352 gone: &mut Vec<Id>,
1353 ) -> Option<(Option<Id>, Vec<Id>)> {
1354 let mut ids = Vec::new();
1355 let cursor = self
1356 .group(group)?
1357 .claimable(start, min_idle, now, count, &mut ids);
1358 let took = self.claim(
1359 group, consumer, &ids, min_idle, now, None, bump, false, now, gone,
1360 )?;
1361 Some((cursor, took))
1362 }
1363
1364 #[must_use]
1370 pub fn memory_bytes(&self) -> usize {
1371 let nodes: usize = self
1372 .nodes
1373 .iter()
1374 .map(|node| node.lp.byte_len() + std::mem::size_of::<Node>())
1375 .sum();
1376 let groups: usize = self
1377 .groups
1378 .iter()
1379 .map(|(name, g)| {
1380 name.capacity() + std::mem::size_of::<(Vec<u8>, Group)>() + g.memory_bytes()
1381 })
1382 .sum();
1383 nodes + groups
1384 }
1385
1386 #[must_use]
1389 pub fn nodes(&self) -> usize {
1390 self.nodes.len()
1391 }
1392
1393 fn walk<F>(&self, start: Id, end: Id, count: Option<usize>, f: &mut F) -> usize
1395 where
1396 F: FnMut(Id, Fields<'_>) -> bool,
1397 {
1398 walk_nodes(&self.nodes, start, end, count, f)
1399 }
1400
1401 fn node_from(&self, id: Id) -> usize {
1403 node_from(&self.nodes, id)
1404 }
1405
1406 fn node_of(&self, id: Id) -> Option<usize> {
1408 let at = self.node_from(id);
1409 let node = self.nodes.get(at)?;
1410 (node.master <= id && id <= last_of(node)).then_some(at)
1411 }
1412}
1413
1414fn node_from(nodes: &VecDeque<Node>, id: Id) -> usize {
1425 let after = nodes.partition_point(|node| node.master <= id);
1426 after.saturating_sub(1)
1427}
1428
1429fn walk_nodes<F>(
1433 nodes: &VecDeque<Node>,
1434 start: Id,
1435 end: Id,
1436 count: Option<usize>,
1437 f: &mut F,
1438) -> usize
1439where
1440 F: FnMut(Id, Fields<'_>) -> bool,
1441{
1442 let mut seen = 0;
1443 let mut stop = false;
1444 for node in nodes.iter().skip(node_from(nodes, start)) {
1445 if node.master > end {
1446 break;
1447 }
1448 each(&node.lp, node.master, &mut |id, fields| {
1449 if id > end {
1450 stop = true;
1451 return false;
1452 }
1453 if id < start {
1454 return true;
1455 }
1456 if count.is_some_and(|want| seen >= want) {
1457 stop = true;
1458 return false;
1459 }
1460 seen += 1;
1461 if !f(id, fields) {
1462 stop = true;
1463 return false;
1464 }
1465 true
1466 });
1467 if stop {
1468 break;
1469 }
1470 }
1471 seen
1472}
1473
1474#[derive(Debug, Clone)]
1481pub struct Fields<'a> {
1482 names: Option<listpack::Iter<'a>>,
1484 body: listpack::Iter<'a>,
1485 left: usize,
1486}
1487
1488impl<'a> Iterator for Fields<'a> {
1489 type Item = (Entry<'a>, Entry<'a>);
1490
1491 fn next(&mut self) -> Option<(Entry<'a>, Entry<'a>)> {
1492 if self.left == 0 {
1493 return None;
1494 }
1495 self.left -= 1;
1496 let name = match &mut self.names {
1497 Some(names) => names.next()?,
1498 None => self.body.next()?,
1499 };
1500 Some((name, self.body.next()?))
1501 }
1502
1503 fn size_hint(&self) -> (usize, Option<usize>) {
1504 (self.left, Some(self.left))
1505 }
1506}
1507
1508impl ExactSizeIterator for Fields<'_> {}
1509
1510impl Fields<'_> {
1511 #[must_use]
1513 #[inline]
1514 pub fn is_empty(&self) -> bool {
1515 self.left == 0
1516 }
1517}
1518
1519fn master_of(fields: &[(&[u8], &[u8])]) -> Listpack {
1521 let mut lp = Listpack::new();
1522 push_int(&mut lp, 0);
1523 push_int(&mut lp, 0);
1524 push_int(&mut lp, fields.len() as i64);
1525 for (name, _) in fields {
1526 lp.push(name);
1527 }
1528 push_int(&mut lp, 0);
1529 lp
1530}
1531
1532fn same_fields(lp: &Listpack, fields: &[(&[u8], &[u8])]) -> bool {
1534 let mut it = lp.iter();
1535 let (_, _, want) = match (it.next(), it.next(), it.next()) {
1536 (Some(_), Some(_), Some(Entry::Int(n))) => ((), (), n),
1537 _ => return false,
1538 };
1539 if want != fields.len() as i64 {
1540 return false;
1541 }
1542 fields.iter().all(|(name, _)| match it.next() {
1543 Some(Entry::Str(s)) => s == *name,
1544 Some(Entry::Int(n)) => {
1545 let mut buf = [0u8; DIGITS_MAX];
1546 i64_digits(&mut buf, n) == *name
1547 }
1548 None => false,
1549 })
1550}
1551
1552fn counts(lp: &Listpack) -> (u64, u64) {
1554 let mut it = lp.iter();
1555 let count = int_or_zero(it.next());
1556 let deleted = int_or_zero(it.next());
1557 (count.max(0) as u64, deleted.max(0) as u64)
1558}
1559
1560fn bump(lp: &mut Listpack, live: i64, dead: i64) {
1562 let (count, deleted) = counts(lp);
1563 let mut buf = [0u8; DIGITS_MAX];
1564 let at = count as i64 + live;
1565 lp.replace(0, u64_digits(&mut buf, at.max(0) as u64));
1566 let at = deleted as i64 + dead;
1567 lp.replace(1, u64_digits(&mut buf, at.max(0) as u64));
1568}
1569
1570fn int_or_zero(entry: Option<Entry<'_>>) -> i64 {
1572 match entry {
1573 Some(Entry::Int(n)) => n,
1574 _ => 0,
1575 }
1576}
1577
1578fn push_int(lp: &mut Listpack, n: i64) {
1580 let mut buf = [0u8; DIGITS_MAX];
1581 lp.push(i64_digits(&mut buf, n));
1582}
1583
1584fn set_int(lp: &mut Listpack, index: usize, n: i64) {
1586 let mut buf = [0u8; DIGITS_MAX];
1587 lp.replace(index, i64_digits(&mut buf, n));
1588}
1589
1590fn write_entry(lp: &mut Listpack, master: Id, id: Id, fields: &[(&[u8], &[u8])], same: bool) {
1592 let flags = if same { LIVE | SAME_FIELDS } else { LIVE };
1593 push_int(lp, flags);
1594 push_int(lp, id.ms.wrapping_sub(master.ms) as i64);
1599 push_int(lp, id.seq.wrapping_sub(master.seq) as i64);
1600 if same {
1601 for (_, value) in fields {
1602 lp.push(value);
1603 }
1604 push_int(lp, fields.len() as i64 + 3);
1605 } else {
1606 push_int(lp, fields.len() as i64);
1607 for (name, value) in fields {
1608 lp.push(name);
1609 lp.push(value);
1610 }
1611 push_int(lp, fields.len() as i64 * 2 + 4);
1612 }
1613}
1614
1615fn last_of(node: &Node) -> Id {
1617 let mut last = node.master;
1618 each(&node.lp, node.master, &mut |id, _| {
1619 last = id;
1620 true
1621 });
1622 last
1623}
1624
1625fn find(lp: &Listpack, master: Id, id: Id) -> Option<(usize, i64)> {
1630 let mut at = None;
1631 walk_node(lp, master, &mut |found, index, flags, _| {
1632 if found == id {
1633 at = Some((index, flags));
1634 return false;
1635 }
1636 found < id
1637 });
1638 at
1639}
1640
1641fn each<'a, F>(lp: &'a Listpack, master: Id, f: &mut F)
1643where
1644 F: FnMut(Id, Fields<'a>) -> bool,
1645{
1646 walk_node(lp, master, &mut |id, _, flags, fields| {
1647 if flags & DELETED != 0 {
1648 return true;
1649 }
1650 f(id, fields)
1651 });
1652}
1653
1654fn walk_node<'a, F>(lp: &'a Listpack, master: Id, f: &mut F)
1660where
1661 F: FnMut(Id, usize, i64, Fields<'a>) -> bool,
1662{
1663 let mut it = lp.iter();
1664 let (Some(_), Some(_), Some(Entry::Int(masters))) = (it.next(), it.next(), it.next()) else {
1665 return;
1666 };
1667 let masters = masters.max(0) as usize;
1668 let names = it.clone();
1671 let mut index = MASTER_FIELDS;
1672 for _ in 0..=masters {
1673 if it.next().is_none() {
1674 return;
1675 }
1676 index += 1;
1677 }
1678
1679 loop {
1680 let at = index;
1681 let (Some(Entry::Int(flags)), Some(Entry::Int(ms)), Some(Entry::Int(seq))) =
1682 (it.next(), it.next(), it.next())
1683 else {
1684 return;
1685 };
1686 index += 3;
1687 let id = Id {
1688 ms: master.ms.wrapping_add(ms as u64),
1689 seq: master.seq.wrapping_add(seq as u64),
1690 };
1691
1692 let same = flags & SAME_FIELDS != 0;
1693 let (fields, skip) = if same {
1694 let fields = Fields {
1695 names: Some(names.clone()),
1696 body: it.clone(),
1697 left: masters,
1698 };
1699 (fields, masters + 1)
1700 } else {
1701 let Some(Entry::Int(n)) = it.next() else {
1702 return;
1703 };
1704 index += 1;
1705 let fields = Fields {
1706 names: None,
1707 body: it.clone(),
1708 left: n.max(0) as usize,
1709 };
1710 (fields, n.max(0) as usize * 2 + 1)
1711 };
1712
1713 if !f(id, at, flags, fields) {
1714 return;
1715 }
1716 for _ in 0..skip {
1717 if it.next().is_none() {
1718 return;
1719 }
1720 index += 1;
1721 }
1722 }
1723}
1724
1725#[cfg(test)]
1726mod tests {
1727 use super::*;
1728
1729 fn pairs<'a>(of: &'a [(&'a str, &'a str)]) -> Vec<(&'a [u8], &'a [u8])> {
1730 of.iter()
1731 .map(|(f, v)| (f.as_bytes(), v.as_bytes()))
1732 .collect()
1733 }
1734
1735 type Flat = (Id, Vec<(Vec<u8>, Vec<u8>)>);
1737
1738 fn dump(s: &Stream) -> Vec<Flat> {
1740 let mut out = Vec::new();
1741 s.range(Id::MIN, Id::MAX, None, |id, fields| {
1742 out.push((id, fields.map(|(f, v)| (f.to_vec(), v.to_vec())).collect()));
1743 true
1744 });
1745 out
1746 }
1747
1748 fn add(s: &mut Stream, ms: u64, seq: u64, fields: &[(&str, &str)]) {
1749 s.append(Id::new(ms, seq), &pairs(fields), Limits::default())
1750 .expect("an append");
1751 }
1752
1753 #[test]
1754 fn an_entry_comes_back_as_it_went_in() {
1755 let mut s = Stream::new();
1756 add(&mut s, 5, 0, &[("sensor", "1"), ("reading", "23.4")]);
1757 let got = dump(&s);
1758 assert_eq!(got.len(), 1);
1759 assert_eq!(got[0].0, Id::new(5, 0));
1760 assert_eq!(
1761 got[0].1,
1762 vec![
1763 (b"sensor".to_vec(), b"1".to_vec()),
1764 (b"reading".to_vec(), b"23.4".to_vec())
1765 ]
1766 );
1767 }
1768
1769 #[test]
1770 fn entries_come_back_in_order() {
1771 let mut s = Stream::new();
1772 for ms in 1..200u64 {
1773 add(&mut s, ms, 0, &[("n", "x")]);
1774 }
1775 let got = dump(&s);
1776 assert_eq!(got.len(), 199);
1777 for (at, (id, _)) in got.iter().enumerate() {
1778 assert_eq!(*id, Id::new(at as u64 + 1, 0));
1779 }
1780 assert_eq!(s.len(), 199);
1781 assert_eq!(s.added(), 199);
1782 assert_eq!(s.last_id(), Id::new(199, 0));
1783 }
1784
1785 #[test]
1787 fn a_long_stream_is_many_nodes() {
1788 let mut s = Stream::new();
1789 for ms in 1..=1000u64 {
1790 add(&mut s, ms, 0, &[("n", "x")]);
1791 }
1792 assert_eq!(s.nodes(), 10, "a hundred entries a node");
1793 assert_eq!(dump(&s).len(), 1000);
1794 }
1795
1796 #[test]
1798 fn the_field_names_are_not_stored_twice() {
1799 let mut shared = Stream::new();
1800 let mut apart = Stream::new();
1801 for ms in 1..=100u64 {
1802 add(
1803 &mut shared,
1804 ms,
1805 0,
1806 &[("temperature_celsius", "21"), ("relative_humidity", "44")],
1807 );
1808 let a = format!("temperature_celsius{ms}");
1809 let b = format!("relative_humidity{ms}");
1810 apart
1811 .append(
1812 Id::new(ms, 0),
1813 &[(a.as_bytes(), b"21"), (b.as_bytes(), b"44")],
1814 Limits::default(),
1815 )
1816 .expect("an append");
1817 }
1818 assert!(
1819 shared.memory_bytes() * 3 < apart.memory_bytes(),
1820 "{} against {}",
1821 shared.memory_bytes(),
1822 apart.memory_bytes()
1823 );
1824 }
1825
1826 #[test]
1836 fn an_entry_costs_about_two_dozen_bytes() {
1837 let mut s = Stream::new();
1838 for ms in 1..=10_000u64 {
1839 let reading = format!("{:.3}", ms as f64 / 7.0);
1840 s.append(
1841 Id::new(ms, 0),
1842 &[(b"sensor", b"a4"), (b"reading", reading.as_bytes())],
1843 Limits::default(),
1844 )
1845 .expect("an append");
1846 }
1847 let each = s.memory_bytes() as f64 / 10_000.0;
1848 assert!(each < 32.0, "{each:.2} bytes an entry");
1849 }
1850
1851 #[test]
1852 fn an_entry_with_its_own_fields_still_reads_back() {
1853 let mut s = Stream::new();
1854 add(&mut s, 1, 0, &[("a", "1"), ("b", "2")]);
1855 add(&mut s, 2, 0, &[("c", "3")]);
1856 add(&mut s, 3, 0, &[("a", "4"), ("b", "5")]);
1857 let got = dump(&s);
1858 assert_eq!(got[1].1, vec![(b"c".to_vec(), b"3".to_vec())]);
1859 assert_eq!(
1860 got[2].1,
1861 vec![
1862 (b"a".to_vec(), b"4".to_vec()),
1863 (b"b".to_vec(), b"5".to_vec())
1864 ]
1865 );
1866 }
1867
1868 #[test]
1870 fn the_order_of_the_names_matters() {
1871 let mut s = Stream::new();
1872 add(&mut s, 1, 0, &[("a", "1"), ("b", "2")]);
1873 add(&mut s, 2, 0, &[("b", "3"), ("a", "4")]);
1874 let got = dump(&s);
1875 assert_eq!(
1876 got[1].1,
1877 vec![
1878 (b"b".to_vec(), b"3".to_vec()),
1879 (b"a".to_vec(), b"4".to_vec())
1880 ]
1881 );
1882 }
1883
1884 #[test]
1885 fn an_id_must_beat_the_last_one() {
1886 let mut s = Stream::new();
1887 add(&mut s, 5, 5, &[("n", "x")]);
1888 let f = pairs(&[("n", "x")]);
1889 for id in [Id::new(5, 5), Id::new(5, 4), Id::new(1, 0)] {
1890 assert_eq!(
1891 s.append(id, &f, Limits::default()),
1892 Err(Refused::NotGreater),
1893 "{id:?}"
1894 );
1895 }
1896 assert_eq!(s.append(Id::new(5, 6), &f, Limits::default()), Ok(()));
1897 }
1898
1899 #[test]
1900 fn nothing_can_be_added_at_zero() {
1901 let mut s = Stream::new();
1902 assert_eq!(
1903 s.append(Id::MIN, &pairs(&[("n", "x")]), Limits::default()),
1904 Err(Refused::Zero)
1905 );
1906 }
1907
1908 #[test]
1909 fn a_range_takes_both_ends() {
1910 let mut s = Stream::new();
1911 for ms in 1..=10u64 {
1912 add(&mut s, ms, 0, &[("n", "x")]);
1913 }
1914 let mut seen = Vec::new();
1915 s.range(Id::new(3, 0), Id::new(6, 0), None, |id, _| {
1916 seen.push(id.ms);
1917 true
1918 });
1919 assert_eq!(seen, vec![3, 4, 5, 6]);
1920 }
1921
1922 #[test]
1924 fn a_range_that_lands_between_entries() {
1925 let mut s = Stream::new();
1926 for ms in [10u64, 20, 30] {
1927 add(&mut s, ms, 0, &[("n", "x")]);
1928 }
1929 let mut seen = Vec::new();
1930 s.range(Id::new(11, 0), Id::new(29, 0), None, |id, _| {
1931 seen.push(id.ms);
1932 true
1933 });
1934 assert_eq!(seen, vec![20]);
1935
1936 let mut none = 0;
1937 s.range(Id::new(31, 0), Id::MAX, None, |_, _| {
1938 none += 1;
1939 true
1940 });
1941 assert_eq!(none, 0);
1942 }
1943
1944 #[test]
1945 fn a_count_stops_the_walk() {
1946 let mut s = Stream::new();
1947 for ms in 1..=500u64 {
1948 add(&mut s, ms, 0, &[("n", "x")]);
1949 }
1950 let mut seen = 0;
1951 let answered = s.range(Id::MIN, Id::MAX, Some(7), |_, _| {
1952 seen += 1;
1953 true
1954 });
1955 assert_eq!((seen, answered), (7, 7));
1956 }
1957
1958 #[test]
1959 fn the_callback_can_stop_the_walk() {
1960 let mut s = Stream::new();
1961 for ms in 1..=500u64 {
1962 add(&mut s, ms, 0, &[("n", "x")]);
1963 }
1964 let mut seen = 0;
1965 s.range(Id::MIN, Id::MAX, None, |_, _| {
1966 seen += 1;
1967 seen < 3
1968 });
1969 assert_eq!(seen, 3);
1970 }
1971
1972 #[test]
1973 fn a_reverse_range_is_the_forward_one_backwards() {
1974 let mut s = Stream::new();
1975 for ms in 1..=350u64 {
1976 add(&mut s, ms, 0, &[("n", "x")]);
1977 }
1978 let mut forward = Vec::new();
1979 s.range(Id::new(50, 0), Id::new(300, 0), None, |id, _| {
1980 forward.push(id);
1981 true
1982 });
1983 let mut back = Vec::new();
1984 s.rev_range(Id::new(50, 0), Id::new(300, 0), None, |id, _| {
1985 back.push(id);
1986 true
1987 });
1988 back.reverse();
1989 assert_eq!(forward, back);
1990 assert_eq!(forward.len(), 251);
1991 }
1992
1993 #[test]
1994 fn a_reverse_range_takes_a_count_from_the_new_end() {
1995 let mut s = Stream::new();
1996 for ms in 1..=350u64 {
1997 add(&mut s, ms, 0, &[("n", "x")]);
1998 }
1999 let mut seen = Vec::new();
2000 s.rev_range(Id::MIN, Id::MAX, Some(3), |id, _| {
2001 seen.push(id.ms);
2002 true
2003 });
2004 assert_eq!(seen, vec![350, 349, 348]);
2005 }
2006
2007 #[test]
2008 fn deleting_leaves_the_rest_readable() {
2009 let mut s = Stream::new();
2010 for ms in 1..=10u64 {
2011 add(&mut s, ms, 0, &[("n", "x")]);
2012 }
2013 assert!(s.delete(Id::new(4, 0)));
2014 assert!(!s.delete(Id::new(4, 0)), "twice is not twice");
2015 assert!(!s.delete(Id::new(99, 0)));
2016 assert_eq!(s.len(), 9);
2017 assert_eq!(s.max_deleted_id(), Id::new(4, 0));
2018 let seen: Vec<u64> = dump(&s).iter().map(|(id, _)| id.ms).collect();
2019 assert_eq!(seen, vec![1, 2, 3, 5, 6, 7, 8, 9, 10]);
2020 }
2021
2022 #[test]
2023 fn deleting_the_first_entry_moves_the_first_id() {
2024 let mut s = Stream::new();
2025 for ms in 1..=5u64 {
2026 add(&mut s, ms, 0, &[("n", "x")]);
2027 }
2028 assert_eq!(s.first_id(), Some(Id::new(1, 0)));
2029 s.delete(Id::new(1, 0));
2030 assert_eq!(s.first_id(), Some(Id::new(2, 0)));
2031 }
2032
2033 #[test]
2035 fn emptying_a_node_drops_it() {
2036 let mut s = Stream::new();
2037 for ms in 1..=250u64 {
2038 add(&mut s, ms, 0, &[("n", "x")]);
2039 }
2040 assert_eq!(s.nodes(), 3);
2041 for ms in 1..=100u64 {
2042 assert!(s.delete(Id::new(ms, 0)), "{ms}");
2043 }
2044 assert_eq!(s.nodes(), 2);
2045 assert_eq!(s.len(), 150);
2046 assert_eq!(dump(&s).len(), 150);
2047 }
2048
2049 #[test]
2051 fn an_emptied_stream_still_remembers_its_last_id() {
2052 let mut s = Stream::new();
2053 add(&mut s, 7, 0, &[("n", "x")]);
2054 s.delete(Id::new(7, 0));
2055 assert!(s.is_empty());
2056 assert_eq!(s.last_id(), Id::new(7, 0));
2057 assert_eq!(s.added(), 1);
2058 assert_eq!(s.first_id(), None);
2059 assert_eq!(
2060 s.append(Id::new(7, 0), &pairs(&[("n", "x")]), Limits::default()),
2061 Err(Refused::NotGreater),
2062 "a deleted id is still used up"
2063 );
2064 }
2065
2066 #[test]
2067 fn trimming_to_a_length_takes_the_oldest() {
2068 let mut s = Stream::new();
2069 for ms in 1..=1000u64 {
2070 add(&mut s, ms, 0, &[("n", "x")]);
2071 }
2072 assert_eq!(s.trim_maxlen(150, true, None), 850);
2073 assert_eq!(s.len(), 150);
2074 assert_eq!(s.first_id(), Some(Id::new(851, 0)));
2075 assert_eq!(s.last_id(), Id::new(1000, 0));
2076 }
2077
2078 #[test]
2080 fn an_approximate_trim_stops_at_a_node() {
2081 let mut s = Stream::new();
2082 for ms in 1..=1000u64 {
2083 add(&mut s, ms, 0, &[("n", "x")]);
2084 }
2085 assert_eq!(s.trim_maxlen(150, false, None), 800);
2086 assert_eq!(s.len(), 200, "left at the node boundary above 150");
2087 assert_eq!(s.nodes(), 2);
2088 }
2089
2090 #[test]
2091 fn trimming_to_a_length_that_is_already_met_does_nothing() {
2092 let mut s = Stream::new();
2093 for ms in 1..=10u64 {
2094 add(&mut s, ms, 0, &[("n", "x")]);
2095 }
2096 assert_eq!(s.trim_maxlen(50, true, None), 0);
2097 assert_eq!(s.len(), 10);
2098 }
2099
2100 #[test]
2101 fn trimming_to_zero_empties_it() {
2102 let mut s = Stream::new();
2103 for ms in 1..=250u64 {
2104 add(&mut s, ms, 0, &[("n", "x")]);
2105 }
2106 assert_eq!(s.trim_maxlen(0, true, None), 250);
2107 assert!(s.is_empty());
2108 assert_eq!(s.nodes(), 0);
2109 assert_eq!(s.last_id(), Id::new(250, 0));
2110 }
2111
2112 #[test]
2113 fn trimming_below_an_id_takes_everything_under_it() {
2114 let mut s = Stream::new();
2115 for ms in 1..=1000u64 {
2116 add(&mut s, ms, 0, &[("n", "x")]);
2117 }
2118 assert_eq!(s.trim_minid(Id::new(400, 0), true, None), 399);
2119 assert_eq!(s.first_id(), Some(Id::new(400, 0)));
2120 assert_eq!(s.len(), 601);
2121 }
2122
2123 #[test]
2124 fn an_approximate_minid_trim_stops_at_a_node() {
2125 let mut s = Stream::new();
2126 for ms in 1..=1000u64 {
2127 add(&mut s, ms, 0, &[("n", "x")]);
2128 }
2129 assert_eq!(s.trim_minid(Id::new(450, 0), false, None), 400);
2130 assert_eq!(s.first_id(), Some(Id::new(401, 0)));
2131 }
2132
2133 #[test]
2134 fn several_entries_share_a_millisecond() {
2135 let mut s = Stream::new();
2136 for seq in 0..250u64 {
2137 add(&mut s, 5, seq, &[("n", "x")]);
2138 }
2139 let got = dump(&s);
2140 assert_eq!(got.len(), 250);
2141 for (at, (id, _)) in got.iter().enumerate() {
2142 assert_eq!(*id, Id::new(5, at as u64), "at {at}");
2143 }
2144 let mut seen = Vec::new();
2145 s.range(Id::new(5, 100), Id::new(5, 102), None, |id, _| {
2146 seen.push(id.seq);
2147 true
2148 });
2149 assert_eq!(seen, vec![100, 101, 102]);
2150 }
2151
2152 #[test]
2155 fn a_sequence_that_crosses_a_node() {
2156 let mut s = Stream::new();
2157 for seq in 0..300u64 {
2158 add(&mut s, 1, seq, &[("n", "x")]);
2159 }
2160 assert!(s.nodes() > 1);
2161 let got = dump(&s);
2162 assert_eq!(got.len(), 300);
2163 assert_eq!(got[299].0, Id::new(1, 299));
2164 assert!(s.delete(Id::new(1, 250)));
2165 assert_eq!(dump(&s).len(), 299);
2166 }
2167
2168 #[test]
2169 fn an_entry_with_no_fields_is_still_an_entry() {
2170 let mut s = Stream::new();
2171 s.append(Id::new(1, 0), &[], Limits::default())
2172 .expect("an append");
2173 add(&mut s, 2, 0, &[("n", "x")]);
2174 let got = dump(&s);
2175 assert_eq!(got.len(), 2);
2176 assert!(got[0].1.is_empty());
2177 }
2178
2179 #[test]
2180 fn a_value_that_looks_like_a_number_comes_back_as_it_went_in() {
2181 let mut s = Stream::new();
2182 add(&mut s, 1, 0, &[("n", "007"), ("m", "7")]);
2183 let got = dump(&s);
2184 assert_eq!(got[0].1[0].1, b"007".to_vec());
2185 assert_eq!(got[0].1[1].1, b"7".to_vec());
2186 }
2187
2188 #[test]
2189 fn the_auto_id_follows_the_clock_and_never_goes_back() {
2190 let mut s = Stream::new();
2191 assert_eq!(s.auto_id(1000), Some(Id::new(1000, 0)));
2192 add(&mut s, 1000, 0, &[("n", "x")]);
2193 assert_eq!(s.auto_id(1000), Some(Id::new(1000, 1)), "same millisecond");
2194 assert_eq!(s.auto_id(900), Some(Id::new(1000, 1)), "clock went back");
2195 assert_eq!(s.auto_id(1001), Some(Id::new(1001, 0)));
2196 }
2197
2198 #[test]
2199 fn an_explicit_millisecond_takes_the_next_sequence() {
2200 let mut s = Stream::new();
2201 add(&mut s, 5, 0, &[("n", "x")]);
2202 assert_eq!(s.auto_seq(5), Some(Id::new(5, 1)));
2203 assert_eq!(s.auto_seq(6), Some(Id::new(6, 0)));
2204 assert_eq!(s.auto_seq(4), None, "below the last one");
2205 }
2206
2207 #[test]
2208 fn an_id_reads_and_writes() {
2209 for (text, default, want) in [
2210 (&b"5"[..], 0, Some(Id::new(5, 0))),
2211 (b"5", u64::MAX, Some(Id::new(5, u64::MAX))),
2212 (b"5-3", 0, Some(Id::new(5, 3))),
2213 (b"0-0", 0, Some(Id::MIN)),
2214 (b"", 0, None),
2215 (b"-1", 0, None),
2216 (b"5-", 0, None),
2217 (b"a", 0, None),
2218 (b"5-a", 0, None),
2219 (b"18446744073709551616", 0, None),
2220 ] {
2221 assert_eq!(
2222 Id::parse(text, default),
2223 want,
2224 "{:?}",
2225 String::from_utf8_lossy(text)
2226 );
2227 }
2228 assert_eq!(Id::new(5, 3).to_vec(), b"5-3".to_vec());
2229 }
2230
2231 #[test]
2232 fn an_id_round_trips_through_its_bytes() {
2233 for id in [Id::MIN, Id::MAX, Id::new(1, 2), Id::new(u64::MAX, 0)] {
2234 assert_eq!(Id::from_bytes(id.to_bytes()), id);
2235 }
2236 assert!(Id::new(1, 2).to_bytes() < Id::new(1, 3).to_bytes());
2238 assert!(Id::new(1, u64::MAX).to_bytes() < Id::new(2, 0).to_bytes());
2239 }
2240
2241 #[test]
2242 fn stepping_an_id_carries_and_stops() {
2243 assert_eq!(Id::new(1, 2).next(), Some(Id::new(1, 3)));
2244 assert_eq!(Id::new(1, u64::MAX).next(), Some(Id::new(2, 0)));
2245 assert_eq!(Id::MAX.next(), None);
2246 assert_eq!(Id::new(1, 3).prev(), Some(Id::new(1, 2)));
2247 assert_eq!(Id::new(2, 0).prev(), Some(Id::new(1, u64::MAX)));
2248 assert_eq!(Id::MIN.prev(), None);
2249 }
2250
2251 #[test]
2253 fn the_node_size_changes_nothing_but_the_node_count() {
2254 let mut want = None;
2255 for entries in [1usize, 2, 7, 100, 4096] {
2256 let mut s = Stream::new();
2257 let limits = Limits {
2258 max_node_bytes: NODE_BYTES,
2259 max_node_entries: entries,
2260 };
2261 for ms in 1..=400u64 {
2262 let value = format!("v{ms}");
2263 s.append(Id::new(ms, 0), &[(b"n", value.as_bytes())], limits)
2264 .expect("an append");
2265 }
2266 for ms in (1..=400u64).step_by(7) {
2267 s.delete(Id::new(ms, 0));
2268 }
2269 let got = dump(&s);
2270 match &want {
2271 None => want = Some(got),
2272 Some(want) => assert_eq!(&got, want, "at {entries} entries a node"),
2273 }
2274 }
2275 }
2276
2277 #[test]
2279 fn a_tiny_byte_limit_still_works() {
2280 let limits = Limits {
2281 max_node_bytes: 1,
2282 max_node_entries: NODE_ENTRIES,
2283 };
2284 let mut s = Stream::new();
2285 for ms in 1..=20u64 {
2286 s.append(Id::new(ms, 0), &[(b"n", b"x")], limits)
2287 .expect("an append");
2288 }
2289 assert_eq!(s.nodes(), 20);
2290 assert_eq!(dump(&s).len(), 20);
2291 }
2292
2293 const REDIS_DUMP: &str = "1b0110000000000000000100000000000000014070\
2310 700000001f000301010102019374656d70657261747572655f63656c736975731491\
2311 72656c61746976655f68756d69646974791200010201000100011501370105010301\
2312 0001010116013801050102010101000117013901050100010201000101018673656e\
2313 736f72078161020601ff030301010101020400406440640000000f00239a5c2c7208\
2314 ea0a";
2315
2316 fn unhex(s: &str) -> Vec<u8> {
2317 let digits: Vec<u8> = s.bytes().filter(|b| !b.is_ascii_whitespace()).collect();
2318 digits
2319 .chunks(2)
2320 .map(|pair| {
2321 let of = |b: u8| (b as char).to_digit(16).expect("a hex digit") as u8;
2322 of(pair[0]) << 4 | of(pair[1])
2323 })
2324 .collect()
2325 }
2326
2327 fn rdb_len(bytes: &[u8], at: usize) -> (usize, usize) {
2333 match bytes[at] >> 6 {
2334 0 => (usize::from(bytes[at] & 0x3F), 1),
2335 1 => (
2336 usize::from(bytes[at] & 0x3F) << 8 | usize::from(bytes[at + 1]),
2337 2,
2338 ),
2339 other => panic!("the fixture used length form {other}"),
2340 }
2341 }
2342
2343 fn redis_node() -> (Id, Vec<u8>, Vec<u8>) {
2345 let dump = unhex(REDIS_DUMP);
2346 assert_eq!(dump[0], 0x1B, "RDB_TYPE_STREAM_LISTPACKS_3");
2347 let (nodes, n) = rdb_len(&dump, 1);
2348 assert_eq!(nodes, 1, "the fixture is one node");
2349 let mut at = 1 + n;
2350 let (key, n) = rdb_len(&dump, at);
2351 assert_eq!(key, 16, "a node key is an id in sixteen bytes");
2352 at += n;
2353 let master = Id::from_bytes(dump[at..at + 16].try_into().expect("sixteen bytes"));
2354 at += 16;
2355 let (len, n) = rdb_len(&dump, at);
2356 at += n;
2357 let lp = dump[at..at + len].to_vec();
2358 let rest = dump[at + len..dump.len() - 10].to_vec();
2361 (master, lp, rest)
2362 }
2363
2364 #[test]
2365 fn a_node_is_written_the_way_redis_writes_one() {
2366 let mut s = Stream::new();
2367 add(
2368 &mut s,
2369 1,
2370 1,
2371 &[("temperature_celsius", "21"), ("relative_humidity", "55")],
2372 );
2373 add(
2374 &mut s,
2375 1,
2376 2,
2377 &[("temperature_celsius", "22"), ("relative_humidity", "56")],
2378 );
2379 add(
2380 &mut s,
2381 2,
2382 1,
2383 &[("temperature_celsius", "23"), ("relative_humidity", "57")],
2384 );
2385 add(&mut s, 3, 1, &[("sensor", "a")]);
2386 assert!(s.delete(Id::new(1, 2)));
2387
2388 let (master, lp, _) = redis_node();
2389 assert_eq!(s.nodes(), 1, "all four fit in one node");
2390 assert_eq!(s.nodes[0].master, master);
2391 assert_eq!(s.nodes[0].lp.as_bytes(), &lp[..]);
2392 }
2393
2394 #[test]
2395 fn a_node_redis_wrote_reads_back() {
2396 let (master, lp, rest) = redis_node();
2397 let lp = Listpack::from_bytes(&lp).expect("a listpack Redis wrote");
2398 let s = Stream {
2399 nodes: VecDeque::from(vec![Node { master, lp }]),
2400 length: u64::from(rest[0]),
2401 last: Id::new(u64::from(rest[1]), u64::from(rest[2])),
2402 max_deleted: Id::new(u64::from(rest[5]), u64::from(rest[6])),
2403 added: u64::from(rest[7]),
2404 groups: Vec::new(),
2405 };
2406
2407 assert_eq!(s.len(), 3);
2408 assert_eq!(s.last_id(), Id::new(3, 1));
2409 assert_eq!(s.max_deleted_id(), Id::new(1, 2));
2410 assert_eq!(s.added(), 4);
2411 assert_eq!(s.first_id(), Some(Id::new(1, 1)));
2412 assert_eq!(
2413 dump(&s),
2414 vec![
2415 (
2416 Id::new(1, 1),
2417 vec![
2418 (b"temperature_celsius".to_vec(), b"21".to_vec()),
2419 (b"relative_humidity".to_vec(), b"55".to_vec())
2420 ]
2421 ),
2422 (
2423 Id::new(2, 1),
2424 vec![
2425 (b"temperature_celsius".to_vec(), b"23".to_vec()),
2426 (b"relative_humidity".to_vec(), b"57".to_vec())
2427 ]
2428 ),
2429 (Id::new(3, 1), vec![(b"sensor".to_vec(), b"a".to_vec())]),
2430 ]
2431 );
2432 }
2433
2434 fn logged(n: u64) -> Stream {
2436 let mut s = Stream::new();
2437 for ms in 1..=n {
2438 add(&mut s, ms, 0, &[("job", "x")]);
2439 }
2440 s
2441 }
2442
2443 fn read(s: &mut Stream, group: &str, who: &str, count: Option<usize>, now: u64) -> Vec<Id> {
2445 let mut out = Vec::new();
2446 s.read_group(
2447 group.as_bytes(),
2448 who.as_bytes(),
2449 count,
2450 false,
2451 now,
2452 |id, _| {
2453 out.push(id);
2454 true
2455 },
2456 )
2457 .expect("the group");
2458 out
2459 }
2460
2461 #[test]
2462 fn a_group_is_made_once() {
2463 let mut s = logged(3);
2464 assert!(s.create_group(b"workers", Id::MIN, Some(0)));
2465 assert!(!s.create_group(b"workers", Id::MIN, Some(0)));
2466 assert!(s.group(b"workers").is_some());
2467 assert!(s.destroy_group(b"workers"));
2468 assert!(!s.destroy_group(b"workers"));
2469 assert!(s.group(b"workers").is_none());
2470 }
2471
2472 #[test]
2473 fn a_group_read_hands_out_what_comes_after_the_bookmark() {
2474 let mut s = logged(5);
2475 s.create_group(b"workers", Id::MIN, Some(0));
2476
2477 assert_eq!(
2478 read(&mut s, "workers", "alice", Some(2), 100),
2479 vec![Id::new(1, 0), Id::new(2, 0)]
2480 );
2481 assert_eq!(
2483 read(&mut s, "workers", "bob", Some(2), 100),
2484 vec![Id::new(3, 0), Id::new(4, 0)]
2485 );
2486 assert_eq!(
2487 read(&mut s, "workers", "alice", None, 100),
2488 vec![Id::new(5, 0)]
2489 );
2490 assert_eq!(read(&mut s, "workers", "alice", None, 100), vec![]);
2491 }
2492
2493 #[test]
2494 fn a_group_read_fills_the_pending_list() {
2495 let mut s = logged(3);
2496 s.create_group(b"workers", Id::MIN, Some(0));
2497 read(&mut s, "workers", "alice", None, 500);
2498
2499 let g = s.group(b"workers").expect("the group");
2500 assert_eq!(g.pending_len(), 3);
2501 assert_eq!(g.last_id(), Id::new(3, 0));
2502 assert_eq!(g.entries_read(), Some(3));
2503 assert_eq!(s.lag(g), Some(0));
2504 let c = g.consumer_named(b"alice").expect("alice");
2505 assert_eq!(c.len(), 3);
2506 assert_eq!(c.active(), Some(500));
2507 }
2508
2509 #[test]
2510 fn a_read_that_finds_nothing_is_seen_but_not_active() {
2511 let mut s = logged(1);
2512 s.create_group(b"workers", Id::MIN, Some(0));
2513 read(&mut s, "workers", "alice", None, 100);
2514 read(&mut s, "workers", "alice", None, 900);
2515
2516 let c = s
2517 .group(b"workers")
2518 .expect("the group")
2519 .consumer_named(b"alice")
2520 .expect("alice");
2521 assert_eq!((c.seen(), c.active()), (900, Some(100)));
2522 }
2523
2524 #[test]
2525 fn a_group_starting_at_the_end_reads_only_what_comes_next() {
2526 let mut s = logged(3);
2527 s.create_group(b"workers", s.last_id(), Some(s.added()));
2528 assert_eq!(read(&mut s, "workers", "alice", None, 1), vec![]);
2529 add(&mut s, 4, 0, &[("job", "x")]);
2530 assert_eq!(
2531 read(&mut s, "workers", "alice", None, 1),
2532 vec![Id::new(4, 0)]
2533 );
2534 }
2535
2536 #[test]
2537 fn reading_a_group_that_is_not_there_says_so() {
2538 let mut s = logged(1);
2539 assert!(
2540 s.read_group(b"nope", b"alice", None, false, 1, |_, _| true)
2541 .is_none()
2542 );
2543 }
2544
2545 #[test]
2546 fn a_consumer_can_re_read_what_it_is_holding() {
2547 let mut s = logged(4);
2548 s.create_group(b"workers", Id::MIN, Some(0));
2549 read(&mut s, "workers", "alice", Some(2), 1);
2550 read(&mut s, "workers", "bob", Some(2), 1);
2551
2552 let mut out = Vec::new();
2553 s.read_group_pending(b"workers", b"alice", Id::MIN, None, 2, |id, fields| {
2554 out.push((id, fields.map(|f| f.len())));
2555 true
2556 })
2557 .expect("the group");
2558 assert_eq!(
2559 out,
2560 vec![(Id::new(1, 0), Some(1)), (Id::new(2, 0), Some(1))]
2561 );
2562
2563 let mut after = Vec::new();
2565 s.read_group_pending(b"workers", b"alice", Id::new(1, 0), None, 2, |id, _| {
2566 after.push(id);
2567 true
2568 });
2569 assert_eq!(after, vec![Id::new(2, 0)]);
2570 }
2571
2572 #[test]
2581 fn re_reading_counts_as_being_handed_it_again() {
2582 let mut s = logged(2);
2583 s.create_group(b"workers", Id::MIN, Some(0));
2584 read(&mut s, "workers", "alice", None, 100);
2585 s.read_group_pending(
2586 b"workers",
2587 b"alice",
2588 Id::MIN,
2589 None,
2590 700,
2591 |_: Id, _: Option<Fields<'_>>| true,
2592 );
2593
2594 let g = s.group(b"workers").expect("the group");
2595 let nack = g.nack(Id::new(1, 0)).expect("a nack");
2596 assert_eq!((nack.count(), nack.time()), (2, 700));
2597 assert_eq!(g.last_id(), Id::new(2, 0));
2599 assert_eq!(g.pending_len(), 2);
2600 }
2601
2602 #[test]
2609 fn a_hole_in_front_of_a_group_takes_its_lag() {
2610 let mut s = logged(5);
2611 s.create_group(b"workers", Id::MIN, Some(0));
2612 read(&mut s, "workers", "alice", Some(3), 1);
2613 assert_eq!(counters(&s), (Some(3), Some(2)));
2614
2615 assert!(s.delete(Id::new(1, 0)));
2619 assert_eq!(counters(&s), (Some(3), Some(2)));
2620
2621 assert!(s.delete(Id::new(5, 0)));
2624 assert_eq!(counters(&s), (Some(3), None));
2625
2626 read(&mut s, "workers", "alice", None, 1);
2630 assert_eq!(s.group(b"workers").expect("g").last_id(), Id::new(4, 0));
2631 assert_eq!(counters(&s), (None, None));
2632 }
2633
2634 #[test]
2640 fn trimming_past_a_group_leaves_it_the_length() {
2641 let mut s = logged(500);
2642 s.create_group(b"workers", Id::MIN, Some(0));
2643 read(&mut s, "workers", "alice", Some(400), 1);
2644 assert_eq!(counters(&s), (Some(400), Some(100)));
2645
2646 assert_eq!(s.trim_maxlen(200, true, None), 300);
2648 assert_eq!(counters(&s), (Some(400), Some(100)));
2649
2650 assert_eq!(s.trim_maxlen(10, true, None), 190);
2653 assert_eq!(counters(&s), (Some(400), Some(10)));
2654 }
2655
2656 fn counters(s: &Stream) -> (Option<u64>, Option<u64>) {
2658 let g = s.group(b"workers").expect("the group");
2659 (g.entries_read(), s.lag(g))
2660 }
2661
2662 #[test]
2663 fn an_entry_that_went_away_still_comes_back_as_a_hole() {
2664 let mut s = logged(3);
2665 s.create_group(b"workers", Id::MIN, Some(0));
2666 read(&mut s, "workers", "alice", None, 1);
2667 assert!(s.delete(Id::new(2, 0)));
2668
2669 let mut out = Vec::new();
2670 s.read_group_pending(b"workers", b"alice", Id::MIN, None, 2, |id, fields| {
2671 out.push((id, fields.is_some()));
2672 true
2673 });
2674 assert_eq!(
2675 out,
2676 vec![
2677 (Id::new(1, 0), true),
2678 (Id::new(2, 0), false),
2679 (Id::new(3, 0), true)
2680 ]
2681 );
2682 }
2683
2684 #[test]
2685 fn acking_clears_the_pending_list() {
2686 let mut s = logged(3);
2687 s.create_group(b"workers", Id::MIN, Some(0));
2688 read(&mut s, "workers", "alice", None, 1);
2689 let g = s.group_mut(b"workers").expect("the group");
2690 assert!(g.ack(Id::new(2, 0)));
2691 assert_eq!(g.pending_len(), 2);
2692 }
2693
2694 #[test]
2695 fn a_claim_moves_work_off_a_consumer_that_stopped() {
2696 let mut s = logged(2);
2697 s.create_group(b"workers", Id::MIN, Some(0));
2698 read(&mut s, "workers", "alice", None, 100);
2699
2700 let mut gone = Vec::new();
2701 let took = s
2702 .claim(
2703 b"workers",
2704 b"bob",
2705 &[Id::new(1, 0), Id::new(2, 0)],
2706 500,
2707 5_000,
2708 None,
2709 true,
2710 false,
2711 5_000,
2712 &mut gone,
2713 )
2714 .expect("the group");
2715 assert_eq!(took, vec![Id::new(1, 0), Id::new(2, 0)]);
2716 assert!(gone.is_empty());
2717
2718 let g = s.group(b"workers").expect("the group");
2719 assert!(g.consumer_named(b"alice").expect("alice").is_empty());
2720 assert_eq!(g.consumer_named(b"bob").expect("bob").len(), 2);
2721 assert_eq!(g.nack(Id::new(1, 0)).expect("a nack").count(), 2);
2722 }
2723
2724 #[test]
2725 fn a_claim_leaves_work_that_is_not_idle_enough_alone() {
2726 let mut s = logged(1);
2727 s.create_group(b"workers", Id::MIN, Some(0));
2728 read(&mut s, "workers", "alice", None, 100);
2729
2730 let mut gone = Vec::new();
2731 let took = s
2732 .claim(
2733 b"workers",
2734 b"bob",
2735 &[Id::new(1, 0)],
2736 5_000,
2737 200,
2738 None,
2739 true,
2740 false,
2741 200,
2742 &mut gone,
2743 )
2744 .expect("the group");
2745 assert!(took.is_empty());
2746 assert_eq!(
2747 s.group(b"workers")
2748 .expect("the group")
2749 .consumer_named(b"alice")
2750 .expect("alice")
2751 .len(),
2752 1
2753 );
2754 }
2755
2756 #[test]
2757 fn claiming_an_entry_that_went_away_drops_it_instead() {
2758 let mut s = logged(2);
2759 s.create_group(b"workers", Id::MIN, Some(0));
2760 read(&mut s, "workers", "alice", None, 100);
2761 assert!(s.delete(Id::new(1, 0)));
2762
2763 let mut gone = Vec::new();
2764 let took = s
2765 .claim(
2766 b"workers",
2767 b"bob",
2768 &[Id::new(1, 0), Id::new(2, 0)],
2769 0,
2770 5_000,
2771 None,
2772 true,
2773 false,
2774 5_000,
2775 &mut gone,
2776 )
2777 .expect("the group");
2778 assert_eq!(took, vec![Id::new(2, 0)]);
2779 assert_eq!(gone, vec![Id::new(1, 0)]);
2780 assert_eq!(s.group(b"workers").expect("the group").pending_len(), 1);
2781 }
2782
2783 #[test]
2784 fn force_only_works_on_an_entry_that_is_really_there() {
2785 let mut s = logged(2);
2786 s.create_group(b"workers", s.last_id(), Some(2));
2787
2788 let mut gone = Vec::new();
2789 let took = s
2790 .claim(
2791 b"workers",
2792 b"bob",
2793 &[Id::new(1, 0), Id::new(99, 0)],
2794 0,
2795 100,
2796 None,
2797 true,
2798 true,
2799 100,
2800 &mut gone,
2801 )
2802 .expect("the group");
2803 assert_eq!(took, vec![Id::new(1, 0)], "99-0 is not in the stream");
2804 assert_eq!(s.group(b"workers").expect("the group").pending_len(), 1);
2805 }
2806
2807 #[test]
2808 fn autoclaim_sweeps_the_stale_ones_and_says_where_it_stopped() {
2809 let mut s = logged(6);
2810 s.create_group(b"workers", Id::MIN, Some(0));
2811 read(&mut s, "workers", "alice", Some(3), 100);
2812 read(&mut s, "workers", "alice", None, 900);
2813
2814 let mut gone = Vec::new();
2815 let (cursor, took) = s
2816 .autoclaim(
2817 b"workers",
2818 b"bob",
2819 Id::MIN,
2820 500,
2821 100,
2822 true,
2823 1_000,
2824 &mut gone,
2825 )
2826 .expect("the group");
2827 assert_eq!(cursor, None, "the sweep reached the end");
2828 assert_eq!(took, vec![Id::new(1, 0), Id::new(2, 0), Id::new(3, 0)]);
2829 assert_eq!(
2830 s.group(b"workers")
2831 .expect("the group")
2832 .consumer_named(b"bob")
2833 .expect("bob")
2834 .len(),
2835 3
2836 );
2837 }
2838
2839 #[test]
2840 fn autoclaim_hands_back_a_cursor_when_it_hits_the_count() {
2841 let mut s = logged(10);
2842 s.create_group(b"workers", Id::MIN, Some(0));
2843 read(&mut s, "workers", "alice", None, 100);
2844
2845 let mut gone = Vec::new();
2846 let (cursor, took) = s
2847 .autoclaim(b"workers", b"bob", Id::MIN, 0, 4, true, 1_000, &mut gone)
2848 .expect("the group");
2849 assert_eq!(took.len(), 4);
2850 assert_eq!(cursor, Some(Id::new(5, 0)));
2851
2852 let (cursor, took) = s
2854 .autoclaim(
2855 b"workers",
2856 b"bob",
2857 cursor.expect("a cursor"),
2858 0,
2859 100,
2860 true,
2861 1_000,
2862 &mut gone,
2863 )
2864 .expect("the group");
2865 assert_eq!(took.len(), 6);
2866 assert_eq!(cursor, None);
2867 }
2868
2869 #[test]
2870 fn a_group_survives_the_stream_being_trimmed_under_it() {
2871 let mut s = logged(10);
2872 s.create_group(b"workers", Id::MIN, Some(0));
2873 read(&mut s, "workers", "alice", Some(5), 100);
2874 assert_eq!(s.trim_maxlen(3, true, None), 7);
2876
2877 assert_eq!(s.group(b"workers").expect("the group").pending_len(), 5);
2878 let mut gone = Vec::new();
2879 let took = s
2880 .claim(
2881 b"workers",
2882 b"bob",
2883 &(1..=5).map(|ms| Id::new(ms, 0)).collect::<Vec<_>>(),
2884 0,
2885 1_000,
2886 None,
2887 true,
2888 false,
2889 1_000,
2890 &mut gone,
2891 )
2892 .expect("the group");
2893 assert!(took.is_empty(), "none of them are there any more");
2894 assert_eq!(gone.len(), 5);
2895 assert_eq!(s.group(b"workers").expect("the group").pending_len(), 0);
2896 }
2897
2898 fn round_trip(s: &Stream) -> Stream {
2900 let mut bytes = Vec::new();
2901 s.freeze(&mut bytes);
2902 let back = Stream::thaw(&bytes).expect("our own bytes");
2903 assert_eq!(dump(&back), dump(s), "the entries");
2904 assert_eq!(back.len(), s.len(), "the length");
2905 assert_eq!(back.added(), s.added(), "the count added");
2906 assert_eq!(back.last_id(), s.last_id(), "the last ID");
2907 assert_eq!(back.max_deleted_id(), s.max_deleted_id(), "the max deleted");
2908 assert_eq!(back.nodes(), s.nodes(), "the node count");
2909 assert_eq!(back, *s, "the whole thing");
2910 back
2911 }
2912
2913 #[test]
2914 fn a_frozen_stream_comes_back_with_every_entry_it_held() {
2915 let mut s = Stream::new();
2916 for ms in 1..=500u64 {
2917 add(&mut s, ms, 0, &[("job", "x"), ("n", "1")]);
2918 }
2919 add(&mut s, 500, 1, &[("job", "y")]);
2920 assert!(s.nodes() > 1, "more than one node, so the walk is tested");
2921 round_trip(&s);
2922 }
2923
2924 #[test]
2925 fn a_frozen_stream_keeps_the_holes_and_the_counters() {
2926 let mut s = logged(200);
2927 for ms in [3u64, 4, 5, 100, 199] {
2928 assert!(s.delete(Id::new(ms, 0)));
2929 }
2930 s.trim_minid(Id::new(20, 0), true, None);
2931 let back = round_trip(&s);
2932 assert_eq!(back.first_id(), Some(Id::new(20, 0)));
2933 assert!(!back.contains(Id::new(100, 0)), "a hole is still a hole");
2934 assert_eq!(back.max_deleted_id(), Id::new(199, 0));
2935 }
2936
2937 #[test]
2938 fn a_frozen_stream_keeps_its_groups_and_who_is_holding_what() {
2939 let mut s = logged(20);
2940 s.create_group(b"workers", Id::MIN, Some(0));
2941 s.create_group(b"audit", Id::new(5, 0), None);
2942 read(&mut s, "workers", "alice", Some(6), 1_000);
2943 read(&mut s, "workers", "bob", Some(4), 2_000);
2944 assert_eq!(
2947 s.nack(b"workers", Id::new(2, 0), Retry::Keep, true),
2948 Some(true)
2949 );
2950 s.group_mut(b"workers")
2953 .expect("the group")
2954 .create_consumer(b"carol", 3_000);
2955 read(&mut s, "workers", "dave", Some(2), 4_000);
2956 s.group_mut(b"workers")
2957 .expect("the group")
2958 .delete_consumer(b"carol");
2959
2960 let back = round_trip(&s);
2961 let g = back.group(b"workers").expect("the group");
2962 assert_eq!(g.pending_len(), 12);
2963 assert_eq!(g.nacked_len(), 1);
2964 assert_eq!(g.entries_read(), Some(12));
2965 assert_eq!(g.consumer_named(b"alice").expect("alice").len(), 5);
2967 assert_eq!(g.consumer_named(b"bob").expect("bob").len(), 4);
2968 assert_eq!(g.consumer_named(b"dave").expect("dave").len(), 2);
2969 assert_eq!(g.consumer_named(b"carol"), None);
2970 assert_eq!(g.slot(b"dave"), Some(3));
2973 assert_eq!(g.nack(Id::new(1, 0)).expect("a nack").owner(), Some(0));
2974 assert_eq!(g.nack(Id::new(2, 0)).expect("a nack").owner(), None);
2975 assert_eq!(
2976 back.group(b"audit").expect("audit").last_id(),
2977 Id::new(5, 0)
2978 );
2979 assert_eq!(back.group(b"audit").expect("audit").entries_read(), None);
2980 }
2981
2982 #[test]
2983 fn a_stream_that_came_back_still_takes_entries_and_reads_them() {
2984 let mut s = logged(10);
2985 s.create_group(b"workers", Id::MIN, Some(0));
2986 read(&mut s, "workers", "alice", Some(4), 1_000);
2987
2988 let mut back = round_trip(&s);
2989 add(&mut back, 11, 0, &[("job", "new")]);
2990 assert_eq!(back.len(), 11);
2991 assert_eq!(
2992 read(&mut back, "workers", "alice", Some(3), 2_000),
2993 vec![Id::new(5, 0), Id::new(6, 0), Id::new(7, 0)]
2994 );
2995 assert!(
2996 back.group_mut(b"workers")
2997 .expect("the group")
2998 .ack(Id::new(1, 0))
2999 );
3000 assert_eq!(back.group(b"workers").expect("the group").pending_len(), 6);
3001 }
3002
3003 #[test]
3004 fn an_empty_stream_that_still_exists_comes_back() {
3005 let mut s = logged(3);
3006 for ms in 1..=3u64 {
3007 assert!(s.delete(Id::new(ms, 0)));
3008 }
3009 assert_eq!(s.nodes(), 0, "the last node went with the last entry");
3010 let back = round_trip(&s);
3011 assert!(back.is_empty());
3012 assert_eq!(back.last_id(), Id::new(3, 0));
3015 round_trip(&Stream::new());
3016 }
3017
3018 #[test]
3019 fn a_frozen_stream_that_arrives_damaged_is_an_error_and_not_a_panic() {
3020 let mut s = logged(8);
3021 s.create_group(b"workers", Id::MIN, Some(0));
3022 read(&mut s, "workers", "alice", Some(3), 1_000);
3023 let mut bytes = Vec::new();
3024 s.freeze(&mut bytes);
3025
3026 for cut in 0..bytes.len() {
3027 assert!(Stream::thaw(&bytes[..cut]).is_err(), "cut at {cut}");
3028 }
3029 for at in 0..bytes.len().min(40) {
3032 for bit in 0..8 {
3033 let mut bad = bytes.clone();
3034 bad[at] ^= 1 << bit;
3035 let _ = Stream::thaw(&bad);
3039 }
3040 }
3041 assert_eq!(Stream::thaw(&[]), Err(Broken::Short));
3042 assert_eq!(Stream::thaw(&[9]), Err(Broken::Form));
3043 }
3044
3045 #[test]
3046 fn nothing_at_all() {
3047 let mut s = Stream::new();
3048 assert!(s.is_empty());
3049 assert_eq!(s.len(), 0);
3050 assert_eq!(s.first_id(), None);
3051 assert_eq!(s.last_id(), Id::MIN);
3052 assert_eq!(s.trim_maxlen(0, true, None), 0);
3053 assert_eq!(s.trim_minid(Id::MAX, true, None), 0);
3054 assert_eq!(dump(&s), vec![]);
3055 }
3056}