dope_core/driver/settings/
mod.rs1use std::{num, process};
2
3use crate::{
4 driver::route::table,
5 io::{datagram, socket::option, transfer},
6};
7
8mod config;
9
10pub use config::Config;
11
12const MIN_SUBMISSION_CAPACITY: u32 = (option::MAX_STREAM_OPTIONS + 1) as u32;
13const MAX_SUBMISSION_CAPACITY: u32 = 32_768;
14const MAX_COMPLETION_CAPACITY: u32 = 65_536;
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18#[repr(u8)]
19pub enum CompletionProgress {
20 Prompt,
22 BatchedWhenSupported,
24}
25
26const _: () = {
27 assert!(std::mem::size_of::<CompletionProgress>() == std::mem::size_of::<bool>());
28 assert!(std::mem::align_of::<CompletionProgress>() == std::mem::align_of::<bool>());
29};
30
31pub trait Profile: 'static {
47 const QUEUES: QueueLayout;
48 const SCHEDULER: SchedulerLayout = SchedulerLayout::DEFAULT;
49 const COMPLETION_PROGRESS: CompletionProgress = CompletionProgress::BatchedWhenSupported;
50
51 const MAX_ACCEPT_SLOTS: u32 = 65_279;
53 const OUTBOUND_SLOTS: u32 = 256;
55 const RECEIVE: Receive = Receive::DEFAULT;
56}
57
58#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59#[repr(transparent)]
60struct SubmissionCapacity(u32);
61
62impl SubmissionCapacity {
63 const fn new(entries: u32) -> Option<Self> {
64 if entries == 0 {
65 return None;
66 }
67 if entries < MIN_SUBMISSION_CAPACITY
68 || !entries.is_power_of_two()
69 || entries > MAX_SUBMISSION_CAPACITY
70 {
71 return None;
72 }
73 Some(Self(entries))
74 }
75
76 const fn get(self) -> u32 {
77 self.0
78 }
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq)]
82pub struct QueueLayout {
83 submissions: SubmissionCapacity,
84 completions: u32,
85}
86
87struct FixedQueueLayout<const SUBMISSIONS: u32, const COMPLETIONS: u32>;
88
89impl<const SUBMISSIONS: u32, const COMPLETIONS: u32> FixedQueueLayout<SUBMISSIONS, COMPLETIONS> {
90 const VALUE: QueueLayout = {
91 assert!(SUBMISSIONS >= MIN_SUBMISSION_CAPACITY);
92 assert!(SUBMISSIONS.is_power_of_two());
93 assert!(SUBMISSIONS <= MAX_SUBMISSION_CAPACITY);
94 assert!(COMPLETIONS.is_power_of_two());
95 assert!(COMPLETIONS <= MAX_COMPLETION_CAPACITY);
96 assert!(COMPLETIONS >= SUBMISSIONS);
97 QueueLayout {
98 submissions: SubmissionCapacity(SUBMISSIONS),
99 completions: COMPLETIONS,
100 }
101 };
102}
103
104impl QueueLayout {
105 pub const MAX: Self = Self::fixed::<MAX_SUBMISSION_CAPACITY, MAX_COMPLETION_CAPACITY>();
106
107 pub const fn fixed<const SUBMISSIONS: u32, const COMPLETIONS: u32>() -> Self {
115 FixedQueueLayout::<SUBMISSIONS, COMPLETIONS>::VALUE
116 }
117
118 #[must_use]
121 pub const fn new(submissions: u32, completions: u32) -> Option<Self> {
122 let Some(submissions) = SubmissionCapacity::new(submissions) else {
123 return None;
124 };
125 if completions == 0 {
126 return None;
127 }
128 if !completions.is_power_of_two()
129 || completions > MAX_COMPLETION_CAPACITY
130 || completions < submissions.get()
131 {
132 return None;
133 }
134 Some(Self {
135 submissions,
136 completions,
137 })
138 }
139
140 #[must_use]
141 pub const fn submissions(self) -> u32 {
142 self.submissions.get()
143 }
144
145 #[must_use]
146 pub const fn completions(self) -> u32 {
147 self.completions
148 }
149}
150
151const _: () = {
152 assert!(QueueLayout::new(0, 8).is_none());
153 assert!(QueueLayout::new(MIN_SUBMISSION_CAPACITY / 2, 8).is_none());
154 assert!(QueueLayout::new(64, 32).is_none());
155 assert!(QueueLayout::MAX.submissions() == MAX_SUBMISSION_CAPACITY);
156 assert!(QueueLayout::MAX.completions() == MAX_COMPLETION_CAPACITY);
157 assert!(std::mem::size_of::<QueueLayout>() == std::mem::size_of::<[u32; 2]>());
158};
159
160#[derive(Clone, Copy, Debug, PartialEq, Eq)]
162#[repr(transparent)]
163pub struct ScheduleCapacity(u32);
164
165impl ScheduleCapacity {
166 pub const ZERO: Self = Self(0);
167 pub const MAX: Self = Self(u32::MAX);
168
169 pub const fn fixed<const SLOTS: u32>() -> Self {
171 Self(SLOTS)
172 }
173
174 #[must_use]
175 pub const fn new(slots: usize) -> Option<Self> {
176 if slots <= u32::MAX as usize {
177 Some(Self(slots as u32))
178 } else {
179 None
180 }
181 }
182
183 #[must_use]
184 pub const fn get(self) -> usize {
185 self.0 as usize
186 }
187}
188
189#[derive(Clone, Copy, Debug, PartialEq, Eq)]
192pub struct SchedulerLayout {
193 ready: ScheduleCapacity,
194 timer_cache_limit: ScheduleCapacity,
195}
196
197impl SchedulerLayout {
198 pub const DEFAULT: Self = Self::fixed::<1024, 1024>();
199
200 pub const fn fixed<const READY: u32, const TIMER_CACHE: u32>() -> Self {
201 Self {
202 ready: ScheduleCapacity::fixed::<READY>(),
203 timer_cache_limit: ScheduleCapacity::fixed::<TIMER_CACHE>(),
204 }
205 }
206
207 #[must_use]
208 pub const fn new(ready: usize, timer_cache_limit: usize) -> Option<Self> {
209 let Some(ready) = ScheduleCapacity::new(ready) else {
210 return None;
211 };
212 let Some(timer_cache_limit) = ScheduleCapacity::new(timer_cache_limit) else {
213 return None;
214 };
215 Some(Self {
216 ready,
217 timer_cache_limit,
218 })
219 }
220
221 #[must_use]
222 pub const fn ready(self) -> ScheduleCapacity {
223 self.ready
224 }
225
226 #[must_use]
227 pub const fn timer_cache_limit(self) -> ScheduleCapacity {
228 self.timer_cache_limit
229 }
230
231 #[must_use]
232 pub const fn with_ready(mut self, ready: ScheduleCapacity) -> Self {
233 self.ready = ready;
234 self
235 }
236
237 #[must_use]
238 pub const fn with_timer_cache_limit(mut self, timer_cache_limit: ScheduleCapacity) -> Self {
239 self.timer_cache_limit = timer_cache_limit;
240 self
241 }
242}
243
244const _: () = {
245 assert!(ScheduleCapacity::new(0).is_some());
246 assert!(ScheduleCapacity::MAX.get() == u32::MAX as usize);
247 if let Some(overflow) = (u32::MAX as usize).checked_add(1) {
248 assert!(ScheduleCapacity::new(overflow).is_none());
249 }
250 assert!(std::mem::size_of::<ScheduleCapacity>() == std::mem::size_of::<u32>());
251 assert!(std::mem::size_of::<SchedulerLayout>() == std::mem::size_of::<[u32; 2]>());
252};
253
254#[derive(Clone, Copy, Debug, PartialEq, Eq)]
255pub struct Receive {
256 entries: u16,
257 len: u32,
258}
259
260struct FixedReceive<const ENTRIES: u16, const LEN: u32>;
261
262impl<const ENTRIES: u16, const LEN: u32> FixedReceive<ENTRIES, LEN> {
263 const VALUE: Receive = {
264 assert!(ENTRIES >= MIN_RECEIVE_ENTRIES);
265 assert!(ENTRIES.is_power_of_two());
266 assert!(LEN != 0);
267 assert!(LEN <= transfer::MAX_BYTES as u32);
268 assert!((ENTRIES as usize) <= (isize::MAX as usize) / (LEN as usize));
269 Receive {
270 entries: ENTRIES,
271 len: LEN,
272 }
273 };
274}
275
276const MIN_RECEIVE_ENTRIES: u16 = 2;
277
278const _: () = {
279 assert!(std::mem::size_of::<Receive>() == std::mem::size_of::<u64>());
280};
281
282impl Receive {
283 pub const DEFAULT: Self = Self::fixed::<4096, 4096>();
284 pub const MIN_DATAGRAM_BUFFER_LEN: u32 = datagram::SlotLen::MIN_BYTES;
285
286 pub const fn fixed<const ENTRIES: u16, const LEN: u32>() -> Self {
294 FixedReceive::<ENTRIES, LEN>::VALUE
295 }
296
297 #[must_use]
300 pub const fn new(entries: u16, len: u32) -> Option<Self> {
301 if len == 0 {
302 return None;
303 }
304 if len > transfer::MAX_BYTES as u32 {
305 return None;
306 }
307 Self::with_len(entries, len)
308 }
309
310 #[must_use]
312 pub const fn for_datagram_payload(entries: u16, payload_len: u32) -> Option<Self> {
313 let Some(len) = datagram::SlotLen::for_payload(payload_len) else {
314 return None;
315 };
316 Self::with_len(entries, len.nonzero().get())
317 }
318
319 const fn with_len(entries: u16, len: u32) -> Option<Self> {
320 if entries < MIN_RECEIVE_ENTRIES || !entries.is_power_of_two() {
321 return None;
322 }
323 let Some(bytes) = (entries as usize).checked_mul(len as usize) else {
324 return None;
325 };
326 if bytes > isize::MAX as usize {
327 return None;
328 }
329 Some(Self { entries, len })
330 }
331
332 #[must_use]
333 pub const fn entries(self) -> u16 {
334 self.entries
335 }
336
337 #[must_use]
338 pub const fn buffer_len(self) -> usize {
339 self.len as usize
340 }
341
342 pub(crate) fn nonzero_buffer_len(self) -> num::NonZeroU32 {
343 let Some(len) = num::NonZeroU32::new(self.len) else {
344 process::abort();
345 };
346 len
347 }
348
349 pub(crate) const fn backing_bytes(self) -> usize {
350 self.entries as usize * self.len as usize
351 }
352}
353
354#[derive(Clone, Copy, Debug, PartialEq, Eq)]
356pub struct FileSlots {
357 accept_end: table::Capacity,
358 table_end: table::Capacity,
359}
360
361struct FixedFileSlots<const ACCEPT: u32, const OUTBOUND: u32>;
362
363impl<const ACCEPT: u32, const OUTBOUND: u32> FixedFileSlots<ACCEPT, OUTBOUND> {
364 const VALUE: FileSlots = {
365 assert!(ACCEPT <= u32::MAX - OUTBOUND);
366 let total = ACCEPT + OUTBOUND;
367 assert!(total != 0);
368 FileSlots {
369 accept_end: table::Capacity::fixed(ACCEPT),
370 table_end: table::Capacity::fixed(total),
371 }
372 };
373}
374
375const _: () =
376 assert!(std::mem::size_of::<FileSlots>() == std::mem::size_of::<[table::Capacity; 2]>());
377
378impl FileSlots {
379 pub const fn fixed<const ACCEPT: u32, const OUTBOUND: u32>() -> Self {
387 FixedFileSlots::<ACCEPT, OUTBOUND>::VALUE
388 }
389
390 #[must_use]
392 pub const fn new(accept: u32, outbound: u32) -> Option<Self> {
393 let Some(total) = accept.checked_add(outbound) else {
394 return None;
395 };
396 if total == 0 {
397 return None;
398 }
399 let Some(accept_end) = table::Capacity::new(accept as usize) else {
400 return None;
401 };
402 let Some(table_end) = table::Capacity::new(total as usize) else {
403 return None;
404 };
405 Some(Self {
406 accept_end,
407 table_end,
408 })
409 }
410
411 #[must_use]
412 pub const fn capacity(self) -> u32 {
413 self.table_end.raw()
414 }
415
416 #[must_use]
417 pub const fn accept(self) -> u32 {
418 self.accept_end.raw()
419 }
420
421 #[must_use]
422 pub const fn outbound(self) -> u32 {
423 self.table_end.raw() - self.accept_end.raw()
424 }
425
426 pub(crate) const fn table_capacity(self) -> table::Capacity {
427 self.table_end
428 }
429
430 pub(crate) const fn accept_capacity(self) -> table::Capacity {
431 self.accept_end
432 }
433}