2012-04-11 60 views
18

Tôi đang làm việc với các đối tượng ArrayBuffer và tôi muốn sao chép chúng. Trong khi điều này là khá dễ dàng với con trỏ thực tế và memcpy, tôi không thể tìm thấy bất kỳ cách đơn giản để làm điều đó trong Javascript.Cách đơn giản nhất để sao chép đối tượng ArrayBuffer là gì?

Ngay bây giờ, đây là cách tôi sao chép của tôi ArrayBuffers:

function copy(buffer) 
{ 
    var bytes = new Uint8Array(buffer); 
    var output = new ArrayBuffer(buffer.byteLength); 
    var outputBytes = new Uint8Array(output); 
    for (var i = 0; i < bytes.length; i++) 
     outputBytes[i] = bytes[i]; 
    return output; 
} 

Có cách nào đẹp hơn?

Trả lời

23

ArrayBuffer là nghĩa vụ phải hỗ trợ slice (http://www.khronos.org/registry/typedarray/specs/latest/), do đó bạn có thể thử,

buffer.slice(0); 

trong đó hoạt động trong Chrome 18 nhưng không phải Firefox 10 hoặc 11. Đối với Firefox, bạn cần phải sao chép nó theo cách thủ công. Bạn có thể khỉ vá slice() trong Firefox vì Chrome slice() sẽ hoạt động tốt hơn bản sao thủ công. Điều này sẽ giống như thế,

if (!ArrayBuffer.prototype.slice) 
    ArrayBuffer.prototype.slice = function (start, end) { 
     var that = new Uint8Array(this); 
     if (end == undefined) end = that.length; 
     var result = new ArrayBuffer(end - start); 
     var resultArray = new Uint8Array(result); 
     for (var i = 0; i < resultArray.length; i++) 
      resultArray[i] = that[i + start]; 
     return result; 
    } 

Sau đó, bạn có thể gọi,

buffer.slice(0); 

để sao chép mảng ở cả Chrome và Firefox.

+0

Trong Chrome (29 tại thời điểm nhận xét này), ArrayBuffer không có phương thức có tên là .slice, nhưng .subarray (bắt đầu [, kết thúc]) thay thế. Không chắc chắn nó như thế nào trong FF. –

+0

Có vẻ như thông số đã thay đổi kể từ khi câu trả lời của tôi được đặt ra. Tôi sẽ làm việc để cập nhật nó. 'subarray()' nằm trong tiêu chuẩn mới thay cho 'slice()'. – chuckj

+1

Nhìn lại spec, 'ArrayBuffer' phải có' slice() '. Các mảng được đánh máy (chẳng hạn như 'Uint8Array') phải có' subarray() '. Ở trên là chính xác cho 'ArrayBuffer'. – chuckj

2

Hmmm ... nếu đó là Uint8Array bạn muốn cắt (điều này cần phải có), điều này có thể hoạt động.

if (!Uint8Array.prototype.slice && 'subarray' in Uint8Array.prototype) 
    Uint8Array.prototype.slice = Uint8Array.prototype.subarray; 
2

Phiên bản nhanh hơn và hơi phức tạp hơn của câu trả lời của chuckj. Nên sử dụng các thao tác sao chép ít hơn ~ 8x trên các mảng lớn được đánh máy. Về cơ bản chúng tôi sao chép càng nhiều byte 8 byte càng tốt và sau đó sao chép 0-7 byte còn lại. Điều này đặc biệt hữu ích trong phiên bản IE hiện tại, vì nó không có phương thức slice được triển khai cho ArrayBuffer.

if (!ArrayBuffer.prototype.slice) 
    ArrayBuffer.prototype.slice = function (start, end) { 
    if (end == undefined) end = that.length; 
    var length = end - start; 
    var lengthDouble = Math.floor(length/Float64Array.BYTES_PER_ELEMENT); 
    // ArrayBuffer that will be returned 
    var result = new ArrayBuffer(length); 

    var that = new Float64Array(this, start, lengthDouble) 
    var resultArray = new Float64Array(result, 0, lengthDouble); 

    for (var i = 0; i < resultArray.length; i++) 
     resultArray[i] = that[i]; 

    // copying over the remaining bytes 
    that = new Uint8Array(this, start + lengthDouble * Float64Array.BYTES_PER_ELEMENT) 
    resultArray = new Uint8Array(result, lengthDouble * Float64Array.BYTES_PER_ELEMENT); 

    for (var i = 0; i < resultArray.length; i++) 
     resultArray[i] = that[i]; 

    return result; 
} 
+0

Điều này gây ra 'chiều dài ArrayBuffer trừ byteOffset không phải là bội số của kích thước phần tử.' trong trình duyệt chứng khoán Android. – duckegg

28

tôi thích phương pháp sau

function copy(src) { 
    var dst = new ArrayBuffer(src.byteLength); 
    new Uint8Array(dst).set(new Uint8Array(src)); 
    return dst; 
} 
+2

Có vẻ như câu trả lời của bạn là [nhanh nhất so với câu trả lời được chọn] (http://jsperf.com/clone-float32array). – MaiaVictor

+0

@Viclib, xin lưu ý rằng bạn có thể cần cung cấp dự phòng cho IE. – Gleno

+0

Lưu ý bổ sung, hiệu suất đó cho các loại khác nhau cũng có thể thay đổi; từ những gì tôi có thể nhớ Uint8 bản sao nhanh hơn Float32. Tôi nghĩ rằng nhanh nhất là Int32; nhưng sau đó bạn phải có dữ liệu đệm đúng cách. YMMV. – Gleno

16

Dường như chỉ đơn giản là đi qua trong dataview nguồn thực hiện một bản sao:

var a = new Uint8Array([2,3,4,5]); 
var b = new Uint8Array(a); 
a[0] = 6; 
console.log(a); // [6, 3, 4, 5] 
console.log(b); // [2, 3, 4, 5] 

Tested trong FF 33 và Chrome 36.

+1

Đây là câu trả lời hay và có vẻ như tương thích với nhiều trình duyệt. Cảm ơn bạn ! –

+1

Thật vậy, điều này là tốt hơn nhiều so với câu trả lời được chấp nhận. Cảm ơn! – Nathan

+2

Một cái gì đó cần lưu ý: 'new Uint8Array (a.buffer)' không * không * sao chép bộ đệm. Điều này có thể hữu ích tùy thuộc vào nhu cầu của bạn. Nó cũng chấp nhận các tham số 'byteOffset' và' length' tùy chọn khi được gọi với một ArrayBuffer. Điều này rất giống với NodeJS's 'Buffer.from (Buffer, byteOffset, length)' trong Node v6. – STRML

Các vấn đề liên quan