neo-decompiler 0.11.0

Neo N3 NEF decompiler: parse, disassemble, lift bytecode to high-level pseudocode and C# skeletons, with a CLI, JSON reports, and optional WebAssembly bindings.
Documentation
use std::collections::HashSet;

/// Sanitize an arbitrary manifest or user-provided identifier into a stable
/// snake-ish form suitable for high-level output.
pub(in super::super) fn sanitize_identifier(input: &str) -> String {
    let mut ident = String::new();
    for ch in input.chars() {
        if ch.is_ascii_alphanumeric() {
            ident.push(ch);
        } else if ch == '_' || (ch.is_whitespace() || ch == '-') && !ident.ends_with('_') {
            ident.push('_');
        }
    }
    while ident.ends_with('_') {
        ident.pop();
    }
    if ident.is_empty() {
        ident.push_str("param");
    }
    if ident
        .chars()
        .next()
        .map(|ch| ch.is_ascii_digit())
        .unwrap_or(false)
    {
        ident.insert(0, '_');
    }
    ident
}

pub(in super::super) fn make_unique_identifier(base: String, used: &mut HashSet<String>) -> String {
    if used.insert(base.clone()) {
        return base;
    }
    // Preserve a leading `@` prefix (C# verbatim identifiers) so that
    // the suffix is appended to the stem, not after the prefix.
    let (prefix, stem) = match base.strip_prefix('@') {
        Some(stem) => ("@", stem),
        None => ("", base.as_str()),
    };
    let mut index = 1usize;
    loop {
        let candidate = format!("{prefix}{stem}_{index}");
        if used.insert(candidate.clone()) {
            return candidate;
        }
        index += 1;
    }
}