ocaml-json: eliminate tuple and closure allocations in hot paths
Two changes together:
1. Remove [get_line_pos] which allocated a 2-tuple on every call.
Inline [d.line] and [d.line_start] at each call site and thread
them as two int labelled args [~first_line_num ~first_line_byte]
through the error/textloc helpers (err_to_here, err_exp_in_const,
err_exp_esc, err_unclosed_string, err_illegal_ctrl_char,
textloc_to_current, textloc_prev_ascii_char,
error_meta_to_current). Roughly 45 call sites adapted.
2. Rewrite [skip_json_string] and [skip_json_number] as imperative
while-loops with a single [done_] flag instead of [let rec loop]
nested in the function body. Avoids the fresh closure allocated on
every invocation.
Memtrace deltas (field-access bench on canada+citm+twitter corpus):
get_line_pos 10.8% -> 0% (removed)
skip_json_number.loop 11.5% -> <1% (closure removed)
skip_json_string.loop 6.9% -> <1% (closure removed)
DOM mode geomean edged up from ~160 to ~172 MB/s (less pressure from
same get_line_pos fix). Field geomean stable at ~480 MB/s; further
wins require member-name interning or SIMD-style byte scanning for
object key dispatch.