Giter Site home page Giter Site logo

bl's Introduction

bl (BufferList)

Build Status

A Node.js Buffer list collector, reader and streamer thingy.

NPM

bl is a storage object for collections of Node Buffers, exposing them with the main Buffer readable API. Also works as a duplex stream so you can collect buffers from a stream that emits them and emit buffers to a stream that consumes them!

The original buffers are kept intact and copies are only done as necessary. Any reads that require the use of a single original buffer will return a slice of that buffer only (which references the same memory as the original buffer). Reads that span buffers perform concatenation as required and return the results transparently.

const { BufferList } = require('bl')

const bl = new BufferList()
bl.append(Buffer.from('abcd'))
bl.append(Buffer.from('efg'))
bl.append('hi')                     // bl will also accept & convert Strings
bl.append(Buffer.from('j'))
bl.append(Buffer.from([ 0x3, 0x4 ]))

console.log(bl.length) // 12

console.log(bl.slice(0, 10).toString('ascii')) // 'abcdefghij'
console.log(bl.slice(3, 10).toString('ascii')) // 'defghij'
console.log(bl.slice(3, 6).toString('ascii'))  // 'def'
console.log(bl.slice(3, 8).toString('ascii'))  // 'defgh'
console.log(bl.slice(5, 10).toString('ascii')) // 'fghij'

console.log(bl.indexOf('def')) // 3
console.log(bl.indexOf('asdf')) // -1

// or just use toString!
console.log(bl.toString())               // 'abcdefghij\u0003\u0004'
console.log(bl.toString('ascii', 3, 8))  // 'defgh'
console.log(bl.toString('ascii', 5, 10)) // 'fghij'

// other standard Buffer readables
console.log(bl.readUInt16BE(10)) // 0x0304
console.log(bl.readUInt16LE(10)) // 0x0403

Give it a callback in the constructor and use it just like concat-stream:

const { BufferListStream } = require('bl')
const fs = require('fs')

fs.createReadStream('README.md')
  .pipe(BufferListStream((err, data) => { // note 'new' isn't strictly required
    // `data` is a complete Buffer object containing the full data
    console.log(data.toString())
  }))

Note that when you use the callback method like this, the resulting data parameter is a concatenation of all Buffer objects in the list. If you want to avoid the overhead of this concatenation (in cases of extreme performance consciousness), then avoid the callback method and just listen to 'end' instead, like a standard Stream.

Or to fetch a URL using hyperquest (should work with request and even plain Node http too!):

const hyperquest = require('hyperquest')
const { BufferListStream } = require('bl')

const url = 'https://raw.github.com/rvagg/bl/master/README.md'

hyperquest(url).pipe(BufferListStream((err, data) => {
  console.log(data.toString())
}))

Or, use it as a readable stream to recompose a list of Buffers to an output source:

const { BufferListStream } = require('bl')
const fs = require('fs')

var bl = new BufferListStream()
bl.append(Buffer.from('abcd'))
bl.append(Buffer.from('efg'))
bl.append(Buffer.from('hi'))
bl.append(Buffer.from('j'))

bl.pipe(fs.createWriteStream('gibberish.txt'))

API


new BufferList([ Buffer | Buffer array | BufferList | BufferList array | String ])

No arguments are required for the constructor, but you can initialise the list by passing in a single Buffer object or an array of Buffer objects.

new is not strictly required, if you don't instantiate a new object, it will be done automatically for you so you can create a new instance simply with:

const { BufferList } = require('bl')
const bl = BufferList()

// equivalent to:

const { BufferList } = require('bl')
const bl = new BufferList()

BufferList.isBufferList(obj)

Determines if the passed object is a BufferList. It will return true if the passed object is an instance of BufferList or BufferListStream and false otherwise.

N.B. this won't return true for BufferList or BufferListStream instances created by versions of this library before this static method was added.


bl.length

Get the length of the list in bytes. This is the sum of the lengths of all of the buffers contained in the list, minus any initial offset for a semi-consumed buffer at the beginning. Should accurately represent the total number of bytes that can be read from the list.


bl.append(Buffer | Buffer array | BufferList | BufferList array | String)

append(buffer) adds an additional buffer or BufferList to the internal list. this is returned so it can be chained.


bl.get(index)

get() will return the byte at the specified index.


bl.indexOf(value[, byteOffset][, encoding])

get() will return the byte at the specified index. indexOf() method returns the first index at which a given element can be found in the BufferList, or -1 if it is not present.


bl.slice([ start, [ end ] ])

slice() returns a new Buffer object containing the bytes within the range specified. Both start and end are optional and will default to the beginning and end of the list respectively.

If the requested range spans a single internal buffer then a slice of that buffer will be returned which shares the original memory range of that Buffer. If the range spans multiple buffers then copy operations will likely occur to give you a uniform Buffer.


bl.shallowSlice([ start, [ end ] ])

shallowSlice() returns a new BufferList object containing the bytes within the range specified. Both start and end are optional and will default to the beginning and end of the list respectively.

No copies will be performed. All buffers in the result share memory with the original list.


bl.copy(dest, [ destStart, [ srcStart [, srcEnd ] ] ])

copy() copies the content of the list in the dest buffer, starting from destStart and containing the bytes within the range specified with srcStart to srcEnd. destStart, start and end are optional and will default to the beginning of the dest buffer, and the beginning and end of the list respectively.


bl.duplicate()

duplicate() performs a shallow-copy of the list. The internal Buffers remains the same, so if you change the underlying Buffers, the change will be reflected in both the original and the duplicate. This method is needed if you want to call consume() or pipe() and still keep the original list.Example:

var bl = new BufferListStream()

bl.append('hello')
bl.append(' world')
bl.append('\n')

bl.duplicate().pipe(process.stdout, { end: false })

console.log(bl.toString())

bl.consume(bytes)

consume() will shift bytes off the start of the list. The number of bytes consumed don't need to line up with the sizes of the internal Buffers—initial offsets will be calculated accordingly in order to give you a consistent view of the data.


bl.toString([encoding, [ start, [ end ]]])

toString() will return a string representation of the buffer. The optional start and end arguments are passed on to slice(), while the encoding is passed on to toString() of the resulting Buffer. See the Buffer#toString() documentation for more information.


bl.readDoubleBE(), bl.readDoubleLE(), bl.readFloatBE(), bl.readFloatLE(), bl.readBigInt64BE(), bl.readBigInt64LE(), bl.readBigUInt64BE(), bl.readBigUInt64LE(), bl.readInt32BE(), bl.readInt32LE(), bl.readUInt32BE(), bl.readUInt32LE(), bl.readInt16BE(), bl.readInt16LE(), bl.readUInt16BE(), bl.readUInt16LE(), bl.readInt8(), bl.readUInt8()

All of the standard byte-reading methods of the Buffer interface are implemented and will operate across internal Buffer boundaries transparently.

See the Buffer documentation for how these work.


new BufferListStream([ callback | Buffer | Buffer array | BufferList | BufferList array | String ])

BufferListStream is a Node Duplex Stream, so it can be read from and written to like a standard Node stream. You can also pipe() to and from a BufferListStream instance.

The constructor takes an optional callback, if supplied, the callback will be called with an error argument followed by a reference to the bl instance, when bl.end() is called (i.e. from a piped stream). This is a convenient method of collecting the entire contents of a stream, particularly when the stream is chunky, such as a network stream.

Normally, no arguments are required for the constructor, but you can initialise the list by passing in a single Buffer object or an array of Buffer object.

new is not strictly required, if you don't instantiate a new object, it will be done automatically for you so you can create a new instance simply with:

const { BufferListStream } = require('bl')
const bl = BufferListStream()

// equivalent to:

const { BufferListStream } = require('bl')
const bl = new BufferListStream()

N.B. For backwards compatibility reasons, BufferListStream is the default export when you require('bl'):

const { BufferListStream } = require('bl')
// equivalent to:
const BufferListStream = require('bl')

Contributors

bl is brought to you by the following hackers:

License & copyright

Copyright (c) 2013-2019 bl contributors (listed above).

bl is licensed under the MIT license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details.

bl's People

Contributors

achingbrain avatar alanshaw avatar alexandercerutti avatar annitya avatar benallfree avatar ctusch avatar dependabot[bot] avatar edwardbetts avatar feross avatar forbeslindesay avatar geloescht avatar heri16 avatar hughsk avatar hugomrdias avatar jacobheun avatar jcrugzz avatar jhaenchen avatar jolg42 avatar kyle-johnson avatar laduke avatar markatwood avatar mcollina avatar nicolashenry avatar nwoltman avatar piranna avatar reconbot avatar rvagg avatar semantic-release-bot avatar shinnn avatar wtgtybhertgeghgtwtg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bl's Issues

Buffer.isBuffer(bl()) !== true

I don't know how possible this is without a change in core but if you're implementing the entire API for buffers then you should be able to effective mask as that type.

Feature request: shallow slice method, that returns another BufferList

I use bl in electron to parse individual JPEG frames from a MJPEG stream. Unfortunately, performance is sluggish. I traced this to the slice() operation that I do in order to combine all chunks that belong to one frame. Since it copies all the buffers into a new buffer, this is quite inefficient.
However, I would be content with an array of buffers instead of one big new buffer. This would only require two buffers to be copied, namely the one at the end and the one at the beginning of the slice (fewer if boundaries of the buffers coincide with those of the slice). The rest of the buffers could be a shallow copy, similar to what duplicate() does.
BTW, is there an official way to just get an array of all buffers inside a BufferList?

Feature request: "Give me a buffer of any length"

So I am using a third party library which returns your BufferList. My own code has an existing "buffering" mechanism which largely reproduces the functions of BufferList— I have a readBytes(dst:Uint8Array, bytesMax:number) which abstracts over several types of data stream. I am now adding BufferLists as a stream that can be read from.

So what I want to work with my code is some way to get "some uint8array" from a BufferList. Your docs say there is slice, but they also say:

If the requested range spans a single internal buffer then a slice of that buffer will be returned which shares the original memory range of that Buffer. If the range spans multiple buffers then copy operations will likely occur to give you a uniform Buffer.

What I want is the longest possible buffer which does not involve copying. slice() can return a buffer without copying if I request a slice of the size of the first internal buffer, but it doesn't give me a way of asking what the internal buffer sizes are.

Expected behavior: There should be a readBuffer or readArbitraryBuffer which pops off the first internal buffer and returns it. The documented semantics could be something like "returns the longest buffer which can currently be returned without copying". This wording would allow for future refactoring or for cases where the user has already read several bytes out of an internal buffer (which means a slice() of the remainder of the internal buffer is needed).

replace inherits with class

looking to reduce our sup dependencies, are you willing to accept a PR that switches the code space to newer class \w extend?

[documentation] - let's address the previous vulnerability issue

hi all,

There was a "Uninitialized Memory Exposure" vulnerability disclosed in bl, which I suspect was patched up since. However, it would be nice to address this to reassure people it really has been fixed. I don't see any issues raised about it nor anything mentioned in the readme about it. It would be nice to have this clarified in the readme. Just a simple mention that it has been fixed since would be enough.

Although I hope it has been fixed already, hasn't it?

cheers,
Roy

Bring in @types/bl?

Any interest in bringing @types/bl into this repo?

The DT version is out of date and I made a PR to freshen it. But if you are interested, I'll make the PR here and the DT package can then be retired. Thoughts?

possible vulnerability in bl 4.1.0

auditjs ossi scan from https://github.com/sonatype-nexus-community/auditjs reports vulnerability:

[22/217] - pkg:npm/[email protected] - 1 vulnerability found!
  Vulnerability Title:  CWE-200: Information Exposure
  ID:  01fa430e-ebce-4504-bcbd-1a58bd22b168
  Description:  An information exposure is the intentional or unintentional disclosure of information to an actor that is not explicitly authorized to have access to that information.
  CVSS Score:  4.3
  CVSS Vector:  CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
  Reference:  https://ossindex.sonatype.org/vuln/01fa430e-ebce-4504-bcbd-1a58bd22b168?component-type=npm&component-name=bl&utm_source=auditjs&utm_medium=integration&utm_content=4.0.23

note: npm audit does not report any issues.

Update to latest readable-stream version

Right now I'm having trouble deploying mqttjs to react-native because I need to manually delete the readable-stream module from the node_modules folder for this lib in order to have it use the version installed at the root of my project.

It would be nice if this module updated to the latest readable-stream in order to get all the fixes included as part of that.

how to write to a file ?

Hi,

How do you write the data to a file ?

I did try some things (trail and error)

// https://github.com/mcollina/msgpack5
function createMsgPack(jsonData) {
    let bl = encode(jsonData) // bl is your lib.

    if (decode(bl) !== jsonData) {
        console.log("Cannot encode to msgPack")
        return false
    }

   // write the encode data to a file.
   // fs.createWriteStream('file.mp')
}

Thanks !

BufferList.js as independent module

Hello,

In order to write a protocol parser with chunky behaviors I am searching for an efficient BufferList implementation that keep the original buffers intact and that copies things only when it is necessary. https://github.com/rvagg/bl/blob/master/BufferList.js would be perfect for that but I understand that since bl v1.0, in order to use it, I also need to import readable-stream via package.json which is not necessary in some cases.

Would you consider publishing BufferList.js as an independent package for those who do not need the streaming aspect of it ? I think that could also create an easier migration path for the packages that depend on old versions (mentioned in #52)

What do you think would be the best way forward ?

Compare/equal

Any plans/wants for comparing buffer lists without having to convert to buffer and then compare?

Republish without npm-shrinkwrap.

The currently published version of bl has an npm-shrinkwrap file, while this file is not committed in the repo.

Plus, that file was created with an older version of bl (v0.8.2):

{
  "name": "bl",
  "version": "0.8.2",
  "dependencies": {
    "readable-stream": {
      "version": "1.0.26-4",
      "from": "readable-stream@~1.0.26",
      "dependencies": {
        "core-util-is": {
          "version": "1.0.1",
          "from": "core-util-is@~1.0.0"
        },
        "isarray": {
          "version": "0.0.1",
          "from": "[email protected]"
        },
        "string_decoder": {
          "version": "0.10.25-1",
          "from": "string_decoder@~0.10.x"
        },
        "inherits": {
          "version": "2.0.1",
          "from": "inherits@~2.0.1"
        }
      }
    }
  }
}

Can you please publish without npm-shrinkwrap? or at least update it (the issue came along because I was bundling two different versions of readable-stream with browserify).

bl without readable-stream

I'm not using any of the streaming facilities. It would be nice to separate the streamy bits from the listy bits so I can keep readable-stream out of my browser bundle.

Would you accept a PR 🙏? I was thinking keep it backwards compatible, just allow me to require('bl/core') or something to get just the version without readable-stream. Any opinions on the implementation?

Maybe something like:

bl.js

const BufferListCore = require('./core')
function BufferList () { /* just the streamy bits */ }
// BufferList.prototype....etc.
Object.assign(BufferList.prototype, BufferListCore.prototype)
util.inherits(BufferList, DuplexStream)
module.exports = BufferList

core.js

function BufferListCore () { /* just the core listy bits from bl.js */ }
// BufferListCore.prototype....etc.
module.exports = BufferListCore

Add option to not implicitly end when there is nothing to read()

I have a use case, where I create a BufferList that is piped to a processor.

I want to be able to setup the bl.pipe(processor) before I even start to feed data into the bufferlist. When I try that, the bufferlist immediately ends.

Failing example:

const Stream = require('stream');
const BufferList = require('bl');

const bl = new BufferList();
const processor = new BufferList((err, buffer) => {    // Can be any writable

    console.log('result:', buffer.toString());
});

bl.pipe(processor);

setImmediate(() => {

    bl.append('hello');
    bl.end();
});

what about piping multiple times?

Hi - I wrote streamsink before I knew that bl existed. Now that I see it solves the same problem I want to switch to using bl instead.

However, there's one thing that confuses me. Once I have a bl instance, and I have piped a stream into it, now I want to keep it around in memory and pipe it to many outputs. It looks like I'm supposed to call bl.pipe(...) but really it makes more sense if it was something like bl.createReadStream().pipe(...) since each reader should have an independent cursor.

For example, here's how streamsink implements it: https://github.com/andrewrk/node-streamsink/blob/master/index.js#L17

What do you think about that?

Here's my use case: https://github.com/andrewrk/connect-static/blob/master/index.js#L85

Is this something bl wants to solve or should I remove the deprecation notice on streamsink and continue using it?

TypeError: bl.pipe is not a function!

Hi!
I'm probably just tired and doing something goofy

var { BufferList } = require('bl')
var bl = BufferList()

bl.append('hello')
bl.append(' world')
bl.append('\n')

bl.pipe(
  process.stdout,
  { end: false }
)

(one of the readme examples ^)

~/s/doot ❯❯❯ node -v                                                        ⏎
v10.16.0
~/s/doot ❯❯❯ node index.js
/Users/travis/src/doot/index.js:9
bl.pipe(
   ^

TypeError: bl.pipe is not a function
    at Object.<anonymous> (/Users/travis/src/doot/index.js:9:4)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)
    at startup (internal/bootstrap/node.js:283:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)
~/s/doot ❯❯❯ npm ls bl                                                      ⏎
/Users/travis/src/doot
└── [email protected]

edit:
Ah, Just a doc thing
It should be
const { BufferListStream } = require('bl') or
const BufferList = require('bl')
not var { BufferList } = require('bl')

BufferList object check method

Exposing a static method called isBufferList to check whether the list can be helpful since Buffer also has isBuffer. Do you think it would be helpful?

Move away from using instanceof

We're starting to use bl more in Libp2p. I've been hitting issues with instanceof causing problems with .append because the buffer being appended is coming from a different version of the module, which causes an empty BufferList of the same size to get appended.

Adding isBufferList (#48) and changing append and indexOf to use that would help mitigate this, or the checks could be changed to just do Duck Typing.

Happy to submit a PR for either of these approaches.

is it possible to actually get errs?

as far as I can see from bl source, theres no possibility the err in the bl callback in will ever be anything truthy:

stream.pipe(bl(function(err) {
  // no point even checking err, will always be undefined or null
}))

I understand leaving it in so it's compatible with functions expecting an err param, but. Perhaps we could do something useful with it, e.g. if the stream piping to bl emits an error, the callback will be fired with an err param:

stream.pipe(bl(function(err) {
   if (err) console.log('whoops!', err)
}))

stream.emit('error', new Error('damn')) // => 'whoops! Error: damn'

Perhaps not, just might want to put something in the documentation mentioning that a truthy err isn't actually possible so people don't bother going about trying to handle them. I'll happily add a PR for such a thing if this is actually true.

Your docs could be friendlier to web devs

Hi, I found myself using your BufferList today. I was initially very confused because the docs mostly assume node.js and I am developing for browser. I needed a Uint8Array but I could not figure out how to get one. A friend explained that it is very simple to get a uint8array out of a node buffer but I did not know that coming in.

Because your library does work in browser, maybe it would be a good idea to add a sentence or two explaining how to convert to and from a Buffer from a uint8array/ArrayBuffer, or something like "to get typed array types like Uint8Array, read here" with a link to the relevant section from the Node documentation. Thanks.

The automated release is failing 🚨

🚨 The automated release from the master branch failed. 🚨

I recommend you give this issue a high priority, so other packages depending on you can benefit from your bug fixes and new features again.

You can find below the list of errors reported by semantic-release. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can fix this 💪.

Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it.

Once all the errors are resolved, semantic-release will release your package the next time you push a commit to the master branch. You can also manually restart the failed CI job that runs semantic-release.

If you are not sure how to resolve this, here are some links that can help you:

If those don’t help, or if this issue is reporting something you think isn’t right, you can always ask the humans behind semantic-release.


No npm token specified.

An npm token must be created and set in the NPM_TOKEN environment variable on your CI environment.

Please make sure to create an npm token and to set it in the NPM_TOKEN environment variable on your CI environment. The token must allow to publish to the registry https://registry.npmjs.org/.


Good luck with your project ✨

Your semantic-release bot 📦🚀

Broken on node v5.6.0

bl tests are failing on node v5.6.0 because of nodejs/node#4951. What is failing is appending multiple buffer list within themselves: https://github.com/rvagg/bl/blob/master/test/test.js#L79-L94.

I am not sure we were relying on some broken functionality of core (not checking if arguments were buffers). In fact, the only thing of Buffer is relying on is a copy method.

Not sure what is the best fix for this, either on node or here.

Also reported here: moscajs/mosca#412.

cc @rvagg

indexOf()

I've got some buffer stream parsing that looks for delimiters. Buffer#indexOf() makes this pretty fast, and combined with bl I can search new data for delimiters and slice off data as needed with minimal copying. Eventually I need to look for multibyte delimiters and it seems I'll have to manually loop over every byte in a buffer list in javascript to accomplish my goal.

It looks like this was attempted before #30 and it looks like it had a similar approach. My question is would it be cheaper to concatenate the buffers and search with the native indexOf for multi byte searches, or to iterate everything in js?

Does "consume" free up memory ?

I want to use bl to help be buffer a large http stream to an async/callback designed api.
So i want to pipe the http stream into bl and read using shallowSlice/slice API.

My http stream will be of 2Gbs, so i want to destroy data as i read thourh theme.
Will the consume API help me here - i.e. free the memory as i'm seeking (it it's the case, 'ill PR the documentation accordingly)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.