reifydb_flow/window/
coord.rs1use std::fmt::Debug;
5
6use reifydb_core::metrics::heap::HeapSize;
7use reifydb_macro::operator_state;
8use reifydb_value::value::{datetime::DateTime, row_number::RowNumber};
9
10use crate::{
11 operator::state::seal::coord::{Coord, IsZero},
12 window::span::Slot,
13};
14
15pub trait TimeStamped {
16 fn row_time(&self) -> DateTime;
17}
18
19impl TimeStamped for DateTime {
20 fn row_time(&self) -> DateTime {
21 *self
22 }
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub struct EventCoord(DateTime);
27
28impl EventCoord {
29 pub fn of(row: &impl TimeStamped) -> Self {
30 Self(row.row_time())
31 }
32
33 pub fn at(self) -> DateTime {
34 self.0
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
39pub struct RowSpan {
40 rows: u64,
41}
42
43impl RowSpan {
44 pub const ZERO: Self = Self {
45 rows: 0,
46 };
47
48 pub fn of(rows: u64) -> Self {
49 Self {
50 rows,
51 }
52 }
53
54 pub fn rows(self) -> u64 {
55 self.rows
56 }
57}
58
59impl IsZero for RowSpan {
60 #[inline]
61 fn is_zero(&self) -> bool {
62 self.rows == 0
63 }
64}
65
66#[operator_state]
67#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
68pub struct OrdinalCoord {
69 ordinal: u64,
70}
71
72impl OrdinalCoord {
73 pub fn from_arrival_counter(ordinal: u64) -> Self {
74 Self {
75 ordinal,
76 }
77 }
78
79 pub fn from_row_number(row_number: RowNumber) -> Self {
80 Self {
81 ordinal: row_number.0,
82 }
83 }
84
85 pub fn value(self) -> u64 {
86 self.ordinal
87 }
88}
89
90impl HeapSize for OrdinalCoord {
91 fn heap_size(&self) -> usize {
92 0
93 }
94}
95
96impl Coord for OrdinalCoord {
97 type Span = RowSpan;
98
99 const MAX: Self = Self {
100 ordinal: u64::MAX,
101 };
102
103 fn saturating_sub_span(self, span: RowSpan) -> Self {
104 Self {
105 ordinal: self.ordinal.saturating_sub(span.rows),
106 }
107 }
108
109 fn checked_sub_span(self, span: RowSpan) -> Option<Self> {
110 self.ordinal.checked_sub(span.rows).map(|ordinal| Self {
111 ordinal,
112 })
113 }
114
115 fn add_span(self, span: RowSpan) -> Self {
116 Self {
117 ordinal: self.ordinal + span.rows,
118 }
119 }
120
121 fn floor_to(self, span: RowSpan) -> Self {
122 Self {
123 ordinal: self.ordinal - (self.ordinal % span.rows),
124 }
125 }
126
127 fn span_since(self, earlier: Self) -> RowSpan {
128 RowSpan {
129 rows: self.ordinal - earlier.ordinal,
130 }
131 }
132
133 fn to_order(self) -> u64 {
134 self.ordinal
135 }
136
137 fn from_order(order: u64) -> Self {
138 Self {
139 ordinal: order,
140 }
141 }
142
143 fn span_millis(_span: RowSpan) -> Option<u64> {
144 None
145 }
146}
147
148impl Slot for OrdinalCoord {
149 type Coord = OrdinalCoord;
150
151 fn order_key(&self) -> OrdinalCoord {
152 *self
153 }
154
155 fn from_order_key(coord: OrdinalCoord) -> Self {
156 coord
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use reifydb_codec::row::operator::encode;
163
164 use super::*;
165
166 struct Row {
167 time: DateTime,
168 other_column: DateTime,
169 }
170
171 impl TimeStamped for Row {
172 fn row_time(&self) -> DateTime {
173 self.time
174 }
175 }
176
177 #[test]
178 fn an_event_coordinate_can_only_come_from_the_row_time() {
179 let row = Row {
183 time: DateTime::from_millis(5_000),
184 other_column: DateTime::from_millis(9_999),
185 };
186
187 assert_eq!(EventCoord::of(&row).at(), DateTime::from_millis(5_000));
188 assert_ne!(EventCoord::of(&row).at(), row.other_column);
189 }
190
191 #[test]
192 fn event_coordinates_order_by_instant() {
193 let early = EventCoord::of(&DateTime::from_millis(1));
196 let late = EventCoord::of(&DateTime::from_millis(2));
197
198 assert!(early < late);
199 }
200
201 #[test]
202 fn an_ordinal_encodes_to_the_same_bytes_as_the_bare_count_it_replaced() {
203 let value = 0x0123_4567_89AB_CDEFu64;
205
206 let wrapped = encode(&OrdinalCoord::from_arrival_counter(value), DateTime::EPOCH).expect("encode");
207 let bare = encode(&value, DateTime::EPOCH).expect("encode");
208
209 assert_eq!(wrapped.body(), bare.body(), "the newtype changed the persisted layout");
210 }
211
212 #[test]
213 fn ordinal_arithmetic_counts_rows_and_refuses_to_answer_in_milliseconds() {
214 let coord = OrdinalCoord::from_arrival_counter(100);
217
218 assert_eq!(coord.saturating_sub_span(RowSpan::of(64)), OrdinalCoord::from_arrival_counter(36));
219 assert_eq!(coord.add_span(RowSpan::of(5)), OrdinalCoord::from_arrival_counter(105));
220 assert_eq!(coord.span_since(OrdinalCoord::from_arrival_counter(60)), RowSpan::of(40));
221 assert_eq!(<OrdinalCoord as Coord>::span_millis(RowSpan::of(64)), None);
222 }
223
224 #[test]
225 fn an_ordinal_below_its_own_span_has_no_earlier_coordinate_rather_than_wrapping() {
226 let coord = OrdinalCoord::from_arrival_counter(10);
228
229 assert_eq!(coord.checked_sub_span(RowSpan::of(11)), None);
230 assert_eq!(coord.checked_sub_span(RowSpan::of(10)), Some(OrdinalCoord::from_arrival_counter(0)));
231 assert_eq!(coord.saturating_sub_span(RowSpan::of(11)), OrdinalCoord::from_arrival_counter(0));
232 }
233
234 #[test]
235 fn both_ordinal_sources_produce_the_same_domain() {
236 let minted = OrdinalCoord::from_arrival_counter(7);
239 let from_row = OrdinalCoord::from_row_number(RowNumber(7));
240
241 assert_eq!(minted, from_row);
242 assert_eq!(minted.value(), 7);
243 }
244}