skip to content

Why does an in-place code-unit swap loop mangle text with emoji or accents?

level: middleimportance: should knowfreq 52%

answer

  1. the loop mirrors slots, not meaning
  2. some characters occupy more than one slot
  3. some marks modify the code point before them
  4. mirroring flips the inside of a character too
  5. reverse whole clusters, not their parts

basics

~20 s

Swapping code units also flips the internal order of multi-unit characters: a surrogate pair comes out low half first and becomes invalid, and a combining accent lands before the letter it belonged to. Correct order-reversal walks grapheme clusters.

solid answer

~40 s

The two-pointer swap is correct for what it operates on — it reverses an array of code units — but code units are not characters. A code point above U+FFFF occupies two UTF-16 units, and reversing puts the low half first, which is not valid text. A combining mark is a separate code point that modifies the code point *before* it, so reversing moves it onto a different base letter and the accent visibly jumps. Multi-code-point sequences such as flags and joined emoji come apart entirely. To reverse text you segment it into grapheme clusters, reverse the sequence of clusters, and leave each cluster's internal order untouched. Only for guaranteed-ASCII input are the naive loop and the correct answer the same.

code

pseudocode · 10 lines
pseudocode
// a is an array of code units; reverse it in place
i = 0
j = length(a) - 1
while i < j:
    swap(a[i], a[j])
    i = i + 1
    j = j - 1
// every slot is now mirrored, including the slots
// INSIDE a surrogate pair and the mark-after-base order
// of a combining accent

go deeper

for a junior

Know that the swap loop mirrors storage slots, and that some characters take more than one slot, so the output can contain broken characters even though the loop itself is correct.

for a middle

Explain both failure layers with a concrete artifact each: a surrogate pair emitted in the wrong order, and a combining mark landing on the wrong base letter. Name grapheme clusters as the correct unit.

for a senior

Discuss the cost of correctness — cluster segmentation keeps O(n) time but gives up the in-place space bound — and when a guaranteed-ASCII input makes the naive loop the right, cheapest choice.

for a principal

Take the position on where text-aware handling is mandatory versus wasteful across a codebase, and how you keep the naive loop from spreading through helpers that were only ever tested on ASCII.

## The loop is not buggy; its unit is The two-pointer swap is one of the first algorithms anyone learns, and it is correct: for an array of `n` elements it produces the mirrored array in `n/2` swaps, O(n) time and O(1) extra space. Nothing about the loop is wrong. What is wrong is the assumption that one array slot equals one character. That assumption fails at two independent layers, and a good answer separates them. ## Layer one: a character can be several storage slots In UTF-16, code points above U+FFFF are stored as a surrogate pair — a high unit followed by a low unit, drawn from ranges reserved for exactly this. Mirroring the array puts the low unit first. The pair is now in an order that no decoder accepts, so the output contains two unpaired surrogates instead of one emoji: replacement glyphs on screen, and a hard failure or a substitution when the text is encoded for storage or transport. UTF-8 has the same problem with different arithmetic. A code point is a lead byte followed by one to three continuation bytes; reversing the byte array puts continuations first, which is not decodable. The lesson is identical: reversing the *storage* is not reversing the *text*. This layer can be fixed by iterating code points instead of code units — decode, collect, reverse the code points, re-encode. Many candidates stop here, and it is a real improvement. It is not the answer. ## Layer two: a character can be several code points Unicode does not promise one code point per visible character. Several code points routinely combine into a single **grapheme cluster** — the thing a reader points at and calls a character: - **Combining marks.** An accented letter can be written as a base letter followed by a combining mark, and the mark applies to the code point *preceding* it. Reverse the code points and every mark lands after a different letter, so the accents visibly slide one position: text that was correct becomes text that is merely wrong-looking, with no invalid data anywhere to detect. - **Regional indicators.** A flag is two regional-indicator code points, valid on their own as letters. Reversed, the pair either forms a different flag or falls apart into two letters. - **Joined sequences.** Emoji built with zero-width joiners — a multi-person group, a profession, a skin-tone modifier applied to a base emoji — are runs of code points glued together. Reversing splits them into their components, and the modifier attaches to whatever now precedes it. Note the difference in symptom: layer one produces *invalid* output that tooling can detect; layer two produces *valid* output that means something else. The second is worse, because nothing downstream will flag it. ## What correct looks like Segment the text into grapheme clusters, reverse the sequence of clusters, and preserve each cluster's internal order. That is still O(n) — segmentation is a single left-to-right pass with bounded lookahead — but it now costs O(n) auxiliary space in the general case, because clusters have different widths and cannot be mirrored in place slot by slot. That tradeoff is worth stating out loud: correctness here buys back the O(1) space the naive loop advertised. A legitimate shortcut exists. If the input is provably ASCII — a hex digest, a base-encoded token, a machine-generated identifier — the code-unit loop is exactly right, and reaching for cluster segmentation is over-engineering. The senior move is not "always segment"; it is knowing which guarantee your input carries and saying so. ## Why this is asked so often Order-reversal is the smallest possible probe that separates a candidate who thinks of text as an array of characters from one who knows it is an encoded sequence with structure. The naive answer passes every ASCII test, so the question also reveals whether someone reasons about inputs they have not seen. Ecosystems differ in how much of this they hide — some expose text as UTF-8 bytes, others as UTF-16 code units, others as code points — so the same naive loop produces three different flavours of wrong depending on where it runs, and only cluster-aware segmentation is right everywhere. ## Answering well Say what the loop actually reverses. Name both layers — multi-unit characters and multi-code-point clusters — and give one concrete artifact for each: an invalid unpaired surrogate, and an accent attached to the wrong letter. Finish with the cost: cluster-aware reversal keeps O(n) time but gives up the in-place space bound, and the naive loop is still correct when the input is guaranteed ASCII.

  • If you reverse by code point instead of code unit, what still breaks?
    Everything built from multiple code points. A combining mark modifies the code point before it, so it ends up on the wrong base letter; a flag made of two regional indicators reverses into a different flag or into plain letters; a joined emoji sequence splits into its parts. The output stays valid text, which is why nothing downstream detects it.
  • What does cluster-aware reversal cost compared with the naive loop?
    Time stays O(n) — segmentation is one left-to-right pass with bounded lookahead — but you lose the O(1) space bound. Clusters have different widths, so you cannot mirror them slot by slot in place; you build the reversed sequence in O(n) auxiliary space. That is the honest trade: correctness for the in-place guarantee.
  • When is the naive code-unit loop the right answer?
    When the input is provably restricted to single-unit characters — a hexadecimal digest, a base-encoded token, a machine-generated identifier, anything ASCII by construction. Then the loop is correct, in place and optimal, and cluster segmentation is wasted work. Say the guarantee out loud, because the correctness depends entirely on it.

saying these in an interview costs you the question

  • Claims the two-pointer loop is correct for all text
  • Fixes only surrogate pairs and calls it done
  • Thinks one code point always equals one visible character
  • Assumes combining marks precede their base letter
  • Insists correct reversal is still O(1) auxiliary space

context