1use std::{
2 fmt,
3 ops::{Add, RangeInclusive, Sub},
4};
5
6use rangemap::StepLite;
7use rusqlite::{ToSql, types::FromSql};
8use serde::{Deserialize, Serialize};
9use speedy::{Context, Readable, Writable};
10
11#[macro_export]
12macro_rules! dbvr {
13 ($start:literal, $end:literal) => {
14 $crate::base::CrsqlDbVersionRange::new(
15 $crate::base::CrsqlDbVersion($start),
16 $crate::base::CrsqlDbVersion($end),
17 )
18 };
19}
20
21#[macro_export]
22macro_rules! dbvri {
23 ($start:literal, $end:literal) => {
24 $crate::base::CrsqlDbVersion($start)..=$crate::base::CrsqlDbVersion($end)
25 };
26}
27
28#[macro_export]
29macro_rules! dbsr {
30 ($start:literal, $end:literal) => {
31 $crate::base::CrsqlSeqRange::new(
32 $crate::base::CrsqlSeq($start),
33 $crate::base::CrsqlSeq($end),
34 )
35 };
36}
37
38#[macro_export]
39macro_rules! dbsri {
40 ($start:literal, $end:literal) => {
41 $crate::base::CrsqlSeq($start)..=$crate::base::CrsqlSeq($end)
42 };
43}
44
45#[derive(
46 Copy, Clone, Debug, Default, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize,
47)]
48#[serde(transparent)]
49pub struct CrsqlDbVersion(pub u64);
50
51impl CrsqlDbVersion {
52 #[inline]
53 pub fn chunked_iter(
54 range: CrsqlDbVersionRange,
55 chunk_size: usize,
56 ) -> ChunkedCrsqlDbVersionIterator {
57 ChunkedCrsqlDbVersionIterator {
58 current: range.start_int(),
59 end: range.end_int(),
60 chunk_size: chunk_size as u64,
61 }
62 }
63}
64
65pub struct ChunkedCrsqlDbVersionIterator {
66 current: u64,
67 end: u64,
68 chunk_size: u64,
69}
70
71impl Iterator for ChunkedCrsqlDbVersionIterator {
72 type Item = CrsqlDbVersionRange;
73
74 #[inline]
75 fn next(&mut self) -> Option<Self::Item> {
76 if self.current > self.end {
77 return None;
78 }
79
80 let next_step = self.current.saturating_add(self.chunk_size);
83
84 let next = CrsqlDbVersionRange::new(
85 CrsqlDbVersion(self.current),
86 CrsqlDbVersion(next_step.min(self.end)),
87 );
88
89 self.current = next_step;
90
91 Some(next)
92 }
93}
94
95impl StepLite for CrsqlDbVersion {
96 fn add_one(&self) -> Self {
97 Self(self.0 + 1)
98 }
99
100 fn sub_one(&self) -> Self {
101 Self(self.0 - 1)
102 }
103}
104
105impl From<CrsqlDbVersion> for u64 {
106 fn from(v: CrsqlDbVersion) -> Self {
107 v.0
108 }
109}
110
111impl ToSql for CrsqlDbVersion {
112 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
113 self.0.to_sql()
114 }
115}
116
117impl FromSql for CrsqlDbVersion {
118 fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
119 u64::column_result(value).map(Self)
120 }
121}
122
123impl Add<u64> for CrsqlDbVersion {
124 type Output = Self;
125
126 fn add(self, rhs: u64) -> Self::Output {
127 Self(self.0.add(rhs))
128 }
129}
130
131impl Sub<u64> for CrsqlDbVersion {
132 type Output = Self;
133
134 fn sub(self, rhs: u64) -> Self::Output {
135 Self(self.0.sub(rhs))
136 }
137}
138
139impl<'a, C> Readable<'a, C> for CrsqlDbVersion
140where
141 C: Context,
142{
143 fn read_from<R: speedy::Reader<'a, C>>(reader: &mut R) -> Result<Self, <C as Context>::Error> {
144 u64::read_from(reader).map(Self)
145 }
146}
147
148impl<C> Writable<C> for CrsqlDbVersion
149where
150 C: Context,
151{
152 fn write_to<T: ?Sized + speedy::Writer<C>>(
153 &self,
154 writer: &mut T,
155 ) -> Result<(), <C as Context>::Error> {
156 self.0.write_to(writer)
157 }
158}
159
160impl fmt::Display for CrsqlDbVersion {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 self.0.fmt(f)
163 }
164}
165
166#[derive(
167 Copy, Clone, Debug, Default, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize,
168)]
169#[serde(transparent)]
170pub struct CrsqlSeq(pub u64);
171
172impl StepLite for CrsqlSeq {
173 fn add_one(&self) -> Self {
174 Self(self.0 + 1)
175 }
176
177 fn sub_one(&self) -> Self {
178 Self(self.0 - 1)
179 }
180}
181
182impl ToSql for CrsqlSeq {
183 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
184 self.0.to_sql()
185 }
186}
187
188impl FromSql for CrsqlSeq {
189 fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
190 u64::column_result(value).map(Self)
191 }
192}
193
194impl Add<u64> for CrsqlSeq {
195 type Output = Self;
196
197 fn add(self, rhs: u64) -> Self::Output {
198 Self(self.0.add(rhs))
199 }
200}
201
202impl Sub<u64> for CrsqlSeq {
203 type Output = Self;
204
205 fn sub(self, rhs: u64) -> Self::Output {
206 Self(self.0.sub(rhs))
207 }
208}
209
210impl<'a, C> Readable<'a, C> for CrsqlSeq
211where
212 C: Context,
213{
214 fn read_from<R: speedy::Reader<'a, C>>(reader: &mut R) -> Result<Self, <C as Context>::Error> {
215 u64::read_from(reader).map(Self)
216 }
217}
218
219impl<C> Writable<C> for CrsqlSeq
220where
221 C: Context,
222{
223 fn write_to<T: ?Sized + speedy::Writer<C>>(
224 &self,
225 writer: &mut T,
226 ) -> Result<(), <C as Context>::Error> {
227 self.0.write_to(writer)
228 }
229}
230
231impl fmt::Display for CrsqlSeq {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 self.0.fmt(f)
234 }
235}
236
237macro_rules! range {
238 ($inner:ident, $name:ident, $iter:ident) => {
239 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
240 pub struct $name {
241 start: u64,
242 end: Option<std::num::NonZeroU64>,
243 }
244
245 impl $name {
246 #[inline]
248 pub fn new(start: $inner, end: $inner) -> Self {
249 Self {
250 start: start.0,
251 end: std::num::NonZeroU64::new(end.0),
252 }
253 }
254
255 #[inline]
257 pub fn empty() -> Self {
258 Self {
259 start: 0,
260 end: None,
261 }
262 }
263
264 #[inline]
266 pub fn single(single: $inner) -> Self {
267 Self {
268 start: single.0,
269 end: std::num::NonZeroU64::new(single.0),
270 }
271 }
272
273 #[inline]
275 pub fn start(&self) -> $inner {
276 $inner(self.start)
277 }
278
279 #[inline]
281 pub fn end(&self) -> $inner {
282 $inner(self.end_int())
283 }
284
285 #[inline]
287 pub fn start_int(&self) -> u64 {
288 self.start
289 }
290
291 #[inline]
293 pub fn end_int(&self) -> u64 {
294 self.end.map_or(0, |e| e.get())
295 }
296
297 #[inline]
300 pub fn len(&self) -> usize {
301 let end = self.end.map_or(0, |e| e.get());
302 if end >= self.start {
303 (end - self.start) as usize + 1
304 } else {
305 0
306 }
307 }
308
309 #[inline]
310 pub fn is_empty(&self) -> bool {
311 self.len() == 0
312 }
313 }
314
315 impl From<RangeInclusive<$inner>> for $name {
316 #[inline]
317 fn from(r: RangeInclusive<$inner>) -> Self {
318 let (start, end) = r.into_inner();
319 Self {
320 start: start.0,
321 end: std::num::NonZeroU64::new(end.0),
322 }
323 }
324 }
325
326 impl<'s> From<&'s RangeInclusive<$inner>> for $name {
327 #[inline]
328 fn from(r: &'s RangeInclusive<$inner>) -> Self {
329 Self {
330 start: r.start().0,
331 end: std::num::NonZeroU64::new(r.end().0),
332 }
333 }
334 }
335
336 impl From<$name> for RangeInclusive<$inner> {
337 #[inline]
338 fn from(r: $name) -> Self {
339 Self::new(r.start(), r.end())
340 }
341 }
342
343 impl<'s> From<&'s $name> for RangeInclusive<$inner> {
344 #[inline]
345 fn from(r: &'s $name) -> Self {
346 Self::new(r.start(), r.end())
347 }
348 }
349
350 impl Iterator for $name {
351 type Item = $inner;
352
353 #[inline]
354 fn next(&mut self) -> Option<Self::Item> {
355 let end = self.end?;
356
357 let next = if self.start < end.get() {
358 let n = $inner(self.start);
359 self.start += 1;
360 n
361 } else {
362 self.end = None;
363 $inner(self.start)
364 };
365
366 Some(next)
367 }
368
369 #[inline]
370 fn size_hint(&self) -> (usize, Option<usize>) {
371 let len = self.len();
372 (len, Some(len))
373 }
374 }
375
376 impl<'a, C: speedy::Context> Readable<'a, C> for $name {
377 #[inline]
378 fn read_from<R: speedy::Reader<'a, C>>(reader: &mut R) -> Result<Self, C::Error> {
379 let start = reader.read_value()?;
380 let end: u64 = reader.read_value()?;
381 Ok(Self {
382 start,
383 end: std::num::NonZeroU64::new(end),
384 })
385 }
386
387 #[inline]
388 fn minimum_bytes_needed() -> usize {
389 16
390 }
391 }
392
393 impl<C: speedy::Context> Writable<C> for $name {
394 #[inline]
395 fn write_to<W: ?Sized + speedy::Writer<C>>(
396 &self,
397 writer: &mut W,
398 ) -> Result<(), C::Error> {
399 self.start.write_to(writer)?;
400 self.end.map_or(0, |e| e.get()).write_to(writer)
401 }
402
403 #[inline]
404 fn bytes_needed(&self) -> Result<usize, C::Error> {
405 Ok(16)
406 }
407 }
408 };
409}
410
411range!(CrsqlDbVersion, CrsqlDbVersionRange, CrsqlDbVersionIter);
412range!(CrsqlSeq, CrsqlSeqRange, CrsqlSeqIter);
413
414#[cfg(test)]
415mod test {
416 use super::*;
417
418 #[test]
419 fn chunked_iter() {
420 fn chunk_range(
423 range: RangeInclusive<u64>,
424 chunk_size: usize,
425 ) -> impl Iterator<Item = RangeInclusive<u64>> {
426 let end = *range.end();
427 range.step_by(chunk_size).map(move |block_start| {
428 let block_end = (block_start + chunk_size as u64).min(end);
429 block_start..=block_end
430 })
431 }
432
433 #[track_caller]
434 fn matches_step_by(range: RangeInclusive<u64>, chunk_size: usize) {
435 let mut ours = CrsqlDbVersion::chunked_iter(
436 CrsqlDbVersionRange::new(
437 CrsqlDbVersion(*range.start()),
438 CrsqlDbVersion(*range.end()),
439 ),
440 chunk_size,
441 );
442 let mut stds = chunk_range(range, chunk_size);
443
444 loop {
445 match (ours.next(), stds.next()) {
446 (Some(o), Some(s)) => {
447 assert_eq!(o.start_int(), *s.start());
448 assert_eq!(o.end_int(), *s.end());
449 }
450 (None, None) => {
451 break;
452 }
453 (Some(o), None) => {
454 panic!("step_by was done but we still had {o:?}");
455 }
456 (None, Some(s)) => {
457 panic!("we were done but step_by still had {s:?}");
458 }
459 }
460 }
461 }
462
463 {
465 matches_step_by(0..=0, 10);
466 matches_step_by(1987..=1987, 1);
467
468 #[allow(clippy::reversed_empty_ranges)]
470 matches_step_by(2..=0, 1);
471 }
472
473 {
475 matches_step_by(1..=10, 11);
476 matches_step_by(0..=1, 1);
477 matches_step_by(1..=13, 14);
478 }
479
480 {
482 matches_step_by(2001..=11000, 13);
483 }
484 }
485
486 #[test]
487 fn ranges() {
488 let r = dbsr!(1, 1000);
489 assert_eq!(r.len(), 1000);
490
491 let ri = RangeInclusive::from(r);
492 assert_eq!(ri.start(), &r.start());
493 assert_eq!(ri.end(), &r.end());
494
495 for (i, (ours, theirs)) in r.zip(r.start_int()..=r.end_int()).enumerate() {
496 assert_eq!(ours.0, theirs, "oops {i}");
497 }
498
499 let max =
500 CrsqlDbVersionRange::from(CrsqlDbVersion(u64::MAX - 1)..=CrsqlDbVersion(u64::MAX));
501 assert_eq!(max.len(), 2);
502 assert_eq!(max.into_iter().count(), 2);
503 }
504
505 #[test]
506 fn serialization() {
507 #[track_caller]
508 fn speedy(input: CrsqlDbVersionRange) {
509 let ser = input.write_to_vec().unwrap();
510 let deser = CrsqlDbVersionRange::read_from_buffer(&ser).unwrap();
511
512 assert_eq!(input, deser);
513 }
514
515 speedy(CrsqlDbVersionRange::empty());
516 speedy(CrsqlDbVersionRange::new(
517 CrsqlDbVersion(0),
518 CrsqlDbVersion(u64::MAX),
519 ));
520 speedy(CrsqlDbVersionRange::new(
521 CrsqlDbVersion(110),
522 CrsqlDbVersion(0),
523 ));
524 }
525}