x11rb_protocol/
id_allocator.rs1use crate::errors::ConnectError;
4use crate::protocol::xc_misc::GetXIDRangeReply;
5
6#[cfg(feature = "std")]
7use std::error::Error;
8
9use core::fmt;
10
11#[derive(Debug, Clone, Copy)]
21pub struct IdAllocator {
22 next_id: u32,
23 max_id: u32,
24 increment: u32,
25}
26
27impl IdAllocator {
28 pub fn new(id_base: u32, id_mask: u32) -> Result<Self, ConnectError> {
33 if id_mask == 0 {
34 return Err(ConnectError::ZeroIdMask);
35 }
36 let increment = id_mask & (1 + !id_mask);
38 Ok(Self {
39 next_id: id_base,
40 max_id: id_base | id_mask,
41 increment,
42 })
43 }
44
45 pub fn update_xid_range(&mut self, xidrange: &GetXIDRangeReply) -> Result<(), IdsExhausted> {
47 let (start, count) = (xidrange.start_id, xidrange.count);
48 if (start, count) == (0, 1) || count == 0 {
51 return Err(IdsExhausted);
52 }
53 self.next_id = start;
54 self.max_id = start + (count - 1) * self.increment;
55 Ok(())
56 }
57
58 pub fn generate_id(&mut self) -> Option<u32> {
60 if self.next_id > self.max_id {
61 None
62 } else {
63 let id = self.next_id;
64 self.next_id += self.increment;
65 Some(id)
66 }
67 }
68}
69
70#[derive(Debug, Copy, Clone)]
72pub struct IdsExhausted;
73
74impl fmt::Display for IdsExhausted {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 write!(f, "XID range has been exhausted")
77 }
78}
79
80#[cfg(feature = "std")]
81impl Error for IdsExhausted {}
82
83#[cfg(test)]
84mod test {
85 use super::{GetXIDRangeReply, IdAllocator, IdsExhausted};
86
87 #[test]
88 fn exhaustive() {
89 let mut allocator = IdAllocator::new(0x2800, 0x1ff).unwrap();
90 for expected in 0x2800..=0x29ff {
91 assert_eq!(Some(expected), allocator.generate_id());
92 }
93 assert_eq!(None, allocator.generate_id());
94 }
95
96 #[test]
97 fn increment() {
98 let mut allocator = IdAllocator::new(0, 0b1100).unwrap();
99 assert_eq!(Some(0b0000), allocator.generate_id());
100 assert_eq!(Some(0b0100), allocator.generate_id());
101 assert_eq!(Some(0b1000), allocator.generate_id());
102 assert_eq!(Some(0b1100), allocator.generate_id());
103 assert_eq!(None, allocator.generate_id());
104 }
105
106 #[test]
107 fn new_range() {
108 let mut allocator = IdAllocator::new(0x420, 2).unwrap();
109 assert_eq!(Some(0x420), allocator.generate_id());
110 assert_eq!(Some(0x422), allocator.generate_id());
111 assert_eq!(None, allocator.generate_id());
113 allocator
114 .update_xid_range(&generate_get_xid_range_reply(0x13370, 3))
115 .unwrap();
116 assert_eq!(Some(0x13370), allocator.generate_id());
117 assert_eq!(Some(0x13372), allocator.generate_id());
118 assert_eq!(Some(0x13374), allocator.generate_id());
119 assert_eq!(None, allocator.generate_id());
121 allocator
122 .update_xid_range(&generate_get_xid_range_reply(0x13370, 3))
123 .unwrap();
124 assert_eq!(Some(0x13370), allocator.generate_id());
125 }
126
127 #[test]
128 fn invalid_new_arg() {
129 let err = IdAllocator::new(1234, 0).unwrap_err();
130 if let super::ConnectError::ZeroIdMask = err {
131 } else {
132 panic!("Wrong error: {:?}", err);
133 }
134 }
135
136 #[test]
137 fn invalid_update_arg() {
138 fn check_ids_exhausted(arg: &Result<(), IdsExhausted>) {
139 if let Err(IdsExhausted) = arg {
140 } else {
141 panic!("Expected IdsExhausted, got {:?}", arg);
142 }
143 }
144
145 let mut allocator = IdAllocator::new(0x420, 2).unwrap();
146 check_ids_exhausted(&allocator.update_xid_range(&generate_get_xid_range_reply(0, 1)));
147 check_ids_exhausted(&allocator.update_xid_range(&generate_get_xid_range_reply(1, 0)));
148 }
149
150 fn generate_get_xid_range_reply(start_id: u32, count: u32) -> GetXIDRangeReply {
151 GetXIDRangeReply {
152 sequence: 0,
153 length: 0,
154 start_id,
155 count,
156 }
157 }
158}