lua_vm/string.rs
1//! Shared string constants and hashing utility.
2//!
3//! This module used to host a standalone `LuaStringImpl`/`StringPool`
4//! bootstrap string table (`TString`/`stringtable` from `lstring.c`), built
5//! solely to construct the pre-allocated out-of-memory message during VM
6//! startup. That subsystem was verified dead (issue #274): `GlobalState`
7//! bootstrap now calls `LuaState::intern_str` — the same interning path
8//! every other Lua string in the VM uses, backed by
9//! `GlobalState::interned_lt` — directly (see `state.rs::init_memerrmsg`).
10//! What remains here is the pure hash function and the short/long-string
11//! length threshold, both still shared with that real path and with the
12//! VM's seed generation.
13
14/// Upper bound (in bytes) on a "short" (interned) Lua string; longer strings
15/// are always long strings and are never interned. Mirrors C's
16/// `LUAI_MAXSHORTLEN`.
17pub(crate) const MAX_SHORT_LEN: usize = 40;
18
19// lstring.h: LUAI_FUNC → pub(crate)
20/// Hash a byte string with a seed using Lua's FNV-style hash.
21///
22/// This is a pure function with no allocations. The algorithm XORs shifts and
23/// additions over each byte in reverse order, seeded by `seed ^ len`. Mirrors
24/// C's `luaS_hash`.
25///
26/// C parenthesises `(h<<5)` and `(h>>2)` explicitly, so the outer additions
27/// are unambiguous despite C's `<<`/`>>` having lower precedence than `+`.
28/// In Rust `<<` and `>>` have higher precedence than `+`, so the same
29/// expression is computed without extra parentheses; `wrapping_add` is used
30/// to match C's unsigned wrap-around arithmetic.
31pub(crate) fn hash_bytes(bytes: &[u8], seed: u32) -> u32 {
32 let mut h: u32 = seed ^ (bytes.len() as u32);
33
34 let mut l = bytes.len();
35 while l > 0 {
36 l -= 1;
37 h ^= (h << 5).wrapping_add(h >> 2).wrapping_add(bytes[l] as u32);
38 }
39
40 h
41}