@g9tm/syncdata 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/PARITY.md +71 -0
- package/README.md +71 -0
- package/THIRD-PARTY-NOTICES.md +31 -0
- package/dist/abstractions/digest.d.ts +27 -0
- package/dist/abstractions/digest.js +78 -0
- package/dist/abstractions/digest.js.map +1 -0
- package/dist/abstractions/enums.d.ts +42 -0
- package/dist/abstractions/enums.js +60 -0
- package/dist/abstractions/enums.js.map +1 -0
- package/dist/abstractions/errors.d.ts +72 -0
- package/dist/abstractions/errors.js +90 -0
- package/dist/abstractions/errors.js.map +1 -0
- package/dist/abstractions/guid.d.ts +24 -0
- package/dist/abstractions/guid.js +93 -0
- package/dist/abstractions/guid.js.map +1 -0
- package/dist/abstractions/key.d.ts +16 -0
- package/dist/abstractions/key.js +47 -0
- package/dist/abstractions/key.js.map +1 -0
- package/dist/abstractions/long.d.ts +103 -0
- package/dist/abstractions/long.js +351 -0
- package/dist/abstractions/long.js.map +1 -0
- package/dist/abstractions/parameters.d.ts +49 -0
- package/dist/abstractions/parameters.js +153 -0
- package/dist/abstractions/parameters.js.map +1 -0
- package/dist/brotli-js/decode.d.ts +8 -0
- package/dist/brotli-js/decode.js +2004 -0
- package/dist/brotli-js/decode.js.map +1 -0
- package/dist/brotli-js/index.d.ts +5 -0
- package/dist/brotli-js/index.js +19 -0
- package/dist/brotli-js/index.js.map +1 -0
- package/dist/client/engine.d.ts +110 -0
- package/dist/client/engine.js +1158 -0
- package/dist/client/engine.js.map +1 -0
- package/dist/client/model.d.ts +61 -0
- package/dist/client/model.js +154 -0
- package/dist/client/model.js.map +1 -0
- package/dist/client/progress.d.ts +101 -0
- package/dist/client/progress.js +251 -0
- package/dist/client/progress.js.map +1 -0
- package/dist/client/runs.d.ts +219 -0
- package/dist/client/runs.js +239 -0
- package/dist/client/runs.js.map +1 -0
- package/dist/client/storeTypes.d.ts +428 -0
- package/dist/client/storeTypes.js +256 -0
- package/dist/client/storeTypes.js.map +1 -0
- package/dist/client/transport.d.ts +19 -0
- package/dist/client/transport.js +2 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/abort.d.ts +21 -0
- package/dist/internal/abort.js +125 -0
- package/dist/internal/abort.js.map +1 -0
- package/dist/internal/base64.d.ts +8 -0
- package/dist/internal/base64.js +85 -0
- package/dist/internal/base64.js.map +1 -0
- package/dist/internal/dotnetFormat.d.ts +32 -0
- package/dist/internal/dotnetFormat.js +207 -0
- package/dist/internal/dotnetFormat.js.map +1 -0
- package/dist/internal/md5.d.ts +2 -0
- package/dist/internal/md5.js +97 -0
- package/dist/internal/md5.js.map +1 -0
- package/dist/internal/sha256.d.ts +20 -0
- package/dist/internal/sha256.js +153 -0
- package/dist/internal/sha256.js.map +1 -0
- package/dist/internal/utf8.d.ts +8 -0
- package/dist/internal/utf8.js +143 -0
- package/dist/internal/utf8.js.map +1 -0
- package/dist/node/index.d.ts +9 -0
- package/dist/node/index.js +35 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/sqliteDriver.d.ts +11 -0
- package/dist/node/sqliteDriver.js +252 -0
- package/dist/node/sqliteDriver.js.map +1 -0
- package/dist/protocol/dtos.d.ts +280 -0
- package/dist/protocol/dtos.js +33 -0
- package/dist/protocol/dtos.js.map +1 -0
- package/dist/protocol/protocol.d.ts +36 -0
- package/dist/protocol/protocol.js +63 -0
- package/dist/protocol/protocol.js.map +1 -0
- package/dist/protocol/rowBatch/columnLayout.d.ts +4 -0
- package/dist/protocol/rowBatch/columnLayout.js +309 -0
- package/dist/protocol/rowBatch/columnLayout.js.map +1 -0
- package/dist/protocol/rowBatch/format.d.ts +74 -0
- package/dist/protocol/rowBatch/format.js +163 -0
- package/dist/protocol/rowBatch/format.js.map +1 -0
- package/dist/protocol/rowBatch/reader.d.ts +119 -0
- package/dist/protocol/rowBatch/reader.js +459 -0
- package/dist/protocol/rowBatch/reader.js.map +1 -0
- package/dist/protocol/rowBatch/writer.d.ts +101 -0
- package/dist/protocol/rowBatch/writer.js +412 -0
- package/dist/protocol/rowBatch/writer.js.map +1 -0
- package/dist/protocol/wire.d.ts +32 -0
- package/dist/protocol/wire.js +329 -0
- package/dist/protocol/wire.js.map +1 -0
- package/dist/signalr/index.d.ts +2 -0
- package/dist/signalr/index.js +5 -0
- package/dist/signalr/index.js.map +1 -0
- package/dist/signalr/protocols.d.ts +17 -0
- package/dist/signalr/protocols.js +109 -0
- package/dist/signalr/protocols.js.map +1 -0
- package/dist/signalr/transport.d.ts +67 -0
- package/dist/signalr/transport.js +286 -0
- package/dist/signalr/transport.js.map +1 -0
- package/dist/sqlite/apply.d.ts +91 -0
- package/dist/sqlite/apply.js +657 -0
- package/dist/sqlite/apply.js.map +1 -0
- package/dist/sqlite/commands.d.ts +24 -0
- package/dist/sqlite/commands.js +167 -0
- package/dist/sqlite/commands.js.map +1 -0
- package/dist/sqlite/connection.d.ts +81 -0
- package/dist/sqlite/connection.js +322 -0
- package/dist/sqlite/connection.js.map +1 -0
- package/dist/sqlite/driver.d.ts +63 -0
- package/dist/sqlite/driver.js +9 -0
- package/dist/sqlite/driver.js.map +1 -0
- package/dist/sqlite/index.d.ts +6 -0
- package/dist/sqlite/index.js +7 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/internals.d.ts +63 -0
- package/dist/sqlite/internals.js +194 -0
- package/dist/sqlite/internals.js.map +1 -0
- package/dist/sqlite/options.d.ts +78 -0
- package/dist/sqlite/options.js +78 -0
- package/dist/sqlite/options.js.map +1 -0
- package/dist/sqlite/push.d.ts +9 -0
- package/dist/sqlite/push.js +272 -0
- package/dist/sqlite/push.js.map +1 -0
- package/dist/sqlite/query.d.ts +38 -0
- package/dist/sqlite/query.js +476 -0
- package/dist/sqlite/query.js.map +1 -0
- package/dist/sqlite/revocation.d.ts +28 -0
- package/dist/sqlite/revocation.js +337 -0
- package/dist/sqlite/revocation.js.map +1 -0
- package/dist/sqlite/sql.d.ts +60 -0
- package/dist/sqlite/sql.js +324 -0
- package/dist/sqlite/sql.js.map +1 -0
- package/dist/sqlite/store.d.ts +116 -0
- package/dist/sqlite/store.js +664 -0
- package/dist/sqlite/store.js.map +1 -0
- package/dist/sqlite/values.d.ts +29 -0
- package/dist/sqlite/values.js +288 -0
- package/dist/sqlite/values.js.map +1 -0
- package/dist/testing/index.d.ts +61 -0
- package/dist/testing/index.js +175 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The nil GUID in canonical form. */
|
|
2
|
+
export declare const EMPTY_GUID = "00000000-0000-0000-0000-000000000000";
|
|
3
|
+
/**
|
|
4
|
+
* Parses a GUID the way `Guid.TryParse` reads the forms G9SyncData meets — "D" (`8-4-4-4-12`), "N" (32 digits),
|
|
5
|
+
* "B" (`{D}`) and "P" (`(D)`), any case, surrounding white space ignored — and returns its 32 lower-case hex digits,
|
|
6
|
+
* or null. (The rare "X" form `{0x…,{0x…}}` is not accepted; PARITY.md.)
|
|
7
|
+
*/
|
|
8
|
+
export declare function guidDigits(text: string | null | undefined): string | null;
|
|
9
|
+
/** Canonical text of a GUID (`Guid.Parse(text).ToString("D")`), or null when `text` is not a GUID. */
|
|
10
|
+
export declare function tryNormalizeGuid(text: string | null | undefined): string | null;
|
|
11
|
+
/** Canonical text of a GUID; throws `SyntaxError` (the .NET `FormatException`) when `text` is not one. */
|
|
12
|
+
export declare function normalizeGuid(text: string): string;
|
|
13
|
+
/** "N" form: 32 lower-case hex digits (`Guid.ToString("N")`). */
|
|
14
|
+
export declare function guidToN(text: string): string;
|
|
15
|
+
/** Writes the 16 big-endian bytes of a GUID. */
|
|
16
|
+
export declare function writeGuidBytes(text: string, bytes: Uint8Array, offset: number): void;
|
|
17
|
+
/** Canonical text of 16 big-endian GUID bytes. */
|
|
18
|
+
export declare function readGuidBytes(bytes: Uint8Array, offset: number): string;
|
|
19
|
+
/**
|
|
20
|
+
* A random (version 4) GUID in canonical form, the `Guid.NewGuid()` twin. Uses `crypto.getRandomValues` where the
|
|
21
|
+
* runtime has it (Lynx's background thread has no `crypto`; it falls back to `Math.random`, which is enough for ids that
|
|
22
|
+
* are unique, not secret).
|
|
23
|
+
*/
|
|
24
|
+
export declare function newGuid(): string;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// GUID text and bytes, the twins of the .NET forms G9SyncData uses: canonical text is lower-case "D"
|
|
2
|
+
// (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, PostgreSQL's uuid::text), and the wire bytes are the big-endian (RFC 4122)
|
|
3
|
+
// order of that text (`Guid.TryWriteBytes(bigEndian: true)`).
|
|
4
|
+
const HEX_DIGIT = /^[0-9a-fA-F]{32}$/;
|
|
5
|
+
/** The nil GUID in canonical form. */
|
|
6
|
+
export const EMPTY_GUID = '00000000-0000-0000-0000-000000000000';
|
|
7
|
+
/**
|
|
8
|
+
* Parses a GUID the way `Guid.TryParse` reads the forms G9SyncData meets — "D" (`8-4-4-4-12`), "N" (32 digits),
|
|
9
|
+
* "B" (`{D}`) and "P" (`(D)`), any case, surrounding white space ignored — and returns its 32 lower-case hex digits,
|
|
10
|
+
* or null. (The rare "X" form `{0x…,{0x…}}` is not accepted; PARITY.md.)
|
|
11
|
+
*/
|
|
12
|
+
export function guidDigits(text) {
|
|
13
|
+
if (typeof text !== 'string')
|
|
14
|
+
return null;
|
|
15
|
+
let s = text.trim();
|
|
16
|
+
if (s.length === 38 && ((s.charAt(0) === '{' && s.charAt(37) === '}') || (s.charAt(0) === '(' && s.charAt(37) === ')'))) {
|
|
17
|
+
s = s.slice(1, 37);
|
|
18
|
+
}
|
|
19
|
+
let digits;
|
|
20
|
+
if (s.length === 36) {
|
|
21
|
+
if (s.charAt(8) !== '-' || s.charAt(13) !== '-' || s.charAt(18) !== '-' || s.charAt(23) !== '-')
|
|
22
|
+
return null;
|
|
23
|
+
digits = s.slice(0, 8) + s.slice(9, 13) + s.slice(14, 18) + s.slice(19, 23) + s.slice(24);
|
|
24
|
+
}
|
|
25
|
+
else if (s.length === 32) {
|
|
26
|
+
digits = s;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return HEX_DIGIT.test(digits) ? digits.toLowerCase() : null;
|
|
32
|
+
}
|
|
33
|
+
/** Canonical lower-case "D" text of 32 hex digits. */
|
|
34
|
+
function formatDigits(digits) {
|
|
35
|
+
return digits.slice(0, 8) + '-' + digits.slice(8, 12) + '-' + digits.slice(12, 16) + '-' + digits.slice(16, 20) + '-' + digits.slice(20);
|
|
36
|
+
}
|
|
37
|
+
/** Canonical text of a GUID (`Guid.Parse(text).ToString("D")`), or null when `text` is not a GUID. */
|
|
38
|
+
export function tryNormalizeGuid(text) {
|
|
39
|
+
const digits = guidDigits(text);
|
|
40
|
+
return digits === null ? null : formatDigits(digits);
|
|
41
|
+
}
|
|
42
|
+
/** Canonical text of a GUID; throws `SyntaxError` (the .NET `FormatException`) when `text` is not one. */
|
|
43
|
+
export function normalizeGuid(text) {
|
|
44
|
+
const canonical = tryNormalizeGuid(text);
|
|
45
|
+
if (canonical === null)
|
|
46
|
+
throw new SyntaxError(`'${text}' is not a GUID.`);
|
|
47
|
+
return canonical;
|
|
48
|
+
}
|
|
49
|
+
/** "N" form: 32 lower-case hex digits (`Guid.ToString("N")`). */
|
|
50
|
+
export function guidToN(text) {
|
|
51
|
+
const digits = guidDigits(text);
|
|
52
|
+
if (digits === null)
|
|
53
|
+
throw new SyntaxError(`'${text}' is not a GUID.`);
|
|
54
|
+
return digits;
|
|
55
|
+
}
|
|
56
|
+
/** Writes the 16 big-endian bytes of a GUID. */
|
|
57
|
+
export function writeGuidBytes(text, bytes, offset) {
|
|
58
|
+
const digits = guidDigits(text);
|
|
59
|
+
if (digits === null)
|
|
60
|
+
throw new SyntaxError(`'${text}' is not a GUID.`);
|
|
61
|
+
for (let i = 0; i < 16; i++)
|
|
62
|
+
bytes[offset + i] = parseInt(digits.substr(i * 2, 2), 16);
|
|
63
|
+
}
|
|
64
|
+
const HEX = '0123456789abcdef';
|
|
65
|
+
/** Canonical text of 16 big-endian GUID bytes. */
|
|
66
|
+
export function readGuidBytes(bytes, offset) {
|
|
67
|
+
let digits = '';
|
|
68
|
+
for (let i = 0; i < 16; i++) {
|
|
69
|
+
const b = bytes[offset + i];
|
|
70
|
+
digits += HEX.charAt(b >> 4) + HEX.charAt(b & 15);
|
|
71
|
+
}
|
|
72
|
+
return formatDigits(digits);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A random (version 4) GUID in canonical form, the `Guid.NewGuid()` twin. Uses `crypto.getRandomValues` where the
|
|
76
|
+
* runtime has it (Lynx's background thread has no `crypto`; it falls back to `Math.random`, which is enough for ids that
|
|
77
|
+
* are unique, not secret).
|
|
78
|
+
*/
|
|
79
|
+
export function newGuid() {
|
|
80
|
+
const bytes = new Uint8Array(16);
|
|
81
|
+
const cryptoObject = globalThis.crypto;
|
|
82
|
+
if (cryptoObject && typeof cryptoObject.getRandomValues === 'function') {
|
|
83
|
+
cryptoObject.getRandomValues(bytes);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
for (let i = 0; i < 16; i++)
|
|
87
|
+
bytes[i] = Math.floor(Math.random() * 256);
|
|
88
|
+
}
|
|
89
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
90
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
91
|
+
return readGuidBytes(bytes, 0);
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=guid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guid.js","sourceRoot":"","sources":["../../src/abstractions/guid.ts"],"names":[],"mappings":"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","sourcesContent":["// GUID text and bytes, the twins of the .NET forms G9SyncData uses: canonical text is lower-case \"D\"\n// (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, PostgreSQL's uuid::text), and the wire bytes are the big-endian (RFC 4122)\n// order of that text (`Guid.TryWriteBytes(bigEndian: true)`).\n\nconst HEX_DIGIT = /^[0-9a-fA-F]{32}$/;\n\n/** The nil GUID in canonical form. */\nexport const EMPTY_GUID = '00000000-0000-0000-0000-000000000000';\n\n/**\n * Parses a GUID the way `Guid.TryParse` reads the forms G9SyncData meets — \"D\" (`8-4-4-4-12`), \"N\" (32 digits),\n * \"B\" (`{D}`) and \"P\" (`(D)`), any case, surrounding white space ignored — and returns its 32 lower-case hex digits,\n * or null. (The rare \"X\" form `{0x…,{0x…}}` is not accepted; PARITY.md.)\n */\nexport function guidDigits(text: string | null | undefined): string | null {\n if (typeof text !== 'string') return null;\n let s = text.trim();\n if (s.length === 38 && ((s.charAt(0) === '{' && s.charAt(37) === '}') || (s.charAt(0) === '(' && s.charAt(37) === ')'))) {\n s = s.slice(1, 37);\n }\n let digits: string;\n if (s.length === 36) {\n if (s.charAt(8) !== '-' || s.charAt(13) !== '-' || s.charAt(18) !== '-' || s.charAt(23) !== '-') return null;\n digits = s.slice(0, 8) + s.slice(9, 13) + s.slice(14, 18) + s.slice(19, 23) + s.slice(24);\n } else if (s.length === 32) {\n digits = s;\n } else {\n return null;\n }\n return HEX_DIGIT.test(digits) ? digits.toLowerCase() : null;\n}\n\n/** Canonical lower-case \"D\" text of 32 hex digits. */\nfunction formatDigits(digits: string): string {\n return digits.slice(0, 8) + '-' + digits.slice(8, 12) + '-' + digits.slice(12, 16) + '-' + digits.slice(16, 20) + '-' + digits.slice(20);\n}\n\n/** Canonical text of a GUID (`Guid.Parse(text).ToString(\"D\")`), or null when `text` is not a GUID. */\nexport function tryNormalizeGuid(text: string | null | undefined): string | null {\n const digits = guidDigits(text);\n return digits === null ? null : formatDigits(digits);\n}\n\n/** Canonical text of a GUID; throws `SyntaxError` (the .NET `FormatException`) when `text` is not one. */\nexport function normalizeGuid(text: string): string {\n const canonical = tryNormalizeGuid(text);\n if (canonical === null) throw new SyntaxError(`'${text}' is not a GUID.`);\n return canonical;\n}\n\n/** \"N\" form: 32 lower-case hex digits (`Guid.ToString(\"N\")`). */\nexport function guidToN(text: string): string {\n const digits = guidDigits(text);\n if (digits === null) throw new SyntaxError(`'${text}' is not a GUID.`);\n return digits;\n}\n\n/** Writes the 16 big-endian bytes of a GUID. */\nexport function writeGuidBytes(text: string, bytes: Uint8Array, offset: number): void {\n const digits = guidDigits(text);\n if (digits === null) throw new SyntaxError(`'${text}' is not a GUID.`);\n for (let i = 0; i < 16; i++) bytes[offset + i] = parseInt(digits.substr(i * 2, 2), 16);\n}\n\nconst HEX = '0123456789abcdef';\n\n/** Canonical text of 16 big-endian GUID bytes. */\nexport function readGuidBytes(bytes: Uint8Array, offset: number): string {\n let digits = '';\n for (let i = 0; i < 16; i++) {\n const b = bytes[offset + i] as number;\n digits += HEX.charAt(b >> 4) + HEX.charAt(b & 15);\n }\n return formatDigits(digits);\n}\n\n/**\n * A random (version 4) GUID in canonical form, the `Guid.NewGuid()` twin. Uses `crypto.getRandomValues` where the\n * runtime has it (Lynx's background thread has no `crypto`; it falls back to `Math.random`, which is enough for ids that\n * are unique, not secret).\n */\nexport function newGuid(): string {\n const bytes = new Uint8Array(16);\n const cryptoObject = (globalThis as { crypto?: { getRandomValues?: (array: Uint8Array) => Uint8Array } }).crypto;\n if (cryptoObject && typeof cryptoObject.getRandomValues === 'function') {\n cryptoObject.getRandomValues(bytes);\n } else {\n for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n }\n bytes[6] = ((bytes[6] as number) & 0x0f) | 0x40;\n bytes[8] = ((bytes[8] as number) & 0x3f) | 0x80;\n return readGuidBytes(bytes, 0);\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { G9Long } from './long.js';
|
|
2
|
+
/**
|
|
3
|
+
* Canonical primary-key text (twin of `G9CSyncKey`). Every place a key is compared, hashed or stored as text uses this
|
|
4
|
+
* form, so the same row can never appear under two spellings. v1 supports single-column keys of type Guid, String,
|
|
5
|
+
* Int32 and Int64 (ADR-0006).
|
|
6
|
+
*/
|
|
7
|
+
export declare const G9SyncKey: {
|
|
8
|
+
/** True when `type` can be a primary key. */
|
|
9
|
+
isSupportedKeyType(type: number): boolean;
|
|
10
|
+
/** Canonical text of an integer key (invariant decimal, PostgreSQL `bigint::text`). */
|
|
11
|
+
fromInt64(value: number | G9Long): string;
|
|
12
|
+
/** Normalises a textual key of the given type to canonical form; throws `SyntaxError` (FormatException) when invalid. */
|
|
13
|
+
normalize(keyType: number, key: string): string;
|
|
14
|
+
/** Tries to normalise; null instead of throwing. */
|
|
15
|
+
tryNormalize(keyType: number, key: string | null | undefined): string | null;
|
|
16
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { G9ESyncColumnType } from './enums.js';
|
|
2
|
+
import { tryNormalizeGuid } from './guid.js';
|
|
3
|
+
import { G9Long } from './long.js';
|
|
4
|
+
/**
|
|
5
|
+
* Canonical primary-key text (twin of `G9CSyncKey`). Every place a key is compared, hashed or stored as text uses this
|
|
6
|
+
* form, so the same row can never appear under two spellings. v1 supports single-column keys of type Guid, String,
|
|
7
|
+
* Int32 and Int64 (ADR-0006).
|
|
8
|
+
*/
|
|
9
|
+
export const G9SyncKey = {
|
|
10
|
+
/** True when `type` can be a primary key. */
|
|
11
|
+
isSupportedKeyType(type) {
|
|
12
|
+
return type === G9ESyncColumnType.Guid || type === G9ESyncColumnType.String || type === G9ESyncColumnType.Int32 || type === G9ESyncColumnType.Int64;
|
|
13
|
+
},
|
|
14
|
+
/** Canonical text of an integer key (invariant decimal, PostgreSQL `bigint::text`). */
|
|
15
|
+
fromInt64(value) {
|
|
16
|
+
return value instanceof G9Long ? value.toString() : G9Long.fromNumber(value).toString();
|
|
17
|
+
},
|
|
18
|
+
/** Normalises a textual key of the given type to canonical form; throws `SyntaxError` (FormatException) when invalid. */
|
|
19
|
+
normalize(keyType, key) {
|
|
20
|
+
const canonical = G9SyncKey.tryNormalize(keyType, key);
|
|
21
|
+
if (canonical === null) {
|
|
22
|
+
if (!G9SyncKey.isSupportedKeyType(keyType))
|
|
23
|
+
throw new RangeError(`Unsupported key type ${keyType}.`);
|
|
24
|
+
throw new SyntaxError(`'${key}' is not a valid key of type ${keyType}.`);
|
|
25
|
+
}
|
|
26
|
+
return canonical;
|
|
27
|
+
},
|
|
28
|
+
/** Tries to normalise; null instead of throwing. */
|
|
29
|
+
tryNormalize(keyType, key) {
|
|
30
|
+
if (typeof key !== 'string')
|
|
31
|
+
return null;
|
|
32
|
+
switch (keyType) {
|
|
33
|
+
case G9ESyncColumnType.Guid:
|
|
34
|
+
return tryNormalizeGuid(key);
|
|
35
|
+
case G9ESyncColumnType.Int32:
|
|
36
|
+
case G9ESyncColumnType.Int64: {
|
|
37
|
+
const parsed = G9Long.tryParse(key);
|
|
38
|
+
return parsed === null ? null : parsed.toString();
|
|
39
|
+
}
|
|
40
|
+
case G9ESyncColumnType.String:
|
|
41
|
+
return key;
|
|
42
|
+
default:
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key.js","sourceRoot":"","sources":["../../src/abstractions/key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,6CAA6C;IAC7C,kBAAkB,CAAC,IAAY;QAC7B,OAAO,IAAI,KAAK,iBAAiB,CAAC,IAAI,IAAI,IAAI,KAAK,iBAAiB,CAAC,MAAM,IAAI,IAAI,KAAK,iBAAiB,CAAC,KAAK,IAAI,IAAI,KAAK,iBAAiB,CAAC,KAAK,CAAC;IACtJ,CAAC;IAED,uFAAuF;IACvF,SAAS,CAAC,KAAsB;QAC9B,OAAO,KAAK,YAAY,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC1F,CAAC;IAED,yHAAyH;IACzH,SAAS,CAAC,OAAe,EAAE,GAAW;QACpC,MAAM,SAAS,GAAG,SAAS,CAAC,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACvD,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,OAAO,CAAC;gBAAE,MAAM,IAAI,UAAU,CAAC,wBAAwB,OAAO,GAAG,CAAC,CAAC;YACrG,MAAM,IAAI,WAAW,CAAC,IAAI,GAAG,gCAAgC,OAAO,GAAG,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,oDAAoD;IACpD,YAAY,CAAC,OAAe,EAAE,GAA8B;QAC1D,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACzC,QAAQ,OAAO,EAAE,CAAC;YAChB,KAAK,iBAAiB,CAAC,IAAI;gBACzB,OAAO,gBAAgB,CAAC,GAAG,CAAC,CAAC;YAC/B,KAAK,iBAAiB,CAAC,KAAK,CAAC;YAC7B,KAAK,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBACpC,OAAO,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpD,CAAC;YACD,KAAK,iBAAiB,CAAC,MAAM;gBAC3B,OAAO,GAAG,CAAC;YACb;gBACE,OAAO,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;CACF,CAAC","sourcesContent":["import { G9ESyncColumnType } from './enums.js';\nimport { tryNormalizeGuid } from './guid.js';\nimport { G9Long } from './long.js';\n\n/**\n * Canonical primary-key text (twin of `G9CSyncKey`). Every place a key is compared, hashed or stored as text uses this\n * form, so the same row can never appear under two spellings. v1 supports single-column keys of type Guid, String,\n * Int32 and Int64 (ADR-0006).\n */\nexport const G9SyncKey = {\n /** True when `type` can be a primary key. */\n isSupportedKeyType(type: number): boolean {\n return type === G9ESyncColumnType.Guid || type === G9ESyncColumnType.String || type === G9ESyncColumnType.Int32 || type === G9ESyncColumnType.Int64;\n },\n\n /** Canonical text of an integer key (invariant decimal, PostgreSQL `bigint::text`). */\n fromInt64(value: number | G9Long): string {\n return value instanceof G9Long ? value.toString() : G9Long.fromNumber(value).toString();\n },\n\n /** Normalises a textual key of the given type to canonical form; throws `SyntaxError` (FormatException) when invalid. */\n normalize(keyType: number, key: string): string {\n const canonical = G9SyncKey.tryNormalize(keyType, key);\n if (canonical === null) {\n if (!G9SyncKey.isSupportedKeyType(keyType)) throw new RangeError(`Unsupported key type ${keyType}.`);\n throw new SyntaxError(`'${key}' is not a valid key of type ${keyType}.`);\n }\n return canonical;\n },\n\n /** Tries to normalise; null instead of throwing. */\n tryNormalize(keyType: number, key: string | null | undefined): string | null {\n if (typeof key !== 'string') return null;\n switch (keyType) {\n case G9ESyncColumnType.Guid:\n return tryNormalizeGuid(key);\n case G9ESyncColumnType.Int32:\n case G9ESyncColumnType.Int64: {\n const parsed = G9Long.tryParse(key);\n return parsed === null ? null : parsed.toString();\n }\n case G9ESyncColumnType.String:\n return key;\n default:\n return null;\n }\n },\n};\n"]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An exact signed 64-bit integer without BigInt (Lynx's PrimJS does not guarantee it): two 32-bit halves, `hi` signed
|
|
3
|
+
* and `lo` unsigned. It is the twin of .NET `long` wherever a value may exceed ±(2^53 − 1) and must stay exact:
|
|
4
|
+
* reconciliation digests and row hashes, dirty masks (up to 62 bits), DateTime ticks, int64 column values.
|
|
5
|
+
*
|
|
6
|
+
* Everything else in the protocol that .NET types as `long` (versions, batch ids, row counts, byte counts) is a plain
|
|
7
|
+
* `number` in TypeScript: those values are far below 2^53 in practice.
|
|
8
|
+
*/
|
|
9
|
+
export declare class G9Long {
|
|
10
|
+
/** High 32 bits, signed (two's complement). */
|
|
11
|
+
readonly hi: number;
|
|
12
|
+
/** Low 32 bits, unsigned. */
|
|
13
|
+
readonly lo: number;
|
|
14
|
+
constructor(hi: number, lo: number);
|
|
15
|
+
static readonly ZERO: G9Long;
|
|
16
|
+
static readonly ONE: G9Long;
|
|
17
|
+
static readonly MINUS_ONE: G9Long;
|
|
18
|
+
static readonly MIN_VALUE: G9Long;
|
|
19
|
+
static readonly MAX_VALUE: G9Long;
|
|
20
|
+
/** From any integer `number`. Exact for safe integers; larger magnitudes are taken from the double as it is. */
|
|
21
|
+
static fromNumber(value: number): G9Long;
|
|
22
|
+
/** From the two halves (hi signed or unsigned, lo unsigned or signed: both are normalised). */
|
|
23
|
+
static fromBits(hi: number, lo: number): G9Long;
|
|
24
|
+
/**
|
|
25
|
+
* Parses invariant decimal text (optional sign, digits only, no white space), the .NET
|
|
26
|
+
* `long.Parse(s, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture)` twin. Throws `RangeError` for anything
|
|
27
|
+
* else or a value outside the int64 range.
|
|
28
|
+
*/
|
|
29
|
+
static parse(text: string): G9Long;
|
|
30
|
+
/** {@link parse} that returns null instead of throwing. */
|
|
31
|
+
static tryParse(text: string | null | undefined): G9Long | null;
|
|
32
|
+
/** Coerces a number, decimal text or G9Long. */
|
|
33
|
+
static from(value: G9Long | number | string | {
|
|
34
|
+
int64: string;
|
|
35
|
+
}): G9Long;
|
|
36
|
+
/** Reads 8 little-endian bytes. */
|
|
37
|
+
static readLE(bytes: Uint8Array, offset: number): G9Long;
|
|
38
|
+
/** Reads 8 big-endian bytes. */
|
|
39
|
+
static readBE(bytes: Uint8Array, offset: number): G9Long;
|
|
40
|
+
/** Writes 8 little-endian bytes. */
|
|
41
|
+
writeLE(bytes: Uint8Array, offset: number): void;
|
|
42
|
+
isZero(): boolean;
|
|
43
|
+
isNegative(): boolean;
|
|
44
|
+
/** True when the value lies within ±(2^53 − 1), i.e. {@link toNumber} is exact. */
|
|
45
|
+
isSafe(): boolean;
|
|
46
|
+
/** The value as a number: exact when {@link isSafe}, the nearest double otherwise. */
|
|
47
|
+
toNumber(): number;
|
|
48
|
+
equals(other: G9Long): boolean;
|
|
49
|
+
/** Signed comparison: negative, zero or positive. */
|
|
50
|
+
compare(other: G9Long): number;
|
|
51
|
+
negate(): G9Long;
|
|
52
|
+
not(): G9Long;
|
|
53
|
+
and(other: G9Long): G9Long;
|
|
54
|
+
or(other: G9Long): G9Long;
|
|
55
|
+
xor(other: G9Long): G9Long;
|
|
56
|
+
/** Wrapping addition (two's complement), like unchecked C#. */
|
|
57
|
+
add(other: G9Long): G9Long;
|
|
58
|
+
subtract(other: G9Long): G9Long;
|
|
59
|
+
/** Wrapping multiplication by a small non-negative integer (< 2^21). */
|
|
60
|
+
multiplySmall(factor: number): G9Long;
|
|
61
|
+
/** Shift left (C# `<<` with the count masked to 0..63). */
|
|
62
|
+
shiftLeft(count: number): G9Long;
|
|
63
|
+
/** Arithmetic shift right (C# `>>` on long). */
|
|
64
|
+
shiftRight(count: number): G9Long;
|
|
65
|
+
/** Logical shift right (C# `>>>` / ulong `>>`). */
|
|
66
|
+
shiftRightUnsigned(count: number): G9Long;
|
|
67
|
+
/** True when bit `index` (0..63) is set. */
|
|
68
|
+
testBit(index: number): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Divides the value as UNSIGNED 64 bits by a small divisor (1..2^21) and returns quotient and remainder. Used for
|
|
71
|
+
* decimal formatting and tick arithmetic.
|
|
72
|
+
*/
|
|
73
|
+
divModUnsignedSmall(divisor: number): {
|
|
74
|
+
quotient: G9Long;
|
|
75
|
+
remainder: number;
|
|
76
|
+
};
|
|
77
|
+
/** Invariant decimal text, the .NET `long.ToString(CultureInfo.InvariantCulture)` twin. */
|
|
78
|
+
toString(): string;
|
|
79
|
+
/** The value as UNSIGNED decimal text (C# `ulong`). */
|
|
80
|
+
toUnsignedString(): string;
|
|
81
|
+
/**
|
|
82
|
+
* JSON form: a sentinel string that {@link stringifyJson} turns into a bare JSON number, so `JSON.stringify` of a DTO
|
|
83
|
+
* holding a G9Long writes the exact integer.
|
|
84
|
+
*/
|
|
85
|
+
toJSON(): string;
|
|
86
|
+
/** The SQLite driver form (G9LynxControls `G9SqlValue`): a number when safe, `{ int64 }` otherwise. */
|
|
87
|
+
toSqlValue(): number | {
|
|
88
|
+
int64: string;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** Marks a G9Long inside `JSON.stringify` output (a NUL, so no ordinary text produces it). */
|
|
92
|
+
export declare const LONG_SENTINEL = "\0g9long:";
|
|
93
|
+
/** `JSON.stringify` that writes every {@link G9Long} as an exact JSON number. */
|
|
94
|
+
export declare function stringifyJson(value: unknown): string;
|
|
95
|
+
/** Turns the G9Long sentinels in `JSON.stringify` output (G9Long.toJSON) into bare JSON numbers. */
|
|
96
|
+
export declare function replaceLongSentinels(text: string): string;
|
|
97
|
+
/**
|
|
98
|
+
* `JSON.parse` that keeps integers outside ±(2^53 − 1) exact: they come back as {@link G9Long} instead of a rounded
|
|
99
|
+
* number. Text without such an integer takes the plain `JSON.parse` path.
|
|
100
|
+
*/
|
|
101
|
+
export declare function parseJson(text: string): unknown;
|
|
102
|
+
/** A protocol `long` read as a number (exact when it fits; a G9Long rounds to the nearest double). */
|
|
103
|
+
export declare function longToNumber(value: unknown): number;
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An exact signed 64-bit integer without BigInt (Lynx's PrimJS does not guarantee it): two 32-bit halves, `hi` signed
|
|
3
|
+
* and `lo` unsigned. It is the twin of .NET `long` wherever a value may exceed ±(2^53 − 1) and must stay exact:
|
|
4
|
+
* reconciliation digests and row hashes, dirty masks (up to 62 bits), DateTime ticks, int64 column values.
|
|
5
|
+
*
|
|
6
|
+
* Everything else in the protocol that .NET types as `long` (versions, batch ids, row counts, byte counts) is a plain
|
|
7
|
+
* `number` in TypeScript: those values are far below 2^53 in practice.
|
|
8
|
+
*/
|
|
9
|
+
export class G9Long {
|
|
10
|
+
constructor(hi, lo) {
|
|
11
|
+
this.hi = hi | 0;
|
|
12
|
+
this.lo = lo >>> 0;
|
|
13
|
+
}
|
|
14
|
+
/** From any integer `number`. Exact for safe integers; larger magnitudes are taken from the double as it is. */
|
|
15
|
+
static fromNumber(value) {
|
|
16
|
+
if (!isFinite(value) || Math.floor(value) !== value)
|
|
17
|
+
throw new RangeError(`${value} is not an integer.`);
|
|
18
|
+
if (value >= 9223372036854775807)
|
|
19
|
+
return G9Long.MAX_VALUE;
|
|
20
|
+
if (value <= -9223372036854775808)
|
|
21
|
+
return G9Long.MIN_VALUE;
|
|
22
|
+
if (value < 0)
|
|
23
|
+
return G9Long.fromNumber(-value).negate();
|
|
24
|
+
const hi = Math.floor(value / TWO_32);
|
|
25
|
+
return new G9Long(hi, value - hi * TWO_32);
|
|
26
|
+
}
|
|
27
|
+
/** From the two halves (hi signed or unsigned, lo unsigned or signed: both are normalised). */
|
|
28
|
+
static fromBits(hi, lo) {
|
|
29
|
+
return new G9Long(hi, lo);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parses invariant decimal text (optional sign, digits only, no white space), the .NET
|
|
33
|
+
* `long.Parse(s, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture)` twin. Throws `RangeError` for anything
|
|
34
|
+
* else or a value outside the int64 range.
|
|
35
|
+
*/
|
|
36
|
+
static parse(text) {
|
|
37
|
+
const parsed = G9Long.tryParse(text);
|
|
38
|
+
if (!parsed)
|
|
39
|
+
throw new RangeError(`'${text}' is not a 64-bit integer.`);
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
42
|
+
/** {@link parse} that returns null instead of throwing. */
|
|
43
|
+
static tryParse(text) {
|
|
44
|
+
if (typeof text !== 'string')
|
|
45
|
+
return null;
|
|
46
|
+
const m = /^([+-]?)(\d+)$/.exec(text);
|
|
47
|
+
if (!m)
|
|
48
|
+
return null;
|
|
49
|
+
const negative = m[1] === '-';
|
|
50
|
+
const digits = m[2];
|
|
51
|
+
// Four 16-bit limbs, little-endian; value = value * 10 + digit, failing past 2^64.
|
|
52
|
+
const limbs = [0, 0, 0, 0];
|
|
53
|
+
for (let i = 0; i < digits.length; i++) {
|
|
54
|
+
let carry = digits.charCodeAt(i) - 48;
|
|
55
|
+
for (let k = 0; k < 4; k++) {
|
|
56
|
+
const v = limbs[k] * 10 + carry;
|
|
57
|
+
limbs[k] = v & 0xffff;
|
|
58
|
+
carry = Math.floor(v / 65536);
|
|
59
|
+
}
|
|
60
|
+
if (carry !== 0)
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const hiU = (limbs[3] * 65536 + limbs[2]) >>> 0;
|
|
64
|
+
const lo = (limbs[1] * 65536 + limbs[0]) >>> 0;
|
|
65
|
+
// Range: magnitude ≤ 2^63 − 1, or exactly 2^63 when negative.
|
|
66
|
+
if (hiU > 0x7fffffff) {
|
|
67
|
+
if (!(negative && hiU === 0x80000000 && lo === 0))
|
|
68
|
+
return null;
|
|
69
|
+
return G9Long.MIN_VALUE;
|
|
70
|
+
}
|
|
71
|
+
const magnitude = new G9Long(hiU, lo);
|
|
72
|
+
return negative ? magnitude.negate() : magnitude;
|
|
73
|
+
}
|
|
74
|
+
/** Coerces a number, decimal text or G9Long. */
|
|
75
|
+
static from(value) {
|
|
76
|
+
if (value instanceof G9Long)
|
|
77
|
+
return value;
|
|
78
|
+
if (typeof value === 'number')
|
|
79
|
+
return G9Long.fromNumber(value);
|
|
80
|
+
if (typeof value === 'string')
|
|
81
|
+
return G9Long.parse(value);
|
|
82
|
+
if (value && typeof value === 'object' && typeof value.int64 === 'string') {
|
|
83
|
+
return G9Long.parse(value.int64);
|
|
84
|
+
}
|
|
85
|
+
throw new TypeError('Not a 64-bit integer.');
|
|
86
|
+
}
|
|
87
|
+
/** Reads 8 little-endian bytes. */
|
|
88
|
+
static readLE(bytes, offset) {
|
|
89
|
+
const lo = bytes[offset] | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24);
|
|
90
|
+
const hi = bytes[offset + 4] | (bytes[offset + 5] << 8) | (bytes[offset + 6] << 16) | (bytes[offset + 7] << 24);
|
|
91
|
+
return new G9Long(hi, lo);
|
|
92
|
+
}
|
|
93
|
+
/** Reads 8 big-endian bytes. */
|
|
94
|
+
static readBE(bytes, offset) {
|
|
95
|
+
const hi = (bytes[offset] << 24) | (bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3];
|
|
96
|
+
const lo = (bytes[offset + 4] << 24) | (bytes[offset + 5] << 16) | (bytes[offset + 6] << 8) | bytes[offset + 7];
|
|
97
|
+
return new G9Long(hi, lo);
|
|
98
|
+
}
|
|
99
|
+
/** Writes 8 little-endian bytes. */
|
|
100
|
+
writeLE(bytes, offset) {
|
|
101
|
+
const lo = this.lo;
|
|
102
|
+
const hi = this.hi;
|
|
103
|
+
bytes[offset] = lo & 0xff;
|
|
104
|
+
bytes[offset + 1] = (lo >>> 8) & 0xff;
|
|
105
|
+
bytes[offset + 2] = (lo >>> 16) & 0xff;
|
|
106
|
+
bytes[offset + 3] = (lo >>> 24) & 0xff;
|
|
107
|
+
bytes[offset + 4] = hi & 0xff;
|
|
108
|
+
bytes[offset + 5] = (hi >>> 8) & 0xff;
|
|
109
|
+
bytes[offset + 6] = (hi >>> 16) & 0xff;
|
|
110
|
+
bytes[offset + 7] = (hi >>> 24) & 0xff;
|
|
111
|
+
}
|
|
112
|
+
isZero() {
|
|
113
|
+
return this.hi === 0 && this.lo === 0;
|
|
114
|
+
}
|
|
115
|
+
isNegative() {
|
|
116
|
+
return this.hi < 0;
|
|
117
|
+
}
|
|
118
|
+
/** True when the value lies within ±(2^53 − 1), i.e. {@link toNumber} is exact. */
|
|
119
|
+
isSafe() {
|
|
120
|
+
const hi = this.hi;
|
|
121
|
+
if (hi >= 0)
|
|
122
|
+
return hi < 0x200000;
|
|
123
|
+
// -2^53 < v ⇔ hi > -0x200000, or hi == -0x200000 and lo > 0.
|
|
124
|
+
return hi > -0x200000 || (hi === -0x200000 && this.lo > 0);
|
|
125
|
+
}
|
|
126
|
+
/** The value as a number: exact when {@link isSafe}, the nearest double otherwise. */
|
|
127
|
+
toNumber() {
|
|
128
|
+
return this.hi * TWO_32 + this.lo;
|
|
129
|
+
}
|
|
130
|
+
equals(other) {
|
|
131
|
+
return this.hi === other.hi && this.lo === other.lo;
|
|
132
|
+
}
|
|
133
|
+
/** Signed comparison: negative, zero or positive. */
|
|
134
|
+
compare(other) {
|
|
135
|
+
if (this.hi !== other.hi)
|
|
136
|
+
return this.hi < other.hi ? -1 : 1;
|
|
137
|
+
if (this.lo !== other.lo)
|
|
138
|
+
return this.lo < other.lo ? -1 : 1;
|
|
139
|
+
return 0;
|
|
140
|
+
}
|
|
141
|
+
negate() {
|
|
142
|
+
return this.not().add(G9Long.ONE);
|
|
143
|
+
}
|
|
144
|
+
not() {
|
|
145
|
+
return new G9Long(~this.hi, ~this.lo);
|
|
146
|
+
}
|
|
147
|
+
and(other) {
|
|
148
|
+
return new G9Long(this.hi & other.hi, this.lo & other.lo);
|
|
149
|
+
}
|
|
150
|
+
or(other) {
|
|
151
|
+
return new G9Long(this.hi | other.hi, this.lo | other.lo);
|
|
152
|
+
}
|
|
153
|
+
xor(other) {
|
|
154
|
+
return new G9Long(this.hi ^ other.hi, this.lo ^ other.lo);
|
|
155
|
+
}
|
|
156
|
+
/** Wrapping addition (two's complement), like unchecked C#. */
|
|
157
|
+
add(other) {
|
|
158
|
+
const lo = this.lo + other.lo;
|
|
159
|
+
const carry = lo >= TWO_32 ? 1 : 0;
|
|
160
|
+
return new G9Long((this.hi + other.hi + carry) | 0, lo >>> 0);
|
|
161
|
+
}
|
|
162
|
+
subtract(other) {
|
|
163
|
+
return this.add(other.negate());
|
|
164
|
+
}
|
|
165
|
+
/** Wrapping multiplication by a small non-negative integer (< 2^21). */
|
|
166
|
+
multiplySmall(factor) {
|
|
167
|
+
// 16-bit limbs keep every partial product below 2^37.
|
|
168
|
+
const l0 = (this.lo & 0xffff) * factor;
|
|
169
|
+
const l1 = (this.lo >>> 16) * factor + Math.floor(l0 / 65536);
|
|
170
|
+
const l2 = (this.hi & 0xffff) * factor + Math.floor(l1 / 65536);
|
|
171
|
+
const l3 = (this.hi >>> 16) * factor + Math.floor(l2 / 65536);
|
|
172
|
+
return new G9Long(((l3 & 0xffff) << 16) | (l2 & 0xffff), ((l1 & 0xffff) << 16) | (l0 & 0xffff));
|
|
173
|
+
}
|
|
174
|
+
/** Shift left (C# `<<` with the count masked to 0..63). */
|
|
175
|
+
shiftLeft(count) {
|
|
176
|
+
const n = count & 63;
|
|
177
|
+
if (n === 0)
|
|
178
|
+
return this;
|
|
179
|
+
if (n >= 32)
|
|
180
|
+
return new G9Long(this.lo << (n - 32), 0);
|
|
181
|
+
return new G9Long((this.hi << n) | (this.lo >>> (32 - n)), this.lo << n);
|
|
182
|
+
}
|
|
183
|
+
/** Arithmetic shift right (C# `>>` on long). */
|
|
184
|
+
shiftRight(count) {
|
|
185
|
+
const n = count & 63;
|
|
186
|
+
if (n === 0)
|
|
187
|
+
return this;
|
|
188
|
+
if (n >= 32)
|
|
189
|
+
return new G9Long(this.hi >> 31, this.hi >> (n - 32));
|
|
190
|
+
return new G9Long(this.hi >> n, (this.lo >>> n) | (this.hi << (32 - n)));
|
|
191
|
+
}
|
|
192
|
+
/** Logical shift right (C# `>>>` / ulong `>>`). */
|
|
193
|
+
shiftRightUnsigned(count) {
|
|
194
|
+
const n = count & 63;
|
|
195
|
+
if (n === 0)
|
|
196
|
+
return this;
|
|
197
|
+
if (n >= 32)
|
|
198
|
+
return new G9Long(0, this.hi >>> (n - 32));
|
|
199
|
+
return new G9Long(this.hi >>> n, (this.lo >>> n) | (this.hi << (32 - n)));
|
|
200
|
+
}
|
|
201
|
+
/** True when bit `index` (0..63) is set. */
|
|
202
|
+
testBit(index) {
|
|
203
|
+
return index >= 32 ? ((this.hi >>> (index - 32)) & 1) !== 0 : ((this.lo >>> index) & 1) !== 0;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Divides the value as UNSIGNED 64 bits by a small divisor (1..2^21) and returns quotient and remainder. Used for
|
|
207
|
+
* decimal formatting and tick arithmetic.
|
|
208
|
+
*/
|
|
209
|
+
divModUnsignedSmall(divisor) {
|
|
210
|
+
const limbs = [this.hi >>> 16, this.hi & 0xffff, this.lo >>> 16, this.lo & 0xffff];
|
|
211
|
+
let remainder = 0;
|
|
212
|
+
for (let i = 0; i < 4; i++) {
|
|
213
|
+
const value = remainder * 65536 + limbs[i];
|
|
214
|
+
limbs[i] = Math.floor(value / divisor);
|
|
215
|
+
remainder = value - limbs[i] * divisor;
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
quotient: new G9Long(((limbs[0] << 16) | limbs[1]) >>> 0, ((limbs[2] << 16) | limbs[3]) >>> 0),
|
|
219
|
+
remainder,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/** Invariant decimal text, the .NET `long.ToString(CultureInfo.InvariantCulture)` twin. */
|
|
223
|
+
toString() {
|
|
224
|
+
if (this.isSafe())
|
|
225
|
+
return String(this.toNumber());
|
|
226
|
+
if (this.equals(G9Long.MIN_VALUE))
|
|
227
|
+
return '-9223372036854775808';
|
|
228
|
+
const negative = this.isNegative();
|
|
229
|
+
let magnitude = negative ? this.negate() : this;
|
|
230
|
+
let out = '';
|
|
231
|
+
while (!magnitude.isZero()) {
|
|
232
|
+
const { quotient, remainder } = magnitude.divModUnsignedSmall(10000000);
|
|
233
|
+
magnitude = quotient;
|
|
234
|
+
const chunk = String(remainder);
|
|
235
|
+
out = (magnitude.isZero() ? chunk : '0000000'.slice(chunk.length) + chunk) + out;
|
|
236
|
+
}
|
|
237
|
+
return negative ? '-' + out : out;
|
|
238
|
+
}
|
|
239
|
+
/** The value as UNSIGNED decimal text (C# `ulong`). */
|
|
240
|
+
toUnsignedString() {
|
|
241
|
+
if (!this.isNegative())
|
|
242
|
+
return this.toString();
|
|
243
|
+
let magnitude = this;
|
|
244
|
+
let out = '';
|
|
245
|
+
while (!magnitude.isZero()) {
|
|
246
|
+
const { quotient, remainder } = magnitude.divModUnsignedSmall(10000000);
|
|
247
|
+
magnitude = quotient;
|
|
248
|
+
const chunk = String(remainder);
|
|
249
|
+
out = (magnitude.isZero() ? chunk : '0000000'.slice(chunk.length) + chunk) + out;
|
|
250
|
+
}
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* JSON form: a sentinel string that {@link stringifyJson} turns into a bare JSON number, so `JSON.stringify` of a DTO
|
|
255
|
+
* holding a G9Long writes the exact integer.
|
|
256
|
+
*/
|
|
257
|
+
toJSON() {
|
|
258
|
+
return LONG_SENTINEL + this.toString();
|
|
259
|
+
}
|
|
260
|
+
/** The SQLite driver form (G9LynxControls `G9SqlValue`): a number when safe, `{ int64 }` otherwise. */
|
|
261
|
+
toSqlValue() {
|
|
262
|
+
return this.isSafe() ? this.toNumber() : { int64: this.toString() };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
G9Long.ZERO = new G9Long(0, 0);
|
|
266
|
+
G9Long.ONE = new G9Long(0, 1);
|
|
267
|
+
G9Long.MINUS_ONE = new G9Long(-1, 0xffffffff);
|
|
268
|
+
G9Long.MIN_VALUE = new G9Long(0x80000000 | 0, 0);
|
|
269
|
+
G9Long.MAX_VALUE = new G9Long(0x7fffffff, 0xffffffff);
|
|
270
|
+
const TWO_32 = 4294967296;
|
|
271
|
+
/** Marks a G9Long inside `JSON.stringify` output (a NUL, so no ordinary text produces it). */
|
|
272
|
+
export const LONG_SENTINEL = '\u0000g9long:';
|
|
273
|
+
const LONG_SENTINEL_JSON = /"\\u0000g9long:(-?\d+)"/g;
|
|
274
|
+
/** `JSON.stringify` that writes every {@link G9Long} as an exact JSON number. */
|
|
275
|
+
export function stringifyJson(value) {
|
|
276
|
+
return replaceLongSentinels(JSON.stringify(value));
|
|
277
|
+
}
|
|
278
|
+
/** Turns the G9Long sentinels in `JSON.stringify` output (G9Long.toJSON) into bare JSON numbers. */
|
|
279
|
+
export function replaceLongSentinels(text) {
|
|
280
|
+
return text.indexOf('\\u0000g9long:') < 0 ? text : text.replace(LONG_SENTINEL_JSON, '$1');
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* `JSON.parse` that keeps integers outside ±(2^53 − 1) exact: they come back as {@link G9Long} instead of a rounded
|
|
284
|
+
* number. Text without such an integer takes the plain `JSON.parse` path.
|
|
285
|
+
*/
|
|
286
|
+
export function parseJson(text) {
|
|
287
|
+
if (!/\d{16}/.test(text))
|
|
288
|
+
return JSON.parse(text);
|
|
289
|
+
// Quote the long integer literals OUTSIDE strings, tagged, then let JSON.parse do the rest.
|
|
290
|
+
let out = '';
|
|
291
|
+
let last = 0;
|
|
292
|
+
let i = 0;
|
|
293
|
+
const n = text.length;
|
|
294
|
+
while (i < n) {
|
|
295
|
+
const c = text.charCodeAt(i);
|
|
296
|
+
if (c === 34) {
|
|
297
|
+
// Skip a string, honouring escapes.
|
|
298
|
+
i++;
|
|
299
|
+
while (i < n) {
|
|
300
|
+
const d = text.charCodeAt(i);
|
|
301
|
+
if (d === 92)
|
|
302
|
+
i += 2;
|
|
303
|
+
else if (d === 34)
|
|
304
|
+
break;
|
|
305
|
+
else
|
|
306
|
+
i++;
|
|
307
|
+
}
|
|
308
|
+
i++;
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (c === 45 || (c >= 48 && c <= 57)) {
|
|
312
|
+
const start = i;
|
|
313
|
+
i++;
|
|
314
|
+
while (i < n) {
|
|
315
|
+
const d = text.charCodeAt(i);
|
|
316
|
+
if ((d >= 48 && d <= 57) || d === 46 || d === 101 || d === 69 || d === 43 || d === 45)
|
|
317
|
+
i++;
|
|
318
|
+
else
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
const literal = text.slice(start, i);
|
|
322
|
+
if (/^-?\d{16,}$/.test(literal)) {
|
|
323
|
+
const parsed = G9Long.tryParse(literal);
|
|
324
|
+
if (parsed && !parsed.isSafe()) {
|
|
325
|
+
// An escaped U+0001 in the JSON text: JSON.parse turns it into the tag the reviver looks for.
|
|
326
|
+
out += text.slice(last, start) + '"\\u0001g9long:' + literal + '"';
|
|
327
|
+
last = i;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
i++;
|
|
333
|
+
}
|
|
334
|
+
if (last === 0)
|
|
335
|
+
return JSON.parse(text);
|
|
336
|
+
out += text.slice(last);
|
|
337
|
+
return JSON.parse(out, (_key, value) => typeof value === 'string' && value.indexOf(LONG_SENTINEL_TEXT) === 0 ? G9Long.parse(value.slice(LONG_SENTINEL_TEXT.length)) : value);
|
|
338
|
+
}
|
|
339
|
+
/** The tag parseJson puts in front of a long literal (a code point no JSON integer literal contains). */
|
|
340
|
+
const LONG_SENTINEL_TEXT = '\u0001g9long:';
|
|
341
|
+
/** A protocol `long` read as a number (exact when it fits; a G9Long rounds to the nearest double). */
|
|
342
|
+
export function longToNumber(value) {
|
|
343
|
+
if (typeof value === 'number')
|
|
344
|
+
return value;
|
|
345
|
+
if (value instanceof G9Long)
|
|
346
|
+
return value.toNumber();
|
|
347
|
+
if (typeof value === 'string' && value !== '')
|
|
348
|
+
return Number(value);
|
|
349
|
+
return 0;
|
|
350
|
+
}
|
|
351
|
+
//# sourceMappingURL=long.js.map
|