GeezerVison is showing github.com — buffer: support aligned allocations · nodejs/node@615273d
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/buffer.md +94-4Lines changed: 94 additions & 4 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -791,11 +791,14 @@ data that might not have been allocated for `Buffer`s. | 791 | 791 |
| 792 | 792 | A `TypeError` will be thrown if `size` is not a number. | 793 | 793 |
| 794 | | -### Static method: `Buffer.allocUnsafe(size)` | | 794 | +### Static method: `Buffer.allocUnsafe(size[, alignment])` | 795 | 795 |
| 796 | 796 | <!-- YAML | 797 | 797 | added: v5.10.0 | 798 | 798 | changes: | | 799 | + - version: REPLACEME | | 800 | + pr-url: https://github.com/nodejs/node/pull/65003 | | 801 | + description: Added the `alignment` argument. | 799 | 802 | - version: v20.0.0 | 800 | 803 | pr-url: https://github.com/nodejs/node/pull/45796 | 801 | 804 | description: Throw ERR_INVALID_ARG_TYPE or ERR_OUT_OF_RANGE instead of | @@ -810,6 +813,9 @@ changes: | 810 | 813 | --> | 811 | 814 |
| 812 | 815 | * `size` {integer} The desired length of the new `Buffer`. | | 816 | +* `alignment` {integer} If given, the memory backing the new `Buffer` will start | | 817 | + at an address that is a multiple of `alignment`. Must be a power of two no | | 818 | + larger than `2 ** 30`. See [Aligned allocations][]. | 813 | 819 | * Returns: {Buffer} | 814 | 820 |
| 815 | 821 | Allocates a new `Buffer` of `size` bytes. If `size` is larger than | @@ -865,11 +871,14 @@ pool, while `Buffer.allocUnsafe(size).fill(fill)` _will_ use the internal | 865 | 871 | difference is subtle but can be important when an application requires the | 866 | 872 | additional performance that [`Buffer.allocUnsafe()`][] provides. | 867 | 873 |
| 868 | | -### Static method: `Buffer.allocUnsafeSlow(size)` | | 874 | +### Static method: `Buffer.allocUnsafeSlow(size[, alignment])` | 869 | 875 |
| 870 | 876 | <!-- YAML | 871 | 877 | added: v5.12.0 | 872 | 878 | changes: | | 879 | + - version: REPLACEME | | 880 | + pr-url: https://github.com/nodejs/node/pull/65003 | | 881 | + description: Added the `alignment` argument. | 873 | 882 | - version: v20.0.0 | 874 | 883 | pr-url: https://github.com/nodejs/node/pull/45796 | 875 | 884 | description: Throw ERR_INVALID_ARG_TYPE or ERR_OUT_OF_RANGE instead of | @@ -881,6 +890,9 @@ changes: | 881 | 890 | --> | 882 | 891 |
| 883 | 892 | * `size` {integer} The desired length of the new `Buffer`. | | 893 | +* `alignment` {integer} If given, the memory backing the new `Buffer` will start | | 894 | + at an address that is a multiple of `alignment`. Must be a power of two no | | 895 | + larger than `2 ** 30`. See [Aligned allocations][]. | 884 | 896 | * Returns: {Buffer} | 885 | 897 |
| 886 | 898 | Allocates a new `Buffer` of `size` bytes. If `size` is larger than | @@ -5608,16 +5620,92 @@ While there are clear performance advantages to using | 5608 | 5620 | [`Buffer.allocUnsafe()`][], extra care _must_ be taken in order to avoid | 5609 | 5621 | introducing security vulnerabilities into an application. | 5610 | 5622 |
| | 5623 | +### Aligned allocations | | 5624 | + | | 5625 | +Some operating system interfaces require the memory they operate on to be | | 5626 | +aligned, and on some hardware alignment is merely faster. The most common | | 5627 | +example of the former is unbuffered ("direct") file I/O, which on Linux requires | | 5628 | +the buffer address, the file offset and the transfer length to all be multiples | | 5629 | +of the logical block size of the underlying device: | | 5630 | + | | 5631 | +```mjs | | 5632 | +import { open } from 'node:fs/promises'; | | 5633 | +import { constants } from 'node:fs'; | | 5634 | +import { Buffer } from 'node:buffer'; | | 5635 | + | | 5636 | +const blockSize = 4096; | | 5637 | + | | 5638 | +// The buffer address must be block-aligned for O_DIRECT to accept it. | | 5639 | +const buf = Buffer.allocUnsafeSlow(blockSize, blockSize); | | 5640 | + | | 5641 | +const file = await open('/dev/sda', constants.O_RDONLY | constants.O_DIRECT); | | 5642 | +try { | | 5643 | + await file.read(buf, 0, blockSize, 0); | | 5644 | +} finally { | | 5645 | + await file.close(); | | 5646 | +} | | 5647 | +``` | | 5648 | + | | 5649 | +```cjs | | 5650 | +const fs = require('node:fs'); | | 5651 | +const { Buffer } = require('node:buffer'); | | 5652 | + | | 5653 | +const blockSize = 4096; | | 5654 | + | | 5655 | +// The buffer address must be block-aligned for O_DIRECT to accept it. | | 5656 | +const buf = Buffer.allocUnsafeSlow(blockSize, blockSize); | | 5657 | + | | 5658 | +const flags = fs.constants.O_RDONLY | fs.constants.O_DIRECT; | | 5659 | +fs.open('/dev/sda', flags, (err, fd) => { | | 5660 | + if (err) throw err; | | 5661 | + fs.read(fd, buf, 0, blockSize, 0, (err) => { | | 5662 | + fs.close(fd, () => {}); | | 5663 | + if (err) throw err; | | 5664 | + }); | | 5665 | +}); | | 5666 | +``` | | 5667 | + | | 5668 | +Alignment can also be worth requesting purely for performance, even when no | | 5669 | +interface demands it. Aligning a hot `Buffer` to the cache line size (64 bytes on | | 5670 | +most contemporary CPUs) keeps it from straddling one more cache line than it | | 5671 | +needs to, so that a small structure is fetched with one cache miss instead of | | 5672 | +two, and page-aligned (4096 bytes) allocations similarly help interfaces that map | | 5673 | +or pin memory. These are micro-optimizations: measure before reaching for them, | | 5674 | +since the extra bytes are not free. | | 5675 | + | | 5676 | +Because the address of a `Buffer`'s memory cannot be chosen directly, extra bytes | | 5677 | +have to be allocated or skipped to reach an aligned address. | | 5678 | +[`Buffer.allocUnsafeSlow()`][] over-allocates up to `alignment - 1` bytes and | | 5679 | +positions the returned `Buffer` at the first suitably aligned byte within them. | | 5680 | +[`Buffer.allocUnsafe()`][] instead pads its offset into the shared internal pool, | | 5681 | +whose start is always aligned to 64 bytes, and only falls back to an allocation | | 5682 | +of its own when `alignment` is larger than that. Either way, | | 5683 | +[`buf.byteOffset`][] is usually not 0 and [`buf.buffer`][] is larger than `size`, | | 5684 | +so code that reaches past the `Buffer` into its underlying `ArrayBuffer` must | | 5685 | +take the offset into account, as it must for pooled `Buffer`s. | | 5686 | + | | 5687 | +The alignment is a property of the returned `Buffer` and is preserved for its | | 5688 | +whole lifetime, but it is not inherited by other views: [`buf.subarray`][], | | 5689 | +[`buf.slice()`][] and `structuredClone()` may all produce unaligned `Buffer`s. | | 5690 | + | | 5691 | +Alignment also does not survive being captured in a startup snapshot: memory does | | 5692 | +not keep its address across serialization, so a `Buffer` allocated while | | 5693 | +[`--build-snapshot`][] is in effect is not aligned in the deserialized process. | | 5694 | +Allocate inside a [`v8.startupSnapshot.setDeserializeMainFunction()`][] callback, | | 5695 | +or after startup, if the alignment has to hold at run time. | | 5696 | + | 5611 | 5697 | [ASCII]: https://en.wikipedia.org/wiki/ASCII | | 5698 | +[Aligned allocations]: #aligned-allocations | 5612 | 5699 | [Base64]: https://en.wikipedia.org/wiki/Base64 | 5613 | 5700 | [ISO-8859-1]: https://en.wikipedia.org/wiki/ISO-8859-1 | 5614 | 5701 | [RFC 4648, Section 5]: https://tools.ietf.org/html/rfc4648#section-5 | 5615 | 5702 | [UTF-16]: https://en.wikipedia.org/wiki/UTF-16 | 5616 | 5703 | [UTF-8]: https://en.wikipedia.org/wiki/UTF-8 | 5617 | 5704 | [WHATWG Encoding Standard]: https://encoding.spec.whatwg.org/ | | 5705 | +[`--build-snapshot`]: cli.md#--build-snapshot | 5618 | 5706 | [`Buffer.alloc()`]: #static-method-bufferallocsize-fill-encoding | 5619 | | -[`Buffer.allocUnsafe()`]: #static-method-bufferallocunsafesize | 5620 | | -[`Buffer.allocUnsafeSlow()`]: #static-method-bufferallocunsafeslowsize | | 5707 | +[`Buffer.allocUnsafe()`]: #static-method-bufferallocunsafesize-alignment | | 5708 | +[`Buffer.allocUnsafeSlow()`]: #static-method-bufferallocunsafeslowsize-alignment | 5621 | 5709 | [`Buffer.concat()`]: #static-method-bufferconcatlist-totallength | 5622 | 5710 | [`Buffer.copyBytesFrom()`]: #static-method-buffercopybytesfromview-offset-length | 5623 | 5711 | [`Buffer.from(array)`]: #static-method-bufferfromarray | @@ -5639,6 +5727,7 @@ introducing security vulnerabilities into an application. | 5639 | 5727 | [`TypedArray.prototype.subarray()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray | 5640 | 5728 | [`blob.stream()`]: #blobstream | 5641 | 5729 | [`buf.buffer`]: #bufbuffer | | 5730 | +[`buf.byteOffset`]: #bufbyteoffset | 5642 | 5731 | [`buf.compare()`]: #bufcomparetarget-targetstart-targetend-sourcestart-sourceend | 5643 | 5732 | [`buf.entries()`]: #bufentries | 5644 | 5733 | [`buf.fill()`]: #buffillvalue-offset-end-encoding | @@ -5653,6 +5742,7 @@ introducing security vulnerabilities into an application. | 5653 | 5742 | [`buffer.constants.MAX_STRING_LENGTH`]: #bufferconstantsmax_string_length | 5654 | 5743 | [`buffer.kMaxLength`]: #bufferkmaxlength | 5655 | 5744 | [`util.inspect()`]: util.md#utilinspectobject-options | | 5745 | +[`v8.startupSnapshot.setDeserializeMainFunction()`]: v8.md#v8startupsnapshotsetdeserializemainfunctioncallback-data | 5656 | 5746 | [`v8::Uint8Array::kMaxLength`]: https://v8.github.io/api/head/classv8_1_1Uint8Array.html#a7677e3d0c9c92e4d40bef7212f5980c6 | 5657 | 5747 | [base64url]: https://tools.ietf.org/html/rfc4648#section-5 | 5658 | 5748 | [endianness]: https://en.wikipedia.org/wiki/Endianness | |
Collapse file Copy file name to clipboardExpand all lines: doc/api/deprecations.md +2-2Lines changed: 2 additions & 2 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -4624,7 +4624,7 @@ will throw an error in a future version. | 4624 | 4624 | [`--pending-deprecation`]: cli.md#--pending-deprecation | 4625 | 4625 | [`--throw-deprecation`]: cli.md#--throw-deprecation | 4626 | 4626 | [`--unhandled-rejections`]: cli.md#--unhandled-rejectionsmode | 4627 | | -[`Buffer.allocUnsafeSlow(size)`]: buffer.md#static-method-bufferallocunsafeslowsize | | 4627 | +[`Buffer.allocUnsafeSlow(size)`]: buffer.md#static-method-bufferallocunsafeslowsize-alignment | 4628 | 4628 | [`Buffer.from(array)`]: buffer.md#static-method-bufferfromarray | 4629 | 4629 | [`Buffer.from(buffer)`]: buffer.md#static-method-bufferfrombuffer | 4630 | 4630 | [`Buffer.isBuffer()`]: buffer.md#static-method-bufferisbufferobj | @@ -4770,7 +4770,7 @@ will throw an error in a future version. | 4770 | 4770 | [`writable.writableLength`]: stream.md#writablewritablelength | 4771 | 4771 | [`zlib.bytesWritten`]: zlib.md#zlibbyteswritten | 4772 | 4772 | [alloc]: buffer.md#static-method-bufferallocsize-fill-encoding | 4773 | | -[alloc_unsafe_size]: buffer.md#static-method-bufferallocunsafesize | | 4773 | +[alloc_unsafe_size]: buffer.md#static-method-bufferallocunsafesize-alignment | 4774 | 4774 | [caveats of asynchronous customization hooks]: module.md#caveats-of-asynchronous-customization-hooks | 4775 | 4775 | [from_arraybuffer]: buffer.md#static-method-bufferfromarraybuffer-byteoffset-length | 4776 | 4776 | [from_string_encoding]: buffer.md#static-method-bufferfromstring-encoding | |
Collapse file Copy file name to clipboardExpand all lines: doc/api/worker_threads.md +1-1Lines changed: 1 addition & 1 deletion - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -2234,7 +2234,7 @@ thread spawned will spawn another until the application crashes. | 2234 | 2234 | [`--max-old-space-size`]: cli.md#--max-old-space-sizesize-in-mib | 2235 | 2235 | [`--max-semi-space-size`]: cli.md#--max-semi-space-sizesize-in-mib | 2236 | 2236 | [`AsyncResource`]: async_hooks.md#class-asyncresource | 2237 | | -[`Buffer.allocUnsafe()`]: buffer.md#static-method-bufferallocunsafesize | | 2237 | +[`Buffer.allocUnsafe()`]: buffer.md#static-method-bufferallocunsafesize-alignment | 2238 | 2238 | [`ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST`]: errors.md#err_missing_message_port_in_transfer_list | 2239 | 2239 | [`ERR_WORKER_MESSAGING_ERRORED`]: errors.md#err_worker_messaging_errored | 2240 | 2240 | [`ERR_WORKER_MESSAGING_FAILED`]: errors.md#err_worker_messaging_failed | |
Collapse file Copy file name to clipboardExpand all lines: lib/buffer.js +77-10Lines changed: 77 additions & 10 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -140,6 +140,7 @@ const { | 140 | 140 | markAsUntransferable, | 141 | 141 | addBufferPrototypeMethods, | 142 | 142 | createUnsafeBuffer, | | 143 | + createUnsafeAlignedBuffer, | 143 | 144 | asciiWrite, | 144 | 145 | latin1Write, | 145 | 146 | utf8Write, | @@ -171,13 +172,27 @@ const constants = ObjectDefineProperties({}, { | 171 | 172 | }, | 172 | 173 | }); | 173 | 174 |
| | 175 | +// The largest alignment accepted by `Buffer.allocUnsafeSlow()`. Any plausible | | 176 | +// I/O alignment requirement (logical block size, memory page size, huge page | | 177 | +// size) is well below this. | | 178 | +const kMaxAlignment = 2 ** 30; | | 179 | + | | 180 | +// Slices handed out of the pool are 8 byte aligned relative to the start of the | | 181 | +// pool, so aligning the pool itself to a cache line keeps them from straddling | | 182 | +// one more cache line than their size requires. | | 183 | +const kPoolAlignment = 64; | | 184 | + | 174 | 185 | Buffer.poolSize = 64 * 1024; | 175 | | -let poolSize, poolOffset, allocPool, allocBuffer; | | 186 | +// `poolOffset` is relative to `poolBase`, which is where the pool starts inside | | 187 | +// `allocPool`. The pool is over-allocated to be able to align it, so `poolBase` | | 188 | +// is not necessarily 0. | | 189 | +let poolSize, poolOffset, poolBase, allocPool, allocBuffer; | 176 | 190 |
| 177 | 191 | function createPool() { | 178 | 192 | poolSize = Buffer.poolSize; | 179 | | - allocBuffer = createUnsafeBuffer(poolSize); | | 193 | + allocBuffer = createUnsafeAlignedBuffer(poolSize, kPoolAlignment); | 180 | 194 | allocPool = allocBuffer.buffer; | | 195 | + poolBase = TypedArrayPrototypeGetByteOffset(allocBuffer); | 181 | 196 | markAsUntransferable(allocPool); | 182 | 197 | poolOffset = 0; | 183 | 198 | } | @@ -444,40 +459,92 @@ Buffer.alloc = function alloc(size, fill, encoding) { | 444 | 459 | /** | 445 | 460 | * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer | 446 | 461 | * instance. If `--zero-fill-buffers` is set, will zero-fill the buffer. | | 462 | + * | | 463 | + * If `alignment` is given, the memory backing the returned buffer starts at an | | 464 | + * address that is a multiple of `alignment`. See `Buffer.allocUnsafeSlow()`. | | 465 | + * @param {number} size | | 466 | + * @param {number} [alignment] A power of two, at most 2 ** 30 | 447 | 467 | * @returns {FastBuffer} | 448 | 468 | */ | 449 | | -Buffer.allocUnsafe = function allocUnsafe(size) { | | 469 | +Buffer.allocUnsafe = function allocUnsafe(size, alignment) { | 450 | 470 | validateNumber(size, 'size', 0, kMaxLength); | 451 | | - return allocate(size); | | 471 | + if (alignment === undefined) { | | 472 | + return allocate(size); | | 473 | + } | | 474 | + validateAlignment(size, alignment); | | 475 | + return allocateAligned(size, alignment); | 452 | 476 | }; | 453 | 477 |
| 454 | 478 | /** | 455 | 479 | * By default creates a non-zero-filled Buffer instance that is not allocated | 456 | 480 | * off the pre-initialized pool. If `--zero-fill-buffers` is set, will zero-fill | 457 | 481 | * the buffer. | | 482 | + * | | 483 | + * If `alignment` is given, the memory backing the returned buffer starts at an | | 484 | + * address that is a multiple of `alignment`, which is required by e.g. reads | | 485 | + * and writes on file descriptors opened with `O_DIRECT`. Note that up to | | 486 | + * `alignment - 1` extra bytes are allocated to satisfy the request, and that | | 487 | + * the returned buffer's `byteOffset` is therefore usually non-zero. | 458 | 488 | * @param {number} size | 459 | | - * @returns {FastBuffer|undefined} | | 489 | + * @param {number} [alignment] A power of two, at most 2 ** 30 | | 490 | + * @returns {FastBuffer} | 460 | 491 | */ | 461 | | -Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) { | | 492 | +Buffer.allocUnsafeSlow = function allocUnsafeSlow(size, alignment) { | 462 | 493 | validateNumber(size, 'size', 0, kMaxLength); | 463 | | - return createUnsafeBuffer(size); | | 494 | + if (alignment === undefined) { | | 495 | + return createUnsafeBuffer(size); | | 496 | + } | | 497 | + validateAlignment(size, alignment); | | 498 | + return createUnsafeAlignedBuffer(size, alignment); | 464 | 499 | }; | 465 | 500 |
| | 501 | +function validateAlignment(size, alignment) { | | 502 | + validateInteger(alignment, 'alignment', 1, kMaxAlignment); | | 503 | + if ((alignment & (alignment - 1)) !== 0) { | | 504 | + throw new ERR_INVALID_ARG_VALUE( | | 505 | + 'alignment', alignment, 'must be a power of two'); | | 506 | + } | | 507 | + // Satisfying the alignment costs up to `alignment - 1` extra bytes. | | 508 | + if (size > kMaxLength - (alignment - 1)) { | | 509 | + throw new ERR_OUT_OF_RANGE( | | 510 | + 'size', `<= ${kMaxLength - (alignment - 1)}`, size); | | 511 | + } | | 512 | +} | | 513 | + | 466 | 514 | function allocate(size) { | 467 | 515 | if (size <= 0) { | 468 | 516 | return new FastBuffer(); | 469 | 517 | } | 470 | 518 | if (size < (Buffer.poolSize >>> 1)) { | 471 | 519 | if (size > (poolSize - poolOffset)) | 472 | 520 | createPool(); | 473 | | - const b = new FastBuffer(allocPool, poolOffset, size); | | 521 | + const b = new FastBuffer(allocPool, poolBase + poolOffset, size); | 474 | 522 | poolOffset += size; | 475 | 523 | alignPool(); | 476 | 524 | return b; | 477 | 525 | } | 478 | 526 | return createUnsafeBuffer(size); | 479 | 527 | } | 480 | 528 |
| | 529 | +function allocateAligned(size, alignment) { | | 530 | + if (size <= 0) { | | 531 | + return new FastBuffer(); | | 532 | + } | | 533 | + // The pool starts at a `kPoolAlignment` aligned address, so any alignment up | | 534 | + // to that can be satisfied by padding the offset into the pool. Stricter | | 535 | + // alignments need an allocation of their own. | | 536 | + if (alignment > kPoolAlignment || size >= (Buffer.poolSize >>> 1)) { | | 537 | + return createUnsafeAlignedBuffer(size, alignment); | | 538 | + } | | 539 | + poolOffset = (poolOffset + alignment - 1) & ~(alignment - 1); | | 540 | + if (size > (poolSize - poolOffset)) | | 541 | + createPool(); | | 542 | + const b = new FastBuffer(allocPool, poolBase + poolOffset, size); | | 543 | + poolOffset += size; | | 544 | + alignPool(); | | 545 | + return b; | | 546 | +} | | 547 | + | 481 | 548 | function fromStringFast(string, ops) { | 482 | 549 | const maxLength = Buffer.poolSize >>> 1; | 483 | 550 |
| @@ -498,7 +565,7 @@ function fromStringFast(string, ops) { | 498 | 565 | createPool(); | 499 | 566 |
| 500 | 567 | const actual = ops.write(allocBuffer, string, poolOffset, length); | 501 | | - const b = new FastBuffer(allocPool, poolOffset, actual); | | 568 | + const b = new FastBuffer(allocPool, poolBase + poolOffset, actual); | 502 | 569 |
| 503 | 570 | poolOffset += actual; | 504 | 571 | alignPool(); | @@ -560,7 +627,7 @@ function fromArrayLike(obj) { | 560 | 627 | if (length < (Buffer.poolSize >>> 1)) { | 561 | 628 | if (length > (poolSize - poolOffset)) | 562 | 629 | createPool(); | 563 | | - const b = new FastBuffer(allocPool, poolOffset, length); | | 630 | + const b = new FastBuffer(allocPool, poolBase + poolOffset, length); | 564 | 631 | TypedArrayPrototypeSet(b, obj, 0); | 565 | 632 | poolOffset += length; | 566 | 633 | alignPool(); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/buffer.js +17Lines changed: 17 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -33,6 +33,7 @@ const { | 33 | 33 | hexWrite, | 34 | 34 | ucs2Write, | 35 | 35 | utf8WriteStatic, | | 36 | + arrayBufferAlignedOffset, | 36 | 37 | createUnsafeArrayBuffer, | 37 | 38 | setDetachKey, | 38 | 39 | } = internalBinding('buffer'); | @@ -1104,12 +1105,28 @@ function createUnsafeBuffer(size) { | 1104 | 1105 | return new FastBuffer(createUnsafeArrayBuffer(size)); | 1105 | 1106 | } | 1106 | 1107 |
| | 1108 | +// Returns an uninitialized buffer of `size` bytes whose first byte is located at | | 1109 | +// a memory address that is a multiple of `alignment`. `alignment` must be a | | 1110 | +// power of two, and `size + alignment - 1` must not exceed the maximum buffer | | 1111 | +// length. Since the address of a backing store cannot be chosen, `alignment - 1` | | 1112 | +// extra bytes are allocated and skipped, which leaves the returned buffer with a | | 1113 | +// non-zero `byteOffset` into a larger ArrayBuffer. | | 1114 | +function createUnsafeAlignedBuffer(size, alignment) { | | 1115 | + if (size === 0) { | | 1116 | + return new FastBuffer(); | | 1117 | + } | | 1118 | + | | 1119 | + const ab = createUnsafeArrayBuffer(size + alignment - 1); | | 1120 | + return new FastBuffer(ab, arrayBufferAlignedOffset(ab, alignment), size); | | 1121 | +} | | 1122 | + | 1107 | 1123 | module.exports = { | 1108 | 1124 | FastBuffer, | 1109 | 1125 | addBufferPrototypeMethods, | 1110 | 1126 | markAsUntransferable, | 1111 | 1127 | isMarkedAsUntransferable, | 1112 | 1128 | createUnsafeBuffer, | | 1129 | + createUnsafeAlignedBuffer, | 1113 | 1130 | readUInt16BE, | 1114 | 1131 | readUInt32BE, | 1115 | 1132 | asciiWrite, | |
|