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

benrr101 / node-taglib-sharp / 51054818

25 Nov 2024 06:32PM UTC coverage: 92.556% (-0.02%) from 92.578%
51054818

push

appveyor

benrr101
Merge branch 'release/v6.0.0'

3251 of 4131 branches covered (78.7%)

Branch coverage included in aggregate %.

580 of 599 new or added lines in 27 files covered. (96.83%)

4 existing lines in 4 files now uncovered.

26752 of 28285 relevant lines covered (94.58%)

471.65 hits per line

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

97.1
/src/stream.ts
1
import * as fs from "fs";
1✔
2

3
import {Guards} from "./utils";
1✔
4
import {ByteVector} from "./byteVector";
1✔
5

6
/**
7
 * Indicates there the seek operation should begin.
8
 */
9
export enum SeekOrigin {
1✔
10
    /**
11
     * Seek should begin at the start of the file.
12
     */
13
    Begin,
1✔
14

15
    /**
16
     * Seek should begin at the current position in the file.
17
     */
18
    Current,
1✔
19

20
    /**
21
     * Seek should begin at the end of the file.
22
     */
23
    End
1✔
24
}
25

26
/**
27
 * Interface for a stream, it wraps around a file descriptor to make reading and writing to files
28
 * using node IO a lot easier.
29
 */
30
export interface IStream {
31
    /**
32
     * Whether or not the stream can be written to
33
     */
34
    readonly canWrite: boolean;
35

36
    /**
37
     * Number of bytes currently stored in file this stream connects to
38
     */
39
    readonly length: number;
40

41
    /**
42
     * Position within the stream
43
     */
44
    position: number;
45

46
    /**
47
     * Closes the stream
48
     */
49
    close(): void;
50

51
    /**
52
     * Reads a block of bytes from the current stream and writes the data to a buffer.
53
     * @param buffer When this method returns, contains the specified byte array with the values
54
     *     between `offset` and (`offset` + `length` - 1) replaced by
55
     *     the characters read from the current stream
56
     * @param offset Zero-based byte offset in `buffer` at which to begin storing data
57
     *     from the current stream
58
     * @param length The maximum number of bytes to read
59
     * @returns
60
     *     Total number of bytes written to the buffer. This can be less than the
61
     *     number of bytes requested if that number of bytes are not currently available or zero if
62
     *     the end of the stream is reached before any bytes are read
63
     */
64
    read(buffer: Uint8Array, offset: number, length: number): number;
65

66
    /**
67
     * Sets the position within the current stream to the specified value.
68
     * @param offset New position within the stream. this is relative to the `origin`
69
     *     parameter and can be positive or negative
70
     * @param origin Seek reference point {@link SeekOrigin}
71
     */
72
    seek(offset: number, origin: SeekOrigin): void;
73

74
    /**
75
     * Sets the length of the current current stream to the specified value.
76
     * @param length Number of bytes to set the length of the stream to
77
     */
78
    setLength(length: number): void;
79

80
    /**
81
     * Writes a block of bytes to the current stream using data read from a buffer.
82
     * @param buffer Buffer to write data from
83
     * @param bufferOffset Zero-based byte offset in `buffer` at which to begin copying
84
     *    bytes to the current stream
85
     * @param length Maximum number of bytes to write
86
     */
87
    write(buffer: Uint8Array | ByteVector, bufferOffset: number, length: number): number;
88
}
89

90
/**
91
 * Wrapper around the Node.js internal file descriptors to mock behavior like .NET Streams
92
 */
93
export class Stream implements IStream {
1✔
94
    private readonly _canWrite: boolean;
95
    private readonly _fd: number;
96
    private _length: number;
97
    private _position: number;
98

99
    // #region Constructors
100

101
    private constructor(fd: number, canWrite: boolean) {
102
        this._canWrite = canWrite;
22✔
103
        this._fd = fd;
22✔
104
        this._position = 0;
22✔
105
        this._length = fs.fstatSync(fd).size;
22✔
106
    }
107

108
    public static createAsRead(path: string): Stream {
109
        const fd = fs.openSync(path, "r");
13✔
110
        return new Stream(fd, false);
12✔
111
    }
112

113
    public static createAsReadWrite(path: string): Stream {
114
        const fd = fs.openSync(path, "r+");
12✔
115
        return new Stream(fd, true);
10✔
116
    }
117

118
    // #endregion
119

120
    // #region Properties
121

122
    /** @inheritDoc */
123
    public get canWrite(): boolean { return this._canWrite; }
4✔
124

125
    /** @inheritDoc */
126
    public get length(): number { return this._length; }
16✔
127

128
    /** @inheritDoc */
129
    public get position(): number { return this._position; }
29✔
130
    /** @inheritDoc */
131
    public set position(position: number) {
132
        Guards.safeUint(position, "position");
26✔
133
        this._position = position;
18✔
134
    }
135

136
    // #endregion
137

138
    // #region Public Methods
139

140
    /** @inheritDoc */
141
    public close(): void {
142
        fs.closeSync(this._fd);
12✔
143
    }
144

145
    /** @inheritDoc */
146
    public read(buffer: Uint8Array, bufferOffset: number, length: number): number {
147
        const bytes = fs.readSync(this._fd, buffer, bufferOffset, length, this._position);
5✔
148
        this._position += bytes;
5✔
149
        return bytes;
5✔
150
    }
151

152
    /** @inheritDoc */
153
    public seek(offset: number, origin: SeekOrigin): void {
154
        Guards.safeInt(offset, "offset");
27✔
155
        switch (origin) {
13✔
156
            case SeekOrigin.Begin:
13✔
157
                this.position = offset;
4✔
158
                break;
3✔
159
            case SeekOrigin.Current:
160
                this.position = this.position + offset;
5✔
161
                break;
4✔
162
            case SeekOrigin.End:
163
                this.position = this.length + offset;
4✔
164
                break;
3✔
165
        }
166
    }
167

168
    /** @inheritDoc */
169
    public setLength(length: number): void {
170
        Guards.safeUint(length, "length");
9✔
171
        if (!this._canWrite) {
4✔
172
            throw new Error("Invalid operation: this stream is a read-only stream");
1✔
173
        }
174

175
        if (length === this._length) {
3✔
176
            return;
1✔
177
        }
178

179
        fs.ftruncateSync(this._fd, length);
2✔
180
        this._length = length;
2✔
181
        if (this._position > this._length) {
2✔
182
            this._position = this._length;
1✔
183
        }
184
    }
185

186
    /** @inheritDoc */
187
    public write(buffer: Uint8Array | ByteVector, bufferOffset: number, length: number): number {
188
        // Make sure we standardize on a Uint8Array
189
        if (buffer instanceof ByteVector) {
5!
190
            // noinspection JSDeprecatedSymbols - As far as I know, we have no choice here.
UNCOV
191
            buffer = buffer.toByteArray();
×
192
        }
193

194
        if (!this._canWrite) {
5✔
195
            throw new Error("Invalid operation: this stream is a read-only stream");
1✔
196
        }
197
        const origLength = this._length;
4✔
198

199
        const bytes = fs.writeSync(this._fd, buffer, bufferOffset, length, this._position);
4✔
200
        this._position += bytes;
4✔
201

202
        // If we wrote past the old end of the file, then the file has increased in size
203
        if (this._position > origLength) {
4✔
204
            this._length = this._position;
1✔
205
        }
206
        return bytes;
4✔
207
    }
208

209
    // #endregion
210
}
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