FastCollections-FastHashMap/Src/ColHash/HashBases/README.md

71 lines
4.3 KiB
Markdown
Raw Permalink Normal View History

2026-09-03 11:03:46 -05:00
# 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.