1use crate::vlq::vlq_decode;
2use crate::{DecodeError, Line, Segment, SourceMapMappings};
3
4pub fn decode(input: &str) -> Result<SourceMapMappings, DecodeError> {
20 if input.is_empty() {
21 return Ok(Vec::new());
22 }
23
24 let bytes = input.as_bytes();
25 let len = bytes.len();
26
27 let semicolons = memchr::memchr_iter(b';', bytes).count();
31 let commas = memchr::memchr_iter(b',', bytes).count();
32 let line_count = semicolons + 1;
33 let approx_segments = commas + line_count;
34 let avg_segments_per_line = approx_segments / line_count;
35 let mut mappings: SourceMapMappings = Vec::with_capacity(line_count);
36
37 let mut source_index: i64 = 0;
39 let mut original_line: i64 = 0;
40 let mut original_column: i64 = 0;
41 let mut name_index: i64 = 0;
42
43 let mut pos: usize = 0;
44
45 loop {
46 let mut generated_column: i64 = 0;
48 let mut line: Line = Vec::with_capacity(avg_segments_per_line);
49 let mut saw_semicolon = false;
50
51 while pos < len {
52 let byte = bytes[pos];
53
54 if byte == b';' {
55 pos += 1;
56 saw_semicolon = true;
57 break;
58 }
59
60 if byte == b',' {
61 pos += 1;
62 continue;
63 }
64
65 let (delta, consumed) = vlq_decode(bytes, pos)?;
67 generated_column += delta;
68 pos += consumed;
69
70 let segment: Segment = if pos < len && bytes[pos] != b',' && bytes[pos] != b';' {
72 let (delta, consumed) = vlq_decode(bytes, pos)?;
74 source_index += delta;
75 pos += consumed;
76
77 if pos >= len || bytes[pos] == b',' || bytes[pos] == b';' {
79 return Err(DecodeError::InvalidSegmentLength { fields: 2, offset: pos });
80 }
81
82 let (delta, consumed) = vlq_decode(bytes, pos)?;
84 original_line += delta;
85 pos += consumed;
86
87 if pos >= len || bytes[pos] == b',' || bytes[pos] == b';' {
89 return Err(DecodeError::InvalidSegmentLength { fields: 3, offset: pos });
90 }
91
92 let (delta, consumed) = vlq_decode(bytes, pos)?;
94 original_column += delta;
95 pos += consumed;
96
97 if pos < len && bytes[pos] != b',' && bytes[pos] != b';' {
99 let (delta, consumed) = vlq_decode(bytes, pos)?;
100 name_index += delta;
101 pos += consumed;
102 Segment::five(
103 generated_column,
104 source_index,
105 original_line,
106 original_column,
107 name_index,
108 )
109 } else {
110 Segment::four(generated_column, source_index, original_line, original_column)
111 }
112 } else {
113 Segment::one(generated_column)
114 };
115
116 line.push(segment);
117 }
118
119 mappings.push(line);
120
121 if !saw_semicolon {
122 break;
123 }
124 }
125
126 Ok(mappings)
127}