Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions lib/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,31 @@ class StringType extends PrimitiveType {
return tap1.matchBytes(tap2);
}

compare (val1, val2) {
// The spec mandates byte (i.e. code point) order, matching the UTF-8
// ordering used by `_match` and `compareBuffers`. Comparing by JS string
// code units (the default `<`) would diverge for supplementary plane
// characters, since their surrogate units sort before BMP code points
// `U+E000` through `U+FFFF`.
if (val1 === val2) {
return 0;
}
let n1 = val1.length;
let n2 = val2.length;
let i = 0;
let j = 0;
while (i < n1 && j < n2) {
let c1 = val1.codePointAt(i);
let c2 = val2.codePointAt(j);
if (c1 !== c2) {
return c1 < c2 ? -1 : 1;
}
i += c1 > 0xffff ? 2 : 1;
j += c2 > 0xffff ? 2 : 1;
}
return utils.compare(n1 - i, n2 - j);
}

_update (resolver, type) {
switch (type.typeName) {
case 'bytes':
Expand Down
29 changes: 29 additions & 0 deletions test/test_types.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,35 @@ suite('types', () => {
assert.equal(s, t.fromBuffer(b)); // Also checks exact length match.
});

test('compare', () => {
let t = Type.forSchema('string');
assert.equal(t.compare('', ''), 0);
assert.equal(t.compare('a', 'b'), -1);
assert.equal(t.compare('b', 'a'), 1);
assert.equal(t.compare('a', 'ab'), -1);
});

test('compare ordering matches compareBuffers', () => {
// The spec mandates byte (code point) order, which `compareBuffers`
// already follows. `compare` must agree, including for supplementary
// plane characters whose UTF-16 surrogate units sort differently.
let t = Type.forSchema('string');
let pairs = [
['', '\u{1f600}'], // U+E000 (BMP) < U+1F600 (astral)
['z', '\u{1f600}'],
['\u{1f600}', '\u{1f601}']
];
pairs.forEach((pair) => {
let direct = t.compare(pair[0], pair[1]);
let buffers = t.compareBuffers(
t.toBuffer(pair[0]),
t.toBuffer(pair[1])
);
assert.equal(direct, buffers, pair.join(' vs '));
assert.equal(direct, -1, pair.join(' vs '));
});
});

});

suite('NullType', () => {
Expand Down