Skip to content

perf(strings): cache character length and add ASCII fast paths #220

Description

@timfennis

Motivation

Andy C++ strings use Rc<RefCell> and expose character-based sequence semantics. Operations such as length, bounds checking, negative-index resolution, indexing, and slicing can scan UTF-8 text repeatedly. Most Advent of Code input is ASCII, where byte offsets and character offsets are identical.

Proposal

Introduce a wrapper with a private String and cached character length:

struct AndyCppString {
    inner: String,
    char_len: usize,
}

A separate ASCII flag is unnecessary while the inner value remains valid UTF-8: the string is ASCII exactly when char_len equals inner.len().

Store Rc<RefCell> in Object::String. Expose immutable text through as_str or AsRef, but do not expose &mut String, DerefMut, or AsMut. All mutation must go through wrapper methods so char_len cannot drift from the contents.

Mutation rules

  • Constructors calculate char_len once, using byte length directly for ASCII input.
  • Concatenation and append add the cached lengths.
  • Character-range replacement subtracts the removed character count and adds the replacement length.
  • Reversal preserves the length.
  • Unicode transformations that may change the number of scalar values construct a new wrapper or recount after mutation.
  • Debug builds assert that char_len matches inner.chars().count() after mutations.

The native-function conversion machinery currently permits functions such as append and reverse to receive &mut String. Migrate those functions to the wrapper or another invariant-preserving API.

Expected impact

  • O(1) string length and negative-index bounds resolution.
  • O(1) ASCII element access and byte-range slicing after bounds checking.
  • Non-ASCII access still scans for the requested UTF-8 boundary, but avoids a separate full length scan.
  • Existing UTF-8 and character-index semantics remain unchanged.

Acceptance criteria

  • Object::String uses the wrapper throughout the VM and standard library.
  • No public mutable access to the inner String can bypass cache maintenance.
  • Existing string behavior tests pass, including multibyte indexing, slicing, assignment, iteration, and native string functions.
  • New tests exercise cache maintenance after every supported mutation category.
  • Benchmarks compare repeated ASCII indexing and length calls before and after the change.

Non-goals

This issue does not change strings to arbitrary byte sequences or change indexing from Unicode scalar values to bytes.

AI disclosure

This issue was drafted with OpenAI Codex assistance.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions