uqa_storage/notifications/publication/
subscription.rs1use super::{
10 hex_digit, invalid, write_number, BudgetedVec, Cursor, NotificationListenerRow,
11 StorageReadControl, VersionResult, MAX_NOTIFICATION_CHANNEL_BYTES,
12};
13
14#[derive(Clone, Copy)]
15pub(super) struct Subscription {
16 owner_id: [u8; 16],
17 session_id: u64,
18 wake_port: u16,
19 next_sequence: u64,
20 position: u64,
21 channels_offset: usize,
22 channel_count: u64,
23}
24
25impl Subscription {
26 pub(super) fn view(self, bytes: &[u8]) -> NotificationSubscriptionView<'_> {
27 NotificationSubscriptionView {
28 owner_id: self.owner_id,
29 session_id: self.session_id,
30 wake_port: self.wake_port,
31 next_sequence: self.next_sequence,
32 position: self.position,
33 channel_bytes: &bytes[self.channels_offset..],
34 channel_count: self.channel_count,
35 }
36 }
37}
38
39#[derive(Clone, Copy)]
41pub struct NotificationSubscriptionView<'a> {
42 pub owner_id: [u8; 16],
43 pub session_id: u64,
44 pub wake_port: u16,
45 pub next_sequence: u64,
46 pub position: u64,
47 channel_bytes: &'a [u8],
48 channel_count: u64,
49}
50
51impl<'a> NotificationSubscriptionView<'a> {
52 pub const fn is_unlisten(&self) -> bool {
53 self.channel_count == 0
54 }
55
56 pub fn channels_json(&self, control: &StorageReadControl) -> VersionResult<BudgetedVec<u8>> {
58 struct Channels<'a>(NotificationSubscriptionView<'a>);
59 impl serde::Serialize for Channels<'_> {
60 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
61 use serde::ser::SerializeSeq;
62 let mut sequence = serializer.serialize_seq(None)?;
63 for channel in self.0.channels() {
64 sequence.serialize_element(channel.map_err(serde::ser::Error::custom)?)?;
65 }
66 sequence.end()
67 }
68 }
69 crate::key_value::record_json::encode(&Channels(*self), control)
70 }
71
72 pub fn channels(&self) -> impl Iterator<Item = VersionResult<&'a str>> + 'a {
73 let mut cursor = Cursor {
74 rest: self.channel_bytes,
75 };
76 let mut remaining = self.channel_count;
77 std::iter::from_fn(move || {
78 if remaining == 0 {
79 return None;
80 }
81 remaining -= 1;
82 Some(cursor.text(MAX_NOTIFICATION_CHANNEL_BYTES))
83 })
84 }
85}
86
87pub(super) fn encode(
88 bytes: &mut BudgetedVec<u8>,
89 listener: Option<&NotificationListenerRow>,
90 process_id: i32,
91 control: &StorageReadControl,
92) -> VersionResult<()> {
93 let Some(listener) = listener else {
94 bytes.extend_from_slice(b"\n0\n")?;
95 return Ok(());
96 };
97 if listener.owner_id == [0; 16]
98 || listener.process_id != process_id
99 || listener.wake_port == 0
100 || listener.transaction_open
101 || i64::try_from(listener.next_sequence).is_err()
102 || i64::try_from(listener.position).is_err()
103 {
104 return Err(invalid());
105 }
106 bytes.extend_from_slice(b"\n1\n")?;
107 for byte in listener.owner_id {
108 bytes.push(b"0123456789abcdef"[usize::from(byte >> 4)])?;
109 bytes.push(b"0123456789abcdef"[usize::from(byte & 15)])?;
110 }
111 bytes.push(b'\n')?;
112 for value in [
113 listener.session_id,
114 u64::from(listener.wake_port),
115 listener.next_sequence,
116 listener.position,
117 u64::try_from(listener.channels.len()).map_err(|_| invalid())?,
118 ] {
119 write_number(bytes, value, b'\n')?;
120 }
121 for channel in &listener.channels {
122 control.cancellation().check()?;
123 if channel.is_empty() || channel.len() >= MAX_NOTIFICATION_CHANNEL_BYTES {
124 return Err(invalid());
125 }
126 write_number(
127 bytes,
128 u64::try_from(channel.len()).map_err(|_| invalid())?,
129 b':',
130 )?;
131 bytes.extend_from_slice(channel.as_bytes())?;
132 }
133 Ok(())
134}
135
136pub(super) fn decode(
137 cursor: &mut Cursor<'_>,
138 record_len: usize,
139 control: &StorageReadControl,
140) -> VersionResult<Option<Subscription>> {
141 if cursor.take(1)? != b"\n" {
142 return Err(invalid());
143 }
144 match cursor.number(b'\n')? {
145 0 => return Ok(None),
146 1 => {}
147 _ => return Err(invalid()),
148 }
149 let mut owner_id = [0; 16];
150 for byte in &mut owner_id {
151 let hex = cursor.take(2)?;
152 *byte = (hex_digit(hex[0])? << 4) | hex_digit(hex[1])?;
153 }
154 if cursor.take(1)? != b"\n" || owner_id == [0; 16] {
155 return Err(invalid());
156 }
157 let session_id = cursor.number(b'\n')?;
158 let wake_port = u16::try_from(cursor.number(b'\n')?).map_err(|_| invalid())?;
159 let next_sequence = cursor.number(b'\n')?;
160 let position = cursor.number(b'\n')?;
161 let channel_count = cursor.number(b'\n')?;
162 if wake_port == 0
163 || i64::try_from(next_sequence).is_err()
164 || i64::try_from(position).is_err()
165 || channel_count > u64::try_from(cursor.rest.len() / 3).map_err(|_| invalid())?
166 {
167 return Err(invalid());
168 }
169 let channels_offset = record_len - cursor.rest.len();
170 for _ in 0..channel_count {
171 control.cancellation().check()?;
172 if cursor.text(MAX_NOTIFICATION_CHANNEL_BYTES)?.is_empty() {
173 return Err(invalid());
174 }
175 }
176 Ok(Some(Subscription {
177 owner_id,
178 session_id,
179 wake_port,
180 next_sequence,
181 position,
182 channels_offset,
183 channel_count,
184 }))
185}