npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@bitpatty/buffered-file-reader

v2.1.1

Published

A NodeJS library for reading a file in buffered chunks

Downloads

4

Readme

Buffered File Reader

A NodeJS library for reading a file in buffered chunks, allowing you to await the processing of a chunk before reading the next one.

Usage

The library by default exports a function that instantiates a BufferedFileReader and returns its generator function. The base skeleton for use within an application could look as follows:

import createReader from '@bitpatty/buffered-file-reader';

// Create the reader instance for the target file
const reader = createReader('path-to-your-file');

for (
  let chunk = await reader.next();
  !chunk.done;
  chunk = await reader.next()
) {
  // Do something with chunk.value.data
}

Chunk Value

The chunk value is passed in the format below - whereas Buffer is a NodeJS Buffer. The typing of the iterator and its result can also be imported from the library as follows: import { Iterator, IteratorResult } from '@bitpatty/buffered-file-reader'. Note: There is a builtin IteratorResult type in NodeJS, make sure you import it from the library if you intend to use it.

type IteratorResult = {
  /**
   * The state of the chunk cursor for this data buffer.
   *
   * Note: Indexing starts at 0
   */
  chunkCursor: {
    /**
     * The cursor position at the start of where the initial chunk
     * of the current buffer was read.
     *
     * This number is inclusive, meaning that the byte at this
     * position has been read into the current data buffer.
     */
    start: number;

    /**
     * The cursor position at the end of where the last chunk of
     * the current buffer was read.
     *
     * This number is exclusive, meaning the the byte at this
     * position has not been read into the current data buffer.
     */
    end: number;
  };

  /**
   * Peeks at the next data buffer following the result of this
   * iteration without moving the cursor forward.
   *
   * @returns  The next data buffer
   */
  peekNext: () => Promise<Omit<IteratorResult, 'peekNext'> | null>;

  /**
   * The data in the current chunk
   */
  data: Buffer;
};

End of file

To know whether the reader reached the end of the file (no more to read), you can either check whether the value is null or whether the done flag is set:

const chunkA = await reader.next();
if (chunkA.value == null) console.log('done');

// .. or ..
const chunkA = await reader.next();
if (chunkA.done) console.log('done');

Exiting early

Note: If you don't read the whole file you should always call the return function to close the open handles.

If you want to stop further reads you can call return on the reader:

import createReader from '@bitpatty/buffered-file-reader';

// Create the reader instance for the target file
const reader = createReader('path-to-your-file');

// ...

reader.return(null);

Configuration Options

The following options can be passed as second argument when instantiating the reader:

{
  // The offset (in Bytes) from which to start reading the file
  //
  // If set to a number greater than 0, that number of bytes
  // will be skipped when reading the first chunk.
  //
  // Defaults to `0`
  startOffset: 123;

  // The default chunk size of the reader.
  //
  // If provided, the specified number of bytes is read on each
  // read operation. If no separator is configured, this will
  // also be the size of the buffer returned on each iteration.
  //
  // Note that when the reader reaches the end of the file,
  // the buffer size may be less than the chunk size.
  //
  // Defaults to `100`
  chunkSize: 100;

  //
  // Whether to fail if the file is modified while its being
  // processed by the file reader.
  //
  // If set to `true`, the file will be tracked for changes
  // by `watchFile`. If any changes are done to the file
  // the following read will throw an error.
  //
  // See: https://nodejs.org/docs/latest/api/fs.html#fs_fs_watchfile_filename_options_listener
  //
  // Defaults to `true`.
  //
  throwOnFileModification: boolean;

  // The interval of file modification checks in milliseconds.
  //
  // The interval specifies at which rate the file stats should
  // be checked for any modifications.
  //
  // Note that the actual detection frequency is still relying
  // on the NodeJS file watcher.
  //
  // Defaults to `1000`
  //
  fileModificationPollInterval: number;

  // The byte pattern to identify the end of a section.
  //
  // If provided, the reader continues to read bytes into the buffer
  // until it encounters the separator and continues reading from
  // the end of the separator during the next iteration.
  //
  // This is especially useful when dealing with text files with
  // a delimiter (such as newlines). Note that the chunk size
  // configuration still applies for read operations. However,
  // the returned buffer size may be lower or higher than the
  // configured size, depending on where it encounters the
  // separator pattern.
  //
  // Defaults to `undefined`
  separator: new Uint8Array([0x01, 0x02, 0x03]);

  // Whether the encountered separator should be trimmed from
  // The returned buffer. This configuration is ignored if no
  // separator is specified.
  //
  // If this is set to true the separator will be removed from
  // the end of the buffer before being returned as result of
  // the current iteration, else it is kept in the buffer.
  //
  // E.g. The file 0x12345678 with the separator 0x56 will return
  //      0x1234 if this is set to `true`, else it will return
  //      0x123456.
  //
  // Defaults to `false`
  trimSeparator: true;
}

Separators

The library provides a set of separators signatures which you can provide in the separator configuration (see example below). Note that you can always define your own separator by passing an Uint8Array.

import createReader, { Separator } '@bitpatty/buffered-file-reader'

// Create the reader instance for the target file
const reader = createReader('path-to-your-file', {
  separator: Separator.CRLF
});

The following separators are available within the library itself:

const Separator = {
  /**
   * Matches the carriage return character `\r`
   */
  CR: new Uint8Array([0x0d]),
  /**
   * Matches the newline character `\n`
   */
  LF: new Uint8Array([0x0a]),
  /**
   * Matches the carriage return newline characters `\r\n`
   */
  CRLF: new Uint8Array([0x0d, 0x0a]),
  /**
   * Matches the NULL terminator `\0`
   */
  NULL_TERMINATOR: new Uint8Array([0x00]),
};

Examples

This section provides a few examples on how the library can be used.

Reading a text file line by line

The following snippet reads a text file containing lines separated with a newline character \n.

Note that since the separator is a new line character (\n) and you choose to trim the separator a potential carriage return (\r) is not trimmed.

// my-file.txt:
// lorem ipsum
// dolor sit amet

import createReader, { Separator } from '@bitpatty/buffered-file-reader';

const reader = createReader('my-file.txt', {
  separator: Separator.LF,
  trimSeparator: true,
});

const line1 = await reader.next();
const line2 = await reader.next();
const line3 = await reader.next();

console.log(line1.value.data.toString()); // lorem ipsum
console.log(line2.value.data.toString()); // dolor sit amet

Reading a binary file byte by byte

The following snippet reads a binary file byte-by-byte

// my-file.bin:
// 0x0011

import createReader from '@bitpatty/buffered-file-reader';

const reader = createReader('my-file.txt', { chunkSize: 1 });

const byte1 = await reader.next();
const byte2 = await reader.next();
const byte3 = await reader.next();
const byte4 = await reader.next();

console.log(byte1.value.data); // <Buffer 00>
console.log(byte2.value.data); // <Buffer 11>
console.log(byte3.value.data); // <null>
console.log(byte4.value.data); // <undefined>