| 1 | /* |
| 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
| 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 5 | * |
| 6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
| 7 | */ |
| 8 | |
| 9 | /* |
| 10 | * @a M. L. Kersten, P. Boncz |
| 11 | * @* Atomic types |
| 12 | * The Binary Association Table library assumes efficient |
| 13 | * implementation of the atoms making up the binary association. This |
| 14 | * section describes the preliminaries for handling both built-in and |
| 15 | * user-defined atomic types. |
| 16 | * New types, such as point and polygons, can be readily added to this |
| 17 | * collection. |
| 18 | */ |
| 19 | /* |
| 20 | * @- inline comparison routines |
| 21 | * Return 0 on l==r, < 0 iff l < r, >0 iff l > r |
| 22 | */ |
| 23 | #include "monetdb_config.h" |
| 24 | #include "gdk.h" |
| 25 | #include "gdk_private.h" |
| 26 | #include <math.h> |
| 27 | |
| 28 | /* the *Cmp functions return a value less than zero if the first |
| 29 | * argument is less than the second; they return zero if the two |
| 30 | * values are equal; and they return a value greater than zero if the |
| 31 | * first argument is greater than the second. Remember that in all |
| 32 | * cases, nil is considered smaller than any other value and nil is |
| 33 | * equal to itself (this has repercussions for the floating point |
| 34 | * implementation if and when its NIL value is the floating point |
| 35 | * NaN). */ |
| 36 | |
| 37 | static int |
| 38 | bteCmp(const bte *l, const bte *r) |
| 39 | { |
| 40 | return (*l > *r) - (*l < *r); |
| 41 | } |
| 42 | |
| 43 | static int |
| 44 | shtCmp(const sht *l, const sht *r) |
| 45 | { |
| 46 | return (*l > *r) - (*l < *r); |
| 47 | } |
| 48 | |
| 49 | static int |
| 50 | intCmp(const int *l, const int *r) |
| 51 | { |
| 52 | return (*l > *r) - (*l < *r); |
| 53 | } |
| 54 | |
| 55 | static int |
| 56 | fltCmp(const flt *l, const flt *r) |
| 57 | { |
| 58 | return is_flt_nil(*l) ? -!is_flt_nil(*r) : is_flt_nil(*r) ? 1 : (*l > *r) - (*l < *r); |
| 59 | } |
| 60 | |
| 61 | static int |
| 62 | lngCmp(const lng *l, const lng *r) |
| 63 | { |
| 64 | return (*l > *r) - (*l < *r); |
| 65 | } |
| 66 | |
| 67 | #ifdef HAVE_HGE |
| 68 | static int |
| 69 | hgeCmp(const hge *l, const hge *r) |
| 70 | { |
| 71 | return (*l > *r) - (*l < *r); |
| 72 | } |
| 73 | #endif |
| 74 | |
| 75 | static int |
| 76 | dblCmp(const dbl *l, const dbl *r) |
| 77 | { |
| 78 | return is_dbl_nil(*l) ? -!is_dbl_nil(*r) : is_dbl_nil(*r) ? 1 : (*l > *r) - (*l < *r); |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | * @- inline hash routines |
| 83 | * Return some positive integer derived from one atom value. |
| 84 | */ |
| 85 | static BUN |
| 86 | bteHash(const bte *v) |
| 87 | { |
| 88 | return (BUN) mix_bte(*(const unsigned char *) v); |
| 89 | } |
| 90 | |
| 91 | static BUN |
| 92 | shtHash(const sht *v) |
| 93 | { |
| 94 | return (BUN) mix_sht(*(const unsigned short *) v); |
| 95 | } |
| 96 | |
| 97 | static BUN |
| 98 | intHash(const int *v) |
| 99 | { |
| 100 | return (BUN) mix_int(*(const unsigned int *) v); |
| 101 | } |
| 102 | |
| 103 | static BUN |
| 104 | lngHash(const lng *v) |
| 105 | { |
| 106 | return (BUN) mix_lng(*(const ulng *) v); |
| 107 | } |
| 108 | |
| 109 | #ifdef HAVE_HGE |
| 110 | static BUN |
| 111 | hgeHash(const hge *v) |
| 112 | { |
| 113 | return (BUN) mix_hge(*(const uhge *) v); |
| 114 | } |
| 115 | #endif |
| 116 | |
| 117 | /* |
| 118 | * @+ Standard Atoms |
| 119 | */ |
| 120 | static int |
| 121 | batFix(const bat *b) |
| 122 | { |
| 123 | return BBPretain(*b); |
| 124 | } |
| 125 | |
| 126 | static int |
| 127 | batUnfix(const bat *b) |
| 128 | { |
| 129 | return BBPrelease(*b); |
| 130 | } |
| 131 | |
| 132 | /* |
| 133 | * @+ Atomic Type Interface |
| 134 | * The collection of built-in types supported for BATs can be extended |
| 135 | * easily. In essence, the user should specify conversion routines |
| 136 | * from values stored anywhere in memory to its equivalent in the BAT, |
| 137 | * and vice verse. Some routines are required for coercion and to |
| 138 | * support the BAT administration. |
| 139 | * |
| 140 | * A new type is incrementally build using the routine |
| 141 | * ATOMallocate(id). The parameter id denotes the type name; an entry |
| 142 | * is created if the type is so far unknown. |
| 143 | * |
| 144 | * The size describes the amount of space to be reserved in the BUN. |
| 145 | * |
| 146 | * The routine put takes a pointer to a memory resident copy and |
| 147 | * prepares a persistent copy in the BAT passed. The inverse |
| 148 | * operation is get. A new value can be directly included into the |
| 149 | * BAT using new, which should prepare a null-value representation. A |
| 150 | * value is removed from the BAT store using del, which can take care |
| 151 | * of garbage collection and BAT administration. |
| 152 | * |
| 153 | * The pair tostr and fromstr should convert a reference to a |
| 154 | * persistent value to a memory resident string equivalent. FromStr |
| 155 | * takes a string and applies a put to store it within a BAT. They |
| 156 | * are used to prepare for readable output/input and to support |
| 157 | * coercion. |
| 158 | * |
| 159 | * The routines cmp and eq are comparison routines used to build |
| 160 | * access structures. The null returns a reference to a null value |
| 161 | * representation. |
| 162 | * |
| 163 | * The incremental atom construction uses hardwired properties. This |
| 164 | * should be improved later on. |
| 165 | */ |
| 166 | int |
| 167 | ATOMallocate(const char *id) |
| 168 | { |
| 169 | int t; |
| 170 | |
| 171 | if (strlen(id) >= IDLENGTH) { |
| 172 | GDKerror("ATOMallocate: name too long" ); |
| 173 | return int_nil; |
| 174 | } |
| 175 | |
| 176 | MT_lock_set(&GDKthreadLock); |
| 177 | t = ATOMindex(id); |
| 178 | if (t < 0) { |
| 179 | t = -t; |
| 180 | if (t == GDKatomcnt) { |
| 181 | if (GDKatomcnt == MAXATOMS) { |
| 182 | MT_lock_unset(&GDKthreadLock); |
| 183 | GDKerror("ATOMallocate: too many types" ); |
| 184 | return int_nil; |
| 185 | } |
| 186 | GDKatomcnt++; |
| 187 | } |
| 188 | BATatoms[t] = (atomDesc) { |
| 189 | .size = sizeof(int), /* default */ |
| 190 | .linear = true, /* default */ |
| 191 | .storage = t, /* default */ |
| 192 | }; |
| 193 | strcpy(BATatoms[t].name, id); |
| 194 | } |
| 195 | MT_lock_unset(&GDKthreadLock); |
| 196 | return t; |
| 197 | } |
| 198 | |
| 199 | int |
| 200 | ATOMindex(const char *nme) |
| 201 | { |
| 202 | int t, j = GDKatomcnt; |
| 203 | |
| 204 | for (t = 0; t < GDKatomcnt; t++) { |
| 205 | if (!BATatoms[t].name[0]) { |
| 206 | if (j == GDKatomcnt) |
| 207 | j = t; |
| 208 | } else if (strcmp(nme, BATatoms[t].name) == 0) { |
| 209 | return t; |
| 210 | } |
| 211 | |
| 212 | } |
| 213 | if (strcmp(nme, "bat" ) == 0) { |
| 214 | return TYPE_bat; |
| 215 | } |
| 216 | return -j; |
| 217 | } |
| 218 | |
| 219 | char * |
| 220 | ATOMname(int t) |
| 221 | { |
| 222 | return t >= 0 && t < GDKatomcnt && *BATatoms[t].name ? BATatoms[t].name : "null" ; |
| 223 | } |
| 224 | |
| 225 | bool |
| 226 | ATOMisdescendant(int tpe, int parent) |
| 227 | { |
| 228 | int cur = -1; |
| 229 | |
| 230 | while (cur != tpe) { |
| 231 | cur = tpe; |
| 232 | if (cur == parent) |
| 233 | return true; |
| 234 | tpe = ATOMstorage(tpe); |
| 235 | } |
| 236 | return false; |
| 237 | } |
| 238 | |
| 239 | |
| 240 | const bte bte_nil = GDK_bte_min-1; |
| 241 | const sht sht_nil = GDK_sht_min-1; |
| 242 | const int int_nil = GDK_int_min-1; |
| 243 | #ifdef NAN_CANNOT_BE_USED_AS_INITIALIZER |
| 244 | /* Definition of NAN is seriously broken on Intel compiler (at least |
| 245 | * in some versions), so we work around it. */ |
| 246 | const union _flt_nil_t _flt_nil_ = { |
| 247 | .l = UINT32_C(0x7FC00000) |
| 248 | }; |
| 249 | const union _dbl_nil_t _dbl_nil_ = { |
| 250 | .l = UINT64_C(0x7FF8000000000000) |
| 251 | }; |
| 252 | #else |
| 253 | const flt flt_nil = NAN; |
| 254 | const dbl dbl_nil = NAN; |
| 255 | #endif |
| 256 | const lng lng_nil = GDK_lng_min-1; |
| 257 | #ifdef HAVE_HGE |
| 258 | const hge hge_nil = GDK_hge_min-1; |
| 259 | #endif |
| 260 | const oid oid_nil = (oid) 1 << (sizeof(oid) * 8 - 1); |
| 261 | const ptr ptr_nil = NULL; |
| 262 | |
| 263 | ptr |
| 264 | ATOMnil(int t) |
| 265 | { |
| 266 | const void *src = ATOMnilptr(t); |
| 267 | size_t len = ATOMlen(ATOMtype(t), src); |
| 268 | ptr dst = GDKmalloc(len); |
| 269 | |
| 270 | if (dst) |
| 271 | memcpy(dst, src, len); |
| 272 | return dst; |
| 273 | } |
| 274 | |
| 275 | /* |
| 276 | * @- Atomic ADT functions |
| 277 | */ |
| 278 | size_t |
| 279 | ATOMlen(int t, const void *src) |
| 280 | { |
| 281 | size_t (*l)(const void *) = BATatoms[t].atomLen; |
| 282 | |
| 283 | return l ? (*l) (src) : ATOMsize(t); |
| 284 | } |
| 285 | |
| 286 | gdk_return |
| 287 | ATOMheap(int t, Heap *hp, size_t cap) |
| 288 | { |
| 289 | void (*h) (Heap *, size_t) = BATatoms[t].atomHeap; |
| 290 | |
| 291 | if (h) { |
| 292 | (*h) (hp, cap); |
| 293 | if (hp->base == NULL) |
| 294 | return GDK_FAIL; |
| 295 | } |
| 296 | return GDK_SUCCEED; |
| 297 | } |
| 298 | |
| 299 | /* |
| 300 | * Atom print avoids coercion to strings for built-in types. |
| 301 | * The comparison against the NULL value is hard coded for speed. |
| 302 | */ |
| 303 | #define LINE_LEN 60 |
| 304 | |
| 305 | int |
| 306 | ATOMprint(int t, const void *p, stream *s) |
| 307 | { |
| 308 | ssize_t (*tostr) (char **, size_t *, const void *, bool); |
| 309 | ssize_t res; |
| 310 | |
| 311 | if (p && t >= 0 && t < GDKatomcnt && (tostr = BATatoms[t].atomToStr)) { |
| 312 | size_t sz; |
| 313 | |
| 314 | if (t != TYPE_bat && t < TYPE_str) { |
| 315 | char buf[dblStrlen], *addr = buf; /* use memory from stack */ |
| 316 | |
| 317 | sz = dblStrlen; |
| 318 | res = (*tostr) (&addr, &sz, p, true); |
| 319 | if (res > 0) |
| 320 | res = mnstr_write(s, buf, (size_t) res, 1); |
| 321 | } else { |
| 322 | str buf = NULL; |
| 323 | |
| 324 | sz = 0; |
| 325 | res = (*tostr) (&buf, &sz, p, true); |
| 326 | if (res > 0) |
| 327 | res = mnstr_write(s, buf, (size_t) res, 1); |
| 328 | GDKfree(buf); |
| 329 | } |
| 330 | } else { |
| 331 | res = mnstr_write(s, "nil" , 1, 3); |
| 332 | } |
| 333 | if (res < 0) |
| 334 | GDKsyserror("ATOMprint: write failure\n" ); |
| 335 | return (int) res; |
| 336 | } |
| 337 | |
| 338 | |
| 339 | char * |
| 340 | ATOMformat(int t, const void *p) |
| 341 | { |
| 342 | ssize_t (*tostr) (char **, size_t *, const void *, bool); |
| 343 | |
| 344 | if (p && 0 <= t && t < GDKatomcnt && (tostr = BATatoms[t].atomToStr)) { |
| 345 | size_t sz = 0; |
| 346 | char *buf = NULL; |
| 347 | ssize_t res = (*tostr) (&buf, &sz, p, true); |
| 348 | if (res < 0 && buf) { |
| 349 | GDKfree(buf); |
| 350 | buf = NULL; |
| 351 | } |
| 352 | return buf; |
| 353 | } |
| 354 | return GDKstrdup("nil" ); |
| 355 | } |
| 356 | |
| 357 | ptr |
| 358 | ATOMdup(int t, const void *p) |
| 359 | { |
| 360 | size_t len = ATOMlen(t, p); |
| 361 | ptr n = GDKmalloc(len); |
| 362 | |
| 363 | if (n) |
| 364 | memcpy(n, p, len); |
| 365 | return n; |
| 366 | } |
| 367 | |
| 368 | /* |
| 369 | * @* Builtin Atomic Operator Implementations |
| 370 | * |
| 371 | * @+ Atom-from-String Conversions |
| 372 | * These routines convert from string to atom. They are used during |
| 373 | * conversion and BAT import. In order to avoid unnecessary |
| 374 | * malloc()/free() sequences, the conversion functions have a meta |
| 375 | * 'dst' pointer to a destination region, and an integer* 'len' |
| 376 | * parameter, that denotes the length of that region (a char region |
| 377 | * for ToStr functions, an atom region from FromStr conversions). Only |
| 378 | * if necessary will the conversion routine do a GDKfree()/GDKmalloc() |
| 379 | * sequence, and increment the 'len'. Passing a pointer to a nil-ptr |
| 380 | * as 'dst' and/or a *len==0 is valid; the conversion function will |
| 381 | * then alloc some region for you. |
| 382 | */ |
| 383 | #define atommem(size) \ |
| 384 | do { \ |
| 385 | if (*dst == NULL || *len < (size)) { \ |
| 386 | GDKfree(*dst); \ |
| 387 | *len = (size); \ |
| 388 | *dst = GDKmalloc(*len); \ |
| 389 | if (*dst == NULL) { \ |
| 390 | *len = 0; \ |
| 391 | return -1; \ |
| 392 | } \ |
| 393 | } \ |
| 394 | } while (0) |
| 395 | |
| 396 | #define is_ptr_nil(val) ((val) == ptr_nil) |
| 397 | |
| 398 | #define atomtostr(TYPE, FMT, FMTCAST) \ |
| 399 | ssize_t \ |
| 400 | TYPE##ToStr(char **dst, size_t *len, const TYPE *src, bool external) \ |
| 401 | { \ |
| 402 | atommem(TYPE##Strlen); \ |
| 403 | if (is_##TYPE##_nil(*src)) { \ |
| 404 | if (external) { \ |
| 405 | strcpy(*dst, "nil"); \ |
| 406 | return 3; \ |
| 407 | } \ |
| 408 | strcpy(*dst, str_nil); \ |
| 409 | return 1; \ |
| 410 | } \ |
| 411 | return snprintf(*dst, *len, FMT, FMTCAST *src); \ |
| 412 | } |
| 413 | |
| 414 | #define num10(x) GDKisdigit(x) |
| 415 | #define base10(x) ((x) - '0') |
| 416 | |
| 417 | #define num16(x) isxdigit((unsigned char) (x)) |
| 418 | #define base16(x) (((x) >= 'a' && (x) <= 'f') ? ((x) - 'a' + 10) : ((x) >= 'A' && (x) <= 'F') ? ((x) - 'A' + 10) : (x) - '0') |
| 419 | #define mult16(x) ((x) << 4) |
| 420 | |
| 421 | static void * |
| 422 | voidRead(void *a, stream *s, size_t cnt) |
| 423 | { |
| 424 | (void) s; |
| 425 | (void) cnt; |
| 426 | return a; |
| 427 | } |
| 428 | |
| 429 | static gdk_return |
| 430 | voidWrite(const void *a, stream *s, size_t cnt) |
| 431 | { |
| 432 | (void) a; |
| 433 | (void) s; |
| 434 | (void) cnt; |
| 435 | return GDK_SUCCEED; |
| 436 | } |
| 437 | |
| 438 | /* |
| 439 | * Converts string values such as TRUE/FALSE/true/false etc to 1/0/NULL. |
| 440 | * Switched from byte-to-byte compare to library function strncasecmp, |
| 441 | * experiments showed that library function is even slightly faster and we |
| 442 | * now also support True/False (and trUe/FAlSE should this become a thing). |
| 443 | */ |
| 444 | ssize_t |
| 445 | bitFromStr(const char *src, size_t *len, bit **dst, bool external) |
| 446 | { |
| 447 | const char *p = src; |
| 448 | |
| 449 | atommem(sizeof(bit)); |
| 450 | |
| 451 | **dst = bit_nil; |
| 452 | |
| 453 | if (GDK_STRNIL(src)) |
| 454 | return 1; |
| 455 | |
| 456 | while (GDKisspace(*p)) |
| 457 | p++; |
| 458 | if (*p == '0') { |
| 459 | **dst = FALSE; |
| 460 | p++; |
| 461 | } else if (*p == '1') { |
| 462 | **dst = TRUE; |
| 463 | p++; |
| 464 | } else if (strncasecmp(p, "true" , 4) == 0) { |
| 465 | **dst = TRUE; |
| 466 | p += 4; |
| 467 | } else if (strncasecmp(p, "false" , 5) == 0) { |
| 468 | **dst = FALSE; |
| 469 | p += 5; |
| 470 | } else if (external && strncasecmp(p, "nil" , 3) == 0) { |
| 471 | p += 3; |
| 472 | } else { |
| 473 | return -1; |
| 474 | } |
| 475 | while (GDKisspace(*p)) |
| 476 | p++; |
| 477 | return (ssize_t) (p - src); |
| 478 | } |
| 479 | |
| 480 | ssize_t |
| 481 | bitToStr(char **dst, size_t *len, const bit *src, bool external) |
| 482 | { |
| 483 | atommem(6); |
| 484 | |
| 485 | if (is_bit_nil(*src)) { |
| 486 | if (external) { |
| 487 | strcpy(*dst, "nil" ); |
| 488 | return 3; |
| 489 | } |
| 490 | strcpy(*dst, str_nil); |
| 491 | return 1; |
| 492 | } |
| 493 | if (*src) { |
| 494 | strcpy(*dst, "true" ); |
| 495 | return 4; |
| 496 | } |
| 497 | strcpy(*dst, "false" ); |
| 498 | return 5; |
| 499 | } |
| 500 | |
| 501 | ssize_t |
| 502 | batFromStr(const char *src, size_t *len, bat **dst, bool external) |
| 503 | { |
| 504 | char *s; |
| 505 | const char *t, *r = src; |
| 506 | int c; |
| 507 | bat bid = 0; |
| 508 | |
| 509 | atommem(sizeof(bat)); |
| 510 | |
| 511 | if (GDK_STRNIL(src)) { |
| 512 | **dst = bat_nil; |
| 513 | return 1; |
| 514 | } |
| 515 | |
| 516 | while (GDKisspace(*r)) |
| 517 | r++; |
| 518 | |
| 519 | if (external && strcmp(r, "nil" ) == 0) { |
| 520 | **dst = bat_nil; |
| 521 | return (ssize_t) (r - src) + 3; |
| 522 | } |
| 523 | |
| 524 | if (*r == '<') |
| 525 | r++; |
| 526 | t = r; |
| 527 | while ((c = *t) && (c == '_' || GDKisalnum(c))) |
| 528 | t++; |
| 529 | |
| 530 | s = GDKstrndup(r, t - r); |
| 531 | if (s == NULL) |
| 532 | return -1; |
| 533 | bid = BBPindex(s); |
| 534 | GDKfree(s); |
| 535 | **dst = bid == 0 ? bat_nil : bid; |
| 536 | return (ssize_t) (t + (c == '>') - src); |
| 537 | } |
| 538 | |
| 539 | ssize_t |
| 540 | batToStr(char **dst, size_t *len, const bat *src, bool external) |
| 541 | { |
| 542 | bat b = *src; |
| 543 | size_t i; |
| 544 | str s; |
| 545 | |
| 546 | if (is_bat_nil(b) || (s = BBPname(b)) == NULL || *s == 0) { |
| 547 | atommem(4); |
| 548 | if (external) { |
| 549 | strcpy(*dst, "nil" ); |
| 550 | return 3; |
| 551 | } |
| 552 | strcpy(*dst, str_nil); |
| 553 | return 1; |
| 554 | } |
| 555 | i = strlen(s) + 3; |
| 556 | atommem(i); |
| 557 | return (ssize_t) strconcat_len(*dst, *len, "<" , s, ">" , NULL); |
| 558 | } |
| 559 | |
| 560 | |
| 561 | /* |
| 562 | * numFromStr parses the head of the string for a number, accepting an |
| 563 | * optional sign. The code has been prepared to continue parsing by |
| 564 | * returning the number of characters read. Both overflow and |
| 565 | * incorrect syntax (not a number) result in the function returning 0 |
| 566 | * and setting the destination to nil. |
| 567 | */ |
| 568 | struct maxdiv { |
| 569 | /* if we want to multiply a value with scale, the value must |
| 570 | * be no larger than maxval for there to not be overflow */ |
| 571 | #ifdef HAVE_HGE |
| 572 | hge scale, maxval; |
| 573 | #else |
| 574 | lng scale, maxval; |
| 575 | #endif |
| 576 | }; |
| 577 | static const struct maxdiv maxdiv[] = { |
| 578 | #ifdef HAVE_HGE |
| 579 | /* maximum hge value: 170141183460469231731687303715884105727 (2**127-1) |
| 580 | * GCC doesn't currently support integer constants that don't |
| 581 | * fit in 8 bytes, so we split large values up*/ |
| 582 | {(hge) LL_CONSTANT(1), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(10000000000000000000U)+ (hge) LL_CONSTANT(1687303715884105727)}, |
| 583 | {(hge) LL_CONSTANT(10), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(1000000000000000000) + (hge) LL_CONSTANT(168730371588410572)}, |
| 584 | {(hge) LL_CONSTANT(100), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(100000000000000000) + (hge) LL_CONSTANT(16873037158841057)}, |
| 585 | {(hge) LL_CONSTANT(1000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(10000000000000000) + (hge) LL_CONSTANT(1687303715884105)}, |
| 586 | {(hge) LL_CONSTANT(10000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(1000000000000000) + (hge) LL_CONSTANT(168730371588410)}, |
| 587 | {(hge) LL_CONSTANT(100000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(100000000000000) + (hge) LL_CONSTANT(16873037158841)}, |
| 588 | {(hge) LL_CONSTANT(1000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(10000000000000) + (hge) LL_CONSTANT(1687303715884)}, |
| 589 | {(hge) LL_CONSTANT(10000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(1000000000000) + (hge) LL_CONSTANT(168730371588)}, |
| 590 | {(hge) LL_CONSTANT(100000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(100000000000) + (hge) LL_CONSTANT(16873037158)}, |
| 591 | {(hge) LL_CONSTANT(1000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(10000000000) + (hge) LL_CONSTANT(1687303715)}, |
| 592 | {(hge) LL_CONSTANT(10000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(1000000000) + (hge) LL_CONSTANT(168730371)}, |
| 593 | {(hge) LL_CONSTANT(100000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(100000000) + (hge) LL_CONSTANT(16873037)}, |
| 594 | {(hge) LL_CONSTANT(1000000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(10000000) + (hge) LL_CONSTANT(1687303)}, |
| 595 | {(hge) LL_CONSTANT(10000000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(1000000) + (hge) LL_CONSTANT(168730)}, |
| 596 | {(hge) LL_CONSTANT(100000000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(100000) + (hge) LL_CONSTANT(16873)}, |
| 597 | {(hge) LL_CONSTANT(1000000000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(10000) + (hge) LL_CONSTANT(1687)}, |
| 598 | {(hge) LL_CONSTANT(10000000000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(1000) + (hge) LL_CONSTANT(168)}, |
| 599 | {(hge) LL_CONSTANT(100000000000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(100) + (hge) LL_CONSTANT(16)}, |
| 600 | {(hge) LL_CONSTANT(1000000000000000000), (hge) LL_CONSTANT(17014118346046923173U) * LL_CONSTANT(10) + (hge) LL_CONSTANT(1)}, |
| 601 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(1), (hge) LL_CONSTANT(17014118346046923173U)}, |
| 602 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(10), (hge) LL_CONSTANT(1701411834604692317)}, |
| 603 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(100), (hge) LL_CONSTANT(170141183460469231)}, |
| 604 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(1000), (hge) LL_CONSTANT(17014118346046923)}, |
| 605 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(10000), (hge) LL_CONSTANT(1701411834604692)}, |
| 606 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(100000), (hge) LL_CONSTANT(170141183460469)}, |
| 607 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(1000000), (hge) LL_CONSTANT(17014118346046)}, |
| 608 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(10000000), (hge) LL_CONSTANT(1701411834604)}, |
| 609 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(100000000), (hge) LL_CONSTANT(170141183460)}, |
| 610 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(1000000000), (hge) LL_CONSTANT(17014118346)}, |
| 611 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(10000000000), (hge) LL_CONSTANT(1701411834)}, |
| 612 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(100000000000), (hge) LL_CONSTANT(170141183)}, |
| 613 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(1000000000000), (hge) LL_CONSTANT(17014118)}, |
| 614 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(10000000000000), (hge) LL_CONSTANT(1701411)}, |
| 615 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(100000000000000), (hge) LL_CONSTANT(170141)}, |
| 616 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(1000000000000000), (hge) LL_CONSTANT(17014)}, |
| 617 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(10000000000000000), (hge) LL_CONSTANT(1701)}, |
| 618 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(100000000000000000), (hge) LL_CONSTANT(170)}, |
| 619 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(1000000000000000000), (hge) LL_CONSTANT(17)}, |
| 620 | {(hge) LL_CONSTANT(10000000000000000000U) * LL_CONSTANT(10000000000000000000U),(hge) LL_CONSTANT(1)}, |
| 621 | #else |
| 622 | /* maximum lng value: 9223372036854775807 (2**63-1) */ |
| 623 | {LL_CONSTANT(1), LL_CONSTANT(9223372036854775807)}, |
| 624 | {LL_CONSTANT(10), LL_CONSTANT(922337203685477580)}, |
| 625 | {LL_CONSTANT(100), LL_CONSTANT(92233720368547758)}, |
| 626 | {LL_CONSTANT(1000), LL_CONSTANT(9223372036854775)}, |
| 627 | {LL_CONSTANT(10000), LL_CONSTANT(922337203685477)}, |
| 628 | {LL_CONSTANT(100000), LL_CONSTANT(92233720368547)}, |
| 629 | {LL_CONSTANT(1000000), LL_CONSTANT(9223372036854)}, |
| 630 | {LL_CONSTANT(10000000), LL_CONSTANT(922337203685)}, |
| 631 | {LL_CONSTANT(100000000), LL_CONSTANT(92233720368)}, |
| 632 | {LL_CONSTANT(1000000000), LL_CONSTANT(9223372036)}, |
| 633 | {LL_CONSTANT(10000000000), LL_CONSTANT(922337203)}, |
| 634 | {LL_CONSTANT(100000000000), LL_CONSTANT(92233720)}, |
| 635 | {LL_CONSTANT(1000000000000), LL_CONSTANT(9223372)}, |
| 636 | {LL_CONSTANT(10000000000000), LL_CONSTANT(922337)}, |
| 637 | {LL_CONSTANT(100000000000000), LL_CONSTANT(92233)}, |
| 638 | {LL_CONSTANT(1000000000000000), LL_CONSTANT(9223)}, |
| 639 | {LL_CONSTANT(10000000000000000), LL_CONSTANT(922)}, |
| 640 | {LL_CONSTANT(100000000000000000), LL_CONSTANT(92)}, |
| 641 | {LL_CONSTANT(1000000000000000000), LL_CONSTANT(9)}, |
| 642 | #endif |
| 643 | }; |
| 644 | static const int maxmod10 = 7; /* (int) (maxdiv[0].maxval % 10) */ |
| 645 | |
| 646 | static ssize_t |
| 647 | numFromStr(const char *src, size_t *len, void **dst, int tp, bool external) |
| 648 | { |
| 649 | const char *p = src; |
| 650 | size_t sz = ATOMsize(tp); |
| 651 | #ifdef HAVE_HGE |
| 652 | hge base = 0; |
| 653 | #else |
| 654 | lng base = 0; |
| 655 | #endif |
| 656 | int sign = 1; |
| 657 | |
| 658 | /* a valid number has the following syntax: |
| 659 | * [-+]?[0-9]+([eE][0-9]+)?(LL)? -- PCRE syntax, or in other words |
| 660 | * optional sign, one or more digits, optional exponent, optional LL |
| 661 | * the exponent has the following syntax: |
| 662 | * lower or upper case letter E, one or more digits |
| 663 | * embedded spaces are not allowed |
| 664 | * the optional LL at the end are only allowed for lng and hge |
| 665 | * values */ |
| 666 | atommem(sz); |
| 667 | |
| 668 | if (GDK_STRNIL(src)) { |
| 669 | memcpy(*dst, ATOMnilptr(tp), sz); |
| 670 | return 1; |
| 671 | } |
| 672 | |
| 673 | while (GDKisspace(*p)) |
| 674 | p++; |
| 675 | if (!num10(*p)) { |
| 676 | switch (*p) { |
| 677 | case 'n': |
| 678 | if (external) { |
| 679 | memcpy(*dst, ATOMnilptr(tp), sz); |
| 680 | if (p[1] == 'i' && p[2] == 'l') { |
| 681 | p += 3; |
| 682 | return (ssize_t) (p - src); |
| 683 | } |
| 684 | } |
| 685 | GDKerror("not a number" ); |
| 686 | goto bailout; |
| 687 | case '-': |
| 688 | sign = -1; |
| 689 | p++; |
| 690 | break; |
| 691 | case '+': |
| 692 | p++; |
| 693 | break; |
| 694 | } |
| 695 | if (!num10(*p)) { |
| 696 | GDKerror("not a number" ); |
| 697 | goto bailout; |
| 698 | } |
| 699 | } |
| 700 | do { |
| 701 | int dig = base10(*p); |
| 702 | if (base > maxdiv[1].maxval || |
| 703 | (base == maxdiv[1].maxval && dig > maxmod10)) { |
| 704 | /* overflow */ |
| 705 | goto overflow; |
| 706 | } |
| 707 | base = 10 * base + dig; |
| 708 | p++; |
| 709 | } while (num10(*p)); |
| 710 | if ((*p == 'e' || *p == 'E') && num10(p[1])) { |
| 711 | p++; |
| 712 | if (base == 0) { |
| 713 | /* if base is 0, any exponent will do, the |
| 714 | * result is still 0 */ |
| 715 | while (num10(*p)) |
| 716 | p++; |
| 717 | } else { |
| 718 | int exp = 0; |
| 719 | do { |
| 720 | /* this calculation cannot overflow */ |
| 721 | exp = exp * 10 + base10(*p); |
| 722 | if (exp >= (int) (sizeof(maxdiv) / sizeof(maxdiv[0]))) { |
| 723 | /* overflow */ |
| 724 | goto overflow; |
| 725 | } |
| 726 | p++; |
| 727 | } while (num10(*p)); |
| 728 | if (base > maxdiv[exp].maxval) { |
| 729 | /* overflow */ |
| 730 | goto overflow; |
| 731 | } |
| 732 | base *= maxdiv[exp].scale; |
| 733 | } |
| 734 | } |
| 735 | base *= sign; |
| 736 | switch (sz) { |
| 737 | case 1: { |
| 738 | bte **dstbte = (bte **) dst; |
| 739 | if (base < GDK_bte_min || base > GDK_bte_max) { |
| 740 | goto overflow; |
| 741 | } |
| 742 | **dstbte = (bte) base; |
| 743 | break; |
| 744 | } |
| 745 | case 2: { |
| 746 | sht **dstsht = (sht **) dst; |
| 747 | if (base < GDK_sht_min || base > GDK_sht_max) { |
| 748 | goto overflow; |
| 749 | } |
| 750 | **dstsht = (sht) base; |
| 751 | break; |
| 752 | } |
| 753 | case 4: { |
| 754 | int **dstint = (int **) dst; |
| 755 | if (base < GDK_int_min || base > GDK_int_max) { |
| 756 | goto overflow; |
| 757 | } |
| 758 | **dstint = (int) base; |
| 759 | break; |
| 760 | } |
| 761 | case 8: { |
| 762 | lng **dstlng = (lng **) dst; |
| 763 | #ifdef HAVE_HGE |
| 764 | if (base < GDK_lng_min || base > GDK_lng_max) { |
| 765 | goto overflow; |
| 766 | } |
| 767 | #endif |
| 768 | **dstlng = (lng) base; |
| 769 | if (p[0] == 'L' && p[1] == 'L') |
| 770 | p += 2; |
| 771 | break; |
| 772 | } |
| 773 | #ifdef HAVE_HGE |
| 774 | case 16: { |
| 775 | hge **dsthge = (hge **) dst; |
| 776 | **dsthge = (hge) base; |
| 777 | if (p[0] == 'L' && p[1] == 'L') |
| 778 | p += 2; |
| 779 | break; |
| 780 | } |
| 781 | #endif |
| 782 | } |
| 783 | while (GDKisspace(*p)) |
| 784 | p++; |
| 785 | return (ssize_t) (p - src); |
| 786 | |
| 787 | overflow: |
| 788 | while (num10(*p)) |
| 789 | p++; |
| 790 | GDKerror("overflow: \"%.*s\" does not fit in %s\n" , |
| 791 | (int) (p - src), src, ATOMname(tp)); |
| 792 | bailout: |
| 793 | memcpy(*dst, ATOMnilptr(tp), sz); |
| 794 | return -1; |
| 795 | } |
| 796 | |
| 797 | ssize_t |
| 798 | bteFromStr(const char *src, size_t *len, bte **dst, bool external) |
| 799 | { |
| 800 | return numFromStr(src, len, (void **) dst, TYPE_bte, external); |
| 801 | } |
| 802 | |
| 803 | ssize_t |
| 804 | shtFromStr(const char *src, size_t *len, sht **dst, bool external) |
| 805 | { |
| 806 | return numFromStr(src, len, (void **) dst, TYPE_sht, external); |
| 807 | } |
| 808 | |
| 809 | ssize_t |
| 810 | intFromStr(const char *src, size_t *len, int **dst, bool external) |
| 811 | { |
| 812 | return numFromStr(src, len, (void **) dst, TYPE_int, external); |
| 813 | } |
| 814 | |
| 815 | ssize_t |
| 816 | lngFromStr(const char *src, size_t *len, lng **dst, bool external) |
| 817 | { |
| 818 | return numFromStr(src, len, (void **) dst, TYPE_lng, external); |
| 819 | } |
| 820 | |
| 821 | #ifdef HAVE_HGE |
| 822 | ssize_t |
| 823 | hgeFromStr(const char *src, size_t *len, hge **dst, bool external) |
| 824 | { |
| 825 | return numFromStr(src, len, (void **) dst, TYPE_hge, external); |
| 826 | } |
| 827 | #endif |
| 828 | |
| 829 | #define atom_io(TYPE, NAME, CAST) \ |
| 830 | static TYPE * \ |
| 831 | TYPE##Read(TYPE *A, stream *s, size_t cnt) \ |
| 832 | { \ |
| 833 | TYPE *a = A; \ |
| 834 | if (a == NULL && (a = GDKmalloc(cnt * sizeof(TYPE))) == NULL) \ |
| 835 | return NULL; \ |
| 836 | if (mnstr_read##NAME##Array(s, (CAST *) a, cnt) == 0 || \ |
| 837 | mnstr_errnr(s)) { \ |
| 838 | if (a != A) \ |
| 839 | GDKfree(a); \ |
| 840 | return NULL; \ |
| 841 | } \ |
| 842 | return a; \ |
| 843 | } \ |
| 844 | static gdk_return \ |
| 845 | TYPE##Write(const TYPE *a, stream *s, size_t cnt) \ |
| 846 | { \ |
| 847 | return mnstr_write##NAME##Array(s, (const CAST *) a, cnt) ? \ |
| 848 | GDK_SUCCEED : GDK_FAIL; \ |
| 849 | } |
| 850 | |
| 851 | atom_io(bat, Int, int) |
| 852 | atom_io(bit, Bte, bte) |
| 853 | |
| 854 | atomtostr(bte, "%hhd" , ) |
| 855 | atom_io(bte, Bte, bte) |
| 856 | |
| 857 | atomtostr(sht, "%hd" , ) |
| 858 | atom_io(sht, Sht, sht) |
| 859 | |
| 860 | atomtostr(int, "%d" , ) |
| 861 | atom_io(int, Int, int) |
| 862 | |
| 863 | atomtostr(lng, LLFMT, ) |
| 864 | atom_io(lng, Lng, lng) |
| 865 | |
| 866 | #ifdef HAVE_HGE |
| 867 | #define HGE_LL018FMT "%018" PRId64 |
| 868 | #define HGE_LL18DIGITS LL_CONSTANT(1000000000000000000) |
| 869 | #define HGE_ABS(a) (((a) < 0) ? -(a) : (a)) |
| 870 | ssize_t |
| 871 | hgeToStr(char **dst, size_t *len, const hge *src, bool external) |
| 872 | { |
| 873 | atommem(hgeStrlen); |
| 874 | if (is_hge_nil(*src)) { |
| 875 | if (external) { |
| 876 | return (ssize_t) strcpy_len(*dst, "nil" , 4); |
| 877 | } |
| 878 | strcpy(*dst, str_nil); |
| 879 | return 1; |
| 880 | } |
| 881 | if ((hge) GDK_lng_min <= *src && *src <= (hge) GDK_lng_max) { |
| 882 | lng s = (lng) *src; |
| 883 | return lngToStr(dst, len, &s, external); |
| 884 | } else { |
| 885 | hge s = *src / HGE_LL18DIGITS; |
| 886 | ssize_t llen = hgeToStr(dst, len, &s, external); |
| 887 | if (llen < 0) |
| 888 | return llen; |
| 889 | snprintf(*dst + llen, *len - llen, HGE_LL018FMT, |
| 890 | (lng) HGE_ABS(*src % HGE_LL18DIGITS)); |
| 891 | return strlen(*dst); |
| 892 | } |
| 893 | } |
| 894 | atom_io(hge, Hge, hge) |
| 895 | #endif |
| 896 | |
| 897 | ssize_t |
| 898 | ptrFromStr(const char *src, size_t *len, ptr **dst, bool external) |
| 899 | { |
| 900 | size_t base = 0; |
| 901 | const char *p = src; |
| 902 | |
| 903 | atommem(sizeof(ptr)); |
| 904 | |
| 905 | **dst = ptr_nil; |
| 906 | if (GDK_STRNIL(src)) |
| 907 | return 1; |
| 908 | |
| 909 | while (GDKisspace(*p)) |
| 910 | p++; |
| 911 | if (external && strncmp(p, "nil" , 3) == 0) { |
| 912 | p += 3; |
| 913 | } else { |
| 914 | if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { |
| 915 | p += 2; |
| 916 | } |
| 917 | if (!num16(*p)) { |
| 918 | GDKerror("not a number\n" ); |
| 919 | return -1; |
| 920 | } |
| 921 | while (num16(*p)) { |
| 922 | if (base >= ((size_t) 1 << (8 * sizeof(size_t) - 4))) { |
| 923 | GDKerror("overflow\n" ); |
| 924 | return -1; |
| 925 | } |
| 926 | base = mult16(base) + base16(*p); |
| 927 | p++; |
| 928 | } |
| 929 | **dst = (ptr) base; |
| 930 | } |
| 931 | while (GDKisspace(*p)) |
| 932 | p++; |
| 933 | return (ssize_t) (p - src); |
| 934 | } |
| 935 | |
| 936 | atomtostr(ptr, "%p" , ) |
| 937 | |
| 938 | #if SIZEOF_VOID_P == SIZEOF_INT |
| 939 | atom_io(ptr, Int, int) |
| 940 | #else /* SIZEOF_VOID_P == SIZEOF_LNG */ |
| 941 | atom_io(ptr, Lng, lng) |
| 942 | #endif |
| 943 | |
| 944 | ssize_t |
| 945 | dblFromStr(const char *src, size_t *len, dbl **dst, bool external) |
| 946 | { |
| 947 | const char *p = src; |
| 948 | ssize_t n = 0; |
| 949 | double d; |
| 950 | |
| 951 | /* alloc memory */ |
| 952 | atommem(sizeof(dbl)); |
| 953 | |
| 954 | if (GDK_STRNIL(src)) { |
| 955 | **dst = dbl_nil; |
| 956 | return 1; |
| 957 | } |
| 958 | |
| 959 | while (GDKisspace(*p)) |
| 960 | p++; |
| 961 | if (external && strncmp(p, "nil" , 3) == 0) { |
| 962 | **dst = dbl_nil; |
| 963 | p += 3; |
| 964 | n = (ssize_t) (p - src); |
| 965 | } else { |
| 966 | /* on overflow, strtod returns HUGE_VAL and sets |
| 967 | * errno to ERANGE; on underflow, it returns a value |
| 968 | * whose magnitude is no greater than the smallest |
| 969 | * normalized double, and may or may not set errno to |
| 970 | * ERANGE. We accept underflow, but not overflow. */ |
| 971 | char *pe; |
| 972 | errno = 0; |
| 973 | d = strtod(p, &pe); |
| 974 | if (p == pe) |
| 975 | p = src; /* nothing converted */ |
| 976 | else |
| 977 | p = pe; |
| 978 | n = (ssize_t) (p - src); |
| 979 | if (n == 0 || (errno == ERANGE && (d < -1 || d > 1)) |
| 980 | || !isfinite(d) /* no NaN or Infinte */ |
| 981 | ) { |
| 982 | GDKerror("overflow or not a number\n" ); |
| 983 | return -1; |
| 984 | } else { |
| 985 | while (src[n] && GDKisspace(src[n])) |
| 986 | n++; |
| 987 | **dst = (dbl) d; |
| 988 | } |
| 989 | } |
| 990 | return n; |
| 991 | } |
| 992 | |
| 993 | ssize_t |
| 994 | dblToStr(char **dst, size_t *len, const dbl *src, bool external) |
| 995 | { |
| 996 | int i; |
| 997 | |
| 998 | atommem(dblStrlen); |
| 999 | if (is_dbl_nil(*src)) { |
| 1000 | if (external) { |
| 1001 | strcpy(*dst, "nil" ); |
| 1002 | return 3; |
| 1003 | } |
| 1004 | strcpy(*dst, str_nil); |
| 1005 | return 1; |
| 1006 | } |
| 1007 | for (i = 4; i < 18; i++) { |
| 1008 | snprintf(*dst, *len, "%.*g" , i, *src); |
| 1009 | if (strtod(*dst, NULL) == *src) |
| 1010 | break; |
| 1011 | } |
| 1012 | return (ssize_t) strlen(*dst); |
| 1013 | } |
| 1014 | |
| 1015 | atom_io(dbl, Lng, lng) |
| 1016 | |
| 1017 | ssize_t |
| 1018 | fltFromStr(const char *src, size_t *len, flt **dst, bool external) |
| 1019 | { |
| 1020 | const char *p = src; |
| 1021 | ssize_t n = 0; |
| 1022 | float f; |
| 1023 | |
| 1024 | /* alloc memory */ |
| 1025 | atommem(sizeof(flt)); |
| 1026 | |
| 1027 | if (GDK_STRNIL(src)) { |
| 1028 | **dst = flt_nil; |
| 1029 | return 1; |
| 1030 | } |
| 1031 | |
| 1032 | while (GDKisspace(*p)) |
| 1033 | p++; |
| 1034 | if (external && strncmp(p, "nil" , 3) == 0) { |
| 1035 | **dst = flt_nil; |
| 1036 | p += 3; |
| 1037 | n = (ssize_t) (p - src); |
| 1038 | } else { |
| 1039 | /* on overflow, strtof returns HUGE_VALF and sets |
| 1040 | * errno to ERANGE; on underflow, it returns a value |
| 1041 | * whose magnitude is no greater than the smallest |
| 1042 | * normalized float, and may or may not set errno to |
| 1043 | * ERANGE. We accept underflow, but not overflow. */ |
| 1044 | char *pe; |
| 1045 | errno = 0; |
| 1046 | f = strtof(p, &pe); |
| 1047 | if (p == pe) |
| 1048 | p = src; /* nothing converted */ |
| 1049 | else |
| 1050 | p = pe; |
| 1051 | n = (ssize_t) (p - src); |
| 1052 | if (n == 0 || (errno == ERANGE && (f < -1 || f > 1)) |
| 1053 | || !isfinite(f) /* no NaN or infinite */) { |
| 1054 | GDKerror("overflow or not a number\n" ); |
| 1055 | return -1; |
| 1056 | } else { |
| 1057 | while (src[n] && GDKisspace(src[n])) |
| 1058 | n++; |
| 1059 | **dst = (flt) f; |
| 1060 | } |
| 1061 | } |
| 1062 | return n; |
| 1063 | } |
| 1064 | |
| 1065 | ssize_t |
| 1066 | fltToStr(char **dst, size_t *len, const flt *src, bool external) |
| 1067 | { |
| 1068 | int i; |
| 1069 | |
| 1070 | atommem(fltStrlen); |
| 1071 | if (is_flt_nil(*src)) { |
| 1072 | if (external) { |
| 1073 | strcpy(*dst, "nil" ); |
| 1074 | return 3; |
| 1075 | } |
| 1076 | strcpy(*dst, str_nil); |
| 1077 | return 1; |
| 1078 | } |
| 1079 | for (i = 4; i < 10; i++) { |
| 1080 | snprintf(*dst, *len, "%.*g" , i, *src); |
| 1081 | if (strtof(*dst, NULL) == *src) |
| 1082 | break; |
| 1083 | } |
| 1084 | return (ssize_t) strlen(*dst); |
| 1085 | } |
| 1086 | |
| 1087 | atom_io(flt, Int, int) |
| 1088 | |
| 1089 | |
| 1090 | /* |
| 1091 | * String conversion routines. |
| 1092 | */ |
| 1093 | ssize_t |
| 1094 | OIDfromStr(const char *src, size_t *len, oid **dst, bool external) |
| 1095 | { |
| 1096 | #if SIZEOF_OID == SIZEOF_INT |
| 1097 | int ui = 0, *uip = &ui; |
| 1098 | #else |
| 1099 | lng ui = 0, *uip = &ui; |
| 1100 | #endif |
| 1101 | size_t l = sizeof(ui); |
| 1102 | ssize_t pos = 0; |
| 1103 | const char *p = src; |
| 1104 | |
| 1105 | atommem(sizeof(oid)); |
| 1106 | |
| 1107 | **dst = oid_nil; |
| 1108 | if (GDK_STRNIL(src)) |
| 1109 | return 1; |
| 1110 | |
| 1111 | while (GDKisspace(*p)) |
| 1112 | p++; |
| 1113 | |
| 1114 | if (external && strncmp(p, "nil" , 3) == 0) |
| 1115 | return (ssize_t) (p - src) + 3; |
| 1116 | |
| 1117 | if (GDKisdigit(*p)) { |
| 1118 | #if SIZEOF_OID == SIZEOF_INT |
| 1119 | pos = intFromStr(p, &l, &uip, external); |
| 1120 | #else |
| 1121 | pos = lngFromStr(p, &l, &uip, external); |
| 1122 | #endif |
| 1123 | if (pos < 0) |
| 1124 | return pos; |
| 1125 | if (p[pos] == '@') { |
| 1126 | pos++; |
| 1127 | while (GDKisdigit(p[pos])) |
| 1128 | pos++; |
| 1129 | } |
| 1130 | if (ui >= 0) { |
| 1131 | **dst = ui; |
| 1132 | } |
| 1133 | p += pos; |
| 1134 | } else { |
| 1135 | GDKerror("not an OID\n" ); |
| 1136 | return -1; |
| 1137 | } |
| 1138 | while (GDKisspace(*p)) |
| 1139 | p++; |
| 1140 | return (ssize_t) (p - src); |
| 1141 | } |
| 1142 | |
| 1143 | ssize_t |
| 1144 | OIDtoStr(char **dst, size_t *len, const oid *src, bool external) |
| 1145 | { |
| 1146 | atommem(oidStrlen); |
| 1147 | |
| 1148 | if (is_oid_nil(*src)) { |
| 1149 | if (external) { |
| 1150 | strcpy(*dst, "nil" ); |
| 1151 | return 3; |
| 1152 | } |
| 1153 | strcpy(*dst, str_nil); |
| 1154 | return 1; |
| 1155 | } |
| 1156 | return snprintf(*dst, *len, OIDFMT "@0" , *src); |
| 1157 | } |
| 1158 | |
| 1159 | atomDesc BATatoms[MAXATOMS] = { |
| 1160 | [TYPE_void] = { |
| 1161 | .name = "void" , |
| 1162 | .storage = TYPE_void, |
| 1163 | .linear = true, |
| 1164 | #if SIZEOF_OID == SIZEOF_INT |
| 1165 | .atomNull = (void *) &int_nil, |
| 1166 | .atomCmp = (int (*)(const void *, const void *)) intCmp, |
| 1167 | .atomHash = (BUN (*)(const void *)) intHash, |
| 1168 | #else |
| 1169 | .atomNull = (void *) &lng_nil, |
| 1170 | .atomCmp = (int (*)(const void *, const void *)) lngCmp, |
| 1171 | .atomHash = (BUN (*)(const void *)) lngHash, |
| 1172 | #endif |
| 1173 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) OIDfromStr, |
| 1174 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) OIDtoStr, |
| 1175 | .atomRead = (void *(*)(void *, stream *, size_t)) voidRead, |
| 1176 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) voidWrite, |
| 1177 | }, |
| 1178 | [TYPE_bit] = { |
| 1179 | .name = "bit" , |
| 1180 | .storage = TYPE_bte, |
| 1181 | .linear = true, |
| 1182 | .size = sizeof(bit), |
| 1183 | .atomNull = (void *) &bte_nil, |
| 1184 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) bitFromStr, |
| 1185 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) bitToStr, |
| 1186 | .atomRead = (void *(*)(void *, stream *, size_t)) bitRead, |
| 1187 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) bitWrite, |
| 1188 | .atomCmp = (int (*)(const void *, const void *)) bteCmp, |
| 1189 | .atomHash = (BUN (*)(const void *)) bteHash, |
| 1190 | }, |
| 1191 | [TYPE_bte] = { |
| 1192 | .name = "bte" , |
| 1193 | .storage = TYPE_bte, |
| 1194 | .linear = true, |
| 1195 | .size = sizeof(bte), |
| 1196 | .atomNull = (void *) &bte_nil, |
| 1197 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) bteFromStr, |
| 1198 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) bteToStr, |
| 1199 | .atomRead = (void *(*)(void *, stream *, size_t)) bteRead, |
| 1200 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) bteWrite, |
| 1201 | .atomCmp = (int (*)(const void *, const void *)) bteCmp, |
| 1202 | .atomHash = (BUN (*)(const void *)) bteHash, |
| 1203 | }, |
| 1204 | [TYPE_sht] = { |
| 1205 | .name = "sht" , |
| 1206 | .storage = TYPE_sht, |
| 1207 | .linear = true, |
| 1208 | .size = sizeof(sht), |
| 1209 | .atomNull = (void *) &sht_nil, |
| 1210 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) shtFromStr, |
| 1211 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) shtToStr, |
| 1212 | .atomRead = (void *(*)(void *, stream *, size_t)) shtRead, |
| 1213 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) shtWrite, |
| 1214 | .atomCmp = (int (*)(const void *, const void *)) shtCmp, |
| 1215 | .atomHash = (BUN (*)(const void *)) shtHash, |
| 1216 | }, |
| 1217 | [TYPE_bat] = { |
| 1218 | .name = "BAT" , |
| 1219 | .storage = TYPE_int, |
| 1220 | .linear = true, |
| 1221 | .size = sizeof(bat), |
| 1222 | .atomNull = (void *) &int_nil, |
| 1223 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) batFromStr, |
| 1224 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) batToStr, |
| 1225 | .atomRead = (void *(*)(void *, stream *, size_t)) batRead, |
| 1226 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) batWrite, |
| 1227 | .atomCmp = (int (*)(const void *, const void *)) intCmp, |
| 1228 | .atomHash = (BUN (*)(const void *)) intHash, |
| 1229 | .atomFix = (int (*)(const void *)) batFix, |
| 1230 | .atomUnfix = (int (*)(const void *)) batUnfix, |
| 1231 | }, |
| 1232 | [TYPE_int] = { |
| 1233 | .name = "int" , |
| 1234 | .storage = TYPE_int, |
| 1235 | .linear = true, |
| 1236 | .size = sizeof(int), |
| 1237 | .atomNull = (void *) &int_nil, |
| 1238 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) intFromStr, |
| 1239 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) intToStr, |
| 1240 | .atomRead = (void *(*)(void *, stream *, size_t)) intRead, |
| 1241 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) intWrite, |
| 1242 | .atomCmp = (int (*)(const void *, const void *)) intCmp, |
| 1243 | .atomHash = (BUN (*)(const void *)) intHash, |
| 1244 | }, |
| 1245 | [TYPE_oid] = { |
| 1246 | .name = "oid" , |
| 1247 | .linear = true, |
| 1248 | .size = sizeof(oid), |
| 1249 | #if SIZEOF_OID == SIZEOF_INT |
| 1250 | .storage = TYPE_int, |
| 1251 | .atomNull = (void *) &int_nil, |
| 1252 | .atomRead = (void *(*)(void *, stream *, size_t)) intRead, |
| 1253 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) intWrite, |
| 1254 | .atomCmp = (int (*)(const void *, const void *)) intCmp, |
| 1255 | .atomHash = (BUN (*)(const void *)) intHash, |
| 1256 | #else |
| 1257 | .storage = TYPE_lng, |
| 1258 | .atomNull = (void *) &lng_nil, |
| 1259 | .atomRead = (void *(*)(void *, stream *, size_t)) lngRead, |
| 1260 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) lngWrite, |
| 1261 | .atomCmp = (int (*)(const void *, const void *)) lngCmp, |
| 1262 | .atomHash = (BUN (*)(const void *)) lngHash, |
| 1263 | #endif |
| 1264 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) OIDfromStr, |
| 1265 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) OIDtoStr, |
| 1266 | }, |
| 1267 | [TYPE_ptr] = { |
| 1268 | .name = "ptr" , |
| 1269 | .storage = TYPE_ptr, |
| 1270 | .linear = true, |
| 1271 | .size = sizeof(void *), |
| 1272 | .atomNull = (void *) &ptr_nil, |
| 1273 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) ptrFromStr, |
| 1274 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) ptrToStr, |
| 1275 | .atomRead = (void *(*)(void *, stream *, size_t)) ptrRead, |
| 1276 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) ptrWrite, |
| 1277 | #if SIZEOF_VOID_P == SIZEOF_INT |
| 1278 | .atomCmp = (int (*)(const void *, const void *)) intCmp, |
| 1279 | .atomHash = (BUN (*)(const void *)) intHash, |
| 1280 | #else /* SIZEOF_VOID_P == SIZEOF_LNG */ |
| 1281 | .atomCmp = (int (*)(const void *, const void *)) lngCmp, |
| 1282 | .atomHash = (BUN (*)(const void *)) lngHash, |
| 1283 | #endif |
| 1284 | }, |
| 1285 | [TYPE_flt] = { |
| 1286 | .name = "flt" , |
| 1287 | .storage = TYPE_flt, |
| 1288 | .linear = true, |
| 1289 | .size = sizeof(flt), |
| 1290 | .atomNull = (void *) &flt_nil, |
| 1291 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) fltFromStr, |
| 1292 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) fltToStr, |
| 1293 | .atomRead = (void *(*)(void *, stream *, size_t)) fltRead, |
| 1294 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) fltWrite, |
| 1295 | .atomCmp = (int (*)(const void *, const void *)) fltCmp, |
| 1296 | .atomHash = (BUN (*)(const void *)) intHash, |
| 1297 | }, |
| 1298 | [TYPE_dbl] = { |
| 1299 | .name = "dbl" , |
| 1300 | .storage = TYPE_dbl, |
| 1301 | .linear = true, |
| 1302 | .size = sizeof(dbl), |
| 1303 | .atomNull = (void *) &dbl_nil, |
| 1304 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) dblFromStr, |
| 1305 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) dblToStr, |
| 1306 | .atomRead = (void *(*)(void *, stream *, size_t)) dblRead, |
| 1307 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) dblWrite, |
| 1308 | .atomCmp = (int (*)(const void *, const void *)) dblCmp, |
| 1309 | .atomHash = (BUN (*)(const void *)) lngHash, |
| 1310 | }, |
| 1311 | [TYPE_lng] = { |
| 1312 | .name = "lng" , |
| 1313 | .storage = TYPE_lng, |
| 1314 | .linear = true, |
| 1315 | .size = sizeof(lng), |
| 1316 | .atomNull = (void *) &lng_nil, |
| 1317 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) lngFromStr, |
| 1318 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) lngToStr, |
| 1319 | .atomRead = (void *(*)(void *, stream *, size_t)) lngRead, |
| 1320 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) lngWrite, |
| 1321 | .atomCmp = (int (*)(const void *, const void *)) lngCmp, |
| 1322 | .atomHash = (BUN (*)(const void *)) lngHash, |
| 1323 | }, |
| 1324 | #ifdef HAVE_HGE |
| 1325 | [TYPE_hge] = { |
| 1326 | .name = "hge" , |
| 1327 | .storage = TYPE_hge, |
| 1328 | .linear = true, |
| 1329 | .size = sizeof(hge), |
| 1330 | .atomNull = (void *) &hge_nil, |
| 1331 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) hgeFromStr, |
| 1332 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) hgeToStr, |
| 1333 | .atomRead = (void *(*)(void *, stream *, size_t)) hgeRead, |
| 1334 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) hgeWrite, |
| 1335 | .atomCmp = (int (*)(const void *, const void *)) hgeCmp, |
| 1336 | .atomHash = (BUN (*)(const void *)) hgeHash, |
| 1337 | }, |
| 1338 | #endif |
| 1339 | [TYPE_str] = { |
| 1340 | .name = "str" , |
| 1341 | .storage = TYPE_str, |
| 1342 | .linear = true, |
| 1343 | .size = sizeof(var_t), |
| 1344 | .atomNull = (void *) str_nil, |
| 1345 | .atomFromStr = (ssize_t (*)(const char *, size_t *, void **, bool)) strFromStr, |
| 1346 | .atomToStr = (ssize_t (*)(char **, size_t *, const void *, bool)) strToStr, |
| 1347 | .atomRead = (void *(*)(void *, stream *, size_t)) strRead, |
| 1348 | .atomWrite = (gdk_return (*)(const void *, stream *, size_t)) strWrite, |
| 1349 | .atomCmp = (int (*)(const void *, const void *)) strCmp, |
| 1350 | .atomHash = (BUN (*)(const void *)) strHash, |
| 1351 | .atomPut = (var_t (*)(Heap *, var_t *, const void *)) strPut, |
| 1352 | .atomLen = (size_t (*)(const void *)) strLen, |
| 1353 | .atomHeap = strHeap, |
| 1354 | }, |
| 1355 | }; |
| 1356 | |
| 1357 | int GDKatomcnt = TYPE_str + 1; |
| 1358 | |
| 1359 | /* |
| 1360 | * Sometimes a bat descriptor is loaded before the dynamic module |
| 1361 | * defining the atom is loaded. To support this an extra set of |
| 1362 | * unknown atoms is kept. These can be accessed via the ATOMunknown |
| 1363 | * interface. Finding an (negative) atom index can be done via |
| 1364 | * ATOMunknown_find, which simply adds the atom if it's not in the |
| 1365 | * unknown set. The index can be used to find the name of an unknown |
| 1366 | * ATOM via ATOMunknown_name. |
| 1367 | */ |
| 1368 | static str unknown[MAXATOMS] = { NULL }; |
| 1369 | |
| 1370 | int |
| 1371 | ATOMunknown_find(const char *nme) |
| 1372 | { |
| 1373 | int i, j = 0; |
| 1374 | |
| 1375 | /* first try to find the atom */ |
| 1376 | MT_lock_set(&GDKthreadLock); |
| 1377 | for (i = 1; i < MAXATOMS; i++) { |
| 1378 | if (unknown[i]) { |
| 1379 | if (strcmp(unknown[i], nme) == 0) { |
| 1380 | MT_lock_unset(&GDKthreadLock); |
| 1381 | return -i; |
| 1382 | } |
| 1383 | } else if (j == 0) |
| 1384 | j = i; |
| 1385 | } |
| 1386 | if (j == 0) { |
| 1387 | /* no space for new atom (shouldn't happen) */ |
| 1388 | MT_lock_unset(&GDKthreadLock); |
| 1389 | return 0; |
| 1390 | } |
| 1391 | if ((unknown[j] = GDKstrdup(nme)) == NULL) { |
| 1392 | MT_lock_unset(&GDKthreadLock); |
| 1393 | return 0; |
| 1394 | } |
| 1395 | MT_lock_unset(&GDKthreadLock); |
| 1396 | return -j; |
| 1397 | } |
| 1398 | |
| 1399 | str |
| 1400 | ATOMunknown_name(int i) |
| 1401 | { |
| 1402 | assert(i < 0); |
| 1403 | assert(unknown[-i]); |
| 1404 | return unknown[-i]; |
| 1405 | } |
| 1406 | |
| 1407 | void |
| 1408 | ATOMunknown_clean(void) |
| 1409 | { |
| 1410 | int i; |
| 1411 | |
| 1412 | MT_lock_set(&GDKthreadLock); |
| 1413 | for (i = 1; i < MAXATOMS; i++) { |
| 1414 | if(unknown[i]) { |
| 1415 | GDKfree(unknown[i]); |
| 1416 | unknown[i] = NULL; |
| 1417 | } else { |
| 1418 | break; |
| 1419 | } |
| 1420 | } |
| 1421 | MT_lock_unset(&GDKthreadLock); |
| 1422 | } |
| 1423 | |