wgtk/net/element.rs
1//! Definitions for elements contained in bundles (and so in packets).
2
3use std::io::{self, Read, Write};
4
5use crate::util::io::*;
6
7
8pub mod login;
9pub mod reply;
10pub mod base;
11pub mod client;
12
13pub mod entity;
14
15
16/// A trait to be implemented on a structure that can be interpreted as
17/// bundle's elements. Elements are slices of data in a bundle of packets.
18/// If a bundle contains multiple elements they are written contiguously.
19///
20/// Note that elements doesn't need to specify their length because they
21/// could be used for replies to requests, if you want to use the element
22/// as a top element (which mean that it provides a way to know its length
23/// in the bundle), implement the [`TopElement`] trait and specify its type
24/// of length.
25///
26/// You must provide a configuration type that will be given to encode
27/// and decode functions.
28pub trait Element: Sized {
29
30 /// Type of the element's config that is being encoded and decoded.
31 type Config;
32
33 /// Encode the element with the given writer and the given configuration.
34 fn encode(&self, write: &mut impl Write, config: &Self::Config) -> io::Result<()>;
35
36 /// Decode the element from the given reader and the given configuration.
37 ///
38 /// The total length that is available in the reader is also given. **Note
39 /// that** the given length will be equal to zero if the element's length
40 /// is set to [`ElementLength::Unknown`] (relevant for top elements).
41 fn decode(read: &mut impl Read, len: usize, config: &Self::Config) -> io::Result<Self>;
42
43}
44
45/// A "top element" extends the behavior of a regular [`Element`] by providing
46/// a length that describes how to encode and decode the length of this element.
47/// Only top elements can be directly written and read from a bundle, non-top
48/// elements are however useful when embedded in other (top) elements, such as
49/// reply element.
50pub trait TopElement: Element {
51
52 /// The type of length that prefixes the element's content and describe
53 /// how much space is taken by the element.
54 const LEN: ElementLength;
55
56}
57
58/// This trait provides an easier implementation of [`Element`] with not config value as
59/// opposed to regular elements, therefore both traits cannot be implemented at the same
60/// time.
61pub trait SimpleElement: Sized {
62
63 /// Encode the element with the given writer.
64 fn encode(&self, write: &mut impl Write) -> io::Result<()>;
65
66 /// Decode the element from the given reader.
67 ///
68 /// The total length that is available in the reader is also given. **Note
69 /// that** the given length will be equal to zero if the element's length
70 /// is set to [`ElementLength::Unknown`] (relevant for top elements).
71 fn decode(read: &mut impl Read, len: usize) -> io::Result<Self>;
72
73}
74
75impl<E: SimpleElement> Element for E {
76
77 type Config = ();
78
79 #[inline]
80 fn encode(&self, write: &mut impl Write, _config: &Self::Config) -> io::Result<()> {
81 SimpleElement::encode(self, write)
82 }
83
84 #[inline]
85 fn decode(read: &mut impl Read, len: usize, _config: &Self::Config) -> io::Result<Self> {
86 SimpleElement::decode(read, len)
87 }
88
89}
90
91
92/// An alternative trait to both [`Element`] that automatically implements
93/// nothing for encode and provides the default value on decoding without
94/// actually reading.
95pub trait NoopElement: Default {}
96
97impl<E: NoopElement> SimpleElement for E {
98
99 fn encode(&self, _write: &mut impl Write) -> io::Result<()> {
100 Ok(())
101 }
102
103 fn decode(_read: &mut impl Read, _len: usize) -> io::Result<Self> {
104 Ok(Self::default())
105 }
106
107}
108
109
110/// The empty tuple is considered an empty element. This can sometime
111/// be useful for default generic types.
112impl NoopElement for () { }
113impl TopElement for () {
114 const LEN: ElementLength = ElementLength::Fixed(0);
115}
116
117
118/// Type of length used by a specific message codec.
119/// This describes how the length of an element should be encoded in the packet.
120#[derive(Copy, Clone, Eq, PartialEq, Debug)]
121pub enum ElementLength {
122 /// A fixed length element, the length is not written in the header.
123 Fixed(u32),
124 /// The length is encoded on 8 bits in the element's header.
125 Variable8,
126 /// The length is encoded on 16 bits in the element's header.
127 Variable16,
128 /// The length is encoded on 24 bits in the element's header.
129 Variable24,
130 /// The length is encoded on 32 bits in the element's header.
131 Variable32,
132 /// The real length of the element is queried dynamically with a callback,
133 /// given the element's identifier.
134 Callback(fn(id: u8) -> ElementLength),
135}
136
137impl ElementLength {
138
139 /// Read the length from a given reader.
140 pub fn read(mut self, mut reader: impl Read, id: u8) -> std::io::Result<u32> {
141
142 // If the length is a callback, get the real length from the message id.
143 if let Self::Callback(cb) = self {
144 self = cb(id);
145 }
146
147 match self {
148 Self::Fixed(len) => Ok(len),
149 Self::Variable8 => reader.read_u8().map(|n| n as u32),
150 Self::Variable16 => reader.read_u16().map(|n| n as u32),
151 Self::Variable24 => reader.read_u24().map(|n| n),
152 Self::Variable32 => reader.read_u32().map(|n| n),
153 Self::Callback(_) => panic!("cyclic callback")
154 }
155
156 }
157
158 /// Write the length to the given writer.
159 pub fn write(self, mut writer: impl Write, len: u32) -> std::io::Result<()> {
160
161 match self {
162 Self::Fixed(expected_len) => {
163 assert_eq!(expected_len, len);
164 Ok(())
165 }
166 Self::Variable8 => writer.write_u8(len as u8),
167 Self::Variable16 => writer.write_u16(len as u16),
168 Self::Variable24 => writer.write_u24(len),
169 Self::Variable32 => writer.write_u32(len),
170 Self::Callback(_) => Ok(())
171 }
172
173 }
174
175 /// Return the size in bytes of this type of length.
176 pub fn len(&self) -> usize {
177 match self {
178 Self::Fixed(_) => 0,
179 Self::Variable8 => 1,
180 Self::Variable16 => 2,
181 Self::Variable24 => 3,
182 Self::Variable32 => 4,
183 Self::Callback(_) => 0,
184 }
185 }
186
187}
188
189
190/// An utility structure for storing ranges of element's ids. It provides way
191/// of converting between **element id** (with optional **sub-id**) and
192/// **exposed id**.
193///
194/// This structure is small and therefore can be copied.
195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196pub struct ElementIdRange {
197 pub first: u8,
198 pub last: u8,
199}
200
201impl ElementIdRange {
202
203 pub const fn new(first: u8, last: u8) -> Self {
204 Self { first, last }
205 }
206
207 /// Returns the number of slots in this range.
208 #[inline]
209 pub const fn slots_count(self) -> u8 {
210 self.last - self.first + 1
211 }
212
213 /// Returns the number of slots that requires a sub-id. These slots are
214 /// starting from the end of the range. For example, if this function
215 /// returns 1, this means that the last slot (`.last`), if used, will be
216 /// followed by a sub-id.
217 ///
218 /// You must given the total number of exposed ids, because the presence
219 /// of sub-id depends on how exposed ids can fit in the id range.
220 #[inline]
221 pub const fn sub_slots_count(self, exposed_count: u16) -> u8 {
222 // Calculate the number of excess exposed ids, compared to slots count.
223 let excess_count = exposed_count as i32 - self.slots_count() as i32;
224 // If the are excess slots, calculate how much additional bytes are
225 // required to represent such number.
226 if excess_count > 0 {
227 (excess_count / 255 + 1) as u8
228 } else {
229 0
230 }
231 }
232
233 /// Returns the number of full slots that don't require a sub-id. This
234 /// is the opposite of `sub_slots_count`, read its documentation.
235 #[inline]
236 pub const fn full_slots_count(self, exposed_count: u16) -> u8 {
237 self.slots_count() - self.sub_slots_count(exposed_count)
238 }
239
240 /// Get the element's id and optional sub-id from the given exposed id
241 /// and total count of exposed ids.
242 pub fn from_exposed_id(self, exposed_count: u16, exposed_id: u16) -> (u8, Option<u8>) {
243
244 let full_slots = self.full_slots_count(exposed_count);
245
246 if exposed_id < full_slots as u16 {
247 // If the exposed id fits in the full slots.
248 (self.first + exposed_id as u8, None)
249 } else {
250 // If the given exposed id require to be put in a sub-slot.
251 // First we get how much offset the given exposed id is from the first
252 // sub slot (full_slots represent the first sub slot).
253 let overflow = exposed_id - full_slots as u16;
254 let first_sub_slot = self.first + full_slots;
255 // Casts are safe.
256 ((first_sub_slot as u16 + overflow / 256) as u8, Some((overflow % 256) as u8))
257 }
258
259 }
260
261 /// Get the exposed id from an element, but only return some exposed id if
262 /// it fits into
263 pub fn to_exposed_id_checked(self, exposed_count: u16, element_id: u8) -> Option<u16> {
264 let raw_exposed_id = element_id - self.first;
265 (raw_exposed_id < self.full_slots_count(exposed_count)).then_some(raw_exposed_id as u16)
266 }
267
268 /// Get the exposed id from an element id and optionally a sub-id, which
269 /// should be lazily provided with a closure.
270 pub fn to_exposed_id(self, exposed_count: u16, element_id: u8, sub_id_getter: impl FnOnce() -> u8) -> u16 {
271
272 // This is the raw exposed id, it will be used, with full_slots to determine
273 // if a sub-id is needed.
274 let exposed_id = element_id - self.first;
275 let full_slots = self.full_slots_count(exposed_count);
276
277 if exposed_id < full_slots {
278 exposed_id as u16
279 } else {
280 // Calculate of the sub-slot offset within sub-slots.
281 let offset_id = exposed_id - full_slots;
282 let sub_id = sub_id_getter();
283 // Calculate the final exposed id from the sub-id and offset.
284 full_slots as u16 + 256 * offset_id as u16 + sub_id as u16
285 }
286
287 }
288
289}