clangd_parser/
clangd.rs

1use std::fs;
2use std::path::PathBuf;
3use std::collections::BTreeMap;
4
5use griff::*;
6
7use crate::symbols;
8use crate::rela;
9use crate::refs;
10use crate::srcs;
11use crate::cmdl;
12
13#[derive(Debug)]
14pub enum ParseError {
15    CannotReadFile,
16    RiffError(ChunkError),
17}
18type ParseReturn = Result<ClangdFile, ParseError>;
19
20pub type ClangdFileMap = BTreeMap<String, ClangdFile>;
21pub type ClangdIdMap = BTreeMap<symbols::SymbolId, symbols::Symbol>;
22pub type ClangdNameMap = BTreeMap<String, symbols::Symbol>;
23#[derive(Debug, Clone)]
24pub struct ClangdDatabase {
25    pub file: ClangdFileMap,
26    pub id: ClangdIdMap,
27    pub name: ClangdNameMap,
28}
29
30pub trait ClangdUtility {
31    fn get_varint(buf: &[u8]) -> (usize, u32) {
32        let mut bytes_read: usize = 0;
33        let mut varint: u32 = 0;
34        let mut shift: u32 = 0;
35        
36        loop {
37            let b = buf[bytes_read];
38            let cont = (b >> 7) & 1;
39            let tmp = (0x7F & b) as u32;
40            varint |= tmp << shift;
41            bytes_read += 1;
42            shift += 7;
43            if cont != 1
44            || !(shift < 32) {
45                break;
46            }
47        }
48        (bytes_read, varint)
49    }
50
51    fn get_string(buf: &[u8], string_table: &Vec<String>) -> (usize, String) {
52        let bytes_read: usize;
53        let mut s: String = String::new();
54        let (sz, idx) = Self::get_varint(buf);
55        bytes_read = sz;
56        if (idx as usize) < string_table.len() {
57            s = string_table[ idx as usize ].clone();
58        }
59        s = s.trim_end_matches("\0")
60             .replace("%2B", "+").to_string();
61        (bytes_read, s)
62    }
63
64    fn get_u32(buf: &[u8]) -> (usize, u32) {
65        let mut ret: u32 = 0;
66        for i in 0..4 {
67            ret |= (buf[i] as u32) << i*8;
68        }
69        (4, ret)
70    }
71
72    fn decompress(buf: &[u8]) -> Vec<u8> {
73        use std::io::{BufReader, Read};
74        use libflate::zlib::Decoder;
75
76        let reader = BufReader::new(buf);
77        let mut v: Vec<u8> = vec![];
78        let mut decoder = Decoder::new(reader).unwrap();
79        decoder.read_to_end(&mut v).unwrap();
80        v
81    }
82}
83
84#[derive(Debug, Clone, Default)]
85pub struct ClangdSymbols {
86    pub data: Vec<symbols::Symbol>,
87}
88
89#[derive(Debug, Clone, Default)]
90pub struct ClangdRelations {
91    pub data: Vec<rela::Rela>,
92}
93
94#[derive(Debug, Clone, Default)]
95pub struct ClangdCmdLine {
96    pub data: Vec<cmdl::Cmdl>,
97}
98
99#[derive(Debug, Clone, Default)]
100pub struct ClangdSources {
101    pub data: Vec<srcs::Srcs>,
102}
103
104#[derive(Debug, Clone, Default)]
105pub struct ClangdMetaData {
106    version: [u8;4],
107}
108
109#[derive(Debug, Clone, Default)]
110pub struct ClangdReferences {
111    pub data: Vec<refs::Refs>,
112}
113
114#[derive(Debug, Clone, Default)]
115pub struct ClangdFileType {
116    #[allow(dead_code)]
117    ftype: [u8;4],
118}
119
120#[derive(Debug, Clone, Default)]
121pub struct ClangdFile {
122    // string table from which everything references
123    pub string: Vec<String>,
124    // symbols defined in the file
125    pub symbols: ClangdSymbols,
126    // ?
127    pub relations: ClangdRelations,
128    // cmdl args to compile file
129    pub cmdline: ClangdCmdLine,
130    // ?
131    pub sources: ClangdSources,
132    // clangd idx metadata
133    pub meta: ClangdMetaData,
134    // all references in the file, internal & external
135    pub references: ClangdReferences,
136    // RIFF file type
137    pub file_type: ClangdFileType,
138
139    #[cfg(feature="post-process")]
140    pub variable_declarations: Vec<symbols::SymbolId>,
141}
142
143impl ClangdFile {
144    #[allow(dead_code)]
145    pub async fn parse(p: PathBuf) -> ParseReturn {
146        let _contents = fs::read(p.as_path());
147        if _contents.is_err() {
148            return Err(ParseError::CannotReadFile);
149        }
150    
151        let contents = _contents.unwrap();
152        let data = contents.as_slice();
153        let riff = Riff::parse(data).await;
154        if riff.is_err() {
155            return Err(ParseError::RiffError(riff.unwrap_err()));
156        }
157        let cd: ClangdFile = ClangdFile::consume_riff(&riff.unwrap());
158        Ok(cd)
159    }
160    
161    fn consume_riff(riff: &Riff) -> ClangdFile {
162        let mut cd: ClangdFile = Default::default();
163        if let Some(r) = &riff.chunk {
164            match r.id {
165                ChunkId::Riff => {
166                    match &r.data {
167                        ChunkData::RiffData(x) => {
168                            if x.file_type == *CDIX {
169                                for child in &x.data {
170                                    match child.id {
171                                        ChunkId::Stri => {
172                                            let data: ChunkStream = ClangdFile::get_stream(&child.data);
173                                            cd.string = ClangdFile::consume_string(&data);
174                                        },
175                                        ChunkId::Symb => {
176                                            let data: ChunkStream = ClangdFile::get_stream(&child.data);
177                                            cd.symbols = ClangdFile::consume_symbols(&data, &cd.string);
178                                        },
179                                        ChunkId::Srcs => {
180                                            let data: ChunkStream = ClangdFile::get_stream(&child.data);
181                                            cd.sources = ClangdFile::consume_sources(&data, &cd.string);
182                                        },
183                                        ChunkId::Rela => {
184                                            let data: ChunkStream = ClangdFile::get_stream(&child.data);
185                                            cd.relations = ClangdFile::consume_relations(&data);
186                                        },
187                                        ChunkId::Refs => {
188                                            let data: ChunkStream = ClangdFile::get_stream(&child.data);
189                                            cd.references = ClangdFile::consume_references(&data, &cd.string);
190                                        },
191                                        ChunkId::Cmdl => {
192                                            let data: ChunkStream = ClangdFile::get_stream(&child.data);
193                                            cd.cmdline = ClangdFile::consume_cmdline(&data, &cd.string);
194                                        },
195                                        ChunkId::Meta => {
196                                            let data: ChunkStream = ClangdFile::get_stream(&child.data);
197                                            cd.meta = ClangdFile::consume_metadata(&data);
198                                        },                                        
199                                        _ => todo!()
200                                    }
201                                }
202                            }
203                        },
204                        _ => todo!()
205                    }
206                },
207                _ => todo!()
208            }
209        }
210    
211        cd
212    }
213
214    fn consume_string(data: &ChunkStream) -> Vec<String> {
215        let v: Vec<String>;
216        let buf: &[u8] = data.data.as_slice();
217        let (sz, compr_sz) = Self::get_u32(buf);
218        if compr_sz == 0 {
219            // uncompressed
220            v = Self::get_strings(buf.get(sz..).unwrap());
221        }
222        else {
223            // compressed
224            let decomp = Self::decompress(buf.get(sz..).unwrap());
225            let buf = decomp.as_slice();
226            v = Self::get_strings(buf);
227        }
228        v
229    }
230
231    fn get_strings(buf: &[u8]) -> Vec<String> {
232        let mut v: Vec<String> = vec![];
233        let mut s: String = String::new();
234        for i in 0..buf.len() {
235            s.push(buf[i] as char);
236            if buf[i] == b'\0' {
237                v.push(s.clone());
238                s.clear();
239            }
240        }
241        v
242    }
243
244    fn consume_symbols(data: &ChunkStream, string_table: &Vec<String>) -> ClangdSymbols {
245        let mut cs: ClangdSymbols = Default::default();
246        cs.data = symbols::Symbol::parse(data, string_table);
247        cs
248    }
249
250    fn consume_sources(data: &ChunkStream, string_table: &Vec<String>) -> ClangdSources {
251        let mut cs: ClangdSources = Default::default();
252        cs.data = srcs::Srcs::parse(data, string_table);
253        cs
254    }
255
256    fn consume_relations(data: &ChunkStream) -> ClangdRelations {
257        let mut cr: ClangdRelations = Default::default();
258        cr.data = rela::Rela::parse(data);
259        cr
260    }
261
262    fn consume_references(data: &ChunkStream, string_table: &Vec<String>) -> ClangdReferences {
263        let mut cr: ClangdReferences = Default::default();
264        cr.data = refs::Refs::parse(data, string_table);
265        cr
266    }
267
268    fn consume_cmdline(data: &ChunkStream, string_table: &Vec<String>) -> ClangdCmdLine {
269        let mut ccl: ClangdCmdLine = Default::default();
270        ccl.data = cmdl::Cmdl::parse(data, string_table);
271        ccl
272    }
273
274    fn consume_metadata(data: &ChunkStream) -> ClangdMetaData {
275        let mut cm: ClangdMetaData = Default::default();
276        cm.version = data.data.clone().as_slice().try_into().unwrap();
277        cm
278    }
279
280    fn get_stream(cd: &ChunkData) -> ChunkStream {
281        match cd {
282            ChunkData::StreamData(x) => x.clone(),
283            _ => todo!()
284        }
285    }
286}
287impl ClangdUtility for ClangdFile{}