test.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. function pack (format) {
  2. // discuss at: http://locutus.io/php/pack/
  3. // original by: Tim de Koning (http://www.kingsquare.nl)
  4. // parts by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
  5. // bugfixed by: Tim de Koning (http://www.kingsquare.nl)
  6. // note 1: Float encoding by: Jonas Raoni Soares Silva
  7. // note 1: Home: http://www.kingsquare.nl/blog/12-12-2009/13507444
  8. // note 1: Feedback: phpjs-pack@kingsquare.nl
  9. // note 1: "machine dependent byte order and size" aren't
  10. // note 1: applicable for JavaScript; pack works as on a 32bit,
  11. // note 1: little endian machine.
  12. // example 1: pack('nvc*', 0x1234, 0x5678, 65, 66)
  13. // returns 1: '\u00124xVAB'
  14. // example 2: pack('H4', '2345')
  15. // returns 2: '#E'
  16. // example 3: pack('H*', 'D5')
  17. // returns 3: 'Õ'
  18. // example 4: pack('d', -100.876)
  19. // returns 4: "\u0000\u0000\u0000\u0000\u00008YÀ"
  20. // test: skip-1
  21. var formatPointer = 0
  22. var argumentPointer = 1
  23. var result = ''
  24. var argument = ''
  25. var i = 0
  26. var r = []
  27. var instruction, quantifier, word, precisionBits, exponentBits, extraNullCount
  28. // vars used by float encoding
  29. var bias
  30. var minExp
  31. var maxExp
  32. var minUnnormExp
  33. var status
  34. var exp
  35. var len
  36. var bin
  37. var signal
  38. var n
  39. var intPart
  40. var floatPart
  41. var lastBit
  42. var rounded
  43. var j
  44. var k
  45. var tmpResult
  46. while (formatPointer < format.length) {
  47. instruction = format.charAt(formatPointer)
  48. quantifier = ''
  49. formatPointer++
  50. while ((formatPointer < format.length) && (format.charAt(formatPointer)
  51. .match(/[\d*]/) !== null)) {
  52. quantifier += format.charAt(formatPointer)
  53. formatPointer++
  54. }
  55. if (quantifier === '') {
  56. quantifier = '1'
  57. }
  58. // Now pack variables: 'quantifier' times 'instruction'
  59. switch (instruction) {
  60. case 'a':
  61. case 'A':
  62. // NUL-padded string
  63. // SPACE-padded string
  64. if (typeof arguments[argumentPointer] === 'undefined') {
  65. throw new Error('Warning: pack() Type ' + instruction + ': not enough arguments')
  66. } else {
  67. argument = String(arguments[argumentPointer])
  68. }
  69. if (quantifier === '*') {
  70. quantifier = argument.length
  71. }
  72. for (i = 0; i < quantifier; i++) {
  73. if (typeof argument[i] === 'undefined') {
  74. if (instruction === 'a') {
  75. result += String.fromCharCode(0)
  76. } else {
  77. result += ' '
  78. }
  79. } else {
  80. result += argument[i]
  81. }
  82. }
  83. argumentPointer++
  84. break
  85. case 'h':
  86. case 'H':
  87. // Hex string, low nibble first
  88. // Hex string, high nibble first
  89. if (typeof arguments[argumentPointer] === 'undefined') {
  90. throw new Error('Warning: pack() Type ' + instruction + ': not enough arguments')
  91. } else {
  92. argument = arguments[argumentPointer]
  93. }
  94. if (quantifier === '*') {
  95. quantifier = argument.length
  96. }
  97. if (quantifier > argument.length) {
  98. var msg = 'Warning: pack() Type ' + instruction + ': not enough characters in string'
  99. throw new Error(msg)
  100. }
  101. for (i = 0; i < quantifier; i += 2) {
  102. // Always get per 2 bytes...
  103. word = argument[i]
  104. if (((i + 1) >= quantifier) || typeof argument[i + 1] === 'undefined') {
  105. word += '0'
  106. } else {
  107. word += argument[i + 1]
  108. }
  109. // The fastest way to reverse?
  110. if (instruction === 'h') {
  111. word = word[1] + word[0]
  112. }
  113. result += String.fromCharCode(parseInt(word, 16))
  114. }
  115. argumentPointer++
  116. break
  117. case 'c':
  118. case 'C':
  119. // signed char
  120. // unsigned char
  121. // c and C is the same in pack
  122. if (quantifier === '*') {
  123. quantifier = arguments.length - argumentPointer
  124. }
  125. if (quantifier > (arguments.length - argumentPointer)) {
  126. throw new Error('Warning: pack() Type ' + instruction + ': too few arguments')
  127. }
  128. for (i = 0; i < quantifier; i++) {
  129. result += String.fromCharCode(arguments[argumentPointer])
  130. argumentPointer++
  131. }
  132. break
  133. case 's':
  134. case 'S':
  135. case 'v':
  136. // signed short (always 16 bit, machine byte order)
  137. // unsigned short (always 16 bit, machine byte order)
  138. // s and S is the same in pack
  139. if (quantifier === '*') {
  140. quantifier = arguments.length - argumentPointer
  141. }
  142. if (quantifier > (arguments.length - argumentPointer)) {
  143. throw new Error('Warning: pack() Type ' + instruction + ': too few arguments')
  144. }
  145. for (i = 0; i < quantifier; i++) {
  146. result += String.fromCharCode(arguments[argumentPointer] & 0xFF)
  147. result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF)
  148. argumentPointer++
  149. }
  150. break
  151. case 'n':
  152. // unsigned short (always 16 bit, big endian byte order)
  153. if (quantifier === '*') {
  154. quantifier = arguments.length - argumentPointer
  155. }
  156. if (quantifier > (arguments.length - argumentPointer)) {
  157. throw new Error('Warning: pack() Type ' + instruction + ': too few arguments')
  158. }
  159. for (i = 0; i < quantifier; i++) {
  160. result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF)
  161. result += String.fromCharCode(arguments[argumentPointer] & 0xFF)
  162. argumentPointer++
  163. }
  164. break
  165. case 'i':
  166. case 'I':
  167. case 'l':
  168. case 'L':
  169. case 'V':
  170. // signed integer (machine dependent size and byte order)
  171. // unsigned integer (machine dependent size and byte order)
  172. // signed long (always 32 bit, machine byte order)
  173. // unsigned long (always 32 bit, machine byte order)
  174. // unsigned long (always 32 bit, little endian byte order)
  175. if (quantifier === '*') {
  176. quantifier = arguments.length - argumentPointer
  177. }
  178. if (quantifier > (arguments.length - argumentPointer)) {
  179. throw new Error('Warning: pack() Type ' + instruction + ': too few arguments')
  180. }
  181. for (i = 0; i < quantifier; i++) {
  182. result += String.fromCharCode(arguments[argumentPointer] & 0xFF)
  183. result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF)
  184. result += String.fromCharCode(arguments[argumentPointer] >> 16 & 0xFF)
  185. result += String.fromCharCode(arguments[argumentPointer] >> 24 & 0xFF)
  186. argumentPointer++
  187. }
  188. break
  189. case 'N':
  190. // unsigned long (always 32 bit, big endian byte order)
  191. if (quantifier === '*') {
  192. quantifier = arguments.length - argumentPointer
  193. }
  194. if (quantifier > (arguments.length - argumentPointer)) {
  195. throw new Error('Warning: pack() Type ' + instruction + ': too few arguments')
  196. }
  197. for (i = 0; i < quantifier; i++) {
  198. result += String.fromCharCode(arguments[argumentPointer] >> 24 & 0xFF)
  199. result += String.fromCharCode(arguments[argumentPointer] >> 16 & 0xFF)
  200. result += String.fromCharCode(arguments[argumentPointer] >> 8 & 0xFF)
  201. result += String.fromCharCode(arguments[argumentPointer] & 0xFF)
  202. argumentPointer++
  203. }
  204. break
  205. case 'f':
  206. case 'd':
  207. // float (machine dependent size and representation)
  208. // double (machine dependent size and representation)
  209. // version based on IEEE754
  210. precisionBits = 23
  211. exponentBits = 8
  212. if (instruction === 'd') {
  213. precisionBits = 52
  214. exponentBits = 11
  215. }
  216. if (quantifier === '*') {
  217. quantifier = arguments.length - argumentPointer
  218. }
  219. if (quantifier > (arguments.length - argumentPointer)) {
  220. throw new Error('Warning: pack() Type ' + instruction + ': too few arguments')
  221. }
  222. for (i = 0; i < quantifier; i++) {
  223. argument = arguments[argumentPointer]
  224. bias = Math.pow(2, exponentBits - 1) - 1
  225. minExp = -bias + 1
  226. maxExp = bias
  227. minUnnormExp = minExp - precisionBits
  228. status = isNaN(n = parseFloat(argument)) || n === -Infinity || n === +Infinity ? n : 0
  229. exp = 0
  230. len = 2 * bias + 1 + precisionBits + 3
  231. bin = new Array(len)
  232. signal = (n = status !== 0 ? 0 : n) < 0
  233. n = Math.abs(n)
  234. intPart = Math.floor(n)
  235. floatPart = n - intPart
  236. for (k = len; k;) {
  237. bin[--k] = 0
  238. }
  239. for (k = bias + 2; intPart && k;) {
  240. bin[--k] = intPart % 2
  241. intPart = Math.floor(intPart / 2)
  242. }
  243. for (k = bias + 1; floatPart > 0 && k; --floatPart) {
  244. (bin[++k] = ((floatPart *= 2) >= 1) - 0)
  245. }
  246. for (k = -1; ++k < len && !bin[k];) {}
  247. // @todo: Make this more readable:
  248. var key = (lastBit = precisionBits - 1 +
  249. (k =
  250. (exp = bias + 1 - k) >= minExp &&
  251. exp <= maxExp ? k + 1 : bias + 1 - (exp = minExp - 1))) + 1
  252. if (bin[key]) {
  253. if (!(rounded = bin[lastBit])) {
  254. for (j = lastBit + 2; !rounded && j < len; rounded = bin[j++]) {}
  255. }
  256. for (j = lastBit + 1; rounded && --j >= 0;
  257. (bin[j] = !bin[j] - 0) && (rounded = 0)) {}
  258. }
  259. for (k = k - 2 < 0 ? -1 : k - 3; ++k < len && !bin[k];) {}
  260. if ((exp = bias + 1 - k) >= minExp && exp <= maxExp) {
  261. ++k
  262. } else {
  263. if (exp < minExp) {
  264. if (exp !== bias + 1 - len && exp < minUnnormExp) {
  265. // "encodeFloat::float underflow"
  266. }
  267. k = bias + 1 - (exp = minExp - 1)
  268. }
  269. }
  270. if (intPart || status !== 0) {
  271. exp = maxExp + 1
  272. k = bias + 2
  273. if (status === -Infinity) {
  274. signal = 1
  275. } else if (isNaN(status)) {
  276. bin[k] = 1
  277. }
  278. }
  279. n = Math.abs(exp + bias)
  280. tmpResult = ''
  281. for (j = exponentBits + 1; --j;) {
  282. tmpResult = (n % 2) + tmpResult
  283. n = n >>= 1
  284. }
  285. n = 0
  286. j = 0
  287. k = (tmpResult = (signal ? '1' : '0') + tmpResult + (bin
  288. .slice(k, k + precisionBits)
  289. .join(''))
  290. ).length
  291. r = []
  292. for (; k;) {
  293. n += (1 << j) * tmpResult.charAt(--k)
  294. if (j === 7) {
  295. r[r.length] = String.fromCharCode(n)
  296. n = 0
  297. }
  298. j = (j + 1) % 8
  299. }
  300. r[r.length] = n ? String.fromCharCode(n) : ''
  301. result += r.join('')
  302. argumentPointer++
  303. }
  304. break
  305. case 'x':
  306. // NUL byte
  307. if (quantifier === '*') {
  308. throw new Error('Warning: pack(): Type x: \'*\' ignored')
  309. }
  310. for (i = 0; i < quantifier; i++) {
  311. result += String.fromCharCode(0)
  312. }
  313. break
  314. case 'X':
  315. // Back up one byte
  316. if (quantifier === '*') {
  317. throw new Error('Warning: pack(): Type X: \'*\' ignored')
  318. }
  319. for (i = 0; i < quantifier; i++) {
  320. if (result.length === 0) {
  321. throw new Error('Warning: pack(): Type X:' + ' outside of string')
  322. } else {
  323. result = result.substring(0, result.length - 1)
  324. }
  325. }
  326. break
  327. case '@':
  328. // NUL-fill to absolute position
  329. if (quantifier === '*') {
  330. throw new Error('Warning: pack(): Type X: \'*\' ignored')
  331. }
  332. if (quantifier > result.length) {
  333. extraNullCount = quantifier - result.length
  334. for (i = 0; i < extraNullCount; i++) {
  335. result += String.fromCharCode(0)
  336. }
  337. }
  338. if (quantifier < result.length) {
  339. result = result.substring(0, quantifier)
  340. }
  341. break
  342. default:
  343. throw new Error('Warning: pack() Type ' + instruction + ': unknown format code')
  344. }
  345. }
  346. if (argumentPointer < arguments.length) {
  347. var msg2 = 'Warning: pack(): ' + (arguments.length - argumentPointer) + ' arguments unused'
  348. throw new Error(msg2)
  349. }
  350. return result
  351. }