surrealdb_core/vs/
mod.rs

1//! vs is a module to handle Versionstamps.
2//! This module is supplemental to the kvs::tx module and is not intended to be used directly
3//! by applications.
4//! This module might be migrated into the kvs or kvs::tx module in the future.
5
6pub use std::{error, fmt, mem};
7
8use revision::Revisioned;
9
10/// Versionstamp is a 10-byte array used to identify a specific version of a key.
11/// The first 8 bytes are significant (the u64), and the remaining 2 bytes are not significant, but used for extra precision.
12/// To convert to and from this module, see the conv module in this same directory.
13///
14/// You're going to want these
15/// 65536
16/// 131072
17/// 196608
18/// 262144
19/// 327680
20/// 393216
21#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, PartialOrd)]
22pub struct VersionStamp([u8; 10]);
23
24impl serde::Serialize for VersionStamp {
25	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26	where
27		S: serde::Serializer,
28	{
29		self.0.serialize(serializer)
30	}
31}
32
33impl<'de> serde::Deserialize<'de> for VersionStamp {
34	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
35	where
36		D: serde::Deserializer<'de>,
37	{
38		let res = <[u8; 10]>::deserialize(deserializer)?;
39		Ok(VersionStamp(res))
40	}
41}
42
43// Version stamp was previously a normal array so it doesn't have any kind of revision tracking and
44// is serialized just like any other array.
45impl Revisioned for VersionStamp {
46	fn revision() -> u16 {
47		0
48	}
49
50	fn serialize_revisioned<W: std::io::Write>(&self, w: &mut W) -> Result<(), revision::Error> {
51		self.0.serialize_revisioned(w)
52	}
53
54	fn deserialize_revisioned<R: std::io::Read>(r: &mut R) -> Result<Self, revision::Error>
55	where
56		Self: Sized,
57	{
58		Revisioned::deserialize_revisioned(r).map(VersionStamp)
59	}
60}
61
62impl Default for VersionStamp {
63	fn default() -> Self {
64		VersionStamp::ZERO
65	}
66}
67
68pub struct VersionStampError(());
69
70impl fmt::Display for VersionStampError {
71	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72		write!(f, "Invalid version stamp conversion")
73	}
74}
75impl fmt::Debug for VersionStampError {
76	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77		fmt::Display::fmt(self, f)
78	}
79}
80impl error::Error for VersionStampError {}
81
82impl VersionStamp {
83	pub const ZERO: VersionStamp = VersionStamp([0; 10]);
84
85	pub const fn from_u64(v: u64) -> Self {
86		let b = v.to_be_bytes();
87		VersionStamp([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], 0, 0])
88	}
89
90	pub const fn from_u64_u16(v: u64, v2: u16) -> Self {
91		let b1 = v.to_be_bytes();
92		let b2 = v2.to_be_bytes();
93		VersionStamp([b1[0], b1[1], b1[2], b1[3], b1[4], b1[5], b1[6], b1[7], b2[0], b2[1]])
94	}
95
96	pub const fn try_from_u128(v: u128) -> Result<Self, VersionStampError> {
97		if (v >> 80) > 0 {
98			return Err(VersionStampError(()));
99		}
100		let b = v.to_be_bytes();
101		Ok(VersionStamp([b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]]))
102	}
103
104	/// Convert the VersionStamp into a u64 ignoring the 2 normally zero bytes.
105	pub const fn into_u64_u16(self) -> (u64, u16) {
106		let b = self.0;
107		(
108			u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]),
109			u16::from_be_bytes([b[8], b[9]]),
110		)
111	}
112
113	/// Convert the VersionStamp into a u64 ignoring the 2 normally zero bytes.
114	pub const fn into_u64_lossy(self) -> u64 {
115		let b = self.0;
116		u64::from_be_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]])
117	}
118
119	pub const fn try_into_u64(self) -> Result<u64, VersionStampError> {
120		if self.0[8] > 0 || self.0[9] > 0 {
121			return Err(VersionStampError(()));
122		}
123		Ok(self.into_u64_lossy())
124	}
125
126	pub const fn into_u128(self) -> u128 {
127		let b = self.0;
128		u128::from_be_bytes([
129			0, 0, 0, 0, 0, 0, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9],
130		])
131	}
132
133	pub const fn as_bytes(self) -> [u8; 10] {
134		self.0
135	}
136
137	pub const fn from_bytes(bytes: [u8; 10]) -> Self {
138		Self(bytes)
139	}
140
141	pub const fn from_slice(slice: &[u8]) -> Result<Self, VersionStampError> {
142		if slice.len() != 10 {
143			return Err(VersionStampError(()));
144		}
145		Ok(VersionStamp([
146			slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7],
147			slice[8], slice[9],
148		]))
149	}
150
151	pub fn next(self) -> Option<Self> {
152		let (v, suffix) = self.into_u64_u16();
153		let v = v.checked_add(1)?;
154		let next = VersionStamp::from_u64_u16(v, suffix);
155		Some(next)
156	}
157
158	/// Returns an iterator of version stamps starting with the current version stamp.
159	pub fn iter(self) -> VersionStampIter {
160		VersionStampIter {
161			cur: Some(self),
162		}
163	}
164}
165
166pub struct VersionStampIter {
167	cur: Option<VersionStamp>,
168}
169
170impl Iterator for VersionStampIter {
171	type Item = VersionStamp;
172
173	fn next(&mut self) -> Option<Self::Item> {
174		let next = self.cur?.next();
175		mem::replace(&mut self.cur, next)
176	}
177}
178
179#[cfg(test)]
180mod test {
181	use super::VersionStamp;
182
183	#[test]
184	pub fn generate_one_vs() {
185		let vs = VersionStamp::ZERO.iter().take(1).collect::<Vec<_>>();
186		assert_eq!(vs.len(), 1, "Should be 1, but was {:?}", vs);
187		assert_eq!(vs[0], VersionStamp::ZERO);
188	}
189
190	#[test]
191	pub fn generate_two_vs_in_sequence() {
192		let vs = VersionStamp::from_bytes([0, 0, 0, 0, 0, 0, 0, 1, 0, 0]).iter().flat_map(|vs| {
193			let skip_because_first_is_equal = 1;
194			vs.iter().skip(skip_because_first_is_equal).map(move |vs2| (vs, vs2))
195		});
196		let versionstamps = vs.take(4).collect::<Vec<(VersionStamp, VersionStamp)>>();
197
198		assert_eq!(
199			versionstamps.len(),
200			4,
201			"We expect the combinations to be 2x2 matrix, but was {:?}",
202			versionstamps
203		);
204
205		let acceptable_values = [65536u128, 131072, 196608, 262144, 327680, 393216];
206		for (first, second) in versionstamps {
207			assert!(first < second, "First: {:?}, Second: {:?}", first, second);
208			let first = first.into_u128();
209			let second = second.into_u128();
210			assert!(acceptable_values.contains(&first));
211			assert!(acceptable_values.contains(&second));
212		}
213	}
214
215	#[test]
216	pub fn iteration_stops_past_end() {
217		let mut iter = VersionStamp::from_bytes([255; 10]).iter();
218		assert!(iter.next().is_some());
219		assert!(iter.next().is_none());
220	}
221
222	#[test]
223	fn try_to_u64_be() {
224		use super::*;
225		// Overflow
226		let v = VersionStamp::from_bytes([255, 255, 255, 255, 255, 255, 255, 255, 0, 1]);
227		let res = v.try_into_u64();
228		assert!(res.is_err());
229		// No overflow
230		let v = VersionStamp::from_bytes([255, 255, 255, 255, 255, 255, 255, 255, 0, 0]);
231		let res = v.try_into_u64().unwrap();
232		assert_eq!(res, u64::MAX);
233	}
234
235	#[test]
236	fn try_u128_to_versionstamp() {
237		use super::*;
238		// Overflow
239		let v = u128::MAX;
240		let res = VersionStamp::try_from_u128(v);
241		assert!(res.is_err());
242		// No overflow
243		let v = u128::MAX >> 48;
244		let res = VersionStamp::try_from_u128(v).unwrap();
245		assert_eq!(res.as_bytes(), [255, 255, 255, 255, 255, 255, 255, 255, 255, 255]);
246	}
247
248	#[test]
249	fn can_add_u64_conversion() {
250		let start = 5u64;
251		let vs = VersionStamp::from_u64(start);
252		// The last 2 bytes are empty
253		assert_eq!("00000000000000050000", hex::encode(vs.as_bytes()));
254		let mid = vs.try_into_u64().unwrap();
255		assert_eq!(start, mid);
256		let mid = mid + 1;
257		let vs = VersionStamp::from_u64(mid);
258		// The last 2 bytes are empty
259		assert_eq!("00000000000000060000", hex::encode(vs.as_bytes()));
260		let end = vs.try_into_u64().unwrap();
261		assert_eq!(end, 6);
262	}
263}