dolphindb 2.0.801 → 2.0.901

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/browser.js CHANGED
@@ -118,15 +118,6 @@ export const nulls = {
118
118
  double: -Number.MAX_VALUE,
119
119
  bytes16: Uint8Array.from(new Array(16).fill(0))
120
120
  };
121
- export const timezone_offset = 1000 * 60 * new Date().getTimezoneOffset();
122
- export var StreamingStatusCode;
123
- (function (StreamingStatusCode) {
124
- StreamingStatusCode[StreamingStatusCode["ok"] = 0] = "ok";
125
- StreamingStatusCode[StreamingStatusCode["connection_existed"] = 1] = "connection_existed";
126
- StreamingStatusCode[StreamingStatusCode["not_leader"] = 2] = "not_leader";
127
- StreamingStatusCode[StreamingStatusCode["other"] = 3] = "other";
128
- StreamingStatusCode[StreamingStatusCode["error"] = 4] = "error";
129
- })(StreamingStatusCode = StreamingStatusCode || (StreamingStatusCode = {}));
130
121
  /** 可以表示所有 DolphinDB 数据库中的数据类型 */
131
122
  export class DdbObj {
132
123
  static dec = new TextDecoder('utf-8');
@@ -522,8 +513,37 @@ export class DdbObj {
522
513
  // 00 reserved
523
514
  // 70 11 01 00 block.lengths = [70000 (0x00011170)]
524
515
  // 01 00 00 00 02 00 00 00 ... 279992 more bytes> block.data
516
+ // decimal array vector
517
+ // a = array(DECIMAL32(2)[], 0, 10)
518
+ // append!(a, [
519
+ // [1, 2, 3],
520
+ // [4, 5],
521
+ // [6, 7, 8],
522
+ // [9, 10]
523
+ // ])
524
+ // print(a)
525
+ // a
526
+ // [[1.00,2.00,3.00],[4.00,5.00],[6.00,7.00,8.00],[9.00,10.00]]
527
+ // <Buffer 65 01 type = 101 = 64 + 37 (decimal32) , form = vector
528
+ // 04 00 00 00 0a 00 00 00 rows = 4, cols = 10
529
+ // scale (只有 decimal32 才有)
530
+ // 02 00 00 00 scale = 2
531
+ // block 0
532
+ // 04 00 block.rows = 4
533
+ // 01 block.unit = 1
534
+ // 00 reserved
535
+ // 03 02 03 02 block.lengths = [3, 2, 3, 2]
536
+ // 64 00 00 00 c8 00 00 00 2c 01 00 00 90 01 00 00 f4 01 00 00 58 02 00 00 bc 02 00 00 ... 12 more bytes> [100, 200, 300, ...]
537
+ // block 1
538
+ // ...
539
+ const type_ = type - 64;
525
540
  const cols = dv.getUint32(4, le);
526
541
  let blocks = [];
542
+ // decimal32/64 会在所有 blocks 之前多一个 scale
543
+ if (type_ === DdbType.decimal32 || type_ === DdbType.decimal64) {
544
+ blocks.scale = dv.getInt32(i_items_start, le);
545
+ i_items_start += 4;
546
+ }
527
547
  let i_block_start = i_items_start;
528
548
  // 解析一个 block
529
549
  for (let i_row = 0; i_row < rows;) {
@@ -552,7 +572,20 @@ export class DdbObj {
552
572
  let total_length = 0;
553
573
  for (const x of lengths)
554
574
  total_length += x;
555
- const [len_items, data] = this.parse_vector_items(buf.subarray(i_data_start), le, type - 64, total_length);
575
+ let len_items;
576
+ let data;
577
+ switch (type_) {
578
+ case DdbType.decimal32:
579
+ len_items = total_length * 4;
580
+ data = new Int32Array(buf.buffer.slice(buf.byteOffset + i_data_start, buf.byteOffset + i_data_start + len_items));
581
+ break;
582
+ case DdbType.decimal64:
583
+ len_items = total_length * 8;
584
+ data = new BigInt64Array(buf.buffer.slice(buf.byteOffset + i_data_start, buf.byteOffset + i_data_start + len_items));
585
+ break;
586
+ default:
587
+ [len_items, data] = this.parse_vector_items(buf.subarray(i_data_start), le, type - 64, total_length);
588
+ }
556
589
  blocks.push({
557
590
  unit,
558
591
  rows,
@@ -867,6 +900,10 @@ export class DdbObj {
867
900
  if (form === DdbForm.vector && 64 <= type && type < 128)
868
901
  return [
869
902
  Uint32Array.of(this.rows, this.cols),
903
+ ...(type - 64 === DdbType.decimal32 || type - 64 === DdbType.decimal64) ?
904
+ [Int32Array.of(this.value.scale)]
905
+ :
906
+ [],
870
907
  ...this.value.map(block => ([
871
908
  Uint16Array.of(block.rows),
872
909
  Uint8Array.of(block.unit, 0),
@@ -925,10 +962,7 @@ export class DdbObj {
925
962
  })();
926
963
  if (!body)
927
964
  return new Uint8Array(0);
928
- return concat([
929
- header,
930
- ...body
931
- ]);
965
+ return concat([header, ...body]);
932
966
  }
933
967
  static pack_vector_body(value, type, length) {
934
968
  switch (type) {
@@ -1054,7 +1088,18 @@ export class DdbObj {
1054
1088
  for (const length of lengths) {
1055
1089
  let _items = new Array(Math.min(limit, length));
1056
1090
  for (let i = 0; i < _items.length; i++)
1057
- _items[i] = format(_type, data[acc_len + i], this.le, options);
1091
+ if (_type === DdbType.decimal32 || _type === DdbType.decimal64) {
1092
+ const x = data[acc_len + i];
1093
+ if (x === nulls.int64 && _type === DdbType.decimal64 ||
1094
+ x === nulls.int32 && _type === DdbType.decimal32)
1095
+ return '';
1096
+ const { scale } = this.value;
1097
+ const s = String(x < 0 ? -x : x).padStart(scale, '0');
1098
+ const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
1099
+ _items[i] = options.colors ? green(str) : str;
1100
+ }
1101
+ else
1102
+ _items[i] = format(_type, data[acc_len + i], this.le, options);
1058
1103
  items[i_items++] = format_array(_items, length > limit);
1059
1104
  acc_len += length;
1060
1105
  }
@@ -1436,6 +1481,7 @@ export function format(type, value, le, options = {}) {
1436
1481
  }
1437
1482
  /** formatted vector, the index-th item in the collection is a string */
1438
1483
  export function formati(obj, index, options = {}) {
1484
+ assert(index < obj.rows, 'index < obj.rows');
1439
1485
  if (64 <= obj.type && obj.type < 128) { // array vector
1440
1486
  // 因为 array vector 目前只支持:Logical, Integral(不包括 INT128, COMPRESS 类型), Floating, Temporal
1441
1487
  // 都对应 TypedArray 中的一格,所以 lengths.length 等于 block 中的 row 的个数
@@ -1463,7 +1509,18 @@ export function formati(obj, index, options = {}) {
1463
1509
  const limit = 10;
1464
1510
  let items = new Array(Math.min(limit, length));
1465
1511
  for (let i = 0; i < items.length; i++)
1466
- items[i] = format(_type, data[acc_len + i], obj.le, options);
1512
+ if (_type === DdbType.decimal32 || _type === DdbType.decimal64) {
1513
+ const x = data[acc_len + i];
1514
+ if (x === nulls.int64 && _type === DdbType.decimal64 ||
1515
+ x === nulls.int32 && _type === DdbType.decimal32)
1516
+ return '';
1517
+ const { scale } = obj.value;
1518
+ const s = String(x < 0 ? -x : x).padStart(scale, '0');
1519
+ const str = (x < 0 ? '-' : '') + (scale ? `${s.slice(0, -scale) || '0'}.${s.slice(-scale)}` : s);
1520
+ items[i] = options.colors ? green(str) : str;
1521
+ }
1522
+ else
1523
+ items[i] = format(_type, data[acc_len + i], obj.le, options);
1467
1524
  return (items.join(', ') + (length > limit ? ', ...' : '')).bracket('square');
1468
1525
  }
1469
1526
  }
@@ -1799,10 +1856,14 @@ export class DdbTable extends DdbObj {
1799
1856
  }
1800
1857
  }
1801
1858
  export function date2ms(date) {
1859
+ // 将 server 的本地时间 (以 ms 为单位,1970.01.01 00:00:00 作为零点) 当成是客户端这里的本地的时间,然后根据本地的时区信息转换为 utc 时间
1860
+ // 本地的时区与实际的时间值相关,timezone offset 可能会受到夏令时 (DST) 的影响
1861
+ // 得到的 utc 毫秒数交给 js date 或者 dayjs 去格式化
1862
+ const ms = 1000 * 3600 * 24 * date;
1802
1863
  return (date === null || date === nulls.int32) ?
1803
1864
  null
1804
1865
  :
1805
- timezone_offset + 1000 * 3600 * 24 * date;
1866
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1806
1867
  }
1807
1868
  export function date2str(date, format = 'YYYY.MM.DD') {
1808
1869
  return (date === null || date === nulls.int32) ?
@@ -1829,7 +1890,7 @@ export function time2ms(time) {
1829
1890
  return (time === null || time === nulls.int32) ?
1830
1891
  null
1831
1892
  :
1832
- timezone_offset + time;
1893
+ 1000 * 60 * new Date(time).getTimezoneOffset() + time;
1833
1894
  }
1834
1895
  export function time2str(time, format = 'HH:mm:ss.SSS') {
1835
1896
  return (time === null || time === nulls.int32) ?
@@ -1838,10 +1899,11 @@ export function time2str(time, format = 'HH:mm:ss.SSS') {
1838
1899
  dayjs(time2ms(time)).format(format);
1839
1900
  }
1840
1901
  export function minute2ms(minute) {
1902
+ const ms = 60 * 1000 * minute;
1841
1903
  return (minute === null || minute === nulls.int32) ?
1842
1904
  null
1843
1905
  :
1844
- timezone_offset + 60 * 1000 * minute;
1906
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1845
1907
  }
1846
1908
  export function minute2str(minute, format = 'HH:mm[m]') {
1847
1909
  return (minute === null || minute === nulls.int32) ?
@@ -1850,10 +1912,11 @@ export function minute2str(minute, format = 'HH:mm[m]') {
1850
1912
  dayjs(minute2ms(minute)).format(format);
1851
1913
  }
1852
1914
  export function second2ms(second) {
1915
+ const ms = 1000 * second;
1853
1916
  return (second === null || second === nulls.int32) ?
1854
1917
  null
1855
1918
  :
1856
- timezone_offset + 1000 * second;
1919
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1857
1920
  }
1858
1921
  export function second2str(second, format = 'HH:mm:ss') {
1859
1922
  return (second === null || second === nulls.int32) ?
@@ -1862,10 +1925,11 @@ export function second2str(second, format = 'HH:mm:ss') {
1862
1925
  dayjs(second2ms(second)).format(format);
1863
1926
  }
1864
1927
  export function datetime2ms(datetime) {
1928
+ const ms = 1000 * datetime;
1865
1929
  return (datetime === null || datetime === nulls.int32) ?
1866
1930
  null
1867
1931
  :
1868
- timezone_offset + 1000 * datetime;
1932
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1869
1933
  }
1870
1934
  export function datetime2str(datetime, format = 'YYYY.MM.DD HH:mm:ss') {
1871
1935
  return (datetime === null || datetime === nulls.int32) ?
@@ -1874,10 +1938,11 @@ export function datetime2str(datetime, format = 'YYYY.MM.DD HH:mm:ss') {
1874
1938
  dayjs(datetime2ms(datetime)).format(format);
1875
1939
  }
1876
1940
  export function timestamp2ms(timestamp) {
1941
+ const ms = Number(timestamp);
1877
1942
  return (timestamp === null || timestamp === nulls.int64) ?
1878
1943
  null
1879
1944
  :
1880
- timezone_offset + Number(timestamp);
1945
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1881
1946
  }
1882
1947
  /** format timestamp (bigint) to string
1883
1948
  - timestamp: bigint value
@@ -1892,16 +1957,18 @@ export function timestamp2str(timestamp, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
1892
1957
  dayjs(timestamp2ms(timestamp)).format(format);
1893
1958
  }
1894
1959
  export function datehour2ms(datehour) {
1960
+ const ms = 1000 * 3600 * datehour;
1895
1961
  return (datehour === null || datehour === nulls.int32) ?
1896
1962
  null
1897
1963
  :
1898
- timezone_offset + 1000 * 3600 * datehour;
1964
+ 1000 * 60 * new Date(ms).getTimezoneOffset() + ms;
1899
1965
  }
1900
1966
  export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
1967
+ const ms = 1000 * 3600 * datehour;
1901
1968
  return (datehour === null || datehour === nulls.int32) ?
1902
1969
  'null'
1903
1970
  :
1904
- dayjs(timezone_offset + 1000 * 3600 * datehour).format(format);
1971
+ dayjs(1000 * 60 * new Date(ms).getTimezoneOffset() + ms).format(format);
1905
1972
  }
1906
1973
  /** parse timestamp string to bigint value
1907
1974
  - str: timestamp string, If it is an empty string or 'null', it will return the corresponding empty value (nulls.int64)
@@ -1912,9 +1979,10 @@ export function datehour2str(datehour, format = 'YYYY.MM.DDTHH') {
1912
1979
  export function str2timestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSS') {
1913
1980
  if (!str || str === 'null')
1914
1981
  return nulls.int64;
1915
- assert('The length of the timestamp string must be equal to the length of the format string');
1916
- return BigInt(-timezone_offset +
1917
- dayjs(str, format).valueOf());
1982
+ assert(str.length === format.length, 'The length of the timestamp string must be equal to the length of the format string');
1983
+ const ms = dayjs(str, format).valueOf();
1984
+ return BigInt(-(1000 * 60 * new Date(ms).getTimezoneOffset()) +
1985
+ ms);
1918
1986
  }
1919
1987
  export function nanotime2ns(nanotime) {
1920
1988
  return nanotimestamp2ns(nanotime);
@@ -1929,15 +1997,17 @@ export function nanotime2str(nanotime, format = 'HH:mm:ss.SSSSSSSSS') {
1929
1997
  const i_second_end = i_second_start + 2;
1930
1998
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1931
1999
  assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1932
- return (dayjs(timezone_offset + (Number(nanotime) / 1000000)).format(format.slice(0, i_second_end)) +
2000
+ const ms = Number(nanotime) / 1000000;
2001
+ return (dayjs(1000 * 60 * new Date(ms).getTimezoneOffset() + ms).format(format.slice(0, i_second_end)) +
1933
2002
  format.slice(i_second_end, i_nanosecond_start) +
1934
2003
  String(nanotime % 1000000000n).padStart(9, '0'));
1935
2004
  }
1936
2005
  export function nanotimestamp2ns(nanotimestamp) {
2006
+ const ms = Number(nanotimestamp / 1000000n);
1937
2007
  return (nanotimestamp === null || nanotimestamp === nulls.int64) ?
1938
2008
  null
1939
2009
  :
1940
- BigInt(timezone_offset) * 1000000n + nanotimestamp;
2010
+ BigInt(1000 * 60 * new Date(ms).getTimezoneOffset()) * 1000000n + nanotimestamp;
1941
2011
  }
1942
2012
  /** format nanotimestamp value (bigint) to string
1943
2013
  - nanotimestamp: bigint value
@@ -1962,7 +2032,8 @@ export function nanotimestamp2str(nanotimestamp, format = 'YYYY.MM.DD HH:mm:ss.S
1962
2032
  assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1963
2033
  const remainder = nanotimestamp % 1000000000n;
1964
2034
  const borrow = remainder < 0n;
1965
- return (dayjs(timezone_offset +
2035
+ const ms = Number(nanotimestamp / 1000000n);
2036
+ return (dayjs(1000 * 60 * new Date(ms).getTimezoneOffset() +
1966
2037
  // 去掉 9 位的纳秒部分,转化为毫秒
1967
2038
  Number((nanotimestamp - remainder + (borrow ? -1000000000n : 0n)) / 1000000n)).format(format.slice(0, i_second_end)) +
1968
2039
  format.slice(i_second_end, i_nanosecond_start) +
@@ -1987,8 +2058,9 @@ export function str2nanotimestamp(str, format = 'YYYY.MM.DD HH:mm:ss.SSSSSSSSS')
1987
2058
  const i_second_end = i_second_start + 2;
1988
2059
  const i_nanosecond_start = format.indexOf('SSSSSSSSS', i_second_end);
1989
2060
  assert(i_nanosecond_start !== -1, 'Format string must contain nanosecond format (SSSSSSSSS)');
1990
- return (BigInt(-timezone_offset +
1991
- dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf()) * 1000000n
2061
+ const ms = dayjs(str.slice(0, i_second_end), format.slice(0, i_second_end)).valueOf();
2062
+ return (BigInt(-(1000 * 60 * new Date(ms).getTimezoneOffset()) +
2063
+ ms) * 1000000n
1992
2064
  +
1993
2065
  BigInt(str.slice(i_nanosecond_start, i_nanosecond_start + 9)));
1994
2066
  }
@@ -2014,7 +2086,7 @@ export function int1282str(buffer, le = true) {
2014
2086
  return [...buf].map(x => x.toString(16)
2015
2087
  .padStart(2, '0')).join('');
2016
2088
  }
2017
- export const winsize = 40;
2089
+ export const winsize = 10_0000;
2018
2090
  export class ConnectionError extends Error {
2019
2091
  ddb;
2020
2092
  constructor(ddb) {
@@ -2086,9 +2158,10 @@ export class DDB {
2086
2158
  constructor(url, options = {}) {
2087
2159
  if (!url) {
2088
2160
  const _url = new URL(location.href);
2089
- const hostname = _url.searchParams.get('hostname') || location.hostname;
2090
- const port = _url.searchParams.get('port') || location.port;
2091
- url = `${location.protocol === 'https:' ? 'wss' : 'ws'}://${hostname}:${port}/`;
2161
+ const dev = _url.pathname.endsWith('/console/');
2162
+ const hostname = _url.searchParams.get('hostname') || (dev ? '127.0.0.1' : location.hostname);
2163
+ const port = _url.searchParams.get('port') || (dev ? '8848' : location.port);
2164
+ url = `${dev ? (_url.searchParams.get('tls') === '1' ? 'wss' : 'ws') : (location.protocol === 'https:' ? 'wss' : 'ws')}://${hostname}${port ? `:${port}` : ''}/`;
2092
2165
  }
2093
2166
  this.url = url;
2094
2167
  if (options.autologin !== undefined)
@@ -2393,8 +2466,9 @@ export class DDB {
2393
2466
  async cancel() {
2394
2467
  let ddb = new DDB(this.url, this);
2395
2468
  try {
2469
+ // 因为是新建的连接,而且执行完脚本之后马上就关闭了,所以不用考虑变量泄漏的问题
2396
2470
  await ddb.eval(`jobs = exec rootJobId from getConsoleJobs() where sessionId = ${this.sid}\n` +
2397
- 'if (size(jobs))\n' +
2471
+ (this.python ? 'if size(jobs):\n' : 'if (size(jobs))\n') +
2398
2472
  ' cancelConsoleJob(jobs)\n', { urgent: true });
2399
2473
  }
2400
2474
  finally {
@@ -2455,110 +2529,58 @@ export class DDB {
2455
2529
  }
2456
2530
  /** Internal stream subscription method */
2457
2531
  async subscribe() {
2458
- const args = DdbObj.to_ddbobjs([
2459
- new DdbVectorString(this.autologin ?
2460
- [this.username, this.password]
2461
- :
2462
- // 不自动登录也要传空的 string vector, 否则 server 无法解析
2463
- []),
2464
- // todo: 还有用吗?需要获取客户端 IP 吗
2532
+ await this.rpc('connect', {});
2533
+ if (this.autologin)
2534
+ await this.call('login', [this.username, this.password]);
2535
+ console.log('Subscribed to streaming table, colnames:',
2536
+ // string[3](['time', 'stock', 'price'])
2537
+ this.streaming.colnames = (await this.call('publishTable', [
2465
2538
  'localhost',
2466
- // todo: 端口还有用吗?
2467
2539
  new DdbInt(0),
2468
2540
  this.streaming.table,
2469
2541
  (this.streaming.action ||= `api_js_${new Date().getTime()}`),
2470
2542
  // new DdbInt(-1), // offset
2471
2543
  // filter
2472
2544
  // allow exists
2473
- ]);
2474
- return new Promise((resolve, reject) => {
2475
- let first = true;
2476
- let second = true;
2477
- // 先准备好收到 websocket message 的 callback
2478
- this.on_message = ({ data: buffer }) => {
2479
- if (first) {
2480
- first = false;
2481
- try {
2482
- const dv = new DataView(buffer);
2483
- const status = dv.getUint8(1);
2484
- this.le = Boolean(dv.getUint8(2));
2485
- const nobjs = dv.getUint32(3, this.le);
2486
- // 解析返回数据,从 server 实现看其实只有一个 DdbObj
2487
- let offset = 7;
2488
- let objs = new Array(nobjs);
2489
- const buf = new Uint8Array(buffer);
2490
- for (let i = 0; i < nobjs; i++) {
2491
- const obj = DdbObj.parse(buf.subarray(offset), this.le);
2492
- offset += obj.length;
2493
- objs[i] = obj;
2494
- console.log(`订阅流表的响应数据: objs[${i}]: ${obj.toString()}`);
2495
- }
2496
- this.streaming.colnames = objs[0].value;
2497
- if (status === StreamingStatusCode.ok)
2498
- console.log('Subscribed to streaming table:', { status: StreamingStatusCode[status], result: objs[0] });
2499
- else
2500
- reject(new Error(`Failed to subscribe to streaming table: { status: ${StreamingStatusCode[status]}, error: '${objs[0]?.value}' }`));
2501
- }
2502
- catch (error) {
2503
- reject(error);
2504
- }
2505
- }
2506
- else {
2507
- try {
2508
- const dv = new DataView(buffer);
2509
- const buf = new Uint8Array(buffer);
2510
- let { streaming } = this;
2511
- streaming.time = dv.getBigInt64(1, this.le);
2512
- streaming.id = dv.getBigInt64(9, this.le);
2513
- const i_topic_end = buf.indexOf(0, 17);
2514
- streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
2515
- if (second) {
2516
- second = false;
2517
- // 是 table
2518
- streaming.schema = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
2519
- console.log('Received streaming table schema:', streaming.schema);
2520
- streaming.window = {
2521
- offset: 0,
2522
- rows: 0,
2523
- segments: [],
2524
- };
2525
- assert(streaming.schema.rows === 0, 'schema.rows === 0');
2526
- resolve(streaming.schema);
2527
- }
2528
- else {
2529
- // 是 column 片段组成的 any vector
2530
- const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
2531
- let { window: win } = streaming;
2532
- win.rows += data.value[0].rows;
2533
- win.segments.push(data);
2534
- if (win.rows >= winsize * 2 && win.segments.length >= 2) {
2535
- let winsize_ = 0;
2536
- let i = win.segments.length - 1;
2537
- // 往前移动至首个累计 winsize_ 超过 winsize 的位置
2538
- for (; winsize_ < winsize; i--)
2539
- winsize_ += win.segments[i].rows;
2540
- win.segments = win.segments.slice(i);
2541
- win.offset += win.rows - winsize_;
2542
- win.rows = winsize_;
2543
- console.log('exceeds winsize, shrink window.segments', win);
2544
- }
2545
- streaming.handler(streaming);
2546
- }
2547
- }
2548
- catch (error) {
2549
- reject(error);
2550
- }
2545
+ ])).value);
2546
+ this.streaming.window = {
2547
+ offset: 0,
2548
+ rows: 0,
2549
+ segments: [],
2550
+ };
2551
+ // 先准备好收到 websocket message 的 callback
2552
+ this.on_message = ({ data: buffer }) => {
2553
+ let { streaming } = this;
2554
+ try {
2555
+ const dv = new DataView(buffer);
2556
+ const buf = new Uint8Array(buffer);
2557
+ streaming.time = dv.getBigInt64(1, this.le);
2558
+ streaming.id = dv.getBigInt64(9, this.le);
2559
+ const i_topic_end = buf.indexOf(0, 17);
2560
+ streaming.topic = this.dec.decode(buf.subarray(17, i_topic_end));
2561
+ // 是 column 片段组成的 any vector
2562
+ const data = streaming.data = DdbObj.parse(buf.subarray(i_topic_end + 1), this.le);
2563
+ let { window: win } = streaming;
2564
+ win.rows += streaming.rows = data.value[0].rows;
2565
+ win.segments.push(data);
2566
+ if (win.rows >= winsize * 2 && win.segments.length >= 2) {
2567
+ let winsize_ = 0;
2568
+ let i = win.segments.length - 1;
2569
+ // 往前移动至首个累计 winsize_ 超过 winsize 的位置
2570
+ for (; winsize_ < winsize; i--)
2571
+ winsize_ += win.segments[i].value[0].rows;
2572
+ win.segments = win.segments.slice(i);
2573
+ win.offset += win.rows - winsize_;
2574
+ win.rows = winsize_;
2551
2575
  }
2552
- };
2553
- // 发送订阅 websocket message
2554
- this.websocket.send(concat([
2555
- Uint8Array.of(1, // from = SubscriberFromType::FROM_API
2556
- 1, // type = SubscriberRPCType::RPC_START
2557
- Number(DDB.le_client)),
2558
- Uint32Array.of(args.length),
2559
- ...args.map(obj => obj.pack())
2560
- ]));
2561
- });
2576
+ }
2577
+ catch (error) {
2578
+ // 将 error 交给 handler 处理
2579
+ streaming.error = error;
2580
+ }
2581
+ this.streaming.handler(streaming);
2582
+ };
2583
+ return this.streaming;
2562
2584
  }
2563
2585
  }
2564
2586
  export let ddb = window.ddb = new DDB();