You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2020/09/24 05:13:13 UTC
[incubator-echarts] 01/01: refact: remove chunks in List.
This is an automated email from the ASF dual-hosted git repository.
shenyi pushed a commit to branch list-remove-chunk
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit 41988f298f6931891cf8f40d9d718f1dda87caef
Author: pissang <bm...@gmail.com>
AuthorDate: Thu Sep 24 13:12:50 2020 +0800
refact: remove chunks in List.
---
src/data/List.ts | 448 ++++++++++++++++------------------------
src/data/helper/dataProvider.ts | 46 ++++-
2 files changed, 225 insertions(+), 269 deletions(-)
diff --git a/src/data/List.ts b/src/data/List.ts
index 4298f9e..d13ae70 100644
--- a/src/data/List.ts
+++ b/src/data/List.ts
@@ -47,6 +47,7 @@ import { isSourceInstance } from './Source';
const mathFloor = Math.floor;
const isObject = zrUtil.isObject;
+const map = zrUtil.map;
const UNDEFINED = 'undefined';
const INDEX_NOT_FOUND = -1;
@@ -88,7 +89,7 @@ type DimValueGetter = (
) => ParsedValue;
type DataValueChunk = ArrayLike<ParsedValue>;
-type DataStorage = {[dimName: string]: DataValueChunk[]};
+type DataStorage = {[dimName: string]: DataValueChunk};
type NameRepeatCount = {[name: string]: number};
@@ -161,8 +162,8 @@ let defaultDimValueGetters: {[sourceFormat: string]: DimValueGetter};
let prepareInvertedIndex: (list: List) => void;
let getRawValueFromStore: (list: List, dimIndex: number, rawIndex: number) => ParsedValue | OrdinalRawValue;
let getIndicesCtor: (list: List) => DataArrayLikeConstructor;
-let prepareChunks: (
- storage: DataStorage, dimInfo: DataDimensionInfo, chunkSize: number, chunkCount: number, end: number
+let prepareStorage: (
+ storage: DataStorage, dimInfo: DataDimensionInfo, end: number, append?: boolean
) => void;
let getRawIndexWithoutIndices: (this: List, idx: number) => number;
let getRawIndexWithIndices: (this: List, idx: number) => number;
@@ -170,7 +171,6 @@ let getId: (list: List, rawIndex: number) => string;
let normalizeDimensions: (dimensions: ItrParamDims) => Array<DimensionLoose>;
let validateDimensions: (list: List, dims: DimensionName[]) => void;
let cloneListForMapAndSample: (original: List, excludeDimensions: DimensionName[]) => List;
-let cloneDimStore: (originalDimStore: DataValueChunk[]) => DataValueChunk[];
let getInitialExtent: () => [number, number];
let setItemDataAndSeriesIndex: (this: Element, child: Element) => void;
let transferProperties: (target: List, source: List) => void;
@@ -213,7 +213,7 @@ class List<
private _count: number = 0;
private _rawCount: number = 0;
private _storage: DataStorage = {};
- private _storageArr: DataValueChunk[][] = [];
+ private _storageArr: DataValueChunk[] = [];
private _nameList: string[] = [];
private _idList: string[] = [];
@@ -236,11 +236,6 @@ class List<
// Graphic elemnents
private _graphicEls: Element[] = [];
- // Max size of each chunk.
- private _chunkSize: number = 1e5;
-
- private _chunkCount: number = 0;
-
private _rawData: DataProvider;
// Raw extent will not be cloned, but only transfered.
@@ -446,7 +441,6 @@ class List<
// Clear
this._storage = {};
- this._storageArr = [];
this._indices = null;
this._nameList = nameList || [];
@@ -516,48 +510,40 @@ class List<
* Each item is exaclty cooresponding to a dimension.
*/
appendValues(values: any[][], names?: string[]): void {
- const chunkSize = this._chunkSize;
const storage = this._storage;
- const storageArr = this._storageArr;
const dimensions = this.dimensions;
const dimLen = dimensions.length;
const rawExtent = this._rawExtent;
const start = this.count();
const end = start + Math.max(values.length, names ? names.length : 0);
- const originalChunkCount = this._chunkCount;
for (let i = 0; i < dimLen; i++) {
const dim = dimensions[i];
if (!rawExtent[dim]) {
rawExtent[dim] = getInitialExtent();
}
- if (!storage[dim]) {
- const store: DataValueChunk[] = [];
- storage[dim] = store;
- storageArr.push(store);
- }
- prepareChunks(storage, this._dimensionInfos[dim], chunkSize, originalChunkCount, end);
- this._chunkCount = storage[dim].length;
+ prepareStorage(storage, this._dimensionInfos[dim], end, true);
}
- const rawExtentArr = zrUtil.map(dimensions, (dim) => {
+ const rawExtentArr = map(dimensions, (dim) => {
return rawExtent[dim];
});
- const emptyDataItem = new Array(dimLen);
+ const storageArr = this._storageArr = map(dimensions, (dim) => {
+ return storage[dim];
+ });
+
+ const emptyDataItem: number[] = [];
for (let idx = start; idx < end; idx++) {
const sourceIdx = idx - start;
- const chunkIndex = mathFloor(idx / chunkSize);
- const chunkOffset = idx % chunkSize;
-
// Store the data by dimensions
for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) {
const dim = dimensions[dimIdx];
const val = this._dimValueGetterArrayRows(
values[sourceIdx] || emptyDataItem, dim, sourceIdx, dimIdx
) as ParsedValueNumeric;
- storageArr[dimIdx][chunkIndex][chunkOffset] = val;
+ storageArr[dimIdx][idx] = val;
const dimRawExtent = rawExtentArr[dimIdx];
val < dimRawExtent[0] && (dimRawExtent[0] = val);
@@ -582,10 +568,8 @@ class List<
return;
}
- const chunkSize = this._chunkSize;
const rawData = this._rawData;
const storage = this._storage;
- const storageArr = this._storageArr;
const dimensions = this.dimensions;
const dimLen = dimensions.length;
const dimensionInfoMap = this._dimensionInfos;
@@ -595,8 +579,6 @@ class List<
const nameRepeatCount: NameRepeatCount = this._nameRepeatCount = {};
let nameDimIdx;
- const originalChunkCount = this._chunkCount;
-
for (let i = 0; i < dimLen; i++) {
const dim = dimensions[i];
if (!rawExtent[dim]) {
@@ -611,92 +593,99 @@ class List<
this._idDimIdx = i;
}
- if (!storage[dim]) {
- const store: DataValueChunk[] = [];
- storage[dim] = store;
- storageArr.push(store);
+ if (!rawData.getStorage) {
+ prepareStorage(storage, dimInfo, end);
}
-
- prepareChunks(storage, dimInfo, chunkSize, originalChunkCount, end);
-
- this._chunkCount = storage[dim].length;
}
- const rawExtentArr = zrUtil.map(dimensions, (dim) => {
- return rawExtent[dim];
+ const storageArr = this._storageArr = map(dimensions, (dim) => {
+ return storage[dim];
});
- let dataItem = [] as OptionDataItem;
- for (let idx = start; idx < end; idx++) {
- // NOTICE: Try not to write things into dataItem
- dataItem = rawData.getItem(idx, dataItem);
- // Each data item is value
- // [1, 2]
- // 2
- // Bar chart, line chart which uses category axis
- // only gives the 'y' value. 'x' value is the indices of category
- // Use a tempValue to normalize the value to be a (x, y) value
- const chunkIndex = mathFloor(idx / chunkSize);
- const chunkOffset = idx % chunkSize;
-
- // Store the data by dimensions
- for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) {
- const dim = dimensions[dimIdx];
- const dimStorage = storageArr[dimIdx][chunkIndex];
- // PENDING NULL is empty or zero
- const val = this._dimValueGetter(dataItem, dim, idx, dimIdx) as ParsedValueNumeric;
- dimStorage[chunkOffset] = val;
+ const rawExtentArr = map(dimensions, (dim) => {
+ return rawExtent[dim];
+ });
- const dimRawExtent = rawExtentArr[dimIdx];
- val < dimRawExtent[0] && (dimRawExtent[0] = val);
- val > dimRawExtent[1] && (dimRawExtent[1] = val);
+ if (rawData.getStorage) {
+ const ret = rawData.getStorage(start, end);
+ const rawStorage = ret.storage;
+ const extent = ret.extent;
+ for (let dimIdx = 0; dimIdx < rawStorage.length; dimIdx++) {
+ storage[dimensions[dimIdx]] = storageArr[dimIdx] = rawStorage[dimIdx];
+ rawExtentArr[dimIdx][0] = Math.min(rawExtentArr[dimIdx][0], extent[dimIdx][0]);
+ rawExtentArr[dimIdx][1] = Math.max(rawExtentArr[dimIdx][1], extent[dimIdx][1]);
}
+ }
+ else {
+ let dataItem = [] as OptionDataItem;
+ for (let idx = start; idx < end; idx++) {
+ // NOTICE: Try not to write things into dataItem
+ dataItem = rawData.getItem(idx, dataItem);
+ // Each data item is value
+ // [1, 2]
+ // 2
+ // Bar chart, line chart which uses category axis
+ // only gives the 'y' value. 'x' value is the indices of category
+ // Use a tempValue to normalize the value to be a (x, y) value
+
+ // Store the data by dimensions
+ for (let dimIdx = 0; dimIdx < dimLen; dimIdx++) {
+ const dim = dimensions[dimIdx];
+ const dimStorage = storageArr[dimIdx];
+ // PENDING NULL is empty or zero
+ const val = this._dimValueGetter(dataItem, dim, idx, dimIdx) as ParsedValueNumeric;
+ dimStorage[idx] = val;
+
+ const dimRawExtent = rawExtentArr[dimIdx];
+ val < dimRawExtent[0] && (dimRawExtent[0] = val);
+ val > dimRawExtent[1] && (dimRawExtent[1] = val);
+ }
- // ??? FIXME not check by pure but sourceFormat?
- // TODO refactor these logic.
- if (!rawData.pure) {
- let name: string = nameList[idx];
-
- if (dataItem && name == null) {
- // If dataItem is {name: ...}, it has highest priority.
- // That is appropriate for many common cases.
- if ((dataItem as any).name != null) {
- // There is no other place to persistent dataItem.name,
- // so save it to nameList.
- nameList[idx] = name = convertOptionIdName((dataItem as any).name, null);
- }
- else if (nameDimIdx != null) {
- const nameDim = dimensions[nameDimIdx];
- const nameDimChunk = storage[nameDim][chunkIndex];
- if (nameDimChunk) {
- const ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
- name = convertOptionIdName(
- (ordinalMeta && ordinalMeta.categories.length)
- ? ordinalMeta.categories[nameDimChunk[chunkOffset] as number]
- : nameDimChunk[chunkOffset],
- null
- );
+ // ??? FIXME not check by pure but sourceFormat?
+ // TODO refactor these logic.
+ if (!rawData.pure) {
+ let name: string = nameList[idx];
+
+ if (dataItem && name == null) {
+ // If dataItem is {name: ...}, it has highest priority.
+ // That is appropriate for many common cases.
+ if ((dataItem as any).name != null) {
+ // There is no other place to persistent dataItem.name,
+ // so save it to nameList.
+ nameList[idx] = name = convertOptionIdName((dataItem as any).name, null);
+ }
+ else if (nameDimIdx != null) {
+ const nameDim = dimensions[nameDimIdx];
+ const nameDimChunk = storage[nameDim];
+ if (nameDimChunk) {
+ const ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
+ name = convertOptionIdName(
+ (ordinalMeta && ordinalMeta.categories.length)
+ ? ordinalMeta.categories[nameDimChunk[idx] as number]
+ : nameDimChunk[idx],
+ null
+ );
+ }
}
}
- }
- // Try using the id in option
- // id or name is used on dynamical data, mapping old and new items.
- let id: string = dataItem == null ? null : convertOptionIdName((dataItem as any).id, null);
+ // Try using the id in option
+ // id or name is used on dynamical data, mapping old and new items.
+ let id: string = dataItem == null ? null : convertOptionIdName((dataItem as any).id, null);
- if (id == null && name != null) {
- // Use name as id and add counter to avoid same name
- nameRepeatCount[name] = nameRepeatCount[name] || 0;
- id = name;
- if (nameRepeatCount[name] > 0) {
- id += '__ec__' + nameRepeatCount[name];
+ if (id == null && name != null) {
+ // Use name as id and add counter to avoid same name
+ nameRepeatCount[name] = nameRepeatCount[name] || 0;
+ id = name;
+ if (nameRepeatCount[name] > 0) {
+ id += '__ec__' + nameRepeatCount[name];
+ }
+ nameRepeatCount[name]++;
}
- nameRepeatCount[name]++;
+ id != null && (idList[idx] = id);
}
- id != null && (idList[idx] = id);
}
}
-
if (!rawData.persistent && rawData.clean) {
// Clean unused data if data source is typed array.
rawData.clean();
@@ -752,18 +741,8 @@ class List<
if (!(idx >= 0 && idx < this._count)) {
return NaN;
}
-
const dimStore = this._storageArr[dimIdx];
- const chunkSize = this._chunkSize;
- if (!dimStore) {
- return NaN;
- }
- idx = this.getRawIndex(idx);
-
- const chunkIndex = mathFloor(idx / chunkSize);
- const chunkOffset = idx % chunkSize;
-
- return dimStore[chunkIndex][chunkOffset];
+ return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
}
/**
@@ -775,17 +754,7 @@ class List<
return NaN;
}
const dimStore = this._storage[dim];
- const chunkSize = this._chunkSize;
- if (!dimStore) {
- return NaN;
- }
-
- idx = this.getRawIndex(idx);
-
- const chunkIndex = mathFloor(idx / chunkSize);
- const chunkOffset = idx % chunkSize;
-
- return dimStore[chunkIndex][chunkOffset];
+ return dimStore ? dimStore[this.getRawIndex(idx)] : NaN;
}
/**
@@ -796,27 +765,7 @@ class List<
return NaN;
}
const dimStore = this._storage[dim];
- const chunkSize = this._chunkSize;
- if (!dimStore) {
- // TODO Warn ?
- return NaN;
- }
-
- const chunkIndex = mathFloor(rawIdx / chunkSize);
- const chunkOffset = rawIdx % chunkSize;
- const chunkStore = dimStore[chunkIndex];
- return chunkStore[chunkOffset];
- }
-
- /**
- * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange).
- * Hack a much simpler _getFast
- */
- private _getFast(dimIdx: number, rawIdx: number): ParsedValue {
- const chunkSize = this._chunkSize;
- const chunkIndex = mathFloor(rawIdx / chunkSize);
- const chunkOffset = rawIdx % chunkSize;
- return this._storageArr[dimIdx][chunkIndex][chunkOffset];
+ return dimStore ? dimStore[rawIdx] : NaN;
}
/**
@@ -866,7 +815,6 @@ class List<
dim = this.getDimension(dim);
const dimData = this._storage[dim];
const initialExtent = getInitialExtent();
- const chunkSize = this._chunkSize;
// stack = !!((stack || false) && this.getCalculationInfo(dim));
@@ -899,9 +847,7 @@ class List<
for (let i = 0; i < currEnd; i++) {
const rawIdx = this.getRawIndex(i);
- const chunkIndex = mathFloor(rawIdx / chunkSize);
- const chunkOffset = rawIdx % chunkSize;
- const value = dimData[chunkIndex][chunkOffset] as ParsedValueNumeric;
+ const value = dimData[rawIdx] as ParsedValueNumeric;
value < min && (min = value);
value > max && (max = value);
}
@@ -1120,7 +1066,6 @@ class List<
const storage = this._storage;
const dimData = storage[dim];
const nearestIndices: number[] = [];
- const chunkSize = this._chunkSize;
if (!dimData) {
return nearestIndices;
@@ -1138,9 +1083,7 @@ class List<
// Check the test case of `test/ut/spec/data/List.js`.
for (let i = 0, len = this.count(); i < len; i++) {
const dataIndex = this.getRawIndex(i);
- const chunkIndex = mathFloor(dataIndex / chunkSize);
- const chunkOffset = dataIndex % chunkSize;
- const diff = value - (dimData[chunkIndex][chunkOffset] as number);
+ const diff = value - (dimData[dataIndex] as number);
const dist = Math.abs(diff);
if (dist <= maxDistance) {
// When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
@@ -1248,36 +1191,37 @@ class List<
// ctxCompat just for compat echarts3
const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
- const dimNames = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
+ const dimNames = map(normalizeDimensions(dims), this.getDimension, this);
if (__DEV__) {
validateDimensions(this, dimNames);
}
const dimSize = dimNames.length;
- const dimIndices = zrUtil.map(dimNames, (dimName) => {
+ const dimIndices = map(dimNames, (dimName) => {
return this._dimensionInfos[dimName].index;
});
+ const storageArr = this._storageArr;
- for (let i = 0; i < this.count(); i++) {
+ for (let i = 0, len = this.count(); i < len; i++) {
// Simple optimization
switch (dimSize) {
case 0:
(cb as EachCb0<Ctx>).call(fCtx, i);
break;
case 1:
- (cb as EachCb1<Ctx>).call(fCtx, this._getFast(dimIndices[0], i), i);
+ (cb as EachCb1<Ctx>).call(fCtx, storageArr[dimIndices[0]][i], i);
break;
case 2:
(cb as EachCb2<Ctx>).call(
- fCtx, this._getFast(dimIndices[0], i), this._getFast(dimIndices[1], i), i
+ fCtx, storageArr[dimIndices[0]][i], storageArr[dimIndices[1]][i], i
);
break;
default:
let k = 0;
const value = [];
for (; k < dimSize; k++) {
- value[k] = this._getFast(dimIndices[k], i);
+ value[k] = storageArr[dimIndices[k]][i];
}
// Index
value[k] = i;
@@ -1316,7 +1260,7 @@ class List<
// ctxCompat just for compat echarts3
const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
- const dimNames = zrUtil.map(
+ const dimNames = map(
normalizeDimensions(dims), this.getDimension, this
);
@@ -1332,10 +1276,11 @@ class List<
const dimSize = dimNames.length;
let offset = 0;
- const dimIndices = zrUtil.map(dimNames, (dimName) => {
+ const dimIndices = map(dimNames, (dimName) => {
return this._dimensionInfos[dimName].index;
});
const dim0 = dimIndices[0];
+ const storageArr = this._storageArr;
for (let i = 0; i < count; i++) {
let keep;
@@ -1345,13 +1290,13 @@ class List<
keep = (cb as FilterCb0<Ctx>).call(fCtx, i);
}
else if (dimSize === 1) {
- const val = this._getFast(dim0, rawIdx);
+ const val = storageArr[dim0][rawIdx];
keep = (cb as FilterCb1<Ctx>).call(fCtx, val, i);
}
else {
let k = 0;
for (; k < dimSize; k++) {
- value[k] = this._getFast(dimIndices[k], rawIdx);
+ value[k] = storageArr[dimIndices[k]][rawIdx];
}
value[k] = i;
keep = (cb as FilterCb<Ctx>).apply(fCtx, value);
@@ -1381,7 +1326,9 @@ class List<
selectRange(range: {[dimName: string]: [number, number]}): List {
'use strict';
- if (!this._count) {
+ const len = this._count;
+
+ if (!len) {
return;
}
@@ -1407,63 +1354,55 @@ class List<
let offset = 0;
const dim0 = dimensions[0];
- const dimIndices = zrUtil.map(dimensions, (dimName) => {
+ const dimIndices = map(dimensions, (dimName) => {
return this._dimensionInfos[dimName].index;
});
const min = range[dim0][0];
const max = range[dim0][1];
+ const storageArr = this._storageArr;
let quickFinished = false;
if (!this._indices) {
// Extreme optimization for common case. About 2x faster in chrome.
let idx = 0;
if (dimSize === 1) {
- const dimStorage = this._storage[dim0];
- for (let k = 0; k < this._chunkCount; k++) {
- const chunkStorage = dimStorage[k];
- const len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
- for (let i = 0; i < len; i++) {
- const val = chunkStorage[i];
- // NaN will not be filtered. Consider the case, in line chart, empty
- // value indicates the line should be broken. But for the case like
- // scatter plot, a data item with empty value will not be rendered,
- // but the axis extent may be effected if some other dim of the data
- // item has value. Fortunately it is not a significant negative effect.
- if (
- (val >= min && val <= max) || isNaN(val as any)
- ) {
- newIndices[offset++] = idx;
- }
- idx++;
+ const dimStorage = storageArr[dimIndices[0]];
+ for (let i = 0; i < len; i++) {
+ const val = dimStorage[i];
+ // NaN will not be filtered. Consider the case, in line chart, empty
+ // value indicates the line should be broken. But for the case like
+ // scatter plot, a data item with empty value will not be rendered,
+ // but the axis extent may be effected if some other dim of the data
+ // item has value. Fortunately it is not a significant negative effect.
+ if (
+ (val >= min && val <= max) || isNaN(val as any)
+ ) {
+ newIndices[offset++] = idx;
}
+ idx++;
}
quickFinished = true;
}
else if (dimSize === 2) {
- const dimStorage = this._storage[dim0];
- const dimStorage2 = this._storage[dimensions[1]];
+ const dimStorage = storageArr[dimIndices[0]];
+ const dimStorage2 = storageArr[dimIndices[1]];
const min2 = range[dimensions[1]][0];
const max2 = range[dimensions[1]][1];
- for (let k = 0; k < this._chunkCount; k++) {
- const chunkStorage = dimStorage[k];
- const chunkStorage2 = dimStorage2[k];
- const len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
- for (let i = 0; i < len; i++) {
- const val = chunkStorage[i];
- const val2 = chunkStorage2[i];
- // Do not filter NaN, see comment above.
- if ((
- (val >= min && val <= max) || isNaN(val as any)
- )
- && (
- (val2 >= min2 && val2 <= max2) || isNaN(val2 as any)
- )
- ) {
- newIndices[offset++] = idx;
- }
- idx++;
+ for (let i = 0; i < len; i++) {
+ const val = dimStorage[i];
+ const val2 = dimStorage2[i];
+ // Do not filter NaN, see comment above.
+ if ((
+ (val >= min && val <= max) || isNaN(val as any)
+ )
+ && (
+ (val2 >= min2 && val2 <= max2) || isNaN(val2 as any)
+ )
+ ) {
+ newIndices[offset++] = idx;
}
+ idx++;
}
quickFinished = true;
}
@@ -1472,7 +1411,7 @@ class List<
if (dimSize === 1) {
for (let i = 0; i < originalCount; i++) {
const rawIndex = this.getRawIndex(i);
- const val = this._getFast(dimIndices[0], rawIndex);
+ const val = storageArr[dimIndices[0]][rawIndex];
// Do not filter NaN, see comment above.
if (
(val >= min && val <= max) || isNaN(val as any)
@@ -1487,7 +1426,7 @@ class List<
const rawIndex = this.getRawIndex(i);
for (let k = 0; k < dimSize; k++) {
const dimk = dimensions[k];
- const val = this._getFast(dimIndices[k], rawIndex);
+ const val = storageArr[dimIndices[k]][rawIndex];
// Do not filter NaN, see comment above.
if (val < range[dimk][0] || val > range[dimk][1]) {
keep = false;
@@ -1565,7 +1504,7 @@ class List<
// ctxCompat just for compat echarts3
const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
- const dimNames = zrUtil.map(
+ const dimNames = map(
normalizeDimensions(dims), this.getDimension, this
);
@@ -1574,16 +1513,14 @@ class List<
}
const list = cloneListForMapAndSample(this, dimNames);
+ const storage = list._storage;
// Following properties are all immutable.
// So we can reference to the same value
list._indices = this._indices;
list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
- const storage = list._storage;
-
const tmpRetValue = [];
- const chunkSize = this._chunkSize;
const dimSize = dimNames.length;
const dataCount = this.count();
const values = [];
@@ -1604,8 +1541,6 @@ class List<
}
const rawIndex = this.getRawIndex(dataIndex);
- const chunkIndex = mathFloor(rawIndex / chunkSize);
- const chunkOffset = rawIndex % chunkSize;
for (let i = 0; i < retValue.length; i++) {
const dim = dimNames[i];
@@ -1614,7 +1549,7 @@ class List<
const dimStore = storage[dim];
if (dimStore) {
- dimStore[chunkIndex][chunkOffset] = val;
+ dimStore[rawIndex] = val;
}
if (val < rawExtentOnDim[0]) {
@@ -1648,7 +1583,6 @@ class List<
const dimStore = targetStorage[dimension];
const len = this.count();
- const chunkSize = this._chunkSize;
const rawExtentOnDim = list._rawExtent[dimension];
const newIndices = new (getIndicesCtor(this))(len);
@@ -1662,18 +1596,14 @@ class List<
}
for (let k = 0; k < frameSize; k++) {
const dataIdx = this.getRawIndex(i + k);
- const originalChunkIndex = mathFloor(dataIdx / chunkSize);
- const originalChunkOffset = dataIdx % chunkSize;
- frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
+ frameValues[k] = dimStore[dataIdx];
}
const value = sampleValue(frameValues);
const sampleFrameIdx = this.getRawIndex(
Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
);
- const sampleChunkIndex = mathFloor(sampleFrameIdx / chunkSize);
- const sampleChunkOffset = sampleFrameIdx % chunkSize;
// Only write value on the filtered data
- dimStore[sampleChunkIndex][sampleChunkOffset] = value;
+ dimStore[sampleFrameIdx] = value;
if (value < rawExtentOnDim[0]) {
rawExtentOnDim[0] = value;
@@ -1704,12 +1634,11 @@ class List<
valueDimension: DimensionName,
targetCount: number
) {
- const list = cloneListForMapAndSample(this, [baseDimension, valueDimension]);
+ const list = cloneListForMapAndSample(this, []);
const targetStorage = list._storage;
const baseDimStore = targetStorage[baseDimension];
const valueDimStore = targetStorage[valueDimension];
const len = this.count();
- const chunkSize = this._chunkSize;
const newIndices = new (getIndicesCtor(this))(len);
let sampledIndex = 0;
@@ -1732,15 +1661,13 @@ class List<
for (let idx = avgRangeStart; idx < avgRangeEnd; idx++) {
const rawIndex = this.getRawIndex(idx);
- const chunkIndex = mathFloor(rawIndex / chunkSize);
- const chunkOffset = rawIndex % chunkSize;
- const x = baseDimStore[chunkIndex][chunkOffset] as number;
- const y = valueDimStore[chunkIndex][chunkOffset] as number;
+ const x = baseDimStore[rawIndex] as number;
+ const y = valueDimStore[rawIndex] as number;
if (isNaN(x) || isNaN(y)) {
continue;
}
- avgX += baseDimStore[chunkIndex][chunkOffset] as number;
- avgY += valueDimStore[chunkIndex][chunkOffset] as number;
+ avgX += x as number;
+ avgY += y as number;
}
avgX /= avgRangeLength;
avgY /= avgRangeLength;
@@ -1748,21 +1675,17 @@ class List<
const rangeOffs = mathFloor((i) * frameSize) + 1;
const rangeTo = mathFloor((i + 1) * frameSize) + 1;
- const chunkIndex = mathFloor(currentRawIndex / chunkSize);
- const chunkOffset = currentRawIndex % chunkSize;
- const pointAX = baseDimStore[chunkIndex][chunkOffset] as number;
- const pointAY = valueDimStore[chunkIndex][chunkOffset] as number;
+ const pointAX = baseDimStore[currentRawIndex] as number;
+ const pointAY = valueDimStore[currentRawIndex] as number;
- maxArea = area = -1;
+ maxArea = -1;
// Find a point from current frame that construct a triangel with largest area with previous selected point
// And the average of next frame.
for (let idx = rangeOffs; idx < rangeTo; idx++) {
const rawIndex = this.getRawIndex(idx);
- const chunkIndex = mathFloor(rawIndex / chunkSize);
- const chunkOffset = rawIndex % chunkSize;
- const x = baseDimStore[chunkIndex][chunkOffset] as number;
- const y = valueDimStore[chunkIndex][chunkOffset] as number;
+ const x = baseDimStore[rawIndex] as number;
+ const y = valueDimStore[rawIndex] as number;
if (isNaN(x) || isNaN(y)) {
continue;
}
@@ -2023,7 +1946,7 @@ class List<
*/
cloneShallow(list?: List<HostModel>): List<HostModel> {
if (!list) {
- const dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
+ const dimensionInfoList = map(this.dimensions, this.getDimensionInfo, this);
list = new List(dimensionInfoList, this.hostModel);
}
@@ -2157,13 +2080,10 @@ class List<
): ParsedValue | OrdinalRawValue {
let val;
if (dimIndex != null) {
- const chunkSize = list._chunkSize;
- const chunkIndex = mathFloor(rawIndex / chunkSize);
- const chunkOffset = rawIndex % chunkSize;
const dim = list.dimensions[dimIndex];
- const chunk = list._storage[dim][chunkIndex];
+ const chunk = list._storage[dim];
if (chunk) {
- val = chunk[chunkOffset];
+ val = chunk[rawIndex];
const ordinalMeta = list._dimensionInfos[dim].ordinalMeta;
if (ordinalMeta && ordinalMeta.categories.length) {
val = ordinalMeta.categories[val as OrdinalNumber];
@@ -2178,30 +2098,30 @@ class List<
return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
};
- prepareChunks = function (
+ prepareStorage = function (
storage: DataStorage,
dimInfo: DataDimensionInfo,
- chunkSize: number,
- chunkCount: number,
- end: number
+ end: number,
+ append?: boolean
): void {
const DataCtor = dataCtors[dimInfo.type];
- const lastChunkIndex = chunkCount - 1;
const dim = dimInfo.name;
- const resizeChunkArray = storage[dim][lastChunkIndex];
- if (resizeChunkArray && resizeChunkArray.length < chunkSize) {
- const newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize));
- // The cost of the copy is probably inconsiderable
- // within the initial chunkSize.
- for (let j = 0; j < resizeChunkArray.length; j++) {
- newStore[j] = resizeChunkArray[j];
+
+ if (append) {
+ const oldStore = storage[dim];
+ const oldLen = oldStore.length;
+ if (oldStore && oldLen < end) {
+ const newStore = new DataCtor(end);
+ // The cost of the copy is probably inconsiderable
+ // within the initial chunkSize.
+ for (let j = 0; j < oldLen; j++) {
+ newStore[j] = oldStore[j];
+ }
+ storage[dim] = newStore;
}
- storage[dim][lastChunkIndex] = newStore;
}
-
- // Create new chunks.
- for (let k = chunkCount * chunkSize; k < end; k += chunkSize) {
- storage[dim].push(new DataCtor(Math.min(end - k, chunkSize)));
+ else {
+ storage[dim] = new DataCtor(end);
}
};
@@ -2256,7 +2176,7 @@ class List<
): List {
const allDimensions = original.dimensions;
const list = new List(
- zrUtil.map(allDimensions, original.getDimensionInfo, original),
+ map(allDimensions, original.getDimensionInfo, original),
original.hostModel
);
// FIXME If needs stackedOn, value may already been stacked
@@ -2264,7 +2184,7 @@ class List<
const storage = list._storage = {} as DataStorage;
const originalStorage = original._storage;
- const storageArr: DataValueChunk[][] = list._storageArr = [];
+ const storageArr: DataValueChunk[] = list._storageArr = [];
// Init storage
for (let i = 0; i < allDimensions.length; i++) {
@@ -2273,7 +2193,7 @@ class List<
// Notice that we do not reset invertedIndicesMap here, becuase
// there is no scenario of mapping or sampling ordinal dimension.
if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
- storage[dim] = cloneDimStore(originalStorage[dim]);
+ storage[dim] = cloneChunk(originalStorage[dim]);
list._rawExtent[dim] = getInitialExtent();
list._extent[dim] = null;
}
@@ -2287,14 +2207,6 @@ class List<
return list;
};
- cloneDimStore = function (originalDimStore: DataValueChunk[]): DataValueChunk[] {
- const newDimStore = new Array(originalDimStore.length);
- for (let j = 0; j < originalDimStore.length; j++) {
- newDimStore[j] = cloneChunk(originalDimStore[j]);
- }
- return newDimStore;
- };
-
function cloneChunk(originalChunk: DataValueChunk): DataValueChunk {
const Ctor = originalChunk.constructor;
// Only shallow clone is enough when Array.
diff --git a/src/data/helper/dataProvider.ts b/src/data/helper/dataProvider.ts
index b3c5290..5f48956 100644
--- a/src/data/helper/dataProvider.ts
+++ b/src/data/helper/dataProvider.ts
@@ -38,7 +38,6 @@ import {
} from '../../util/types';
import List from '../List';
-
export interface DataProvider {
// If data is pure without style configuration
pure: boolean;
@@ -48,6 +47,10 @@ export interface DataProvider {
getSource(): Source;
count(): number;
getItem(idx: number, out?: OptionDataItem): OptionDataItem;
+ getStorage?(start: number, end: number): {
+ storage: ArrayLike<number>[]
+ extent: number[][]
+ }
appendData(newData: ArrayLike<OptionDataItem>): void;
clean(): void;
}
@@ -56,6 +59,12 @@ export interface DataProvider {
let providerMethods: Dictionary<any>;
let mountMethods: (provider: DefaultDataProvider, data: OptionSourceData, source: Source) => void;
+export interface DefaultDataProvider {
+ getStorage?(start: number, end: number): {
+ storage: ArrayLike<number>[],
+ extent: number[][]
+ }
+}
/**
* If normal array used, mutable chunk size is supported.
* If typed array used, chunk size must be fixed.
@@ -144,6 +153,7 @@ export class DefaultDataProvider implements DataProvider {
if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
provider.getItem = getItemForTypedArray;
provider.count = countForTypedArray;
+ provider.getStorage = getStorageForTypedArray;
}
else {
const rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy);
@@ -167,6 +177,40 @@ export class DefaultDataProvider implements DataProvider {
return out;
};
+ const getStorageForTypedArray: DefaultDataProvider['getStorage'] = function (
+ this: DefaultDataProvider, start: number, end: number
+ ) {
+ const data = this._data as ArrayLike<number>;
+ const Ctor = data.constructor;
+ const dimSize = this._dimSize;
+ const offset = this._offset;
+ const storage: ArrayLike<number>[] = [];
+ const extent = [];
+
+ start -= offset;
+ end -= offset;
+
+ for (let dim = 0; dim < dimSize; dim++) {
+ let min = Infinity;
+ let max = -Infinity;
+ const count = end - start;
+ const arr = new (Ctor as any)(count);
+ for (let i = 0; i < count; i++) {
+ const val = data[(offset + start + i) * dimSize + dim];
+ arr[i] = val;
+ val < min && (min = val);
+ val > max && (max = val);
+ }
+ storage.push(arr);
+ extent.push([min, max]);
+ }
+
+ return {
+ storage,
+ extent
+ };
+ };
+
const countForTypedArray: DefaultDataProvider['count'] = function (
this: DefaultDataProvider
) {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org