nom/error.rs
1//! Error management
2//!
3//! Parsers are generic over their error type, requiring that it implements
4//! the `error::ParseError<Input>` trait.
5
6use crate::internal::{Err, Mode, OutputMode, PResult, Parser};
7use crate::lib::std::fmt;
8
9#[cfg(feature = "alloc")]
10use crate::alloc::borrow::ToOwned;
11
12#[cfg(feature = "std")]
13use crate::internal::IResult;
14
15/// This trait must be implemented by the error type of a nom parser.
16///
17/// There are already implementations of it for `(Input, ErrorKind)`
18/// and `Error<Input>`.
19///
20/// It provides methods to create an error from some combinators,
21/// and combine existing errors in combinators like `alt`.
22pub trait ParseError<I>: Sized {
23 /// Creates an error from the input position and an [ErrorKind]
24 fn from_error_kind(input: I, kind: ErrorKind) -> Self;
25
26 /// Combines an existing error with a new one created from the input
27 /// position and an [ErrorKind]. This is useful when backtracking
28 /// through a parse tree, accumulating error context on the way
29 fn append(input: I, kind: ErrorKind, other: Self) -> Self;
30
31 /// Creates an error from an input position and an expected character
32 fn from_char(input: I, _: char) -> Self {
33 Self::from_error_kind(input, ErrorKind::Char)
34 }
35
36 /// Combines two existing errors. This function is used to compare errors
37 /// generated in various branches of `alt`.
38 fn or(self, other: Self) -> Self {
39 other
40 }
41}
42
43/// This trait is required by the `context` combinator to add a static string
44/// to an existing error
45pub trait ContextError<I>: Sized {
46 /// Creates a new error from an input position, a static string and an existing error.
47 /// This is used mainly in the [context] combinator, to add user friendly information
48 /// to errors when backtracking through a parse tree
49 fn add_context(_input: I, _ctx: &'static str, other: Self) -> Self {
50 other
51 }
52}
53
54/// This trait is required by the `map_res` combinator to integrate
55/// error types from external functions, like [std::str::FromStr]
56pub trait FromExternalError<I, E> {
57 /// Creates a new error from an input position, an [ErrorKind] indicating the
58 /// wrapping parser, and an external error
59 fn from_external_error(input: I, kind: ErrorKind, e: E) -> Self;
60}
61
62/// default error type, only contains the error's location and code
63#[derive(Clone, Debug, Eq, PartialEq)]
64pub struct Error<I> {
65 /// position of the error in the input data
66 pub input: I,
67 /// nom error code
68 pub code: ErrorKind,
69}
70
71impl<I> Error<I> {
72 /// creates a new basic error
73 pub fn new(input: I, code: ErrorKind) -> Error<I> {
74 Error { input, code }
75 }
76}
77
78impl<I> ParseError<I> for Error<I> {
79 fn from_error_kind(input: I, kind: ErrorKind) -> Self {
80 Error { input, code: kind }
81 }
82
83 fn append(_: I, _: ErrorKind, other: Self) -> Self {
84 other
85 }
86}
87
88impl<I> ContextError<I> for Error<I> {}
89
90impl<I, E> FromExternalError<I, E> for Error<I> {
91 /// Create a new error from an input position and an external error
92 fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
93 Error { input, code: kind }
94 }
95}
96
97/// The Display implementation allows the std::error::Error implementation
98impl<I: fmt::Display> fmt::Display for Error<I> {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 write!(f, "error {:?} at: {}", self.code, self.input)
101 }
102}
103
104#[cfg(feature = "alloc")]
105impl<I: ToOwned + ?Sized> Error<&I> {
106 /// Converts `Error<&I>` into `Error<I::Owned>` by cloning.
107 pub fn cloned(self) -> Error<I::Owned> {
108 Error {
109 input: self.input.to_owned(),
110 code: self.code,
111 }
112 }
113}
114
115#[cfg(feature = "alloc")]
116impl<I: ToOwned + ?Sized> Error<&mut I> {
117 /// Converts `Error<&mut I>` into `Error<I::Owned>` by cloning.
118 pub fn cloned(self) -> Error<I::Owned> {
119 Error {
120 input: self.input.to_owned(),
121 code: self.code,
122 }
123 }
124}
125
126impl<I: Copy> Error<&I> {
127 /// Converts `Error<&I>` into `Error<I>` by copying.
128 pub fn copied(self) -> Error<I> {
129 Error {
130 input: *self.input,
131 code: self.code,
132 }
133 }
134}
135
136impl<I: Copy> Error<&mut I> {
137 /// Converts `Error<&mut I>` into `Error<I>` by copying.
138 pub fn copied(self) -> Error<I> {
139 Error {
140 input: *self.input,
141 code: self.code,
142 }
143 }
144}
145
146#[cfg(feature = "std")]
147impl<I: fmt::Debug + fmt::Display> std::error::Error for Error<I> {}
148
149#[cfg(feature = "alloc")]
150#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
151impl From<Error<&[u8]>> for Error<crate::lib::std::vec::Vec<u8>> {
152 fn from(value: Error<&[u8]>) -> Self {
153 Error {
154 input: value.input.to_owned(),
155 code: value.code,
156 }
157 }
158}
159
160#[cfg(feature = "alloc")]
161#[cfg_attr(feature = "docsrs", doc(cfg(feature = "alloc")))]
162impl From<Error<&str>> for Error<crate::lib::std::string::String> {
163 fn from(value: Error<&str>) -> Self {
164 Error {
165 input: value.input.to_owned(),
166 code: value.code,
167 }
168 }
169}
170
171// for backward compatibility, keep those trait implementations
172// for the previously used error type
173impl<I> ParseError<I> for (I, ErrorKind) {
174 fn from_error_kind(input: I, kind: ErrorKind) -> Self {
175 (input, kind)
176 }
177
178 fn append(_: I, _: ErrorKind, other: Self) -> Self {
179 other
180 }
181}
182
183impl<I> ContextError<I> for (I, ErrorKind) {}
184
185impl<I, E> FromExternalError<I, E> for (I, ErrorKind) {
186 fn from_external_error(input: I, kind: ErrorKind, _e: E) -> Self {
187 (input, kind)
188 }
189}
190
191impl<I> ParseError<I> for () {
192 fn from_error_kind(_: I, _: ErrorKind) -> Self {}
193
194 fn append(_: I, _: ErrorKind, _: Self) -> Self {}
195}
196
197impl<I> ContextError<I> for () {}
198
199impl<I, E> FromExternalError<I, E> for () {
200 fn from_external_error(_input: I, _kind: ErrorKind, _e: E) -> Self {}
201}
202
203/// Creates an error from the input position and an [ErrorKind]
204pub fn make_error<I, E: ParseError<I>>(input: I, kind: ErrorKind) -> E {
205 E::from_error_kind(input, kind)
206}
207
208/// Combines an existing error with a new one created from the input
209/// position and an [ErrorKind]. This is useful when backtracking
210/// through a parse tree, accumulating error context on the way
211pub fn append_error<I, E: ParseError<I>>(input: I, kind: ErrorKind, other: E) -> E {
212 E::append(input, kind, other)
213}
214
215/// Create a new error from an input position, a static string and an existing error.
216/// This is used mainly in the [context] combinator, to add user friendly information
217/// to errors when backtracking through a parse tree
218pub fn context<F>(context: &'static str, parser: F) -> Context<F> {
219 Context { context, parser }
220}
221
222/// Parser implementation for [context]
223pub struct Context<F> {
224 context: &'static str,
225 parser: F,
226}
227
228impl<I, F> Parser<I> for Context<F>
229where
230 I: Clone,
231 F: Parser<I>,
232 <F as Parser<I>>::Error: ContextError<I>,
233{
234 type Output = <F as Parser<I>>::Output;
235 type Error = <F as Parser<I>>::Error;
236
237 fn process<OM: OutputMode>(&mut self, input: I) -> PResult<OM, I, Self::Output, Self::Error> {
238 match self.parser.process::<OM>(input.clone()) {
239 Err(Err::Error(e)) => Err(Err::Error(OM::Error::map(e, |e| {
240 <F as Parser<I>>::Error::add_context(input, self.context, e)
241 }))),
242 Err(Err::Failure(e)) => Err(Err::Failure(<F as Parser<I>>::Error::add_context(
243 input,
244 self.context,
245 e,
246 ))),
247 x => x,
248 }
249 }
250}
251
252/// Indicates which parser returned an error
253#[rustfmt::skip]
254#[derive(Debug,PartialEq,Eq,Hash,Clone,Copy)]
255#[allow(deprecated,missing_docs)]
256pub enum ErrorKind {
257 Tag,
258 MapRes,
259 MapOpt,
260 Alt,
261 IsNot,
262 IsA,
263 SeparatedList,
264 SeparatedNonEmptyList,
265 Many0,
266 Many1,
267 ManyTill,
268 Count,
269 TakeUntil,
270 LengthValue,
271 TagClosure,
272 Alpha,
273 Digit,
274 HexDigit,
275 OctDigit,
276 BinDigit,
277 AlphaNumeric,
278 Space,
279 MultiSpace,
280 LengthValueFn,
281 Eof,
282 Switch,
283 TagBits,
284 OneOf,
285 NoneOf,
286 Char,
287 CrLf,
288 RegexpMatch,
289 RegexpMatches,
290 RegexpFind,
291 RegexpCapture,
292 RegexpCaptures,
293 TakeWhile1,
294 Complete,
295 Fix,
296 Escaped,
297 EscapedTransform,
298 NonEmpty,
299 ManyMN,
300 Not,
301 Permutation,
302 Verify,
303 TakeTill1,
304 TakeWhileMN,
305 TooLarge,
306 Many0Count,
307 Many1Count,
308 Float,
309 Satisfy,
310 Fail,
311 Many,
312 Fold,
313 Precedence,
314}
315
316#[rustfmt::skip]
317#[allow(deprecated)]
318/// Converts an ErrorKind to a number
319pub fn error_to_u32(e: &ErrorKind) -> u32 {
320 match *e {
321 ErrorKind::Tag => 1,
322 ErrorKind::MapRes => 2,
323 ErrorKind::MapOpt => 3,
324 ErrorKind::Alt => 4,
325 ErrorKind::IsNot => 5,
326 ErrorKind::IsA => 6,
327 ErrorKind::SeparatedList => 7,
328 ErrorKind::SeparatedNonEmptyList => 8,
329 ErrorKind::Many1 => 9,
330 ErrorKind::Count => 10,
331 ErrorKind::TakeUntil => 12,
332 ErrorKind::LengthValue => 15,
333 ErrorKind::TagClosure => 16,
334 ErrorKind::Alpha => 17,
335 ErrorKind::Digit => 18,
336 ErrorKind::AlphaNumeric => 19,
337 ErrorKind::Space => 20,
338 ErrorKind::MultiSpace => 21,
339 ErrorKind::LengthValueFn => 22,
340 ErrorKind::Eof => 23,
341 ErrorKind::Switch => 27,
342 ErrorKind::TagBits => 28,
343 ErrorKind::OneOf => 29,
344 ErrorKind::NoneOf => 30,
345 ErrorKind::Char => 40,
346 ErrorKind::CrLf => 41,
347 ErrorKind::RegexpMatch => 42,
348 ErrorKind::RegexpMatches => 43,
349 ErrorKind::RegexpFind => 44,
350 ErrorKind::RegexpCapture => 45,
351 ErrorKind::RegexpCaptures => 46,
352 ErrorKind::TakeWhile1 => 47,
353 ErrorKind::Complete => 48,
354 ErrorKind::Fix => 49,
355 ErrorKind::Escaped => 50,
356 ErrorKind::EscapedTransform => 51,
357 ErrorKind::NonEmpty => 56,
358 ErrorKind::ManyMN => 57,
359 ErrorKind::HexDigit => 59,
360 ErrorKind::OctDigit => 61,
361 ErrorKind::Many0 => 62,
362 ErrorKind::Not => 63,
363 ErrorKind::Permutation => 64,
364 ErrorKind::ManyTill => 65,
365 ErrorKind::Verify => 66,
366 ErrorKind::TakeTill1 => 67,
367 ErrorKind::TakeWhileMN => 69,
368 ErrorKind::TooLarge => 70,
369 ErrorKind::Many0Count => 71,
370 ErrorKind::Many1Count => 72,
371 ErrorKind::Float => 73,
372 ErrorKind::Satisfy => 74,
373 ErrorKind::Fail => 75,
374 ErrorKind::Many => 76,
375 ErrorKind::Fold => 77,
376 ErrorKind::BinDigit => 78,
377 ErrorKind::Precedence => 79,
378 }
379}
380
381impl ErrorKind {
382 #[rustfmt::skip]
383 #[allow(deprecated)]
384 /// Converts an ErrorKind to a text description
385 pub fn description(&self) -> &str {
386 match *self {
387 ErrorKind::Tag => "Tag",
388 ErrorKind::MapRes => "Map on Result",
389 ErrorKind::MapOpt => "Map on Option",
390 ErrorKind::Alt => "Alternative",
391 ErrorKind::IsNot => "IsNot",
392 ErrorKind::IsA => "IsA",
393 ErrorKind::SeparatedList => "Separated list",
394 ErrorKind::SeparatedNonEmptyList => "Separated non empty list",
395 ErrorKind::Many0 => "Many0",
396 ErrorKind::Many1 => "Many1",
397 ErrorKind::Count => "Count",
398 ErrorKind::TakeUntil => "Take until",
399 ErrorKind::LengthValue => "Length followed by value",
400 ErrorKind::TagClosure => "Tag closure",
401 ErrorKind::Alpha => "Alphabetic",
402 ErrorKind::Digit => "Digit",
403 ErrorKind::AlphaNumeric => "AlphaNumeric",
404 ErrorKind::Space => "Space",
405 ErrorKind::MultiSpace => "Multiple spaces",
406 ErrorKind::LengthValueFn => "LengthValueFn",
407 ErrorKind::Eof => "End of file",
408 ErrorKind::Switch => "Switch",
409 ErrorKind::TagBits => "Tag on bitstream",
410 ErrorKind::OneOf => "OneOf",
411 ErrorKind::NoneOf => "NoneOf",
412 ErrorKind::Char => "Char",
413 ErrorKind::CrLf => "CrLf",
414 ErrorKind::RegexpMatch => "RegexpMatch",
415 ErrorKind::RegexpMatches => "RegexpMatches",
416 ErrorKind::RegexpFind => "RegexpFind",
417 ErrorKind::RegexpCapture => "RegexpCapture",
418 ErrorKind::RegexpCaptures => "RegexpCaptures",
419 ErrorKind::TakeWhile1 => "TakeWhile1",
420 ErrorKind::Complete => "Complete",
421 ErrorKind::Fix => "Fix",
422 ErrorKind::Escaped => "Escaped",
423 ErrorKind::EscapedTransform => "EscapedTransform",
424 ErrorKind::NonEmpty => "NonEmpty",
425 ErrorKind::ManyMN => "Many(m, n)",
426 ErrorKind::HexDigit => "Hexadecimal Digit",
427 ErrorKind::OctDigit => "Octal digit",
428 ErrorKind::BinDigit => "Binary digit",
429 ErrorKind::Not => "Negation",
430 ErrorKind::Permutation => "Permutation",
431 ErrorKind::ManyTill => "ManyTill",
432 ErrorKind::Verify => "predicate verification",
433 ErrorKind::TakeTill1 => "TakeTill1",
434 ErrorKind::TakeWhileMN => "TakeWhileMN",
435 ErrorKind::TooLarge => "Needed data size is too large",
436 ErrorKind::Many0Count => "Count occurrence of >=0 patterns",
437 ErrorKind::Many1Count => "Count occurrence of >=1 patterns",
438 ErrorKind::Float => "Float",
439 ErrorKind::Satisfy => "Satisfy",
440 ErrorKind::Fail => "Fail",
441 ErrorKind::Many => "Many",
442 ErrorKind::Fold => "Fold",
443 ErrorKind::Precedence => "Precedence",
444 }
445 }
446}
447
448/// Creates a parse error from a `nom::ErrorKind`
449/// and the position in the input
450#[allow(unused_variables)]
451#[macro_export(local_inner_macros)]
452macro_rules! error_position(
453 ($input:expr, $code:expr $(,)?) => ({
454 $crate::error::make_error($input, $code)
455 });
456);
457
458/// Creates a parse error from a `nom::ErrorKind`,
459/// the position in the input and the next error in
460/// the parsing tree
461#[allow(unused_variables)]
462#[macro_export(local_inner_macros)]
463macro_rules! error_node_position(
464 ($input:expr, $code:expr, $next:expr $(,)?) => ({
465 $crate::error::append_error($input, $code, $next)
466 });
467);
468
469/// Prints a message and the input if the parser fails.
470///
471/// The message prints the `Error` or `Incomplete`
472/// and the parser's calling code.
473///
474/// It also displays the input in hexdump format
475///
476/// ```rust
477/// use nom::{IResult, error::dbg_dmp, bytes::complete::tag};
478///
479/// fn f(i: &[u8]) -> IResult<&[u8], &[u8]> {
480/// dbg_dmp(tag("abcd"), "tag")(i)
481/// }
482///
483/// let a = &b"efghijkl"[..];
484///
485/// // Will print the following message:
486/// // Error(Position(0, [101, 102, 103, 104, 105, 106, 107, 108])) at l.5 by ' tag ! ( "abcd" ) '
487/// // 00000000 65 66 67 68 69 6a 6b 6c efghijkl
488/// f(a);
489/// ```
490#[cfg(feature = "std")]
491#[cfg_attr(feature = "docsrs", doc(cfg(feature = "std")))]
492pub fn dbg_dmp<'a, F, O, E: std::fmt::Debug>(
493 f: F,
494 context: &'static str,
495) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], O, E>
496where
497 F: Fn(&'a [u8]) -> IResult<&'a [u8], O, E>,
498{
499 use crate::HexDisplay;
500 move |i: &'a [u8]| match f(i) {
501 Err(e) => {
502 println!("{}: Error({:?}) at:\n{}", context, e, i.to_hex(8));
503 Err(e)
504 }
505 a => a,
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512
513 #[test]
514 fn context_test() {
515 use crate::{character::char, combinator::cut, internal::Needed};
516
517 #[derive(Debug, PartialEq)]
518 struct Error<I> {
519 input: I,
520 ctx: Option<&'static str>,
521 }
522
523 impl<I> ParseError<I> for Error<I> {
524 fn from_error_kind(input: I, _kind: ErrorKind) -> Self {
525 Self { input, ctx: None }
526 }
527
528 fn append(input: I, _kind: ErrorKind, other: Self) -> Self {
529 Self {
530 input,
531 ctx: other.ctx,
532 }
533 }
534 }
535
536 impl<I> ContextError<I> for Error<I> {
537 fn add_context(input: I, ctx: &'static str, _other: Self) -> Self {
538 Self {
539 input,
540 ctx: Some(ctx),
541 }
542 }
543 }
544
545 assert_eq!(
546 context("ctx", char::<_, Error<_>>('a')).parse("abcd"),
547 Ok(("bcd", 'a'))
548 );
549 assert_eq!(
550 context("ctx", char::<_, Error<_>>('a')).parse(""),
551 Err(Err::Incomplete(Needed::new(1)))
552 );
553 assert_eq!(
554 context("ctx", char::<_, Error<_>>('a')).parse_complete(""),
555 Err(Err::Error(Error {
556 input: "",
557 ctx: Some("ctx")
558 }))
559 );
560 assert_eq!(
561 context("ctx", cut(char::<_, Error<_>>('a'))).parse("bcd"),
562 Err(Err::Failure(Error {
563 input: "bcd",
564 ctx: Some("ctx")
565 }))
566 );
567 }
568
569 #[cfg(feature = "alloc")]
570 #[test]
571 fn clone_error() {
572 use crate::lib::std::string::String;
573 let err = Error {
574 code: ErrorKind::Eof,
575 input: "test",
576 };
577
578 let _err: Error<String> = err.cloned();
579 }
580
581 #[test]
582 fn copy_error() {
583 let err = Error {
584 code: ErrorKind::Eof,
585 input: &0_u8,
586 };
587
588 let _err: Error<u8> = err.copied();
589 }
590}
591
592/*
593#[cfg(feature = "alloc")]
594use lib::std::{vec::Vec, collections::HashMap};
595
596#[cfg(feature = "std")]
597use lib::std::hash::Hash;
598
599#[cfg(feature = "std")]
600pub fn add_error_pattern<'a, I: Clone + Hash + Eq, O, E: Clone + Hash + Eq>(
601 h: &mut HashMap<VerboseError<I>, &'a str>,
602 e: VerboseError<I>,
603 message: &'a str,
604) -> bool {
605 h.insert(e, message);
606 true
607}
608
609pub fn slice_to_offsets(input: &[u8], s: &[u8]) -> (usize, usize) {
610 let start = input.as_ptr();
611 let off1 = s.as_ptr() as usize - start as usize;
612 let off2 = off1 + s.len();
613 (off1, off2)
614}
615
616#[cfg(feature = "std")]
617pub fn prepare_errors<O, E: Clone>(input: &[u8], e: VerboseError<&[u8]>) -> Option<Vec<(ErrorKind, usize, usize)>> {
618 let mut v: Vec<(ErrorKind, usize, usize)> = Vec::new();
619
620 for (p, kind) in e.errors.drain(..) {
621 let (o1, o2) = slice_to_offsets(input, p);
622 v.push((kind, o1, o2));
623 }
624
625 v.reverse();
626 Some(v)
627}
628
629#[cfg(feature = "std")]
630pub fn print_error<O, E: Clone>(input: &[u8], res: VerboseError<&[u8]>) {
631 if let Some(v) = prepare_errors(input, res) {
632 let colors = generate_colors(&v);
633 println!("parser codes: {}", print_codes(&colors, &HashMap::new()));
634 println!("{}", print_offsets(input, 0, &v));
635 } else {
636 println!("not an error");
637 }
638}
639
640#[cfg(feature = "std")]
641pub fn generate_colors<E>(v: &[(ErrorKind, usize, usize)]) -> HashMap<u32, u8> {
642 let mut h: HashMap<u32, u8> = HashMap::new();
643 let mut color = 0;
644
645 for &(ref c, _, _) in v.iter() {
646 h.insert(error_to_u32(c), color + 31);
647 color = color + 1 % 7;
648 }
649
650 h
651}
652
653pub fn code_from_offset(v: &[(ErrorKind, usize, usize)], offset: usize) -> Option<u32> {
654 let mut acc: Option<(u32, usize, usize)> = None;
655 for &(ref ek, s, e) in v.iter() {
656 let c = error_to_u32(ek);
657 if s <= offset && offset <= e {
658 if let Some((_, start, end)) = acc {
659 if start <= s && e <= end {
660 acc = Some((c, s, e));
661 }
662 } else {
663 acc = Some((c, s, e));
664 }
665 }
666 }
667 if let Some((code, _, _)) = acc {
668 return Some(code);
669 } else {
670 return None;
671 }
672}
673
674#[cfg(feature = "alloc")]
675pub fn reset_color(v: &mut Vec<u8>) {
676 v.push(0x1B);
677 v.push(b'[');
678 v.push(0);
679 v.push(b'm');
680}
681
682#[cfg(feature = "alloc")]
683pub fn write_color(v: &mut Vec<u8>, color: u8) {
684 v.push(0x1B);
685 v.push(b'[');
686 v.push(1);
687 v.push(b';');
688 let s = color.to_string();
689 let bytes = s.as_bytes();
690 v.extend(bytes.iter().cloned());
691 v.push(b'm');
692}
693
694#[cfg(feature = "std")]
695#[cfg_attr(feature = "cargo-clippy", allow(implicit_hasher))]
696pub fn print_codes(colors: &HashMap<u32, u8>, names: &HashMap<u32, &str>) -> String {
697 let mut v = Vec::new();
698 for (code, &color) in colors {
699 if let Some(&s) = names.get(code) {
700 let bytes = s.as_bytes();
701 write_color(&mut v, color);
702 v.extend(bytes.iter().cloned());
703 } else {
704 let s = code.to_string();
705 let bytes = s.as_bytes();
706 write_color(&mut v, color);
707 v.extend(bytes.iter().cloned());
708 }
709 reset_color(&mut v);
710 v.push(b' ');
711 }
712 reset_color(&mut v);
713
714 String::from_utf8_lossy(&v[..]).into_owned()
715}
716
717#[cfg(feature = "std")]
718pub fn print_offsets(input: &[u8], from: usize, offsets: &[(ErrorKind, usize, usize)]) -> String {
719 let mut v = Vec::with_capacity(input.len() * 3);
720 let mut i = from;
721 let chunk_size = 8;
722 let mut current_code: Option<u32> = None;
723 let mut current_code2: Option<u32> = None;
724
725 let colors = generate_colors(&offsets);
726
727 for chunk in input.chunks(chunk_size) {
728 let s = format!("{:08x}", i);
729 for &ch in s.as_bytes().iter() {
730 v.push(ch);
731 }
732 v.push(b'\t');
733
734 let mut k = i;
735 let mut l = i;
736 for &byte in chunk {
737 if let Some(code) = code_from_offset(&offsets, k) {
738 if let Some(current) = current_code {
739 if current != code {
740 reset_color(&mut v);
741 current_code = Some(code);
742 if let Some(&color) = colors.get(&code) {
743 write_color(&mut v, color);
744 }
745 }
746 } else {
747 current_code = Some(code);
748 if let Some(&color) = colors.get(&code) {
749 write_color(&mut v, color);
750 }
751 }
752 }
753 v.push(CHARS[(byte >> 4) as usize]);
754 v.push(CHARS[(byte & 0xf) as usize]);
755 v.push(b' ');
756 k = k + 1;
757 }
758
759 reset_color(&mut v);
760
761 if chunk_size > chunk.len() {
762 for _ in 0..(chunk_size - chunk.len()) {
763 v.push(b' ');
764 v.push(b' ');
765 v.push(b' ');
766 }
767 }
768 v.push(b'\t');
769
770 for &byte in chunk {
771 if let Some(code) = code_from_offset(&offsets, l) {
772 if let Some(current) = current_code2 {
773 if current != code {
774 reset_color(&mut v);
775 current_code2 = Some(code);
776 if let Some(&color) = colors.get(&code) {
777 write_color(&mut v, color);
778 }
779 }
780 } else {
781 current_code2 = Some(code);
782 if let Some(&color) = colors.get(&code) {
783 write_color(&mut v, color);
784 }
785 }
786 }
787 if (byte >= 32 && byte <= 126) || byte >= 128 {
788 v.push(byte);
789 } else {
790 v.push(b'.');
791 }
792 l = l + 1;
793 }
794 reset_color(&mut v);
795
796 v.push(b'\n');
797 i = i + chunk_size;
798 }
799
800 String::from_utf8_lossy(&v[..]).into_owned()
801}
802*/