Skip to content

Commit 555deba

Browse files
committed
buffer: create copy static method
Fixes: #53700
1 parent 4e1f39b commit 555deba

File tree

3 files changed

+653
-38
lines changed

3 files changed

+653
-38
lines changed

lib/buffer.js

Lines changed: 57 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
const {
2525
Array,
26-
ArrayBufferIsView,
2726
ArrayIsArray,
2827
ArrayPrototypeForEach,
2928
MathFloor,
@@ -65,6 +64,7 @@ const {
6564
indexOfBuffer,
6665
indexOfNumber,
6766
indexOfString,
67+
staticCopy,
6868
swap16: _swap16,
6969
swap32: _swap32,
7070
swap64: _swap64,
@@ -203,42 +203,6 @@ function toInteger(n, defaultVal) {
203203
return defaultVal;
204204
}
205205

206-
function copyImpl(source, target, targetStart, sourceStart, sourceEnd) {
207-
if (!ArrayBufferIsView(source))
208-
throw new ERR_INVALID_ARG_TYPE('source', ['Buffer', 'Uint8Array'], source);
209-
if (!ArrayBufferIsView(target))
210-
throw new ERR_INVALID_ARG_TYPE('target', ['Buffer', 'Uint8Array'], target);
211-
212-
if (targetStart === undefined) {
213-
targetStart = 0;
214-
} else {
215-
targetStart = NumberIsInteger(targetStart) ? targetStart : toInteger(targetStart, 0);
216-
if (targetStart < 0)
217-
throw new ERR_OUT_OF_RANGE('targetStart', '>= 0', targetStart);
218-
}
219-
220-
if (sourceStart === undefined) {
221-
sourceStart = 0;
222-
} else {
223-
sourceStart = NumberIsInteger(sourceStart) ? sourceStart : toInteger(sourceStart, 0);
224-
if (sourceStart < 0 || sourceStart > source.byteLength)
225-
throw new ERR_OUT_OF_RANGE('sourceStart', `>= 0 && <= ${source.byteLength}`, sourceStart);
226-
}
227-
228-
if (sourceEnd === undefined) {
229-
sourceEnd = source.byteLength;
230-
} else {
231-
sourceEnd = NumberIsInteger(sourceEnd) ? sourceEnd : toInteger(sourceEnd, 0);
232-
if (sourceEnd < 0)
233-
throw new ERR_OUT_OF_RANGE('sourceEnd', '>= 0', sourceEnd);
234-
}
235-
236-
if (targetStart >= target.byteLength || sourceStart >= sourceEnd)
237-
return 0;
238-
239-
return _copyActual(source, target, targetStart, sourceStart, sourceEnd);
240-
}
241-
242206
function _copyActual(source, target, targetStart, sourceStart, sourceEnd, isUint8Copy = false) {
243207
if (sourceEnd - sourceStart > target.byteLength - targetStart)
244208
sourceEnd = sourceStart + target.byteLength - targetStart;
@@ -618,6 +582,61 @@ Buffer.concat = function concat(list, length) {
618582
return buffer;
619583
};
620584

585+
Buffer.copy = function copy(source, target, targetStart, sourceStart, sourceEnd) {
586+
if (!isAnyArrayBuffer(source) && !isArrayBufferView(source)) {
587+
throw new ERR_INVALID_ARG_TYPE('source', ['ArrayBuffer', 'SharedArrayBuffer', 'TypedArray'], source);
588+
}
589+
590+
if (!isAnyArrayBuffer(target) && !isArrayBufferView(target)) {
591+
throw new ERR_INVALID_ARG_TYPE('target', ['Buffer', 'ArrayBuffer', 'SharedArrayBuffer', 'TypedArray'], target);
592+
}
593+
594+
if (targetStart === undefined) {
595+
targetStart = 0;
596+
} else {
597+
targetStart = NumberIsInteger(targetStart) ? targetStart : toInteger(targetStart, 0);
598+
if (targetStart < 0) {
599+
throw new ERR_OUT_OF_RANGE('targetStart', '>= 0', targetStart);
600+
}
601+
}
602+
603+
const sourceByteLengthValue = source.byteLength;
604+
605+
if (sourceStart === undefined) {
606+
sourceStart = 0;
607+
} else {
608+
sourceStart = NumberIsInteger(sourceStart) ? sourceStart : toInteger(sourceStart, 0);
609+
if (sourceStart < 0 || sourceStart > sourceByteLengthValue) {
610+
throw new ERR_OUT_OF_RANGE('sourceStart', `>= 0 && <= ${sourceByteLengthValue}`, sourceStart);
611+
}
612+
}
613+
614+
if (sourceEnd === undefined) {
615+
sourceEnd = sourceByteLengthValue;
616+
} else {
617+
sourceEnd = NumberIsInteger(sourceEnd) ? sourceEnd : toInteger(sourceEnd, 0);
618+
if (sourceEnd < 0) {
619+
throw new ERR_OUT_OF_RANGE('sourceEnd', '>= 0', sourceEnd);
620+
}
621+
622+
if (sourceEnd > sourceByteLengthValue) {
623+
sourceEnd = sourceByteLengthValue;
624+
}
625+
}
626+
627+
if (sourceStart >= sourceEnd) {
628+
return 0;
629+
}
630+
631+
const targetByteLengthValue = target.byteLength;
632+
633+
if (targetStart >= targetByteLengthValue) {
634+
return 0;
635+
}
636+
637+
return staticCopy(source, target, targetStart, sourceStart, sourceEnd);
638+
};
639+
621640
function base64ByteLength(str, bytes) {
622641
// Handle padding
623642
if (StringPrototypeCharCodeAt(str, bytes - 1) === 0x3D)
@@ -827,7 +846,7 @@ ObjectDefineProperty(Buffer.prototype, 'offset', {
827846

828847
Buffer.prototype.copy =
829848
function copy(target, targetStart, sourceStart, sourceEnd) {
830-
return copyImpl(this, target, targetStart, sourceStart, sourceEnd);
849+
return Buffer.copy(this, target, targetStart, sourceStart, sourceEnd);
831850
};
832851

833852
// No need to verify that "buf.length <= MAX_UINT32" since it's a read-only

src/node_buffer.cc

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1449,6 +1449,91 @@ void CopyArrayBuffer(const FunctionCallbackInfo<Value>& args) {
14491449
memcpy(dest, src, bytes_to_copy);
14501450
}
14511451

1452+
std::pair<void*, size_t> DecomposeSourceToParts(Local<Value> source) {
1453+
void* pointer;
1454+
size_t byte_length;
1455+
1456+
if (source->IsArrayBufferView()) {
1457+
Local<ArrayBufferView> view = source.As<ArrayBufferView>();
1458+
Local<ArrayBuffer> buffer = view->Buffer();
1459+
1460+
// Handle potential detached buffer case
1461+
if (buffer.IsEmpty() || buffer->Data() == nullptr) {
1462+
pointer = nullptr;
1463+
byte_length = 0;
1464+
} else {
1465+
pointer = static_cast<uint8_t*>(buffer->Data()) + view->ByteOffset();
1466+
byte_length = view->ByteLength();
1467+
}
1468+
} else if (source->IsArrayBuffer()) {
1469+
Local<ArrayBuffer> ab = source.As<ArrayBuffer>();
1470+
pointer = ab->Data();
1471+
byte_length = ab->ByteLength();
1472+
} else if (source->IsSharedArrayBuffer()) {
1473+
Local<SharedArrayBuffer> ab = source.As<SharedArrayBuffer>();
1474+
pointer = ab->Data();
1475+
byte_length = ab->ByteLength();
1476+
} else {
1477+
UNREACHABLE(); // Caller must validate.
1478+
}
1479+
1480+
return {pointer, byte_length};
1481+
}
1482+
1483+
void StaticCopy(const FunctionCallbackInfo<Value>& args) {
1484+
Environment* env = Environment::GetCurrent(args);
1485+
1486+
Local<Value> source = args[0];
1487+
Local<Value> target = args[1];
1488+
1489+
void* source_data;
1490+
size_t source_byte_length;
1491+
std::tie(source_data, source_byte_length) = DecomposeSourceToParts(source);
1492+
1493+
void* target_data;
1494+
size_t target_byte_length;
1495+
std::tie(target_data, target_byte_length) = DecomposeSourceToParts(target);
1496+
1497+
size_t target_start = static_cast<size_t>(args[2].As<Number>()->Value());
1498+
size_t source_start = static_cast<size_t>(args[3].As<Number>()->Value());
1499+
size_t source_end = static_cast<size_t>(args[4].As<Number>()->Value());
1500+
1501+
if (source_data == nullptr || target_data == nullptr) {
1502+
args.GetReturnValue().Set(0);
1503+
return;
1504+
}
1505+
1506+
if (target_start >= target_byte_length) {
1507+
return THROW_ERR_OUT_OF_RANGE(
1508+
env, "targetStart is out of bounds");
1509+
}
1510+
1511+
if (source_start > source_byte_length || source_end > source_byte_length) {
1512+
return THROW_ERR_OUT_OF_RANGE(
1513+
env, "sourceStart or sourceEnd is out of bounds");
1514+
}
1515+
1516+
if (source_start >= source_end) {
1517+
args.GetReturnValue().Set(0);
1518+
return;
1519+
}
1520+
1521+
size_t bytes_to_copy = source_end - source_start;
1522+
size_t target_remaining = target_byte_length - target_start;
1523+
1524+
if (bytes_to_copy > target_remaining) {
1525+
bytes_to_copy = target_remaining;
1526+
}
1527+
1528+
if (bytes_to_copy > 0) {
1529+
uint8_t* dest = static_cast<uint8_t*>(target_data) + target_start;
1530+
uint8_t* src = static_cast<uint8_t*>(source_data) + source_start;
1531+
memmove(dest, src, bytes_to_copy);
1532+
}
1533+
1534+
args.GetReturnValue().Set(static_cast<double>(bytes_to_copy));
1535+
}
1536+
14521537
template <encoding encoding>
14531538
uint32_t WriteOneByteString(const char* src,
14541539
uint32_t src_len,
@@ -1576,6 +1661,7 @@ void Initialize(Local<Object> target,
15761661
SetMethodNoSideEffect(context, target, "indexOfString", IndexOfString);
15771662

15781663
SetMethod(context, target, "copyArrayBuffer", CopyArrayBuffer);
1664+
SetMethod(context, target, "staticCopy", StaticCopy);
15791665

15801666
SetMethod(context, target, "swap16", Swap16);
15811667
SetMethod(context, target, "swap32", Swap32);
@@ -1646,6 +1732,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
16461732
registry->Register(SlowIndexOfNumber);
16471733
registry->Register(fast_index_of_number);
16481734
registry->Register(IndexOfString);
1735+
registry->Register(StaticCopy);
16491736

16501737
registry->Register(Swap16);
16511738
registry->Register(Swap32);

0 commit comments

Comments
 (0)