You you can install via yarn add locutus and
require this function via const pack = require('locutus/php/misc/pack').
It is important to use a bundler that supports tree-shaking
so that you only ship the functions that you actually use to your browser,
instead of all of Locutus, which is massive. Examples are:
Parcel,
webpack, or
rollup.js.
For server-side use this is typically less of a concern.
Examples
Please note that these examples are distilled from test cases that automatically verify
our functions still work correctly. This could explain some quirky ones.
Here’s what our current JavaScript equivalent to PHP's pack looks like.
module.exports = functionpack(format) { // discuss at: https://locutus.io/php/pack/ // original by: Tim de Koning (https://www.kingsquare.nl) // parts by: Jonas Raoni Soares Silva (https://www.jsfromhell.com) // bugfixed by: Tim de Koning (https://www.kingsquare.nl) // note 1: Float encoding by: Jonas Raoni Soares Silva // note 1: Home: https://www.kingsquare.nl/blog/12-12-2009/13507444 // note 1: Feedback: phpjs-pack@kingsquare.nl // note 1: "machine dependent byte order and size" aren't // note 1: applicable for JavaScript; pack works as on a 32bit, // note 1: little endian machine. // example 1: pack('nvc*', 0x1234, 0x5678, 65, 66) // returns 1: '\u00124xVAB' // example 2: pack('H4', '2345') // returns 2: '#E' // example 3: pack('H*', 'D5') // returns 3: 'Õ' // example 4: pack('d', -100.876) // returns 4: "\u0000\u0000\u0000\u0000\u00008YÀ" // test: skip-1
let formatPointer = 0 let argumentPointer = 1 let result = '' let argument = '' let i = 0 let r = [] let instruction, quantifier, word, precisionBits, exponentBits, extraNullCount
// vars used by float encoding let bias let minExp let maxExp let minUnnormExp let status let exp let len let bin let signal let n let intPart let floatPart let lastBit let rounded let j let k let tmpResult
// Now pack variables: 'quantifier' times 'instruction' switch (instruction) { case'a': case'A': // NUL-padded string // SPACE-padded string if (typeofarguments[argumentPointer] === 'undefined') { thrownewError('Warning: pack() Type ' + instruction + ': not enough arguments') } else { argument = String(arguments[argumentPointer]) } if (quantifier === '*') { quantifier = argument.length } for (i = 0; i < quantifier; i++) { if (typeof argument[i] === 'undefined') { if (instruction === 'a') { result += String.fromCharCode(0) } else { result += ' ' } } else { result += argument[i] } } argumentPointer++ break case'h': case'H': // Hex string, low nibble first // Hex string, high nibble first if (typeofarguments[argumentPointer] === 'undefined') { thrownewError('Warning: pack() Type ' + instruction + ': not enough arguments') } else { argument = arguments[argumentPointer] } if (quantifier === '*') { quantifier = argument.length } if (quantifier > argument.length) { const msg = 'Warning: pack() Type ' + instruction + ': not enough characters in string' thrownewError(msg) }
for (i = 0; i < quantifier; i += 2) { // Always get per 2 bytes... word = argument[i] if (i + 1 >= quantifier || typeof argument[i + 1] === 'undefined') { word += '0' } else { word += argument[i + 1] } // The fastest way to reverse? if (instruction === 'h') { word = word[1] + word[0] } result += String.fromCharCode(parseInt(word, 16)) } argumentPointer++ break
case'c': case'C': // signed char // unsigned char // c and C is the same in pack if (quantifier === '*') { quantifier = arguments.length - argumentPointer } if (quantifier > arguments.length - argumentPointer) { thrownewError('Warning: pack() Type ' + instruction + ': too few arguments') }
for (i = 0; i < quantifier; i++) { result += String.fromCharCode(arguments[argumentPointer]) argumentPointer++ } break
case's': case'S': case'v': // signed short (always 16 bit, machine byte order) // unsigned short (always 16 bit, machine byte order) // s and S is the same in pack if (quantifier === '*') { quantifier = arguments.length - argumentPointer } if (quantifier > arguments.length - argumentPointer) { thrownewError('Warning: pack() Type ' + instruction + ': too few arguments') }
for (i = 0; i < quantifier; i++) { result += String.fromCharCode(arguments[argumentPointer] & 0xff) result += String.fromCharCode((arguments[argumentPointer] >> 8) & 0xff) argumentPointer++ } break
case'n': // unsigned short (always 16 bit, big endian byte order) if (quantifier === '*') { quantifier = arguments.length - argumentPointer } if (quantifier > arguments.length - argumentPointer) { thrownewError('Warning: pack() Type ' + instruction + ': too few arguments') }
for (i = 0; i < quantifier; i++) { result += String.fromCharCode((arguments[argumentPointer] >> 8) & 0xff) result += String.fromCharCode(arguments[argumentPointer] & 0xff) argumentPointer++ } break
case'i': case'I': case'l': case'L': case'V': // signed integer (machine dependent size and byte order) // unsigned integer (machine dependent size and byte order) // signed long (always 32 bit, machine byte order) // unsigned long (always 32 bit, machine byte order) // unsigned long (always 32 bit, little endian byte order) if (quantifier === '*') { quantifier = arguments.length - argumentPointer } if (quantifier > arguments.length - argumentPointer) { thrownewError('Warning: pack() Type ' + instruction + ': too few arguments') }
for (i = 0; i < quantifier; i++) { result += String.fromCharCode(arguments[argumentPointer] & 0xff) result += String.fromCharCode((arguments[argumentPointer] >> 8) & 0xff) result += String.fromCharCode((arguments[argumentPointer] >> 16) & 0xff) result += String.fromCharCode((arguments[argumentPointer] >> 24) & 0xff) argumentPointer++ }
break case'N': // unsigned long (always 32 bit, big endian byte order) if (quantifier === '*') { quantifier = arguments.length - argumentPointer } if (quantifier > arguments.length - argumentPointer) { thrownewError('Warning: pack() Type ' + instruction + ': too few arguments') }
for (i = 0; i < quantifier; i++) { result += String.fromCharCode((arguments[argumentPointer] >> 24) & 0xff) result += String.fromCharCode((arguments[argumentPointer] >> 16) & 0xff) result += String.fromCharCode((arguments[argumentPointer] >> 8) & 0xff) result += String.fromCharCode(arguments[argumentPointer] & 0xff) argumentPointer++ } break
case'f': case'd': // float (machine dependent size and representation) // double (machine dependent size and representation) // version based on IEEE754 precisionBits = 23 exponentBits = 8 if (instruction === 'd') { precisionBits = 52 exponentBits = 11 }
if (quantifier === '*') { quantifier = arguments.length - argumentPointer } if (quantifier > arguments.length - argumentPointer) { thrownewError('Warning: pack() Type ' + instruction + ': too few arguments') } for (i = 0; i < quantifier; i++) { argument = arguments[argumentPointer] bias = Math.pow(2, exponentBits - 1) - 1 minExp = -bias + 1 maxExp = bias minUnnormExp = minExp - precisionBits status = isNaN((n = parseFloat(argument))) || n === -Infinity || n === +Infinity ? n : 0 exp = 0 len = 2 * bias + 1 + precisionBits + 3 bin = newArray(len) signal = (n = status !== 0 ? 0 : n) < 0 n = Math.abs(n) intPart = Math.floor(n) floatPart = n - intPart
if (intPart || status !== 0) { exp = maxExp + 1 k = bias + 2 if (status === -Infinity) { signal = 1 } elseif (isNaN(status)) { bin[k] = 1 } }
n = Math.abs(exp + bias) tmpResult = ''
for (j = exponentBits + 1; --j; ) { tmpResult = (n % 2) + tmpResult n = n >>= 1 }
n = 0 j = 0 k = (tmpResult = (signal ? '1' : '0') + tmpResult + bin.slice(k, k + precisionBits).join('')).length r = []
for (; k; ) { n += (1 << j) * tmpResult.charAt(--k) if (j === 7) { r[r.length] = String.fromCharCode(n) n = 0 } j = (j + 1) % 8 }
r[r.length] = n ? String.fromCharCode(n) : '' result += r.join('') argumentPointer++ } break
case'x': // NUL byte if (quantifier === '*') { thrownewError("Warning: pack(): Type x: '*' ignored") } for (i = 0; i < quantifier; i++) { result += String.fromCharCode(0) } break
case'X': // Back up one byte if (quantifier === '*') { thrownewError("Warning: pack(): Type X: '*' ignored") } for (i = 0; i < quantifier; i++) { if (result.length === 0) { thrownewError('Warning: pack(): Type X:' + ' outside of string') } else { result = result.substring(0, result.length - 1) } } break
case'@': // NUL-fill to absolute position if (quantifier === '*') { thrownewError("Warning: pack(): Type X: '*' ignored") } if (quantifier > result.length) { extraNullCount = quantifier - result.length for (i = 0; i < extraNullCount; i++) { result += String.fromCharCode(0) } } if (quantifier < result.length) { result = result.substring(0, quantifier) } break
Not unlike Wikipedia, Locutus is an ongoing community effort. Our philosophy follows
The McDonald’s Theory.
This means that we assimilate first iterations with imperfections,
hoping for others to take issue with-and improve them.
This unorthodox approach has worked very well to foster fun and fruitful collaboration,
but please be reminded to use our creations at your own risk.
THE SOFTWARE IS PROVIDED "AS IS" has never been more true than for Locutus.