टाइपस्क्रिप्ट में `DataView`

टाइपस्क्रिप्ट में `DataView`

यह लेख टाइपस्क्रिप्ट में DataView के बारे में समझाता है।

हम TypeScript में DataView को, मूल बातें से लेकर व्यावहारिक उपयोग तक, चरण दर चरण समझाएंगे।

YouTube Video

टाइपस्क्रिप्ट में DataView

DataView का उपयोग करके, आप TypeScript में किसी ArrayBuffer पर बाइट-स्तरीय पढ़ने और लिखने के जटिल ऑपरेशन्स कर सकते हैं। DataView कम स्तर की बाइनरी प्रोसेसिंग जैसे प्रोटोकॉल इम्प्लीमेंटेशन, बाइनरी फाइल विश्लेषण, तथा WebSocket पर बाइनरी डेटा भेजने/प्राप्त करने के लिए अत्यंत उपयोगी है।

मूल अवधारणाएँ: ArrayBuffer, TypedArray, और DataView के बीच अंतर

ArrayBuffer एक मूलभूत डेटा संरचना है जिसका उपयोग निश्चित लंबाई वाले बाइट्स के अनुक्रम को संग्रहीत करने के लिए किया जाता है। TypedArray जैसे कि Uint8Array, ऐसे दृश्य हैं जो बफर को किसी विशेष प्रकार के ऐरे के रूप में मानते हैं, जहाँ प्रत्येक तत्व का निश्चित प्रकार होता है।

दूसरी ओर, DataView एक लचीला व्यू है जो आपको किसी भी डेटा प्रकार के मानों को किसी भी ऑफ़सेट स्थिति पर पढ़ने और लिखने की अनुमति देता है। क्योंकि यह निश्चित प्रकारों को मानकर नहीं चलता और बाइट स्तर पर सटीक नियंत्रण की अनुमति देता है, इसलिए यह बाइनरी प्रोटोकॉल का विश्लेषण करने और निम्न-स्तरीय प्रोसेसिंग के लिए उपयुक्त है।

नीचे दिया गया उदाहरण ArrayBuffer बनाने और फिर उससे Uint8ArrayDataView बनाने का है।

 1// Create an ArrayBuffer (8 bytes)
 2const buffer = new ArrayBuffer(8);
 3
 4// Create a Uint8Array view (easy to manipulate individual bytes)
 5const u8 = new Uint8Array(buffer);
 6u8[0] = 0x12;
 7u8[1] = 0x34;
 8
 9// Create a DataView (allows reading/writing any type at any offset)
10const dv = new DataView(buffer);
11
12// Read a 16-bit unsigned integer (big-endian assumed if second argument is omitted)
13console.log(dv.getUint16(0)); // 0x1234
  • इस कोड में, एक ही बफर पर दो विभिन्न प्रकार के व्यू—Uint8Array और DataView—एक साथ उपयोग किए गए हैं। DataView का उपयोग करके, आप मनचाहा ऑफसेट और एंडिएननेस निर्दिष्ट करके मूल्यों को लचीले तरीके से पढ़ और लिख सकते हैं।

DataView की मुख्य विधियाँ

DataView एक इंटरफ़ेस है जो बाइट स्तर पर ArrayBuffer को नियंत्रित करता है, और इसका उपयोग विभिन्न प्रकार (जैसे पूर्णांक और फ्लोटिंग-पॉइंट संख्या) पढ़ने और लिखने के लिए किया जाता है।

मुख्य विधियाँ निम्नलिखित हैं:।

  • पढ़ें: getUint8, getInt8, getUint16, getInt16, getUint32, getInt32, getFloat32, getFloat64
  • लिखें: setUint8, setInt8, setUint16, setInt16, setUint32, setInt32, setFloat32, setFloat64

इन सभी विधियों में पहला तर्क 'बाइट ऑफ़सेट' होता है: get उस स्थान पर मान पढ़ता है, और set उस स्थान पर मान लिखता है। 16, 32, या 64-बिट डेटा के साथ काम करते समय, आप दूसरे तर्क में एंडिएननेस भी निर्दिष्ट कर सकते हैं। व्यावहारिक रूप से, डेटा विनिर्देशन के अनुसार हमेशा एंडिएननेस निर्दिष्ट करना सबसे सुरक्षित होता है।

नीचे दिया उदाहरण दिखाता है कि कैसे बफर में लिटिल एंडियन फॉर्मेट में 32-बिट पूर्णांक और 32-बिट फ्लोटिंग-पॉइंट संख्या लिखी और पढ़ी जा सकती है।

 1const buf = new ArrayBuffer(12); // Enough space for integer + float
 2const view = new DataView(buf);
 3
 4// Write a 32-bit unsigned integer at offset 0 (little-endian)
 5view.setUint32(0, 305419896, true); // 305419896 = 0x12345678
 6
 7// Read the same 32-bit unsigned integer back from offset 0 (little-endian)
 8const intVal = view.getUint32(0, true);
 9console.log(intVal); // 305419896
10
11// Write a 32-bit floating number at offset 4 (little-endian)
12view.setFloat32(4, 3.1415926, true);
13
14// Read the 32-bit floating number back from offset 4 (little-endian)
15const floatVal = view.getFloat32(4, true);
16console.log(floatVal); // 3.1415926 (approx)
  • स्पष्ट रूप से एंडिएननेस निर्दिष्ट करके, आप विभिन्न प्लेटफार्मों और बाइनरी विनिर्देशों के साथ अनुकूलता सुनिश्चित कर सकते हैं।

एंडिएननेस (बाइट क्रम) के बारे में

कुछ प्रोटोकॉल (जैसे नेटवर्किंग में प्रयुक्त) बिग-एंडियन का उपयोग करते हैं, जबकि कई CPU और फाइल फॉर्मेट प्रायः लिटिल-एंडियन डिजाइन का उपयोग करते हैं। DataView में यदि दूसरा तर्क true है, तो डेटा को लिटिल एंडियन समझा जाता है; यदि यह false है या छोड़ा जाता है, तो इसे बिग एंडियन माना जाता है।

निम्नलिखित उदाहरण में, आप देख सकते हैं कि जब समान संख्या को big-endian और little-endian दोनों प्रारूपों का उपयोग करके लिखा जाता है तो मेमोरी में बाइट ऐरे कैसे बदलता है।

 1const b = new ArrayBuffer(4);
 2const a = new Uint8Array(b);
 3const dv = new DataView(b);
 4
 5// Write in big-endian order
 6dv.setUint32(0, 0x0A0B0C0D, false); // big-endian
 7console.log([...a]); // [10, 11, 12, 13]
 8
 9// Write the same value in little-endian order
10dv.setUint32(0, 0x0A0B0C0D, true); // little-endian
11console.log([...a]); // [13, 12, 11, 10]
  • यह समझना कि बाइट क्रम बिग एंडियन और लिटिल एंडियन के बीच कैसे बदलता है, संचार डेटा या बाइनरी प्रारूपों का विश्लेषण करना बहुत आसान बना देगा।

स्ट्रिंग इनपुट/आउटपुट (TextEncoder और Decoder का एक साथ उपयोग)

DataView संख्याएँ पढ़ने और लिखने के लिए उत्कृष्ट है, लेकिन यह सीधे स्ट्रिंग्स को संभालता नहीं है। आमतौर पर स्ट्रिंग्स को TextEncoder या TextDecoder (जैसे UTF-8 में) द्वारा एनकोड करके, फिर उन्हें Uint8Array के माध्यम से बफर में कॉपी किया जाता है। नीचे एक उदाहरण है जिसमें UTF-8 स्ट्रिंग को बफर में लिखा गया है और फिर पढ़ा गया है।

 1const encoder = new TextEncoder();
 2const decoder = new TextDecoder();
 3
 4const str = "\u3053\u3093\u306b\u3061\u306f";
 5const encoded = encoder.encode(str); // Uint8Array (UTF-8 encoded bytes)
 6
 7// Create a buffer and write the encoded string bytes into it
 8const buf2 = new ArrayBuffer(encoded.length);
 9const u8v = new Uint8Array(buf2);
10u8v.set(encoded);
11
12// Read the bytes and decode them back into a string
13const decodedStr = decoder.decode(new Uint8Array(buf2));
14console.log(decodedStr);
  • स्ट्रिंग्स को बाइनरी में रूपांतरित करके, और आवश्यकता पड़ने पर उनकी लंबाई पहले जोड़कर, आप परिवर्तनीय लंबाई वाली स्ट्रिंग्स को स्टोर कर सकते हैं।

व्यावहारिक उदाहरण: कस्टम बाइनरी फ़ॉर्मेट का एनकोडिंग/डीकोडिंग

नीचे, हम एक सरल संदेश प्रारूप परिभाषित करते हैं जिसमें एक संस्करण नंबर, आईडी और नाम शामिल है। हम एक एनकोडिंग प्रक्रिया को लागू करते हैं जो संदेशों को बाइनरी डेटा में परिवर्तित करती है, और एक डिकोडिंग प्रक्रिया को लागू करते हैं जो मूल ऑब्जेक्ट को बाइनरी से पुनर्स्थापित करती है। इस संदेश प्रारूप में, पहला बाइट संस्करण संग्रहीत करता है, अगले 4 बाइट्स छोटे एंडियन प्रारूप में आईडी संग्रहीत करते हैं, उसके बाद वाला बाइट नाम की लंबाई रखता है, और अंत में UTF-8 एन्कोडेड नाम संग्रहीत किया जाता है।

 1// Encode an object into a binary message
 2function encodeMessage(msg: { version: number; id: number; name: string }): ArrayBuffer {
 3  const encoder = new TextEncoder();
 4  const nameBytes = encoder.encode(msg.name);
 5  const total = 1 + 4 + 1 + nameBytes.length;
 6  const buf = new ArrayBuffer(total);
 7  const dv = new DataView(buf);
 8  let offset = 0;
 9
10  dv.setUint8(offset, msg.version); offset += 1;      // write version
11  dv.setUint32(offset, msg.id, true); offset += 4;    // write ID (little-endian)
12  dv.setUint8(offset, nameBytes.length); offset += 1; // write name length
13
14  // write name bytes
15  new Uint8Array(buf, offset).set(nameBytes);
16  return buf;
17}
18
19// Decode a binary message back into an object
20function decodeMessage(buf: ArrayBuffer) {
21  const dv = new DataView(buf);
22  let offset = 0;
23
24  const version = dv.getUint8(offset); offset += 1;   // read version
25  const id = dv.getUint32(offset, true); offset += 4; // read ID (little-endian)
26  const nameLen = dv.getUint8(offset); offset += 1;   // read name length
27
28  // extract name bytes
29  const nameBytes = new Uint8Array(buf, offset, nameLen);
30  const decoder = new TextDecoder();
31  // decode UTF-8 string
32  const name = decoder.decode(nameBytes);
33
34  return { version, id, name };
35}
36
37// Example usage
38const packed = encodeMessage({ version: 1, id: 42, name: "Alice" });
39const unpacked = decodeMessage(packed);
40console.log(unpacked); // { version: 1, id: 42, name: "Alice" }
  • यह एक आम कार्यान्वयन है बदलती लंबाई के स्ट्रिंग वाले मूल संदेश फॉर्मेट के लिए, और यह नेटवर्क संचार या कस्टम बाइनरी फाइल डिज़ाइन जैसी कई व्यावहारिक स्थितियों में लागू किया जा सकता है।

सावधानियाँ और सर्वोत्तम प्रक्रियाएँ

DataView के साथ बाइनरी डेटा पर काम करते समय, केवल पढ़ने या लिखने के अलावा कई व्यावहारिक बातें ध्यान रखनी चाहिए — जैसे सुरक्षा, एंडिएननेस का एकरूप उपयोग, तथा प्रकारों का उचित प्रबंधन। विशेष रूप से, जब आप बाहरी स्रोतों से प्राप्त बाइनरी डेटा या बड़े पूर्णांकों के साथ काम करते हैं, तब कोड को इस तरह डिज़ाइन करना आवश्यक है कि गलत पढ़ाई और बफर ओवररन न हों। व्यावहारिक उपयोग के लिए ध्यान रखने योग्य कुछ उपयोगी बिंदु नीचे दिए गए हैं।

  • सीमा जाँच यदि ऑफ़सेट या आकार बफर सीमा से अधिक हो जाता है तो DataView अपवाद फेंकता है। अविश्वसनीय बाइनरी डेटा से निपटते समय हमेशा लंबाई की जाँच करें।

  • हमेशा एंडिएननेस निर्दिष्ट करें अपने कोड में हमेशा स्पष्ट और एकरूपता से लिटिल एंडियन या बिग एंडियन निर्दिष्ट करें।

  • प्रकार की निरंतरता JavaScript में संख्याएँ 64-बिट IEEE-754 फ्लोटिंग-पॉइंट मान होती हैं। getUint32 जैसी विधियाँ उपयुक्त रूप से संभाली जाती हैं, लेकिन getUint64 नहीं होती, इसलिए 64-बिट पूर्णांकों के लिए विशेष प्रबंधन की आवश्यकता होती है।

  • 64-बिट पूर्णांक का प्रबंधन 64-बिट पूर्णांकों के प्रबंधन के लिए आपको BigInt का उपयोग करना चाहिए या मान को ऊपरी और निचले 32-बिट भागों में विभाजित करना चाहिए। यहाँ 64-बिट अनसाइन्ड पूर्णांक को पढ़ने का एक साधारण उदाहरण है।

1function getUint64(dv: DataView, offset: number, littleEndian = true): bigint {
2  const low = BigInt(dv.getUint32(offset, littleEndian));
3  const high = BigInt(dv.getUint32(offset + 4, littleEndian));
4  return littleEndian ? (high << 32n) | low : (low << 32n) | high;
5}
  • BigInt का उपयोग करके आप 64-बिट से बड़े पूर्णांकों को भी सुरक्षित तरीके से संभाल सकते हैं।

Node.js में उपयोग (Buffer के साथ इंटरऑपरेटिंग)

हालांकि Buffer आमतौर पर Node.js में उपयोग किया जाता है, ArrayBuffer और DataView के बीच भी आसानी से रूपांतरण किया जा सकता है। परिवर्तन के लिए Buffer ऑब्जेक्ट्स की buffer प्रॉपर्टी या Uint8Array कंस्ट्रक्टर का उपयोग करें।

1// Node.js: Buffer -> ArrayBuffer
2const nodeBuf = Buffer.from([1,2,3,4]);
3const arrBuf = nodeBuf.buffer.slice(nodeBuf.byteOffset, nodeBuf.byteOffset + nodeBuf.byteLength);
4const dvNode = new DataView(arrBuf);
5console.log(dvNode.getUint16(0));
  • यह Node.js और ब्राउज़र के बीच बाइनरी डेटा के आदान-प्रदान के लिये एक उपकरण के तौर पर इस्तेमाल किया जा सकता है।

सारांश

DataView बाइनरी डेटा को स्वतंत्र रूप से पढ़ने और लिखने के लिए एक शक्तिशाली तंत्र है—खासकर उन स्थितियों में जहां एंडिएननेस निर्दिष्ट करने और मनचाही स्थिति तक पहुँचने जैसी लचीली नियंत्रण की आवश्यकता होती है। ArrayBuffer, TypedArray, और DataView को एक साथ इस्तेमाल करके आप TypeScript में बाइनरी डेटा को लचीले व सटीक रूप से संभाल सकते हैं, जिससे प्रोटोकॉल इम्प्लीमेंटेशन से लेकर फाइल एनालिसिस तक कई उपयोग संभव होते हैं। साथ ही, आवश्यकता अनुसार स्ट्रिंग एनकोडिंग और 64-बिट पूर्णांक के प्रबंधन को शामिल करके, आप और भी व्यावहारिक बाइनरी ऑपरेशन हासिल कर सकते हैं।

आप हमारे YouTube चैनल पर Visual Studio Code का उपयोग करके ऊपर दिए गए लेख के साथ आगे बढ़ सकते हैं। कृपया YouTube चैनल को भी देखें।

YouTube Video