2024-01-01 21:11:32 +00:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2023-03-18 12:36:00 +00:00
|
|
|
// This module is browser compatible.
|
|
|
|
|
2024-04-29 02:57:30 +00:00
|
|
|
import { copy } from "@std/bytes/copy";
|
2024-01-14 11:18:36 +00:00
|
|
|
import type { Reader, ReaderSync, Writer, WriterSync } from "./types.ts";
|
2021-03-22 21:45:35 +00:00
|
|
|
|
|
|
|
// MIN_READ is the minimum ArrayBuffer size passed to a read call by
|
|
|
|
// buffer.ReadFrom. As long as the Buffer has at least MIN_READ bytes beyond
|
|
|
|
// what is required to hold the contents of r, readFrom() will not grow the
|
|
|
|
// underlying buffer.
|
|
|
|
const MIN_READ = 32 * 1024;
|
|
|
|
const MAX_SIZE = 2 ** 32 - 2;
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* A variable-sized buffer of bytes with `read()` and `write()` methods.
|
2021-03-22 21:45:35 +00:00
|
|
|
*
|
|
|
|
* Buffer is almost always used with some I/O like files and sockets. It allows
|
|
|
|
* one to buffer up a download from a socket. Buffer grows and shrinks as
|
|
|
|
* necessary.
|
|
|
|
*
|
|
|
|
* Buffer is NOT the same thing as Node's Buffer. Node's Buffer was created in
|
|
|
|
* 2009 before JavaScript had the concept of ArrayBuffers. It's simply a
|
|
|
|
* non-standard ArrayBuffer.
|
|
|
|
*
|
|
|
|
* ArrayBuffer is a fixed memory allocation. Buffer is implemented on top of
|
|
|
|
* ArrayBuffer.
|
|
|
|
*
|
2024-01-31 22:19:46 +00:00
|
|
|
* Based on {@link https://golang.org/pkg/bytes/#Buffer | Go Buffer}.
|
2024-08-08 14:20:43 +00:00
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* await buf.write(new TextEncoder().encode("Hello, "));
|
|
|
|
* await buf.write(new TextEncoder().encode("world!"));
|
|
|
|
*
|
|
|
|
* const data = new Uint8Array(13);
|
|
|
|
* await buf.read(data);
|
|
|
|
*
|
|
|
|
* assertEquals(new TextDecoder().decode(data), "Hello, world!");
|
|
|
|
* ```
|
2023-09-20 11:20:00 +00:00
|
|
|
*/
|
2024-01-14 11:18:36 +00:00
|
|
|
export class Buffer implements Writer, WriterSync, Reader, ReaderSync {
|
2021-03-22 21:45:35 +00:00
|
|
|
#buf: Uint8Array; // contents are the bytes buf[off : len(buf)]
|
|
|
|
#off = 0; // read at buf[off], write at buf[buf.byteLength]
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Constructs a new instance with the specified {@linkcode ArrayBuffer} as its
|
|
|
|
* initial contents.
|
|
|
|
*
|
|
|
|
* @param ab The ArrayBuffer to use as the initial contents of the buffer.
|
|
|
|
*/
|
2021-04-28 06:05:09 +00:00
|
|
|
constructor(ab?: ArrayBufferLike | ArrayLike<number>) {
|
|
|
|
this.#buf = ab === undefined ? new Uint8Array(0) : new Uint8Array(ab);
|
2021-03-22 21:45:35 +00:00
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Returns a slice holding the unread portion of the buffer.
|
2021-03-22 21:45:35 +00:00
|
|
|
*
|
|
|
|
* The slice is valid for use only until the next buffer modification (that
|
|
|
|
* is, only until the next call to a method like `read()`, `write()`,
|
|
|
|
* `reset()`, or `truncate()`). If `options.copy` is false the slice aliases the buffer content at
|
|
|
|
* least until the next buffer modification, so immediate changes to the
|
|
|
|
* slice will affect the result of future reads.
|
2024-08-08 14:20:43 +00:00
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* await buf.write(new TextEncoder().encode("Hello, world!"));
|
|
|
|
*
|
|
|
|
* const slice = buf.bytes();
|
|
|
|
* assertEquals(new TextDecoder().decode(slice), "Hello, world!");
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param options The options for the slice.
|
|
|
|
* @returns A slice holding the unread portion of the buffer.
|
2021-03-22 21:45:35 +00:00
|
|
|
*/
|
|
|
|
bytes(options = { copy: true }): Uint8Array {
|
|
|
|
if (options.copy === false) return this.#buf.subarray(this.#off);
|
|
|
|
return this.#buf.slice(this.#off);
|
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Returns whether the unread portion of the buffer is empty.
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* assertEquals(buf.empty(), true);
|
|
|
|
* await buf.write(new TextEncoder().encode("Hello, world!"));
|
|
|
|
* assertEquals(buf.empty(), false);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @returns `true` if the unread portion of the buffer is empty, `false`
|
|
|
|
* otherwise.
|
|
|
|
*/
|
2021-03-22 21:45:35 +00:00
|
|
|
empty(): boolean {
|
|
|
|
return this.#buf.byteLength <= this.#off;
|
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* A read only number of bytes of the unread portion of the buffer.
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* await buf.write(new TextEncoder().encode("Hello, world!"));
|
|
|
|
*
|
|
|
|
* assertEquals(buf.length, 13);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @returns The number of bytes of the unread portion of the buffer.
|
|
|
|
*/
|
2021-03-22 21:45:35 +00:00
|
|
|
get length(): number {
|
|
|
|
return this.#buf.byteLength - this.#off;
|
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* The read only capacity of the buffer's underlying byte slice, that is,
|
|
|
|
* the total space allocated for the buffer's data.
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* assertEquals(buf.capacity, 0);
|
|
|
|
* await buf.write(new TextEncoder().encode("Hello, world!"));
|
|
|
|
* assertEquals(buf.capacity, 13);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @returns The capacity of the buffer.
|
|
|
|
*/
|
2021-03-22 21:45:35 +00:00
|
|
|
get capacity(): number {
|
|
|
|
return this.#buf.buffer.byteLength;
|
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Discards all but the first `n` unread bytes from the buffer but
|
2021-03-22 21:45:35 +00:00
|
|
|
* continues to use the same allocated storage. It throws if `n` is
|
2024-08-08 14:20:43 +00:00
|
|
|
* negative or greater than the length of the buffer.
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* await buf.write(new TextEncoder().encode("Hello, world!"));
|
|
|
|
* buf.truncate(6);
|
|
|
|
* assertEquals(buf.length, 6);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param n The number of bytes to keep.
|
|
|
|
*/
|
2022-08-24 01:21:57 +00:00
|
|
|
truncate(n: number) {
|
2021-03-22 21:45:35 +00:00
|
|
|
if (n === 0) {
|
|
|
|
this.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (n < 0 || n > this.length) {
|
2024-08-28 22:55:24 +00:00
|
|
|
throw new Error("Buffer truncation out of range");
|
2021-03-22 21:45:35 +00:00
|
|
|
}
|
|
|
|
this.#reslice(this.#off + n);
|
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Resets the contents
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* await buf.write(new TextEncoder().encode("Hello, world!"));
|
|
|
|
* buf.reset();
|
|
|
|
* assertEquals(buf.length, 0);
|
|
|
|
* ```
|
|
|
|
*/
|
2022-08-24 01:21:57 +00:00
|
|
|
reset() {
|
2021-03-22 21:45:35 +00:00
|
|
|
this.#reslice(0);
|
|
|
|
this.#off = 0;
|
|
|
|
}
|
|
|
|
|
2021-05-30 00:46:38 +00:00
|
|
|
#tryGrowByReslice(n: number) {
|
2021-03-22 21:45:35 +00:00
|
|
|
const l = this.#buf.byteLength;
|
|
|
|
if (n <= this.capacity - l) {
|
|
|
|
this.#reslice(l + n);
|
|
|
|
return l;
|
|
|
|
}
|
|
|
|
return -1;
|
2021-05-30 00:46:38 +00:00
|
|
|
}
|
2021-03-22 21:45:35 +00:00
|
|
|
|
2021-05-30 00:46:38 +00:00
|
|
|
#reslice(len: number) {
|
2024-06-05 23:37:24 +00:00
|
|
|
if (len > this.#buf.buffer.byteLength) {
|
|
|
|
throw new RangeError("Length is greater than buffer capacity");
|
|
|
|
}
|
2021-03-22 21:45:35 +00:00
|
|
|
this.#buf = new Uint8Array(this.#buf.buffer, 0, len);
|
2021-05-30 00:46:38 +00:00
|
|
|
}
|
2021-03-22 21:45:35 +00:00
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Reads the next `p.length` bytes from the buffer or until the buffer is
|
2021-03-22 21:45:35 +00:00
|
|
|
* drained. Returns the number of bytes read. If the buffer has no data to
|
2024-08-08 14:20:43 +00:00
|
|
|
* return, the return is EOF (`null`).
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* await buf.write(new TextEncoder().encode("Hello, world!"));
|
|
|
|
*
|
|
|
|
* const data = new Uint8Array(5);
|
|
|
|
* const res = await buf.read(data);
|
|
|
|
*
|
|
|
|
* assertEquals(res, 5);
|
|
|
|
* assertEquals(new TextDecoder().decode(data), "Hello");
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param p The buffer to read data into.
|
|
|
|
* @returns The number of bytes read.
|
|
|
|
*/
|
2021-03-22 21:45:35 +00:00
|
|
|
readSync(p: Uint8Array): number | null {
|
|
|
|
if (this.empty()) {
|
|
|
|
// Buffer is empty, reset to recover space.
|
|
|
|
this.reset();
|
|
|
|
if (p.byteLength === 0) {
|
|
|
|
// this edge case is tested in 'bufferReadEmptyAtEOF' test
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
2021-04-15 08:32:34 +00:00
|
|
|
const nread = copy(this.#buf.subarray(this.#off), p);
|
2021-03-22 21:45:35 +00:00
|
|
|
this.#off += nread;
|
|
|
|
return nread;
|
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Reads the next `p.length` bytes from the buffer or until the buffer is
|
2021-03-22 21:45:35 +00:00
|
|
|
* drained. Resolves to the number of bytes read. If the buffer has no
|
|
|
|
* data to return, resolves to EOF (`null`).
|
|
|
|
*
|
|
|
|
* NOTE: This methods reads bytes synchronously; it's provided for
|
|
|
|
* compatibility with `Reader` interfaces.
|
2024-08-08 14:20:43 +00:00
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* await buf.write(new TextEncoder().encode("Hello, world!"));
|
|
|
|
*
|
|
|
|
* const data = new Uint8Array(5);
|
|
|
|
* const res = await buf.read(data);
|
|
|
|
*
|
|
|
|
* assertEquals(res, 5);
|
|
|
|
* assertEquals(new TextDecoder().decode(data), "Hello");
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param p The buffer to read data into.
|
|
|
|
* @returns The number of bytes read.
|
2021-03-22 21:45:35 +00:00
|
|
|
*/
|
|
|
|
read(p: Uint8Array): Promise<number | null> {
|
|
|
|
const rr = this.readSync(p);
|
|
|
|
return Promise.resolve(rr);
|
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Writes the given data to the buffer.
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* const data = new TextEncoder().encode("Hello, world!");
|
|
|
|
* buf.writeSync(data);
|
|
|
|
*
|
|
|
|
* const slice = buf.bytes();
|
|
|
|
* assertEquals(new TextDecoder().decode(slice), "Hello, world!");
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param p The data to write to the buffer.
|
|
|
|
* @returns The number of bytes written.
|
|
|
|
*/
|
2021-03-22 21:45:35 +00:00
|
|
|
writeSync(p: Uint8Array): number {
|
|
|
|
const m = this.#grow(p.byteLength);
|
2021-04-15 08:32:34 +00:00
|
|
|
return copy(p, this.#buf, m);
|
2021-03-22 21:45:35 +00:00
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Writes the given data to the buffer. Resolves to the number of bytes
|
|
|
|
* written.
|
|
|
|
*
|
|
|
|
* > [!NOTE]
|
|
|
|
* > This methods writes bytes synchronously; it's provided for compatibility
|
|
|
|
* > with the {@linkcode Writer} interface.
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* const data = new TextEncoder().encode("Hello, world!");
|
|
|
|
* await buf.write(data);
|
|
|
|
*
|
|
|
|
* const slice = buf.bytes();
|
|
|
|
* assertEquals(new TextDecoder().decode(slice), "Hello, world!");
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param p The data to write to the buffer.
|
|
|
|
* @returns The number of bytes written.
|
|
|
|
*/
|
2021-03-22 21:45:35 +00:00
|
|
|
write(p: Uint8Array): Promise<number> {
|
|
|
|
const n = this.writeSync(p);
|
|
|
|
return Promise.resolve(n);
|
|
|
|
}
|
|
|
|
|
2021-05-30 00:46:38 +00:00
|
|
|
#grow(n: number) {
|
2021-03-22 21:45:35 +00:00
|
|
|
const m = this.length;
|
|
|
|
// If buffer is empty, reset to recover space.
|
|
|
|
if (m === 0 && this.#off !== 0) {
|
|
|
|
this.reset();
|
|
|
|
}
|
|
|
|
// Fast: Try to grow by means of a reslice.
|
|
|
|
const i = this.#tryGrowByReslice(n);
|
|
|
|
if (i >= 0) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
const c = this.capacity;
|
|
|
|
if (n <= Math.floor(c / 2) - m) {
|
|
|
|
// We can slide things down instead of allocating a new
|
|
|
|
// ArrayBuffer. We only need m+n <= c to slide, but
|
|
|
|
// we instead let capacity get twice as large so we
|
|
|
|
// don't spend all our time copying.
|
2021-04-15 08:32:34 +00:00
|
|
|
copy(this.#buf.subarray(this.#off), this.#buf);
|
2021-03-22 21:45:35 +00:00
|
|
|
} else if (c + n > MAX_SIZE) {
|
2024-08-20 03:27:43 +00:00
|
|
|
throw new Error(
|
|
|
|
`The buffer cannot be grown beyond the maximum size of "${MAX_SIZE}"`,
|
|
|
|
);
|
2021-03-22 21:45:35 +00:00
|
|
|
} else {
|
|
|
|
// Not enough space anywhere, we need to allocate.
|
|
|
|
const buf = new Uint8Array(Math.min(2 * c + n, MAX_SIZE));
|
2021-04-15 08:32:34 +00:00
|
|
|
copy(this.#buf.subarray(this.#off), buf);
|
2021-03-22 21:45:35 +00:00
|
|
|
this.#buf = buf;
|
|
|
|
}
|
|
|
|
// Restore this.#off and len(this.#buf).
|
|
|
|
this.#off = 0;
|
|
|
|
this.#reslice(Math.min(m + n, MAX_SIZE));
|
|
|
|
return m;
|
2021-05-30 00:46:38 +00:00
|
|
|
}
|
2021-03-22 21:45:35 +00:00
|
|
|
|
|
|
|
/** Grows the buffer's capacity, if necessary, to guarantee space for
|
|
|
|
* another `n` bytes. After `.grow(n)`, at least `n` bytes can be written to
|
|
|
|
* the buffer without another allocation. If `n` is negative, `.grow()` will
|
|
|
|
* throw. If the buffer can't grow it will throw an error.
|
|
|
|
*
|
|
|
|
* Based on Go Lang's
|
2024-08-08 14:20:43 +00:00
|
|
|
* {@link https://golang.org/pkg/bytes/#Buffer.Grow | Buffer.Grow}.
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
|
|
|
* buf.grow(10);
|
|
|
|
* assertEquals(buf.capacity, 10);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param n The number of bytes to grow the buffer by.
|
|
|
|
*/
|
2022-08-24 01:21:57 +00:00
|
|
|
grow(n: number) {
|
2021-03-22 21:45:35 +00:00
|
|
|
if (n < 0) {
|
2024-08-28 22:55:24 +00:00
|
|
|
throw new Error("Buffer growth cannot be negative");
|
2021-03-22 21:45:35 +00:00
|
|
|
}
|
|
|
|
const m = this.#grow(n);
|
|
|
|
this.#reslice(m);
|
|
|
|
}
|
|
|
|
|
2024-08-08 14:20:43 +00:00
|
|
|
/**
|
|
|
|
* Reads data from `r` until EOF (`null`) and appends it to the buffer,
|
2021-03-22 21:45:35 +00:00
|
|
|
* growing the buffer as needed. It resolves to the number of bytes read.
|
|
|
|
* If the buffer becomes too large, `.readFrom()` will reject with an error.
|
|
|
|
*
|
|
|
|
* Based on Go Lang's
|
2024-08-08 14:20:43 +00:00
|
|
|
* {@link https://golang.org/pkg/bytes/#Buffer.ReadFrom | Buffer.ReadFrom}.
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
2024-09-26 03:25:37 +00:00
|
|
|
* const r = new Buffer(new TextEncoder().encode("Hello, world!"));
|
2024-08-08 14:20:43 +00:00
|
|
|
* const n = await buf.readFrom(r);
|
|
|
|
*
|
|
|
|
* assertEquals(n, 13);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param r The reader to read from.
|
|
|
|
* @returns The number of bytes read.
|
|
|
|
*/
|
2021-05-14 00:26:00 +00:00
|
|
|
async readFrom(r: Reader): Promise<number> {
|
2021-03-22 21:45:35 +00:00
|
|
|
let n = 0;
|
|
|
|
const tmp = new Uint8Array(MIN_READ);
|
|
|
|
while (true) {
|
|
|
|
const shouldGrow = this.capacity - this.length < MIN_READ;
|
|
|
|
// read into tmp buffer if there's not enough room
|
|
|
|
// otherwise read directly into the internal buffer
|
|
|
|
const buf = shouldGrow
|
|
|
|
? tmp
|
|
|
|
: new Uint8Array(this.#buf.buffer, this.length);
|
|
|
|
|
|
|
|
const nread = await r.read(buf);
|
|
|
|
if (nread === null) {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write will grow if needed
|
|
|
|
if (shouldGrow) this.writeSync(buf.subarray(0, nread));
|
|
|
|
else this.#reslice(this.length + nread);
|
|
|
|
|
|
|
|
n += nread;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Reads data from `r` until EOF (`null`) and appends it to the buffer,
|
|
|
|
* growing the buffer as needed. It returns the number of bytes read. If the
|
|
|
|
* buffer becomes too large, `.readFromSync()` will throw an error.
|
|
|
|
*
|
|
|
|
* Based on Go Lang's
|
2024-08-08 14:20:43 +00:00
|
|
|
* {@link https://golang.org/pkg/bytes/#Buffer.ReadFrom | Buffer.ReadFrom}.
|
|
|
|
*
|
|
|
|
* @example Usage
|
|
|
|
* ```ts
|
|
|
|
* import { Buffer } from "@std/io/buffer";
|
|
|
|
* import { assertEquals } from "@std/assert/equals";
|
|
|
|
*
|
|
|
|
* const buf = new Buffer();
|
2024-09-26 03:25:37 +00:00
|
|
|
* const r = new Buffer(new TextEncoder().encode("Hello, world!"));
|
2024-08-08 14:20:43 +00:00
|
|
|
* const n = buf.readFromSync(r);
|
|
|
|
*
|
|
|
|
* assertEquals(n, 13);
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param r The reader to read from.
|
|
|
|
* @returns The number of bytes read.
|
|
|
|
*/
|
2021-05-14 00:26:00 +00:00
|
|
|
readFromSync(r: ReaderSync): number {
|
2021-03-22 21:45:35 +00:00
|
|
|
let n = 0;
|
|
|
|
const tmp = new Uint8Array(MIN_READ);
|
|
|
|
while (true) {
|
|
|
|
const shouldGrow = this.capacity - this.length < MIN_READ;
|
|
|
|
// read into tmp buffer if there's not enough room
|
|
|
|
// otherwise read directly into the internal buffer
|
|
|
|
const buf = shouldGrow
|
|
|
|
? tmp
|
|
|
|
: new Uint8Array(this.#buf.buffer, this.length);
|
|
|
|
|
|
|
|
const nread = r.readSync(buf);
|
|
|
|
if (nread === null) {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write will grow if needed
|
|
|
|
if (shouldGrow) this.writeSync(buf.subarray(0, nread));
|
|
|
|
else this.#reslice(this.length + nread);
|
|
|
|
|
|
|
|
n += nread;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|