/rust/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.2.0/src/dfa/accel.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // This module defines some core types for dealing with accelerated DFA states. |
2 | | // Briefly, a DFA state can be "accelerated" if all of its transitions except |
3 | | // for a few loop back to itself. This directly implies that the only way out |
4 | | // of such a state is if a byte corresponding to one of those non-loopback |
5 | | // transitions is found. Such states are often found in simple repetitions in |
6 | | // non-Unicode regexes. For example, consider '(?-u)[^a]+a'. We can look at its |
7 | | // DFA with regex-cli: |
8 | | // |
9 | | // $ regex-cli debug dfa dense '(?-u)[^a]+a' -BbC |
10 | | // dense::DFA( |
11 | | // D 000000: |
12 | | // Q 000001: |
13 | | // *000002: |
14 | | // A 000003: \x00-` => 3, a => 5, b-\xFF => 3 |
15 | | // >000004: \x00-` => 3, a => 4, b-\xFF => 3 |
16 | | // 000005: \x00-\xFF => 2, EOI => 2 |
17 | | // ) |
18 | | // |
19 | | // In particular, state 3 is accelerated (shown via the 'A' indicator) since |
20 | | // the only way to leave that state once entered is to see an 'a' byte. If |
21 | | // there is a long run of non-'a' bytes, then using something like 'memchr' |
22 | | // to find the next 'a' byte can be significantly faster than just using the |
23 | | // standard byte-at-a-time state machine. |
24 | | // |
25 | | // Unfortunately, this optimization rarely applies when Unicode is enabled. |
26 | | // For example, patterns like '[^a]' don't actually match any byte that isn't |
27 | | // 'a', but rather, any UTF-8 encoding of a Unicode scalar value that isn't |
28 | | // 'a'. This makes the state machine much more complex---far beyond a single |
29 | | // state---and removes the ability to easily accelerate it. (Because if the |
30 | | // machine sees a non-UTF-8 sequence, then the machine won't match through it.) |
31 | | // |
32 | | // In practice, we only consider accelerating states that have 3 or fewer |
33 | | // non-loop transitions. At a certain point, you get diminishing returns, but |
34 | | // also because that's what the memchr crate supports. The structures below |
35 | | // hard-code this assumption and provide (de)serialization APIs for use inside |
36 | | // a DFA. |
37 | | // |
38 | | // And finally, note that there is some trickery involved in making it very |
39 | | // fast to not only check whether a state is accelerated at search time, but |
40 | | // also to access the bytes to search for to implement the acceleration itself. |
41 | | // dfa/special.rs provides more detail, but the short story is that all |
42 | | // accelerated states appear contiguously in a DFA. This means we can represent |
43 | | // the ID space of all accelerated DFA states with a single range. So given |
44 | | // a state ID, we can determine whether it's accelerated via |
45 | | // |
46 | | // min_accel_id <= id <= max_accel_id |
47 | | // |
48 | | // And find its corresponding accelerator with: |
49 | | // |
50 | | // accels.get((id - min_accel_id) / dfa_stride) |
51 | | |
52 | | use core::convert::{TryFrom, TryInto}; |
53 | | |
54 | | #[cfg(feature = "alloc")] |
55 | | use alloc::{vec, vec::Vec}; |
56 | | |
57 | | use crate::util::bytes::{self, DeserializeError, Endian, SerializeError}; |
58 | | |
59 | | /// The base type used to represent a collection of accelerators. |
60 | | /// |
61 | | /// While an `Accel` is represented as a fixed size array of bytes, a |
62 | | /// *collection* of `Accel`s (called `Accels`) is represented internally as a |
63 | | /// slice of u32. While it's a bit unnatural to do this and costs us a bit of |
64 | | /// fairly low-risk not-safe code, it lets us remove the need for a second type |
65 | | /// parameter in the definition of dense::DFA. (Which really wants everything |
66 | | /// to be a slice of u32.) |
67 | | type AccelTy = u32; |
68 | | |
69 | | /// The size of the unit of representation for accelerators. |
70 | | /// |
71 | | /// ACCEL_CAP *must* be a multiple of this size. |
72 | | const ACCEL_TY_SIZE: usize = core::mem::size_of::<AccelTy>(); |
73 | | |
74 | | /// The maximum length in bytes that a single Accel can be. This is distinct |
75 | | /// from the capacity of an accelerator in that the length represents only the |
76 | | /// bytes that should be read. |
77 | | const ACCEL_LEN: usize = 4; |
78 | | |
79 | | /// The capacity of each accelerator, in bytes. We set this to 8 since it's a |
80 | | /// multiple of 4 (our ID size) and because it gives us a little wiggle room |
81 | | /// if we want to support more accel bytes in the future without a breaking |
82 | | /// change. |
83 | | /// |
84 | | /// This MUST be a multiple of ACCEL_TY_SIZE. |
85 | | const ACCEL_CAP: usize = 8; |
86 | | |
87 | | /// Search for between 1 and 3 needle bytes in the given haystack, starting the |
88 | | /// search at the given position. If `needles` has a length other than 1-3, |
89 | | /// then this panics. |
90 | | #[inline(always)] |
91 | 0 | pub(crate) fn find_fwd( |
92 | 0 | needles: &[u8], |
93 | 0 | haystack: &[u8], |
94 | 0 | at: usize, |
95 | 0 | ) -> Option<usize> { |
96 | 0 | let bs = needles; |
97 | 0 | let i = match needles.len() { |
98 | 0 | 1 => memchr::memchr(bs[0], &haystack[at..])?, |
99 | 0 | 2 => memchr::memchr2(bs[0], bs[1], &haystack[at..])?, |
100 | 0 | 3 => memchr::memchr3(bs[0], bs[1], bs[2], &haystack[at..])?, |
101 | 0 | 0 => panic!("cannot find with empty needles"), |
102 | 0 | n => panic!("invalid needles length: {}", n), |
103 | | }; |
104 | 0 | Some(at + i) |
105 | 0 | } |
106 | | |
107 | | /// Search for between 1 and 3 needle bytes in the given haystack in reverse, |
108 | | /// starting the search at the given position. If `needles` has a length other |
109 | | /// than 1-3, then this panics. |
110 | | #[inline(always)] |
111 | 0 | pub(crate) fn find_rev( |
112 | 0 | needles: &[u8], |
113 | 0 | haystack: &[u8], |
114 | 0 | at: usize, |
115 | 0 | ) -> Option<usize> { |
116 | 0 | let bs = needles; |
117 | 0 | match needles.len() { |
118 | 0 | 1 => memchr::memrchr(bs[0], &haystack[..at]), |
119 | 0 | 2 => memchr::memrchr2(bs[0], bs[1], &haystack[..at]), |
120 | 0 | 3 => memchr::memrchr3(bs[0], bs[1], bs[2], &haystack[..at]), |
121 | 0 | 0 => panic!("cannot find with empty needles"), |
122 | 0 | n => panic!("invalid needles length: {}", n), |
123 | | } |
124 | 0 | } |
125 | | |
126 | | /// Represents the accelerators for all accelerated states in a dense DFA. |
127 | | /// |
128 | | /// The `A` type parameter represents the type of the underlying bytes. |
129 | | /// Generally, this is either `&[AccelTy]` or `Vec<AccelTy>`. |
130 | | #[derive(Clone)] |
131 | | pub(crate) struct Accels<A> { |
132 | | /// A length prefixed slice of contiguous accelerators. See the top comment |
133 | | /// in this module for more details on how we can jump from a DFA's state |
134 | | /// ID to an accelerator in this list. |
135 | | /// |
136 | | /// The first 4 bytes always correspond to the number of accelerators |
137 | | /// that follow. |
138 | | accels: A, |
139 | | } |
140 | | |
141 | | #[cfg(feature = "alloc")] |
142 | | impl Accels<Vec<AccelTy>> { |
143 | | /// Create an empty sequence of accelerators for a DFA. |
144 | | pub fn empty() -> Accels<Vec<AccelTy>> { |
145 | | Accels { accels: vec![0] } |
146 | | } |
147 | | |
148 | | /// Add an accelerator to this sequence. |
149 | | /// |
150 | | /// This adds to the accelerator to the end of the sequence and therefore |
151 | | /// should be done in correspondence with its state in the DFA. |
152 | | /// |
153 | | /// This panics if this results in more accelerators than AccelTy::MAX. |
154 | | pub fn add(&mut self, accel: Accel) { |
155 | | self.accels.extend_from_slice(&accel.as_accel_tys()); |
156 | | let len = self.len(); |
157 | | self.set_len(len + 1); |
158 | | } |
159 | | |
160 | | /// Set the number of accelerators in this sequence, which is encoded in |
161 | | /// the first 4 bytes of the underlying bytes. |
162 | | fn set_len(&mut self, new_len: usize) { |
163 | | // The only way an accelerator gets added is if a state exists for |
164 | | // it, and if a state exists, then its index is guaranteed to be |
165 | | // representable by a AccelTy by virtue of the guarantees provided by |
166 | | // StateID. |
167 | | let new_len = AccelTy::try_from(new_len).unwrap(); |
168 | | self.accels[0] = new_len; |
169 | | } |
170 | | } |
171 | | |
172 | | impl<'a> Accels<&'a [AccelTy]> { |
173 | | /// Deserialize a sequence of accelerators from the given bytes. If there |
174 | | /// was a problem deserializing, then an error is returned. |
175 | | /// |
176 | | /// This is guaranteed to run in constant time. This does not guarantee |
177 | | /// that every accelerator in the returned collection is valid. Thus, |
178 | | /// accessing one may panic, or not-safe code that relies on accelerators |
179 | | /// being correct my result in UB. |
180 | | /// |
181 | | /// Callers may check the validity of every accelerator with the `validate` |
182 | | /// method. |
183 | 0 | pub unsafe fn from_bytes_unchecked( |
184 | 0 | mut slice: &'a [u8], |
185 | 0 | ) -> Result<(Accels<&'a [AccelTy]>, usize), DeserializeError> { |
186 | 0 | let slice_start = slice.as_ptr() as usize; |
187 | | |
188 | 0 | let (count, _) = |
189 | 0 | bytes::try_read_u32_as_usize(slice, "accelerators count")?; |
190 | | // The accelerator count is part of the accel_tys slice that |
191 | | // we deserialize. This is perhaps a bit idiosyncratic. It would |
192 | | // probably be better to split out the count into a real field. |
193 | | |
194 | 0 | let accel_tys_count = bytes::add( |
195 | 0 | bytes::mul(count, 2, "total number of accelerator accel_tys")?, |
196 | | 1, |
197 | | "total number of accel_tys", |
198 | 0 | )?; |
199 | 0 | let accel_tys_len = bytes::mul( |
200 | 0 | ACCEL_TY_SIZE, |
201 | 0 | accel_tys_count, |
202 | 0 | "total number of bytes in accelerators", |
203 | 0 | )?; |
204 | 0 | bytes::check_slice_len(slice, accel_tys_len, "accelerators")?; |
205 | 0 | bytes::check_alignment::<AccelTy>(slice)?; |
206 | 0 | let accel_tys = &slice[..accel_tys_len]; |
207 | 0 | slice = &slice[accel_tys_len..]; |
208 | 0 | // SAFETY: We've checked the length and alignment above, and since |
209 | 0 | // slice is just bytes, we can safely cast to a slice of &[AccelTy]. |
210 | 0 | #[allow(unused_unsafe)] |
211 | 0 | let accels = unsafe { |
212 | 0 | core::slice::from_raw_parts( |
213 | 0 | accel_tys.as_ptr() as *const AccelTy, |
214 | 0 | accel_tys_count, |
215 | 0 | ) |
216 | 0 | }; |
217 | 0 | Ok((Accels { accels }, slice.as_ptr() as usize - slice_start)) |
218 | 0 | } |
219 | | } |
220 | | |
221 | | impl<A: AsRef<[AccelTy]>> Accels<A> { |
222 | | /// Return an owned version of the accelerators. |
223 | | #[cfg(feature = "alloc")] |
224 | | pub fn to_owned(&self) -> Accels<Vec<AccelTy>> { |
225 | | Accels { accels: self.accels.as_ref().to_vec() } |
226 | | } |
227 | | |
228 | | /// Return a borrowed version of the accelerators. |
229 | 0 | pub fn as_ref(&self) -> Accels<&[AccelTy]> { |
230 | 0 | Accels { accels: self.accels.as_ref() } |
231 | 0 | } |
232 | | |
233 | | /// Return the bytes representing the serialization of the accelerators. |
234 | 0 | pub fn as_bytes(&self) -> &[u8] { |
235 | 0 | let accels = self.accels.as_ref(); |
236 | 0 | // SAFETY: This is safe because accels is a just a slice of AccelTy, |
237 | 0 | // and u8 always has a smaller alignment. |
238 | 0 | unsafe { |
239 | 0 | core::slice::from_raw_parts( |
240 | 0 | accels.as_ptr() as *const u8, |
241 | 0 | accels.len() * ACCEL_TY_SIZE, |
242 | 0 | ) |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | /// Returns the memory usage, in bytes, of these accelerators. |
247 | | /// |
248 | | /// The memory usage is computed based on the number of bytes used to |
249 | | /// represent all of the accelerators. |
250 | | /// |
251 | | /// This does **not** include the stack size used by this value. |
252 | 0 | pub fn memory_usage(&self) -> usize { |
253 | 0 | self.as_bytes().len() |
254 | 0 | } |
255 | | |
256 | | /// Return the bytes to search for corresponding to the accelerator in this |
257 | | /// sequence at index `i`. If no such accelerator exists, then this panics. |
258 | | /// |
259 | | /// The significance of the index is that it should be in correspondence |
260 | | /// with the index of the corresponding DFA. That is, accelerated DFA |
261 | | /// states are stored contiguously in the DFA and have an ordering implied |
262 | | /// by their respective state IDs. The state's index in that sequence |
263 | | /// corresponds to the index of its corresponding accelerator. |
264 | | #[inline(always)] |
265 | 0 | pub fn needles(&self, i: usize) -> &[u8] { |
266 | 0 | if i >= self.len() { |
267 | 0 | panic!("invalid accelerator index {}", i); |
268 | 0 | } |
269 | 0 | let bytes = self.as_bytes(); |
270 | 0 | let offset = ACCEL_TY_SIZE + i * ACCEL_CAP; |
271 | 0 | let len = bytes[offset] as usize; |
272 | 0 | &bytes[offset + 1..offset + 1 + len] |
273 | 0 | } |
274 | | |
275 | | /// Return the total number of accelerators in this sequence. |
276 | 0 | pub fn len(&self) -> usize { |
277 | 0 | // This should never panic since deserialization checks that the |
278 | 0 | // length can fit into a usize. |
279 | 0 | usize::try_from(self.accels.as_ref()[0]).unwrap() |
280 | 0 | } |
281 | | |
282 | | /// Return the accelerator in this sequence at index `i`. If no such |
283 | | /// accelerator exists, then this returns None. |
284 | | /// |
285 | | /// See the docs for `needles` on the significance of the index. |
286 | 0 | fn get(&self, i: usize) -> Option<Accel> { |
287 | 0 | if i >= self.len() { |
288 | 0 | return None; |
289 | 0 | } |
290 | 0 | let offset = ACCEL_TY_SIZE + i * ACCEL_CAP; |
291 | 0 | let accel = Accel::from_slice(&self.as_bytes()[offset..]) |
292 | 0 | .expect("Accels must contain valid accelerators"); |
293 | 0 | Some(accel) |
294 | 0 | } |
295 | | |
296 | | /// Returns an iterator of accelerators in this sequence. |
297 | 0 | fn iter(&self) -> IterAccels<'_, A> { |
298 | 0 | IterAccels { accels: self, i: 0 } |
299 | 0 | } |
300 | | |
301 | | /// Writes these accelerators to the given byte buffer using the indicated |
302 | | /// endianness. If the given buffer is too small, then an error is |
303 | | /// returned. Upon success, the total number of bytes written is returned. |
304 | | /// The number of bytes written is guaranteed to be a multiple of 8. |
305 | 0 | pub fn write_to<E: Endian>( |
306 | 0 | &self, |
307 | 0 | dst: &mut [u8], |
308 | 0 | ) -> Result<usize, SerializeError> { |
309 | 0 | let nwrite = self.write_to_len(); |
310 | 0 | assert_eq!( |
311 | 0 | nwrite % ACCEL_TY_SIZE, |
312 | | 0, |
313 | 0 | "expected accelerator bytes written to be a multiple of {}", |
314 | | ACCEL_TY_SIZE, |
315 | | ); |
316 | 0 | if dst.len() < nwrite { |
317 | 0 | return Err(SerializeError::buffer_too_small("accelerators")); |
318 | 0 | } |
319 | 0 |
|
320 | 0 | // The number of accelerators can never exceed AccelTy::MAX. |
321 | 0 | E::write_u32(AccelTy::try_from(self.len()).unwrap(), dst); |
322 | 0 | // The actual accelerators are just raw bytes and thus their endianness |
323 | 0 | // is irrelevant. So we can copy them as bytes. |
324 | 0 | dst[ACCEL_TY_SIZE..nwrite] |
325 | 0 | .copy_from_slice(&self.as_bytes()[ACCEL_TY_SIZE..nwrite]); |
326 | 0 | Ok(nwrite) |
327 | 0 | } |
328 | | |
329 | | /// Validates that every accelerator in this collection can be successfully |
330 | | /// deserialized as a valid accelerator. |
331 | 0 | pub fn validate(&self) -> Result<(), DeserializeError> { |
332 | 0 | for chunk in self.as_bytes()[ACCEL_TY_SIZE..].chunks(ACCEL_CAP) { |
333 | 0 | let _ = Accel::from_slice(chunk)?; |
334 | | } |
335 | 0 | Ok(()) |
336 | 0 | } |
337 | | |
338 | | /// Returns the total number of bytes written by `write_to`. |
339 | 0 | pub fn write_to_len(&self) -> usize { |
340 | 0 | self.as_bytes().len() |
341 | 0 | } |
342 | | } |
343 | | |
344 | | impl<A: AsRef<[AccelTy]>> core::fmt::Debug for Accels<A> { |
345 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
346 | 0 | write!(f, "Accels(")?; |
347 | 0 | let mut list = f.debug_list(); |
348 | 0 | for a in self.iter() { |
349 | 0 | list.entry(&a); |
350 | 0 | } |
351 | 0 | list.finish()?; |
352 | 0 | write!(f, ")") |
353 | 0 | } |
354 | | } |
355 | | |
356 | | #[derive(Debug)] |
357 | | struct IterAccels<'a, A: AsRef<[AccelTy]>> { |
358 | | accels: &'a Accels<A>, |
359 | | i: usize, |
360 | | } |
361 | | |
362 | | impl<'a, A: AsRef<[AccelTy]>> Iterator for IterAccels<'a, A> { |
363 | | type Item = Accel; |
364 | | |
365 | 0 | fn next(&mut self) -> Option<Accel> { |
366 | 0 | let accel = self.accels.get(self.i)?; |
367 | 0 | self.i += 1; |
368 | 0 | Some(accel) |
369 | 0 | } |
370 | | } |
371 | | |
372 | | /// Accel represents a structure for determining how to "accelerate" a DFA |
373 | | /// state. |
374 | | /// |
375 | | /// Namely, it contains zero or more bytes that must be seen in order for the |
376 | | /// DFA to leave the state it is associated with. In practice, the actual range |
377 | | /// is 1 to 3 bytes. |
378 | | /// |
379 | | /// The purpose of acceleration is to identify states whose vast majority |
380 | | /// of transitions are just loops back to the same state. For example, |
381 | | /// in the regex `(?-u)^[^a]+b`, the corresponding DFA will have a state |
382 | | /// (corresponding to `[^a]+`) where all transitions *except* for `a` and |
383 | | /// `b` loop back to itself. Thus, this state can be "accelerated" by simply |
384 | | /// looking for the next occurrence of either `a` or `b` instead of explicitly |
385 | | /// following transitions. (In this case, `b` transitions to the next state |
386 | | /// where as `a` would transition to the dead state.) |
387 | | #[derive(Clone)] |
388 | | pub(crate) struct Accel { |
389 | | /// The first byte is the length. Subsequent bytes are the accelerated |
390 | | /// bytes. |
391 | | /// |
392 | | /// Note that we make every accelerator 8 bytes as a slightly wasteful |
393 | | /// way of making sure alignment is always correct for state ID sizes of |
394 | | /// 1, 2, 4 and 8. This should be okay since accelerated states aren't |
395 | | /// particularly common, especially when Unicode is enabled. |
396 | | bytes: [u8; ACCEL_CAP], |
397 | | } |
398 | | |
399 | | impl Accel { |
400 | | /// Returns an empty accel, where no bytes are accelerated. |
401 | | #[cfg(feature = "alloc")] |
402 | | pub fn new() -> Accel { |
403 | | Accel { bytes: [0; ACCEL_CAP] } |
404 | | } |
405 | | |
406 | | /// Returns a verified accelerator derived from the beginning of the given |
407 | | /// slice. |
408 | | /// |
409 | | /// If the slice is not long enough or contains invalid bytes for an |
410 | | /// accelerator, then this returns an error. |
411 | 0 | pub fn from_slice(mut slice: &[u8]) -> Result<Accel, DeserializeError> { |
412 | 0 | slice = &slice[..core::cmp::min(ACCEL_LEN, slice.len())]; |
413 | 0 | let bytes = slice |
414 | 0 | .try_into() |
415 | 0 | .map_err(|_| DeserializeError::buffer_too_small("accelerator"))?; |
416 | 0 | Accel::from_bytes(bytes) |
417 | 0 | } |
418 | | |
419 | | /// Returns a verified accelerator derived from raw bytes. |
420 | | /// |
421 | | /// If the given bytes are invalid, then this returns an error. |
422 | 0 | fn from_bytes(bytes: [u8; 4]) -> Result<Accel, DeserializeError> { |
423 | 0 | if bytes[0] as usize >= ACCEL_LEN { |
424 | 0 | return Err(DeserializeError::generic( |
425 | 0 | "accelerator bytes cannot have length more than 3", |
426 | 0 | )); |
427 | 0 | } |
428 | 0 | Ok(Accel::from_bytes_unchecked(bytes)) |
429 | 0 | } |
430 | | |
431 | | /// Returns an accelerator derived from raw bytes. |
432 | | /// |
433 | | /// This does not check whether the given bytes are valid. Invalid bytes |
434 | | /// cannot sacrifice memory safety, but may result in panics or silent |
435 | | /// logic bugs. |
436 | 0 | fn from_bytes_unchecked(bytes: [u8; 4]) -> Accel { |
437 | 0 | Accel { bytes: [bytes[0], bytes[1], bytes[2], bytes[3], 0, 0, 0, 0] } |
438 | 0 | } |
439 | | |
440 | | /// Attempts to add the given byte to this accelerator. If the accelerator |
441 | | /// is already full then this returns false. Otherwise, returns true. |
442 | | /// |
443 | | /// If the given byte is already in this accelerator, then it panics. |
444 | | #[cfg(feature = "alloc")] |
445 | | pub fn add(&mut self, byte: u8) -> bool { |
446 | | if self.len() >= 3 { |
447 | | return false; |
448 | | } |
449 | | assert!( |
450 | | !self.contains(byte), |
451 | | "accelerator already contains {:?}", |
452 | | crate::util::DebugByte(byte) |
453 | | ); |
454 | | self.bytes[self.len() + 1] = byte; |
455 | | self.bytes[0] += 1; |
456 | | true |
457 | | } |
458 | | |
459 | | /// Return the number of bytes in this accelerator. |
460 | 0 | pub fn len(&self) -> usize { |
461 | 0 | self.bytes[0] as usize |
462 | 0 | } |
463 | | |
464 | | /// Returns true if and only if there are no bytes in this accelerator. |
465 | | #[cfg(feature = "alloc")] |
466 | | pub fn is_empty(&self) -> bool { |
467 | | self.len() == 0 |
468 | | } |
469 | | |
470 | | /// Returns the slice of bytes to accelerate. |
471 | | /// |
472 | | /// If this accelerator is empty, then this returns an empty slice. |
473 | 0 | fn needles(&self) -> &[u8] { |
474 | 0 | &self.bytes[1..1 + self.len()] |
475 | 0 | } |
476 | | |
477 | | /// Returns true if and only if this accelerator will accelerate the given |
478 | | /// byte. |
479 | | #[cfg(feature = "alloc")] |
480 | | fn contains(&self, byte: u8) -> bool { |
481 | | self.needles().iter().position(|&b| b == byte).is_some() |
482 | | } |
483 | | |
484 | | /// Returns the accelerator bytes as an array of AccelTys. |
485 | | #[cfg(feature = "alloc")] |
486 | | fn as_accel_tys(&self) -> [AccelTy; 2] { |
487 | | assert_eq!(ACCEL_CAP, 8); |
488 | | // These unwraps are OK since ACCEL_CAP is set to 8. |
489 | | let first = |
490 | | AccelTy::from_ne_bytes(self.bytes[0..4].try_into().unwrap()); |
491 | | let second = |
492 | | AccelTy::from_ne_bytes(self.bytes[4..8].try_into().unwrap()); |
493 | | [first, second] |
494 | | } |
495 | | } |
496 | | |
497 | | impl core::fmt::Debug for Accel { |
498 | 0 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
499 | 0 | write!(f, "Accel(")?; |
500 | 0 | let mut set = f.debug_set(); |
501 | 0 | for &b in self.needles() { |
502 | 0 | set.entry(&crate::util::DebugByte(b)); |
503 | 0 | } |
504 | 0 | set.finish()?; |
505 | 0 | write!(f, ")") |
506 | 0 | } |
507 | | } |