Skip to content

String NFC/NFD normalization and lone surrogate handling #18

Closed

Description

@proggeramlug

Description

Three related Unicode string gaps (all in test_gap_string_methods):

  1. NFC/NFD normalization: "é".normalize("NFC").length returns 2 instead of 1; "é".normalize("NFD").length returns 3 instead of 2. Perry stores strings as UTF-8 bytes and .length returns byte length, not codepoint/grapheme count.

  2. isWellFormed() / toWellFormed(): Lone surrogate detection returns wrong results. "\uD800".isWellFormed() returns true (should be false).

  3. UTF-16 length semantics: JS .length counts UTF-16 code units, Perry counts bytes. Diverges on any string with characters outside BMP.

These are all manifestations of the same root issue: Perry uses UTF-8 byte-oriented string representation while JS uses UTF-16.

Activity

  1. added
    parityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem
    on Apr 12, 2026
  2. proggeramlug commented on Apr 14, 2026

    @proggeramlug
    ContributorAuthor

    Partially resolved in v0.5.20.

    .length now returns UTF-16 code units instead of UTF-8 byte count. All position-based string APIs (charAt, slice, substring, indexOf, lastIndexOf, padStart, padEnd) converted to UTF-16 indexing.

    Results:

    • "café".length → 4 (was 5)
    • "日本語".length → 3 (was 9)
    • "😀".length → 2 (was 4)
    • NFC/NFD .normalize().length now matches Node

    test_gap_string_methods diffs: 4 → 2. Remaining 2 are lone surrogate handling (isWellFormed/toWellFormed) — needs WTF-8 string support, tracked separately.

  3. proggeramlug commented on Apr 14, 2026

    @proggeramlug
    ContributorAuthor

    Two of three sub-issues resolved in v0.5.20:

    ✅ NFC/NFD normalization length — .normalize("NFC").length now returns correct codepoint count
    ✅ UTF-16 length semantics — "café".length → 4, "日本語".length → 3, "😀".length → 2

    Remaining issue (lone surrogate handling) tracked separately in a new ticket.

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

    parityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions