If GC occurs in `mrb_realloc()` in `ht_init()` called from `ar_set()`, the following inconsistency occurs:
- If `h_ht_on()` is called before `mrb_realloc()`, `hash->hsh.ht` is referenced instead of `hash->hsh.ea` during GC.
- If the pointer is changed by `ea_adjust()` in `ar_set()`, `hash->hsh.ea` (`hash->hsh.ht`) is referenced in GC before the change.
These modifications can be resolved by changing the order of processing.
However, if a `NoMemoryError` exception is raised, it is presumed that the size of the "AR" will be exceeded and the unintended state will continue.
To prevent this, elements should be added after they have been converted to "HT".
In theory, we should not call lzb() with 0, and we assume checks are
done before calling it. But we got a report that we call lzb(0) in some
use-case. We could not reproduce the issue, so we add this guard.
`mrb_class_find_path` resolves a `char*` pointer to a class name string
by calling `mrb_class_name`. It then allocates a new string with
capacity 40 to copy that `char*` into.
https://github.com/mruby/mruby/blob/e04184185ab43b94980550e850d8813a415fa438/src/variable.c#L1111-L1112
`mrb_class_name` resolves the class name via `class_name_str`, which
returns an `mrb_value` with type tag `MRB_TT_STRING` and backed by an
`RString*`. Then `mrb_class_name` extracts the `RSTRING_PTR`:
https://github.com/mruby/mruby/blob/e04184185ab43b94980550e850d8813a415fa438/src/class.c#L2133-L2134
That `RString*`-backed `mrb_value` ultimately comes from `mrb_class_path`
which resolves the string from the symbol table:
https://github.com/mruby/mruby/blob/e04184185ab43b94980550e850d8813a415fa438/src/class.c#L2111
The allocation of the target `str` after resolving the class name
`mrb_value` and extracting its pointer is fragile and assumes the
`RString*` is "static". If the `RString*` is not static, the
interleaving of extracting the `RSTRING_PTR` followed by a subsequent
allocation might result in the class name `mrb_value` being garbage
collected, which will leave the extracted pointer invalid.
Fix this bad interleaving by allocating the destination string first
before taking a raw pointer to an `RString*`.
Instead of splitting mp_limb by bit operations (using HIGH/LOW macros),
now we use mp_limb2 (which is bigger integer size, e.g uint64_t). It
makes operations (especially mulitiplication) a lot faster. As a side
effect, it also reduces memory consumption (16,452,033 -> 15,545,853 on
my Linux machine).
Other changes:
- trailing zeros are removed after operations.
- division algorithm is simplified.