runtime: String modification fixes

- For inline strings fix an off-by-one in the calculation of
  allocSlackBytes that was causing a string being resized to exactly 12
  bytes to attempt to convert to a new heap string. The result was the
  end of the inline string was overwritten by garbage and the
  SharedByteArray was leaked

- Our logic for triggering heap reallocations when replacing from the
  same string did not work for inline strings. Instead, detect when our
  source pattern overlaps the destination and create a temporary copy.

- Add more tests to catch similar issues in the future
stdlib: Implement (string-fill!)

This is a wrapper around StringCell::replace(). The tests require the
previous commit to work correctly.
stdlib: Implement (numerator), (denominator)

For exact integers this is trivial. We use the value as the numerator
and 1 as the denominator

For flonums this is much trickier. We use frexp() to split the float in
to a significand and exponent. We then reduce the resulting fraction to
lowest terms.

This produces a numerator and denominator that exactly capture the
original flonum. However, this is subject to the issues of floating
point representations. For example, 0.1 can't be represented exactly in
binary floating point and its closest floating point value produces a
fraction with very large terms.