use core::ffi::c_void;
use core::fmt;
use core::sync::atomic::AtomicU32;
use crate::Ordinal;
use crate::mapping;
use crate::vlq::VLQ;
use crate::{
BakeSourceProvider, DevServerSourceProvider, InternalSourceMap, Mapping, ParseUrl,
ParseUrlResultHint, SourceMapLoadHint, SourceProviderMap,
};
pub struct ParsedSourceMap {
pub ref_count: AtomicU32,
pub input_line_count: usize,
pub mappings: mapping::List,
pub internal: Option<InternalSourceMap>,
pub external_source_names: Vec<Box<[u8]>>,
pub underlying_provider: SourceContentPtr,
pub is_standalone_module_graph: bool,
}
impl Drop for ParsedSourceMap {
fn drop(&mut self) {
if let Some(ism) = self.internal.take() {
if !self.is_standalone_module_graph {
ism.free_owned();
}
}
}
}
impl Default for ParsedSourceMap {
fn default() -> Self {
Self {
ref_count: AtomicU32::new(1),
input_line_count: 0,
mappings: mapping::List::default(),
internal: None,
external_source_names: Vec::new(),
underlying_provider: SourceContentPtr::NONE,
is_standalone_module_graph: false,
}
}
}
#[repr(u8)] #[derive(Copy, Clone, Eq, PartialEq)]
enum SourceProviderKind {
Zig = 0,
Bake = 1,
DevServer = 2,
}
pub enum AnySourceProvider {
Zig(*mut SourceProviderMap),
Bake(*mut BakeSourceProvider),
DevServer(*mut DevServerSourceProvider),
}
impl AnySourceProvider {
pub fn ptr(&self) -> *mut c_void {
match self {
AnySourceProvider::Zig(p) => (*p).cast::<c_void>(),
AnySourceProvider::Bake(p) => (*p).cast::<c_void>(),
AnySourceProvider::DevServer(p) => (*p).cast::<c_void>(),
}
}
pub fn get_source_map(
&self,
source_filename: &[u8],
load_hint: SourceMapLoadHint,
result: ParseUrlResultHint,
) -> Option<ParseUrl> {
match self {
AnySourceProvider::Zig(p) => unsafe {
(**p).get_source_map(source_filename, load_hint, result)
},
AnySourceProvider::Bake(p) => unsafe {
(**p).get_source_map(source_filename, load_hint, result)
},
AnySourceProvider::DevServer(p) => unsafe {
(**p).get_source_map(source_filename, load_hint, result)
},
}
}
}
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct SourceContentPtr(u64);
impl SourceContentPtr {
const LOAD_HINT_SHIFT: u32 = 0;
const LOAD_HINT_MASK: u64 = 0b11;
const KIND_SHIFT: u32 = 2;
const KIND_MASK: u64 = 0b11;
const DATA_SHIFT: u32 = 4;
const DATA_MASK: u64 = (1u64 << 60) - 1;
pub const NONE: SourceContentPtr = SourceContentPtr(0);
const fn new(load_hint: SourceMapLoadHint, kind: SourceProviderKind, data: u64) -> Self {
Self(
((load_hint as u64) & Self::LOAD_HINT_MASK) << Self::LOAD_HINT_SHIFT
| ((kind as u64) & Self::KIND_MASK) << Self::KIND_SHIFT
| (data & Self::DATA_MASK) << Self::DATA_SHIFT,
)
}
#[inline]
pub fn load_hint(self) -> SourceMapLoadHint {
match ((self.0 >> Self::LOAD_HINT_SHIFT) & Self::LOAD_HINT_MASK) as u8 {
1 => SourceMapLoadHint::IsInlineMap,
2 => SourceMapLoadHint::IsExternalMap,
v => {
debug_assert_eq!(v, 0);
SourceMapLoadHint::None
}
}
}
#[inline]
pub fn set_load_hint(&mut self, hint: SourceMapLoadHint) {
self.0 = (self.0 & !(Self::LOAD_HINT_MASK << Self::LOAD_HINT_SHIFT))
| ((hint as u64) & Self::LOAD_HINT_MASK) << Self::LOAD_HINT_SHIFT;
}
#[inline]
fn kind(self) -> SourceProviderKind {
match ((self.0 >> Self::KIND_SHIFT) & Self::KIND_MASK) as u8 {
0 => SourceProviderKind::Zig,
1 => SourceProviderKind::Bake,
v => {
debug_assert_eq!(v, 2);
SourceProviderKind::DevServer
}
}
}
#[inline]
pub fn data(self) -> u64 {
(self.0 >> Self::DATA_SHIFT) & Self::DATA_MASK
}
pub fn from_provider(p: *const SourceProviderMap) -> SourceContentPtr {
Self::new(
SourceMapLoadHint::None,
SourceProviderKind::Zig,
u64::try_from(p as usize).expect("int cast"),
)
}
pub fn from_bake_provider(p: *mut BakeSourceProvider) -> SourceContentPtr {
Self::new(
SourceMapLoadHint::None,
SourceProviderKind::Bake,
u64::try_from(p as usize).expect("int cast"),
)
}
pub fn from_dev_server_provider(p: *const DevServerSourceProvider) -> SourceContentPtr {
Self::new(
SourceMapLoadHint::None,
SourceProviderKind::DevServer,
u64::try_from(p as usize).expect("int cast"),
)
}
pub fn provider(self) -> Option<AnySourceProvider> {
let data = self.data() as usize;
match self.kind() {
SourceProviderKind::Zig => Some(AnySourceProvider::Zig(data as *mut SourceProviderMap)),
SourceProviderKind::Bake => {
Some(AnySourceProvider::Bake(data as *mut BakeSourceProvider))
}
SourceProviderKind::DevServer => Some(AnySourceProvider::DevServer(
data as *mut DevServerSourceProvider,
)),
}
}
}
impl ParsedSourceMap {
#[inline]
pub unsafe fn ref_(this: *mut Self) {
unsafe { std::sync::Arc::increment_strong_count(this.cast_const()) };
}
#[inline]
pub unsafe fn deref(this: *mut Self) {
unsafe { std::sync::Arc::decrement_strong_count(this.cast_const()) };
}
pub fn from_internal(internal: InternalSourceMap) -> Self {
Self {
ref_count: AtomicU32::new(1),
input_line_count: internal.input_line_count(),
mappings: mapping::List::default(),
internal: Some(internal),
external_source_names: Vec::new(),
underlying_provider: SourceContentPtr::NONE,
is_standalone_module_graph: false,
}
}
pub fn is_external(&self) -> bool {
!self.external_source_names.is_empty()
}
pub fn find_mapping(&self, line: Ordinal, column: Ordinal) -> Option<Mapping> {
if let Some(ism) = &self.internal {
return ism.find(line, column);
}
self.mappings.find(line, column)
}
pub fn internal_cursor(&self) -> Option<crate::internal_source_map::Cursor> {
self.internal.as_ref().map(|ism| ism.cursor())
}
pub fn standalone_module_graph_data(&self) -> *mut crate::SerializedSourceMap::Loaded {
debug_assert!(self.is_standalone_module_graph);
self.underlying_provider.data() as usize as *mut crate::SerializedSourceMap::Loaded
}
pub fn memory_cost(&self) -> usize {
let mappings_cost = if let Some(ism) = &self.internal {
ism.memory_cost()
} else {
self.mappings.memory_cost()
};
core::mem::size_of::<ParsedSourceMap>()
+ mappings_cost
+ self.external_source_names.len() * core::mem::size_of::<Box<[u8]>>()
}
pub fn write_vlqs<W: bun_io::Write + ?Sized>(&self, writer: &mut W) -> bun_io::Result<()> {
if let Some(ism) = &self.internal {
let mut buf = bun_core::MutableString::init_empty();
ism.append_vlq_to(&mut buf);
writer.write_all(buf.list.as_slice())?;
return Ok(());
}
let mut last_col: i32 = 0;
let mut last_src: i32 = 0;
let mut last_ol: i32 = 0;
let mut last_oc: i32 = 0;
let mut current_line: i32 = 0;
debug_assert_eq!(
self.mappings.generated().len(),
self.mappings.original().len()
);
debug_assert_eq!(
self.mappings.generated().len(),
self.mappings.source_index().len()
);
for (i, ((gn, orig), source_index)) in self
.mappings
.generated()
.iter()
.zip(self.mappings.original())
.zip(self.mappings.source_index())
.enumerate()
{
if current_line != gn.lines.zero_based() {
debug_assert!(gn.lines.zero_based() > current_line);
let inc = gn.lines.zero_based() - current_line;
writer.splat_byte_all(b';', usize::try_from(inc).expect("int cast"))?;
current_line = gn.lines.zero_based();
last_col = 0;
} else if i != 0 {
writer.write_byte(b',')?;
}
writer.write_all(VLQ::encode(gn.columns.zero_based() - last_col).slice())?;
last_col = gn.columns.zero_based();
writer.write_all(VLQ::encode(*source_index - last_src).slice())?;
last_src = *source_index;
writer.write_all(VLQ::encode(orig.lines.zero_based() - last_ol).slice())?;
last_ol = orig.lines.zero_based();
writer.write_all(VLQ::encode(orig.columns.zero_based() - last_oc).slice())?;
last_oc = orig.columns.zero_based();
}
Ok(())
}
pub fn format_vlqs(&self) -> VlqsFmt<'_> {
VlqsFmt(self)
}
}
pub struct VlqsFmt<'a>(&'a ParsedSourceMap);
impl<'a> fmt::Display for VlqsFmt<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut adapter = bun_io::FmtAdapter::new(f);
self.0.write_vlqs(&mut adapter).map_err(|_| fmt::Error)
}
}