use std::sync::Arc;
use crate::diagnostic::DiagnosticCode;
use crate::snapshot::error::{DecodeError, DecodeErrorCode};
use crate::snapshot::format::{
read_u16_le, read_u32_le, SectionKind, DIAGNOSTIC_LABEL_RECORD_SIZE, DIAGNOSTIC_RECORD_SIZE,
EDGE_KIND_NODE, EDGE_KIND_TOKEN, EDGE_RECORD_SIZE, HEADER_SIZE, NODE_RECORD_SIZE, NONE_REF,
ROOT_RECORD_SIZE, SECTION_ALIGNMENT, SECTION_FLAG_REQUIRED, SECTION_RECORD_SIZE,
SNAPSHOT_FEATURE_FLAGS, SNAPSHOT_MAGIC, SNAPSHOT_MAJOR_VERSION, SNAPSHOT_MINOR_VERSION,
SOURCE_RECORD_SIZE, STRING_OFFSET_RECORD_SIZE, TOKEN_RECORD_SIZE, TRIVIA_RECORD_SIZE,
};
use crate::snapshot::view::{SectionIndex, SectionSlice, SnapshotView, SnapshotViewOwned};
use crate::syntax_kind::SyntaxKind;
pub fn decode_snapshot(bytes: &[u8]) -> Result<SnapshotView<'_>, DecodeError> {
let sections = validate_and_build_index(bytes)?;
Ok(SnapshotView::from_validated(bytes, sections))
}
pub fn decode_snapshot_owned(
bytes: impl Into<Arc<[u8]>>,
) -> Result<SnapshotViewOwned, DecodeError> {
let arc = bytes.into();
let sections = validate_and_build_index(&arc)?;
Ok(SnapshotViewOwned::from_validated(arc, sections))
}
pub(crate) fn validate_and_build_index(bytes: &[u8]) -> Result<SectionIndex, DecodeError> {
if (bytes.len() as u64) < HEADER_SIZE as u64 {
return Err(DecodeError::new(DecodeErrorCode::BufferTooShort));
}
if bytes[0..8] != SNAPSHOT_MAGIC {
return Err(DecodeError::new(DecodeErrorCode::InvalidMagic));
}
let major = read_u16_le(bytes, 8);
let minor = read_u16_le(bytes, 10);
if major != SNAPSHOT_MAJOR_VERSION {
return Err(DecodeError::new(DecodeErrorCode::UnsupportedMajorVersion));
}
if minor != SNAPSHOT_MINOR_VERSION {
return Err(DecodeError::new(DecodeErrorCode::UnsupportedMinorVersion));
}
let feature_flags = read_u32_le(bytes, 12);
if feature_flags != SNAPSHOT_FEATURE_FLAGS {
return Err(DecodeError::new(DecodeErrorCode::InvalidFeatureFlags));
}
let header_len = read_u32_le(bytes, 16);
if header_len != HEADER_SIZE {
return Err(DecodeError::new(DecodeErrorCode::InvalidHeaderLength));
}
let section_table_offset = read_u32_le(bytes, 20);
if section_table_offset != HEADER_SIZE {
return Err(DecodeError::new(DecodeErrorCode::InvalidHeaderLength));
}
let section_count = read_u16_le(bytes, 24);
let reserved = read_u16_le(bytes, 26);
if reserved != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidReservedField));
}
let reserved_tail = read_u32_le(bytes, 28);
if reserved_tail != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidReservedField));
}
let section_table_byte_len = (section_count as u64) * (SECTION_RECORD_SIZE as u64);
let section_table_end = (HEADER_SIZE as u64).saturating_add(section_table_byte_len);
if section_table_end > bytes.len() as u64 {
return Err(DecodeError::new(DecodeErrorCode::SectionTableOutOfBounds));
}
let mut records: Vec<DecodedSection> = Vec::with_capacity(section_count as usize);
let mut seen_kinds: u32 = 0;
for i in 0..section_count {
let rec_offset = HEADER_SIZE as usize + (i as usize) * (SECTION_RECORD_SIZE as usize);
let kind_raw = read_u16_le(bytes, rec_offset);
let kind = SectionKind::from_u16(kind_raw).ok_or_else(|| {
DecodeError::new(DecodeErrorCode::UnknownSection).with_index(i as u32)
})?;
let bit = 1u32 << (kind.as_u16() as u32);
if (seen_kinds & bit) != 0 {
return Err(DecodeError::new(DecodeErrorCode::DuplicateSection)
.with_section(kind)
.with_index(i as u32));
}
seen_kinds |= bit;
let flags = read_u16_le(bytes, rec_offset + 2);
let offset = read_u32_le(bytes, rec_offset + 4);
let byte_len = read_u32_le(bytes, rec_offset + 8);
let count = read_u32_le(bytes, rec_offset + 12);
let record_size = read_u16_le(bytes, rec_offset + 14 + 2);
let alignment = bytes[rec_offset + 18];
let reserved = bytes[rec_offset + 19];
if reserved != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidReservedField)
.with_section(kind)
.with_index(i as u32));
}
if alignment != SECTION_ALIGNMENT as u8 {
return Err(DecodeError::new(DecodeErrorCode::InvalidSectionAlignment)
.with_section(kind)
.with_index(i as u32));
}
if record_size != kind.record_size() {
return Err(DecodeError::new(DecodeErrorCode::InvalidRecordSize)
.with_section(kind)
.with_index(i as u32));
}
if (flags & !SECTION_FLAG_REQUIRED) != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidSectionFlags)
.with_section(kind)
.with_index(i as u32));
}
let required = (flags & SECTION_FLAG_REQUIRED) != 0;
if kind.is_core() && !required {
return Err(DecodeError::new(DecodeErrorCode::InvalidSectionFlags)
.with_section(kind)
.with_index(i as u32));
}
if !kind.is_core() && required {
return Err(DecodeError::new(DecodeErrorCode::InvalidSectionFlags)
.with_section(kind)
.with_index(i as u32));
}
if (offset % SECTION_ALIGNMENT) != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidSectionAlignment)
.with_section(kind)
.with_offset(offset));
}
let end = (offset as u64).saturating_add(byte_len as u64);
if end > bytes.len() as u64 {
return Err(DecodeError::new(DecodeErrorCode::InvalidSectionBounds)
.with_section(kind)
.with_offset(offset));
}
if record_size != 0 {
let expected = (count as u64) * (record_size as u64);
if expected != byte_len as u64 {
return Err(DecodeError::new(DecodeErrorCode::InvalidSectionCount)
.with_section(kind)
.with_offset(offset));
}
}
records.push(DecodedSection {
kind,
offset,
byte_len,
count,
});
}
for kind in [
SectionKind::Roots,
SectionKind::Sources,
SectionKind::Nodes,
SectionKind::Edges,
SectionKind::Tokens,
SectionKind::StringOffsets,
SectionKind::StringData,
] {
let bit = 1u32 << (kind.as_u16() as u32);
if (seen_kinds & bit) == 0 {
return Err(
DecodeError::new(DecodeErrorCode::MissingRequiredSection).with_section(kind)
);
}
}
let mut sorted_indexes: Vec<usize> = (0..records.len()).collect();
sorted_indexes.sort_by_key(|i| {
let rec = records[*i];
(rec.offset, rec.byte_len != 0)
});
let mut cursor = HEADER_SIZE + (section_count as u32) * SECTION_RECORD_SIZE;
for (sorted_pos, src_index) in sorted_indexes.iter().enumerate() {
let rec = records[*src_index];
if rec.offset < cursor {
if sorted_pos == 0 {
return Err(DecodeError::new(DecodeErrorCode::SectionTableOutOfBounds)
.with_section(rec.kind));
}
return Err(
DecodeError::new(DecodeErrorCode::OverlappingSection).with_section(rec.kind)
);
}
for byte in &bytes[cursor as usize..rec.offset as usize] {
if *byte != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidPadding)
.with_section(rec.kind)
.with_offset(cursor));
}
}
cursor = rec
.offset
.checked_add(rec.byte_len)
.ok_or_else(|| DecodeError::new(DecodeErrorCode::InvalidSectionBounds))?;
}
if (cursor as usize) != bytes.len() {
return Err(DecodeError::new(DecodeErrorCode::TrailingPadding).with_offset(cursor));
}
let mut roots = None;
let mut sources = None;
let mut nodes = None;
let mut edges = None;
let mut tokens = None;
let mut trivia = None;
let mut diagnostics = None;
let mut diagnostic_labels = None;
let mut string_offsets = None;
let mut string_data = None;
let mut source_text_data = None;
let mut extended_data = None;
for rec in &records {
let slice = SectionSlice {
offset: rec.offset,
byte_len: rec.byte_len,
count: rec.count,
};
match rec.kind {
SectionKind::Roots => roots = Some(slice),
SectionKind::Sources => sources = Some(slice),
SectionKind::Nodes => nodes = Some(slice),
SectionKind::Edges => edges = Some(slice),
SectionKind::Tokens => tokens = Some(slice),
SectionKind::Trivia => trivia = Some(slice),
SectionKind::Diagnostics => diagnostics = Some(slice),
SectionKind::DiagnosticLabels => diagnostic_labels = Some(slice),
SectionKind::StringOffsets => string_offsets = Some(slice),
SectionKind::StringData => string_data = Some(slice),
SectionKind::SourceTextData => source_text_data = Some(slice),
SectionKind::ExtendedData => extended_data = Some(slice),
}
}
let roots = roots.expect("required Roots section validated above");
let sources = sources.expect("required Sources section validated above");
let nodes = nodes.expect("required Nodes section validated above");
let edges = edges.expect("required Edges section validated above");
let tokens = tokens.expect("required Tokens section validated above");
let string_offsets = string_offsets.expect("required StringOffsets validated above");
let string_data = string_data.expect("required StringData validated above");
if roots.count < 1 {
return Err(
DecodeError::new(DecodeErrorCode::InvalidSectionCount).with_section(SectionKind::Roots)
);
}
if sources.count < 1 {
return Err(DecodeError::new(DecodeErrorCode::InvalidSectionCount)
.with_section(SectionKind::Sources));
}
if nodes.count < 1 {
return Err(
DecodeError::new(DecodeErrorCode::InvalidSectionCount).with_section(SectionKind::Nodes)
);
}
validate_string_offsets(bytes, string_offsets, string_data)?;
validate_source_records(bytes, sources, string_offsets.count, source_text_data)?;
validate_root_records(
bytes,
roots,
nodes.count,
sources.count,
diagnostics.map_or(0, |s| s.count),
)?;
validate_node_records(bytes, nodes, edges.count)?;
validate_edge_records(bytes, edges, nodes.count, tokens.count)?;
validate_token_records(bytes, tokens, sources.count, trivia.map_or(0, |s| s.count))?;
if let Some(t) = trivia {
validate_trivia_records(bytes, t, sources.count)?;
}
if let Some(d) = diagnostics {
validate_diagnostic_records(
bytes,
d,
sources.count,
string_offsets.count,
diagnostic_labels.map_or(0, |s| s.count),
)?;
}
if let Some(l) = diagnostic_labels {
validate_diagnostic_label_records(bytes, l, sources.count, string_offsets.count)?;
}
if let Some(ed) = extended_data {
if ed.byte_len > 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidExtendedData)
.with_section(SectionKind::ExtendedData)
.with_offset(ed.offset));
}
}
Ok(SectionIndex {
roots,
sources,
nodes,
edges,
tokens,
trivia,
diagnostics,
diagnostic_labels,
string_offsets,
string_data,
source_text_data,
extended_data,
})
}
#[derive(Debug, Clone, Copy)]
struct DecodedSection {
kind: SectionKind,
offset: u32,
byte_len: u32,
count: u32,
}
fn validate_string_offsets(
bytes: &[u8],
offsets: SectionSlice,
data: SectionSlice,
) -> Result<(), DecodeError> {
let data_start = data.offset as usize;
let data_end = data_start + data.byte_len as usize;
if core::str::from_utf8(&bytes[data_start..data_end]).is_err() {
return Err(
DecodeError::new(DecodeErrorCode::InvalidUtf8).with_section(SectionKind::StringData)
);
}
let mut prev_end: u32 = 0;
for i in 0..offsets.count {
let rec_offset =
offsets.offset as usize + (i as usize) * STRING_OFFSET_RECORD_SIZE as usize;
let off = read_u32_le(bytes, rec_offset);
let len = read_u32_le(bytes, rec_offset + 4);
if off < prev_end {
return Err(DecodeError::new(DecodeErrorCode::InvalidStringOffset)
.with_section(SectionKind::StringOffsets)
.with_index(i));
}
let end = off
.checked_add(len)
.ok_or_else(|| DecodeError::new(DecodeErrorCode::InvalidStringOffset))?;
if end > data.byte_len {
return Err(DecodeError::new(DecodeErrorCode::InvalidStringOffset)
.with_section(SectionKind::StringOffsets)
.with_index(i));
}
let abs_start = data_start + off as usize;
let abs_end = data_start + end as usize;
if core::str::from_utf8(&bytes[abs_start..abs_end]).is_err() {
return Err(DecodeError::new(DecodeErrorCode::InvalidUtf8)
.with_section(SectionKind::StringOffsets)
.with_index(i));
}
prev_end = end;
}
Ok(())
}
#[inline]
fn check_span(start: u32, end: u32, section: SectionKind, index: u32) -> Result<(), DecodeError> {
if start > end {
return Err(DecodeError::new(DecodeErrorCode::InvalidSpan)
.with_section(section)
.with_index(index));
}
Ok(())
}
fn check_string_ref(
raw: u32,
string_count: u32,
section: SectionKind,
index: u32,
) -> Result<(), DecodeError> {
if raw == NONE_REF {
return Ok(());
}
if raw >= string_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidStringRef)
.with_section(section)
.with_index(index));
}
Ok(())
}
fn validate_source_records(
bytes: &[u8],
sources: SectionSlice,
string_count: u32,
source_text_data: Option<SectionSlice>,
) -> Result<(), DecodeError> {
for i in 0..sources.count {
let rec_offset = sources.offset as usize + (i as usize) * SOURCE_RECORD_SIZE as usize;
let source_id = read_u32_le(bytes, rec_offset);
let path = read_u32_le(bytes, rec_offset + 4);
let locale = read_u32_le(bytes, rec_offset + 8);
let message = read_u32_le(bytes, rec_offset + 12);
let _base_offset = read_u32_le(bytes, rec_offset + 16);
let text_source = read_u32_le(bytes, rec_offset + 20);
let text_offset = read_u32_le(bytes, rec_offset + 24);
let text_len = read_u32_le(bytes, rec_offset + 28);
if source_id != i {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceRef)
.with_section(SectionKind::Sources)
.with_index(i));
}
check_string_ref(path, string_count, SectionKind::Sources, i)?;
check_string_ref(locale, string_count, SectionKind::Sources, i)?;
check_string_ref(message, string_count, SectionKind::Sources, i)?;
if text_source == NONE_REF {
if text_offset != 0 || text_len != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceTextRange)
.with_section(SectionKind::Sources)
.with_index(i));
}
} else {
if text_source != source_id {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceTextRange)
.with_section(SectionKind::Sources)
.with_index(i));
}
let Some(ed) = source_text_data else {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceTextRange)
.with_section(SectionKind::Sources)
.with_index(i));
};
let end = text_offset.checked_add(text_len).ok_or_else(|| {
DecodeError::new(DecodeErrorCode::InvalidSourceTextRange)
.with_section(SectionKind::Sources)
.with_index(i)
})?;
if end > ed.byte_len {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceTextRange)
.with_section(SectionKind::Sources)
.with_index(i));
}
let data_start = ed.offset as usize + text_offset as usize;
let data_end = data_start + text_len as usize;
if core::str::from_utf8(&bytes[data_start..data_end]).is_err() {
return Err(DecodeError::new(DecodeErrorCode::InvalidUtf8)
.with_section(SectionKind::SourceTextData)
.with_index(i));
}
}
}
Ok(())
}
fn validate_root_records(
bytes: &[u8],
roots: SectionSlice,
node_count: u32,
source_count: u32,
diag_count: u32,
) -> Result<(), DecodeError> {
for i in 0..roots.count {
let rec_offset = roots.offset as usize + (i as usize) * ROOT_RECORD_SIZE as usize;
let root_node = read_u32_le(bytes, rec_offset);
let source_id = read_u32_le(bytes, rec_offset + 4);
let diag_start = read_u32_le(bytes, rec_offset + 8);
let diag_n = read_u32_le(bytes, rec_offset + 12);
if root_node >= node_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidRootRef)
.with_section(SectionKind::Roots)
.with_index(i));
}
if source_id >= source_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceRef)
.with_section(SectionKind::Roots)
.with_index(i));
}
let diag_end = diag_start.checked_add(diag_n).ok_or_else(|| {
DecodeError::new(DecodeErrorCode::InvalidDiagnosticRange)
.with_section(SectionKind::Roots)
.with_index(i)
})?;
if diag_end > diag_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidDiagnosticRange)
.with_section(SectionKind::Roots)
.with_index(i));
}
}
Ok(())
}
fn validate_node_records(
bytes: &[u8],
nodes: SectionSlice,
edge_count: u32,
) -> Result<(), DecodeError> {
for i in 0..nodes.count {
let rec_offset = nodes.offset as usize + (i as usize) * NODE_RECORD_SIZE as usize;
let kind = read_u16_le(bytes, rec_offset);
let flags = read_u16_le(bytes, rec_offset + 2);
let span_start = read_u32_le(bytes, rec_offset + 4);
let span_end = read_u32_le(bytes, rec_offset + 8);
check_span(span_start, span_end, SectionKind::Nodes, i)?;
let first_child = read_u32_le(bytes, rec_offset + 12);
let child_count = read_u32_le(bytes, rec_offset + 16);
let data_ref = read_u32_le(bytes, rec_offset + 20);
if flags != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidReservedField)
.with_section(SectionKind::Nodes)
.with_index(i));
}
if syntax_kind_is_unknown(kind) {
return Err(DecodeError::new(DecodeErrorCode::UnknownSyntaxKind)
.with_section(SectionKind::Nodes)
.with_index(i));
}
let end = first_child.checked_add(child_count).ok_or_else(|| {
DecodeError::new(DecodeErrorCode::InvalidNodeRef)
.with_section(SectionKind::Nodes)
.with_index(i)
})?;
if end > edge_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidNodeRef)
.with_section(SectionKind::Nodes)
.with_index(i));
}
if data_ref != NONE_REF {
return Err(DecodeError::new(DecodeErrorCode::InvalidExtendedData)
.with_section(SectionKind::Nodes)
.with_index(i));
}
}
Ok(())
}
fn validate_edge_records(
bytes: &[u8],
edges: SectionSlice,
node_count: u32,
token_count: u32,
) -> Result<(), DecodeError> {
for i in 0..edges.count {
let rec_offset = edges.offset as usize + (i as usize) * EDGE_RECORD_SIZE as usize;
let kind = read_u16_le(bytes, rec_offset);
let flags = read_u16_le(bytes, rec_offset + 2);
let ref_id = read_u32_le(bytes, rec_offset + 4);
if flags != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidReservedField)
.with_section(SectionKind::Edges)
.with_index(i));
}
match kind {
EDGE_KIND_NODE => {
if ref_id >= node_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidNodeRef)
.with_section(SectionKind::Edges)
.with_index(i));
}
}
EDGE_KIND_TOKEN => {
if ref_id >= token_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidTokenRef)
.with_section(SectionKind::Edges)
.with_index(i));
}
}
_ => {
return Err(DecodeError::new(DecodeErrorCode::InvalidEdgeKind)
.with_section(SectionKind::Edges)
.with_index(i));
}
}
}
Ok(())
}
fn validate_token_records(
bytes: &[u8],
tokens: SectionSlice,
source_count: u32,
trivia_count: u32,
) -> Result<(), DecodeError> {
for i in 0..tokens.count {
let rec_offset = tokens.offset as usize + (i as usize) * TOKEN_RECORD_SIZE as usize;
let kind = read_u16_le(bytes, rec_offset);
let flags = read_u16_le(bytes, rec_offset + 2);
let span_start = read_u32_le(bytes, rec_offset + 4);
let span_end = read_u32_le(bytes, rec_offset + 8);
check_span(span_start, span_end, SectionKind::Tokens, i)?;
let source_id = read_u32_le(bytes, rec_offset + 12);
let lead_start = read_u32_le(bytes, rec_offset + 16);
let lead_count = read_u32_le(bytes, rec_offset + 20);
let trail_start = read_u32_le(bytes, rec_offset + 24);
let trail_count = read_u32_le(bytes, rec_offset + 28);
let reserved_tail = read_u32_le(bytes, rec_offset + 32);
if flags != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidReservedField)
.with_section(SectionKind::Tokens)
.with_index(i));
}
if reserved_tail != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidReservedField)
.with_section(SectionKind::Tokens)
.with_index(i));
}
if syntax_kind_is_unknown(kind) {
return Err(DecodeError::new(DecodeErrorCode::UnknownSyntaxKind)
.with_section(SectionKind::Tokens)
.with_index(i));
}
if source_id >= source_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceRef)
.with_section(SectionKind::Tokens)
.with_index(i));
}
for (start, count) in [(lead_start, lead_count), (trail_start, trail_count)] {
let end = start.checked_add(count).ok_or_else(|| {
DecodeError::new(DecodeErrorCode::InvalidTriviaRef)
.with_section(SectionKind::Tokens)
.with_index(i)
})?;
if end > trivia_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidTriviaRef)
.with_section(SectionKind::Tokens)
.with_index(i));
}
}
}
Ok(())
}
fn validate_trivia_records(
bytes: &[u8],
trivia: SectionSlice,
source_count: u32,
) -> Result<(), DecodeError> {
for i in 0..trivia.count {
let rec_offset = trivia.offset as usize + (i as usize) * TRIVIA_RECORD_SIZE as usize;
let kind = read_u16_le(bytes, rec_offset);
let flags = read_u16_le(bytes, rec_offset + 2);
let span_start = read_u32_le(bytes, rec_offset + 4);
let span_end = read_u32_le(bytes, rec_offset + 8);
check_span(span_start, span_end, SectionKind::Trivia, i)?;
let source_id = read_u32_le(bytes, rec_offset + 12);
if flags != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidReservedField)
.with_section(SectionKind::Trivia)
.with_index(i));
}
if syntax_kind_is_unknown(kind) {
return Err(DecodeError::new(DecodeErrorCode::UnknownSyntaxKind)
.with_section(SectionKind::Trivia)
.with_index(i));
}
if source_id >= source_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceRef)
.with_section(SectionKind::Trivia)
.with_index(i));
}
}
Ok(())
}
fn validate_diagnostic_records(
bytes: &[u8],
diags: SectionSlice,
source_count: u32,
string_count: u32,
label_count: u32,
) -> Result<(), DecodeError> {
for i in 0..diags.count {
let rec_offset = diags.offset as usize + (i as usize) * DIAGNOSTIC_RECORD_SIZE as usize;
let source_id = read_u32_le(bytes, rec_offset);
let span_start = read_u32_le(bytes, rec_offset + 4);
let span_end = read_u32_le(bytes, rec_offset + 8);
check_span(span_start, span_end, SectionKind::Diagnostics, i)?;
let severity = bytes[rec_offset + 12];
let reserved = bytes[rec_offset + 13];
let code = read_u16_le(bytes, rec_offset + 14);
let message = read_u32_le(bytes, rec_offset + 16);
let label_start = read_u32_le(bytes, rec_offset + 20);
let lc = read_u32_le(bytes, rec_offset + 24);
if reserved != 0 {
return Err(DecodeError::new(DecodeErrorCode::InvalidReservedField)
.with_section(SectionKind::Diagnostics)
.with_index(i));
}
if severity > 3 {
return Err(DecodeError::new(DecodeErrorCode::InvalidDiagnosticSeverity)
.with_section(SectionKind::Diagnostics)
.with_index(i));
}
if diagnostic_code_is_unknown(code) {
return Err(DecodeError::new(DecodeErrorCode::UnknownDiagnosticCode)
.with_section(SectionKind::Diagnostics)
.with_index(i));
}
if source_id >= source_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceRef)
.with_section(SectionKind::Diagnostics)
.with_index(i));
}
check_string_ref(message, string_count, SectionKind::Diagnostics, i)?;
let end = label_start.checked_add(lc).ok_or_else(|| {
DecodeError::new(DecodeErrorCode::InvalidDiagnosticRange)
.with_section(SectionKind::Diagnostics)
.with_index(i)
})?;
if end > label_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidDiagnosticRange)
.with_section(SectionKind::Diagnostics)
.with_index(i));
}
}
Ok(())
}
fn validate_diagnostic_label_records(
bytes: &[u8],
labels: SectionSlice,
source_count: u32,
string_count: u32,
) -> Result<(), DecodeError> {
for i in 0..labels.count {
let rec_offset =
labels.offset as usize + (i as usize) * DIAGNOSTIC_LABEL_RECORD_SIZE as usize;
let source_id = read_u32_le(bytes, rec_offset);
let span_start = read_u32_le(bytes, rec_offset + 4);
let span_end = read_u32_le(bytes, rec_offset + 8);
check_span(span_start, span_end, SectionKind::DiagnosticLabels, i)?;
let message = read_u32_le(bytes, rec_offset + 12);
if source_id >= source_count {
return Err(DecodeError::new(DecodeErrorCode::InvalidSourceRef)
.with_section(SectionKind::DiagnosticLabels)
.with_index(i));
}
check_string_ref(message, string_count, SectionKind::DiagnosticLabels, i)?;
}
Ok(())
}
fn syntax_kind_is_unknown(value: u16) -> bool {
matches!(value, 0) || syntax_kind_from_u16(value).is_none()
}
pub(crate) fn syntax_kind_from_u16(value: u16) -> Option<SyntaxKind> {
Some(match value {
v if v == SyntaxKind::Root.as_u16() => SyntaxKind::Root,
v if v == SyntaxKind::SimpleMessage.as_u16() => SyntaxKind::SimpleMessage,
v if v == SyntaxKind::ComplexMessage.as_u16() => SyntaxKind::ComplexMessage,
v if v == SyntaxKind::Pattern.as_u16() => SyntaxKind::Pattern,
v if v == SyntaxKind::Text.as_u16() => SyntaxKind::Text,
v if v == SyntaxKind::QuotedPattern.as_u16() => SyntaxKind::QuotedPattern,
v if v == SyntaxKind::Placeholder.as_u16() => SyntaxKind::Placeholder,
v if v == SyntaxKind::LiteralExpression.as_u16() => SyntaxKind::LiteralExpression,
v if v == SyntaxKind::VariableExpression.as_u16() => SyntaxKind::VariableExpression,
v if v == SyntaxKind::FunctionExpression.as_u16() => SyntaxKind::FunctionExpression,
v if v == SyntaxKind::Function.as_u16() => SyntaxKind::Function,
v if v == SyntaxKind::Option.as_u16() => SyntaxKind::Option,
v if v == SyntaxKind::Attribute.as_u16() => SyntaxKind::Attribute,
v if v == SyntaxKind::LocalDeclaration.as_u16() => SyntaxKind::LocalDeclaration,
v if v == SyntaxKind::InputDeclaration.as_u16() => SyntaxKind::InputDeclaration,
v if v == SyntaxKind::ComplexBody.as_u16() => SyntaxKind::ComplexBody,
v if v == SyntaxKind::Matcher.as_u16() => SyntaxKind::Matcher,
v if v == SyntaxKind::Selector.as_u16() => SyntaxKind::Selector,
v if v == SyntaxKind::Variant.as_u16() => SyntaxKind::Variant,
v if v == SyntaxKind::VariantKey.as_u16() => SyntaxKind::VariantKey,
v if v == SyntaxKind::CatchAllKey.as_u16() => SyntaxKind::CatchAllKey,
v if v == SyntaxKind::Markup.as_u16() => SyntaxKind::Markup,
v if v == SyntaxKind::MarkupOpen.as_u16() => SyntaxKind::MarkupOpen,
v if v == SyntaxKind::MarkupStandalone.as_u16() => SyntaxKind::MarkupStandalone,
v if v == SyntaxKind::MarkupClose.as_u16() => SyntaxKind::MarkupClose,
v if v == SyntaxKind::QuotedLiteral.as_u16() => SyntaxKind::QuotedLiteral,
v if v == SyntaxKind::UnquotedLiteral.as_u16() => SyntaxKind::UnquotedLiteral,
v if v == SyntaxKind::Name.as_u16() => SyntaxKind::Name,
v if v == SyntaxKind::Identifier.as_u16() => SyntaxKind::Identifier,
v if v == SyntaxKind::Variable.as_u16() => SyntaxKind::Variable,
v if v == SyntaxKind::LeftBraceToken.as_u16() => SyntaxKind::LeftBraceToken,
v if v == SyntaxKind::RightBraceToken.as_u16() => SyntaxKind::RightBraceToken,
v if v == SyntaxKind::LeftDoubleBraceToken.as_u16() => SyntaxKind::LeftDoubleBraceToken,
v if v == SyntaxKind::RightDoubleBraceToken.as_u16() => SyntaxKind::RightDoubleBraceToken,
v if v == SyntaxKind::DotToken.as_u16() => SyntaxKind::DotToken,
v if v == SyntaxKind::AtToken.as_u16() => SyntaxKind::AtToken,
v if v == SyntaxKind::PipeToken.as_u16() => SyntaxKind::PipeToken,
v if v == SyntaxKind::EqualsToken.as_u16() => SyntaxKind::EqualsToken,
v if v == SyntaxKind::ColonToken.as_u16() => SyntaxKind::ColonToken,
v if v == SyntaxKind::DollarToken.as_u16() => SyntaxKind::DollarToken,
v if v == SyntaxKind::SlashToken.as_u16() => SyntaxKind::SlashToken,
v if v == SyntaxKind::StarToken.as_u16() => SyntaxKind::StarToken,
v if v == SyntaxKind::HashToken.as_u16() => SyntaxKind::HashToken,
v if v == SyntaxKind::InputKeyword.as_u16() => SyntaxKind::InputKeyword,
v if v == SyntaxKind::LocalKeyword.as_u16() => SyntaxKind::LocalKeyword,
v if v == SyntaxKind::MatchKeyword.as_u16() => SyntaxKind::MatchKeyword,
v if v == SyntaxKind::NameToken.as_u16() => SyntaxKind::NameToken,
v if v == SyntaxKind::TextToken.as_u16() => SyntaxKind::TextToken,
v if v == SyntaxKind::QuotedTextToken.as_u16() => SyntaxKind::QuotedTextToken,
v if v == SyntaxKind::EscapeToken.as_u16() => SyntaxKind::EscapeToken,
v if v == SyntaxKind::WhitespaceTrivia.as_u16() => SyntaxKind::WhitespaceTrivia,
v if v == SyntaxKind::BidiTrivia.as_u16() => SyntaxKind::BidiTrivia,
v if v == SyntaxKind::Error.as_u16() => SyntaxKind::Error,
v if v == SyntaxKind::Missing.as_u16() => SyntaxKind::Missing,
v if v == SyntaxKind::Unknown.as_u16() => SyntaxKind::Unknown,
_ => return None,
})
}
fn diagnostic_code_is_unknown(value: u16) -> bool {
diagnostic_code_from_u16_strict(value).is_none()
}
pub(crate) fn diagnostic_code_from_u16_strict(value: u16) -> Option<DiagnosticCode> {
Some(match value {
v if v == DiagnosticCode::Unspecified.as_u16() => DiagnosticCode::Unspecified,
v if v == DiagnosticCode::UnexpectedEndOfInput.as_u16() => {
DiagnosticCode::UnexpectedEndOfInput
}
v if v == DiagnosticCode::UnclosedExpression.as_u16() => DiagnosticCode::UnclosedExpression,
v if v == DiagnosticCode::UnclosedQuotedLiteral.as_u16() => {
DiagnosticCode::UnclosedQuotedLiteral
}
v if v == DiagnosticCode::UnclosedQuotedPattern.as_u16() => {
DiagnosticCode::UnclosedQuotedPattern
}
v if v == DiagnosticCode::InvalidDeclarationStart.as_u16() => {
DiagnosticCode::InvalidDeclarationStart
}
v if v == DiagnosticCode::InvalidMatcherSyntax.as_u16() => {
DiagnosticCode::InvalidMatcherSyntax
}
v if v == DiagnosticCode::InvalidVariantBoundary.as_u16() => {
DiagnosticCode::InvalidVariantBoundary
}
v if v == DiagnosticCode::InvalidMarkupBoundary.as_u16() => {
DiagnosticCode::InvalidMarkupBoundary
}
v if v == DiagnosticCode::MissingComplexBody.as_u16() => DiagnosticCode::MissingComplexBody,
v if v == DiagnosticCode::UnexpectedToken.as_u16() => DiagnosticCode::UnexpectedToken,
v if v == DiagnosticCode::SpanOverflow.as_u16() => DiagnosticCode::SpanOverflow,
v if v == DiagnosticCode::InvalidEscape.as_u16() => DiagnosticCode::InvalidEscape,
v if v == DiagnosticCode::AmbiguousMessageMode.as_u16() => {
DiagnosticCode::AmbiguousMessageMode
}
v if v == DiagnosticCode::MissingRequiredWhitespace.as_u16() => {
DiagnosticCode::MissingRequiredWhitespace
}
v if v == DiagnosticCode::MissingIdentifierName.as_u16() => {
DiagnosticCode::MissingIdentifierName
}
v if v == DiagnosticCode::InvalidInputDeclaration.as_u16() => {
DiagnosticCode::InvalidInputDeclaration
}
_ => return None,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn equal_offset_sort_places_empty_before_non_empty() {
let records = [
DecodedSection {
kind: SectionKind::StringData,
offset: 32,
byte_len: 16,
count: 0,
},
DecodedSection {
kind: SectionKind::StringOffsets,
offset: 32,
byte_len: 0,
count: 0,
},
];
let mut sorted: Vec<usize> = (0..records.len()).collect();
sorted.sort_by_key(|i| {
let rec = records[*i];
(rec.offset, rec.byte_len != 0)
});
assert_eq!(records[sorted[0]].kind, SectionKind::StringOffsets);
assert_eq!(records[sorted[1]].kind, SectionKind::StringData);
}
#[test]
fn check_span_accepts_zero_and_equal_endpoints() {
assert!(check_span(0, 0, SectionKind::Nodes, 0).is_ok());
assert!(check_span(5, 5, SectionKind::Nodes, 0).is_ok());
assert!(check_span(2, 7, SectionKind::Nodes, 0).is_ok());
}
#[test]
fn check_span_rejects_inverted_spans() {
let err = check_span(5, 4, SectionKind::Tokens, 3).unwrap_err();
assert_eq!(err.code, DecodeErrorCode::InvalidSpan);
assert_eq!(err.section, Some(SectionKind::Tokens));
assert_eq!(err.index, Some(3));
}
}