use std::sync::Arc;
use crate::diagnostic::{DiagnosticCode, DiagnosticSeverity};
use crate::error::{OxMf2ErrorCode, SourceTextErrorCode};
use crate::snapshot::decoder::{diagnostic_code_from_u16_strict, syntax_kind_from_u16};
use crate::snapshot::format::{
read_u16_le, read_u32_le, RootId, SectionKind, StringId, DIAGNOSTIC_LABEL_RECORD_SIZE,
DIAGNOSTIC_RECORD_SIZE, EDGE_RECORD_SIZE, NODE_RECORD_SIZE, NONE_REF, ROOT_RECORD_SIZE,
SOURCE_RECORD_SIZE, STRING_OFFSET_RECORD_SIZE, TOKEN_RECORD_SIZE, TRIVIA_RECORD_SIZE,
};
use crate::span::{NodeId, SourceId, Span, TokenId, TriviaId};
use crate::syntax_kind::SyntaxKind;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SourceTextUnavailable {
NotIncluded,
SpanOutOfBounds,
}
impl core::fmt::Display for SourceTextUnavailable {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(match self {
Self::NotIncluded => {
"snapshot was encoded without source text (include_source_text = false)"
}
Self::SpanOutOfBounds => {
"span extends past the encoded source text or splits a UTF-8 scalar"
}
})
}
}
impl std::error::Error for SourceTextUnavailable {}
impl SourceTextUnavailable {
#[inline]
pub const fn code(self) -> SourceTextErrorCode {
match self {
Self::NotIncluded => SourceTextErrorCode::SourceTextNotIncluded,
Self::SpanOutOfBounds => SourceTextErrorCode::SourceTextSpanOutOfBounds,
}
}
#[inline]
pub const fn as_ox_mf2_error_code(self) -> OxMf2ErrorCode {
self.code().as_ox_mf2_error_code()
}
}
#[derive(Debug, Clone, Copy)]
pub struct SectionSlice {
pub offset: u32,
pub byte_len: u32,
pub count: u32,
}
#[derive(Debug, Clone, Copy)]
pub struct SectionIndex {
pub roots: SectionSlice,
pub sources: SectionSlice,
pub nodes: SectionSlice,
pub edges: SectionSlice,
pub tokens: SectionSlice,
pub trivia: Option<SectionSlice>,
pub diagnostics: Option<SectionSlice>,
pub diagnostic_labels: Option<SectionSlice>,
pub string_offsets: SectionSlice,
pub string_data: SectionSlice,
pub source_text_data: Option<SectionSlice>,
pub extended_data: Option<SectionSlice>,
}
#[derive(Debug, Clone, Copy)]
pub struct SectionView {
pub kind: SectionKind,
pub offset: u32,
pub byte_len: u32,
pub count: u32,
pub record_size: u16,
pub alignment: u8,
}
#[derive(Debug, Clone, Copy)]
pub struct SnapshotView<'a> {
bytes: &'a [u8],
sections: SectionIndex,
}
impl<'a> SnapshotView<'a> {
pub(crate) fn from_validated(bytes: &'a [u8], sections: SectionIndex) -> Self {
Self { bytes, sections }
}
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
pub fn sections(&self) -> &SectionIndex {
&self.sections
}
pub fn section(&self, kind: SectionKind) -> Option<SectionView> {
let slice = match kind {
SectionKind::Roots => Some(self.sections.roots),
SectionKind::Sources => Some(self.sections.sources),
SectionKind::Nodes => Some(self.sections.nodes),
SectionKind::Edges => Some(self.sections.edges),
SectionKind::Tokens => Some(self.sections.tokens),
SectionKind::Trivia => self.sections.trivia,
SectionKind::Diagnostics => self.sections.diagnostics,
SectionKind::DiagnosticLabels => self.sections.diagnostic_labels,
SectionKind::StringOffsets => Some(self.sections.string_offsets),
SectionKind::StringData => Some(self.sections.string_data),
SectionKind::SourceTextData => self.sections.source_text_data,
SectionKind::ExtendedData => self.sections.extended_data,
}?;
Some(SectionView {
kind,
offset: slice.offset,
byte_len: slice.byte_len,
count: slice.count,
record_size: kind.record_size(),
alignment: crate::snapshot::format::SECTION_ALIGNMENT as u8,
})
}
pub fn root_count(&self) -> u32 {
self.sections.roots.count
}
pub fn root(&self, id: RootId) -> Option<RootView<'a>> {
if id.raw() >= self.sections.roots.count {
return None;
}
Some(RootView {
bytes: self.bytes,
sections: self.sections,
id,
})
}
pub fn node_count(&self) -> u32 {
self.sections.nodes.count
}
pub fn token_count(&self) -> u32 {
self.sections.tokens.count
}
pub fn trivia_count(&self) -> u32 {
self.sections.trivia.map_or(0, |s| s.count)
}
pub fn node(&self, id: NodeId) -> Option<NodeView<'a>> {
if id.is_none() || id.raw() >= self.sections.nodes.count {
return None;
}
Some(self.node_unchecked(id))
}
pub(crate) fn node_unchecked(&self, id: NodeId) -> NodeView<'a> {
debug_assert!(!id.is_none());
debug_assert!(id.raw() < self.sections.nodes.count);
NodeView {
bytes: self.bytes,
sections: self.sections,
id,
}
}
pub fn token(&self, id: TokenId) -> Option<TokenView<'a>> {
if id.is_none() || id.raw() >= self.sections.tokens.count {
return None;
}
Some(TokenView {
bytes: self.bytes,
sections: self.sections,
id,
})
}
pub fn trivia(&self, id: TriviaId) -> Option<TriviaView<'a>> {
let trivia = self.sections.trivia?;
if id.is_none() || id.raw() >= trivia.count {
return None;
}
Some(TriviaView {
bytes: self.bytes,
sections: self.sections,
id,
})
}
pub fn source_count(&self) -> u32 {
self.sections.sources.count
}
pub fn source(&self, id: SourceId) -> Option<SourceView<'a>> {
if id.is_none() || id.raw() >= self.sections.sources.count {
return None;
}
Some(SourceView {
bytes: self.bytes,
sections: self.sections,
id,
})
}
pub fn string(&self, id: StringId) -> Option<&'a str> {
if id.is_none() {
return None;
}
string_slice(self.bytes, &self.sections, id.raw())
}
pub fn diagnostic_count(&self) -> u32 {
self.sections.diagnostics.map_or(0, |s| s.count)
}
pub fn diagnostic(&self, index: u32) -> Option<DiagnosticRecordView<'a>> {
let diags = self.sections.diagnostics?;
if index >= diags.count {
return None;
}
Some(DiagnosticRecordView {
bytes: self.bytes,
sections: self.sections,
index,
})
}
}
#[derive(Debug, Clone)]
pub struct SnapshotViewOwned {
bytes: Arc<[u8]>,
sections: SectionIndex,
}
impl SnapshotViewOwned {
pub(crate) fn from_validated(bytes: Arc<[u8]>, sections: SectionIndex) -> Self {
Self { bytes, sections }
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn view(&self) -> SnapshotView<'_> {
SnapshotView::from_validated(&self.bytes, self.sections)
}
pub fn bytes_arc(&self) -> Arc<[u8]> {
Arc::clone(&self.bytes)
}
pub fn section(&self, kind: SectionKind) -> Option<SectionView> {
self.view().section(kind)
}
pub fn sections(&self) -> SectionIndex {
self.sections
}
}
#[derive(Debug, Clone, Copy)]
pub struct RootView<'a> {
bytes: &'a [u8],
sections: SectionIndex,
id: RootId,
}
impl<'a> RootView<'a> {
pub fn id(&self) -> RootId {
self.id
}
pub fn root_node(&self) -> NodeId {
let off = self.sections.roots.offset as usize + self.id.index() * ROOT_RECORD_SIZE as usize;
NodeId::new(read_u32_le(self.bytes, off))
}
pub fn source_id(&self) -> SourceId {
let off = self.sections.roots.offset as usize + self.id.index() * ROOT_RECORD_SIZE as usize;
SourceId::new(read_u32_le(self.bytes, off + 4))
}
pub fn diagnostic_range(&self) -> (u32, u32) {
let off = self.sections.roots.offset as usize + self.id.index() * ROOT_RECORD_SIZE as usize;
(
read_u32_le(self.bytes, off + 8),
read_u32_le(self.bytes, off + 12),
)
}
pub fn node(&self) -> NodeView<'a> {
NodeView {
bytes: self.bytes,
sections: self.sections,
id: self.root_node(),
}
}
pub fn diagnostics(&self) -> DiagnosticIter<'a> {
let (start, count) = self.diagnostic_range();
DiagnosticIter {
bytes: self.bytes,
sections: self.sections,
cursor: start,
remaining: count,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct SourceView<'a> {
bytes: &'a [u8],
sections: SectionIndex,
id: SourceId,
}
impl<'a> SourceView<'a> {
pub fn id(&self) -> SourceId {
self.id
}
fn base(&self) -> usize {
self.sections.sources.offset as usize + self.id.index() * SOURCE_RECORD_SIZE as usize
}
pub fn path(&self) -> Option<&'a str> {
let raw = read_u32_le(self.bytes, self.base() + 4);
string_slice(self.bytes, &self.sections, raw)
}
pub fn locale(&self) -> Option<&'a str> {
let raw = read_u32_le(self.bytes, self.base() + 8);
string_slice(self.bytes, &self.sections, raw)
}
pub fn message_id(&self) -> Option<&'a str> {
let raw = read_u32_le(self.bytes, self.base() + 12);
string_slice(self.bytes, &self.sections, raw)
}
pub fn base_offset(&self) -> u32 {
read_u32_le(self.bytes, self.base() + 16)
}
pub fn text(&self) -> Option<&'a str> {
let base = self.base();
let text_source = read_u32_le(self.bytes, base + 20);
if text_source == NONE_REF {
return None;
}
let offset = read_u32_le(self.bytes, base + 24);
let len = read_u32_le(self.bytes, base + 28);
let ed = self.sections.source_text_data?;
let start = ed.offset as usize + offset as usize;
let end = start + len as usize;
core::str::from_utf8(&self.bytes[start..end]).ok()
}
pub fn source_slice(&self, span: Span) -> Result<&'a str, SourceTextUnavailable> {
let text = self.text().ok_or(SourceTextUnavailable::NotIncluded)?;
slice_source_text(text, span)
}
pub fn source_slice_with_external_text(
&self,
span: Span,
external_source_text: &'a str,
) -> Result<&'a str, SourceTextUnavailable> {
let text = self.text().unwrap_or(external_source_text);
slice_source_text(text, span)
}
}
fn slice_source_text(text: &str, span: Span) -> Result<&str, SourceTextUnavailable> {
if span.start > span.end {
return Err(SourceTextUnavailable::SpanOutOfBounds);
}
let len_u32 = text.len() as u32;
if span.end > len_u32 {
return Err(SourceTextUnavailable::SpanOutOfBounds);
}
let start = span.start as usize;
let end = span.end as usize;
text.get(start..end)
.ok_or(SourceTextUnavailable::SpanOutOfBounds)
}
#[derive(Debug, Clone, Copy)]
pub struct NodeView<'a> {
bytes: &'a [u8],
sections: SectionIndex,
id: NodeId,
}
impl<'a> NodeView<'a> {
pub fn id(&self) -> NodeId {
self.id
}
fn base(&self) -> usize {
self.sections.nodes.offset as usize + self.id.index() * NODE_RECORD_SIZE as usize
}
pub fn kind(&self) -> SyntaxKind {
syntax_kind_from_u16(read_u16_le(self.bytes, self.base())).unwrap_or(SyntaxKind::Unknown)
}
pub fn span(&self) -> Span {
Span::new(
read_u32_le(self.bytes, self.base() + 4),
read_u32_le(self.bytes, self.base() + 8),
)
}
pub fn child_count(&self) -> u32 {
read_u32_le(self.bytes, self.base() + 16)
}
pub fn children(&self) -> ChildIter<'a> {
let start = read_u32_le(self.bytes, self.base() + 12);
let count = self.child_count();
ChildIter {
bytes: self.bytes,
sections: self.sections,
edge_start: start,
remaining: count,
}
}
pub fn child_at(&self, index: u32) -> Option<ChildView<'a>> {
if index >= self.child_count() {
return None;
}
let start = read_u32_le(self.bytes, self.base() + 12);
Some(read_child(self.bytes, &self.sections, start + index))
}
}
#[derive(Debug, Clone, Copy)]
pub enum ChildView<'a> {
Node(NodeView<'a>),
Token(TokenView<'a>),
}
#[derive(Debug, Clone)]
pub struct ChildIter<'a> {
bytes: &'a [u8],
sections: SectionIndex,
edge_start: u32,
remaining: u32,
}
impl<'a> Iterator for ChildIter<'a> {
type Item = ChildView<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let child = read_child(self.bytes, &self.sections, self.edge_start);
self.edge_start += 1;
self.remaining -= 1;
Some(child)
}
}
fn read_child<'a>(bytes: &'a [u8], sections: &SectionIndex, edge_index: u32) -> ChildView<'a> {
let off = sections.edges.offset as usize + edge_index as usize * EDGE_RECORD_SIZE as usize;
let kind = read_u16_le(bytes, off);
let ref_id = read_u32_le(bytes, off + 4);
if kind == crate::snapshot::format::EDGE_KIND_TOKEN {
ChildView::Token(TokenView {
bytes,
sections: *sections,
id: TokenId::new(ref_id),
})
} else {
ChildView::Node(NodeView {
bytes,
sections: *sections,
id: NodeId::new(ref_id),
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct TokenView<'a> {
bytes: &'a [u8],
sections: SectionIndex,
id: TokenId,
}
impl<'a> TokenView<'a> {
pub fn id(&self) -> TokenId {
self.id
}
fn base(&self) -> usize {
self.sections.tokens.offset as usize + self.id.index() * TOKEN_RECORD_SIZE as usize
}
pub fn kind(&self) -> SyntaxKind {
syntax_kind_from_u16(read_u16_le(self.bytes, self.base())).unwrap_or(SyntaxKind::Unknown)
}
pub fn span(&self) -> Span {
Span::new(
read_u32_le(self.bytes, self.base() + 4),
read_u32_le(self.bytes, self.base() + 8),
)
}
pub fn source_id(&self) -> SourceId {
SourceId::new(read_u32_le(self.bytes, self.base() + 12))
}
pub fn leading_trivia(&self) -> TriviaIter<'a> {
let start = read_u32_le(self.bytes, self.base() + 16);
let count = read_u32_le(self.bytes, self.base() + 20);
TriviaIter {
bytes: self.bytes,
sections: self.sections,
cursor: start,
remaining: count,
}
}
pub fn trailing_trivia(&self) -> TriviaIter<'a> {
let start = read_u32_le(self.bytes, self.base() + 24);
let count = read_u32_le(self.bytes, self.base() + 28);
TriviaIter {
bytes: self.bytes,
sections: self.sections,
cursor: start,
remaining: count,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct TriviaView<'a> {
bytes: &'a [u8],
sections: SectionIndex,
id: TriviaId,
}
impl TriviaView<'_> {
pub fn id(&self) -> TriviaId {
self.id
}
fn base(&self) -> usize {
let trivia = self.sections.trivia.expect("trivia view requires section");
trivia.offset as usize + self.id.index() * TRIVIA_RECORD_SIZE as usize
}
pub fn kind(&self) -> SyntaxKind {
syntax_kind_from_u16(read_u16_le(self.bytes, self.base())).unwrap_or(SyntaxKind::Unknown)
}
pub fn span(&self) -> Span {
Span::new(
read_u32_le(self.bytes, self.base() + 4),
read_u32_le(self.bytes, self.base() + 8),
)
}
pub fn source_id(&self) -> SourceId {
SourceId::new(read_u32_le(self.bytes, self.base() + 12))
}
}
#[derive(Debug, Clone)]
pub struct TriviaIter<'a> {
bytes: &'a [u8],
sections: SectionIndex,
cursor: u32,
remaining: u32,
}
impl<'a> Iterator for TriviaIter<'a> {
type Item = TriviaView<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let item = TriviaView {
bytes: self.bytes,
sections: self.sections,
id: TriviaId::new(self.cursor),
};
self.cursor += 1;
self.remaining -= 1;
Some(item)
}
}
#[derive(Debug, Clone, Copy)]
pub struct DiagnosticRecordView<'a> {
bytes: &'a [u8],
sections: SectionIndex,
index: u32,
}
#[derive(Debug, Clone)]
pub struct DiagnosticIter<'a> {
bytes: &'a [u8],
sections: SectionIndex,
cursor: u32,
remaining: u32,
}
impl<'a> Iterator for DiagnosticIter<'a> {
type Item = DiagnosticRecordView<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let item = DiagnosticRecordView {
bytes: self.bytes,
sections: self.sections,
index: self.cursor,
};
self.cursor += 1;
self.remaining -= 1;
Some(item)
}
}
impl<'a> DiagnosticRecordView<'a> {
fn base(&self) -> usize {
let diags = self
.sections
.diagnostics
.expect("diagnostic view requires section");
diags.offset as usize + self.index as usize * DIAGNOSTIC_RECORD_SIZE as usize
}
pub fn source_id(&self) -> SourceId {
SourceId::new(read_u32_le(self.bytes, self.base()))
}
pub fn span(&self) -> Span {
Span::new(
read_u32_le(self.bytes, self.base() + 4),
read_u32_le(self.bytes, self.base() + 8),
)
}
pub fn severity(&self) -> DiagnosticSeverity {
match self.bytes[self.base() + 12] {
0 => DiagnosticSeverity::Error,
1 => DiagnosticSeverity::Warning,
2 => DiagnosticSeverity::Information,
_ => DiagnosticSeverity::Hint,
}
}
pub fn code(&self) -> DiagnosticCode {
let raw = read_u16_le(self.bytes, self.base() + 14);
diagnostic_code_from_u16_strict(raw).unwrap_or(DiagnosticCode::Unspecified)
}
pub fn message(&self) -> Option<&'a str> {
let raw = read_u32_le(self.bytes, self.base() + 16);
string_slice(self.bytes, &self.sections, raw)
}
pub fn label_range(&self) -> (u32, u32) {
(
read_u32_le(self.bytes, self.base() + 20),
read_u32_le(self.bytes, self.base() + 24),
)
}
pub fn labels(&self) -> DiagnosticLabelIter<'a> {
let (start, count) = self.label_range();
DiagnosticLabelIter {
bytes: self.bytes,
sections: self.sections,
cursor: start,
remaining: count,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct DiagnosticLabelView<'a> {
bytes: &'a [u8],
sections: SectionIndex,
index: u32,
}
impl<'a> DiagnosticLabelView<'a> {
fn base(&self) -> usize {
let labels = self
.sections
.diagnostic_labels
.expect("label view requires section");
labels.offset as usize + self.index as usize * DIAGNOSTIC_LABEL_RECORD_SIZE as usize
}
pub fn source_id(&self) -> SourceId {
SourceId::new(read_u32_le(self.bytes, self.base()))
}
pub fn span(&self) -> Span {
Span::new(
read_u32_le(self.bytes, self.base() + 4),
read_u32_le(self.bytes, self.base() + 8),
)
}
pub fn message(&self) -> Option<&'a str> {
let raw = read_u32_le(self.bytes, self.base() + 12);
string_slice(self.bytes, &self.sections, raw)
}
}
#[derive(Debug, Clone)]
pub struct DiagnosticLabelIter<'a> {
bytes: &'a [u8],
sections: SectionIndex,
cursor: u32,
remaining: u32,
}
impl<'a> Iterator for DiagnosticLabelIter<'a> {
type Item = DiagnosticLabelView<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let item = DiagnosticLabelView {
bytes: self.bytes,
sections: self.sections,
index: self.cursor,
};
self.cursor += 1;
self.remaining -= 1;
Some(item)
}
}
fn string_slice<'a>(bytes: &'a [u8], sections: &SectionIndex, id_raw: u32) -> Option<&'a str> {
if id_raw == NONE_REF || id_raw >= sections.string_offsets.count {
return None;
}
let rec_off = sections.string_offsets.offset as usize
+ id_raw as usize * STRING_OFFSET_RECORD_SIZE as usize;
let off = read_u32_le(bytes, rec_off);
let len = read_u32_le(bytes, rec_off + 4);
let start = sections.string_data.offset as usize + off as usize;
let end = start + len as usize;
core::str::from_utf8(&bytes[start..end]).ok()
}