1#[cfg(feature = "dev")]
2use arbitrary::Arbitrary;
3use compact_u64::{CompactU64, Tag, TagWidth};
4use ufotofu_codec::{
5 Blame, Decodable, DecodeError, Encodable, RelativeDecodable, RelativeEncodable,
6};
7use willow_encoding::is_bitflagged;
8
9use crate::{
10 grouping::{Area, AreaSubspace, Range, RangeEnd},
11 Entry, NamespaceId, Path, PayloadDigest, PrivatePathContext, SubspaceId,
12};
13
14#[derive(Debug, Clone)]
15pub struct PrivateInterest<
18 const MCL: usize,
19 const MCC: usize,
20 const MPL: usize,
21 N: NamespaceId,
22 S: SubspaceId,
23> {
24 namespace_id: N,
25 subspace_id: AreaSubspace<S>,
26 path: Path<MCL, MCC, MPL>,
27}
28
29impl<const MCL: usize, const MCC: usize, const MPL: usize, N: NamespaceId, S: SubspaceId>
30 PrivateInterest<MCL, MCC, MPL, N, S>
31{
32 pub fn namespace_id(&self) -> &N {
33 &self.namespace_id
34 }
35
36 pub fn subspace_id(&self) -> &AreaSubspace<S> {
37 &self.subspace_id
38 }
39
40 pub fn path(&self) -> &Path<MCL, MCC, MPL> {
41 &self.path
42 }
43
44 pub fn is_more_specific(&self, other: &Self) -> bool {
45 self.namespace_id == other.namespace_id
46 && other.subspace_id.includes_area_subspace(&self.subspace_id)
47 && self.path.is_prefixed_by(&other.path)
48 }
49
50 pub fn is_less_specific(&self, other: &Self) -> bool {
51 other.is_more_specific(self)
52 }
53
54 pub fn is_comparable(&self, other: &Self) -> bool {
55 self.is_more_specific(other) || other.is_more_specific(other)
56 }
57
58 pub fn includes_entry<PD: PayloadDigest>(
59 &self,
60 entry: &Entry<MCL, MCC, MPL, N, S, PD>,
61 ) -> bool {
62 &self.namespace_id == entry.namespace_id()
63 && self.subspace_id.includes(entry.subspace_id())
64 && self.path.is_prefix_of(entry.path())
65 }
66
67 pub fn is_disjoint(&self, other: &Self) -> bool {
68 if self.namespace_id != other.namespace_id {
71 return true;
72 }
73
74 if !self.subspace_id.includes_area_subspace(&other.subspace_id)
75 && !self.subspace_id.includes_area_subspace(&other.subspace_id)
76 {
77 return true;
78 }
79
80 if !self.path.is_related(&other.path) {
81 return true;
82 }
83
84 false
85 }
86
87 pub fn awkward(&self, other: &Self) -> bool {
88 !self.is_comparable(other) && !self.is_disjoint(other)
90 }
91
92 pub fn includes_area(&self, area: &Area<MCL, MCC, MPL, S>) -> bool {
93 self.subspace_id.includes_area_subspace(area.subspace())
94 && self.path.is_prefix_of(area.path())
95 }
96
97 pub fn is_related_to_area(&self, area: &Area<MCL, MCC, MPL, S>) -> bool {
98 self.subspace_id.includes_area_subspace(area.subspace())
99 && self.path.is_related(area.path())
100 }
101
102 pub fn almost_includes_area(&self, area: &Area<MCL, MCC, MPL, S>) -> bool {
103 let subspace_is_fine = match (self.subspace_id(), area.subspace()) {
104 (AreaSubspace::Id(self_id), AreaSubspace::Id(other_id)) => self_id == other_id,
105 _ => true,
106 };
107
108 subspace_is_fine && self.path.is_related(area.path())
109 }
110}
111
112#[cfg(feature = "dev")]
113impl<'a, const MCL: usize, const MCC: usize, const MPL: usize, N, S> Arbitrary<'a>
114 for PrivateInterest<MCL, MCC, MPL, N, S>
115where
116 N: NamespaceId + Arbitrary<'a>,
117 S: SubspaceId + Arbitrary<'a>,
118{
119 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
120 let namespace_id: N = Arbitrary::arbitrary(u)?;
121 let subspace_id: AreaSubspace<S> = Arbitrary::arbitrary(u)?;
122 let path: Path<MCL, MCC, MPL> = Arbitrary::arbitrary(u)?;
123
124 Ok(Self {
125 namespace_id,
126 subspace_id,
127 path,
128 })
129 }
130}
131
132#[derive(Debug)]
133pub struct PrivateAreaContext<
135 const MCL: usize,
136 const MCC: usize,
137 const MPL: usize,
138 N: NamespaceId,
139 S: SubspaceId,
140> {
141 private: PrivateInterest<MCL, MCC, MPL, N, S>,
143
144 rel: Area<MCL, MCC, MPL, S>,
146}
147
148#[derive(Debug)]
149pub struct AreaNotAlmostIncludedError;
150
151impl<const MCL: usize, const MCC: usize, const MPL: usize, N: NamespaceId, S: SubspaceId>
152 PrivateAreaContext<MCL, MCC, MPL, N, S>
153{
154 pub fn new(
155 private: PrivateInterest<MCL, MCC, MPL, N, S>,
156 rel: Area<MCL, MCC, MPL, S>,
157 ) -> Result<Self, AreaNotAlmostIncludedError> {
158 if !private.path.is_prefix_of(rel.path()) {
159 }
161
162 Ok(Self { private, rel })
163 }
164
165 pub fn private(&self) -> &PrivateInterest<MCL, MCC, MPL, N, S> {
166 &self.private
167 }
168
169 pub fn rel(&self) -> &Area<MCL, MCC, MPL, S> {
170 &self.rel
171 }
172}
173
174#[cfg(feature = "dev")]
175impl<'a, const MCL: usize, const MCC: usize, const MPL: usize, N: NamespaceId, S: SubspaceId>
176 Arbitrary<'a> for PrivateAreaContext<MCL, MCC, MPL, N, S>
177where
178 N: NamespaceId + Arbitrary<'a>,
179 S: SubspaceId + Arbitrary<'a>,
180{
181 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
182 Ok(Self {
183 private: Arbitrary::arbitrary(u)?,
184 rel: Arbitrary::arbitrary(u)?,
185 })
186 }
187}
188
189impl<const MCL: usize, const MCC: usize, const MPL: usize, N: NamespaceId, S: SubspaceId>
190 RelativeEncodable<PrivateAreaContext<MCL, MCC, MPL, N, S>> for Area<MCL, MCC, MPL, S>
191where
192 S: Encodable,
193{
194 async fn relative_encode<C>(
195 &self,
196 consumer: &mut C,
197 r: &PrivateAreaContext<MCL, MCC, MPL, N, S>,
198 ) -> Result<(), C::Error>
199 where
200 C: ufotofu::BulkConsumer<Item = u8>,
201 {
202 if !r.rel.almost_includes_area(self) || !r.private.almost_includes_area(self) {
203 panic!(
204 "Tried to encode an Area relative to a PrivateAreaContext it is not almost included by."
205 )
206 }
207
208 let (start_from_start, end_from_start) = match (r.rel.times().end, self.times().end) {
209 (RangeEnd::Open, RangeEnd::Open) => (true, false),
210 (RangeEnd::Open, RangeEnd::Closed(_)) => (true, true),
211 (RangeEnd::Closed(rel_end), RangeEnd::Open) => {
212 let start_from_start =
213 self.times().start - r.rel().times().start <= rel_end - self.times().start;
214
215 (start_from_start, false)
216 }
217 (RangeEnd::Closed(rel_end), RangeEnd::Closed(val_end)) => {
218 let start_from_start =
219 self.times().start - r.rel().times().start <= rel_end - self.times().start;
220
221 let end_from_start = val_end - r.rel().times().start <= rel_end - val_end;
222
223 (start_from_start, end_from_start)
224 }
225 };
226
227 let mut header = 0b0000_0000;
228
229 if self.subspace() != r.rel().subspace() {
230 header |= 0b1000_0000;
231 }
232
233 if self.subspace() == &AreaSubspace::Any {
234 header |= 0b0100_0000
235 }
236
237 if start_from_start {
238 header |= 0b0010_0000
239 }
240
241 if end_from_start {
242 header |= 0b0001_0000
243 }
244
245 let start_diff = match (r.rel.times().end, self.times().end) {
246 (RangeEnd::Open, RangeEnd::Open) => self.times().start - r.rel.times().start,
247 (RangeEnd::Open, RangeEnd::Closed(_)) => self.times().start - r.rel.times().start,
248 (RangeEnd::Closed(rel_end), RangeEnd::Open) => {
249 let start_from_start_diff = self.times().start - r.rel().times().start;
250
251 let start_from_end_diff = rel_end - self.times().start;
252
253 if start_from_start_diff <= start_from_end_diff {
254 start_from_start_diff
255 } else {
256 start_from_end_diff
257 }
258 }
259 (RangeEnd::Closed(rel_end), RangeEnd::Closed(_)) => {
260 let start_from_start_diff = self.times().start - r.rel().times().start;
261 let start_from_end_diff = rel_end - self.times().start;
262
263 if start_from_start_diff <= start_from_end_diff {
264 start_from_start_diff
265 } else {
266 start_from_end_diff
267 }
268 }
269 };
270
271 let start_tag = Tag::min_tag(start_diff, TagWidth::two());
272 header |= start_tag.data_at_offset(4);
273
274 let end_diff = match (r.rel.times().end, self.times().end) {
275 (RangeEnd::Open, RangeEnd::Open) | (RangeEnd::Closed(_), RangeEnd::Open) => None,
276 (RangeEnd::Open, RangeEnd::Closed(val_end)) => {
277 let diff = val_end - r.rel.times().start;
278
279 let tag = Tag::min_tag(diff, TagWidth::two());
280 header |= tag.data_at_offset(6);
281 Some(diff)
282 }
283 (RangeEnd::Closed(rel_end), RangeEnd::Closed(val_end)) => {
284 let end_from_start = val_end - r.rel().times().start <= rel_end - val_end;
285 let diff = if end_from_start {
288 val_end - r.rel.times().start
289 } else {
290 rel_end - val_end
291 };
292
293 let tag = Tag::min_tag(diff, TagWidth::two());
294 header |= tag.data_at_offset(6);
295 Some(diff)
296 }
297 };
298
299 consumer.consume(header).await?;
300
301 if let AreaSubspace::Id(id) = self.subspace() {
302 if r.rel().subspace() != id {
303 id.encode(consumer).await?;
304 }
305 }
306
307 CompactU64(start_diff)
308 .relative_encode(consumer, &start_tag.encoding_width())
309 .await?;
310
311 if let Some(end_diff) = end_diff {
312 let end_from_start_tag = Tag::min_tag(end_diff, TagWidth::two());
313 CompactU64(end_diff)
314 .relative_encode(consumer, &end_from_start_tag.encoding_width())
315 .await?;
316 }
317
318 let private_path_context =
320 PrivatePathContext::new_unchecked(r.private().path().clone(), r.rel().path().clone());
321
322 self.path()
323 .relative_encode(consumer, &private_path_context)
324 .await?;
325
326 Ok(())
327 }
328}
329
330impl<const MCL: usize, const MCC: usize, const MPL: usize, N: NamespaceId, S: SubspaceId>
331 RelativeDecodable<PrivateAreaContext<MCL, MCC, MPL, N, S>, Blame> for Area<MCL, MCC, MPL, S>
332where
333 S: Decodable,
334 Blame: From<S::ErrorReason>,
335{
336 async fn relative_decode<P>(
337 producer: &mut P,
338 r: &PrivateAreaContext<MCL, MCC, MPL, N, S>,
339 ) -> Result<Self, ufotofu_codec::DecodeError<P::Final, P::Error, Blame>>
340 where
341 P: ufotofu::BulkProducer<Item = u8>,
342 Self: Sized,
343 {
344 let header = producer.produce_item().await?;
345
346 let is_subspace_not_equal = is_bitflagged(header, 0);
347 let is_subspace_any = is_bitflagged(header, 1);
348 let is_start_from_start = is_bitflagged(header, 2);
349 let is_end_from_start = is_bitflagged(header, 3);
350 let start_diff_tag = Tag::from_raw(header, TagWidth::two(), 4);
351 let end_diff_tag = Tag::from_raw(header, TagWidth::two(), 6);
352
353 let subspace = if !is_subspace_any && is_subspace_not_equal {
354 let id = S::decode(producer)
355 .await
356 .map_err(DecodeError::map_other_from)?;
357
358 AreaSubspace::Id(id)
359 } else if is_subspace_any {
360 AreaSubspace::Any
361 } else {
362 r.rel().subspace().clone()
363 };
364
365 let start_diff = CompactU64::relative_decode(producer, &start_diff_tag)
366 .await
367 .map_err(DecodeError::map_other_from)?;
368
369 let time_start = if is_start_from_start {
370 start_diff
371 .0
372 .checked_add(r.rel().times().start)
373 .ok_or(DecodeError::Other(Blame::TheirFault))?
374 } else {
375 match r.rel().times().end {
376 RangeEnd::Closed(end) => end
377 .checked_sub(start_diff.0)
378 .ok_or(DecodeError::Other(Blame::TheirFault))?,
379 RangeEnd::Open => {
380 return Err(DecodeError::Other(Blame::TheirFault));
381 }
382 }
383 };
384
385 let is_time_end_open = match r.rel.times().end {
386 RangeEnd::Closed(_) => false,
387 RangeEnd::Open => !is_end_from_start,
388 };
389
390 let time_end = if is_time_end_open {
391 RangeEnd::Open
392 } else {
393 let end_diff = CompactU64::relative_decode(producer, &end_diff_tag)
394 .await
395 .map_err(DecodeError::map_other_from)?;
396
397 let end = if is_end_from_start {
398 end_diff
399 .0
400 .checked_add(r.rel.times().start)
401 .ok_or(DecodeError::Other(Blame::TheirFault))?
402 } else {
403 match r.rel.times().end {
404 RangeEnd::Closed(end) => end
405 .checked_sub(end_diff.0)
406 .ok_or(DecodeError::Other(Blame::TheirFault))?,
407 RangeEnd::Open => {
408 return Err(DecodeError::Other(Blame::TheirFault));
409 }
410 }
411 };
412
413 RangeEnd::Closed(end)
414 };
415
416 let private_path_context =
418 PrivatePathContext::new_unchecked(r.private().path().clone(), r.rel().path().clone());
419
420 let path = Path::relative_decode(producer, &private_path_context).await?;
421
422 if time_start > time_end {
423 return Err(DecodeError::Other(Blame::TheirFault));
424 }
425
426 let times = Range::new(time_start, time_end);
427
428 Ok(Area::new(subspace, path, times))
429 }
430}