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

buffer: add {read|write}[U]Int64{BE|LE} methods #15152

Closed
wants to merge 5 commits into from
Closed

buffer: add {read|write}[U]Int64{BE|LE} methods #15152

wants to merge 5 commits into from

Conversation

seishun
Copy link
Contributor

@seishun seishun commented Sep 2, 2017

This is basically a resurrection of @TooTallNate's PR #1750 with a couple differences:

  • read[U]Int64{BE|LE} always returns strings to make the output more predictable.
  • No address() method, and thus no changes to the inspect output for Buffers

I'm not sure how to deal with attribution. @TooTallNate did most of the work here, but I also did significant work rebasing and updating.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines
Affected core subsystem(s)

buffer

@nodejs-github-bot nodejs-github-bot added buffer Issues and PRs related to the buffer subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. labels Sep 2, 2017
@mscdex
Copy link
Contributor

mscdex commented Sep 2, 2017

I'm leaning more towards -1 because of the current lack of support in the language (and thus an inconsistency with the return values for the 64-bit read*() functions). Besides, I believe we already cram 64-bit integers into JS numbers in places in core, so returning a string would be deviating from that. Some end users may not even care about a potential loss in precision (e.g. when implementing a protocol that utilizes a 64-bit field whose values never exceed Number.MAX_SAFE_INTEGER), so they would rather have a number value instead of a string.

However, it is probably best to just wait until something like the BigInt ES proposal gets accepted before adding 64-bit integer support.

@seishun
Copy link
Contributor Author

seishun commented Sep 2, 2017

(edited because the message above was edited)

Besides, I believe we already cram 64-bit integers into JS numbers in places in core, so returning a string would be deviating from that.

Do we do that anywhere where the integer could be greater than Number.MAX_SAFE_INTEGER?

Some end users may not even care about a potential loss in precision (e.g. when implementing a protocol that utilizes a 64-bit field whose values never exceed Number.MAX_SAFE_INTEGER), so they would rather have a number value instead of a string.

They can use + to convert it to a number. The other way around wouldn't work.

Would you be less opposed to returning a number when it's less than Number.MAX_SAFE_INTEGER, as the original PR did?

Also, it is probably best to just wait until something like the BigInt ES proposal gets accepted before adding 64-bit integer support.

That's far in the future.

Besides, with BigInt reading 64-bit integers would still be inconsistent.

@mscdex
Copy link
Contributor

mscdex commented Sep 2, 2017

Besides, I believe we already cram 64-bit integers into a JS number value in places in core.

So?

So there would be an inconsistency in how we deal with 64-bit integer values in core. I don't think that would be a good thing.

Also, it is probably best to just wait until something like the BigInt ES proposal gets accepted before adding 64-bit integer support.

That's far in the future.

Maybe, maybe not. It's already at stage 3. Besides, we've gone this long without 64-bit integer Buffer read() and write() functions, waiting a little longer won't hurt and it's better to implement it in the most appropriate way from the get-go (rather than having to switch from strings/numbers to BigInt later on in a semver-major which will cause unnecessary pain for end users).

@seishun
Copy link
Contributor Author

seishun commented Sep 2, 2017

So there would be an inconsistency in how we deal with 64-bit integer values in core. I don't think that would be a good thing.

Can you give specific examples?

it's better to implement it in the most appropriate way from the get-go

I'm not convinced that using BigInt would be the most appropriate way. A common usecase for 64-bit integers is some kind of an identifier, in which case a primitive is more convenient (and actually more consistent with other read/write functions).

Copy link
Member

@addaleax addaleax left a comment

Choose a reason for hiding this comment

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

I don’t see anything terribly wrong with this, fwiw.

@@ -1722,6 +1722,22 @@ console.log(buf.readInt32LE());
console.log(buf.readInt32LE(1));
```

### buf.readInt64LE(offset[, noAssert])
### buf.readInt64BE(offset[, noAssert])
Copy link
Member

Choose a reason for hiding this comment

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

<!-- YAML
added: replaceme
-->

:)

THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
SPREAD_BUFFER_ARG(args[0], ts_obj);

uint32_t offset = args[1]->Uint32Value();
Copy link
Member

Choose a reason for hiding this comment

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

Can you use the overload that takes a context argument?

Copy link
Contributor Author

@seishun seishun Sep 11, 2017

Choose a reason for hiding this comment

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

@addaleax This is copied from

uint32_t offset = args[1]->Uint32Value();

I could fix it in ReadInt64Generic and make it inconsistent with ReadFloatGeneric, or fix it in both places as part of this PR, or make a separate PR to fix it in ReadFloatGeneric.


* `offset` {integer} Where to start reading. Must satisfy: `0 <= offset <= buf.length - 8`
* `noAssert` {boolean} Skip `offset` validation? **Default:** `false`
* Returns: {integer}
Copy link
Member

Choose a reason for hiding this comment

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

is this accurate?


T val;
if (args[1]->IsNumber()) {
val = args[1]->IntegerValue();
Copy link
Member

Choose a reason for hiding this comment

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

ditto (using the non-deprecated overload taking a context argument)

Copy link
Contributor Author

@seishun seishun Sep 3, 2017

Choose a reason for hiding this comment

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

@addaleax I didn't realize that this overload is deprecated. Is this documented somewhere?

Copy link
Member

Choose a reason for hiding this comment

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

@seishun Yes:

node/deps/v8/include/v8.h

Lines 2297 to 2309 in 94be2b1

V8_WARN_UNUSED_RESULT Maybe<bool> BooleanValue(Local<Context> context) const;
V8_WARN_UNUSED_RESULT Maybe<double> NumberValue(Local<Context> context) const;
V8_WARN_UNUSED_RESULT Maybe<int64_t> IntegerValue(
Local<Context> context) const;
V8_WARN_UNUSED_RESULT Maybe<uint32_t> Uint32Value(
Local<Context> context) const;
V8_WARN_UNUSED_RESULT Maybe<int32_t> Int32Value(Local<Context> context) const;
V8_DEPRECATE_SOON("Use maybe version", bool BooleanValue() const);
V8_DEPRECATE_SOON("Use maybe version", double NumberValue() const);
V8_DEPRECATE_SOON("Use maybe version", int64_t IntegerValue() const);
V8_DEPRECATE_SOON("Use maybe version", uint32_t Uint32Value() const);
V8_DEPRECATE_SOON("Use maybe version", int32_t Int32Value() const);

And also V8 API changes:

Ongoing and Planned Changes

... APIs that are marked as V8_DEPRECATE_SOON will be marked as V8_DEPRECATED in the future. APIs marked as V8_DEPRECATED will usually be removed after one V8 release.

  • Introduction of MaybeLocal<> and Maybe<> APIs: bug
  • ...

@TimothyGu
Copy link
Member

I don't see the necessity of waiting until BigInt. In fact when it's introduced we can add a readBig[U]Int64 to match DataView. IMO returning a string isn't ideal, but the fact that the developer can cast it easily to a number by prefixing a + softens the blow for me.

@jasnell
Copy link
Member

jasnell commented Sep 5, 2017

Just alternatively, the return could be an array similar to that used by process.hrtime() ... which should make calculations faster than string parsing / conversion.

@seishun
Copy link
Contributor Author

seishun commented Sep 7, 2017

@jasnell an array is difficult to convert to either a number or a string, and it would be inconsistent with other read functions which returns primitives, so I'm -1 on that.

Copy link
Member

@tniessen tniessen left a comment

Choose a reason for hiding this comment

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

I wonder which possible use cases this might benefit. Sure, people can read 64 bit values as strings, and can then parse (some of them!) as numbers, but there is not even a guarantee that arithmetic operations with those numbers will be accurate, and there is still no fixed-size arithmetic.

If you decide to return numbers as a decimal representation, that should probably be mentioned in the docs.

format (`writeUInt64BE()` writes big endian, `writeUInt64LE()` writes little
endian). `value` should be a valid unsigned 64-bit integer or a String
representation of one. Behavior is undefined when `value` is anything other than
an unsigned 32-bit integer or a String representation of one.
Copy link
Member

Choose a reason for hiding this comment

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

32 → 64

format (`writeInt64BE()` writes big endian, `writeInt64LE()` writes little
endian). `value` *should* be a valid signed 64-bit integer or a String
representation of one. Behavior is undefined when `value` is anything other than
a signed 32-bit integer or a String representation of one.
Copy link
Member

Choose a reason for hiding this comment

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

32 → 64

@tniessen tniessen added the semver-minor PRs that contain new features and should be released in the next minor version. label Sep 8, 2017
@seishun
Copy link
Contributor Author

seishun commented Sep 8, 2017

@tniessen

I wonder which possible use cases this might benefit. Sure, people can read 64 bit values as strings, and can then parse (some of them!) as numbers, but there is not even a guarantee that arithmetic operations with those numbers will be accurate, and there is still no fixed-size arithmetic.

Not everyone requires arithmetic operations on the values they read or write. As I've mentioned before, 64-bit integers are often used as identifiers, for example SteamID.

@seishun
Copy link
Contributor Author

seishun commented Sep 10, 2017

cc @nodejs/collaborators need more input.

@benjamingr
Copy link
Member

benjamingr commented Sep 11, 2017

@littledan how far is BigInt from V8?

Edit: found https://bugs.chromium.org/p/v8/issues/detail?id=6791

@mcollina
Copy link
Member

IMHO we should wait for BigInt.

@bnoordhuis
Copy link
Member

Sorry, missed that I was asked to review this. I'm also in the "Waiting for Godot^WBigInt" camp; it's at stage 3, implementation work has started, it won't be long (no C programmer pun intended.)

@littledan
Copy link

I'm working on BigInt specifically in order to enable these use cases. Strings seem like a functioning workaround, but I'm wondering what's motivating adding this to core right now at a time when BigInts are actually under development.

@benjamingr
Copy link
Member

@littledan

right now at a time when BigInts are actually under development.

Well, there are several reasons Node.js might do this:

  • There is no guarantee of when this will actually ship.
  • Since it's Stage 3, while unlikely it is possible that the proposal will be dropped, and Node.js will end up having to maintain its own BigInt module which will perform worse after they will be removed from V8.

That said, I vote "wait for BigInt" too.

@tniessen
Copy link
Member

Whether BigInt is coming or not, I am not convinced that we should represent decimals as strings just to make them accessible at all (even though I won't block this PR). I think very few users need to represent 64 bit numbers as decimal strings and even fewer need to use a buffer to convert to and from those numbers. With BigInt coming up, we got a promising alternative, which only reinforces my opinion.

@ronkorving
Copy link
Contributor

I believe we should be conservative with breaking (in this case, future) API, and we should always aim for consistency and elegance when it comes to naming things. Given that, I'm also voting we wait for BigInt.

Copy link
Member

@benjamingr benjamingr left a comment

Choose a reason for hiding this comment

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

Making it explicit

@BridgeAR
Copy link
Member

@seishun I am sure this was quite some work but I think this will not land and I would rather close it for now. Are you ok with that? I think it is a good basis for a PR as soon as v8 supports BigInt out of the box!

@seishun
Copy link
Contributor Author

seishun commented Sep 13, 2017

Yes, I'm okay with that.

@seishun seishun closed this Sep 13, 2017
@seishun
Copy link
Contributor Author

seishun commented Sep 23, 2017

@littledan @mscdex @bnoordhuis Any way I can be informed when a V8 version with BigInt support makes it to Node.js?

@mscdex
Copy link
Contributor

mscdex commented Sep 23, 2017

@seishun Not really (other than reading V8/node changelogs), but for right now you can track the BigInt repo or the ECMAScript finished proposals list to watch for when the BigInt proposal reaches stage 4 (finished). I doubt V8 would add support for BigInt (behind a flag or otherwise) before it reaches stage 4, but I could be wrong about that as I don't know of their plans.

@vsemozhetbyt
Copy link
Contributor

@seishun You can also track this issue: https://bugs.chromium.org/p/v8/issues/detail?id=6791

@mscdex
Copy link
Contributor

mscdex commented Sep 23, 2017

It does look like they do have it behind a flag (--harmony-bigint) as of a few days ago, although it will be awhile before node sees that feature addition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
buffer Issues and PRs related to the buffer subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. semver-minor PRs that contain new features and should be released in the next minor version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.