]>
Commit | Line | Data |
---|---|---|
1d09f67e TL |
1 | // Licensed to the Apache Software Foundation (ASF) under one |
2 | // or more contributor license agreements. See the NOTICE file | |
3 | // distributed with this work for additional information | |
4 | // regarding copyright ownership. The ASF licenses this file | |
5 | // to you under the Apache License, Version 2.0 (the | |
6 | // "License"); you may not use this file except in compliance | |
7 | // with the License. You may obtain a copy of the License at | |
8 | // | |
9 | // http://www.apache.org/licenses/LICENSE-2.0 | |
10 | // | |
11 | // Unless required by applicable law or agreed to in writing, | |
12 | // software distributed under the License is distributed on an | |
13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
14 | // KIND, either express or implied. See the License for the | |
15 | // specific language governing permissions and limitations | |
16 | // under the License. | |
17 | ||
18 | import { Vector } from '../vector'; | |
19 | import { MapRow, StructRow } from '../vector/row'; | |
20 | import { compareArrayLike } from '../util/buffer'; | |
21 | import { BigInt, BigIntAvailable } from './compat'; | |
22 | ||
23 | /** @ignore */ | |
24 | type RangeLike = { length: number; stride?: number }; | |
25 | /** @ignore */ | |
26 | type ClampThen<T extends RangeLike> = (source: T, index: number) => any; | |
27 | /** @ignore */ | |
28 | type ClampRangeThen<T extends RangeLike> = (source: T, offset: number, length: number) => any; | |
29 | ||
30 | export function clampIndex<T extends RangeLike>(source: T, index: number): number; | |
31 | export function clampIndex<T extends RangeLike, N extends ClampThen<T> = ClampThen<T>>(source: T, index: number, then: N): ReturnType<N>; | |
32 | /** @ignore */ | |
33 | export function clampIndex<T extends RangeLike, N extends ClampThen<T> = ClampThen<T>>(source: T, index: number, then?: N) { | |
34 | const length = source.length; | |
35 | const adjust = index > -1 ? index : (length + (index % length)); | |
36 | return then ? then(source, adjust) : adjust; | |
37 | } | |
38 | ||
39 | /** @ignore */ | |
40 | let tmp: number; | |
41 | export function clampRange<T extends RangeLike>(source: T, begin: number | undefined, end: number | undefined): [number, number]; | |
42 | export function clampRange<T extends RangeLike, N extends ClampRangeThen<T> = ClampRangeThen<T>>(source: T, begin: number | undefined, end: number | undefined, then: N): ReturnType<N>; | |
43 | /** @ignore */ | |
44 | export function clampRange<T extends RangeLike, N extends ClampRangeThen<T> = ClampRangeThen<T>>(source: T, begin: number | undefined, end: number | undefined, then?: N) { | |
45 | ||
46 | // Adjust args similar to Array.prototype.slice. Normalize begin/end to | |
47 | // clamp between 0 and length, and wrap around on negative indices, e.g. | |
48 | // slice(-1, 5) or slice(5, -1) | |
49 | const { length: len = 0 } = source; | |
50 | let lhs = typeof begin !== 'number' ? 0 : begin; | |
51 | let rhs = typeof end !== 'number' ? len : end; | |
52 | // wrap around on negative start/end positions | |
53 | (lhs < 0) && (lhs = ((lhs % len) + len) % len); | |
54 | (rhs < 0) && (rhs = ((rhs % len) + len) % len); | |
55 | // ensure lhs <= rhs | |
56 | (rhs < lhs) && (tmp = lhs, lhs = rhs, rhs = tmp); | |
57 | // ensure rhs <= length | |
58 | (rhs > len) && (rhs = len); | |
59 | ||
60 | return then ? then(source, lhs, rhs) : [lhs, rhs]; | |
61 | } | |
62 | ||
63 | const big0 = BigIntAvailable ? BigInt(0) : 0; | |
64 | const isNaNFast = (value: any) => value !== value; | |
65 | ||
66 | /** @ignore */ | |
67 | export function createElementComparator(search: any) { | |
68 | const typeofSearch = typeof search; | |
69 | // Compare primitives | |
70 | if (typeofSearch !== 'object' || search === null) { | |
71 | // Compare NaN | |
72 | if (isNaNFast(search)) { | |
73 | return isNaNFast; | |
74 | } | |
75 | return typeofSearch !== 'bigint' | |
76 | ? (value: any) => value === search | |
77 | : (value: any) => (big0 + value) === search; | |
78 | } | |
79 | // Compare Dates | |
80 | if (search instanceof Date) { | |
81 | const valueOfSearch = search.valueOf(); | |
82 | return (value: any) => value instanceof Date ? (value.valueOf() === valueOfSearch) : false; | |
83 | } | |
84 | // Compare TypedArrays | |
85 | if (ArrayBuffer.isView(search)) { | |
86 | return (value: any) => value ? compareArrayLike(search, value) : false; | |
87 | } | |
88 | // Compare Maps and Rows | |
89 | if (search instanceof Map) { return creatMapComparator(search); } | |
90 | // Compare Array-likes | |
91 | if (Array.isArray(search)) { return createArrayLikeComparator(search); } | |
92 | // Compare Vectors | |
93 | if (search instanceof Vector) { return createVectorComparator(search); } | |
94 | // Compare non-empty Objects | |
95 | return createObjectComparator(search); | |
96 | } | |
97 | ||
98 | /** @ignore */ | |
99 | function createArrayLikeComparator(lhs: ArrayLike<any>) { | |
100 | const comparators = [] as ((x: any) => boolean)[]; | |
101 | for (let i = -1, n = lhs.length; ++i < n;) { | |
102 | comparators[i] = createElementComparator(lhs[i]); | |
103 | } | |
104 | return createSubElementsComparator(comparators); | |
105 | } | |
106 | ||
107 | /** @ignore */ | |
108 | function creatMapComparator(lhs: Map<any, any>) { | |
109 | let i = -1; | |
110 | const comparators = [] as ((x: any) => boolean)[]; | |
111 | lhs.forEach((v) => comparators[++i] = createElementComparator(v)); | |
112 | return createSubElementsComparator(comparators); | |
113 | } | |
114 | ||
115 | /** @ignore */ | |
116 | function createVectorComparator(lhs: Vector<any>) { | |
117 | const comparators = [] as ((x: any) => boolean)[]; | |
118 | for (let i = -1, n = lhs.length; ++i < n;) { | |
119 | comparators[i] = createElementComparator(lhs.get(i)); | |
120 | } | |
121 | return createSubElementsComparator(comparators); | |
122 | } | |
123 | ||
124 | /** @ignore */ | |
125 | function createObjectComparator(lhs: any) { | |
126 | const keys = Object.keys(lhs); | |
127 | // Only compare non-empty Objects | |
128 | if (keys.length === 0) { return () => false; } | |
129 | const comparators = [] as ((x: any) => boolean)[]; | |
130 | for (let i = -1, n = keys.length; ++i < n;) { | |
131 | comparators[i] = createElementComparator(lhs[keys[i]]); | |
132 | } | |
133 | return createSubElementsComparator(comparators, keys); | |
134 | } | |
135 | ||
136 | function createSubElementsComparator(comparators: ((x: any) => boolean)[], keys?: Iterable<string>) { | |
137 | return (rhs: any) => { | |
138 | if (!rhs || typeof rhs !== 'object') { | |
139 | return false; | |
140 | } | |
141 | switch (rhs.constructor) { | |
142 | case Array: return compareArray(comparators, rhs); | |
143 | case Map: | |
144 | case MapRow: | |
145 | case StructRow: | |
146 | return compareObject(comparators, rhs, rhs.keys()); | |
147 | case Object: | |
148 | case undefined: // support `Object.create(null)` objects | |
149 | return compareObject(comparators, rhs, keys || Object.keys(rhs)); | |
150 | } | |
151 | return rhs instanceof Vector ? compareVector(comparators, rhs) : false; | |
152 | }; | |
153 | } | |
154 | ||
155 | function compareArray(comparators: ((x: any) => boolean)[], arr: any[]) { | |
156 | const n = comparators.length; | |
157 | if (arr.length !== n) { return false; } | |
158 | for (let i = -1; ++i < n;) { | |
159 | if (!(comparators[i](arr[i]))) { return false; } | |
160 | } | |
161 | return true; | |
162 | } | |
163 | ||
164 | function compareVector(comparators: ((x: any) => boolean)[], vec: Vector) { | |
165 | const n = comparators.length; | |
166 | if (vec.length !== n) { return false; } | |
167 | for (let i = -1; ++i < n;) { | |
168 | if (!(comparators[i](vec.get(i)))) { return false; } | |
169 | } | |
170 | return true; | |
171 | } | |
172 | ||
173 | function compareObject(comparators: ((x: any) => boolean)[], obj: Map<any, any>, keys: Iterable<string>) { | |
174 | ||
175 | const lKeyItr = keys[Symbol.iterator](); | |
176 | const rKeyItr = obj instanceof Map ? obj.keys() : Object.keys(obj)[Symbol.iterator](); | |
177 | const rValItr = obj instanceof Map ? obj.values() : Object.values(obj)[Symbol.iterator](); | |
178 | ||
179 | let i = 0; | |
180 | const n = comparators.length; | |
181 | let rVal = rValItr.next(); | |
182 | let lKey = lKeyItr.next(); | |
183 | let rKey = rKeyItr.next(); | |
184 | ||
185 | for (; i < n && !lKey.done && !rKey.done && !rVal.done; | |
186 | ++i, lKey = lKeyItr.next(), rKey = rKeyItr.next(), rVal = rValItr.next()) { | |
187 | if (lKey.value !== rKey.value || !comparators[i](rVal.value)) { | |
188 | break; | |
189 | } | |
190 | } | |
191 | if (i === n && lKey.done && rKey.done && rVal.done) { | |
192 | return true; | |
193 | } | |
194 | lKeyItr.return && lKeyItr.return(); | |
195 | rKeyItr.return && rKeyItr.return(); | |
196 | rValItr.return && rValItr.return(); | |
197 | return false; | |
198 | } |