Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(buffer): add Node.js compatible Buffer module #11084

Closed
wants to merge 9 commits into from

Conversation

sgtcoolguy
Copy link
Contributor

JIRA: https://jira.appcelerator.org/browse/TIMOB-26573

Description:
This adds a Node-compatible buffer module in JS.

In Node, a Buffer represents a byte array (specifically an unsigned byte array). In Titanium, Ti.Buffer represents the exact same concept. In both cases these APIs were created because JS engines did not yet have the concept, but have long ago added TypedArrays (or more specifically in this case, a Uint8Array).

This shim is intended to expose Node's API, which is backed by a Ti.Buffer internally. It's important to note that this could result in performance issues as a result, because to support array style index setter/getters I also had to wrap instances in a JS Proxy. So to read/write specific bytes you end up going through a JS Proxy, then traversing our JS/native binding, grabbing the value natively, returning it across the JS/native binding layer and then back out the JS Proxy. So clearly on a per-byte level this isn't ideal. But obviously a number of operations are pure JS, and there are methods that support a little bit of "bulk" inserts to help avoid the penalties.

I think long term we should investigate possibly doing a few things:

  • When not explicitly wrapping an existing Ti.Buffer, use a faster implementation that is backed by Uint8Array a la Browserify: https://github.com/feross/buffer/blob/master/index.js
  • Support accepting Uint8Array for any Titanium APIs that take in a Ti.Buffer
  • Possibly deprecate Ti.Buffer afterwards

Caveats
This does not include:

  • .buffer property
  • #lastIndexOf()
  • #read/writeBigInt method variants (we do not yet support BigInt)
  • #transcode()
  • .INSPECT_MAX_BYTES
  • .kMaxLength
  • .constants.MAX_LENGTH
  • .constants.MAX_STRING_LENGTH

The last 5 methods/properties are special in that they're exposed by the module returned by require('buffer') but not buffer instances or the global Buffer object.

I'm sure there are also other edge cases creating a Buffer from an ArrayBuffer/SharedArrayBuffer/Uint8Array that do not yet work.

@build
Copy link
Contributor

build commented Jul 26, 2019

Fails
🚫 Tests have failed, see below for more information.
Warnings
⚠️

Commit 9500dc55a76d9cb54fca2f0f250c185f48c5b059 has a message "Update common/Resources/ti.internal/extensions/node/buffer.js

Co-Authored-By: Jan Vennemann jan.vennemann@gmx.net" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit dbd07bbff551e658657590fa55ade6b26dbab64b has a message "Update common/Resources/ti.internal/extensions/node/buffer.js

Co-Authored-By: Jan Vennemann jan.vennemann@gmx.net" giving 2 errors:

  • subject may not be empty
  • type may not be empty
⚠️

Commit d90810046f821134e65bf033ecf27b7fe1ea40b4 has a message "Update common/Resources/ti.internal/extensions/node/buffer.js

Co-Authored-By: Jan Vennemann jan.vennemann@gmx.net" giving 2 errors:

  • subject may not be empty
  • type may not be empty
Messages
📖

💾 Here's the generated SDK zipfile.

📖 ❌ 1 tests have failed There are 1 tests failing and 472 skipped out of 4328 total tests.
📖

🚨 This PR has one or more commits with warnings/errors for commit messages not matching our configuration. You may want to squash merge this PR and edit the message to match our conventions, or ask the original developer to modify their history.

Tests:

ClassnameNameTimeError
android.emulator.Titanium.Network.HTTPClientclearCookieUnaffectedCheck60.004
Error: timeout of 60000ms exceeded
at Titanium.<anonymous> (/ti-mocha.js:4326:18)

Generated by 🚫 dangerJS against cdf8506

Copy link
Contributor

@janvennemann janvennemann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a first pass and this looks really solid! Just a few minor notes.

I'll continue to test this in my WebSocket/socket.io module.

common/Resources/ti.internal/extensions/node/buffer.js Outdated Show resolved Hide resolved
common/Resources/ti.internal/extensions/node/buffer.js Outdated Show resolved Hide resolved
const fillChar = bufToFillWith._tiBuffer[i % fillBufLength];
console.log(`going to use ${fillChar} to fill at index ${i + offset}`);
this._tiBuffer[i + offset] = fillChar;
console.log(`here's what it got set to: ${this._tiBuffer[i + offset]}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove debug logs

common/Resources/ti.internal/extensions/node/buffer.js Outdated Show resolved Hide resolved
} else if (encoding === 'hex') {
return Buffer.from(stringToHexBytes(value));
}
const type = getTiCodecCharset(encoding);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throw an uknown encoding error here if the codec map returns undefined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

return newBuffer(value);
}
}
throw new TypeError('The \'value\' argument must be of type: \'string\', \'Array\', \'Buffer\'');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That method checks using typeof versus a single type. So I can't use it here...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, damn! For some reason i thought that function includes more type checks than it actually does.


it('handles 2-byte utf-8 character fill', () => {
const b = Buffer.allocUnsafe(3).fill('\u0222');
should(b[0]).eql(0xc8); // FIXME: get expected undefined to equal 200
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this still throw? Do we need to skip this and the following test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outdated FIXME. I removed this and the others in this file

should(buf.readUInt16BE(1).toString(16)).eql('3456');
});

// it('returns signed value coerced to unsigned', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a note why this is commented out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got lazy and didn't want to write the test properly :)

}, RangeError);
});

// it('throws when trying to write values out of range', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, can you add a fixme note and explain why this is commented out?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add it back and get it right. I got a bit fatigued of writing all these tests!

sgtcoolguy and others added 4 commits July 31, 2019 14:19
Co-Authored-By: Jan Vennemann <jan.vennemann@gmx.net>
Co-Authored-By: Jan Vennemann <jan.vennemann@gmx.net>
Co-Authored-By: Jan Vennemann <jan.vennemann@gmx.net>
@sgtcoolguy
Copy link
Contributor Author

@janvennemann Ok, I pushed changes to address the review comments.


Buffer.poolSize = 8192;

export default Buffer;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The export is not compatible with the node buffer module this way. The core module exports it as Buffer instead of default.

Some third-party node modules do the following for example:

var Buffer = require('buffer').Buffer;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, pushing commits to fix this now

@sgtcoolguy sgtcoolguy closed this Aug 6, 2019
@sgtcoolguy sgtcoolguy deleted the node-Buffer branch August 6, 2019 15:09
@sgtcoolguy
Copy link
Contributor Author

squash merged manually

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants