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 pub string: Vec<String>,
124 pub symbols: ClangdSymbols,
126 pub relations: ClangdRelations,
128 pub cmdline: ClangdCmdLine,
130 pub sources: ClangdSources,
132 pub meta: ClangdMetaData,
134 pub references: ClangdReferences,
136 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 v = Self::get_strings(buf.get(sz..).unwrap());
221 }
222 else {
223 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{}