byte_slice/
macros.rs

1// +-----------------------------------------------------------------------------------------------+
2// | Copyright 2016 Sean Kerr                                                                      |
3// |                                                                                               |
4// | Licensed under the Apache License, Version 2.0 (the "License");                               |
5// | you may not use this file except in compliance with the License.                              |
6// | You may obtain a copy of the License at                                                       |
7// |                                                                                               |
8// |  http://www.apache.org/licenses/LICENSE-2.0                                                   |
9// |                                                                                               |
10// | Unless required by applicable law or agreed to in writing, software                           |
11// | distributed under the License is distributed on an "AS IS" BASIS,                             |
12// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.                      |
13// | See the License for the specific language governing permissions and                           |
14// | limitations under the License.                                                                |
15// +-----------------------------------------------------------------------------------------------+
16// | Author: Sean Kerr <sean@metatomic.io>                                                         |
17// +-----------------------------------------------------------------------------------------------+
18
19//! Byte stream collection macros.
20
21/// Retrieve the amount of readable bytes.
22#[macro_export]
23macro_rules! bs_available {
24    ($context:expr) => ({
25        $context.stream.len() - bs_index!($context)
26    });
27}
28
29/// Iterate `$context.stream`, and for each byte execute `$on_byte`. Upon locating end-of-stream
30/// execute `$on_eos`.
31#[macro_export]
32macro_rules! bs_collect {
33    ($context:expr, $on_byte:expr, $on_eos:expr) => ({
34        loop {
35            if bs_is_eos!($context) {
36                $on_eos
37            } else {
38                bs_next!($context);
39
40                $on_byte
41            }
42        }
43    });
44}
45
46/// Collect all sequential digit bytes into `$var` (u8), and convert them into an unsigned integer.
47/// If `$on_byte` is supplied, for each new byte execute `$on_byte`. Upon locating end-of-stream
48/// execute `$on_eos`. If an overflow would occur, execute `$on_overflow`.
49///
50/// Exit the collection loop upon locating a non-digit byte.
51#[macro_export]
52macro_rules! bs_collect_digits8 {
53    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr, $ty:ty) => ({
54        bs_collect!($context,
55            if is_digit!($context.byte) {
56                if let Some(value) = ($var as u8).checked_mul(10) {
57                    if let Some(value) = value.checked_add($context.byte - b'0') {
58                        $var = value as $ty;
59                        $on_byte
60                    } else {
61                        $on_overflow;
62                    }
63                } else {
64                    $on_overflow;
65                }
66            } else {
67                break;
68            },
69            $on_eos
70        );
71    });
72
73    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr) => ({
74        bs_collect_digits8!($context, $var, $on_byte, $on_overflow, $on_eos, u8);
75    });
76
77    ($context:expr, $var:expr, $on_overflow:expr, $on_eos:expr) => ({
78        bs_collect_digits8!($context, $var, {}, $on_overflow, $on_eos, u8);
79    });
80}
81
82/// Collect all sequential digit bytes into `$var` (u16), and convert them into an unsigned integer.
83/// If `$on_byte` is supplied, for each new byte execute `$on_byte`. Upon locating end-of-stream
84/// execute `$on_eos`. If an overflow would occur, execute `$on_overflow`.
85///
86/// Exit the collection loop upon locating a non-digit byte.
87#[macro_export]
88macro_rules! bs_collect_digits16 {
89    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr, $ty:ty) => ({
90        bs_collect!($context,
91            if is_digit!($context.byte) {
92                if let Some(value) = ($var as u16).checked_mul(10) {
93                    if let Some(value) = value.checked_add(($context.byte - b'0') as u16) {
94                        $var = value as $ty;
95                        $on_byte
96                    } else {
97                        $on_overflow
98                    }
99                } else {
100                    $on_overflow
101                }
102            } else {
103                break;
104            },
105            $on_eos
106        );
107    });
108
109    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr) => ({
110        bs_collect_digits16!($context, $var, $on_byte, $on_overflow, $on_eos, u16);
111    });
112
113    ($context:expr, $var:expr, $on_overflow:expr, $on_eos:expr) => ({
114        bs_collect_digits16!($context, $var, {}, $on_overflow, $on_eos, u16);
115    });
116}
117
118/// Collect all sequential digit bytes into `$var` (u32), and convert them into an unsigned integer.
119/// If `$on_byte` is supplied, for each new byte execute `$on_byte`. Upon locating end-of-stream
120/// execute `$on_eos`. If an overflow would occur, execute `$on_overflow`.
121///
122/// Exit the collection loop upon locating a non-digit byte.
123#[macro_export]
124macro_rules! bs_collect_digits32 {
125    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr, $ty:ty) => ({
126        bs_collect!($context,
127            if is_digit!($context.byte) {
128                if let Some(value) = ($var as u32).checked_mul(10) {
129                    if let Some(value) = value.checked_add(($context.byte - b'0') as u32) {
130                        $var = value as $ty;
131                        $on_byte
132                    } else {
133                        $on_overflow
134                    }
135                } else {
136                    $on_overflow
137                }
138            } else {
139                break;
140            },
141            $on_eos
142        );
143    });
144
145    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr) => ({
146        bs_collect_digits32!($context, $var, $on_byte, $on_overflow, $on_eos, u32);
147    });
148
149    ($context:expr, $var:expr, $on_overflow:expr, $on_eos:expr) => ({
150        bs_collect_digits32!($context, $var, {}, $on_overflow, $on_eos, u32);
151    });
152}
153
154/// Collect all sequential digit bytes into `$var` (u64), and convert them into an unsigned integer.
155/// If `$on_byte` is supplied, for each new byte execute `$on_byte`. Upon locating end-of-stream
156/// execute `$on_eos`. If an overflow would occur, execute `$on_overflow`.
157///
158/// Exit the collection loop upon locating a non-digit byte.
159#[macro_export]
160macro_rules! bs_collect_digits64 {
161    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr, $ty:ty) => ({
162        bs_collect!($context,
163            if is_digit!($context.byte) {
164                if let Some(value) = ($var as u64).checked_mul(10) {
165                    if let Some(value) = value.checked_add(($context.byte - b'0') as u64) {
166                        $var = value as $ty;
167                        $on_byte
168                    } else {
169                        $on_overflow
170                    }
171                } else {
172                    $on_overflow
173                }
174            } else {
175                break;
176            },
177            $on_eos
178        );
179    });
180
181    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr) => ({
182        bs_collect_digits64!($context, $var, $on_byte, $on_overflow, $on_eos, u64);
183    });
184
185    ($context:expr, $var:expr, $on_overflow:expr, $on_eos:expr) => ({
186        bs_collect_digits64!($context, $var, {}, $on_overflow, $on_eos, u64);
187    });
188}
189
190/// Collect all sequential hex bytes into `$var` (u8), and convert them into an unsigned integer. If
191/// `$on_byte` is supplied, for each new byte execute `$on_byte`. Upon locating end-of-stream
192/// execute `$on_eos`. If an overflow would occur, execute `$on_overflow`.
193///
194/// Exit the collection loop upon locating a non-hex byte.
195#[macro_export]
196macro_rules! bs_collect_hex8 {
197    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr, $ty:ty) => ({
198        bs_collect!($context,
199            if $context.byte > b'/' && $context.byte < b':' {
200                // digit
201                if let Some(value) = ($var as u8).checked_mul(16) {
202                    if let Some(value) = value.checked_add($context.byte - b'0') {
203                        $var = value as $ty;
204                        $on_byte
205                    } else {
206                        $on_overflow
207                    }
208                } else {
209                    $on_overflow
210                }
211            } else if $context.byte > b'@' && $context.byte < b'G' {
212                // A-F
213                if let Some(value) = ($var as u8).checked_mul(16) {
214                    if let Some(value) = value.checked_add($context.byte - b'7') {
215                        $var = value as $ty;
216                        $on_byte
217                    } else {
218                        $on_overflow
219                    }
220                } else {
221                    $on_overflow
222                }
223            } else if $context.byte > 0x60 && $context.byte < 0x67 {
224                // a-f
225                if let Some(value) = ($var as u8).checked_mul(16) {
226                    if let Some(value) = value.checked_add($context.byte - b'W') {
227                        $var = value as $ty;
228                        $on_byte
229                    } else {
230                        $on_overflow
231                    }
232                } else {
233                    $on_overflow
234                }
235            } else {
236                break;
237            },
238            $on_eos
239        );
240    });
241
242    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr) => ({
243        bs_collect_hex8!($context, $var, $on_byte, $on_overflow, $on_eos, u8)
244    });
245
246    ($context:expr, $var:expr, $on_overflow:expr, $on_eos:expr) => ({
247        bs_collect_hex8!($context, $var, {}, $on_overflow, $on_eos, u8)
248    });
249}
250
251/// Collect all sequential hex bytes into `$var` (u16), and convert them into an unsigned integer. If
252/// `$on_byte` is supplied, for each new byte execute `$on_byte`. Upon locating end-of-stream
253/// execute `$on_eos`. If an overflow would occur, execute `$on_overflow`.
254///
255/// Exit the collection loop upon locating a non-hex byte.
256#[macro_export]
257macro_rules! bs_collect_hex16 {
258    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr, $ty:ty) => ({
259        bs_collect!($context,
260            if $context.byte > b'/' && $context.byte < b':' {
261                // digit
262                if let Some(value) = ($var as u16).checked_mul(16) {
263                    if let Some(value) = value.checked_add(($context.byte - b'0') as u16) {
264                        $var = value as $ty;
265                        $on_byte
266                    } else {
267                        $on_overflow
268                    }
269                } else {
270                    $on_overflow
271                }
272            } else if $context.byte > b'@' && $context.byte < b'G' {
273                // A-F
274                if let Some(value) = ($var as u16).checked_mul(16) {
275                    if let Some(value) = value.checked_add(($context.byte - b'7') as u16) {
276                        $var = value as $ty;
277                        $on_byte
278                    } else {
279                        $on_overflow
280                    }
281                } else {
282                    $on_overflow
283                }
284            } else if $context.byte > 0x60 && $context.byte < 0x67 {
285                // a-f
286                if let Some(value) = ($var as u16).checked_mul(16) {
287                    if let Some(value) = value.checked_add(($context.byte - b'W') as u16) {
288                        $var = value as $ty;
289                        $on_byte
290                    } else {
291                        $on_overflow
292                    }
293                } else {
294                    $on_overflow
295                }
296            } else {
297                break;
298            },
299            $on_eos
300        );
301    });
302
303    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr) => ({
304        bs_collect_hex16!($context, $var, $on_byte, $on_overflow, $on_eos, u16)
305    });
306
307    ($context:expr, $var:expr, $on_overflow:expr, $on_eos:expr) => ({
308        bs_collect_hex16!($context, $var, {}, $on_overflow, $on_eos, u16)
309    });
310}
311
312/// Collect all sequential hex bytes into `$var` (u32), and convert them into an unsigned integer. If
313/// `$on_byte` is supplied, for each new byte execute `$on_byte`. Upon locating end-of-stream
314/// execute `$on_eos`. If an overflow would occur, execute `$on_overflow`.
315///
316/// Exit the collection loop upon locating a non-hex byte.
317#[macro_export]
318macro_rules! bs_collect_hex32 {
319    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr, $ty:ty) => ({
320        bs_collect!($context,
321            if $context.byte > b'/' && $context.byte < b':' {
322                // digit
323                if let Some(value) = ($var as u32).checked_mul(16) {
324                    if let Some(value) = value.checked_add(($context.byte - b'0') as u32) {
325                        $var = value as $ty;
326                        $on_byte
327                    } else {
328                        $on_overflow
329                    }
330                } else {
331                    $on_overflow
332                }
333            } else if $context.byte > b'@' && $context.byte < b'G' {
334                // A-F
335                if let Some(value) = ($var as u32).checked_mul(16) {
336                    if let Some(value) = value.checked_add(($context.byte - b'7') as u32) {
337                        $var = value as $ty;
338                        $on_byte
339                    } else {
340                        $on_overflow
341                    }
342                } else {
343                    $on_overflow
344                }
345            } else if $context.byte > 0x60 && $context.byte < 0x67 {
346                // a-f
347                if let Some(value) = ($var as u32).checked_mul(16) {
348                    if let Some(value) = value.checked_add(($context.byte - b'W') as u32) {
349                        $var = value as $ty;
350                        $on_byte
351                    } else {
352                        $on_overflow
353                    }
354                } else {
355                    $on_overflow
356                }
357            } else {
358                break;
359            },
360            $on_eos
361        );
362    });
363
364    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr) => ({
365        bs_collect_hex32!($context, $var, $on_byte, $on_overflow, $on_eos, u32)
366    });
367
368    ($context:expr, $var:expr, $on_overflow:expr, $on_eos:expr) => ({
369        bs_collect_hex32!($context, $var, {}, $on_overflow, $on_eos, u32)
370    });
371}
372
373/// Collect all sequential hex bytes into `$var` (u64), and convert them into an unsigned integer. If
374/// `$on_byte` is supplied, for each new byte execute `$on_byte`. Upon locating end-of-stream
375/// execute `$on_eos`. If an overflow would occur, execute `$on_overflow`.
376///
377/// Exit the collection loop upon locating a non-hex byte.
378#[macro_export]
379macro_rules! bs_collect_hex64 {
380    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr, $ty:ty) => ({
381        bs_collect!($context,
382            if $context.byte > b'/' && $context.byte < b':' {
383                // digit
384                if let Some(value) = ($var as u64).checked_mul(16) {
385                    if let Some(value) = value.checked_add(($context.byte - b'0') as u64) {
386                        $var = value as $ty;
387                        $on_byte
388                    } else {
389                        $on_overflow
390                    }
391                } else {
392                    $on_overflow
393                }
394            } else if $context.byte > b'@' && $context.byte < b'G' {
395                // A-F
396                if let Some(value) = ($var as u64).checked_mul(16) {
397                    if let Some(value) = value.checked_add(($context.byte - b'7') as u64) {
398                        $var = value as $ty;
399                        $on_byte
400                    } else {
401                        $on_overflow
402                    }
403                } else {
404                    $on_overflow
405                }
406            } else if $context.byte > 0x60 && $context.byte < 0x67 {
407                // a-f
408                if let Some(value) = ($var as u64).checked_mul(16) {
409                    if let Some(value) = value.checked_add(($context.byte - b'W') as u64) {
410                        $var = value as $ty;
411                        $on_byte
412                    } else {
413                        $on_overflow
414                    }
415                } else {
416                    $on_overflow
417                }
418            } else {
419                break;
420            },
421            $on_eos
422        );
423    });
424
425    ($context:expr, $var:expr, $on_byte:expr, $on_overflow:expr, $on_eos:expr) => ({
426        bs_collect_hex64!($context, $var, $on_byte, $on_overflow, $on_eos, u64)
427    });
428
429    ($context:expr, $var:expr, $on_overflow:expr, $on_eos:expr) => ({
430        bs_collect_hex64!($context, $var, {}, $on_overflow, $on_eos, u64)
431    });
432}
433
434/// Collect `$length` bytes. If `$on_byte` and `$on_eos` are supplied, for each new byte execute
435/// `$on_byte`. Upon locating end-of-stream execute `$on_eos`.
436///
437/// When `$on_byte` and `$on_eos` are not supplied, this macro assumes that `$length` bytes are
438/// available for reading, and immediately advances the `$context.stream_index` by `$length` bytes.
439#[macro_export]
440macro_rules! bs_collect_length {
441    ($context:expr, $length:expr, $on_byte:expr, $on_eos:expr) => ({
442        bs_collect!($context, {
443                $on_byte
444
445                if bs_index!($context) == $context.mark_index + $length {
446                    break;
447                }
448            },
449            $on_eos
450        );
451    });
452
453    ($context:expr, $length:expr) => ({
454        $context.stream_index += $length;
455    });
456}
457
458/// Collect if `$when` yields `true`.
459///
460/// Exit the collection loop if `$when` yields `false`.
461#[macro_export]
462macro_rules! bs_collect_when {
463    ($context:expr, $when:expr, $on_eos:expr) => ({
464        bs_collect!($context,
465            if $when {
466            } else {
467                break;
468            },
469            $on_eos
470        );
471    });
472}
473
474/// Collect if `$until` yields `false`.
475///
476/// Exit the collection loop if `$until` yields `true`.
477#[macro_export]
478macro_rules! bs_collect_until {
479    ($context:expr, $until:expr, $on_eos:expr) => ({
480        bs_collect!($context,
481            if $until {
482                break;
483            },
484            $on_eos
485        );
486    });
487}
488
489/// Count each occurrence of `$byte` starting at `$context.stream_index` until end-of-stream.
490#[macro_export]
491macro_rules! bs_count {
492    ($context:expr, $byte:expr) => ({
493        let mut count = 0;
494
495        for n in bs_index!($context)..bs_available!($context) {
496            if $context.stream[n] == $byte {
497                count += 1;
498            }
499        }
500
501        count
502    });
503}
504
505/// Count each byte loop starting at `$context.stream_index` until end-of-stream, if `$when`
506/// yields `true`.
507#[macro_export]
508macro_rules! bs_count_when {
509    ($context:expr, $when:expr) => ({
510        let mut count = 0;
511
512        for n in bs_index!($context)..bs_available!($context) {
513            $context.byte = $context.stream[n];
514
515            if $when {
516                count += 1;
517            }
518        }
519
520        $context.byte = $context.stream[bs_index!($context)];
521
522        count
523    });
524}
525
526/// Find the first occurrence of `$byte` and return the index relative to `$context.stream_index`.
527///
528/// `$start` is the starting index relative to `$context.stream_index`.
529#[macro_export]
530macro_rules! bs_find {
531    ($context:expr, $start:expr, $byte:expr) => ({
532        let mut index = None;
533
534        if bs_index!($context) + $start < $context.stream.len() {
535            for n in bs_index!($context) + $start..$context.stream.len() {
536                if $byte == $context.stream[n] {
537                    index = Some(n);
538                    break;
539                }
540            }
541        }
542
543        index
544    });
545
546    ($context:expr, $byte:expr) => (
547        bs_find!($context, 0, $byte);
548    );
549}
550
551/// Find the first occurrence of `$pattern` and return the index relative to
552/// `$context.stream_index`.
553///
554/// `$start` is the starting index relative to `$context.stream_index`.
555#[macro_export]
556macro_rules! bs_find_pattern {
557    ($context:expr, $start:expr, $pattern:expr) => ({
558        let mut index = None;
559
560        if bs_index!($context) + $start < $context.stream.len() {
561            'outer:
562            for s in bs_index!($context) + $start..$context.stream.len() {
563                for (p, byte) in $pattern.iter().enumerate() {
564                    if $context.stream.len() <= s + p || *byte != $context.stream[s + p] {
565                        break;
566                    } else if $pattern.len() == p + 1 {
567                        index = Some(s);
568
569                        break 'outer;
570                    }
571                }
572            }
573        }
574
575        index
576    });
577
578    ($context:expr, $pattern:expr) => (
579        bs_find_pattern!($context, 0, $pattern);
580    );
581}
582
583/// Indicates that a specified amount of bytes are available for reading.
584#[macro_export]
585macro_rules! bs_has_bytes {
586    ($context:expr, $length:expr) => (
587        bs_index!($context) + $length <= $context.stream.len()
588    );
589}
590
591/// Retrieve the current stream index.
592#[macro_export]
593macro_rules! bs_index {
594    ($context:expr) => (
595        $context.stream_index
596    );
597}
598
599/// Indicates that we're at the end of the stream.
600#[macro_export]
601macro_rules! bs_is_eos {
602    ($context:expr) => ({
603        bs_index!($context) == $context.stream.len()
604    });
605}
606
607/// Jump `$length` bytes.
608///
609/// This macro assumes that `$length` bytes are available for reading.
610#[macro_export]
611macro_rules! bs_jump {
612    ($context:expr, $length:expr) => ({
613        $context.stream_index += $length;
614    });
615}
616
617/// Set `$context.mark_index` to the current stream index or `$index`.
618#[macro_export]
619macro_rules! bs_mark {
620    ($context:expr) => ({
621        $context.mark_index = bs_index!($context);
622    });
623
624    ($context:expr, $index:expr) => ({
625        $context.mark_index = $index;
626    });
627}
628
629/// Advance `$context.stream_index` one byte and set `$context.byte` to the new byte.
630#[macro_export]
631macro_rules! bs_next {
632    ($context:expr) => ({
633        $context.byte = unsafe {
634            *$context.stream.get_unchecked(bs_index!($context))
635        };
636
637        $context.stream_index += 1;
638    });
639}
640
641/// Peek at a slice of bytes.
642///
643/// This macro assumes that `$length` bytes are available for reading.
644#[macro_export]
645macro_rules! bs_peek {
646    ($context:expr, $length:expr) => (
647        &$context.stream[bs_index!($context)..bs_index!($context) + $length]
648    );
649}
650
651/// Retrieve the remaining available bytes.
652#[macro_export]
653macro_rules! bs_remaining {
654    ($context:expr) => (
655        &$context.stream[bs_index!($context)..]
656    );
657}
658
659/// Replay the most recent byte, but do not change the current `$context.byte`.
660#[macro_export]
661macro_rules! bs_replay {
662    ($context:expr) => ({
663        bs_rewind!($context, 1);
664    });
665}
666
667/// Rewind `$context.stream_index` by `$length` bytes, but do not change the current
668/// `$context.byte`.
669#[macro_export]
670macro_rules! bs_rewind {
671    ($context:expr, $length:expr) => ({
672        $context.stream_index -= $length;
673    });
674}
675
676/// Rewind `$context.stream_index` to index `$index`, but do not change the current `$context.byte`.
677#[macro_export]
678macro_rules! bs_rewind_to {
679    ($context:expr, $index:expr) => ({
680        $context.stream_index = $index;
681    });
682}
683
684/// Retrieve the slice of marked bytes.
685#[macro_export]
686macro_rules! bs_slice {
687    ($context:expr) => (
688        &$context.stream[$context.mark_index..bs_index!($context)];
689    );
690}
691
692/// Retrieve the slice of marked bytes ignoring the very last byte.
693#[macro_export]
694macro_rules! bs_slice_ignore {
695    ($context:expr) => (
696        &$context.stream[$context.mark_index..bs_index!($context) - 1];
697    );
698}
699
700/// Retrieve the length of marked bytes.
701#[macro_export]
702macro_rules! bs_slice_length {
703    ($context:expr) => ({
704        bs_index!($context) - $context.mark_index
705    });
706}
707
708/// Determine if the remaining stream starts with `$pattern`.
709///
710/// This macro assumes that `$pattern.len()` bytes are available for reading.
711#[macro_export]
712macro_rules! bs_starts_with {
713    ($context:expr, $pattern:expr) => ({
714        &$context.stream[$context.stream_index..$context.stream_index+$pattern.len()] == $pattern
715    });
716}
717
718/// Determine if the remaining stream starts with `$pattern`, comparing only the first byte.
719///
720/// This macro assumes that `$pattern.len()` bytes are available for reading.
721#[macro_export]
722macro_rules! bs_starts_with1 {
723    ($context:expr, $pattern:expr) => (unsafe {
724           $context.stream.get_unchecked($context.stream_index) == $pattern.get_unchecked(0)
725    });
726}
727
728/// Determine if the remaining stream starts with `$pattern`, comparing only the first 2 bytes.
729///
730/// This macro assumes that `$pattern.len()` bytes are available for reading.
731#[macro_export]
732macro_rules! bs_starts_with2 {
733    ($context:expr, $pattern:expr) => (unsafe {
734           $context.stream.get_unchecked($context.stream_index)
735        == $pattern.get_unchecked(0)
736        && $context.stream.get_unchecked($context.stream_index + 1) == $pattern.get_unchecked(1)
737    });
738}
739
740/// Determine if the remaining stream starts with `$pattern`, comparing only the first 3 bytes.
741///
742/// This macro assumes that `$pattern.len()` bytes are available for reading.
743#[macro_export]
744macro_rules! bs_starts_with3 {
745    ($context:expr, $pattern:expr) => (unsafe {
746           $context.stream.get_unchecked($context.stream_index)     == $pattern.get_unchecked(0)
747        && $context.stream.get_unchecked($context.stream_index + 1) == $pattern.get_unchecked(1)
748        && $context.stream.get_unchecked($context.stream_index + 2) == $pattern.get_unchecked(2)
749    });
750}
751
752/// Determine if the remaining stream starts with `$pattern`, comparing only the first 4 bytes.
753///
754/// This macro assumes that `$pattern.len()` bytes are available for reading.
755#[macro_export]
756macro_rules! bs_starts_with4 {
757    ($context:expr, $pattern:expr) => (unsafe {
758           $context.stream.get_unchecked($context.stream_index)     == $pattern.get_unchecked(0)
759        && $context.stream.get_unchecked($context.stream_index + 1) == $pattern.get_unchecked(1)
760        && $context.stream.get_unchecked($context.stream_index + 2) == $pattern.get_unchecked(2)
761        && $context.stream.get_unchecked($context.stream_index + 3) == $pattern.get_unchecked(3)
762    });
763}
764
765/// Determine if the remaining stream starts with `$pattern`, comparing only the first 5 bytes.
766///
767/// This macro assumes that `$pattern.len()` bytes are available for reading.
768#[macro_export]
769macro_rules! bs_starts_with5 {
770    ($context:expr, $pattern:expr) => (unsafe {
771           $context.stream.get_unchecked($context.stream_index)     == $pattern.get_unchecked(0)
772        && $context.stream.get_unchecked($context.stream_index + 1) == $pattern.get_unchecked(1)
773        && $context.stream.get_unchecked($context.stream_index + 2) == $pattern.get_unchecked(2)
774        && $context.stream.get_unchecked($context.stream_index + 3) == $pattern.get_unchecked(3)
775        && $context.stream.get_unchecked($context.stream_index + 4) == $pattern.get_unchecked(4)
776    });
777}
778
779/// Determine if the remaining stream starts with `$pattern`, comparing only the first 6 bytes.
780///
781/// This macro assumes that `$pattern.len()` bytes are available for reading.
782#[macro_export]
783macro_rules! bs_starts_with6 {
784    ($context:expr, $pattern:expr) => (unsafe {
785           $context.stream.get_unchecked($context.stream_index)     == $pattern.get_unchecked(0)
786        && $context.stream.get_unchecked($context.stream_index + 1) == $pattern.get_unchecked(1)
787        && $context.stream.get_unchecked($context.stream_index + 2) == $pattern.get_unchecked(2)
788        && $context.stream.get_unchecked($context.stream_index + 3) == $pattern.get_unchecked(3)
789        && $context.stream.get_unchecked($context.stream_index + 4) == $pattern.get_unchecked(4)
790        && $context.stream.get_unchecked($context.stream_index + 5) == $pattern.get_unchecked(5)
791    });
792}
793
794/// Determine if the remaining stream starts with `$pattern`, comparing only the first 7 bytes.
795///
796/// This macro assumes that `$pattern.len()` bytes are available for reading.
797#[macro_export]
798macro_rules! bs_starts_with7 {
799    ($context:expr, $pattern:expr) => (unsafe {
800           $context.stream.get_unchecked($context.stream_index)     == $pattern.get_unchecked(0)
801        && $context.stream.get_unchecked($context.stream_index + 1) == $pattern.get_unchecked(1)
802        && $context.stream.get_unchecked($context.stream_index + 2) == $pattern.get_unchecked(2)
803        && $context.stream.get_unchecked($context.stream_index + 3) == $pattern.get_unchecked(3)
804        && $context.stream.get_unchecked($context.stream_index + 4) == $pattern.get_unchecked(4)
805        && $context.stream.get_unchecked($context.stream_index + 5) == $pattern.get_unchecked(5)
806        && $context.stream.get_unchecked($context.stream_index + 6) == $pattern.get_unchecked(6)
807    });
808}
809
810/// Determine if the remaining stream starts with `$pattern`, comparing only the first 8 bytes.
811///
812/// This macro assumes that `$pattern.len()` bytes are available for reading.
813#[macro_export]
814macro_rules! bs_starts_with8 {
815    ($context:expr, $pattern:expr) => (unsafe {
816           $context.stream.get_unchecked($context.stream_index)     == $pattern.get_unchecked(0)
817        && $context.stream.get_unchecked($context.stream_index + 1) == $pattern.get_unchecked(1)
818        && $context.stream.get_unchecked($context.stream_index + 2) == $pattern.get_unchecked(2)
819        && $context.stream.get_unchecked($context.stream_index + 3) == $pattern.get_unchecked(3)
820        && $context.stream.get_unchecked($context.stream_index + 4) == $pattern.get_unchecked(4)
821        && $context.stream.get_unchecked($context.stream_index + 5) == $pattern.get_unchecked(5)
822        && $context.stream.get_unchecked($context.stream_index + 6) == $pattern.get_unchecked(6)
823        && $context.stream.get_unchecked($context.stream_index + 7) == $pattern.get_unchecked(7)
824    });
825}
826
827/// Determine if the remaining stream starts with `$pattern`, comparing only the first 9 bytes.
828///
829/// This macro assumes that `$pattern.len()` bytes are available for reading.
830#[macro_export]
831macro_rules! bs_starts_with9 {
832    ($context:expr, $pattern:expr) => (unsafe {
833           $context.stream.get_unchecked($context.stream_index)     == $pattern.get_unchecked(0)
834        && $context.stream.get_unchecked($context.stream_index + 1) == $pattern.get_unchecked(1)
835        && $context.stream.get_unchecked($context.stream_index + 2) == $pattern.get_unchecked(2)
836        && $context.stream.get_unchecked($context.stream_index + 3) == $pattern.get_unchecked(3)
837        && $context.stream.get_unchecked($context.stream_index + 4) == $pattern.get_unchecked(4)
838        && $context.stream.get_unchecked($context.stream_index + 5) == $pattern.get_unchecked(5)
839        && $context.stream.get_unchecked($context.stream_index + 6) == $pattern.get_unchecked(6)
840        && $context.stream.get_unchecked($context.stream_index + 7) == $pattern.get_unchecked(7)
841        && $context.stream.get_unchecked($context.stream_index + 8) == $pattern.get_unchecked(8)
842    });
843}
844
845/// Determine if the remaining stream starts with `$pattern`, comparing only the first 10 bytes.
846///
847/// This macro assumes that `$pattern.len()` bytes are available for reading.
848#[macro_export]
849macro_rules! bs_starts_with10 {
850    ($context:expr, $pattern:expr) => (unsafe {
851           $context.stream.get_unchecked($context.stream_index)     == $pattern.get_unchecked(0)
852        && $context.stream.get_unchecked($context.stream_index + 1) == $pattern.get_unchecked(1)
853        && $context.stream.get_unchecked($context.stream_index + 2) == $pattern.get_unchecked(2)
854        && $context.stream.get_unchecked($context.stream_index + 3) == $pattern.get_unchecked(3)
855        && $context.stream.get_unchecked($context.stream_index + 4) == $pattern.get_unchecked(4)
856        && $context.stream.get_unchecked($context.stream_index + 5) == $pattern.get_unchecked(5)
857        && $context.stream.get_unchecked($context.stream_index + 6) == $pattern.get_unchecked(6)
858        && $context.stream.get_unchecked($context.stream_index + 7) == $pattern.get_unchecked(7)
859        && $context.stream.get_unchecked($context.stream_index + 8) == $pattern.get_unchecked(8)
860        && $context.stream.get_unchecked($context.stream_index + 9) == $pattern.get_unchecked(9)
861    });
862}
863
864/// Determine if the remaining stream starts with `$pattern`, comparing only the first 11 bytes.
865///
866/// This macro assumes that `$pattern.len()` bytes are available for reading.
867#[macro_export]
868macro_rules! bs_starts_with11 {
869    ($context:expr, $pattern:expr) => (unsafe {
870           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
871        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
872        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
873        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
874        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
875        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
876        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
877        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
878        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
879        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
880        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
881    });
882}
883
884/// Determine if the remaining stream starts with `$pattern`, comparing only the first 12 bytes.
885///
886/// This macro assumes that `$pattern.len()` bytes are available for reading.
887#[macro_export]
888macro_rules! bs_starts_with12 {
889    ($context:expr, $pattern:expr) => (unsafe {
890           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
891        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
892        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
893        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
894        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
895        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
896        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
897        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
898        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
899        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
900        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
901        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
902    });
903}
904
905/// Determine if the remaining stream starts with `$pattern`, comparing only the first 13 bytes.
906///
907/// This macro assumes that `$pattern.len()` bytes are available for reading.
908#[macro_export]
909macro_rules! bs_starts_with13 {
910    ($context:expr, $pattern:expr) => (unsafe {
911           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
912        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
913        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
914        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
915        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
916        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
917        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
918        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
919        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
920        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
921        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
922        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
923        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
924    });
925}
926
927/// Determine if the remaining stream starts with `$pattern`, comparing only the first 14 bytes.
928///
929/// This macro assumes that `$pattern.len()` bytes are available for reading.
930#[macro_export]
931macro_rules! bs_starts_with14 {
932    ($context:expr, $pattern:expr) => (unsafe {
933           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
934        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
935        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
936        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
937        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
938        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
939        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
940        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
941        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
942        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
943        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
944        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
945        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
946        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
947    });
948}
949
950/// Determine if the remaining stream starts with `$pattern`, comparing only the first 15 bytes.
951///
952/// This macro assumes that `$pattern.len()` bytes are available for reading.
953#[macro_export]
954macro_rules! bs_starts_with15 {
955    ($context:expr, $pattern:expr) => (unsafe {
956           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
957        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
958        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
959        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
960        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
961        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
962        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
963        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
964        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
965        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
966        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
967        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
968        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
969        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
970        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
971    });
972}
973
974/// Determine if the remaining stream starts with `$pattern`, comparing only the first 16 bytes.
975///
976/// This macro assumes that `$pattern.len()` bytes are available for reading.
977#[macro_export]
978macro_rules! bs_starts_with16 {
979    ($context:expr, $pattern:expr) => (unsafe {
980           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
981        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
982        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
983        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
984        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
985        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
986        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
987        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
988        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
989        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
990        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
991        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
992        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
993        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
994        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
995        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
996    });
997}
998
999/// Determine if the remaining stream starts with `$pattern`, comparing only the first 17 bytes.
1000///
1001/// This macro assumes that `$pattern.len()` bytes are available for reading.
1002#[macro_export]
1003macro_rules! bs_starts_with17 {
1004    ($context:expr, $pattern:expr) => (unsafe {
1005           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
1006        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
1007        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
1008        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
1009        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
1010        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
1011        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
1012        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
1013        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
1014        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
1015        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
1016        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
1017        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
1018        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
1019        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
1020        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
1021        && $context.stream.get_unchecked($context.stream_index + 16) == $pattern.get_unchecked(16)
1022    });
1023}
1024
1025/// Determine if the remaining stream starts with `$pattern`, comparing only the first 18 bytes.
1026///
1027/// This macro assumes that `$pattern.len()` bytes are available for reading.
1028#[macro_export]
1029macro_rules! bs_starts_with18 {
1030    ($context:expr, $pattern:expr) => (unsafe {
1031           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
1032        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
1033        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
1034        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
1035        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
1036        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
1037        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
1038        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
1039        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
1040        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
1041        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
1042        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
1043        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
1044        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
1045        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
1046        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
1047        && $context.stream.get_unchecked($context.stream_index + 16) == $pattern.get_unchecked(16)
1048        && $context.stream.get_unchecked($context.stream_index + 17) == $pattern.get_unchecked(17)
1049    });
1050}
1051
1052/// Determine if the remaining stream starts with `$pattern`, comparing only the first 19 bytes.
1053///
1054/// This macro assumes that `$pattern.len()` bytes are available for reading.
1055#[macro_export]
1056macro_rules! bs_starts_with19 {
1057    ($context:expr, $pattern:expr) => (unsafe {
1058           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
1059        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
1060        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
1061        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
1062        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
1063        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
1064        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
1065        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
1066        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
1067        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
1068        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
1069        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
1070        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
1071        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
1072        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
1073        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
1074        && $context.stream.get_unchecked($context.stream_index + 16) == $pattern.get_unchecked(16)
1075        && $context.stream.get_unchecked($context.stream_index + 17) == $pattern.get_unchecked(17)
1076        && $context.stream.get_unchecked($context.stream_index + 18) == $pattern.get_unchecked(18)
1077    });
1078}
1079
1080/// Determine if the remaining stream starts with `$pattern`, comparing only the first 20 bytes.
1081///
1082/// This macro assumes that `$pattern.len()` bytes are available for reading.
1083#[macro_export]
1084macro_rules! bs_starts_with20 {
1085    ($context:expr, $pattern:expr) => (unsafe {
1086           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
1087        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
1088        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
1089        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
1090        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
1091        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
1092        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
1093        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
1094        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
1095        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
1096        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
1097        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
1098        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
1099        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
1100        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
1101        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
1102        && $context.stream.get_unchecked($context.stream_index + 16) == $pattern.get_unchecked(16)
1103        && $context.stream.get_unchecked($context.stream_index + 17) == $pattern.get_unchecked(17)
1104        && $context.stream.get_unchecked($context.stream_index + 18) == $pattern.get_unchecked(18)
1105        && $context.stream.get_unchecked($context.stream_index + 19) == $pattern.get_unchecked(19)
1106    });
1107}
1108
1109/// Determine if the remaining stream starts with `$pattern`, comparing only the first 21 bytes.
1110///
1111/// This macro assumes that `$pattern.len()` bytes are available for reading.
1112#[macro_export]
1113macro_rules! bs_starts_with21 {
1114    ($context:expr, $pattern:expr) => (unsafe {
1115           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
1116        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
1117        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
1118        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
1119        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
1120        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
1121        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
1122        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
1123        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
1124        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
1125        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
1126        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
1127        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
1128        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
1129        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
1130        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
1131        && $context.stream.get_unchecked($context.stream_index + 16) == $pattern.get_unchecked(16)
1132        && $context.stream.get_unchecked($context.stream_index + 17) == $pattern.get_unchecked(17)
1133        && $context.stream.get_unchecked($context.stream_index + 18) == $pattern.get_unchecked(18)
1134        && $context.stream.get_unchecked($context.stream_index + 19) == $pattern.get_unchecked(19)
1135        && $context.stream.get_unchecked($context.stream_index + 20) == $pattern.get_unchecked(20)
1136    });
1137}
1138
1139/// Determine if the remaining stream starts with `$pattern`, comparing only the first 22 bytes.
1140///
1141/// This macro assumes that `$pattern.len()` bytes are available for reading.
1142#[macro_export]
1143macro_rules! bs_starts_with22 {
1144    ($context:expr, $pattern:expr) => (unsafe {
1145           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
1146        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
1147        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
1148        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
1149        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
1150        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
1151        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
1152        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
1153        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
1154        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
1155        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
1156        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
1157        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
1158        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
1159        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
1160        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
1161        && $context.stream.get_unchecked($context.stream_index + 16) == $pattern.get_unchecked(16)
1162        && $context.stream.get_unchecked($context.stream_index + 17) == $pattern.get_unchecked(17)
1163        && $context.stream.get_unchecked($context.stream_index + 18) == $pattern.get_unchecked(18)
1164        && $context.stream.get_unchecked($context.stream_index + 19) == $pattern.get_unchecked(19)
1165        && $context.stream.get_unchecked($context.stream_index + 20) == $pattern.get_unchecked(20)
1166        && $context.stream.get_unchecked($context.stream_index + 21) == $pattern.get_unchecked(21)
1167    });
1168}
1169
1170/// Determine if the remaining stream starts with `$pattern`, comparing only the first 23 bytes.
1171///
1172/// This macro assumes that `$pattern.len()` bytes are available for reading.
1173#[macro_export]
1174macro_rules! bs_starts_with23 {
1175    ($context:expr, $pattern:expr) => (unsafe {
1176           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
1177        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
1178        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
1179        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
1180        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
1181        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
1182        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
1183        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
1184        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
1185        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
1186        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
1187        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
1188        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
1189        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
1190        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
1191        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
1192        && $context.stream.get_unchecked($context.stream_index + 16) == $pattern.get_unchecked(16)
1193        && $context.stream.get_unchecked($context.stream_index + 17) == $pattern.get_unchecked(17)
1194        && $context.stream.get_unchecked($context.stream_index + 18) == $pattern.get_unchecked(18)
1195        && $context.stream.get_unchecked($context.stream_index + 19) == $pattern.get_unchecked(19)
1196        && $context.stream.get_unchecked($context.stream_index + 20) == $pattern.get_unchecked(20)
1197        && $context.stream.get_unchecked($context.stream_index + 21) == $pattern.get_unchecked(21)
1198        && $context.stream.get_unchecked($context.stream_index + 22) == $pattern.get_unchecked(22)
1199    });
1200}
1201
1202/// Determine if the remaining stream starts with `$pattern`, comparing only the first 24 bytes.
1203///
1204/// This macro assumes that `$pattern.len()` bytes are available for reading.
1205#[macro_export]
1206macro_rules! bs_starts_with24 {
1207    ($context:expr, $pattern:expr) => (unsafe {
1208           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
1209        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
1210        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
1211        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
1212        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
1213        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
1214        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
1215        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
1216        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
1217        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
1218        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
1219        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
1220        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
1221        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
1222        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
1223        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
1224        && $context.stream.get_unchecked($context.stream_index + 16) == $pattern.get_unchecked(16)
1225        && $context.stream.get_unchecked($context.stream_index + 17) == $pattern.get_unchecked(17)
1226        && $context.stream.get_unchecked($context.stream_index + 18) == $pattern.get_unchecked(18)
1227        && $context.stream.get_unchecked($context.stream_index + 19) == $pattern.get_unchecked(19)
1228        && $context.stream.get_unchecked($context.stream_index + 20) == $pattern.get_unchecked(20)
1229        && $context.stream.get_unchecked($context.stream_index + 21) == $pattern.get_unchecked(21)
1230        && $context.stream.get_unchecked($context.stream_index + 22) == $pattern.get_unchecked(22)
1231        && $context.stream.get_unchecked($context.stream_index + 23) == $pattern.get_unchecked(23)
1232    });
1233}
1234
1235/// Determine if the remaining stream starts with `$pattern`, comparing only the first 25 bytes.
1236///
1237/// This macro assumes that `$pattern.len()` bytes are available for reading.
1238#[macro_export]
1239macro_rules! bs_starts_with25 {
1240    ($context:expr, $pattern:expr) => (unsafe {
1241           $context.stream.get_unchecked($context.stream_index)      == $pattern.get_unchecked(0)
1242        && $context.stream.get_unchecked($context.stream_index + 1)  == $pattern.get_unchecked(1)
1243        && $context.stream.get_unchecked($context.stream_index + 2)  == $pattern.get_unchecked(2)
1244        && $context.stream.get_unchecked($context.stream_index + 3)  == $pattern.get_unchecked(3)
1245        && $context.stream.get_unchecked($context.stream_index + 4)  == $pattern.get_unchecked(4)
1246        && $context.stream.get_unchecked($context.stream_index + 5)  == $pattern.get_unchecked(5)
1247        && $context.stream.get_unchecked($context.stream_index + 6)  == $pattern.get_unchecked(6)
1248        && $context.stream.get_unchecked($context.stream_index + 7)  == $pattern.get_unchecked(7)
1249        && $context.stream.get_unchecked($context.stream_index + 8)  == $pattern.get_unchecked(8)
1250        && $context.stream.get_unchecked($context.stream_index + 9)  == $pattern.get_unchecked(9)
1251        && $context.stream.get_unchecked($context.stream_index + 10) == $pattern.get_unchecked(10)
1252        && $context.stream.get_unchecked($context.stream_index + 11) == $pattern.get_unchecked(11)
1253        && $context.stream.get_unchecked($context.stream_index + 12) == $pattern.get_unchecked(12)
1254        && $context.stream.get_unchecked($context.stream_index + 13) == $pattern.get_unchecked(13)
1255        && $context.stream.get_unchecked($context.stream_index + 14) == $pattern.get_unchecked(14)
1256        && $context.stream.get_unchecked($context.stream_index + 15) == $pattern.get_unchecked(15)
1257        && $context.stream.get_unchecked($context.stream_index + 16) == $pattern.get_unchecked(16)
1258        && $context.stream.get_unchecked($context.stream_index + 17) == $pattern.get_unchecked(17)
1259        && $context.stream.get_unchecked($context.stream_index + 18) == $pattern.get_unchecked(18)
1260        && $context.stream.get_unchecked($context.stream_index + 19) == $pattern.get_unchecked(19)
1261        && $context.stream.get_unchecked($context.stream_index + 20) == $pattern.get_unchecked(20)
1262        && $context.stream.get_unchecked($context.stream_index + 21) == $pattern.get_unchecked(21)
1263        && $context.stream.get_unchecked($context.stream_index + 22) == $pattern.get_unchecked(22)
1264        && $context.stream.get_unchecked($context.stream_index + 23) == $pattern.get_unchecked(23)
1265        && $context.stream.get_unchecked($context.stream_index + 24) == $pattern.get_unchecked(24)
1266    });
1267}
1268
1269/// Indicates that a byte is alphabetical.
1270#[macro_export]
1271macro_rules! is_alpha {
1272    ($byte:expr) => ({
1273           ($byte > 0x40 && $byte < 0x5B)
1274        || ($byte > 0x60 && $byte < 0x7B)
1275    });
1276}
1277
1278/// Indicates that a byte is a control character.
1279#[macro_export]
1280macro_rules! is_control {
1281    ($byte:expr) => ({
1282        $byte < 0x20 || $byte == 0x7F
1283    });
1284}
1285
1286/// Indicates that a byte is a digit.
1287#[macro_export]
1288macro_rules! is_digit {
1289    ($byte:expr) => ({
1290        $byte > 0x2F && $byte < 0x3A
1291    });
1292}
1293
1294/// Indicates that a byte is a hex character.
1295#[macro_export]
1296macro_rules! is_hex {
1297    ($byte:expr) => ({
1298           ($byte > 0x2F && $byte < 0x3A)
1299        || ($byte > 0x40 && $byte < 0x47)
1300        || ($byte > 0x60 && $byte < 0x67)
1301    });
1302}
1303
1304/// Indicates that a byte is not a visible 7-bit character. Space is not considered visible.
1305#[macro_export]
1306macro_rules! is_not_visible_7bit {
1307    ($byte:expr) => ({
1308        $byte < 0x21 || $byte > 0x7E
1309    })
1310}
1311
1312/// Indicates that a byte is not a visible 8-bit character. Space is not considered visible.
1313#[macro_export]
1314macro_rules! is_not_visible_8bit {
1315    ($byte:expr) => ({
1316           $byte < 0x21 || $byte == 0x7F || $byte == 0xFF
1317    })
1318}
1319
1320/// Indicates that a byte is a visible 7-bit character. Space is not considered visible.
1321#[macro_export]
1322macro_rules! is_visible_7bit {
1323    ($byte:expr) => ({
1324        $byte > 0x20 && $byte < 0x7F
1325    })
1326}
1327
1328/// Indicates that a byte is a visible 8-bit character. Space is not considered visible.
1329#[macro_export]
1330macro_rules! is_visible_8bit {
1331    ($byte:expr) => ({
1332           $byte > 0x20 && $byte < 0xFF && $byte != 0x7F
1333    })
1334}