Skip to main content

tuwunel_matrix/pdu/
raw_id.rs

1use std::fmt;
2
3use arrayvec::ArrayVec;
4use serde::{Deserialize, Deserializer};
5
6use super::{
7	super::{ShortId, ShortRoomId},
8	Count, Id,
9};
10
11/// Packed database key for a room's normal or backfilled PDU.
12///
13/// Both layouts begin with the big-endian compact room identifier. Normal keys
14/// are 16 bytes and backfilled keys are 24 bytes.
15#[derive(Clone, Copy, Eq, Hash, PartialEq)]
16pub enum RawId {
17	/// Packed key for an event in the normal timeline.
18	///
19	/// The key concatenates the room surrogate and unsigned count as two
20	/// big-endian integers.
21	Normal(RawIdNormal),
22
23	/// Packed key for an event in the backfilled timeline.
24	///
25	/// The key inserts an eight-byte zero marker between the room surrogate and
26	/// signed count bits.
27	Backfilled(RawIdBackfilled),
28}
29
30type RawIdNormal = [u8; RawId::NORMAL_LEN];
31type RawIdBackfilled = [u8; RawId::BACKFILLED_LEN];
32
33struct RawIdVisitor;
34
35const INT_LEN: usize = size_of::<ShortId>();
36
37impl RawId {
38	const BACKFILLED_LEN: usize = size_of::<ShortRoomId>() + INT_LEN + size_of::<i64>();
39	const MAX_LEN: usize = Self::BACKFILLED_LEN;
40	const NORMAL_LEN: usize = size_of::<ShortRoomId>() + size_of::<u64>();
41
42	/// Reads a raw PDU key from bytes that may not be one.
43	///
44	/// A slice of either layout's length is taken as that layout, and any other
45	/// length gives `None`, where the `From` conversion panics. Integrity checks
46	/// read keys through here, since a damaged key is what they look for.
47	#[inline]
48	#[must_use]
49	pub fn try_from_bytes(id: &[u8]) -> Option<Self> {
50		<&RawIdNormal>::try_from(id)
51			.map(Self::from)
52			.or_else(|_| <&RawIdBackfilled>::try_from(id).map(Self::from))
53			.ok()
54	}
55
56	/// Checks whether two raw PDU keys belong to the same room.
57	///
58	/// Only the leading compact room identifier is compared. Timeline kind and
59	/// event count do not affect the result.
60	#[inline]
61	#[must_use]
62	pub fn is_room_eq(self, other: Self) -> bool { self.shortroomid() == other.shortroomid() }
63
64	/// Decodes the timeline count stored in this raw key.
65	///
66	/// Normal and backfilled layouts are converted to the corresponding `Count`
67	/// variant. The compact room identifier is ignored.
68	#[inline]
69	#[must_use]
70	pub fn pdu_count(self) -> Count {
71		let id: Id = self.into();
72		id.count
73	}
74
75	/// Returns the encoded compact room identifier.
76	///
77	/// The bytes remain in big-endian database order. Use `Id::from` when a
78	/// typed integer value is needed.
79	#[inline]
80	#[must_use]
81	pub fn shortroomid(self) -> [u8; INT_LEN] {
82		match self {
83			| Self::Normal(raw) => raw[0..INT_LEN]
84				.try_into()
85				.expect("normal raw shortroomid array from slice"),
86			| Self::Backfilled(raw) => raw[0..INT_LEN]
87				.try_into()
88				.expect("backfilled raw shortroomid array from slice"),
89		}
90	}
91
92	/// Returns the encoded timeline count.
93	///
94	/// The bytes remain in big-endian database order. For backfilled keys the
95	/// intervening zero marker is omitted.
96	#[inline]
97	#[must_use]
98	pub fn count(self) -> [u8; INT_LEN] {
99		match self {
100			| Self::Normal(raw) => raw[INT_LEN..INT_LEN * 2]
101				.try_into()
102				.expect("normal raw indice array from slice"),
103			| Self::Backfilled(raw) => raw[INT_LEN * 2..INT_LEN * 3]
104				.try_into()
105				.expect("backfilled raw indice array from slice"),
106		}
107	}
108
109	/// Borrows the complete packed database key.
110	///
111	/// The returned slice is 16 bytes for a normal key and 24 bytes for a
112	/// backfilled key. Its lifetime is tied to this value.
113	#[inline]
114	#[must_use]
115	pub fn as_bytes(&self) -> &[u8] {
116		match self {
117			| Self::Normal(raw) => raw,
118			| Self::Backfilled(raw) => raw,
119		}
120	}
121}
122
123impl fmt::Debug for RawId {
124	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125		let id: Id = (*self).into();
126		write!(f, "{id:?}")
127	}
128}
129
130impl<'de> Deserialize<'de> for RawId {
131	#[inline]
132	fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
133		d.deserialize_bytes(RawIdVisitor)
134	}
135}
136
137impl serde::de::Visitor<'_> for RawIdVisitor {
138	type Value = RawId;
139
140	fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141		f.write_str("RawId byte array")
142	}
143
144	#[inline]
145	fn visit_bytes<E>(self, buf: &[u8]) -> Result<RawId, E> { Ok(RawId::from(buf)) }
146}
147
148impl From<Id> for RawId {
149	#[inline]
150	fn from(id: Id) -> Self {
151		const MAX_LEN: usize = RawId::MAX_LEN;
152		type RawVec = ArrayVec<u8, MAX_LEN>;
153
154		let mut vec = RawVec::new();
155		vec.extend(id.shortroomid.to_be_bytes());
156		id.count.debug_assert_valid();
157		match id.count {
158			| Count::Normal(count) => {
159				vec.extend(count.to_be_bytes());
160				Self::Normal(
161					vec.as_ref()
162						.try_into()
163						.expect("RawVec into RawId::Normal"),
164				)
165			},
166			| Count::Backfilled(count) => {
167				vec.extend(0_u64.to_be_bytes());
168				vec.extend(count.to_be_bytes());
169				Self::Backfilled(
170					vec.as_ref()
171						.try_into()
172						.expect("RawVec into RawId::Backfilled"),
173				)
174			},
175		}
176	}
177}
178
179impl From<&[u8]> for RawId {
180	#[inline]
181	fn from(id: &[u8]) -> Self {
182		match id.len() {
183			| Self::NORMAL_LEN => Self::Normal(
184				id[0..Self::NORMAL_LEN]
185					.try_into()
186					.expect("normal RawId from [u8]"),
187			),
188			| Self::BACKFILLED_LEN => Self::Backfilled(
189				id[0..Self::BACKFILLED_LEN]
190					.try_into()
191					.expect("backfilled RawId from [u8]"),
192			),
193			| _ => unimplemented!("unrecognized RawId length"),
194		}
195	}
196}
197
198impl From<&[u8; Self::NORMAL_LEN]> for RawId {
199	#[inline]
200	fn from(id: &[u8; Self::NORMAL_LEN]) -> Self { Self::Normal(*id) }
201}
202
203impl From<&[u8; Self::BACKFILLED_LEN]> for RawId {
204	#[inline]
205	fn from(id: &[u8; Self::BACKFILLED_LEN]) -> Self { Self::Backfilled(*id) }
206}
207
208impl AsRef<[u8]> for RawId {
209	#[inline]
210	fn as_ref(&self) -> &[u8] { self.as_bytes() }
211}