• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

node-opcua / node-opcua / 14452017930

14 Apr 2025 05:35PM UTC coverage: 90.863% (+0.001%) from 90.862%
14452017930

push

github

erossignon
chore: tidy up imports

10983 of 13932 branches covered (78.83%)

3 of 3 new or added lines in 3 files covered. (100.0%)

245 existing lines in 14 files now uncovered.

29991 of 33007 relevant lines covered (90.86%)

322514.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.6
/packages/node-opcua-numeric-range/source/numeric_range.ts
1
/**
2
 * @module node-opcua-numeric-range
3
 */
4
import { assert } from "node-opcua-assert";
5✔
5

6
import { decodeString, encodeString, UAString } from "node-opcua-basic-types";
5✔
7
import { BinaryStream, OutputBinaryStream } from "node-opcua-binary-stream";
8
import { registerBasicType } from "node-opcua-factory";
5✔
9
import { StatusCode, StatusCodes } from "node-opcua-status-code";
5✔
10

11
// OPC.UA Part 4 7.21 Numerical Range
12
// The syntax for the string contains one of the following two constructs. The first construct is the string
13
// representation of an individual integer. For example, '6' is   valid, but '6.0' and '3.2' are not. The
14
// minimum and maximum values that can be expressed are defined by the use of this parameter and
15
// not by this parameter type definition. The second construct is a range represented by two integers
16
// separated by the colon   (':') character. The first integer shall always have a lower value than the
17
// second. For example, '5:7' is valid, while '7:5' and '5:5' are not. The minimum and maximum values
18
// that can be expressed by these integers are defined by the use of this parameter , and not by this
19
// parameter type definition. No other characters, including white - space characters, are permitted.
20
// Multi- dimensional arrays can be indexed by specifying a range for each dimension separated by a ','.
21
//
22
// For example, a 2x2 block in a 4x4 matrix   could be selected with the range '1:2,0:1'. A single element
23
// in a multi - dimensional array can be selected by specifying a single number instead of a range.
24
// For example, '1,1' specifies selects the [1,1] element in a two dimensional array.
25
// Dimensions are specified in the order that they appear in the  ArrayDimensions Attribute.
26
// All dimensions shall be specified for a  NumericRange  to be valid.
27
//
28
// All indexes start with 0. The maximum value for any index is one less than the length of the
29
// dimension.
30

31
const NUMERIC_RANGE_EMPTY_STRING = "NumericRange:<Empty>";
5✔
32

33
// BNF of NumericRange
34
// The following BNF describes the syntax of the NumericRange parameter type.
35
// <numeric-range>    ::= <dimension> [',' <dimension>]
36
//     <dimension>    ::= <index> [':' <index>]
37
//         <index>    ::= <digit> [<digit>]
38
//         <digit>    ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' |9'
39
//
40
// tslint:disable:object-literal-shorthand
41
// tslint:disable:only-arrow-functions
42
export const schemaNumericRange = {
5✔
43
    name: "NumericRange",
44
    subType: "String",
45

46
    defaultValue: (): NumericRange => {
47
        return new NumericRange();
245,054✔
48
    },
49
    encode: encodeNumericRange,
50

51
    decode: decodeNumericRange,
52

53
    random: (): NumericRange => {
54
        function r() {
55
            return Math.ceil(Math.random() * 100);
2✔
56
        }
57

58
        const start = r();
1✔
59
        const end = start + r();
1✔
60
        return new NumericRange(start, end);
1✔
61
    },
62

63
    coerce: coerceNumericRange
64
};
65

66
registerBasicType(schemaNumericRange);
5✔
67

68
export enum NumericRangeType {
5✔
69
    Empty = 0,
5✔
70
    SingleValue = 1,
5✔
71
    ArrayRange = 2,
5✔
72
    MatrixRange = 3,
5✔
73
    InvalidRange = 4
5✔
74
}
75

76
// new Enum(["Empty", "SingleValue", "ArrayRange", "MatrixRange", "InvalidRange"]);
77

78
const regexNumericRange = /^[0-9:,]*$/;
5✔
79

80
function _valid_range(low: number, high: number): boolean {
81
    return !(low >= high || low < 0 || high < 0);
213✔
82
}
83

84
type NumericalRangeValueType = null | number | string | number[] | number[][];
85

86
export interface NumericalRangeSingleValue {
87
    type: NumericRangeType.SingleValue;
88
    value: number;
89
}
90

91
export interface NumericalRangeArrayRange {
92
    type: NumericRangeType.ArrayRange;
93
    value: number[];
94
}
95

96
export interface NumericalRangeMatrixRange {
97
    type: NumericRangeType.MatrixRange;
98
    value: number[][];
99
}
100

101
export interface NumericalRangeEmpty {
102
    type: NumericRangeType.Empty;
103
    value: null;
104
}
105

106
export interface NumericalRangeInvalid {
107
    type: NumericRangeType.InvalidRange;
108
    value: string;
109
}
110

111
export type NumericalRange0 =
112
    | NumericalRangeSingleValue
113
    | NumericalRangeArrayRange
114
    | NumericalRangeMatrixRange
115
    | NumericalRangeEmpty
116
    | NumericalRangeInvalid;
117

118
export interface NumericalRange1 {
119
    type: NumericRangeType;
120
    value: NumericalRangeValueType;
121
}
122

123
function construct_numeric_range_bit_from_string(str: string): NumericalRange0 {
124
    const values = str.split(":");
296✔
125

126
    if (values.length === 1) {
296✔
127
        return {
107✔
128
            type: NumericRangeType.SingleValue,
129
            value: parseInt(values[0], 10)
130
        };
131
    } else if (values.length === 2) {
189✔
132
        const array = values.map((a) => parseInt(a, 10));
376✔
133

134
        if (!_valid_range(array[0], array[1])) {
188✔
135
            return {
10✔
136
                type: NumericRangeType.InvalidRange,
137
                value: str
138
            };
139
        }
140
        return {
178✔
141
            type: NumericRangeType.ArrayRange,
142
            value: array
143
        };
144
    } else {
145
        return {
1✔
146
            type: NumericRangeType.InvalidRange,
147
            value: str
148
        };
149
    }
150
}
151

152
function _normalize(e: NumericalRange1): number | number[] {
153
    if (e.type === NumericRangeType.SingleValue) {
104✔
154
        const ee = e as NumericalRangeSingleValue;
67✔
155
        return [ee.value, ee.value];
67✔
156
    }
157
    return e.value as number;
37✔
158
}
159

160
function construct_numeric_range_from_string(str: string): NumericalRange0 {
161
    if (!regexNumericRange.test(str)) {
274✔
162
        return {
32✔
163
            type: NumericRangeType.InvalidRange,
164
            value: str
165
        };
166
    }
167
    /* detect multi dim range*/
168
    const values = str.split(",");
242✔
169

170
    if (values.length === 1) {
242✔
171
        return construct_numeric_range_bit_from_string(values[0]);
186✔
172
    } else if (values.length === 2) {
56✔
173
        const elements = values.map(construct_numeric_range_bit_from_string);
55✔
174
        let rowRange: any = elements[0];
55✔
175
        let colRange: any = elements[1];
55✔
176
        if (rowRange.type === NumericRangeType.InvalidRange || colRange.type === NumericRangeType.InvalidRange) {
55✔
177
            return { type: NumericRangeType.InvalidRange, value: str };
3✔
178
        }
179
        rowRange = _normalize(rowRange);
52✔
180
        colRange = _normalize(colRange);
52✔
181
        return {
52✔
182
            type: NumericRangeType.MatrixRange,
183
            value: [rowRange, colRange]
184
        };
185
    } else {
186
        // not supported yet
187
        return { type: NumericRangeType.InvalidRange, value: str };
1✔
188
    }
189
}
190

191
function construct_from_string(value: string): NumericalRange0 {
192
    return construct_numeric_range_from_string(value);
274✔
193
}
194

195
function _set_single_value(value: number | null): NumericalRange0 {
196
    if (value === null || value < 0 || !isFinite(value)) {
16✔
197
        return {
1✔
198
            type: NumericRangeType.InvalidRange,
199
            value: "" + value?.toString()
200
        };
201
    } else {
202
        return {
15✔
203
            type: NumericRangeType.SingleValue,
204
            value: value
205
        };
206
    }
207
}
208

209
function _check_range(numericalRange: NumericalRange0) {
210
    switch (numericalRange.type) {
25✔
211
        case NumericRangeType.ArrayRange:
212
            return _valid_range(numericalRange.value[0], numericalRange.value[1]);
25✔
213
    }
214
    // istanbul ignore next
215
    throw new Error("unsupported case");
216
}
217

218
function _set_range_value(low: number, high: number): NumericalRangeSingleValue | NumericalRangeArrayRange | NumericalRangeInvalid {
219
    if (low === high) {
31✔
220
        return {
6✔
221
            type: NumericRangeType.SingleValue,
222
            value: low
223
        };
224
    }
225
    const numericalRange: NumericalRangeArrayRange = {
25✔
226
        type: NumericRangeType.ArrayRange,
227
        value: [low, high]
228
    };
229
    if (!_check_range(numericalRange as NumericalRangeArrayRange)) {
25✔
230
        return {
3✔
231
            type: NumericRangeType.InvalidRange,
232
            value: ""
233
        };
234
    }
235
    return numericalRange;
22✔
236
}
237

238
function construct_from_values(value: number, secondValue?: number): NumericalRange0 {
239
    if (secondValue === undefined) {
38✔
240
        return _set_single_value(value);
16✔
241
    } else {
242
        if (!isFinite(secondValue)) {
22!
UNCOV
243
            throw new Error(" invalid second argument, expecting a number");
×
244
        }
245
        return _set_range_value(value, secondValue);
22✔
246
    }
247
}
248

249
function _construct_from_array(value: number[], value2?: any): NumericalRange0 {
250
    assert(value.length === 2);
9✔
251

252
    // istanbul ignore next
253
    if (!isFinite(value[0]) || !isFinite(value[1])) {
254
        return { type: NumericRangeType.InvalidRange, value: "" + value };
255
    }
256
    let range1 = _set_range_value(value[0], value[1]);
9✔
257
    if (!value2) {
9✔
258
        return range1;
6✔
259
    }
260
    // we have a matrix
261
    const nr2 = new NumericRange(value2);
3✔
262
    // istanbul ignore next
263
    if (
264
        nr2.type === NumericRangeType.InvalidRange ||
265
        nr2.type === NumericRangeType.MatrixRange ||
266
        nr2.type === NumericRangeType.Empty
267
    ) {
268
        return { type: NumericRangeType.InvalidRange, value: "" + value };
269
    }
270
    if (range1.type === NumericRangeType.SingleValue) {
3✔
271
        range1 = {
2✔
272
            type: NumericRangeType.ArrayRange,
273
            value: [range1.value, range1.value]
274
        };
275
    }
276
    if (nr2.type === NumericRangeType.SingleValue) {
3✔
277
        nr2.type = NumericRangeType.ArrayRange;
2✔
278
        nr2.value = [nr2.value as number, nr2.value as number];
2✔
279
    }
280

281
    // istanbul ignore next
282
    return {
283
        type: NumericRangeType.MatrixRange,
284
        value: [range1.value as number[], nr2.value as number[]]
285
    };
286
}
287

288
export class NumericRange implements NumericalRange1 {
5✔
289
    public static coerce = coerceNumericRange;
5✔
290

291
    public static schema = schemaNumericRange;
5✔
292
    // tslint:disable:variable-name
293
    public static NumericRangeType = NumericRangeType;
5✔
294

295
    public static readonly empty = new NumericRange() as NumericalRange0;
5✔
296

297
    public static overlap(nr1?: NumericalRange0, nr2?: NumericalRange0): boolean {
298
        nr1 = nr1 || NumericRange.empty;
96!
299
        nr2 = nr2 || NumericRange.empty;
96!
300

301
        if (NumericRangeType.Empty === nr1.type || NumericRangeType.Empty === nr2.type) {
96✔
302
            return true;
84✔
303
        }
304
        if (NumericRangeType.SingleValue === nr1.type && NumericRangeType.SingleValue === nr2.type) {
12✔
305
            return nr1.value === nr2.value;
2✔
306
        }
307
        if (NumericRangeType.ArrayRange === nr1.type && NumericRangeType.ArrayRange === nr2.type) {
10!
308
            // +-----+        +------+     +---+       +------+
309
            //     +----+       +---+    +--------+  +---+
310
            const l1 = nr1.value[0];
10✔
311
            const h1 = nr1.value[1];
10✔
312
            const l2 = nr2.value[0];
10✔
313
            const h2 = nr2.value[1];
10✔
314
            return _overlap(l1, h1, l2, h2);
10✔
315
        }
316
        // istanbul ignore next
317
        assert(false, "NumericalRange#overlap : case not implemented yet "); // TODO
318
        // istanbul ignore next
319
        return false;
320
    }
321

322
    public type: NumericRangeType;
323
    public value: NumericalRangeValueType;
324

325
    constructor();
326
    // tslint:disable-next-line: unified-signatures
327
    constructor(value: string | null);
328
    // tslint:disable-next-line: unified-signatures
329
    constructor(value: number, secondValue?: number);
330
    // tslint:disable-next-line: unified-signatures
331
    constructor(value: number[]);
332
    // tslint:disable-next-line: unified-signatures
333
    constructor(value: number[], secondValue: number[]);
334
    constructor(value?: null | string | number | number[], secondValue?: number | number[]) {
335
        this.type = NumericRangeType.InvalidRange;
724,485✔
336
        this.value = null;
724,485✔
337

338
        assert(!value || !(value instanceof NumericRange), "use coerce to create a NumericRange");
724,485✔
339
        assert(!secondValue || typeof secondValue === "number" || Array.isArray(secondValue));
724,484✔
340
        if (typeof value === "string") {
724,483✔
341
            const a = construct_from_string(value as string);
274✔
342
            this.type = a.type;
274✔
343
            this.value = a.value;
274✔
344
        } else if (
724,209✔
345
            typeof value === "number" &&
724,329✔
346
            isFinite(value) &&
347
            (secondValue === undefined || (typeof secondValue === "number" && isFinite(secondValue)))
348
        ) {
349
            const a = construct_from_values(value, secondValue);
38✔
350
            this.type = a.type;
38✔
351
            this.value = a.value;
38✔
352
        } else if (Array.isArray(value)) {
724,171✔
353
            const a = _construct_from_array(value, secondValue);
9✔
354
            this.type = a.type;
9✔
355
            this.value = a.value;
9✔
356
        } else {
357
            this.value = "<invalid>";
724,162✔
358
            this.type = NumericRangeType.Empty;
724,162✔
359
        }
360

361
        // xx assert((this.type !== NumericRangeType.ArrayRange) || Array.isArray(this.value));
362
    }
363

364
    public isValid(): boolean {
365
        if (this.type === NumericRangeType.ArrayRange) {
60,439✔
366
            const value = this.value as number[];
50✔
367
            if (value[0] < 0 || value[1] < 0) {
50✔
368
                return false;
1✔
369
            }
370
        }
371
        if (this.type === NumericRangeType.SingleValue) {
60,438✔
372
            const value = this.value as number;
8✔
373
            // istanbul ignore next
374
            if (value < 0) {
375
                return false;
376
            }
377
        }
378
        return this.type !== NumericRangeType.InvalidRange;
60,438✔
379
    }
380

381
    public isEmpty(): boolean {
382
        return this.type === NumericRangeType.Empty;
194,962✔
383
    }
384

385
    public isDefined(): boolean {
386
        return this.type !== NumericRangeType.Empty && this.type !== NumericRangeType.InvalidRange;
166,264✔
387
    }
388

389
    public toString(): string {
390
        function array_range_to_string(values: number[]): string {
391
            assert(Array.isArray(values));
90✔
392
            if (values.length === 2 && values[0] === values[1]) {
90✔
393
                return values[0].toString();
24✔
394
            }
395
            return values.map((value) => value.toString(10)).join(":");
132✔
396
        }
397

398
        function matrix_range_to_string(values: any) {
399
            return values
28✔
400
                .map((value: any) => {
401
                    return Array.isArray(value) ? array_range_to_string(value) : value.toString(10);
56!
402
                })
403
                .join(",");
404
        }
405

406
        switch (this.type) {
138✔
407
            case NumericRangeType.SingleValue:
408
                return (this.value as any).toString(10);
13✔
409

410
            case NumericRangeType.ArrayRange:
411
                return array_range_to_string(this.value as number[]);
34✔
412

413
            case NumericRangeType.Empty:
414
                return NUMERIC_RANGE_EMPTY_STRING;
45✔
415

416
            case NumericRangeType.MatrixRange:
417
                return matrix_range_to_string(this.value);
28✔
418

419
            default:
420
                assert(this.type === NumericRangeType.InvalidRange);
18✔
421
                return "NumericRange:<Invalid>";
18✔
422
        }
423
    }
424

425
    public toJSON(): string {
426
        return this.toString();
36✔
427
    }
428

429
    public toEncodeableString(): UAString {
430
        switch (this.type) {
481,170✔
431
            case NumericRangeType.SingleValue:
432
            case NumericRangeType.ArrayRange:
433
            case NumericRangeType.MatrixRange:
434
                return this.toString();
49✔
435
            case NumericRangeType.InvalidRange:
436
                // istanbul ignore next
437
                if (!(typeof this.value === "string")) {
438
                    throw new Error("Internal Error");
439
                }
440
                return this.value; // value contains the original strings which was detected invalid
20✔
441
            default:
442
                return null;
481,101✔
443
        }
444
    }
445

446
    /**
447

448
     * @param array   flat array containing values or string
449
     * @param dimensions: of the matrix if data is a matrix
450
     * @return {*}
451
     */
452
    public extract_values<U, T extends ArrayLike<U>>(array: T, dimensions?: number[] | null): ExtractResult<T> {
453
        const self = this as NumericalRange0;
45,685✔
454

455
        if (!array) {
45,685✔
456
            return {
8,060✔
457
                array,
458
                statusCode: this.type === NumericRangeType.Empty ? StatusCodes.Good : StatusCodes.BadIndexRangeNoData
8,060✔
459
            };
460
        }
461

462
        let index;
463
        let low_index;
464
        let high_index;
465
        let rowRange;
466
        let colRange;
467
        switch (self.type) {
37,625✔
468
            case NumericRangeType.Empty:
469
                return extract_empty(array, dimensions);
37,106✔
470

471
            case NumericRangeType.SingleValue:
472
                index = self.value;
16✔
473
                return extract_single_value(array, index);
16✔
474

475
            case NumericRangeType.ArrayRange:
476
                low_index = self.value[0];
71✔
477
                high_index = self.value[1];
71✔
478
                return extract_array_range(array, low_index, high_index);
71✔
479

480
            case NumericRangeType.MatrixRange:
481
                rowRange = self.value[0];
422✔
482
                colRange = self.value[1];
422✔
483
                return extract_matrix_range(array, rowRange, colRange, dimensions);
422✔
484

485
            default:
486
                return { statusCode: StatusCodes.BadIndexRangeInvalid };
10✔
487
        }
488
    }
489

490
    public set_values_matrix(
491
        sourceToAlter: { matrix: Buffer | []; dimensions: number[] },
492
        newMatrix: Buffer | []
493
    ): { matrix: Buffer | []; statusCode: StatusCode } {
494
        const { matrix, dimensions } = sourceToAlter;
6✔
495
        const self = this as NumericalRange0;
6✔
496
        assert(dimensions, "expecting valid dimensions here");
6✔
497
        if (self.type !== NumericRangeType.MatrixRange) {
6!
498
            // istanbul ignore next
499
            return { matrix, statusCode: StatusCodes.BadTypeMismatch };
500
        }
501

502
        assert(dimensions.length === 2);
6✔
503
        const nbRows = dimensions[0];
6✔
504
        const nbCols = dimensions[1];
6✔
505
        assert(sourceToAlter.matrix.length === nbRows * nbCols);
6✔
506
        const [rowStart, rowEnd] = self.value[0];
6✔
507
        const [colStart, colEnd] = self.value[1];
6✔
508

509
        const nbRowInNew = rowEnd - rowStart + 1;
6✔
510
        const nbColInNew = colEnd - colStart + 1;
6✔
511
        if (nbRowInNew * nbColInNew !== newMatrix.length) {
6!
UNCOV
512
            return { matrix, statusCode: StatusCodes.BadTypeMismatch };
×
513
        }
514
        // check if the sub-matrix is in th range of the initial matrix
515
        if (rowEnd >= nbRows || colEnd >= nbCols) {
6✔
516
            // debugLog("out of band range => ", { rowEnd, nbRows, colEnd, nbCols });
517
            return { matrix, statusCode: StatusCodes.BadTypeMismatch };
1✔
518
        }
519
        for (let row = rowStart; row <= rowEnd; row++) {
5✔
520
            const ri = row - rowStart;
8✔
521
            for (let col = colStart; col <= colEnd; col++) {
8✔
522
                const ci = col - colStart;
20✔
523
                matrix[row * nbCols + col] = newMatrix[ri * nbColInNew + ci];
20✔
524
            }
525
        }
526
        return {
5✔
527
            matrix,
528
            statusCode: StatusCodes.Good
529
        };
530
    }
531
    public set_values(arrayToAlter: Buffer | [], newValues: Buffer | []): { array: Buffer | [] | null; statusCode: StatusCode } {
532
        assert_array_or_buffer(arrayToAlter);
120✔
533
        assert_array_or_buffer(newValues);
120✔
534

535
        const self = this as NumericalRange0;
120✔
536

537
        let low_index;
538
        let high_index;
539
        switch (self.type) {
120✔
540
            case NumericRangeType.Empty:
541
                low_index = 0;
12✔
542
                high_index = arrayToAlter.length - 1;
12✔
543
                break;
12✔
544
            case NumericRangeType.SingleValue:
545
                low_index = self.value;
27✔
546
                high_index = self.value;
27✔
547
                break;
27✔
548
            case NumericRangeType.ArrayRange:
549
                low_index = self.value[0];
67✔
550
                high_index = self.value[1];
67✔
551
                break;
67✔
552
            case NumericRangeType.MatrixRange:
553
                // for the time being MatrixRange is not supported
554
                return { array: arrayToAlter, statusCode: StatusCodes.BadIndexRangeNoData };
10✔
555
            default:
556
                return { array: null, statusCode: StatusCodes.BadIndexRangeInvalid };
4✔
557
        }
558

559
        if (high_index >= arrayToAlter.length || low_index >= arrayToAlter.length) {
106✔
560
            return { array: null, statusCode: StatusCodes.BadIndexRangeNoData };
2✔
561
        }
562
        if (this.type !== NumericRangeType.Empty && newValues.length !== high_index - low_index + 1) {
104!
UNCOV
563
            return { array: null, statusCode: StatusCodes.BadIndexRangeInvalid };
×
564
        }
565
        const insertInPlace = Array.isArray(arrayToAlter)
104✔
566
            ? insertInPlaceStandardArray
567
            : arrayToAlter instanceof Buffer
88✔
568
            ? insertInPlaceBuffer
569
            : insertInPlaceTypedArray;
570
        return {
104✔
571
            array: insertInPlace(arrayToAlter, low_index, high_index, newValues),
572
            statusCode: StatusCodes.Good
573
        };
574
    }
575
}
576

577
function slice<U, T extends ArrayLike<U>>(arr: T, start: number, end: number): T {
578
    if (start === 0 && end === arr.length) {
37,186✔
579
        return arr;
37,107✔
580
    }
581

582
    let res;
583
    if ((arr as any).buffer instanceof ArrayBuffer) {
79✔
584
        res = (arr as any).subarray(start, end);
58✔
585
    } else if (arr instanceof Buffer) {
21!
UNCOV
586
        res = arr.subarray(start, end);
×
587
    } else {
588
        assert(typeof (arr as any).slice === "function");
21✔
589
        assert(arr instanceof Buffer || arr instanceof Array || typeof arr === "string");
21✔
590
        res = (arr as any).slice(start, end);
21✔
591
    }
592
    if (res instanceof Uint8Array && arr instanceof Buffer) {
79✔
593
        // note in io-js 3.00 onward standard Buffer are implemented differently and
594
        // provides a buffer member and a subarray method, in fact in io-js 3.0
595
        // it seems that Buffer acts as a Uint8Array. in this very special case
596
        // we need to make sure that we end up with a Buffer object and not a Uint8Array.
597
        res = Buffer.from(res);
8✔
598
    }
599
    return res;
79✔
600
}
601

602
export interface ExtractResult<T> {
603
    array?: T | null;
604
    statusCode: StatusCode;
605
    dimensions?: number[];
606
}
607

608
function extract_empty<U, T extends ArrayLike<U>>(array: T, dimensions: any): ExtractResult<T> {
609
    return {
37,106✔
610
        array: slice(array, 0, array.length),
611
        dimensions,
612
        statusCode: StatusCodes.Good
613
    };
614
}
615

616
function extract_single_value<U, T extends ArrayLike<U>>(array: T, index: number): ExtractResult<T> {
617
    if (index >= array.length) {
16✔
618
        if (typeof array === "string") {
2✔
619
            return { array: "" as any as T, statusCode: StatusCodes.BadIndexRangeNoData };
1✔
620
        }
621
        return { array: null as any as T, statusCode: StatusCodes.BadIndexRangeNoData };
1✔
622
    }
623
    return {
14✔
624
        array: slice(array, index, index + 1),
625
        statusCode: StatusCodes.Good
626
    };
627
}
628

629
function extract_array_range<U, T extends ArrayLike<U>>(array: T, low_index: number, high_index: number): ExtractResult<T> {
630
    assert(isFinite(low_index) && isFinite(high_index));
82✔
631
    assert(low_index >= 0);
82✔
632
    assert(low_index <= high_index);
82✔
633
    if (low_index >= array.length) {
82✔
634
        if (typeof array === "string") {
16✔
635
            return { array: "" as any as T, statusCode: StatusCodes.BadIndexRangeNoData };
2✔
636
        }
637
        return { array: null as any as T, statusCode: StatusCodes.BadIndexRangeNoData };
14✔
638
    }
639
    // clamp high index
640
    high_index = Math.min(high_index, array.length - 1);
66✔
641

642
    return {
66✔
643
        array: slice(array, low_index, high_index + 1),
644
        statusCode: StatusCodes.Good
645
    };
646
}
647

648
function isArrayLike(value: any): boolean {
649
    return typeof value.length === "number" || Object.prototype.hasOwnProperty.call(value, "length");
421✔
650
}
651

652
function extract_matrix_range<U, T extends ArrayLike<U>>(
653
    array: T,
654
    rowRange: number[],
655
    colRange: number[],
656
    dimension?: number[] | null
657
): ExtractResult<T> {
658
    assert(Array.isArray(rowRange) && Array.isArray(colRange));
422✔
659

660
    if (array.length === 0) {
422✔
661
        return {
1✔
662
            array: null,
663
            statusCode: StatusCodes.BadIndexRangeNoData
664
        };
665
    }
666
    if (isArrayLike((array as any)[0]) && !dimension) {
421✔
667
        // like extracting data from a one dimensional array of strings or byteStrings...
668
        const result = extract_array_range(array, rowRange[0], rowRange[1]);
5✔
669
        for (let i = 0; i < result.array!.length; i++) {
5✔
670
            const e = (result.array! as any)[i];
6✔
671
            (result.array as any)[i] = extract_array_range(e, colRange[0], colRange[1]).array;
6✔
672
        }
673
        return result;
4✔
674
    }
675
    if (!dimension) {
416✔
676
        return {
409✔
677
            array: null,
678
            statusCode: StatusCodes.BadIndexRangeNoData
679
        };
680
    }
681

682
    assert(dimension, "expecting dimension to know the shape of the matrix represented by the flat array");
7✔
683

684
    //
685
    const rowLow = rowRange[0];
7✔
686
    const rowHigh = rowRange[1];
7✔
687
    const colLow = colRange[0];
7✔
688
    const colHigh = colRange[1];
7✔
689

690
    const nbRow = dimension[0];
7✔
691
    const nbCol = dimension[1];
7✔
692

693
    const nbRowDest = rowHigh - rowLow + 1;
7✔
694
    const nbColDest = colHigh - colLow + 1;
7✔
695

696
    // construct an array of the same type with the appropriate length to
697
    // store the extracted matrix.
698
    const tmp = new (array as any).constructor(nbColDest * nbRowDest);
7✔
699

700
    let row;
701
    let col;
702
    let r;
703
    let c;
704
    r = 0;
7✔
705
    for (row = rowLow; row <= rowHigh; row++) {
7✔
706
        c = 0;
10✔
707
        for (col = colLow; col <= colHigh; col++) {
10✔
708
            const srcIndex = row * nbCol + col;
17✔
709
            const destIndex = r * nbColDest + c;
17✔
710
            tmp[destIndex] = (array as any)[srcIndex];
17✔
711
            c++;
17✔
712
        }
713
        r += 1;
10✔
714
    }
715
    return {
7✔
716
        array: tmp,
717
        dimensions: [nbRowDest, nbColDest],
718
        statusCode: StatusCodes.Good
719
    };
720
}
721

722
function assert_array_or_buffer(array: any) {
723
    assert(Array.isArray(array) || array.buffer instanceof ArrayBuffer || array instanceof Buffer);
240!
724
}
725

726
function insertInPlaceStandardArray(arrayToAlter: any, low: number, high: number, newValues: any): any {
727
    const args = [low, high - low + 1].concat(newValues);
16✔
728
    arrayToAlter.splice(...args);
16✔
729
    return arrayToAlter;
16✔
730
}
731

732
function insertInPlaceTypedArray(arrayToAlter: any, low: number, high: number, newValues: any): any {
733
    if (low === 0 && high === arrayToAlter.length - 1) {
72✔
734
        return new arrayToAlter.constructor(newValues);
16✔
735
    }
736
    assert(newValues.length === high - low + 1);
56✔
737
    arrayToAlter.subarray(low, high + 1).set(newValues);
56✔
738
    return arrayToAlter;
56✔
739
}
740

741
function insertInPlaceBuffer(bufferToAlter: Buffer | [], low: number, high: number, newValues: any): Buffer {
742
    // insertInPlaceBuffer with buffer is not really possible as existing Buffer cannot be resized
743
    if (!(bufferToAlter instanceof Buffer)) throw new Error("expecting a buffer");
16!
744
    if (low === 0 && high === bufferToAlter.length - 1) {
16✔
745
        bufferToAlter = Buffer.from(newValues);
4✔
746
        return bufferToAlter;
4✔
747
    }
748
    assert(newValues.length === high - low + 1);
12✔
749
    for (let i = 0; i < newValues.length; i++) {
12✔
750
        bufferToAlter[i + low] = newValues[i];
26✔
751
    }
752
    return bufferToAlter;
12✔
753
}
754

755
function _overlap(l1: number, h1: number, l2: number, h2: number): boolean {
756
    return Math.max(l1, l2) <= Math.min(h1, h2);
10✔
757
}
758

759
export function encodeNumericRange(numericRange: NumericRange, stream: OutputBinaryStream): void {
5✔
760
    assert(numericRange instanceof NumericRange);
481,156✔
761
    encodeString(numericRange.toEncodeableString(), stream);
481,156✔
762
}
763

764
export function decodeNumericRange(stream: BinaryStream, _value?: NumericRange): NumericRange {
5✔
765
    const str = decodeString(stream)!;
239,502✔
766
    return new NumericRange(str);
239,502✔
767
}
768

769
function coerceNumericRange(value: any | string | NumericRange | null | number[]): NumericRange {
770
    if (value instanceof NumericRange) {
261,010✔
771
        return value;
260,938✔
772
    }
773
    if (value === null || value === undefined) {
72✔
774
        return new NumericRange();
37✔
775
    }
776
    if (value === NUMERIC_RANGE_EMPTY_STRING) {
35✔
777
        return new NumericRange();
1✔
778
    }
779
    assert(typeof value === "string" || Array.isArray(value));
34!
780
    return new NumericRange(value);
34✔
781
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc