@fuman/io 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/_utils.cjs +23 -0
- package/_utils.d.ts +2 -0
- package/_utils.js +23 -0
- package/bits/index.d.ts +2 -0
- package/bits/reader.cjs +122 -0
- package/bits/reader.d.ts +15 -0
- package/bits/reader.js +122 -0
- package/bits/utils.cjs +49 -0
- package/bits/utils.d.ts +12 -0
- package/bits/utils.js +49 -0
- package/buf-reader.cjs +60 -0
- package/buf-reader.d.ts +9 -0
- package/buf-reader.js +60 -0
- package/bytes.cjs +114 -0
- package/bytes.d.ts +24 -0
- package/bytes.js +114 -0
- package/codec/delimiter.cjs +38 -0
- package/codec/delimiter.d.ts +22 -0
- package/codec/delimiter.js +38 -0
- package/codec/index.d.ts +6 -0
- package/codec/length-delimited.cjs +42 -0
- package/codec/length-delimited.d.ts +14 -0
- package/codec/length-delimited.js +42 -0
- package/codec/reader.cjs +51 -0
- package/codec/reader.d.ts +12 -0
- package/codec/reader.js +51 -0
- package/codec/text-delimiter.cjs +24 -0
- package/codec/text-delimiter.d.ts +11 -0
- package/codec/text-delimiter.js +24 -0
- package/codec/types.d.ts +16 -0
- package/codec/writer.cjs +24 -0
- package/codec/writer.d.ts +10 -0
- package/codec/writer.js +24 -0
- package/errors.cjs +9 -0
- package/errors.d.ts +9 -0
- package/errors.js +9 -0
- package/index.cjs +39 -0
- package/index.d.ts +12 -0
- package/index.js +39 -0
- package/package.json +29 -0
- package/read/adapters.cjs +90 -0
- package/read/adapters.d.ts +4 -0
- package/read/adapters.js +90 -0
- package/read/async/index.cjs +5 -0
- package/read/async/index.d.ts +1 -0
- package/read/async/index.js +5 -0
- package/read/async/strings.cjs +40 -0
- package/read/async/strings.d.ts +3 -0
- package/read/async/strings.js +40 -0
- package/read/index.cjs +40 -0
- package/read/index.d.ts +4 -0
- package/read/index.js +40 -0
- package/read/numbers.cjs +181 -0
- package/read/numbers.d.ts +27 -0
- package/read/numbers.js +181 -0
- package/read/strings.cjs +67 -0
- package/read/strings.d.ts +9 -0
- package/read/strings.js +67 -0
- package/reader-with-final.cjs +69 -0
- package/reader-with-final.d.ts +13 -0
- package/reader-with-final.js +69 -0
- package/streams.cjs +15 -0
- package/streams.d.ts +1 -0
- package/streams.js +15 -0
- package/types.d.ts +66 -0
- package/write/adapters.cjs +38 -0
- package/write/adapters.d.ts +4 -0
- package/write/adapters.js +38 -0
- package/write/index.cjs +37 -0
- package/write/index.d.ts +3 -0
- package/write/index.js +37 -0
- package/write/numbers.cjs +311 -0
- package/write/numbers.d.ts +27 -0
- package/write/numbers.js +311 -0
- package/write/pipe.cjs +11 -0
- package/write/pipe.d.ts +2 -0
- package/write/pipe.js +11 -0
- package/write/strings.cjs +38 -0
- package/write/strings.d.ts +6 -0
- package/write/strings.js +38 -0
package/bytes.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { IReadable, ISyncReadable, ISyncWritable, IWritable } from './types.js';
|
|
2
|
+
export declare class Bytes implements IReadable, IWritable, ISyncReadable, ISyncWritable {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(buf: Uint8Array);
|
|
5
|
+
static alloc(capacity?: number): Bytes;
|
|
6
|
+
static from(data: Uint8Array): Bytes;
|
|
7
|
+
/** Total number of bytes in the underlying buffer */
|
|
8
|
+
get capacity(): number;
|
|
9
|
+
/** Number of bytes available to be read */
|
|
10
|
+
get available(): number;
|
|
11
|
+
/** Number of bytes written */
|
|
12
|
+
get written(): number;
|
|
13
|
+
readSync(bytes: number): Uint8Array;
|
|
14
|
+
read(into: Uint8Array): Promise<number>;
|
|
15
|
+
writeSync(size: number): Uint8Array;
|
|
16
|
+
disposeWriteSync(written?: number): void;
|
|
17
|
+
write(bytes: Uint8Array): Promise<void>;
|
|
18
|
+
result(): Uint8Array;
|
|
19
|
+
/** Reclaim memory by only keeping the yet-unread data */
|
|
20
|
+
reclaim(): void;
|
|
21
|
+
/** Mark last n bytes as unread */
|
|
22
|
+
rewind(n: number): void;
|
|
23
|
+
reset(): void;
|
|
24
|
+
}
|
package/bytes.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { u8 } from "@fuman/utils";
|
|
2
|
+
import { nextPowerOfTwo } from "./_utils.js";
|
|
3
|
+
class Bytes {
|
|
4
|
+
/** Underlying buffer */
|
|
5
|
+
#buffer;
|
|
6
|
+
/** Position of the write cursor (should always be >= {@link #readPos}) */
|
|
7
|
+
#writePos = 0;
|
|
8
|
+
/** Position of the read cursor */
|
|
9
|
+
#readPos = 0;
|
|
10
|
+
#preferredCapacity;
|
|
11
|
+
constructor(buf) {
|
|
12
|
+
this.#buffer = buf;
|
|
13
|
+
this.#preferredCapacity = buf.length;
|
|
14
|
+
}
|
|
15
|
+
static alloc(capacity = 1024 * 16) {
|
|
16
|
+
const bytes = new Bytes(u8.alloc(capacity));
|
|
17
|
+
return bytes;
|
|
18
|
+
}
|
|
19
|
+
static from(data) {
|
|
20
|
+
const bytes = new Bytes(data);
|
|
21
|
+
bytes.#writePos = data.length;
|
|
22
|
+
return bytes;
|
|
23
|
+
}
|
|
24
|
+
/** Total number of bytes in the underlying buffer */
|
|
25
|
+
get capacity() {
|
|
26
|
+
return this.#buffer.byteLength;
|
|
27
|
+
}
|
|
28
|
+
/** Number of bytes available to be read */
|
|
29
|
+
get available() {
|
|
30
|
+
return this.#writePos - this.#readPos;
|
|
31
|
+
}
|
|
32
|
+
/** Number of bytes written */
|
|
33
|
+
get written() {
|
|
34
|
+
return this.#writePos;
|
|
35
|
+
}
|
|
36
|
+
#sharedRead = new Uint8Array(1);
|
|
37
|
+
readSync(bytes) {
|
|
38
|
+
if (this.#readPos >= this.#writePos) {
|
|
39
|
+
return u8.empty;
|
|
40
|
+
}
|
|
41
|
+
if (bytes === 1) {
|
|
42
|
+
this.#sharedRead[0] = this.#buffer[this.#readPos++];
|
|
43
|
+
return this.#sharedRead;
|
|
44
|
+
}
|
|
45
|
+
const end = Math.min(this.#writePos, this.#readPos + bytes);
|
|
46
|
+
const result = this.#buffer.subarray(this.#readPos, end);
|
|
47
|
+
this.#readPos = end;
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
async read(into) {
|
|
51
|
+
const size = Math.min(into.length, this.#writePos - this.#readPos);
|
|
52
|
+
into.set(this.#buffer.subarray(this.#readPos, this.#readPos + size));
|
|
53
|
+
this.#readPos += size;
|
|
54
|
+
return size;
|
|
55
|
+
}
|
|
56
|
+
#lastWriteSize = 0;
|
|
57
|
+
writeSync(size) {
|
|
58
|
+
this.#lastWriteSize = size;
|
|
59
|
+
const newPos = this.#writePos + size;
|
|
60
|
+
if (newPos > this.#buffer.length) {
|
|
61
|
+
const newBuffer = u8.alloc(nextPowerOfTwo(newPos));
|
|
62
|
+
newBuffer.set(this.#buffer);
|
|
63
|
+
this.#buffer = newBuffer;
|
|
64
|
+
}
|
|
65
|
+
const slice = this.#buffer.subarray(this.#writePos, newPos);
|
|
66
|
+
this.#writePos = newPos;
|
|
67
|
+
return slice;
|
|
68
|
+
}
|
|
69
|
+
disposeWriteSync(written) {
|
|
70
|
+
if (written !== void 0) {
|
|
71
|
+
if (written > this.#lastWriteSize) {
|
|
72
|
+
throw new RangeError(`written exceeds last write size: ${written} > ${this.#lastWriteSize}`);
|
|
73
|
+
}
|
|
74
|
+
this.#writePos -= this.#lastWriteSize - written;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async write(bytes) {
|
|
78
|
+
this.writeSync(bytes.length).set(bytes);
|
|
79
|
+
this.disposeWriteSync();
|
|
80
|
+
}
|
|
81
|
+
result() {
|
|
82
|
+
return this.#buffer.subarray(0, this.#writePos);
|
|
83
|
+
}
|
|
84
|
+
/** Reclaim memory by only keeping the yet-unread data */
|
|
85
|
+
reclaim() {
|
|
86
|
+
if (this.#readPos === 0) return;
|
|
87
|
+
const remaining = this.#writePos - this.#readPos;
|
|
88
|
+
if (remaining > 0) {
|
|
89
|
+
if (remaining < this.#preferredCapacity && this.capacity > this.#preferredCapacity) {
|
|
90
|
+
const newBuffer = u8.alloc(this.#preferredCapacity);
|
|
91
|
+
newBuffer.set(this.#buffer.subarray(this.#readPos, this.#writePos));
|
|
92
|
+
this.#buffer = newBuffer;
|
|
93
|
+
} else {
|
|
94
|
+
this.#buffer.copyWithin(0, this.#readPos, this.#writePos);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
this.#writePos = remaining;
|
|
98
|
+
this.#readPos = 0;
|
|
99
|
+
}
|
|
100
|
+
/** Mark last n bytes as unread */
|
|
101
|
+
rewind(n) {
|
|
102
|
+
if (n > this.#readPos) {
|
|
103
|
+
throw new RangeError(`rewind: ${n} > ${this.#readPos}`);
|
|
104
|
+
}
|
|
105
|
+
this.#readPos -= n;
|
|
106
|
+
}
|
|
107
|
+
reset() {
|
|
108
|
+
this.#readPos = 0;
|
|
109
|
+
this.#writePos = 0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
Bytes
|
|
114
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const utils = require("@fuman/utils");
|
|
4
|
+
class DelimiterCodec {
|
|
5
|
+
constructor(delimiter, options) {
|
|
6
|
+
this.delimiter = delimiter;
|
|
7
|
+
this.options = options;
|
|
8
|
+
this.#strategy = options?.strategy ?? "discard";
|
|
9
|
+
}
|
|
10
|
+
#strategy;
|
|
11
|
+
decode(buf, eof) {
|
|
12
|
+
const { delimiter } = this;
|
|
13
|
+
const data = buf.readSync(buf.available);
|
|
14
|
+
if (eof && data.length === 0) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const delimiterIdx = utils.typed.indexOfArray(data, delimiter);
|
|
18
|
+
if (delimiterIdx === -1) {
|
|
19
|
+
if (eof) {
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
22
|
+
buf.rewind(data.length);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
buf.rewind(data.length - delimiterIdx - delimiter.length);
|
|
26
|
+
const frameEnd = this.#strategy === "keep" ? delimiterIdx + delimiter.length : delimiterIdx;
|
|
27
|
+
return data.slice(0, frameEnd);
|
|
28
|
+
}
|
|
29
|
+
encode(data, into) {
|
|
30
|
+
const buf = into.writeSync(data.length + this.delimiter.length);
|
|
31
|
+
buf.set(data);
|
|
32
|
+
buf.set(this.delimiter, data.length);
|
|
33
|
+
into.disposeWriteSync();
|
|
34
|
+
}
|
|
35
|
+
reset() {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.DelimiterCodec = DelimiterCodec;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Bytes } from '../bytes.js';
|
|
2
|
+
import { ISyncWritable } from '../types.js';
|
|
3
|
+
import { IFrameDecoder, IFrameEncoder } from './types.js';
|
|
4
|
+
export interface DelimiterCodecOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Strategy for handling delimiter.
|
|
7
|
+
* - `keep` - delimiter is kept at the end of each frame
|
|
8
|
+
* - `discard` - delimiter is discarded
|
|
9
|
+
*
|
|
10
|
+
* Ignored for encoding (delimiter is always appended after the frame)
|
|
11
|
+
*/
|
|
12
|
+
strategy?: 'keep' | 'discard';
|
|
13
|
+
}
|
|
14
|
+
export declare class DelimiterCodec implements IFrameDecoder, IFrameEncoder {
|
|
15
|
+
#private;
|
|
16
|
+
readonly delimiter: Uint8Array;
|
|
17
|
+
readonly options?: DelimiterCodecOptions | undefined;
|
|
18
|
+
constructor(delimiter: Uint8Array, options?: DelimiterCodecOptions | undefined);
|
|
19
|
+
decode(buf: Bytes, eof: boolean): Uint8Array | null;
|
|
20
|
+
encode(data: Uint8Array, into: ISyncWritable): void;
|
|
21
|
+
reset(): void;
|
|
22
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { typed } from "@fuman/utils";
|
|
2
|
+
class DelimiterCodec {
|
|
3
|
+
constructor(delimiter, options) {
|
|
4
|
+
this.delimiter = delimiter;
|
|
5
|
+
this.options = options;
|
|
6
|
+
this.#strategy = options?.strategy ?? "discard";
|
|
7
|
+
}
|
|
8
|
+
#strategy;
|
|
9
|
+
decode(buf, eof) {
|
|
10
|
+
const { delimiter } = this;
|
|
11
|
+
const data = buf.readSync(buf.available);
|
|
12
|
+
if (eof && data.length === 0) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
const delimiterIdx = typed.indexOfArray(data, delimiter);
|
|
16
|
+
if (delimiterIdx === -1) {
|
|
17
|
+
if (eof) {
|
|
18
|
+
return data;
|
|
19
|
+
}
|
|
20
|
+
buf.rewind(data.length);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
buf.rewind(data.length - delimiterIdx - delimiter.length);
|
|
24
|
+
const frameEnd = this.#strategy === "keep" ? delimiterIdx + delimiter.length : delimiterIdx;
|
|
25
|
+
return data.slice(0, frameEnd);
|
|
26
|
+
}
|
|
27
|
+
encode(data, into) {
|
|
28
|
+
const buf = into.writeSync(data.length + this.delimiter.length);
|
|
29
|
+
buf.set(data);
|
|
30
|
+
buf.set(this.delimiter, data.length);
|
|
31
|
+
into.disposeWriteSync();
|
|
32
|
+
}
|
|
33
|
+
reset() {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
DelimiterCodec
|
|
38
|
+
};
|
package/codec/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const utils = require("@fuman/utils");
|
|
4
|
+
class LengthDelimitedCodec {
|
|
5
|
+
#read;
|
|
6
|
+
#write;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.#read = options.read;
|
|
9
|
+
this.#write = options.write;
|
|
10
|
+
}
|
|
11
|
+
#pendingLength = null;
|
|
12
|
+
decode(buf) {
|
|
13
|
+
if (!this.#read) {
|
|
14
|
+
throw new Error("LengthDelimitedCodec: read function not provided");
|
|
15
|
+
}
|
|
16
|
+
if (this.#pendingLength !== null) {
|
|
17
|
+
const pendingLength = this.#pendingLength;
|
|
18
|
+
if (buf.available < pendingLength) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const data = buf.readSync(pendingLength);
|
|
22
|
+
this.#pendingLength = null;
|
|
23
|
+
return utils.u8.allocWith(data);
|
|
24
|
+
}
|
|
25
|
+
const length = this.#read(buf);
|
|
26
|
+
if (length === null) return null;
|
|
27
|
+
this.#pendingLength = length;
|
|
28
|
+
return this.decode(buf);
|
|
29
|
+
}
|
|
30
|
+
encode(frame, into) {
|
|
31
|
+
if (!this.#write) {
|
|
32
|
+
throw new Error("LengthDelimitedCodec: write function not provided");
|
|
33
|
+
}
|
|
34
|
+
this.#write(into, frame.length);
|
|
35
|
+
into.writeSync(frame.length).set(frame);
|
|
36
|
+
into.disposeWriteSync();
|
|
37
|
+
}
|
|
38
|
+
reset() {
|
|
39
|
+
this.#pendingLength = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.LengthDelimitedCodec = LengthDelimitedCodec;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Bytes } from '../bytes.js';
|
|
2
|
+
import { ISyncWritable } from '../types.js';
|
|
3
|
+
import { IFrameDecoder, IFrameEncoder } from './types.js';
|
|
4
|
+
export interface LengthDelimitedCodecOptions {
|
|
5
|
+
read?: (r: Bytes) => number | null;
|
|
6
|
+
write?: (w: ISyncWritable, n: number) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare class LengthDelimitedCodec implements IFrameDecoder, IFrameEncoder {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(options: LengthDelimitedCodecOptions);
|
|
11
|
+
decode(buf: Bytes): Uint8Array | null;
|
|
12
|
+
encode(frame: Uint8Array, into: ISyncWritable): void;
|
|
13
|
+
reset(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { u8 } from "@fuman/utils";
|
|
2
|
+
class LengthDelimitedCodec {
|
|
3
|
+
#read;
|
|
4
|
+
#write;
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.#read = options.read;
|
|
7
|
+
this.#write = options.write;
|
|
8
|
+
}
|
|
9
|
+
#pendingLength = null;
|
|
10
|
+
decode(buf) {
|
|
11
|
+
if (!this.#read) {
|
|
12
|
+
throw new Error("LengthDelimitedCodec: read function not provided");
|
|
13
|
+
}
|
|
14
|
+
if (this.#pendingLength !== null) {
|
|
15
|
+
const pendingLength = this.#pendingLength;
|
|
16
|
+
if (buf.available < pendingLength) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const data = buf.readSync(pendingLength);
|
|
20
|
+
this.#pendingLength = null;
|
|
21
|
+
return u8.allocWith(data);
|
|
22
|
+
}
|
|
23
|
+
const length = this.#read(buf);
|
|
24
|
+
if (length === null) return null;
|
|
25
|
+
this.#pendingLength = length;
|
|
26
|
+
return this.decode(buf);
|
|
27
|
+
}
|
|
28
|
+
encode(frame, into) {
|
|
29
|
+
if (!this.#write) {
|
|
30
|
+
throw new Error("LengthDelimitedCodec: write function not provided");
|
|
31
|
+
}
|
|
32
|
+
this.#write(into, frame.length);
|
|
33
|
+
into.writeSync(frame.length).set(frame);
|
|
34
|
+
into.disposeWriteSync();
|
|
35
|
+
}
|
|
36
|
+
reset() {
|
|
37
|
+
this.#pendingLength = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
LengthDelimitedCodec
|
|
42
|
+
};
|
package/codec/reader.cjs
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const bytes = require("../bytes.cjs");
|
|
4
|
+
class FramedReader {
|
|
5
|
+
#readable;
|
|
6
|
+
#decoder;
|
|
7
|
+
#buffer;
|
|
8
|
+
#readChunkSize;
|
|
9
|
+
#eof = false;
|
|
10
|
+
#canDecode = false;
|
|
11
|
+
constructor(readable, decoder, options) {
|
|
12
|
+
this.#readable = readable;
|
|
13
|
+
this.#decoder = decoder;
|
|
14
|
+
this.#buffer = bytes.Bytes.alloc(options?.initialBufferSize ?? 1024 * 16);
|
|
15
|
+
this.#readChunkSize = options?.readChunkSize ?? 1024 * 16;
|
|
16
|
+
}
|
|
17
|
+
async read() {
|
|
18
|
+
while (true) {
|
|
19
|
+
if (this.#canDecode) {
|
|
20
|
+
const frame = await this.#decoder.decode(this.#buffer, this.#eof);
|
|
21
|
+
this.#buffer.reclaim();
|
|
22
|
+
if (frame !== null) {
|
|
23
|
+
return frame;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (this.#eof) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const into = this.#buffer.writeSync(this.#readChunkSize);
|
|
30
|
+
const read = await this.#readable.read(into);
|
|
31
|
+
this.#buffer.disposeWriteSync(read);
|
|
32
|
+
if (read === 0) {
|
|
33
|
+
this.#eof = true;
|
|
34
|
+
} else {
|
|
35
|
+
this.#canDecode = true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
[Symbol.asyncIterator]() {
|
|
40
|
+
return {
|
|
41
|
+
next: async () => {
|
|
42
|
+
const res = await this.read();
|
|
43
|
+
if (res === null) {
|
|
44
|
+
return { done: true, value: void 0 };
|
|
45
|
+
}
|
|
46
|
+
return { done: false, value: res };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.FramedReader = FramedReader;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IReadable } from '../types.js';
|
|
2
|
+
import { IFrameDecoder } from './types.js';
|
|
3
|
+
export interface FramedReaderOptions {
|
|
4
|
+
initialBufferSize?: number;
|
|
5
|
+
readChunkSize?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class FramedReader<Frame> {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(readable: IReadable, decoder: IFrameDecoder<Frame>, options?: FramedReaderOptions);
|
|
10
|
+
read(): Promise<Frame | null>;
|
|
11
|
+
[Symbol.asyncIterator](): AsyncIterator<Frame>;
|
|
12
|
+
}
|
package/codec/reader.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Bytes } from "../bytes.js";
|
|
2
|
+
class FramedReader {
|
|
3
|
+
#readable;
|
|
4
|
+
#decoder;
|
|
5
|
+
#buffer;
|
|
6
|
+
#readChunkSize;
|
|
7
|
+
#eof = false;
|
|
8
|
+
#canDecode = false;
|
|
9
|
+
constructor(readable, decoder, options) {
|
|
10
|
+
this.#readable = readable;
|
|
11
|
+
this.#decoder = decoder;
|
|
12
|
+
this.#buffer = Bytes.alloc(options?.initialBufferSize ?? 1024 * 16);
|
|
13
|
+
this.#readChunkSize = options?.readChunkSize ?? 1024 * 16;
|
|
14
|
+
}
|
|
15
|
+
async read() {
|
|
16
|
+
while (true) {
|
|
17
|
+
if (this.#canDecode) {
|
|
18
|
+
const frame = await this.#decoder.decode(this.#buffer, this.#eof);
|
|
19
|
+
this.#buffer.reclaim();
|
|
20
|
+
if (frame !== null) {
|
|
21
|
+
return frame;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (this.#eof) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const into = this.#buffer.writeSync(this.#readChunkSize);
|
|
28
|
+
const read = await this.#readable.read(into);
|
|
29
|
+
this.#buffer.disposeWriteSync(read);
|
|
30
|
+
if (read === 0) {
|
|
31
|
+
this.#eof = true;
|
|
32
|
+
} else {
|
|
33
|
+
this.#canDecode = true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
[Symbol.asyncIterator]() {
|
|
38
|
+
return {
|
|
39
|
+
next: async () => {
|
|
40
|
+
const res = await this.read();
|
|
41
|
+
if (res === null) {
|
|
42
|
+
return { done: true, value: void 0 };
|
|
43
|
+
}
|
|
44
|
+
return { done: false, value: res };
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
FramedReader
|
|
51
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const utils = require("@fuman/utils");
|
|
4
|
+
const delimiter = require("./delimiter.cjs");
|
|
5
|
+
class TextDelimiterCodec {
|
|
6
|
+
#inner;
|
|
7
|
+
constructor(delimiter$1, options) {
|
|
8
|
+
if (typeof delimiter$1 === "string") {
|
|
9
|
+
delimiter$1 = utils.utf8.encoder.encode(delimiter$1);
|
|
10
|
+
}
|
|
11
|
+
this.#inner = new delimiter.DelimiterCodec(delimiter$1, options);
|
|
12
|
+
}
|
|
13
|
+
decode(buf, eof) {
|
|
14
|
+
const data = this.#inner.decode(buf, eof);
|
|
15
|
+
return data === null ? null : utils.utf8.decoder.decode(data);
|
|
16
|
+
}
|
|
17
|
+
encode(data, into) {
|
|
18
|
+
this.#inner.encode(utils.utf8.encoder.encode(data), into);
|
|
19
|
+
}
|
|
20
|
+
reset() {
|
|
21
|
+
this.#inner.reset();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.TextDelimiterCodec = TextDelimiterCodec;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Bytes } from '../bytes.js';
|
|
2
|
+
import { ISyncWritable } from '../types.js';
|
|
3
|
+
import { DelimiterCodecOptions } from './delimiter.js';
|
|
4
|
+
import { IFrameDecoder, IFrameEncoder } from './types.js';
|
|
5
|
+
export declare class TextDelimiterCodec implements IFrameDecoder<string>, IFrameEncoder<string> {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(delimiter: Uint8Array | string, options?: DelimiterCodecOptions);
|
|
8
|
+
decode(buf: Bytes, eof: boolean): string | null;
|
|
9
|
+
encode(data: string, into: ISyncWritable): void;
|
|
10
|
+
reset(): void;
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { utf8 } from "@fuman/utils";
|
|
2
|
+
import { DelimiterCodec } from "./delimiter.js";
|
|
3
|
+
class TextDelimiterCodec {
|
|
4
|
+
#inner;
|
|
5
|
+
constructor(delimiter, options) {
|
|
6
|
+
if (typeof delimiter === "string") {
|
|
7
|
+
delimiter = utf8.encoder.encode(delimiter);
|
|
8
|
+
}
|
|
9
|
+
this.#inner = new DelimiterCodec(delimiter, options);
|
|
10
|
+
}
|
|
11
|
+
decode(buf, eof) {
|
|
12
|
+
const data = this.#inner.decode(buf, eof);
|
|
13
|
+
return data === null ? null : utf8.decoder.decode(data);
|
|
14
|
+
}
|
|
15
|
+
encode(data, into) {
|
|
16
|
+
this.#inner.encode(utf8.encoder.encode(data), into);
|
|
17
|
+
}
|
|
18
|
+
reset() {
|
|
19
|
+
this.#inner.reset();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
TextDelimiterCodec
|
|
24
|
+
};
|
package/codec/types.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { MaybePromise } from '@fuman/utils';
|
|
2
|
+
import { Bytes } from '../bytes.js';
|
|
3
|
+
import { ISyncWritable } from '../types.js';
|
|
4
|
+
export interface IFrameDecoder<Frame = Uint8Array> {
|
|
5
|
+
/**
|
|
6
|
+
* Decode a frame from a buffer
|
|
7
|
+
*
|
|
8
|
+
* > **Important**: When returning byte arrays, make sure that the returned array is **not**
|
|
9
|
+
* > a view into the original buffer, as the underlying buffer may get invalidated
|
|
10
|
+
*/
|
|
11
|
+
decode: (buf: Bytes, eof: boolean) => MaybePromise<Frame | null>;
|
|
12
|
+
}
|
|
13
|
+
export interface IFrameEncoder<Frame = Uint8Array> {
|
|
14
|
+
encode: (frame: Frame, into: ISyncWritable) => MaybePromise<void>;
|
|
15
|
+
reset: () => void;
|
|
16
|
+
}
|
package/codec/writer.cjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const bytes = require("../bytes.cjs");
|
|
4
|
+
class FramedWriter {
|
|
5
|
+
#writable;
|
|
6
|
+
#encoder;
|
|
7
|
+
#buffer;
|
|
8
|
+
#highWaterMark;
|
|
9
|
+
constructor(writable, encoder, options) {
|
|
10
|
+
this.#writable = writable;
|
|
11
|
+
this.#encoder = encoder;
|
|
12
|
+
this.#highWaterMark = options?.initialBufferSize ?? 1024;
|
|
13
|
+
this.#buffer = bytes.Bytes.alloc(this.#highWaterMark);
|
|
14
|
+
}
|
|
15
|
+
async write(frame) {
|
|
16
|
+
await this.#encoder.encode(frame, this.#buffer);
|
|
17
|
+
const buffer = this.#buffer.result();
|
|
18
|
+
if (buffer.length > 0) {
|
|
19
|
+
this.#buffer.reset();
|
|
20
|
+
await this.#writable.write(buffer);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.FramedWriter = FramedWriter;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IWritable } from '../types.js';
|
|
2
|
+
import { IFrameEncoder } from './types.js';
|
|
3
|
+
export interface FramedWriterOptions {
|
|
4
|
+
initialBufferSize?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare class FramedWriter<Frame = Uint8Array> {
|
|
7
|
+
#private;
|
|
8
|
+
constructor(writable: IWritable, encoder: IFrameEncoder<Frame>, options?: FramedWriterOptions);
|
|
9
|
+
write(frame: Frame): Promise<void>;
|
|
10
|
+
}
|
package/codec/writer.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Bytes } from "../bytes.js";
|
|
2
|
+
class FramedWriter {
|
|
3
|
+
#writable;
|
|
4
|
+
#encoder;
|
|
5
|
+
#buffer;
|
|
6
|
+
#highWaterMark;
|
|
7
|
+
constructor(writable, encoder, options) {
|
|
8
|
+
this.#writable = writable;
|
|
9
|
+
this.#encoder = encoder;
|
|
10
|
+
this.#highWaterMark = options?.initialBufferSize ?? 1024;
|
|
11
|
+
this.#buffer = Bytes.alloc(this.#highWaterMark);
|
|
12
|
+
}
|
|
13
|
+
async write(frame) {
|
|
14
|
+
await this.#encoder.encode(frame, this.#buffer);
|
|
15
|
+
const buffer = this.#buffer.result();
|
|
16
|
+
if (buffer.length > 0) {
|
|
17
|
+
this.#buffer.reset();
|
|
18
|
+
await this.#writable.write(buffer);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
FramedWriter
|
|
24
|
+
};
|
package/errors.cjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
class PartialReadError extends RangeError {
|
|
4
|
+
constructor(part, expectedLength) {
|
|
5
|
+
super(`expected to read ${expectedLength} bytes, but only ${part.length} are available`);
|
|
6
|
+
this.part = part;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.PartialReadError = PartialReadError;
|
package/errors.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error thrown when trying to read more bytes than available.
|
|
3
|
+
*
|
|
4
|
+
* The part that was read is available in the `part` property.
|
|
5
|
+
*/
|
|
6
|
+
export declare class PartialReadError extends RangeError {
|
|
7
|
+
readonly part: Uint8Array;
|
|
8
|
+
constructor(part: Uint8Array, expectedLength: number);
|
|
9
|
+
}
|
package/errors.js
ADDED
package/index.cjs
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const index = require("./read/index.cjs");
|
|
4
|
+
const index$1 = require("./write/index.cjs");
|
|
5
|
+
const bufReader = require("./buf-reader.cjs");
|
|
6
|
+
const bytes = require("./bytes.cjs");
|
|
7
|
+
const errors = require("./errors.cjs");
|
|
8
|
+
const adapters = require("./read/adapters.cjs");
|
|
9
|
+
const readerWithFinal = require("./reader-with-final.cjs");
|
|
10
|
+
const adapters$1 = require("./write/adapters.cjs");
|
|
11
|
+
const reader = require("./bits/reader.cjs");
|
|
12
|
+
const utils = require("./bits/utils.cjs");
|
|
13
|
+
const delimiter = require("./codec/delimiter.cjs");
|
|
14
|
+
const lengthDelimited = require("./codec/length-delimited.cjs");
|
|
15
|
+
const reader$1 = require("./codec/reader.cjs");
|
|
16
|
+
const textDelimiter = require("./codec/text-delimiter.cjs");
|
|
17
|
+
const writer = require("./codec/writer.cjs");
|
|
18
|
+
exports.read = index;
|
|
19
|
+
exports.write = index$1;
|
|
20
|
+
exports.BufReader = bufReader.BufReader;
|
|
21
|
+
exports.Bytes = bytes.Bytes;
|
|
22
|
+
exports.PartialReadError = errors.PartialReadError;
|
|
23
|
+
exports.fumanReadableToWeb = adapters.fumanReadableToWeb;
|
|
24
|
+
exports.fumanSyncReadableToAsync = adapters.fumanSyncReadableToAsync;
|
|
25
|
+
exports.webReadableToFuman = adapters.webReadableToFuman;
|
|
26
|
+
exports.ReaderWithFinal = readerWithFinal.ReaderWithFinal;
|
|
27
|
+
exports.fumanSyncWritableToAsync = adapters$1.fumanSyncWritableToAsync;
|
|
28
|
+
exports.fumanWritableToWeb = adapters$1.fumanWritableToWeb;
|
|
29
|
+
exports.webWritableToFuman = adapters$1.webWritableToFuman;
|
|
30
|
+
exports.BitReader = reader.BitReader;
|
|
31
|
+
exports.reverse8Bits = utils.reverse8Bits;
|
|
32
|
+
exports.reverseBits = utils.reverseBits;
|
|
33
|
+
exports.reverseBitsAll = utils.reverseBitsAll;
|
|
34
|
+
exports.reverseBitsBig = utils.reverseBitsBig;
|
|
35
|
+
exports.DelimiterCodec = delimiter.DelimiterCodec;
|
|
36
|
+
exports.LengthDelimitedCodec = lengthDelimited.LengthDelimitedCodec;
|
|
37
|
+
exports.FramedReader = reader$1.FramedReader;
|
|
38
|
+
exports.TextDelimiterCodec = textDelimiter.TextDelimiterCodec;
|
|
39
|
+
exports.FramedWriter = writer.FramedWriter;
|