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

benrr101 / node-taglib-sharp / 48391045

29 Oct 2023 04:39AM UTC coverage: 92.535% (-1.4%) from 93.934%
48391045

push

appveyor

benrr101
Merge branch 'release/v5.2.0'

3244 of 4129 branches covered (0.0%)

Branch coverage included in aggregate %.

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

26728 of 28261 relevant lines covered (94.58%)

423.2 hits per line

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

90.87
/src/utils.ts
1
import * as Path from "path";
1✔
2
import {ByteVector} from "./byteVector";
3

4
export class Guards {
1✔
5
    private static readonly MAX_LONG = BigInt("9223372036854775807");
1✔
6
    private static readonly MAX_ULONG = BigInt("18446744073709551615");
1✔
7
    private static readonly MIN_LONG = BigInt("-9223372036854775808");
1✔
8

9
    public static all<TElement>(value: TElement[], guard: (val: TElement, name: string) => void, name: string): void {
10
        for (const element of value) {
2✔
11
            guard(element, `All elements in ${name}`);
4✔
12
        }
13
    }
14

15
    public static betweenExclusive(value: number, minValue: number, maxValue: number, name: string): void {
16
        if (value <= minValue || value >= maxValue) {
17✔
17
            throw new Error(`Argument out of range: ${name} must satisfy ${maxValue} <= ${name} <= ${minValue}`);
2✔
18
        }
19
    }
20

21
    public static betweenInclusive(value: number, minValue: number, maxValue: number, name: string): void {
22
        if (value < minValue || value > maxValue) {
1,156✔
23
            throw new Error(`Argument out of range: ${name} must satisfy ${maxValue} < ${name} < ${minValue}`);
8✔
24
        }
25
    }
26

27
    public static byte(value: number, name: string): void {
28
        if (!Number.isSafeInteger(value) || value < 0 || value > 0xFF) {
30,391✔
29
            throw new Error(`Argument out of range: ${name} must be a safe, positive, 8-bit integer`);
106✔
30
        }
31
    }
32

33
    public static greaterThanInclusive(value: number, lowerBound: number, name: string): void {
34
        if (value < lowerBound) {
579✔
35
            throw new Error(`Argument out of range: ${name} must greater than ${lowerBound}`);
5✔
36
        }
37
    }
38

39
    public static equals(value: number, expectedValue: number, name: string): void {
40
        if (value !== expectedValue) {
1,226!
41
            throw new Error(`Argument out of range: ${name} must equal ${expectedValue}`);
×
42
        }
43
    }
44

45
    public static int(value: number, name: string): void {
46
        if (!Number.isInteger(value) || value < -2147483648 || value > 2147483647) {
92✔
47
            throw new Error(`Argument out of range: ${name} must be a 32-bit integer`);
18✔
48
        }
49
    }
50

51
    public static lessThanInclusive(value: number, upperBound: number, name: string): void {
52
        if (value > upperBound) {
1,171,997✔
53
            throw new Error(`Argument out of range: ${name} must be less than ${upperBound}`);
7✔
54
        }
55
    }
56

57
    public static long(value: bigint, name: string): void {
58
        if (value > Guards.MAX_LONG || value < Guards.MIN_LONG) {
38✔
59
            throw new Error(`Argument out of range: ${name} must be a 64-bit integer`);
2✔
60
        }
61
    }
62

63
    public static notNullOrUndefined(value: unknown, name: string): void {
64
        if (value === undefined || value === null) {
15,241✔
65
            throw new Error(`Argument null: ${name} was not provided`);
46✔
66
        }
67
    }
68

69
    public static optionalByte(value: number | undefined, name: string): void {
70
        if (value === undefined) {
12✔
71
            return;
2✔
72
        }
73
        Guards.byte(value, name);
10✔
74
    }
75

76
    /**
77
     * Throws if the provided value is not a safe, integer. Use this method instead of
78
     * {@link Guards.int()} if validating an argument for use in file manipulation.
79
     * @param value Value to validate
80
     * @param name Name of the parameter in calling function
81
     */
82
    public static safeInt(value: number, name: string): void {
83
        if (!Number.isSafeInteger(value)) {
987✔
84
            throw new Error(`Argument out of range: ${name} must be a safe JS integer`);
28✔
85
        }
86
    }
87

88
    /**
89
     * Throws if the provided value is not a safe, positive integer. Use this method instead of
90
     * {@link Guards.uint()} if validating an argument for use in file manipulation.
91
     * @param value Value to validate
92
     * @param name Name of the parameter in calling function
93
     */
94
    public static safeUint(value: number, name: string): void {
95
        if (!Number.isSafeInteger(value) || value < 0) {
67,276✔
96
            throw new Error(`Argument out of range ${name} must be a safe, positive JS integer`);
161✔
97
        }
98
    }
99

100
    public static short(value: number, name: string): void {
101
        if (!Number.isSafeInteger(value) || value > 32767 || value < -32768) {
40✔
102
            throw new Error(`Argument out of range: ${name} must be a 16-bit integer`);
7✔
103
        }
104
    }
105

106
    public static truthy(value: object|string, name: string): void {
107
        if (!value) {
45,747✔
108
            throw new Error(`Argument null: ${name} was not provided`);
427✔
109
        }
110
    }
111

112
    public static uint(value: number, name: string): void {
113
        if (!Number.isSafeInteger(value) || value > 4294967295 || value < 0) {
1,165,944✔
114
            throw new Error(`Argument out of range: ${name} must be a positive, 32-bit integer`);
516✔
115
        }
116
    }
117

118
    public static ushort(value: number, name: string): void {
119
        if (!Number.isSafeInteger(value) || value > 65535 || value < 0) {
2,375✔
120
            throw new Error(`Argument out of range: ${name} must be a positive, 16-bit integer`);
44✔
121
        }
122
    }
123

124
    public static ulong(value: bigint, name: string): void {
125
        if (value > this.MAX_ULONG || value < 0) {
234✔
126
            throw new Error(`Argument out of range: ${name} must be a positive, 64-bit integer`);
7✔
127
        }
128
    }
129
}
130

131
export class StringComparison {
1✔
132
    public static caseInsensitive(a: string, b: string): boolean {
133
        Guards.notNullOrUndefined(a, "a");
923✔
134
        Guards.notNullOrUndefined(b, "b");
923✔
135
        return a.toUpperCase() === b.toUpperCase();
923✔
136
    }
137

138
    public static caseSensitive(a: string, b: string): boolean {
139
        return a === b;
41✔
140
    }
141
}
142

143
export class FileUtils {
1✔
144
    public static getExtension(name: string): string {
145
        let ext = Path.extname(name);
41✔
146
        if (!ext) {
41✔
147
            ext = name.startsWith(".") ? name.substring(1) : name;
19✔
148
        } else {
149
            ext = ext.substring(1);
22✔
150
        }
151

152
        return ext.toLowerCase();
41✔
153
    }
154
}
155

156
export class ArrayUtils {
1✔
157
    public static remove<T>(array: T[], callbackFn: (e: T, i: number) => boolean): void {
158
        let i = array.length;
34✔
159
        while (i--) {
34✔
160
            if (callbackFn(array[i], i)) {
116✔
161
                array.splice(i, 1);
64✔
162
            }
163
        }
164
    }
165

166
    public static isFalsyOrEmpty(array: unknown[]): boolean {
167
        return !array || array.length === 0;
194✔
168
    }
169

170
    public static safePush<T extends object>(array: T[], element: T): void {
171
        if (element) {
489!
172
            array.push(element);
489✔
173
        }
174
    }
175

176
    public static safePushRange<T extends object>(array: T[], elements: T[]): void {
177
        if (elements) {
194!
178
            for (const element of elements) {
194✔
179
                ArrayUtils.safePush(array, element);
230✔
180
            }
181
        }
182
    }
183
}
184

185
export class NumberUtils {
1✔
186
    public static readonly BIG_ZERO = BigInt(0);
1✔
187
    public static readonly BIG_ONE = BigInt(1);
1✔
188
    public static readonly BIG_TWO = BigInt(2);
1✔
189
    public static readonly BIG_ONE_THOUSAND = BigInt(1000);
1✔
190
    public static readonly TICKS_PER_MILLISECOND_BIG = BigInt(10000);
1✔
191
    public static readonly TICKS_PER_MILLISECOND_NUM = 10000;
1✔
192
    public static readonly MAX_UINT = 4294967295;
1✔
193

194
    public static bigPow(x: bigint, y: number): bigint {
195
        Guards.uint(y, "y");
16✔
196
        // @TODO: Consider upgrading target to es2016 to get the ** syntax
197

198
        let result = BigInt(1);
16✔
199
        for (let i = 0; i < y; i++) {
16✔
200
            result *= x;
299✔
201
        }
202

203
        return result;
16✔
204
    }
205

206
    public static hasFlag(haystack: number, needle: number, strict: boolean = false): boolean {
8,106✔
207
        return strict
8,425✔
208
            ? (haystack & needle) === needle
8,425✔
209
            : (haystack & needle) !== 0;
210
    }
211

212
    /**
213
     * Performs the same operation as ldexp does in C/C++
214
     * @param x Number to be multiplied by 2^y
215
     * @param y Power to raise 2 to
216
     * @returns Number x * 2^y
217
     */
218
    public static ldexp(x: number, y: number): number {
219
        return x * Math.pow(2, y);
6✔
220
    }
221

222
    /**
223
     * Converts .NET DateTime ticks (100 nanosecond units) into milliseconds
224
     * @param ticks 100 nanosecond ticks to convert
225
     */
226
    public static ticksToMilli(ticks: bigint|number): number {
227
        // Ticks are 100 nanosecond units
228
        return typeof(ticks) === "number"
6✔
229
            ? ticks / NumberUtils.TICKS_PER_MILLISECOND_NUM
6✔
230
            : Number(ticks / NumberUtils.TICKS_PER_MILLISECOND_BIG);
231
    }
232

233
    /**
234
     * Provides way to do unsigned bitwise AND without all the mess of parenthesis.
235
     * @param x Left operand
236
     * @param y Right operand
237
     * @returns Number (x & y) >>> 0
238
     */
239
    public static uintAnd(x: number, y: number): number {
240
        return (x & y) >>> 0;
4,625✔
241
    }
242

243
    /**
244
     * Provides way to do unsigned bitwise OR without all the mess of parenthesis.
245
     * @param numbers Operands to bitwise or together
246
     * @returns Number (x | y | ...) >>> 0
247
     */
248
    public static uintOr(... numbers: number[]): number {
249
        return numbers.reduce((acc, cur) => (acc | cur) >>> 0, 0);
1,103✔
250
    }
251

252
    /**
253
     * Provides way to do unsigned bitwise XOR without all the mess of parenthesis.
254
     * @param x Left operand
255
     * @param y Right operant
256
     * @returns Number (x ^ y) >>> 0
257
     */
258
    public static uintXor(x: number, y: number): number {
259
        return (x ^ y) >>> 0;
1,918✔
260
    }
261

262
    /**
263
     * Provides way to do unsigned bitshift left without all the mess of parenthesis.
264
     * @param x Number
265
     * @param y Bits to shift to the left
266
     * @returns Number (x << y) >>> 0;
267
     */
268
    public static uintLShift(x: number, y: number): number {
269
        return (x << y) >>> 0;
1,381✔
270
    }
271

272
    /**
273
     * Provides unified way to do unsigned right bitshift.
274
     * @param x Number
275
     * @param y Bits to shift to the right
276
     */
277
    public static uintRShift(x: number, y: number): number {
278
        return x >>> y;
4,121✔
279
    }
280

281
    /**
282
     * Converts IEEE 80-bit floating point numbers (SANE "extended" type) to double precision
283
     * floating point number.
284
     * @source http://www33146ue.sakura.ne.jp/staff/iz/formats/ieee.c
285
     */
286
    public static convertFromIeeeExtended(bytes: ByteVector): number {
287
        let f: number;
288

289
        let exponent = NumberUtils.uintLShift(NumberUtils.uintAnd(bytes.get(0), 0x7F), 8);
3✔
290
        exponent = NumberUtils.uintOr(exponent, bytes.get(1));
3✔
291

292
        let hiMantissa = NumberUtils.uintLShift(bytes.get(2), 24);
3✔
293
        hiMantissa = NumberUtils.uintOr(hiMantissa, NumberUtils.uintLShift(bytes.get(3), 16));
3✔
294
        hiMantissa = NumberUtils.uintOr(hiMantissa, NumberUtils.uintLShift(bytes.get(4), 8));
3✔
295
        hiMantissa = NumberUtils.uintOr(hiMantissa, bytes.get(5));
3✔
296
        let loMantissa = NumberUtils.uintLShift(bytes.get(6), 24);
3✔
297
        loMantissa = NumberUtils.uintOr(loMantissa, NumberUtils.uintLShift(bytes.get(7), 16));
3✔
298
        loMantissa = NumberUtils.uintOr(loMantissa, NumberUtils.uintLShift(bytes.get(8), 8));
3✔
299
        loMantissa = NumberUtils.uintOr(loMantissa, bytes.get(9));
3✔
300

301
        if (exponent === 0 && hiMantissa === 0 && loMantissa === 0) {
3!
302
            return 0;
×
303
        }
304

305
        if (exponent === 0x7FFF) {
3!
306
            f = Number.POSITIVE_INFINITY;
×
307
        } else {
308
            exponent -= 16383;
3✔
309
            f = NumberUtils.ldexp(hiMantissa, exponent -= 31);
3✔
310
            f += NumberUtils.ldexp(loMantissa, exponent - 32);
3✔
311
        }
312

313
        if ((bytes.get(0) & 0x80) !== 0) {
3!
314
            return -f;
×
315
        } else {
316
            return f;
3✔
317
        }
318
    }
319
}
320

321
export class StringUtils {
1✔
322
    public static readonly BCP47_REGEX = new RegExp(/^[a-z]{2,3}(-[a-z]{2,8})*$/i)
1✔
323
    public static readonly ISO369_2_REGEX = new RegExp(/^[a-z]{2,3}([-\/][a-z]{2,3})?$/i);
1✔
324

325
    public static findLastByteFromRight(haystack: string, needle: number): number {
326
        let length = haystack.length;
×
327
        while (length > 0 && haystack.charCodeAt(length - 1) === needle) {
×
328
            length--;
×
329
        }
330
        return length;
×
331
    }
332

333
    public static isBcp47(value: string): boolean {
334
        return this.BCP47_REGEX.test(value);
5✔
335
    }
336

337
    public static isIso3692(value: string): boolean {
338
        return this.ISO369_2_REGEX.test(value);
4✔
339
    }
340

341
    public static trimStart(toTrim: string, chars: string): string {
342
        while (toTrim.length > 0 && chars.indexOf(toTrim[0]) > -1) {
881✔
343
            toTrim = toTrim.substring(0);
×
344
        }
345
        return toTrim;
881✔
346
    }
347
}
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