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

benrr101 / node-taglib-sharp / 46462134

pending completion
46462134

push

appveyor

Benjamin Russell
Merge branch 'release/v5.1.0'

3096 of 3788 branches covered (81.73%)

Branch coverage included in aggregate %.

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

25320 of 26463 relevant lines covered (95.68%)

437.0 hits per line

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

88.95
/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) {
28,860✔
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 int(value: number, name: string): void {
40
        if (!Number.isInteger(value) || value < -2147483648 || value > 2147483647) {
92✔
41
            throw new Error(`Argument out of range: ${name} must be a 32-bit integer`);
18✔
42
        }
43
    }
44

45
    public static lessThanInclusive(value: number, upperBound: number, name: string): void {
46
        if (value > upperBound) {
1,143,661✔
47
            throw new Error(`Argument out of range: ${name} must be less than ${upperBound}`);
7✔
48
        }
49
    }
50

51
    public static long(value: bigint, name: string): void {
52
        if (value > Guards.MAX_LONG || value < Guards.MIN_LONG) {
38✔
53
            throw new Error(`Argument out of range: ${name} must be a 64-bit integer`);
2✔
54
        }
55
    }
56

57
    public static notNullOrUndefined(value: unknown, name: string): void {
58
        if (value === undefined || value === null) {
14,292✔
59
            throw new Error(`Argument null: ${name} was not provided`);
46✔
60
        }
61
    }
62

63
    public static optionalByte(value: number | undefined, name: string): void {
64
        if (value === undefined) {
12✔
65
            return;
2✔
66
        }
67
        Guards.byte(value, name);
10✔
68
    }
69

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

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

94
    public static short(value: number, name: string): void {
95
        if (!Number.isSafeInteger(value) || value > 32767 || value < -32768) {
36✔
96
            throw new Error(`Argument out of range: ${name} must be a 16-bit integer`);
7✔
97
        }
98
    }
99

100
    public static truthy(value: object|string, name: string): void {
101
        if (!value) {
30,397✔
102
            throw new Error(`Argument null: ${name} was not provided`);
417✔
103
        }
104
    }
105

106
    public static uint(value: number, name: string): void {
107
        if (!Number.isSafeInteger(value) || value > 4294967295 || value < 0) {
1,137,526✔
108
            throw new Error(`Argument out of range: ${name} must be a positive, 32-bit integer`);
490✔
109
        }
110
    }
111

112
    public static ushort(value: number, name: string): void {
113
        if (!Number.isSafeInteger(value) || value > 65535 || value < 0) {
2,330✔
114
            throw new Error(`Argument out of range: ${name} must be a positive, 16-bit integer`);
40✔
115
        }
116
    }
117

118
    public static ulong(value: bigint, name: string): void {
119
        if (value > this.MAX_ULONG || value < 0) {
234✔
120
            throw new Error(`Argument out of range: ${name} must be a positive, 64-bit integer`);
7✔
121
        }
122
    }
123
}
124

125
export class StringComparison {
1✔
126
    public static caseInsensitive(a: string, b: string): boolean {
127
        Guards.notNullOrUndefined(a, "a");
923✔
128
        Guards.notNullOrUndefined(b, "b");
923✔
129
        return a.toUpperCase() === b.toUpperCase();
923✔
130
    }
131

132
    public static caseSensitive(a: string, b: string): boolean {
133
        return a === b;
41✔
134
    }
135
}
136

137
export class FileUtils {
1✔
138
    public static getExtension(name: string): string {
139
        let ext = Path.extname(name);
40✔
140
        if (!ext) {
40✔
141
            ext = name.startsWith(".") ? name.substring(1) : name;
19✔
142
        } else {
143
            ext = ext.substring(1);
21✔
144
        }
145

146
        return ext.toLowerCase();
40✔
147
    }
148
}
149

150
export class ArrayUtils {
1✔
151
    public static remove<T>(array: T[], callbackFn: (e: T, i: number) => boolean): void {
152
        let i = this.length;
×
153
        while (i--) {
×
154
            if (callbackFn(array[i], i)) {
×
155
                array.splice(i, 1);
×
156
            }
157
        }
158
    }
159
}
160

161
export class NumberUtils {
1✔
162
    public static readonly BIG_ZERO = BigInt(0);
1✔
163
    public static readonly BIG_ONE = BigInt(1);
1✔
164
    public static readonly BIG_TWO = BigInt(2);
1✔
165
    public static readonly TICKS_PER_MILLISECOND_BIG = BigInt(10000);
1✔
166
    public static readonly TICKS_PER_MILLISECOND_NUM = 10000;
1✔
167

168
    public static bigPow(x: bigint, y: number): bigint {
169
        Guards.uint(y, "y");
16✔
170
        // @TODO: Consider upgrading target to es2016 to get the ** syntax
171

172
        let result = BigInt(1);
16✔
173
        for (let i = 0; i < y; i++) {
16✔
174
            result *= x;
299✔
175
        }
176

177
        return result;
16✔
178
    }
179

180
    public static hasFlag(haystack: number, needle: number, strict: boolean = false): boolean {
7,739✔
181
        return strict
7,752✔
182
            ? (haystack & needle) === needle
7,752✔
183
            : (haystack & needle) !== 0;
184
    }
185

186
    /**
187
     * Performs the same operation as ldexp does in C/C++
188
     * @param x Number to be multiplied by 2^y
189
     * @param y Power to raise 2 to
190
     * @returns Number x * 2^y
191
     */
192
    public static ldexp(x: number, y: number): number {
193
        return x * Math.pow(2, y);
6✔
194
    }
195

196
    /**
197
     * Converts .NET DateTime ticks (100 nanosecond units) into milliseconds
198
     * @param ticks 100 nanosecond ticks to convert
199
     */
200
    public static ticksToMilli(ticks: bigint|number): number {
201
        // Ticks are 100 nanosecond units
202
        return typeof(ticks) === "number"
6✔
203
            ? ticks / NumberUtils.TICKS_PER_MILLISECOND_NUM
6✔
204
            : Number(ticks / NumberUtils.TICKS_PER_MILLISECOND_BIG);
205
    }
206

207
    /**
208
     * Provides way to do unsigned bitwise AND without all the mess of parenthesis.
209
     * @param x Left operand
210
     * @param y Right operand
211
     * @returns Number (x & y) >>> 0
212
     */
213
    public static uintAnd(x: number, y: number): number {
214
        return (x & y) >>> 0;
4,625✔
215
    }
216

217
    /**
218
     * Provides way to do unsigned bitwise OR without all the mess of parenthesis.
219
     * @param numbers Operands to bitwise or together
220
     * @returns Number (x | y | ...) >>> 0
221
     */
222
    public static uintOr(... numbers: number[]): number {
223
        return numbers.reduce((acc, cur) => (acc | cur) >>> 0, 0);
1,103✔
224
    }
225

226
    /**
227
     * Provides way to do unsigned bitwise XOR without all the mess of parenthesis.
228
     * @param x Left operand
229
     * @param y Right operant
230
     * @returns Number (x ^ y) >>> 0
231
     */
232
    public static uintXor(x: number, y: number): number {
233
        return (x ^ y) >>> 0;
1,918✔
234
    }
235

236
    /**
237
     * Provides way to do unsigned bitshift left without all the mess of parenthesis.
238
     * @param x Number
239
     * @param y Bits to shift to the left
240
     * @returns Number (x << y) >>> 0;
241
     */
242
    public static uintLShift(x: number, y: number): number {
243
        return (x << y) >>> 0;
1,383✔
244
    }
245

246
    /**
247
     * Provides unified way to do unsigned right bitshift.
248
     * @param x Number
249
     * @param y Bits to shift to the right
250
     */
251
    public static uintRShift(x: number, y: number): number {
252
        return x >>> y;
4,123✔
253
    }
254

255
    /**
256
     * Converts IEEE 80-bit floating point numbers (SANE "extended" type) to double precision
257
     * floating point number.
258
     * @source http://www33146ue.sakura.ne.jp/staff/iz/formats/ieee.c
259
     */
260
    public static convertFromIeeeExtended(bytes: ByteVector): number {
261
        let f: number;
262

263
        let exponent = NumberUtils.uintLShift(NumberUtils.uintAnd(bytes.get(0), 0x7F), 8);
3✔
264
        exponent = NumberUtils.uintOr(exponent, bytes.get(1));
3✔
265

266
        let hiMantissa = NumberUtils.uintLShift(bytes.get(2), 24);
3✔
267
        hiMantissa = NumberUtils.uintOr(hiMantissa, NumberUtils.uintLShift(bytes.get(3), 16));
3✔
268
        hiMantissa = NumberUtils.uintOr(hiMantissa, NumberUtils.uintLShift(bytes.get(4), 8));
3✔
269
        hiMantissa = NumberUtils.uintOr(hiMantissa, bytes.get(5));
3✔
270
        let loMantissa = NumberUtils.uintLShift(bytes.get(6), 24);
3✔
271
        loMantissa = NumberUtils.uintOr(loMantissa, NumberUtils.uintLShift(bytes.get(7), 16));
3✔
272
        loMantissa = NumberUtils.uintOr(loMantissa, NumberUtils.uintLShift(bytes.get(8), 8));
3✔
273
        loMantissa = NumberUtils.uintOr(loMantissa, bytes.get(9));
3✔
274

275
        if (exponent === 0 && hiMantissa === 0 && loMantissa === 0) {
3!
276
            return 0;
×
277
        }
278

279
        if (exponent === 0x7FFF) {
3!
280
            f = Number.POSITIVE_INFINITY;
×
281
        } else {
282
            exponent -= 16383;
3✔
283
            f = NumberUtils.ldexp(hiMantissa, exponent -= 31);
3✔
284
            f += NumberUtils.ldexp(loMantissa, exponent - 32);
3✔
285
        }
286

287
        if ((bytes.get(0) & 0x80) !== 0) {
3!
288
            return -f;
×
289
        } else {
290
            return f;
3✔
291
        }
292
    }
293
}
294

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

299
    public static findLastByteFromRight(haystack: string, needle: number): number {
300
        let length = haystack.length;
×
301
        while (length > 0 && haystack.charCodeAt(length - 1) === needle) {
×
302
            length--;
×
303
        }
304
        return length;
×
305
    }
306

307
    public static isBcp47(value: string): boolean {
308
        return this.BCP47_REGEX.test(value);
5✔
309
    }
310

311
    public static isIso3692(value: string): boolean {
312
        return this.ISO369_2_REGEX.test(value);
4✔
313
    }
314

315
    public static trimStart(toTrim: string, chars: string): string {
316
        while (toTrim.length > 0 && chars.indexOf(toTrim[0]) > -1) {
30✔
317
            toTrim = toTrim.substring(0);
×
318
        }
319
        return toTrim;
30✔
320
    }
321
}
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