UNPKG

@catamphetamine/id3js

Version:

A modern ID3 parser written completely in JavaScript, making use of typed arrays and the HTML5 File API

142 lines (141 loc) 4.3 kB
/** * Retrieves a string from a specific offset of a data view * @param {DataView} view View to retrieve string from * @param {number|null} length Bytes to read * @param {number=} offset Offset to read from * @param {boolean=} raw Whether to return the raw string or not * @return {string} */ export function getString(view, length, offset = 0, raw) { let len = length || view.byteLength - offset; const useBuffer = typeof Buffer !== 'undefined'; if (len < 0) { len += view.byteLength; } const data = []; const limit = offset + len; for (let i = offset; i < limit; i++) { const current = view.getUint8(i); if (current === 0) { break; } data.push(current); } if (useBuffer) { return Buffer.from(data).toString(); } const str = data.map((chr) => String.fromCharCode(chr)).join(''); if (raw) { return str; } return decodeURIComponent(escape(str)); } /** * Retrieves a UTF16 string from a specific offset of a data view * @param {DataView} view View to retrieve string from * @param {number|null} length Bytes to read * @param {number=} offset Offset to read from * @param {boolean=} bom Whether to use BOM or not * @return {string} */ export function getStringUtf16(view, length, offset = 0, bom) { let littleEndian = false; let len = length || view.byteLength - offset; const str = []; const useBuffer = typeof Buffer !== 'undefined'; if (len < 0) { len += view.byteLength; } if (offset + 1 > view.byteLength) { return ''; } if (bom) { const bomInt = view.getUint16(offset); if (bomInt === 0xfffe) { littleEndian = true; } offset += 2; len -= 2; } const limit = offset + len; for (let i = offset; i < limit; i += 2) { let ch = view.getUint16(i, littleEndian); if (i < limit - 1 && ch === 0 && view.getUint16(i + 1, littleEndian) === 0) { break; } if ((ch >= 0 && ch <= 0xd7ff) || (ch >= 0xe000 && ch <= 0xffff)) { str.push(ch); } else if (ch >= 0x10000 && ch <= 0x10ffff) { ch -= 0x10000; str.push(((0xffc00 & ch) >> 10) + 0xd800); str.push((0x3ff & ch) + 0xdc00); } } if (useBuffer) { const buf = Buffer.alloc(str.length * 2); for (let i = 0; i < str.length; i++) { const chr = str[i]; if (littleEndian) { buf.writeUInt16LE(chr, i * 2); } else { buf.writeUInt16BE(chr, i * 2); } } return buf.toString(); } return String.fromCharCode.apply(null, new Uint16Array(str)); } /** * Gets the "synch" representation of a number * @param {number} num Number to convert * @return {number} */ export function getSynch(num) { let out = 0; let mask = 0x7f000000; while (mask) { out >>= 1; out |= num & mask; mask >>= 8; } return out; } /** * Gets a "synch2 uint8 from a view * @param {DataView} view View to read * @param {number=} offset Offset to read from * @return {number} */ export function getUint8Synch(view, offset = 0) { return getSynch(view.getUint8(offset)); } /** * Gets a "synch2 uint32 from a view * @param {DataView} view View to read * @param {number=} offset Offset to read from * @return {number} */ export function getUint32Synch(view, offset = 0) { return getSynch(view.getUint32(offset)); } /** * Gets a uint24 from a view * @param {DataView} view View to read * @param {number=} offset Offset to read from * @param {boolean=} littleEndian Whether to use little endian or not * @return {number} */ export function getUint24(view, offset = 0, littleEndian) { if (littleEndian) { return (view.getUint8(offset) + (view.getUint8(offset + 1) << 8) + (view.getUint8(offset + 2) << 16)); } return (view.getUint8(offset + 2) + (view.getUint8(offset + 1) << 8) + (view.getUint8(offset) << 16)); }