Skip to content
Astera Technology

Check citation offsets after a Unicode representation change

Byte offsets are reused as code-point offsets and select a different substring.

Download complete case notes and answer keys ↓

Define the local evaluation task

A synthetic citation interface declares zero-based code-point offsets with the end excluded. The evaluator uses Array.from to form that coordinate sequence, checks whole positions within it and extracts the requested span. Node Buffer is used only to show separate UTF-8 byte length; byte counts must not be reused as these offsets.

  1. Declare the offset unit
  2. Check bounds in that coordinate sequence
  3. Extract span without borrowing byte counts

Invalid positions yield a span error; the evaluator does not truncate them silently.

Use the filled cases

Original synthetic input and literal answer key
CaseSupplied inputExpected structured outputDecision and reason
Chinese character span{"source":"A中B","start":1,"end":2}{"text":"中","pointLength":3,"byteLength":5}The declared coordinate picks one character while the source uses five UTF-8 bytes.
Byte endpoint misused{"source":"A中B","start":1,"end":4}{"error":"span-bounds","pointLength":3}Four is beyond the declared three-point sequence.
Supplementary symbol{"source":"A😀B","start":1,"end":2}{"text":"😀","pointLength":3,"byteLength":6}Array.from retains this symbol as one point in the supplied coordinate model.
Download editable CSV ↓

Work the supplementary-symbol case. Which output is required, and why?

Show the worked answer

{"text":"😀","pointLength":3,"byteLength":6} — Array.from retains this symbol as one point in the supplied coordinate model.

Run the self-contained evaluator

Save as .mjs and run with Node.js; supplied fixtures only
import assert from 'node:assert/strict';
import {Buffer} from 'node:buffer';

function evaluate(x){
 const points=Array.from(x.source);if(!Number.isInteger(x.start)||!Number.isInteger(x.end)||x.start<0||x.end<x.start||x.end>points.length)return {error:'span-bounds',pointLength:points.length};
 return {text:points.slice(x.start,x.end).join(''),pointLength:points.length,byteLength:Buffer.byteLength(x.source,'utf8')};
}

const cases = [
  {
    "id": "chinese-span",
    "input": {
      "source": "A中B",
      "start": 1,
      "end": 2
    },
    "expected": {
      "text": "中",
      "pointLength": 3,
      "byteLength": 5
    }
  },
  {
    "id": "byte-end-reused",
    "input": {
      "source": "A中B",
      "start": 1,
      "end": 4
    },
    "expected": {
      "error": "span-bounds",
      "pointLength": 3
    }
  },
  {
    "id": "supplementary-symbol",
    "input": {
      "source": "A😀B",
      "start": 1,
      "end": 2
    },
    "expected": {
      "text": "😀",
      "pointLength": 3,
      "byteLength": 6
    }
  }
];
for (const c of cases) assert.deepEqual(evaluate(c.input), c.expected, c.id);
console.log(JSON.stringify({checked:cases.length,caseIds:cases.map(c=>c.id)}));
Download runnable example ↓

Keep offset unit beside each citation record. This code-point model is not a visual glyph/grapheme model; choose a separate interface contract if selection follows displayed clusters.

Related resources