@typesol/cli 0.17.0 → 0.19.0
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/CHANGELOG.md +99 -0
- package/README.md +4 -4
- package/dist/clientgen.js +2 -0
- package/dist/compiler/analyze.d.ts +10 -1
- package/dist/compiler/analyze.js +165 -49
- package/dist/compiler/anchor-errors.d.ts +1 -0
- package/dist/compiler/anchor-errors.js +80 -0
- package/dist/compiler/emit/base.d.ts +29 -1
- package/dist/compiler/emit/base.js +79 -8
- package/dist/compiler/emit/borsh.js +71 -5
- package/dist/compiler/emit/calls.d.ts +2 -3
- package/dist/compiler/emit/calls.js +101 -15
- package/dist/compiler/emit/cpi.d.ts +31 -0
- package/dist/compiler/emit/cpi.js +1061 -39
- package/dist/compiler/emit/expressions.d.ts +24 -0
- package/dist/compiler/emit/expressions.js +137 -19
- package/dist/compiler/emit/function.js +57 -10
- package/dist/compiler/emit/shared.d.ts +8 -0
- package/dist/compiler/emit/shared.js +4 -1
- package/dist/compiler/emit/statements.d.ts +21 -0
- package/dist/compiler/emit/statements.js +386 -16
- package/dist/compiler/emit/storage.d.ts +2 -0
- package/dist/compiler/emit/storage.js +28 -4
- package/dist/compiler/idl.js +4 -1
- package/dist/compiler/model.d.ts +29 -2
- package/dist/portgen.js +32 -2
- package/docs/errors.md +3 -2
- package/docs/language.md +31 -24
- package/docs/migrate-from-anchor.md +13 -3
- package/lib/typesol.d.ts +528 -22
- package/package.json +1 -1
- package/runtime/typesol_rt.h +279 -5
package/package.json
CHANGED
package/runtime/typesol_rt.h
CHANGED
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
#define TS_ERR_TOKEN_OWNER 2015 /* ConstraintTokenOwner */
|
|
31
31
|
#define TS_ERR_MINT_AUTHORITY 2016 /* ConstraintMintMintAuthority */
|
|
32
32
|
#define TS_ERR_FREEZE_AUTHORITY 2017 /* ConstraintMintFreezeAuthority */
|
|
33
|
+
#define TS_ERR_MINT_DECIMALS 2018 /* ConstraintMintDecimals */
|
|
33
34
|
#define TS_ERR_DUPLICATE_MUT 2040 /* ConstraintDuplicateMutableAccount */
|
|
34
35
|
#define TS_ERR_ALREADY_INITIALIZED 3000 /* AccountDiscriminatorAlreadySet */
|
|
35
36
|
#define TS_ERR_DISCRIMINATOR 3002 /* AccountDiscriminatorMismatch */
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
#define TS_ERR_WRONG_PROGRAM 3008 /* InvalidProgramId */
|
|
43
44
|
#define TS_ERR_NOT_EXECUTABLE 3009 /* InvalidProgramExecutable */
|
|
44
45
|
#define TS_ERR_NOT_SIGNER 3010 /* AccountNotSigner */
|
|
46
|
+
#define TS_ERR_NOT_SYSTEM_OWNED 3011 /* AccountNotSystemOwned */
|
|
45
47
|
#define TS_ERR_NOT_INITIALIZED 3012 /* AccountNotInitialized */
|
|
46
48
|
#define TS_ERR_REALLOC 3016 /* AccountReallocExceedsLimit */
|
|
47
49
|
#define TS_ERR_CAST 4102 /* InvalidNumericConversion */
|
|
@@ -634,6 +636,129 @@ static inline uint64_t ts_create_pda(TsParams *params, TsAccount *payer, TsAccou
|
|
|
634
636
|
return SUCCESS;
|
|
635
637
|
}
|
|
636
638
|
|
|
639
|
+
/*
|
|
640
|
+
* Create `acct` with `space` bytes owned by `owner` (a token program), funded by `payer`: a PDA of this program when
|
|
641
|
+
* `n` > 0 (seeds without the bump; a pre-funded address is topped up, allocated and assigned), otherwise a new keypair
|
|
642
|
+
* account that signed the transaction. Writes the bump (0 for a keypair).
|
|
643
|
+
*/
|
|
644
|
+
static inline uint64_t ts_create_owned(TsParams *params, TsAccount *payer, TsAccount *acct, SolSignerSeed *seeds, int n,
|
|
645
|
+
const SolPubkey *owner, uint64_t space, uint8_t *bump_out) {
|
|
646
|
+
uint8_t bump = 0;
|
|
647
|
+
if (n > 0) {
|
|
648
|
+
SolPubkey derived;
|
|
649
|
+
if (ts_find_pda(seeds, n, params->program_id, &derived, &bump) != SUCCESS || !ts_key_eq(&derived, &acct->key))
|
|
650
|
+
TS_FAIL(TS_ERR_SEEDS, "TypeSOL: PDA seeds do not match account");
|
|
651
|
+
seeds[n].addr = ≎
|
|
652
|
+
seeds[n].len = 1;
|
|
653
|
+
} else if (!acct->is_signer) {
|
|
654
|
+
TS_FAIL(TS_ERR_NOT_SIGNER, "TypeSOL: a new keypair account must sign");
|
|
655
|
+
}
|
|
656
|
+
if (acct->data_len != 0 || !ts_key_eq(&acct->owner, &TS_SYSTEM_PROGRAM_ID))
|
|
657
|
+
TS_FAIL(TS_ERR_ALREADY_INITIALIZED, "TypeSOL: account already exists");
|
|
658
|
+
uint64_t lamports;
|
|
659
|
+
TS_TRY(ts_minimum_balance(space, &lamports));
|
|
660
|
+
SolSignerSeeds signer = {seeds, (uint64_t)(n + 1)};
|
|
661
|
+
SolSignerSeeds *signers = n > 0 ? &signer : NULL;
|
|
662
|
+
int nsigners = n > 0 ? 1 : 0;
|
|
663
|
+
if (acct->lamports == 0) {
|
|
664
|
+
uint8_t data[52];
|
|
665
|
+
uint32_t tag = 0; /* CreateAccount */
|
|
666
|
+
ts_memcpy(data, &tag, 4);
|
|
667
|
+
ts_memcpy(data + 4, &lamports, 8);
|
|
668
|
+
ts_memcpy(data + 12, &space, 8);
|
|
669
|
+
ts_memcpy(data + 20, owner, 32);
|
|
670
|
+
SolAccountMeta metas[2] = {{&payer->key, true, true}, {&acct->key, true, true}};
|
|
671
|
+
SolInstruction ix = {(SolPubkey *)&TS_SYSTEM_PROGRAM_ID, metas, 2, data, sizeof(data)};
|
|
672
|
+
TS_TRY(ts_invoke(params, &ix, signers, nsigners));
|
|
673
|
+
} else {
|
|
674
|
+
if (acct->lamports < lamports) TS_TRY(ts_system_transfer(params, payer, acct, lamports - acct->lamports));
|
|
675
|
+
uint8_t alloc[12];
|
|
676
|
+
uint32_t alloc_tag = 8; /* Allocate */
|
|
677
|
+
ts_memcpy(alloc, &alloc_tag, 4);
|
|
678
|
+
ts_memcpy(alloc + 4, &space, 8);
|
|
679
|
+
SolAccountMeta one[1] = {{&acct->key, true, true}};
|
|
680
|
+
SolInstruction alloc_ix = {(SolPubkey *)&TS_SYSTEM_PROGRAM_ID, one, 1, alloc, sizeof(alloc)};
|
|
681
|
+
TS_TRY(ts_invoke(params, &alloc_ix, signers, nsigners));
|
|
682
|
+
uint8_t assign[36];
|
|
683
|
+
uint32_t assign_tag = 1; /* Assign */
|
|
684
|
+
ts_memcpy(assign, &assign_tag, 4);
|
|
685
|
+
ts_memcpy(assign + 4, owner, 32);
|
|
686
|
+
SolInstruction assign_ix = {(SolPubkey *)&TS_SYSTEM_PROGRAM_ID, one, 1, assign, sizeof(assign)};
|
|
687
|
+
TS_TRY(ts_invoke(params, &assign_ix, signers, nsigners));
|
|
688
|
+
}
|
|
689
|
+
*bump_out = bump;
|
|
690
|
+
return SUCCESS;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/* InitializeMint2: decimals, mint authority, optional freeze authority. */
|
|
694
|
+
static inline uint64_t ts_token_init_mint(TsParams *params, TsAccount *tp, TsAccount *mint, uint8_t decimals,
|
|
695
|
+
const SolPubkey *authority, const SolPubkey *freeze) {
|
|
696
|
+
uint8_t data[67];
|
|
697
|
+
data[0] = 20;
|
|
698
|
+
data[1] = decimals;
|
|
699
|
+
ts_memcpy(data + 2, authority, 32);
|
|
700
|
+
data[34] = freeze ? 1 : 0;
|
|
701
|
+
if (freeze) ts_memcpy(data + 35, freeze, 32);
|
|
702
|
+
SolAccountMeta metas[1] = {{&mint->key, true, false}};
|
|
703
|
+
SolInstruction ix = {(SolPubkey *)&tp->key, metas, 1, data, freeze ? 67 : 35};
|
|
704
|
+
return ts_invoke(params, &ix, NULL, 0);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/* Bytes a token account of `mint` needs: 165 under SPL Token, Token-2022's GetAccountDataSize answer otherwise
|
|
708
|
+
(a mint's extensions can add account extensions). */
|
|
709
|
+
static inline uint64_t ts_token_account_space(TsParams *params, TsAccount *tp, TsAccount *mint, uint64_t *space) {
|
|
710
|
+
if (ts_key_eq(&tp->key, &TS_TOKEN_PROGRAM_ID)) {
|
|
711
|
+
*space = TS_TOKEN_ACCOUNT_LEN;
|
|
712
|
+
return SUCCESS;
|
|
713
|
+
}
|
|
714
|
+
uint8_t data[1] = {21}; /* GetAccountDataSize */
|
|
715
|
+
SolAccountMeta metas[1] = {{&mint->key, false, false}};
|
|
716
|
+
SolInstruction ix = {(SolPubkey *)&tp->key, metas, 1, data, 1};
|
|
717
|
+
TS_TRY(ts_invoke(params, &ix, NULL, 0));
|
|
718
|
+
SolPubkey from;
|
|
719
|
+
uint64_t size = 0;
|
|
720
|
+
if (sol_get_return_data((uint8_t *)&size, 8, &from) != 8 || !ts_key_eq(&from, &tp->key))
|
|
721
|
+
TS_FAIL(TS_ERR_TOKEN_ACCOUNT, "TypeSOL: the token program did not report an account size");
|
|
722
|
+
*space = size;
|
|
723
|
+
return SUCCESS;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/* InitializeAccount3: `acct` holds `mint` tokens for `owner`. */
|
|
727
|
+
static inline uint64_t ts_token_init_account(TsParams *params, TsAccount *tp, TsAccount *acct, TsAccount *mint, const SolPubkey *owner) {
|
|
728
|
+
uint8_t data[33];
|
|
729
|
+
data[0] = 18;
|
|
730
|
+
ts_memcpy(data + 1, owner, 32);
|
|
731
|
+
SolAccountMeta metas[2] = {{&acct->key, true, false}, {&mint->key, false, false}};
|
|
732
|
+
SolInstruction ix = {(SolPubkey *)&tp->key, metas, 2, data, sizeof(data)};
|
|
733
|
+
return ts_invoke(params, &ix, NULL, 0);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/* An existing account `init_if_needed` found: the PDA it must be (when seeds are given) and owned by token program `tp`. */
|
|
737
|
+
static inline uint64_t ts_token_existing(TsParams *params, TsAccount *acct, TsAccount *tp, SolSignerSeed *seeds, int n,
|
|
738
|
+
uint8_t *bump_out) {
|
|
739
|
+
*bump_out = 0;
|
|
740
|
+
if (n > 0) {
|
|
741
|
+
SolPubkey derived;
|
|
742
|
+
if (ts_find_pda(seeds, n, params->program_id, &derived, bump_out) != SUCCESS || !ts_key_eq(&derived, &acct->key))
|
|
743
|
+
TS_FAIL(TS_ERR_SEEDS, "TypeSOL: PDA seeds do not match account");
|
|
744
|
+
}
|
|
745
|
+
if (!ts_key_eq(&acct->owner, &tp->key)) TS_FAIL(TS_ERR_WRONG_OWNER, "TypeSOL: account not owned by the token program");
|
|
746
|
+
return SUCCESS;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/* An existing mint must have these decimals and authorities (Anchor's mint:: constraints on init_if_needed). */
|
|
750
|
+
static inline uint64_t ts_check_mint_is(const TsAccount *mint, uint8_t decimals, const SolPubkey *authority, const SolPubkey *freeze) {
|
|
751
|
+
const uint8_t *d = mint->data;
|
|
752
|
+
uint32_t tag;
|
|
753
|
+
ts_memcpy(&tag, d, 4);
|
|
754
|
+
if (tag != 1 || !ts_key_eq((const SolPubkey *)(d + 4), authority)) TS_FAIL(TS_ERR_MINT_AUTHORITY, "TypeSOL: mint authority does not match");
|
|
755
|
+
ts_memcpy(&tag, d + 46, 4);
|
|
756
|
+
if (freeze ? (tag != 1 || !ts_key_eq((const SolPubkey *)(d + 50), freeze)) : tag != 0)
|
|
757
|
+
TS_FAIL(TS_ERR_FREEZE_AUTHORITY, "TypeSOL: freeze authority does not match");
|
|
758
|
+
if (d[44] != decimals) TS_FAIL(TS_ERR_MINT_DECIMALS, "TypeSOL: mint decimals do not match");
|
|
759
|
+
return SUCCESS;
|
|
760
|
+
}
|
|
761
|
+
|
|
637
762
|
/*
|
|
638
763
|
* Resize a program-owned account to `new_len` bytes (realloc). Growth is limited to 10 KiB past the
|
|
639
764
|
* size the account had when the instruction started; new bytes are zeroed. When growing, `payer`
|
|
@@ -975,15 +1100,129 @@ static inline uint64_t ts_alloc(uint64_t n, uint8_t **out) {
|
|
|
975
1100
|
return SUCCESS;
|
|
976
1101
|
}
|
|
977
1102
|
|
|
978
|
-
|
|
1103
|
+
/* Base58 text of a 32-byte value (a Pubkey or Hash in a template), on the heap. The value is eight big-endian u32
|
|
1104
|
+
limbs divided by 58^5 at a time, so each pass gives five digits with one 64-bit division per limb. */
|
|
1105
|
+
static uint64_t ts_base58_32(const uint8_t *in, TS_Str *out) {
|
|
1106
|
+
static const char digits[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
1107
|
+
const uint64_t D = 656356768ULL; /* 58^5 */
|
|
1108
|
+
uint64_t limb[8];
|
|
1109
|
+
for (int i = 0; i < 8; i++) limb[i] = __builtin_bswap32(*(const ts_u32_unaligned *)(in + 4 * i)); /* big-endian words */
|
|
1110
|
+
/* Digits are written as characters from the end of `buf`, five per pass; 32 bytes need at most 44. */
|
|
1111
|
+
char buf[50];
|
|
1112
|
+
uint64_t pos = sizeof(buf);
|
|
1113
|
+
int start = 0;
|
|
1114
|
+
while (start < 8 && limb[start] == 0) start++;
|
|
1115
|
+
while (start < 8) {
|
|
1116
|
+
uint64_t rem = 0;
|
|
1117
|
+
/* One long-division pass from the first nonzero limb, unrolled. */
|
|
1118
|
+
#define TS_B58_STEP(i) \
|
|
1119
|
+
{ \
|
|
1120
|
+
uint64_t cur = (rem << 32) | limb[i]; \
|
|
1121
|
+
uint64_t q = cur / D; \
|
|
1122
|
+
rem = cur - q * D; \
|
|
1123
|
+
limb[i] = q; \
|
|
1124
|
+
}
|
|
1125
|
+
switch (start) {
|
|
1126
|
+
case 0: TS_B58_STEP(0) /* fallthrough */
|
|
1127
|
+
case 1: TS_B58_STEP(1) /* fallthrough */
|
|
1128
|
+
case 2: TS_B58_STEP(2) /* fallthrough */
|
|
1129
|
+
case 3: TS_B58_STEP(3) /* fallthrough */
|
|
1130
|
+
case 4: TS_B58_STEP(4) /* fallthrough */
|
|
1131
|
+
case 5: TS_B58_STEP(5) /* fallthrough */
|
|
1132
|
+
case 6: TS_B58_STEP(6) /* fallthrough */
|
|
1133
|
+
default: TS_B58_STEP(7)
|
|
1134
|
+
}
|
|
1135
|
+
#undef TS_B58_STEP
|
|
1136
|
+
while (start < 8 && limb[start] == 0) start++;
|
|
1137
|
+
uint64_t q;
|
|
1138
|
+
q = rem / 58; buf[--pos] = digits[rem - q * 58]; rem = q;
|
|
1139
|
+
q = rem / 58; buf[--pos] = digits[rem - q * 58]; rem = q;
|
|
1140
|
+
q = rem / 58; buf[--pos] = digits[rem - q * 58]; rem = q;
|
|
1141
|
+
q = rem / 58; buf[--pos] = digits[rem - q * 58]; rem = q;
|
|
1142
|
+
buf[--pos] = digits[rem];
|
|
1143
|
+
}
|
|
1144
|
+
/* Leading zero digits of the number are not printed; each leading zero byte is a '1'. */
|
|
1145
|
+
while (pos < sizeof(buf) && buf[pos] == '1') pos++;
|
|
1146
|
+
uint64_t zeros = 0;
|
|
1147
|
+
while (zeros < 32 && in[zeros] == 0) zeros++;
|
|
1148
|
+
uint64_t len = sizeof(buf) - pos;
|
|
979
1149
|
uint8_t *p;
|
|
980
|
-
TS_TRY(ts_alloc(
|
|
981
|
-
|
|
982
|
-
ts_memcpy(p +
|
|
983
|
-
*out = ts_str(p,
|
|
1150
|
+
TS_TRY(ts_alloc(zeros + len, &p));
|
|
1151
|
+
for (uint64_t i = 0; i < zeros; i++) p[i] = '1';
|
|
1152
|
+
ts_memcpy(p + zeros, buf + pos, len);
|
|
1153
|
+
*out = ts_str(p, zeros + len);
|
|
984
1154
|
return SUCCESS;
|
|
985
1155
|
}
|
|
986
1156
|
|
|
1157
|
+
/*
|
|
1158
|
+
* `s += …` on a local string: the pieces are copied right after `s` when it ends where the heap's free space starts
|
|
1159
|
+
* (it was the last thing allocated), and `s` grows in place; otherwise `s` and the pieces are joined into a new string.
|
|
1160
|
+
* Other views of the old `s` keep their length, so they still read the same bytes. The bytes written are past the
|
|
1161
|
+
* heap's top, where no view can point (ts_alloc is the only source of heap pointers and never hands out memory past
|
|
1162
|
+
* the top), so no piece is overwritten while it is copied.
|
|
1163
|
+
*/
|
|
1164
|
+
static uint64_t ts_str_append_parts(TS_Str *s, const TS_Str *parts, uint64_t n) {
|
|
1165
|
+
uint64_t *next = (uint64_t *)HEAP_START_ADDRESS;
|
|
1166
|
+
uint64_t add = 0;
|
|
1167
|
+
for (uint64_t i = 0; i < n; i++) add += parts[i].len;
|
|
1168
|
+
if (*next != 0 && s->len > 0 && (uint64_t)(s->ptr + s->len) == HEAP_START_ADDRESS + *next && *next + add <= TS_HEAP_LENGTH) {
|
|
1169
|
+
for (uint64_t i = 0; i < n; i++) {
|
|
1170
|
+
ts_memcpy((uint8_t *)s->ptr + s->len, parts[i].ptr, parts[i].len);
|
|
1171
|
+
s->len += parts[i].len;
|
|
1172
|
+
}
|
|
1173
|
+
*next += add;
|
|
1174
|
+
return SUCCESS;
|
|
1175
|
+
}
|
|
1176
|
+
uint8_t *p;
|
|
1177
|
+
TS_TRY(ts_alloc(s->len + add, &p));
|
|
1178
|
+
ts_memcpy(p, s->ptr, s->len);
|
|
1179
|
+
uint64_t at = s->len;
|
|
1180
|
+
for (uint64_t i = 0; i < n; i++) {
|
|
1181
|
+
ts_memcpy(p + at, parts[i].ptr, parts[i].len);
|
|
1182
|
+
at += parts[i].len;
|
|
1183
|
+
}
|
|
1184
|
+
*s = ts_str(p, at);
|
|
1185
|
+
return SUCCESS;
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
/* `s += b` (and `a + b`, with `s` a copy of `a`): the one-piece case of ts_str_append_parts. */
|
|
1189
|
+
static uint64_t ts_str_append(TS_Str *s, TS_Str b) {
|
|
1190
|
+
uint64_t *next = (uint64_t *)HEAP_START_ADDRESS;
|
|
1191
|
+
if (*next != 0 && s->len > 0 && (uint64_t)(s->ptr + s->len) == HEAP_START_ADDRESS + *next && *next + b.len <= TS_HEAP_LENGTH) {
|
|
1192
|
+
ts_memcpy((uint8_t *)s->ptr + s->len, b.ptr, b.len);
|
|
1193
|
+
*next += b.len;
|
|
1194
|
+
s->len += b.len;
|
|
1195
|
+
return SUCCESS;
|
|
1196
|
+
}
|
|
1197
|
+
uint8_t *p;
|
|
1198
|
+
TS_TRY(ts_alloc(s->len + b.len, &p));
|
|
1199
|
+
ts_memcpy(p, s->ptr, s->len);
|
|
1200
|
+
ts_memcpy(p + s->len, b.ptr, b.len);
|
|
1201
|
+
*s = ts_str(p, s->len + b.len);
|
|
1202
|
+
return SUCCESS;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
/* The pieces of a template literal, joined with one heap allocation and one copy each. */
|
|
1206
|
+
static uint64_t ts_str_join(const TS_Str *parts, uint64_t n, TS_Str *out) {
|
|
1207
|
+
uint64_t total = 0;
|
|
1208
|
+
for (uint64_t i = 0; i < n; i++) total += parts[i].len;
|
|
1209
|
+
uint8_t *p;
|
|
1210
|
+
TS_TRY(ts_alloc(total, &p));
|
|
1211
|
+
uint64_t at = 0;
|
|
1212
|
+
for (uint64_t i = 0; i < n; i++) {
|
|
1213
|
+
ts_memcpy(p + at, parts[i].ptr, parts[i].len);
|
|
1214
|
+
at += parts[i].len;
|
|
1215
|
+
}
|
|
1216
|
+
*out = ts_str(p, total);
|
|
1217
|
+
return SUCCESS;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/* `a + b`: a new string (`a` grows in place when it is the newest heap string; its own view keeps its length). */
|
|
1221
|
+
static inline uint64_t ts_str_concat(TS_Str a, TS_Str b, TS_Str *out) {
|
|
1222
|
+
*out = a;
|
|
1223
|
+
return ts_str_append(out, b);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
987
1226
|
/* Decimal text of an integer, on the heap. */
|
|
988
1227
|
/* Copy a string's bytes to the heap (a stable value, independent of where the view pointed). */
|
|
989
1228
|
static inline uint64_t ts_str_copy(TS_Str a, TS_Str *out) {
|
|
@@ -1005,6 +1244,41 @@ static inline uint64_t ts_mul_div(uint64_t a, uint64_t b, uint64_t c, bool ceil,
|
|
|
1005
1244
|
return SUCCESS;
|
|
1006
1245
|
}
|
|
1007
1246
|
|
|
1247
|
+
/* floor or ceil of a * b / c for u128 through a 256-bit product; fails on c == 0 or a result above u128. */
|
|
1248
|
+
static uint64_t ts_mul_div_u128(unsigned __int128 a, unsigned __int128 b, unsigned __int128 c, bool ceil, unsigned __int128 *out) {
|
|
1249
|
+
if (c == 0) TS_FAIL(TS_ERR_DIV_ZERO, "TypeSOL: division by zero");
|
|
1250
|
+
const unsigned __int128 M = (unsigned __int128)UINT64_MAX;
|
|
1251
|
+
unsigned __int128 a0 = a & M, a1 = a >> 64, b0 = b & M, b1 = b >> 64;
|
|
1252
|
+
unsigned __int128 p00 = a0 * b0, p01 = a0 * b1, p10 = a1 * b0, p11 = a1 * b1;
|
|
1253
|
+
unsigned __int128 mid = (p00 >> 64) + (p01 & M) + (p10 & M);
|
|
1254
|
+
unsigned __int128 lo = (p00 & M) | (mid << 64);
|
|
1255
|
+
unsigned __int128 hi = p11 + (p01 >> 64) + (p10 >> 64) + (mid >> 64);
|
|
1256
|
+
unsigned __int128 q, r;
|
|
1257
|
+
if (hi == 0) {
|
|
1258
|
+
q = lo / c;
|
|
1259
|
+
r = lo % c;
|
|
1260
|
+
} else {
|
|
1261
|
+
/* The quotient fits in 128 bits only when hi < c; then long division of hi:lo by c, one bit at a time. */
|
|
1262
|
+
if (hi >= c) TS_FAIL(TS_ERR_OVERFLOW, "TypeSOL: arithmetic overflow");
|
|
1263
|
+
q = 0;
|
|
1264
|
+
r = hi;
|
|
1265
|
+
for (int i = 127; i >= 0; i--) {
|
|
1266
|
+
bool carry = (r >> 127) != 0;
|
|
1267
|
+
r = (r << 1) | ((lo >> i) & 1);
|
|
1268
|
+
if (carry || r >= c) {
|
|
1269
|
+
r -= c;
|
|
1270
|
+
q |= (unsigned __int128)1 << i;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
if (ceil && r != 0) {
|
|
1275
|
+
if (q == ~(unsigned __int128)0) TS_FAIL(TS_ERR_OVERFLOW, "TypeSOL: arithmetic overflow");
|
|
1276
|
+
q += 1;
|
|
1277
|
+
}
|
|
1278
|
+
*out = q;
|
|
1279
|
+
return SUCCESS;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1008
1282
|
/* Integer square roots (floor), by Newton's method from an over-estimate. */
|
|
1009
1283
|
static inline uint64_t ts_isqrt_u64(uint64_t x) {
|
|
1010
1284
|
if (x < 2) return x;
|