pub(crate) const BASIS: u32 = 2_166_136_261;
const PRIME: u32 = 16_777_619;
pub(crate) fn fnv1a(bytes: &[u8], mut hash: u32) -> u32 {
for byte in bytes {
hash ^= *byte as u32;
hash = hash.wrapping_mul(PRIME);
}
hash
}
pub fn type_definitions(source: &[u8]) -> String {
let text = String::from_utf8_lossy(source);
let mut kept = String::new();
let mut pending_attrs = String::new();
let mut depth = 0usize;
let mut capturing = false;
for raw_line in text.lines() {
let line = match raw_line.find("//") {
Some(at) => &raw_line[..at],
None => raw_line,
};
let line = line.trim();
if line.is_empty() && !capturing {
continue;
}
if capturing {
push_normalised(&mut kept, line);
depth += line.matches('{').count();
depth = depth.saturating_sub(line.matches('}').count());
if depth == 0 && (line.ends_with('}') || line.ends_with(';')) {
capturing = false;
}
continue;
}
if line.starts_with("#[") || line.starts_with("#!") {
push_normalised(&mut pending_attrs, line);
continue;
}
if declares_type(line) {
kept.push_str(&pending_attrs);
pending_attrs.clear();
push_normalised(&mut kept, line);
depth = line.matches('{').count().saturating_sub(line.matches('}').count());
capturing = depth > 0;
continue;
}
pending_attrs.clear();
}
kept
}
fn declares_type(line: &str) -> bool {
let mut words = line.split_whitespace().peekable();
while let Some(word) = words.next() {
match word {
"pub" => continue,
w if w.starts_with("pub(") => continue,
"struct" | "enum" | "union" => return words.peek().is_some(),
_ => return false,
}
}
false
}
pub(crate) fn push_normalised(out: &mut String, line: &str) {
for word in line.split_whitespace() {
out.push_str(word);
}
}
pub(crate) fn strip_trailing_commas(text: &str) -> String {
text.replace(",}", "}").replace(",)", ")")
}
pub fn version_of_sources<I, B>(sources: I) -> u32
where
I: IntoIterator<Item = B>,
B: AsRef<[u8]>,
{
let mut hash = BASIS;
for source in sources {
let text: Vec<u8> = source.as_ref().iter().copied().filter(|b| *b != b'\r').collect();
hash = fnv1a(strip_trailing_commas(&type_definitions(&text)).as_bytes(), hash);
}
hash.max(1)
}