use crate::SourceMapMappings;
use crate::vlq::vlq_encode_unchecked;
pub fn encode(mappings: &SourceMapMappings) -> String {
if mappings.is_empty() {
return String::new();
}
let segment_count: usize = mappings.iter().map(|line| line.len()).sum();
let mut buf: Vec<u8> = Vec::with_capacity(segment_count * 7 * 5 + mappings.len());
let mut prev_source: i64 = 0;
let mut prev_original_line: i64 = 0;
let mut prev_original_column: i64 = 0;
let mut prev_name: i64 = 0;
for (line_idx, line) in mappings.iter().enumerate() {
if line_idx > 0 {
buf.push(b';');
}
let mut prev_generated_column: i64 = 0;
let mut wrote_segment = false;
for segment in line.iter() {
if segment.is_empty() {
continue;
}
if wrote_segment {
buf.push(b',');
}
wrote_segment = true;
unsafe {
vlq_encode_unchecked(&mut buf, segment[0] - prev_generated_column);
prev_generated_column = segment[0];
if segment.len() >= 4 {
vlq_encode_unchecked(&mut buf, segment[1] - prev_source);
prev_source = segment[1];
vlq_encode_unchecked(&mut buf, segment[2] - prev_original_line);
prev_original_line = segment[2];
vlq_encode_unchecked(&mut buf, segment[3] - prev_original_column);
prev_original_column = segment[3];
if segment.len() >= 5 {
vlq_encode_unchecked(&mut buf, segment[4] - prev_name);
prev_name = segment[4];
}
}
}
}
}
debug_assert!(buf.is_ascii());
unsafe { String::from_utf8_unchecked(buf) }
}
#[cfg(feature = "parallel")]
fn encode_line_to_bytes(
segments: &[crate::Segment],
init_source: i64,
init_original_line: i64,
init_original_column: i64,
init_name: i64,
) -> Vec<u8> {
let mut buf = Vec::with_capacity(segments.len() * 7 * 5);
let mut prev_generated_column: i64 = 0;
let mut prev_source = init_source;
let mut prev_original_line = init_original_line;
let mut prev_original_column = init_original_column;
let mut prev_name = init_name;
let mut wrote_segment = false;
for segment in segments {
if segment.is_empty() {
continue;
}
if wrote_segment {
buf.push(b',');
}
wrote_segment = true;
unsafe {
vlq_encode_unchecked(&mut buf, segment[0] - prev_generated_column);
prev_generated_column = segment[0];
if segment.len() >= 4 {
vlq_encode_unchecked(&mut buf, segment[1] - prev_source);
prev_source = segment[1];
vlq_encode_unchecked(&mut buf, segment[2] - prev_original_line);
prev_original_line = segment[2];
vlq_encode_unchecked(&mut buf, segment[3] - prev_original_column);
prev_original_column = segment[3];
if segment.len() >= 5 {
vlq_encode_unchecked(&mut buf, segment[4] - prev_name);
prev_name = segment[4];
}
}
}
}
buf
}
#[cfg(feature = "parallel")]
pub fn encode_parallel(mappings: &SourceMapMappings) -> String {
use rayon::prelude::*;
if mappings.is_empty() {
return String::new();
}
let total_segments: usize = mappings.iter().map(|l| l.len()).sum();
if mappings.len() < 1024 || total_segments < 4096 {
return encode(mappings);
}
let mut states: Vec<(i64, i64, i64, i64)> = Vec::with_capacity(mappings.len());
let mut prev_source: i64 = 0;
let mut prev_original_line: i64 = 0;
let mut prev_original_column: i64 = 0;
let mut prev_name: i64 = 0;
for line in mappings.iter() {
states.push((prev_source, prev_original_line, prev_original_column, prev_name));
for segment in line.iter() {
if segment.len() >= 4 {
prev_source = segment[1];
prev_original_line = segment[2];
prev_original_column = segment[3];
if segment.len() >= 5 {
prev_name = segment[4];
}
}
}
}
let encoded_lines: Vec<Vec<u8>> = mappings
.par_iter()
.zip(states.par_iter())
.map(|(line, &(src, ol, oc, name))| encode_line_to_bytes(line, src, ol, oc, name))
.collect();
let total_len: usize =
encoded_lines.iter().map(|l| l.len()).sum::<usize>() + encoded_lines.len() - 1;
let mut buf: Vec<u8> = Vec::with_capacity(total_len);
for (i, line_bytes) in encoded_lines.iter().enumerate() {
if i > 0 {
buf.push(b';');
}
buf.extend_from_slice(line_bytes);
}
debug_assert!(buf.is_ascii());
unsafe { String::from_utf8_unchecked(buf) }
}