|
13 | 13 | * </script> |
14 | 14 | */ |
15 | 15 |
|
16 | | -(function(root, factory) { |
| 16 | +(function (root, factory) { |
17 | 17 | if (typeof define === 'function' && define.amd) { |
18 | 18 | // AMD |
19 | 19 | define([], factory); |
|
24 | 24 | // Browser global |
25 | 25 | root.Rayforce = factory(); |
26 | 26 | } |
27 | | -}(typeof self !== 'undefined' ? self : this, function() { |
| 27 | +}(typeof self !== 'undefined' ? self : this, function () { |
28 | 28 | 'use strict'; |
29 | 29 |
|
30 | 30 | // ============================================================================ |
|
229 | 229 | if (this._typedArray === null) { |
230 | 230 | const ArrayType = TYPED_ARRAY_MAP[this._elementType]; |
231 | 231 | if (!ArrayType) throw new Error(`No TypedArray for type ${this._elementType}`); |
232 | | - |
| 232 | + |
233 | 233 | const dataPtr = this._sdk._getDataPtr(this._ptr); |
234 | 234 | const byteSize = this._sdk._getDataByteSize(this._ptr); |
235 | 235 | const elementSize = ELEMENT_SIZES[this._elementType]; |
236 | 236 | const length = byteSize / elementSize; |
237 | | - |
| 237 | + |
238 | 238 | this._typedArray = new ArrayType(this._sdk._wasm.HEAPU8.buffer, dataPtr, length); |
239 | 239 | } |
240 | 240 | return this._typedArray; |
|
326 | 326 | class Dict extends RayObject { |
327 | 327 | keys() { return this._sdk._wrapPtr(this._sdk._dictKeys(this._ptr)); } |
328 | 328 | values() { return this._sdk._wrapPtr(this._sdk._dictVals(this._ptr)); } |
329 | | - |
| 329 | + |
330 | 330 | get(key) { |
331 | 331 | const keyObj = typeof key === 'string' ? this._sdk.symbol(key) : key; |
332 | 332 | return this._sdk._wrapPtr(this._sdk._dictGet(this._ptr, keyObj._ptr)); |
333 | 333 | } |
334 | | - |
| 334 | + |
335 | 335 | has(key) { return !this.get(key).isNull; } |
336 | 336 |
|
337 | 337 | toJS() { |
|
360 | 360 |
|
361 | 361 | class Table extends RayObject { |
362 | 362 | columns() { return this._sdk._wrapPtr(this._sdk._tableKeys(this._ptr)); } |
363 | | - |
| 363 | + |
364 | 364 | columnNames() { |
365 | 365 | const cols = this.columns(); |
366 | 366 | const names = []; |
|
450 | 450 | le(value) { return this._binOp('<=', value); } |
451 | 451 | gt(value) { return this._binOp('>', value); } |
452 | 452 | ge(value) { return this._binOp('>=', value); } |
453 | | - |
| 453 | + |
454 | 454 | and(other) { return this._logicOp('and', other); } |
455 | 455 | or(other) { return this._logicOp('or', other); } |
456 | 456 | not() { return new Expr(this._sdk, ['(not', ...this._parts, ')']); } |
457 | | - |
| 457 | + |
458 | 458 | sum() { return new Expr(this._sdk, ['(sum', ...this._parts, ')']); } |
459 | 459 | avg() { return new Expr(this._sdk, ['(avg', ...this._parts, ')']); } |
460 | 460 | min() { return new Expr(this._sdk, ['(min', ...this._parts, ')']); } |
|
463 | 463 | first() { return new Expr(this._sdk, ['(first', ...this._parts, ')']); } |
464 | 464 | last() { return new Expr(this._sdk, ['(last', ...this._parts, ')']); } |
465 | 465 | distinct() { return new Expr(this._sdk, ['(distinct', ...this._parts, ')']); } |
466 | | - |
| 466 | + |
467 | 467 | _binOp(op, value) { |
468 | 468 | const valStr = this._valueToStr(value); |
469 | 469 | return new Expr(this._sdk, [`(${op}`, ...this._parts, valStr, ')']); |
470 | 470 | } |
471 | | - |
| 471 | + |
472 | 472 | _logicOp(op, other) { |
473 | 473 | return new Expr(this._sdk, [`(${op}`, ...this._parts, ...other._parts, ')']); |
474 | 474 | } |
475 | | - |
| 475 | + |
476 | 476 | _valueToStr(value) { |
477 | 477 | if (value instanceof Expr) return value.toString(); |
478 | 478 | if (typeof value === 'string') return `"${value}"`; |
479 | 479 | return String(value); |
480 | 480 | } |
481 | | - |
| 481 | + |
482 | 482 | toString() { return this._parts.join(' '); } |
483 | 483 | } |
484 | 484 |
|
|
564 | 564 |
|
565 | 565 | _setupBindings() { |
566 | 566 | const w = this._wasm; |
567 | | - |
| 567 | + |
568 | 568 | this._evalCmd = w.cwrap('eval_cmd', 'number', ['string', 'string']); |
569 | 569 | this._evalStr = w.cwrap('eval_str', 'number', ['string']); |
570 | 570 | this._strOfObj = w.cwrap('strof_obj', 'string', ['number']); |
571 | 571 | this._dropObj = w.cwrap('drop_obj', null, ['number']); |
572 | 572 | this._cloneObj = w.cwrap('clone_obj', 'number', ['number']); |
573 | 573 | this._versionStr = w.cwrap('version_str', 'string', []); |
574 | | - |
| 574 | + |
575 | 575 | this._getObjType = w.cwrap('get_obj_type', 'number', ['number']); |
576 | 576 | this._getObjLen = w.cwrap('get_obj_len', 'number', ['number']); |
577 | 577 | this._isObjAtom = w.cwrap('is_obj_atom', 'number', ['number']); |
578 | 578 | this._isObjVector = w.cwrap('is_obj_vector', 'number', ['number']); |
579 | 579 | this._isObjNull = w.cwrap('is_obj_null', 'number', ['number']); |
580 | 580 | this._isObjError = w.cwrap('is_obj_error', 'number', ['number']); |
581 | 581 | this._getObjRc = w.cwrap('get_obj_rc', 'number', ['number']); |
582 | | - |
| 582 | + |
583 | 583 | this._getDataPtr = w.cwrap('get_data_ptr', 'number', ['number']); |
584 | 584 | this._getElementSize = w.cwrap('get_element_size', 'number', ['number']); |
585 | 585 | this._getDataByteSize = w.cwrap('get_data_byte_size', 'number', ['number']); |
586 | | - |
| 586 | + |
587 | 587 | this._initB8 = w.cwrap('init_b8', 'number', ['number']); |
588 | 588 | this._initU8 = w.cwrap('init_u8', 'number', ['number']); |
589 | 589 | this._initC8 = w.cwrap('init_c8', 'number', ['number']); |
|
596 | 596 | this._initTimestamp = w.cwrap('init_timestamp', 'number', ['number']); |
597 | 597 | this._initSymbolStr = w.cwrap('init_symbol_str', 'number', ['string', 'number']); |
598 | 598 | this._initStringStr = w.cwrap('init_string_str', 'number', ['string', 'number']); |
599 | | - |
| 599 | + |
600 | 600 | this._readB8 = w.cwrap('read_b8', 'number', ['number']); |
601 | 601 | this._readU8 = w.cwrap('read_u8', 'number', ['number']); |
602 | 602 | this._readC8 = w.cwrap('read_c8', 'number', ['number']); |
|
609 | 609 | this._readTimestamp = w.cwrap('read_timestamp', 'number', ['number']); |
610 | 610 | this._readSymbolId = w.cwrap('read_symbol_id', 'number', ['number']); |
611 | 611 | this._symbolToStr = w.cwrap('symbol_to_str', 'string', ['number']); |
612 | | - |
| 612 | + // Change signature to number (ptr) to handle manual heap allocation |
| 613 | + this._readCSV = w.cwrap('read_csv', 'number', ['number', 'number']); |
| 614 | + |
613 | 615 | this._initVector = w.cwrap('init_vector', 'number', ['number', 'number']); |
614 | 616 | this._initList = w.cwrap('init_list', 'number', ['number']); |
615 | 617 | this._vecAtIdx = w.cwrap('vec_at_idx', 'number', ['number', 'number']); |
616 | 618 | this._atIdx = w.cwrap('at_idx', 'number', ['number', 'number']); |
617 | 619 | this._atObj = w.cwrap('at_obj', 'number', ['number', 'number']); |
618 | 620 | this._pushObj = w.cwrap('push_obj', 'number', ['number', 'number']); |
619 | 621 | this._insObj = w.cwrap('ins_obj', 'number', ['number', 'number', 'number']); |
620 | | - |
| 622 | + |
621 | 623 | this._initDict = w.cwrap('init_dict', 'number', ['number', 'number']); |
622 | 624 | this._dictKeys = w.cwrap('dict_keys', 'number', ['number']); |
623 | 625 | this._dictVals = w.cwrap('dict_vals', 'number', ['number']); |
624 | 626 | this._dictGet = w.cwrap('dict_get', 'number', ['number', 'number']); |
625 | | - |
| 627 | + |
626 | 628 | this._initTable = w.cwrap('init_table', 'number', ['number', 'number']); |
627 | 629 | this._tableKeys = w.cwrap('table_keys', 'number', ['number']); |
628 | 630 | this._tableVals = w.cwrap('table_vals', 'number', ['number']); |
629 | 631 | this._tableCol = w.cwrap('table_col', 'number', ['number', 'string', 'number']); |
630 | 632 | this._tableRow = w.cwrap('table_row', 'number', ['number', 'number']); |
631 | 633 | this._tableCount = w.cwrap('table_count', 'number', ['number']); |
632 | | - |
| 634 | + |
633 | 635 | this._querySelect = w.cwrap('query_select', 'number', ['number']); |
634 | 636 | this._queryUpdate = w.cwrap('query_update', 'number', ['number']); |
635 | 637 | this._tableInsert = w.cwrap('table_insert', 'number', ['number', 'number']); |
636 | 638 | this._tableUpsert = w.cwrap('table_upsert', 'number', ['number', 'number', 'number']); |
637 | | - |
| 639 | + |
638 | 640 | this._internSymbol = w.cwrap('intern_symbol', 'number', ['string', 'number']); |
639 | 641 | this._globalSet = w.cwrap('global_set', null, ['number', 'number']); |
640 | 642 | this._quoteObj = w.cwrap('quote_obj', 'number', ['number']); |
|
655 | 657 |
|
656 | 658 | _wrapPtr(ptr) { |
657 | 659 | if (ptr === 0) return new RayNull(this, 0); |
658 | | - |
| 660 | + |
659 | 661 | const type = this._getObjType(ptr); |
660 | 662 | const isAtom = this._isObjAtom(ptr); |
661 | 663 | const absType = type < 0 ? -type : type; |
662 | | - |
| 664 | + |
663 | 665 | if (type === Types.ERR) return new RayError(this, ptr); |
664 | 666 | if (type === Types.NULL || this._isObjNull(ptr)) return new RayNull(this, ptr); |
665 | | - |
| 667 | + |
666 | 668 | if (isAtom) { |
667 | 669 | switch (absType) { |
668 | 670 | case Types.B8: return new B8(this, ptr); |
|
680 | 682 | default: return new RayObject(this, ptr); |
681 | 683 | } |
682 | 684 | } |
683 | | - |
| 685 | + |
684 | 686 | switch (type) { |
685 | 687 | case Types.C8: return new RayString(this, ptr); |
686 | 688 | case Types.LIST: return new List(this, ptr); |
|
701 | 703 | i32(value) { return new I32(this, this._initI32(value | 0)); } |
702 | 704 | i64(value) { return new I64(this, this._initI64(Number(value))); } |
703 | 705 | f64(value) { return new F64(this, this._initF64(value)); } |
704 | | - |
| 706 | + |
705 | 707 | date(value) { |
706 | 708 | let days; |
707 | 709 | if (value instanceof Date) { |
|
712 | 714 | } |
713 | 715 | return new RayDate(this, this._initDate(days)); |
714 | 716 | } |
715 | | - |
| 717 | + |
716 | 718 | time(value) { |
717 | 719 | let ms; |
718 | 720 | if (value instanceof Date) { |
719 | | - ms = value.getHours() * 3600000 + value.getMinutes() * 60000 + |
720 | | - value.getSeconds() * 1000 + value.getMilliseconds(); |
| 721 | + ms = value.getHours() * 3600000 + value.getMinutes() * 60000 + |
| 722 | + value.getSeconds() * 1000 + value.getMilliseconds(); |
721 | 723 | } else { |
722 | 724 | ms = value | 0; |
723 | 725 | } |
724 | 726 | return new RayTime(this, this._initTime(ms)); |
725 | 727 | } |
726 | | - |
| 728 | + |
727 | 729 | timestamp(value) { |
728 | 730 | let ns; |
729 | 731 | if (value instanceof Date) { |
|
734 | 736 | } |
735 | 737 | return new RayTimestamp(this, this._initTimestamp(ns)); |
736 | 738 | } |
737 | | - |
| 739 | + |
738 | 740 | symbol(value) { return new RaySymbol(this, this._initSymbolStr(value, value.length)); } |
739 | 741 | string(value) { return new RayString(this, this._initStringStr(value, value.length)); } |
740 | | - |
| 742 | + |
741 | 743 | vector(type, lengthOrData) { |
742 | 744 | if (Array.isArray(lengthOrData)) { |
743 | 745 | const arr = lengthOrData; |
|
754 | 756 | } |
755 | 757 | return new Vector(this, this._initVector(type, lengthOrData), type); |
756 | 758 | } |
757 | | - |
| 759 | + |
758 | 760 | list(items) { |
759 | 761 | const len = items ? items.length : 0; |
760 | 762 | const list = new List(this, this._initList(len)); |
|
765 | 767 | } |
766 | 768 | return list; |
767 | 769 | } |
768 | | - |
| 770 | + |
769 | 771 | dict(obj) { |
770 | 772 | const keys = Object.keys(obj); |
771 | 773 | const keyVec = this.vector(Types.SYMBOL, keys.length); |
|
776 | 778 | const valList = this.list(Object.values(obj).map(v => this._toRayObject(v))); |
777 | 779 | return new Dict(this, this._initDict(keyVec._ptr, valList._ptr)); |
778 | 780 | } |
779 | | - |
| 781 | + |
780 | 782 | table(columns) { |
781 | 783 | const colNames = Object.keys(columns); |
782 | 784 | const keyVec = this.vector(Types.SYMBOL, colNames.length); |
|
790 | 792 | } |
791 | 793 | return new Table(this, this._initTable(keyVec._ptr, valList._ptr)); |
792 | 794 | } |
793 | | - |
| 795 | + |
794 | 796 | _arrayToVector(arr) { |
795 | 797 | if (arr.length === 0) return this.vector(Types.I64, 0); |
796 | | - |
| 798 | + |
797 | 799 | const first = arr[0]; |
798 | 800 | let type; |
799 | | - |
| 801 | + |
800 | 802 | if (typeof first === 'boolean') type = Types.B8; |
801 | 803 | else if (typeof first === 'number') type = Number.isInteger(first) ? Types.I64 : Types.F64; |
802 | 804 | else if (typeof first === 'bigint') type = Types.I64; |
803 | 805 | else if (typeof first === 'string') type = Types.SYMBOL; |
804 | 806 | else if (first instanceof Date) type = Types.TIMESTAMP; |
805 | 807 | else return this.list(arr.map(v => this._toRayObject(v))); |
806 | | - |
| 808 | + |
807 | 809 | const vec = this.vector(type, arr.length); |
808 | 810 | const view = vec.typedArray; |
809 | | - |
| 811 | + |
810 | 812 | for (let i = 0; i < arr.length; i++) { |
811 | 813 | if (type === Types.SYMBOL) { |
812 | 814 | view[i] = BigInt(this._internSymbol(arr[i], arr[i].length)); |
|
823 | 825 | view[i] = arr[i]; |
824 | 826 | } |
825 | 827 | } |
826 | | - |
| 828 | + |
827 | 829 | return vec; |
828 | 830 | } |
829 | | - |
| 831 | + |
830 | 832 | _toRayObject(value) { |
831 | 833 | if (value instanceof RayObject) return value; |
832 | 834 | if (value === null || value === undefined) return new RayNull(this, 0); |
|
839 | 841 | if (typeof value === 'object') return this.dict(value); |
840 | 842 | return new RayNull(this, 0); |
841 | 843 | } |
842 | | - |
| 844 | + |
843 | 845 | set(name, value) { |
844 | 846 | const sym = this.symbol(name); |
845 | 847 | const val = value instanceof RayObject ? value : this._toRayObject(value); |
846 | 848 | this._globalSet(sym._ptr, val._ptr); |
847 | 849 | } |
848 | | - |
| 850 | + |
849 | 851 | get(name) { return this.eval(name); } |
850 | 852 | typeName(typeCode) { return this._getTypeName(typeCode); } |
851 | | - |
| 853 | + |
852 | 854 | col(name) { return Expr.col(this, name); } |
| 855 | + |
| 856 | + read_csv(content) { |
| 857 | + if (typeof content !== 'string') throw new Error('Content must be a string'); |
| 858 | + |
| 859 | + // Manually allocate memory on WASM heap to avoid stack overflow with large CSVs |
| 860 | + const lengthBytes = this._wasm.lengthBytesUTF8(content) + 1; |
| 861 | + const stringOnHeap = this._wasm._malloc(lengthBytes); |
| 862 | + |
| 863 | + try { |
| 864 | + this._wasm.stringToUTF8(content, stringOnHeap, lengthBytes); |
| 865 | + // Pass pointer and length (excluding null terminator) |
| 866 | + return new Table(this, this._readCSV(stringOnHeap, lengthBytes - 1)); |
| 867 | + } finally { |
| 868 | + this._wasm._free(stringOnHeap); |
| 869 | + } |
| 870 | + } |
853 | 871 | } |
854 | 872 |
|
855 | 873 | // ============================================================================ |
|
869 | 887 | try { |
870 | 888 | // For browser usage, we need to load the WASM module |
871 | 889 | let createRayforce; |
872 | | - |
| 890 | + |
873 | 891 | if (typeof window !== 'undefined') { |
874 | 892 | // Browser environment - expect global createRayforce or load via script |
875 | 893 | if (typeof window.createRayforce === 'function') { |
|
884 | 902 | const module = await import(wasmPath); |
885 | 903 | createRayforce = module.default; |
886 | 904 | } |
887 | | - |
| 905 | + |
888 | 906 | const wasm = await createRayforce({ |
889 | 907 | rayforce_ready: (msg) => { if (onReady) onReady(msg); } |
890 | 908 | }); |
|
0 commit comments