// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Compile-time feature registry namespace.
//!
//! This module owns the `features` namespace: each feature name the runtime
//! understands is declared here as a `pub const &'static str` handle, and
//! `KNOWN_FEATURES` lists every one of them.
//!
//! Sibling tables (e.g. the builtin-upgrade registry, the compute-budget gating
//! table) carry their own compile-time assertions that every `feature_name`
//! they reference is a member of `KNOWN_FEATURES`.

/// Enables the `StakeInfo` schema version 2 on-disk layout.
///
/// While active, the bank-level `native-staking` writers — genesis self-bond
/// and epoch-reward compounding — emit the V2 stake-account format; before
/// activation they emit V1. The instruction-handler write path
/// (`write_stake_info`) runs inside the program runtime, which sees the Solana
/// `feature_set` rather than this runtime `ActiveFeatures` flag, so it keeps
/// emitting V1 for now; the versioned reader decodes both, so V1 and V2
/// records coexist. (The V2-only field `last_reward_slot` has no consumer yet,
/// so a handler rewriting a V2 record back to V1 is currently lossless in
/// practice; feature-aware handler emission lands with that consumer.)
/// Append-only: like all feature activation this cannot be reversed, and once
/// any account has been written in the new format the storage-format bump is
/// effectively permanent. This is the worked example for the writer-gated
/// storage-format migration template.
pub const ENABLE_STAKE_INFO_V2: &str = "ENABLE_STAKE_INFO_V2";

/// Every feature name the runtime knows about.
///
/// Hot-path gating sites reference entries through the `pub const &'static str`
/// handles declared in this module. The slice itself exists so that:
///
/// * tooling can enumerate the registry,
/// * compile-time assertions on sibling tables (e.g. builtin upgrade registry,
///   compute-budget gating table) can verify that every referenced
///   `feature_name` is known.
pub const KNOWN_FEATURES: &[&str] = &[ENABLE_STAKE_INFO_V2];

/// Const-evaluable string equality.
///
/// `==` on `&str` is not usable in `const fn` on stable, so sibling registry
/// tables in other crates (e.g. the builtin-upgrade and storage-format
/// writer-gate registries in `svm-execution`) compose this byte-compare into
/// their own compile-time assertions instead of each carrying a private copy.
pub const fn const_str_eq(a: &str, b: &str) -> bool {
    let a = a.as_bytes();
    let b = b.as_bytes();
    if a.len() != b.len() {
        return false;
    }
    let mut i = 0;
    while i < a.len() {
        if a[i] != b[i] {
            return false;
        }
        i += 1;
    }
    true
}

const fn const_slice_contains(haystack: &[&str], needle: &str) -> bool {
    let mut i = 0;
    while i < haystack.len() {
        if const_str_eq(haystack[i], needle) {
            return true;
        }
        i += 1;
    }
    false
}

/// Returns `true` if `name` appears in [`KNOWN_FEATURES`].
///
/// Const-evaluable so sibling tables in other crates (e.g. the compute-budget
/// gating table in `svm-execution`) can assert at compile time that every
/// feature name they reference is known to the runtime.
pub const fn is_known_feature(name: &str) -> bool {
    const_slice_contains(KNOWN_FEATURES, name)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn const_str_eq_matches() {
        assert!(const_str_eq("foo", "foo"));
        assert!(!const_str_eq("foo", "bar"));
        assert!(!const_str_eq("foo", "foobar"));
    }

    #[test]
    fn const_slice_contains_matches() {
        let s: &[&str] = &["a", "b", "c"];
        assert!(const_slice_contains(s, "b"));
        assert!(!const_slice_contains(s, "z"));
    }

    #[test]
    fn is_known_feature_matches() {
        assert!(is_known_feature(ENABLE_STAKE_INFO_V2));
        assert!(!is_known_feature("definitely_not_a_real_feature"));
    }
}
