Buffer در Node.js: راهنمای کامل

کلاس Buffer در Node.js برای نمایش دنباله‌ای از بایت‌ها با طول ثابت استفاده می‌شود. بسیاری از APIهای Node.js از Buffer پشتیبانی می‌کنند. این کلاس زیرکلاس Uint8Array است و متدهای اضافی برای کار با داده‌های باینری ارائه می‌دهد. Bufferها برای مدیریت داده‌های خام مانند فایل‌ها، جریان‌ها و پروتکل‌های شبکه ضروری هستند.

BufferUint8ArrayCharacter EncodingsTypedArrayBlobBase64 / Hex

~12 دقیقه مطالعه • بروزرسانی ۵ دی ۱۴۰۴

1. ایجاد Buffer


روش‌های مختلفی برای ساخت Buffer وجود دارد:

const { Buffer } = require('node:buffer');

Buffer.alloc(10);        // Buffer صفرشده با طول 10
Buffer.alloc(10, 1);     // Buffer پرشده با مقدار 1
Buffer.allocUnsafe(10);  // Buffer بدون مقداردهی اولیه (سریع‌تر اما ناامن)
Buffer.from([1, 2, 3]);  // Buffer از آرایه
Buffer.from('tést');     // Buffer از رشته UTF-8
Buffer.from('tést', 'latin1'); // Buffer از رشته Latin-1

2. Buffer و Encoding


هنگام تبدیل بین Buffer و رشته‌ها، می‌توان encoding مشخص کرد. پیش‌فرض UTF-8 است.

Encodingهای پشتیبانی‌شده:

  • utf8: پیش‌فرض، چندبایتی
  • utf16le: یونیکد با little-endian
  • latin1: کاراکترهای U+0000 تا U+00FF
  • base64 و base64url: تبدیل داده به متن
  • hex: نمایش هر بایت به‌صورت دو رقم هگز
  • ascii و binary: سازگاری قدیمی
  • ucs2: معادل utf16le
const buf = Buffer.from('hello world', 'utf8');
console.log(buf.toString('hex'));    // 68656c6c6f20776f726c64
console.log(buf.toString('base64')); // aGVsbG8gd29ybGQ=

3. Buffer و TypedArray


Bufferها نمونه‌های Uint8Array هستند و متدهای TypedArray را دارند، اما تفاوت‌هایی وجود دارد:

  • Buffer.slice() یک view ایجاد می‌کند، نه کپی.
  • TypedArray.slice() کپی واقعی ایجاد می‌کند.

مثال:

const buf = Buffer.from([1, 2, 3, 4]);
const uint32array = new Uint32Array(buf);
console.log(uint32array); // Uint32Array(4) [1, 2, 3, 4]

4. اشتراک حافظه با TypedArray


می‌توان Buffer و TypedArray را طوری ساخت که حافظه مشترک داشته باشند:

const buf = Buffer.from('hello', 'utf16le');
const uint16array = new Uint16Array(buf.buffer, buf.byteOffset, buf.length / 2);
console.log(uint16array); // Uint16Array(5) [104, 101, 108, 108, 111]

5. Iteration روی Buffer


Bufferها قابل پیمایش با for..of هستند:

const buf = Buffer.from([1, 2, 3]);
for (const b of buf) console.log(b);
// 1, 2, 3

6. کلاس Blob


Blob دادهٔ خام غیرقابل تغییر را کپسوله می‌کند و می‌تواند بین threadها به اشتراک گذاشته شود.

ویژگی‌ها:

  • new Blob(sources[, options]): ساخت Blob از رشته‌ها، ArrayBufferها و غیره
  • blob.arrayBuffer(): بازگرداندن ArrayBuffer
  • blob.bytes(): بازگرداندن Uint8Array
  • blob.size: اندازه Blob
  • blob.slice(): ایجاد Blob جدید از بخشی از داده
  • blob.stream(): بازگرداندن ReadableStream
  • blob.text(): بازگرداندن داده به‌صورت رشته UTF-8
  • blob.type: نوع محتوا

مثال:

const { Blob } = require('node:buffer');
const blob = new Blob(['hello']);
blob.text().then(console.log); // "hello"

نتیجه‌گیری


Buffer در Node.js ابزاری قدرتمند برای مدیریت داده‌های باینری است. با پشتیبانی از انواع encodingها، سازگاری با TypedArrayها و وجود کلاس Blob، توسعه‌دهندگان می‌توانند داده‌های خام را به‌طور مؤثر پردازش و انتقال دهند. این قابلیت‌ها برای کار با فایل‌ها، شبکه و داده‌های سطح پایین حیاتی هستند.

1. Buffer.alloc(size[, fill[, encoding]])


یک Buffer جدید با طول مشخص ایجاد می‌کند. اگر مقدار fill داده شود، محتوا با آن مقدار پر می‌شود.

const buf = Buffer.alloc(5); // 
const buf2 = Buffer.alloc(5, 'a'); // 
const buf3 = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');
// 

2. Buffer.allocUnsafe(size)


Buffer جدید بدون مقداردهی اولیه ایجاد می‌کند. سریع‌تر است اما ممکن است داده‌های حساس قبلی را شامل شود.

const buf = Buffer.allocUnsafe(10);
console.log(buf); // محتوا ناشناخته
buf.fill(0);      // مقداردهی با صفر

3. Buffer.allocUnsafeSlow(size)


Buffer بدون مقداردهی اولیه ایجاد می‌کند اما خارج از pool داخلی. مناسب برای نگهداری طولانی‌مدت بخش‌های کوچک حافظه.

const sb = Buffer.allocUnsafeSlow(10);
data.copy(sb, 0, 0, 10);
store.push(sb);

4. Buffer.byteLength(string[, encoding])


طول بایت‌های یک رشته یا داده را بر اساس encoding مشخص محاسبه می‌کند.

const str = '½ + ¼ = ¾';
console.log(Buffer.byteLength(str, 'utf8')); // 12

5. Buffer.compare(buf1, buf2)


دو Buffer را مقایسه می‌کند و نتیجه -1، 0 یا 1 برمی‌گرداند. برای مرتب‌سازی مفید است.

const buf1 = Buffer.from('1234');
const buf2 = Buffer.from('0123');
console.log([buf1, buf2].sort(Buffer.compare));

6. Buffer.concat(list[, totalLength])


Bufferهای موجود در یک لیست را به هم متصل می‌کند.

const bufA = Buffer.concat([buf1, buf2, buf3], totalLength);

7. Buffer.copyBytesFrom(view[, offset[, length]])


محتوای یک TypedArray را به Buffer جدید کپی می‌کند.

const u16 = new Uint16Array([0, 0xffff]);
const buf = Buffer.copyBytesFrom(u16, 1, 1);

8. Buffer.from(array)


Buffer جدید از آرایهٔ بایت‌ها ایجاد می‌کند.

const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); // "buffer"

9. Buffer.from(arrayBuffer[, byteOffset[, length]])


Buffer جدید از ArrayBuffer یا SharedArrayBuffer ایجاد می‌کند. حافظه مشترک خواهد بود.

const arr = new Uint16Array(2);
arr[0] = 5000; arr[1] = 4000;
const buf = Buffer.from(arr.buffer);
console.log(buf); // 

نتیجه‌گیری


کلاس Buffer در Node.js ابزار قدرتمندی برای مدیریت داده‌های باینری است. متدهای مختلف آن امکان تخصیص امن یا سریع، محاسبه طول داده، مقایسه، اتصال و کپی حافظه را فراهم می‌کنند. استفادهٔ درست از این متدها باعث افزایش کارایی و امنیت در برنامه‌های Node.js می‌شود.

1. Buffer.from(buffer)


یک Buffer جدید با کپی داده‌های یک Buffer یا Uint8Array موجود ایجاد می‌کند.

const buf1 = Buffer.from('buffer');
const buf2 = Buffer.from(buf1);
buf1[0] = 0x61;
console.log(buf1.toString()); // auffer
console.log(buf2.toString()); // buffer

2. Buffer.from(object[, offsetOrEncoding[, length]])


برای اشیائی که valueOf() یا Symbol.toPrimitive دارند، یک Buffer از مقدار بازگشتی ایجاد می‌کند.

const buf = Buffer.from(new String('this is a test'));
class Foo { [Symbol.toPrimitive]() { return 'this is a test'; } }
const buf2 = Buffer.from(new Foo(), 'utf8');

3. Buffer.from(string[, encoding])


یک Buffer جدید از رشته ایجاد می‌کند. پیش‌فرض encoding برابر با UTF-8 است.

const buf1 = Buffer.from('this is a tést');
const buf2 = Buffer.from('7468697320697320612074c3a97374', 'hex');
console.log(buf1.toString()); // this is a tést
console.log(buf2.toString()); // this is a tést

4. Buffer.isBuffer(obj)


بررسی می‌کند که آیا شیء داده‌شده یک Buffer است یا خیر.

Buffer.isBuffer(Buffer.alloc(10)); // true
Buffer.isBuffer('string'); // false

5. Buffer.isEncoding(encoding)


بررسی می‌کند که آیا encoding داده‌شده معتبر است یا خیر.

Buffer.isEncoding('utf8'); // true
Buffer.isEncoding('utf/8'); // false

6. Buffer.poolSize


اندازهٔ پیش‌فرض pool داخلی Buffer (8192 بایت) را مشخص می‌کند. این مقدار قابل تغییر است.

7. buf[index]


دسترسی به بایت‌ها با استفاده از اندیس. مقادیر بین 0 و 255 معتبر هستند.

const str = 'Node.js';
const buf = Buffer.allocUnsafe(str.length);
for (let i = 0; i < str.length; i++) buf[i] = str.charCodeAt(i);
console.log(buf.toString('utf8')); // Node.js

8. buf.buffer و buf.byteOffset


ویژگی buffer به ArrayBuffer زیرین اشاره دارد. byteOffset موقعیت شروع در آن ArrayBuffer را مشخص می‌کند.

const arrayBuffer = new ArrayBuffer(16);
const buffer = Buffer.from(arrayBuffer);
console.log(buffer.buffer === arrayBuffer); // true

9. buf.compare(target[, ...])


دو Buffer را مقایسه می‌کند و نتیجه مرتب‌سازی را برمی‌گرداند (-1، 0 یا 1).

const buf1 = Buffer.from('ABC');
const buf2 = Buffer.from('BCD');
console.log(buf1.compare(buf2)); // -1

10. buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])


داده‌ها را از یک بخش Buffer به بخش دیگر یا Buffer هدف کپی می‌کند.

const buf1 = Buffer.allocUnsafe(26);
const buf2 = Buffer.allocUnsafe(26).fill('!');
for (let i = 0; i < 26; i++) buf1[i] = i + 97;
buf1.copy(buf2, 8, 16, 20);
console.log(buf2.toString('ascii', 0, 25));

نتیجه‌گیری


کلاس Buffer در Node.js مجموعه‌ای از متدهای قدرتمند برای ایجاد، بررسی، مقایسه و کپی داده‌های باینری ارائه می‌دهد. این قابلیت‌ها برای مدیریت داده‌های سطح پایین در فایل‌ها، شبکه و پردازش‌های باینری حیاتی هستند.

1. buf.entries()


یک iterator از جفت‌های [index, byte] برمی‌گرداند.

const buf = Buffer.from('buffer');
for (const pair of buf.entries()) console.log(pair);
// [0, 98], [1, 117], ...

2. buf.equals(otherBuffer)


بررسی می‌کند که آیا دو Buffer دقیقاً همان داده‌ها را دارند یا خیر.

const buf1 = Buffer.from('ABC');
const buf2 = Buffer.from('414243', 'hex');
console.log(buf1.equals(buf2)); // true

3. buf.fill(value[, offset[, end]][, encoding])


Buffer را با مقدار مشخص پر می‌کند.

const b = Buffer.allocUnsafe(10).fill('h');
console.log(b.toString()); // hhhhhhhhhh

4. buf.includes(value[, byteOffset][, encoding])


بررسی می‌کند که آیا Buffer شامل مقدار داده‌شده هست یا خیر.

const buf = Buffer.from('this is a buffer');
console.log(buf.includes('this')); // true
console.log(buf.includes(97));     // true ('a')

5. buf.indexOf(value[, byteOffset][, encoding])


اندیس اولین وقوع مقدار را برمی‌گرداند یا -1 اگر یافت نشود.

const buf = Buffer.from('this is a buffer');
console.log(buf.indexOf('is')); // 2

6. buf.keys()


یک iterator از اندیس‌های Buffer برمی‌گرداند.

const buf = Buffer.from('buffer');
for (const key of buf.keys()) console.log(key);
// 0, 1, 2, 3, 4, 5

7. buf.lastIndexOf(value[, byteOffset][, encoding])


اندیس آخرین وقوع مقدار را برمی‌گرداند.

const buf = Buffer.from('this buffer is a buffer');
console.log(buf.lastIndexOf('buffer')); // 17

8. buf.length


تعداد بایت‌های موجود در Buffer را برمی‌گرداند.

const buf = Buffer.alloc(1234);
console.log(buf.length); // 1234

9. buf.readBigInt64BE / buf.readBigInt64LE


یک عدد صحیح ۶۴ بیتی signed را از Buffer می‌خواند (big-endian یا little-endian).

10. buf.readBigUInt64BE / buf.readBigUInt64LE


یک عدد صحیح ۶۴ بیتی unsigned را از Buffer می‌خواند.

const buf = Buffer.from([0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff]);
console.log(buf.readBigUInt64BE(0)); // 4294967295n

11. buf.readDoubleBE / buf.readDoubleLE


یک عدد double ۶۴ بیتی را از Buffer می‌خواند.

const buf = Buffer.from([1,2,3,4,5,6,7,8]);
console.log(buf.readDoubleBE(0));

نتیجه‌گیری


این متدهای پیشرفتهٔ Buffer کنترل دقیق روی داده‌های باینری فراهم می‌کنند. پیمایش، بررسی برابری، جستجو و خواندن اعداد بزرگ، Buffer را به ابزاری همه‌کاره برای وظایف سطح پایین تبدیل کرده است. تسلط بر این متدها باعث مدیریت مؤثر و مطمئن داده‌های خام در برنامه‌های Node.js می‌شود.

1. خواندن Float 32 بیتی


  • buf.readFloatBE(offset): خواندن عدد اعشاری ۳۲ بیتی به‌صورت big-endian.
  • buf.readFloatLE(offset): خواندن عدد اعشاری ۳۲ بیتی به‌صورت little-endian.
const buf = Buffer.from([1, 2, 3, 4]);
console.log(buf.readFloatBE(0)); // 2.387939260590663e-38
console.log(buf.readFloatLE(0)); // 1.539989614439558e-36

2. خواندن Int8 و UInt8


  • buf.readInt8(offset): خواندن عدد صحیح signed ۸ بیتی.
  • buf.readUInt8(offset): خواندن عدد صحیح unsigned ۸ بیتی.
const buf = Buffer.from([-1, 5]);
console.log(buf.readInt8(0));  // -1
console.log(buf.readUInt8(1)); // 254

3. خواندن Int16 و UInt16


  • buf.readInt16BE(offset): خواندن عدد signed ۱۶ بیتی big-endian.
  • buf.readInt16LE(offset): خواندن عدد signed ۱۶ بیتی little-endian.
  • buf.readUInt16BE(offset): خواندن عدد unsigned ۱۶ بیتی big-endian.
  • buf.readUInt16LE(offset): خواندن عدد unsigned ۱۶ بیتی little-endian.
const buf = Buffer.from([0x12, 0x34, 0x56]);
console.log(buf.readInt16BE(0));   // 4660
console.log(buf.readUInt16LE(1)); // 5634

4. خواندن Int32 و UInt32


  • buf.readInt32BE(offset): خواندن عدد signed ۳۲ بیتی big-endian.
  • buf.readInt32LE(offset): خواندن عدد signed ۳۲ بیتی little-endian.
  • buf.readUInt32BE(offset): خواندن عدد unsigned ۳۲ بیتی big-endian.
  • buf.readUInt32LE(offset): خواندن عدد unsigned ۳۲ بیتی little-endian.
const buf = Buffer.from([0x12, 0x34, 0x56, 0x78]);
console.log(buf.readUInt32BE(0).toString(16)); // 12345678
console.log(buf.readUInt32LE(0).toString(16)); // 78563412

5. خواندن IntBE / IntLE


خواندن عدد signed با طول دلخواه (۱ تا ۶ بایت) به‌صورت big-endian یا little-endian.

const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]);
console.log(buf.readIntBE(0, 6).toString(16)); // 1234567890ab
console.log(buf.readIntLE(0, 6).toString(16)); // -546f87a9cbee

6. خواندن UIntBE / UIntLE


خواندن عدد unsigned با طول دلخواه (۱ تا ۶ بایت) به‌صورت big-endian یا little-endian.

const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]);
console.log(buf.readUIntBE(0, 6).toString(16)); // 1234567890ab
console.log(buf.readUIntLE(0, 6).toString(16)); // ab9078563412

نتیجه‌گیری


متدهای خواندن در کلاس Buffer امکان استخراج داده‌های باینری در قالب‌های مختلف را فراهم می‌کنند. با پشتیبانی از انواع signed و unsigned، و ترتیب‌های big-endian و little-endian، این متدها برای پردازش داده‌های سطح پایین در Node.js بسیار حیاتی هستند.

1. buf.subarray(start, end)


یک Buffer جدید ایجاد می‌کند که به همان حافظه اصلی اشاره دارد اما با محدودهٔ مشخص. تغییر در subarray باعث تغییر در Buffer اصلی می‌شود.

const buf1 = Buffer.allocUnsafe(26);
for (let i = 0; i < 26; i++) buf1[i] = i + 97;
const buf2 = buf1.subarray(0, 3);
console.log(buf2.toString()); // abc
buf1[0] = 33;
console.log(buf2.toString()); // !bc

2. buf.slice(start, end)


مشابه subarray اما منسوخ شده. تغییر در slice باعث تغییر در Buffer اصلی می‌شود.

3. buf.swap16 / buf.swap32 / buf.swap64


ترتیب بایت‌ها را درجا تغییر می‌دهد:

  • swap16: برای اعداد ۱۶ بیتی
  • swap32: برای اعداد ۳۲ بیتی
  • swap64: برای اعداد ۶۴ بیتی
const buf = Buffer.from([1,2,3,4,5,6,7,8]);
buf.swap16(); // 

4. buf.toJSON()


نمایش JSON از Buffer را برمی‌گرداند. برای ذخیره یا بازسازی Buffer مفید است.

const buf = Buffer.from([1,2,3,4,5]);
console.log(JSON.stringify(buf));
// {"type":"Buffer","data":[1,2,3,4,5]}

5. buf.toString([encoding, start, end])


Buffer را به رشته تبدیل می‌کند. پیش‌فرض encoding برابر UTF-8 است.

const buf = Buffer.from('tést');
console.log(buf.toString('hex'));   // 74c3a97374
console.log(buf.toString('utf8',0,3)); // té

6. buf.values()


یک iterator از مقادیر بایت‌ها برمی‌گرداند. در for..of به‌طور خودکار استفاده می‌شود.

const buf = Buffer.from('buffer');
for (const val of buf) console.log(val);
// 98, 117, 102, ...

7. buf.write(string[, offset[, length]][, encoding])


رشته‌ای را در Buffer می‌نویسد. اگر فضای کافی نباشد، بخشی از رشته نوشته می‌شود.

const buf = Buffer.alloc(256);
const len = buf.write('½ + ¼ = ¾', 0);
console.log(`${len} bytes: ${buf.toString('utf8',0,len)}`);

8. buf.writeBigInt64BE / buf.writeBigInt64LE


یک عدد بزرگ (bigint) را به‌صورت ۶۴ بیتی signed در Buffer می‌نویسد، در قالب big-endian یا little-endian.

const buf = Buffer.allocUnsafe(8);
buf.writeBigInt64BE(0x0102030405060708n, 0);
console.log(buf); // 

نتیجه‌گیری


متدهای پیشرفتهٔ Buffer مانند subarray، swap، toJSON، toString و write ابزارهای قدرتمندی برای مدیریت داده‌های باینری هستند. این متدها امکان پردازش سریع، تبدیل و ذخیره داده‌ها را فراهم می‌کنند و برای توسعهٔ برنامه‌های سطح پایین در Node.js حیاتی‌اند.

1. نوشتن BigUInt64


  • buf.writeBigUInt64BE(value, offset): نوشتن عدد bigint به‌صورت unsigned ۶۴ بیتی big-endian.
  • buf.writeBigUInt64LE(value, offset): نوشتن عدد bigint به‌صورت unsigned ۶۴ بیتی little-endian.
const buf = Buffer.allocUnsafe(8);
buf.writeBigUInt64BE(0xdecafafecacefaden, 0);
console.log(buf); // 

2. نوشتن Double و Float


  • buf.writeDoubleBE(value, offset): نوشتن عدد اعشاری ۶۴ بیتی big-endian.
  • buf.writeDoubleLE(value, offset): نوشتن عدد اعشاری ۶۴ بیتی little-endian.
  • buf.writeFloatBE(value, offset): نوشتن عدد اعشاری ۳۲ بیتی big-endian.
  • buf.writeFloatLE(value, offset): نوشتن عدد اعشاری ۳۲ بیتی little-endian.
const buf = Buffer.allocUnsafe(8);
buf.writeDoubleBE(123.456, 0);
console.log(buf);

3. نوشتن اعداد صحیح کوچک


  • buf.writeInt8(value, offset): نوشتن عدد signed ۸ بیتی.
  • buf.writeUInt8(value, offset): نوشتن عدد unsigned ۸ بیتی.
const buf = Buffer.allocUnsafe(2);
buf.writeInt8(2, 0);
buf.writeInt8(-2, 1);
console.log(buf); // 

4. نوشتن Int16 و UInt16


  • buf.writeInt16BE(value, offset): نوشتن عدد signed ۱۶ بیتی big-endian.
  • buf.writeInt16LE(value, offset): نوشتن عدد signed ۱۶ بیتی little-endian.
  • buf.writeUInt16BE(value, offset): نوشتن عدد unsigned ۱۶ بیتی big-endian.
  • buf.writeUInt16LE(value, offset): نوشتن عدد unsigned ۱۶ بیتی little-endian.

5. نوشتن Int32 و UInt32


  • buf.writeInt32BE(value, offset): نوشتن عدد signed ۳۲ بیتی big-endian.
  • buf.writeInt32LE(value, offset): نوشتن عدد signed ۳۲ بیتی little-endian.
  • buf.writeUInt32BE(value, offset): نوشتن عدد unsigned ۳۲ بیتی big-endian.
  • buf.writeUInt32LE(value, offset): نوشتن عدد unsigned ۳۲ بیتی little-endian.

6. نوشتن IntBE / IntLE و UIntBE / UIntLE


نوشتن اعداد صحیح با طول دلخواه (۱ تا ۶ بایت) به‌صورت big-endian یا little-endian.

const buf = Buffer.allocUnsafe(6);
buf.writeIntBE(0x1234567890ab, 0, 6);
console.log(buf); // 

7. نوشتن رشته‌ها


buf.write(string[, offset[, length]][, encoding]): نوشتن رشته در Buffer با encoding مشخص (پیش‌فرض UTF-8).

const buf = Buffer.alloc(256);
const len = buf.write('½ + ¼ = ¾', 0);
console.log(`${len} bytes: ${buf.toString('utf8',0,len)}`);

نتیجه‌گیری


متدهای نوشتن در کلاس Buffer امکان ذخیرهٔ داده‌های باینری در قالب‌های مختلف را فراهم می‌کنند. با پشتیبانی از انواع signed و unsigned، و ترتیب‌های big-endian و little-endian، این متدها برای پردازش داده‌های سطح پایین در Node.js بسیار حیاتی هستند.

نوشته و پژوهش شده توسط دکتر شاهین صیامی