# Uso de bajo nivel de `CFastHashTableBase` Esta guía es solo para quien quiere exprimir el rendimiento al máximo y está dispuesto a acoplarse al estado interno del algoritmo. **Si solo usas la API normal (`Add`, `TryGet`, `Contains`, `Remove(key)`, etc.) no necesitas leer esto** — este documento describe los miembros públicos "residuales" (`m_i`, `m_bit_pos`, `m_last_pos_tbl`) que el motor deja expuestos para evitar un segundo recorrido de la tabla (el clásico patrón *find + luego add/replace* hecho en una sola pasada). Estos tres campos **no son un resultado estable**: su valor solo tiene sentido *inmediatamente después* de la llamada que los dejó ahí, y **cualquier otra llamada al mismo hashmap los vuelve a pisar** (incluida una llamada anidada dentro de la misma expresión). No los guardes para usarlos más tarde. ## Los tres campos | Campo | Qué es | |------------------|----------------------------------------------------------------| | `m_i` | Índice de **grupo** (grupo de 8 slots) donde terminó la búsqueda. | | `m_bit_pos` | Posición dentro del grupo, (multiplo de 8) - 1, en Remove* puede ser multiplo de ((8)-1)-7 | | `m_last_pos_tbl` | Índice **absoluto y ya resuelto** en `m_table[]`. Es el único que puedes indexar directamente sin más cálculo. | `m_last_pos_tbl` existe justamente para que **no tengas que calcular nada**: siempre que un método lo garantice actualizado (ver tabla), puedes hacer `m_table[m_last_pos_tbl]` directo. `m_i` y `m_bit_pos` son el detalle interno que lo construye, expuestos solo para casos muy puntuales de bajo nivel (por ejemplo, `Remove` los reutiliza sin tener que rebuscar la key). ## Qué deja cada método, exactamente | Método | Retorno | `m_i` | `m_bit_pos` | `m_last_pos_tbl` | |----------------------------------|---------|-------------------|-------------------------------------|-------------------------------------| | `ReserveSlot(key)` | `false` (ya existía) | grupo del slot existente | *(sin uso)* | posición exacta de la key existente | | `ReserveSlot(key)` | `true` (no existía, se reservó) | grupo del slot nuevo | *(sin uso)* | posición del slot recién reservado (sin `key` todavía escrita — eso te toca a ti) | | `ContainsKey(key)` / `ContainsKeyByHash(hash)` | `true` | grupo del match | **bit-pos** (múltiplo de 8, +7 → 7,15,23…63) posicion inicial del grupo en bit | posición exacta del match | | `ContainsKey(key)` / `ContainsKeyByHash(hash)` | `false` | último grupo probado | ultima posicion probada en grupo | sin significado | | `Remove(key)` | `true` | igual que el `ContainsKey` interno que usa | Mismo valor que `ContainsKey` - 7 | posición exacta del match (true) | | `Remove(key)` | `false` | último grupo probado | ultima posicion probada en grupo | sin significado | | `RemoveLastFindCall()` | — | usa el `m_i`/`m_bit_pos` que haya dejado la llamada previa a `ContainsKey`/`ContainsKeyByHash` | Valor reducido en 7 | — | ## Patrón recomendado (find-or-insert en una sola pasada) ```mql5 if(!hashmap.ReserveSlot(key)) { // Ya existía → reemplazar/leer directo, sin recalcular nada hashmap.m_table[hashmap.m_last_pos_tbl].value = nuevo_valor; } else { // No existía → el slot ya está reservado, solo falta llenar key/value hashmap.m_table[hashmap.m_last_pos_tbl].key = key; hashmap.m_table[hashmap.m_last_pos_tbl].value = nuevo_valor; } ``` Este es el patrón que usan `CDomNodeBase::Set()` y `operator[]` en [BasesParserSLan](https://forge.mql5.io/nique_372/BasesParserSLan) para evitar un `Find` + `Add` por separado. ## Advertencia sobre re-entrancia Estos campos son **miembros de instancia**, no valores locales por-llamada. Si entre que guardas la intención de usarlos y realmente los usas se cuela **cualquier otra llamada al mismo hashmap** (otro `ReserveSlot`, `ContainsKey`, `Remove`, o un `Reserve`/`Rehash` disparado internamente por estar cerca del límite de resize), el valor queda pisado. Usa siempre `m_last_pos_tbl` inmediatamente después de la llamada que lo dejó, nunca lo arrastres a través de otra operación sobre el mismo objeto.