-
Notifications
You must be signed in to change notification settings - Fork 3
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
Add randomFillSync #2
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good start! This particular function has some sharp edges though:
-
It should work for plain
ArrayBuffer
and all array buffer views, includingBuffer
,TypedArray
, andDataView
. -
It shouldn't perform any allocations but should instead write directly to the passed buffer. The way you've implemented
randomFill()
in terms ofrandomBytes()
is great and if you simply do it the other way around you can make it allocation-less while still keeping the implementation succinct.
Sharp tips. So, the key points of the incremented solution IMO are:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much better! 👌 It can be further simplified by doing the following:
-
Instead of using
bare-type
, we just needArrayBuffer.isView()
to tell us if we're dealing with a plainArrayBuffer
or a view, i.e.TypedArray
orDataView
. If the latter is the case, simply unwrap the innerArrayBuffer
by grabbingbuffer.buffer
,buffer.byteOffset
, andbuffer.byteLength
. -
Always pass a plain
ArrayBuffer
alongside abyteOffset
andbyteLength
to the native layer. This way the binding stays monomorphic and you can therefore always calljs_get_arraybuffer_info()
on the native side.
yes indeed, sweet refactoring. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost there! 🙌 The native side is Just Right(tm) now, the only bits missing are some devilish details on the JavaScript side:
-
offset
andsize
are relative to the number of bytes per element of the passed view when not passing a plainArrayBuffer
. For example, for a 16 byte view, such asUint16Array
,offset
andsize
are not expressed in 1 byte units but in 2 byte units. The simplest way to handle this is to use thesubarray(start, end)
method if eitheroffset
isn't0
orsize
isn'tbuffer.length
. -
When an
ArrayBuffer
view is passed, instead of a plainArrayBuffer
, the view may not necessarily be over the entire underlyingArrayBuffer
. The offset and size passed to the native side should therefore bebuffer.byteLength
andbuffer.byteOffset || 0
to reflect this. As a plainArrayBuffer
doesn't have abyteOffset
, it can be defaulted to0
.
I've added some test cases that exercise both of those points.
e9749da
to
d06ad9f
Compare
Tricky changes, I tried not to proliferate many conditionals and keep the flow sane. My solution was to refine and reshape the values from And to apply the window view I relied on byteOffset and byteLength as default values. A little adjustment needed was to combine the offset argument with the byteOffset from the view: In that way, we still pass all the buffer to the native code regardless of how small a window view could be the case, I was wondering about the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, and good point about subarray(start, end)
not being defined for DataView
!
Now for the final touch to avoid surprises and nasty CVEs: Bounds checking. In general, whenever you slice into a piece of memory from C, you must ask yourself: Is this slice within bounds of the underlying memory? If you can either guarantee that or write it off as undefined behaviour, all good! If you can't guarantee that, however, there's risk of causing buffer under- and/or overflow. One such case is...
RAND_bytes(&buf[offset], num)
...where you slice into buf
at offset
for num
bytes. There's currently no guarantee that offset
and offset + num
actually resides within buf
though and so we risk RAND_bytes()
writing random bytes outside of buf
.
A similar, albeit less severe, situation arises when you have a partial view over an underlying ArrayBuffer
. Here we have to make sure that offset
and offset + num
doesn't cause the partial view to expand.
I've added some initial test cases for both of these points. It'd probably be beneficial with similar tests for views with an element size larger than 1 byte such as Uint16Array
and friends.
Checks were added, and I cleaned the code style a little bit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing, let's land it! 🤩
It's hard to write unit tests on random values without mocking, so my approach was using big values and making it easy to change the sample size.