1use std::sync::Arc;
88
89mod index;
90mod link;
91mod mmap;
92mod storage;
93mod universe;
94
95pub use index::{ChainedIndex, DefinitionLocation, SymbolChain};
96pub use link::{LinkResolver, LockFreeLink, SymbolLinker};
97pub use mmap::{MemoryMappedStorage, MmapIndex};
98pub use storage::{PackageId, SoAStorage, SymbolId, SymbolStorage};
99pub use universe::{SymbolUniverse, SymbolUniverseGuard, UniverseBuilder, UniverseSnapshot};
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105#[repr(u8)]
106pub enum SymbolKind {
107 Function = 0,
109 Type = 1,
111 Interface = 2,
113 Struct = 3,
115 Const = 4,
117 Var = 5,
119 Method = 6,
121 Field = 7,
123 Package = 8,
125}
126
127impl SymbolKind {
128 pub fn as_str(&self) -> &'static str {
139 match self {
140 SymbolKind::Function => "func",
141 SymbolKind::Type => "type",
142 SymbolKind::Interface => "interface",
143 SymbolKind::Struct => "struct",
144 SymbolKind::Const => "const",
145 SymbolKind::Var => "var",
146 SymbolKind::Method => "method",
147 SymbolKind::Field => "field",
148 SymbolKind::Package => "package",
149 }
150 }
151
152 pub fn is_callable(&self) -> bool {
154 matches!(self, SymbolKind::Function | SymbolKind::Method)
155 }
156
157 pub fn is_type(&self) -> bool {
159 matches!(
160 self,
161 SymbolKind::Type | SymbolKind::Interface | SymbolKind::Struct
162 )
163 }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
172#[repr(u8)]
173pub enum Visibility {
174 Public = 0,
176 Private = 1,
178 Internal = 2,
180}
181
182impl Visibility {
183 pub fn from_name(name: &str) -> Self {
185 if name.starts_with("internal/") {
186 Visibility::Internal
187 } else if name
188 .chars()
189 .next()
190 .map(|c| c.is_uppercase())
191 .unwrap_or(false)
192 {
193 Visibility::Public
194 } else {
195 Visibility::Private
196 }
197 }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq)]
205#[repr(C)]
206pub struct Symbol {
207 pub id: u32,
209
210 pub package_id: u32,
212
213 pub kind: SymbolKind,
215
216 pub visibility: Visibility,
218
219 pub name_offset: u32,
221
222 pub name_len: u16,
224
225 pub doc_offset: u32,
227
228 pub doc_len: u16,
230
231 pub signature_offset: u32,
233
234 pub signature_len: u16,
236
237 pub def_file_id: u32,
239
240 pub def_offset: u32,
242
243 pub chain_next: u32,
245}
246
247impl Symbol {
248 pub fn new(
268 id: u32,
269 package_id: u32,
270 kind: SymbolKind,
271 name_offset: u32,
272 name_len: u16,
273 ) -> Self {
274 Self {
275 id,
276 package_id,
277 kind,
278 visibility: Visibility::Public,
279 name_offset,
280 name_len,
281 doc_offset: 0,
282 doc_len: 0,
283 signature_offset: 0,
284 signature_len: 0,
285 def_file_id: 0,
286 def_offset: 0,
287 chain_next: 0,
288 }
289 }
290
291 pub fn with_definition(mut self, file_id: u32, offset: u32) -> Self {
293 self.def_file_id = file_id;
294 self.def_offset = offset;
295 self
296 }
297
298 pub fn with_chain(mut self, next: u32) -> Self {
300 self.chain_next = next;
301 self
302 }
303
304 pub fn is_exported(&self) -> bool {
306 matches!(self.visibility, Visibility::Public)
307 }
308
309 pub fn has_doc(&self) -> bool {
311 self.doc_offset != 0
312 }
313
314 pub fn has_signature(&self) -> bool {
316 self.signature_offset != 0
317 }
318
319 pub fn estimated_size(&self) -> usize {
321 std::mem::size_of::<Self>()
322 }
323}
324
325#[derive(Debug, Clone, PartialEq, Eq)]
329pub struct Package {
330 pub id: u32,
332
333 pub path_offset: u32,
335 pub path_len: u16,
336
337 pub name_offset: u32,
339 pub name_len: u16,
340
341 pub version_offset: u32,
343 pub version_len: u16,
344
345 pub first_symbol: u32,
347
348 pub symbol_count: u16,
350
351 pub import_count: u16,
353}
354
355impl Package {
356 pub fn symbol_range(&self) -> std::ops::Range<u32> {
358 self.first_symbol..(self.first_symbol + self.symbol_count as u32)
359 }
360
361 pub fn contains_symbol(&self, symbol_id: u32) -> bool {
363 self.symbol_range().contains(&symbol_id)
364 }
365}
366
367#[derive(Debug, Clone, Copy, PartialEq, Eq)]
371pub struct Import {
372 pub from_package: u32,
374
375 pub to_package: u32,
377
378 pub alias_offset: u32,
380 pub alias_len: u16,
381}
382
383impl Import {
384 pub fn is_aliased(&self) -> bool {
386 self.alias_offset != 0
387 }
388}
389
390#[derive(Debug, Clone, Copy, Default)]
392pub struct UniverseStats {
393 pub total_symbols: usize,
395 pub total_packages: usize,
397 pub total_imports: usize,
399 pub string_pool_size: usize,
401 pub memory_usage_bytes: usize,
403}
404
405impl UniverseStats {
406 pub fn avg_bytes_per_symbol(&self) -> f64 {
408 if self.total_symbols == 0 {
409 0.0
410 } else {
411 self.memory_usage_bytes as f64 / self.total_symbols as f64
412 }
413 }
414}
415
416#[derive(Debug, thiserror::Error)]
418pub enum SymbolError {
419 #[error("Symbol not found: {0}")]
420 NotFound(String),
421
422 #[error("Package not found: {0}")]
423 PackageNotFound(String),
424
425 #[error("Invalid symbol ID: {0}")]
426 InvalidId(u32),
427
428 #[error("Chain broken at symbol {0}")]
429 BrokenChain(u32),
430
431 #[error("Mmap error: {0}")]
432 MmapError(#[from] std::io::Error),
433
434 #[error("Serialization error: {0}")]
435 SerializationError(String),
436
437 #[error("Index out of bounds: index={index}, len={len}")]
438 OutOfBounds { index: usize, len: usize },
439}
440
441pub type Result<T> = std::result::Result<T, SymbolError>;
443
444#[cfg(test)]
445mod tests {
446 use super::*;
447
448 #[test]
449 fn test_symbol_creation() {
450 let sym = Symbol::new(1, 0, SymbolKind::Function, 0, 4);
451 assert_eq!(sym.id, 1);
452 assert_eq!(sym.kind, SymbolKind::Function);
453 assert!(sym.is_exported());
454 }
455
456 #[test]
457 fn test_symbol_kind_as_str() {
458 assert_eq!(SymbolKind::Function.as_str(), "func");
459 assert_eq!(SymbolKind::Type.as_str(), "type");
460 assert_eq!(SymbolKind::Interface.as_str(), "interface");
461 }
462
463 #[test]
464 fn test_symbol_kind_is_callable() {
465 assert!(SymbolKind::Function.is_callable());
466 assert!(SymbolKind::Method.is_callable());
467 assert!(!SymbolKind::Type.is_callable());
468 }
469
470 #[test]
471 fn test_visibility_from_name() {
472 assert_eq!(Visibility::from_name("Public"), Visibility::Public);
473 assert_eq!(Visibility::from_name("private"), Visibility::Private);
474 assert_eq!(Visibility::from_name("internal/foo"), Visibility::Internal);
475 }
476
477 #[test]
478 fn test_package_symbol_range() {
479 let pkg = Package {
480 id: 1,
481 path_offset: 0,
482 path_len: 10,
483 name_offset: 0,
484 name_len: 4,
485 version_offset: 0,
486 version_len: 0,
487 first_symbol: 100,
488 symbol_count: 10,
489 import_count: 0,
490 };
491
492 assert!(pkg.contains_symbol(100));
493 assert!(pkg.contains_symbol(109));
494 assert!(!pkg.contains_symbol(99));
495 assert!(!pkg.contains_symbol(110));
496 }
497
498 #[test]
499 fn test_universe_stats() {
500 let stats = UniverseStats {
501 total_symbols: 1000,
502 total_packages: 10,
503 total_imports: 50,
504 string_pool_size: 10000,
505 memory_usage_bytes: 50000,
506 };
507
508 assert_eq!(stats.avg_bytes_per_symbol(), 50.0);
509 }
510}