1use std::num::NonZeroU64;
2
3use crate::FHashMap;
4
5#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct SpanId(NonZeroU64);
7
8impl SpanId {
9 const OFFSET_BITS: u32 = 38;
10 const OFFSET_MASK: u64 = (1 << Self::OFFSET_BITS) - 1;
11 const LEN_MAX: u64 = (1 << (63 - Self::OFFSET_BITS)) - 1;
12
13 fn expand(self) -> ExpandedSpanId {
14 let inner = self.0.get();
15 if (inner & (1 << 63)) == 0 {
16 let offset = (inner & Self::OFFSET_MASK) - 1;
17 let len = (inner >> Self::OFFSET_BITS) as usize;
18 ExpandedSpanId::Inline(offset, len)
19 } else {
20 ExpandedSpanId::Interned((inner & !(1 << 63)) as usize)
21 }
22 }
23}
24
25impl std::fmt::Debug for SpanId {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self.expand() {
28 ExpandedSpanId::Inline(offset, len) => f
29 .debug_struct("Inline")
30 .field("offset", &offset)
31 .field("len", &len)
32 .finish(),
33 ExpandedSpanId::Interned(i) => f.debug_tuple("Interned").field(&i).finish(),
34 }
35 }
36}
37
38enum ExpandedSpanId {
39 Inline(u64, usize),
40 Interned(usize),
41}
42
43#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
44pub struct SpanContextId(usize);
45
46#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
47pub struct SourceId(usize);
48
49#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum SpanContext {
51 Source(SourceId),
52}
53
54pub struct SpanManager {
55 contexts: Vec<(u64, SpanContext)>,
56 sources: Vec<SpanContextId>,
57 span_to_idx: FHashMap<(SpanContextId, usize, usize), usize>,
59 idx_to_span: Vec<(SpanContextId, usize, usize)>,
60}
61
62impl Default for SpanManager {
63 #[inline]
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl SpanManager {
70 pub fn new() -> Self {
71 Self {
72 contexts: Vec::new(),
73 sources: Vec::new(),
74 span_to_idx: FHashMap::default(),
75 idx_to_span: Vec::new(),
76 }
77 }
78
79 pub fn insert_source_context(&mut self, len: usize) -> (SpanContextId, SourceId) {
80 let source_id = SourceId(self.sources.len());
81 let context_id = self.insert_context(len, SpanContext::Source(source_id));
82 self.sources.push(context_id);
83 (context_id, source_id)
84 }
85
86 fn insert_context(&mut self, len: usize, context: SpanContext) -> SpanContextId {
87 let len = u64::try_from(len).unwrap();
88 let base_offset = if let Some(&(last_end, _)) = self.contexts.last() {
89 last_end
90 } else {
91 0
92 };
93 let context_id = SpanContextId(self.contexts.len());
94 self.contexts.push((base_offset + len + 1, context));
95 context_id
96 }
97
98 #[must_use]
99 pub fn get_context(&self, context: SpanContextId) -> &SpanContext {
100 &self.contexts[context.0].1
101 }
102
103 #[must_use]
104 fn get_context_offsets(&self, context: SpanContextId) -> (u64, u64) {
105 let i = context.0;
106 if i == 0 {
107 (0, self.contexts[0].0)
108 } else {
109 (self.contexts[i - 1].0, self.contexts[i].0)
110 }
111 }
112
113 #[must_use]
114 fn get_context_from_offset(&self, offset: u64) -> SpanContextId {
115 match self.contexts.binary_search_by_key(&offset, |entry| entry.0) {
116 Ok(i) => SpanContextId(i + 1),
117 Err(i) => SpanContextId(i),
118 }
119 }
120
121 pub fn intern_span(&mut self, context: SpanContextId, start: usize, end: usize) -> SpanId {
122 let start_u64 = u64::try_from(start).unwrap();
123 let end_u64 = u64::try_from(end).unwrap();
124 let (min_offset, max_offset) = self.get_context_offsets(context);
125 assert!(start_u64 <= end_u64);
126 let start_offset = min_offset + start_u64;
127 let end_offset = min_offset + end_u64;
128 assert!(start_offset < max_offset);
129 assert!(end_offset < max_offset);
130
131 let len = end_u64 - start_u64;
132 if len > SpanId::LEN_MAX || start_offset >= SpanId::OFFSET_MASK {
133 let span = (context, start, end);
134 let i = match self.span_to_idx.entry(span) {
135 std::collections::hash_map::Entry::Occupied(entry) => *entry.get(),
136 std::collections::hash_map::Entry::Vacant(entry) => {
137 let i = self.idx_to_span.len();
138 self.idx_to_span.push(span);
139 entry.insert(i);
140 i
141 }
142 };
143 SpanId(NonZeroU64::new((i as u64) | (1 << 63)).unwrap())
144 } else {
145 SpanId(NonZeroU64::new((start_offset + 1) | (len << SpanId::OFFSET_BITS)).unwrap())
146 }
147 }
148
149 #[must_use]
150 pub fn get_span(&self, span: SpanId) -> (SpanContextId, usize, usize) {
151 match span.expand() {
152 ExpandedSpanId::Inline(start_offset, len) => {
153 let context = self.get_context_from_offset(start_offset);
154 let (min_offset, _) = self.get_context_offsets(context);
155 let start = (start_offset - min_offset) as usize;
156 (context, start, start + len)
157 }
158 ExpandedSpanId::Interned(i) => self.idx_to_span[i],
159 }
160 }
161
162 pub(crate) fn make_surrounding_span(&mut self, start_span: SpanId, end_span: SpanId) -> SpanId {
163 let (start_ctx_id, start_start_pos, _) = self.get_span(start_span);
164 let (end_ctx_id, _, end_end_pos) = self.get_span(end_span);
165 assert_eq!(start_ctx_id, end_ctx_id);
166 assert!(start_start_pos <= end_end_pos);
167 self.intern_span(start_ctx_id, start_start_pos, end_end_pos)
168 }
169}