Ein Projekt das es ermöglicht Beerpong über das Internet von zwei unabhängigen positionen aus zu spielen. Entstehung im Rahmen einer Praktikumsaufgabe im Fach Interaktion.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

dbcs-codec.js 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. // Multibyte codec. In this scheme, a character is represented by 1 or more bytes.
  2. // Our codec supports UTF-16 surrogates, extensions for GB18030 and unicode sequences.
  3. // To save memory and loading time, we read table files only when requested.
  4. exports._dbcs = function(options) {
  5. return new DBCSCodec(options);
  6. }
  7. var UNASSIGNED = -1,
  8. GB18030_CODE = -2,
  9. SEQ_START = -10,
  10. NODE_START = -1000,
  11. UNASSIGNED_NODE = new Array(0x100),
  12. DEF_CHAR = -1;
  13. for (var i = 0; i < 0x100; i++)
  14. UNASSIGNED_NODE[i] = UNASSIGNED;
  15. // Class DBCSCodec reads and initializes mapping tables.
  16. function DBCSCodec(options) {
  17. this.options = options;
  18. if (!options)
  19. throw new Error("DBCS codec is called without the data.")
  20. if (!options.table)
  21. throw new Error("Encoding '" + options.encodingName + "' has no data.");
  22. // Load tables.
  23. var mappingTable = options.table();
  24. // Decode tables: MBCS -> Unicode.
  25. // decodeTables is a trie, encoded as an array of arrays of integers. Internal arrays are trie nodes and all have len = 256.
  26. // Trie root is decodeTables[0].
  27. // Values: >= 0 -> unicode character code. can be > 0xFFFF
  28. // == UNASSIGNED -> unknown/unassigned sequence.
  29. // == GB18030_CODE -> this is the end of a GB18030 4-byte sequence.
  30. // <= NODE_START -> index of the next node in our trie to process next byte.
  31. // <= SEQ_START -> index of the start of a character code sequence, in decodeTableSeq.
  32. this.decodeTables = [];
  33. this.decodeTables[0] = UNASSIGNED_NODE.slice(0); // Create root node.
  34. // Sometimes a MBCS char corresponds to a sequence of unicode chars. We store them as arrays of integers here.
  35. this.decodeTableSeq = [];
  36. // Actual mapping tables consist of chunks. Use them to fill up decode tables.
  37. for (var i = 0; i < mappingTable.length; i++)
  38. this._addDecodeChunk(mappingTable[i]);
  39. this.defaultCharUnicode = options.iconv.defaultCharUnicode;
  40. // Encode tables: Unicode -> DBCS.
  41. // `encodeTable` is array mapping from unicode char to encoded char. All its values are integers for performance.
  42. // Because it can be sparse, it is represented as array of buckets by 256 chars each. Bucket can be null.
  43. // Values: >= 0 -> it is a normal char. Write the value (if <=256 then 1 byte, if <=65536 then 2 bytes, etc.).
  44. // == UNASSIGNED -> no conversion found. Output a default char.
  45. // <= SEQ_START -> it's an index in encodeTableSeq, see below. The character starts a sequence.
  46. this.encodeTable = [];
  47. // `encodeTableSeq` is used when a sequence of unicode characters is encoded as a single code. We use a tree of
  48. // objects where keys correspond to characters in sequence and leafs are the encoded dbcs values. A special DEF_CHAR key
  49. // means end of sequence (needed when one sequence is a strict subsequence of another).
  50. // Objects are kept separately from encodeTable to increase performance.
  51. this.encodeTableSeq = [];
  52. // Some chars can be decoded, but need not be encoded.
  53. var skipEncodeChars = {};
  54. if (options.encodeSkipVals)
  55. for (var i = 0; i < options.encodeSkipVals.length; i++) {
  56. var range = options.encodeSkipVals[i];
  57. for (var j = range.from; j <= range.to; j++)
  58. skipEncodeChars[j] = true;
  59. }
  60. // Use decode trie to recursively fill out encode tables.
  61. this._fillEncodeTable(0, 0, skipEncodeChars);
  62. // Add more encoding pairs when needed.
  63. for (var uChar in options.encodeAdd || {})
  64. this._setEncodeChar(uChar.charCodeAt(0), options.encodeAdd[uChar]);
  65. this.defCharSB = this.encodeTable[0][options.iconv.defaultCharSingleByte.charCodeAt(0)];
  66. if (this.defCharSB === UNASSIGNED) this.defCharSB = this.encodeTable[0]['?'];
  67. if (this.defCharSB === UNASSIGNED) this.defCharSB = "?".charCodeAt(0);
  68. // Load & create GB18030 tables when needed.
  69. if (typeof options.gb18030 === 'function') {
  70. this.gb18030 = options.gb18030(); // Load GB18030 ranges.
  71. // Add GB18030 decode tables.
  72. var thirdByteNodeIdx = this.decodeTables.length;
  73. var thirdByteNode = this.decodeTables[thirdByteNodeIdx] = UNASSIGNED_NODE.slice(0);
  74. var fourthByteNodeIdx = this.decodeTables.length;
  75. var fourthByteNode = this.decodeTables[fourthByteNodeIdx] = UNASSIGNED_NODE.slice(0);
  76. for (var i = 0x81; i <= 0xFE; i++) {
  77. var secondByteNodeIdx = NODE_START - this.decodeTables[0][i];
  78. var secondByteNode = this.decodeTables[secondByteNodeIdx];
  79. for (var j = 0x30; j <= 0x39; j++)
  80. secondByteNode[j] = NODE_START - thirdByteNodeIdx;
  81. }
  82. for (var i = 0x81; i <= 0xFE; i++)
  83. thirdByteNode[i] = NODE_START - fourthByteNodeIdx;
  84. for (var i = 0x30; i <= 0x39; i++)
  85. fourthByteNode[i] = GB18030_CODE
  86. }
  87. }
  88. // Public interface: create encoder and decoder objects.
  89. // The methods (write, end) are simple functions to not inhibit optimizations.
  90. DBCSCodec.prototype.encoder = function encoderDBCS(options) {
  91. return {
  92. // Methods
  93. write: encoderDBCSWrite,
  94. end: encoderDBCSEnd,
  95. // Encoder state
  96. leadSurrogate: -1,
  97. seqObj: undefined,
  98. // Static data
  99. encodeTable: this.encodeTable,
  100. encodeTableSeq: this.encodeTableSeq,
  101. defaultCharSingleByte: this.defCharSB,
  102. gb18030: this.gb18030,
  103. // Export for testing
  104. findIdx: findIdx,
  105. }
  106. }
  107. DBCSCodec.prototype.decoder = function decoderDBCS(options) {
  108. return {
  109. // Methods
  110. write: decoderDBCSWrite,
  111. end: decoderDBCSEnd,
  112. // Decoder state
  113. nodeIdx: 0,
  114. prevBuf: new Buffer(0),
  115. // Static data
  116. decodeTables: this.decodeTables,
  117. decodeTableSeq: this.decodeTableSeq,
  118. defaultCharUnicode: this.defaultCharUnicode,
  119. gb18030: this.gb18030,
  120. }
  121. }
  122. // Decoder helpers
  123. DBCSCodec.prototype._getDecodeTrieNode = function(addr) {
  124. var bytes = [];
  125. for (; addr > 0; addr >>= 8)
  126. bytes.push(addr & 0xFF);
  127. if (bytes.length == 0)
  128. bytes.push(0);
  129. var node = this.decodeTables[0];
  130. for (var i = bytes.length-1; i > 0; i--) { // Traverse nodes deeper into the trie.
  131. var val = node[bytes[i]];
  132. if (val == UNASSIGNED) { // Create new node.
  133. node[bytes[i]] = NODE_START - this.decodeTables.length;
  134. this.decodeTables.push(node = UNASSIGNED_NODE.slice(0));
  135. }
  136. else if (val <= NODE_START) { // Existing node.
  137. node = this.decodeTables[NODE_START - val];
  138. }
  139. else
  140. throw new Error("Overwrite byte in " + this.options.encodingName + ", addr: " + addr.toString(16));
  141. }
  142. return node;
  143. }
  144. DBCSCodec.prototype._addDecodeChunk = function(chunk) {
  145. // First element of chunk is the hex mbcs code where we start.
  146. var curAddr = parseInt(chunk[0], 16);
  147. // Choose the decoding node where we'll write our chars.
  148. var writeTable = this._getDecodeTrieNode(curAddr);
  149. curAddr = curAddr & 0xFF;
  150. // Write all other elements of the chunk to the table.
  151. for (var k = 1; k < chunk.length; k++) {
  152. var part = chunk[k];
  153. if (typeof part === "string") { // String, write as-is.
  154. for (var l = 0; l < part.length;) {
  155. var code = part.charCodeAt(l++);
  156. if (0xD800 <= code && code < 0xDC00) { // Decode surrogate
  157. var codeTrail = part.charCodeAt(l++);
  158. if (0xDC00 <= codeTrail && codeTrail < 0xE000)
  159. writeTable[curAddr++] = 0x10000 + (code - 0xD800) * 0x400 + (codeTrail - 0xDC00);
  160. else
  161. throw new Error("Incorrect surrogate pair in " + this.options.encodingName + " at chunk " + chunk[0]);
  162. }
  163. else if (0x0FF0 < code && code <= 0x0FFF) { // Character sequence (our own encoding used)
  164. var len = 0xFFF - code + 2;
  165. var seq = [];
  166. for (var m = 0; m < len; m++)
  167. seq.push(part.charCodeAt(l++)); // Simple variation: don't support surrogates or subsequences in seq.
  168. writeTable[curAddr++] = SEQ_START - this.decodeTableSeq.length;
  169. this.decodeTableSeq.push(seq);
  170. }
  171. else
  172. writeTable[curAddr++] = code; // Basic char
  173. }
  174. }
  175. else if (typeof part === "number") { // Integer, meaning increasing sequence starting with prev character.
  176. var charCode = writeTable[curAddr - 1] + 1;
  177. for (var l = 0; l < part; l++)
  178. writeTable[curAddr++] = charCode++;
  179. }
  180. else
  181. throw new Error("Incorrect type '" + typeof part + "' given in " + this.options.encodingName + " at chunk " + chunk[0]);
  182. }
  183. if (curAddr > 0xFF)
  184. throw new Error("Incorrect chunk in " + this.options.encodingName + " at addr " + chunk[0] + ": too long" + curAddr);
  185. }
  186. // Encoder helpers
  187. DBCSCodec.prototype._getEncodeBucket = function(uCode) {
  188. var high = uCode >> 8; // This could be > 0xFF because of astral characters.
  189. if (this.encodeTable[high] === undefined)
  190. this.encodeTable[high] = UNASSIGNED_NODE.slice(0); // Create bucket on demand.
  191. return this.encodeTable[high];
  192. }
  193. DBCSCodec.prototype._setEncodeChar = function(uCode, dbcsCode) {
  194. var bucket = this._getEncodeBucket(uCode);
  195. var low = uCode & 0xFF;
  196. if (bucket[low] <= SEQ_START)
  197. this.encodeTableSeq[SEQ_START-bucket[low]][DEF_CHAR] = dbcsCode; // There's already a sequence, set a single-char subsequence of it.
  198. else if (bucket[low] == UNASSIGNED)
  199. bucket[low] = dbcsCode;
  200. }
  201. DBCSCodec.prototype._setEncodeSequence = function(seq, dbcsCode) {
  202. // Get the root of character tree according to first character of the sequence.
  203. var uCode = seq[0];
  204. var bucket = this._getEncodeBucket(uCode);
  205. var low = uCode & 0xFF;
  206. var node;
  207. if (bucket[low] <= SEQ_START) {
  208. // There's already a sequence with - use it.
  209. node = this.encodeTableSeq[SEQ_START-bucket[low]];
  210. }
  211. else {
  212. // There was no sequence object - allocate a new one.
  213. node = {};
  214. if (bucket[low] !== UNASSIGNED) node[DEF_CHAR] = bucket[low]; // If a char was set before - make it a single-char subsequence.
  215. bucket[low] = SEQ_START - this.encodeTableSeq.length;
  216. this.encodeTableSeq.push(node);
  217. }
  218. // Traverse the character tree, allocating new nodes as needed.
  219. for (var j = 1; j < seq.length-1; j++) {
  220. var oldVal = node[uCode];
  221. if (typeof oldVal === 'object')
  222. node = oldVal;
  223. else {
  224. node = node[uCode] = {}
  225. if (oldVal !== undefined)
  226. node[DEF_CHAR] = oldVal
  227. }
  228. }
  229. // Set the leaf to given dbcsCode.
  230. uCode = seq[seq.length-1];
  231. node[uCode] = dbcsCode;
  232. }
  233. DBCSCodec.prototype._fillEncodeTable = function(nodeIdx, prefix, skipEncodeChars) {
  234. var node = this.decodeTables[nodeIdx];
  235. for (var i = 0; i < 0x100; i++) {
  236. var uCode = node[i];
  237. var mbCode = prefix + i;
  238. if (skipEncodeChars[mbCode])
  239. continue;
  240. if (uCode >= 0)
  241. this._setEncodeChar(uCode, mbCode);
  242. else if (uCode <= NODE_START)
  243. this._fillEncodeTable(NODE_START - uCode, mbCode << 8, skipEncodeChars);
  244. else if (uCode <= SEQ_START)
  245. this._setEncodeSequence(this.decodeTableSeq[SEQ_START - uCode], mbCode);
  246. }
  247. }
  248. // == Actual Encoding ==========================================================
  249. function encoderDBCSWrite(str) {
  250. var newBuf = new Buffer(str.length * (this.gb18030 ? 4 : 3)),
  251. leadSurrogate = this.leadSurrogate,
  252. seqObj = this.seqObj, nextChar = -1,
  253. i = 0, j = 0;
  254. while (true) {
  255. // 0. Get next character.
  256. if (nextChar === -1) {
  257. if (i == str.length) break;
  258. var uCode = str.charCodeAt(i++);
  259. }
  260. else {
  261. var uCode = nextChar;
  262. nextChar = -1;
  263. }
  264. // 1. Handle surrogates.
  265. if (0xD800 <= uCode && uCode < 0xE000) { // Char is one of surrogates.
  266. if (uCode < 0xDC00) { // We've got lead surrogate.
  267. if (leadSurrogate === -1) {
  268. leadSurrogate = uCode;
  269. continue;
  270. } else {
  271. leadSurrogate = uCode;
  272. // Double lead surrogate found.
  273. uCode = UNASSIGNED;
  274. }
  275. } else { // We've got trail surrogate.
  276. if (leadSurrogate !== -1) {
  277. uCode = 0x10000 + (leadSurrogate - 0xD800) * 0x400 + (uCode - 0xDC00);
  278. leadSurrogate = -1;
  279. } else {
  280. // Incomplete surrogate pair - only trail surrogate found.
  281. uCode = UNASSIGNED;
  282. }
  283. }
  284. }
  285. else if (leadSurrogate !== -1) {
  286. // Incomplete surrogate pair - only lead surrogate found.
  287. nextChar = uCode; uCode = UNASSIGNED; // Write an error, then current char.
  288. leadSurrogate = -1;
  289. }
  290. // 2. Convert uCode character.
  291. var dbcsCode = UNASSIGNED;
  292. if (seqObj !== undefined && uCode != UNASSIGNED) { // We are in the middle of the sequence
  293. var resCode = seqObj[uCode];
  294. if (typeof resCode === 'object') { // Sequence continues.
  295. seqObj = resCode;
  296. continue;
  297. } else if (typeof resCode == 'number') { // Sequence finished. Write it.
  298. dbcsCode = resCode;
  299. } else if (resCode == undefined) { // Current character is not part of the sequence.
  300. // Try default character for this sequence
  301. resCode = seqObj[DEF_CHAR];
  302. if (resCode !== undefined) {
  303. dbcsCode = resCode; // Found. Write it.
  304. nextChar = uCode; // Current character will be written too in the next iteration.
  305. } else {
  306. // TODO: What if we have no default? (resCode == undefined)
  307. // Then, we should write first char of the sequence as-is and try the rest recursively.
  308. // Didn't do it for now because no encoding has this situation yet.
  309. // Currently, just skip the sequence and write current char.
  310. }
  311. }
  312. seqObj = undefined;
  313. }
  314. else if (uCode >= 0) { // Regular character
  315. var subtable = this.encodeTable[uCode >> 8];
  316. if (subtable !== undefined)
  317. dbcsCode = subtable[uCode & 0xFF];
  318. if (dbcsCode <= SEQ_START) { // Sequence start
  319. seqObj = this.encodeTableSeq[SEQ_START-dbcsCode];
  320. continue;
  321. }
  322. if (dbcsCode == UNASSIGNED && this.gb18030) {
  323. // Use GB18030 algorithm to find character(s) to write.
  324. var idx = findIdx(this.gb18030.uChars, uCode);
  325. if (idx != -1) {
  326. var dbcsCode = this.gb18030.gbChars[idx] + (uCode - this.gb18030.uChars[idx]);
  327. newBuf[j++] = 0x81 + Math.floor(dbcsCode / 12600); dbcsCode = dbcsCode % 12600;
  328. newBuf[j++] = 0x30 + Math.floor(dbcsCode / 1260); dbcsCode = dbcsCode % 1260;
  329. newBuf[j++] = 0x81 + Math.floor(dbcsCode / 10); dbcsCode = dbcsCode % 10;
  330. newBuf[j++] = 0x30 + dbcsCode;
  331. continue;
  332. }
  333. }
  334. }
  335. // 3. Write dbcsCode character.
  336. if (dbcsCode === UNASSIGNED)
  337. dbcsCode = this.defaultCharSingleByte;
  338. if (dbcsCode < 0x100) {
  339. newBuf[j++] = dbcsCode;
  340. }
  341. else if (dbcsCode < 0x10000) {
  342. newBuf[j++] = dbcsCode >> 8; // high byte
  343. newBuf[j++] = dbcsCode & 0xFF; // low byte
  344. }
  345. else {
  346. newBuf[j++] = dbcsCode >> 16;
  347. newBuf[j++] = (dbcsCode >> 8) & 0xFF;
  348. newBuf[j++] = dbcsCode & 0xFF;
  349. }
  350. }
  351. this.seqObj = seqObj;
  352. this.leadSurrogate = leadSurrogate;
  353. return newBuf.slice(0, j);
  354. }
  355. function encoderDBCSEnd() {
  356. if (this.leadSurrogate === -1 && this.seqObj === undefined)
  357. return; // All clean. Most often case.
  358. var newBuf = new Buffer(10), j = 0;
  359. if (this.seqObj) { // We're in the sequence.
  360. var dbcsCode = this.seqObj[DEF_CHAR];
  361. if (dbcsCode !== undefined) { // Write beginning of the sequence.
  362. if (dbcsCode < 0x100) {
  363. newBuf[j++] = dbcsCode;
  364. }
  365. else {
  366. newBuf[j++] = dbcsCode >> 8; // high byte
  367. newBuf[j++] = dbcsCode & 0xFF; // low byte
  368. }
  369. } else {
  370. // See todo above.
  371. }
  372. this.seqObj = undefined;
  373. }
  374. if (this.leadSurrogate !== -1) {
  375. // Incomplete surrogate pair - only lead surrogate found.
  376. newBuf[j++] = this.defaultCharSingleByte;
  377. this.leadSurrogate = -1;
  378. }
  379. return newBuf.slice(0, j);
  380. }
  381. // == Actual Decoding ==========================================================
  382. function decoderDBCSWrite(buf) {
  383. var newBuf = new Buffer(buf.length*2),
  384. nodeIdx = this.nodeIdx,
  385. prevBuf = this.prevBuf, prevBufOffset = this.prevBuf.length,
  386. seqStart = -this.prevBuf.length, // idx of the start of current parsed sequence.
  387. uCode;
  388. if (prevBufOffset > 0) // Make prev buf overlap a little to make it easier to slice later.
  389. prevBuf = Buffer.concat([prevBuf, buf.slice(0, 10)]);
  390. for (var i = 0, j = 0; i < buf.length; i++) {
  391. var curByte = (i >= 0) ? buf[i] : prevBuf[i + prevBufOffset];
  392. // Lookup in current trie node.
  393. var uCode = this.decodeTables[nodeIdx][curByte];
  394. if (uCode >= 0) {
  395. // Normal character, just use it.
  396. }
  397. else if (uCode === UNASSIGNED) { // Unknown char.
  398. // TODO: Callback with seq.
  399. //var curSeq = (seqStart >= 0) ? buf.slice(seqStart, i+1) : prevBuf.slice(seqStart + prevBufOffset, i+1 + prevBufOffset);
  400. i = seqStart; // Try to parse again, after skipping first byte of the sequence ('i' will be incremented by 'for' cycle).
  401. uCode = this.defaultCharUnicode.charCodeAt(0);
  402. }
  403. else if (uCode === GB18030_CODE) {
  404. var curSeq = (seqStart >= 0) ? buf.slice(seqStart, i+1) : prevBuf.slice(seqStart + prevBufOffset, i+1 + prevBufOffset);
  405. var ptr = (curSeq[0]-0x81)*12600 + (curSeq[1]-0x30)*1260 + (curSeq[2]-0x81)*10 + (curSeq[3]-0x30);
  406. var idx = findIdx(this.gb18030.gbChars, ptr);
  407. uCode = this.gb18030.uChars[idx] + ptr - this.gb18030.gbChars[idx];
  408. }
  409. else if (uCode <= NODE_START) { // Go to next trie node.
  410. nodeIdx = NODE_START - uCode;
  411. continue;
  412. }
  413. else if (uCode <= SEQ_START) { // Output a sequence of chars.
  414. var seq = this.decodeTableSeq[SEQ_START - uCode];
  415. for (var k = 0; k < seq.length - 1; k++) {
  416. uCode = seq[k];
  417. newBuf[j++] = uCode & 0xFF;
  418. newBuf[j++] = uCode >> 8;
  419. }
  420. uCode = seq[seq.length-1];
  421. }
  422. else
  423. throw new Error("Unknown table value when decoding: " + val);
  424. // Write the character to buffer, handling higher planes using surrogate pair.
  425. if (uCode > 0xFFFF) {
  426. uCode -= 0x10000;
  427. var uCodeLead = 0xD800 + Math.floor(uCode / 0x400);
  428. newBuf[j++] = uCodeLead & 0xFF;
  429. newBuf[j++] = uCodeLead >> 8;
  430. uCode = 0xDC00 + uCode % 0x400;
  431. }
  432. newBuf[j++] = uCode & 0xFF;
  433. newBuf[j++] = uCode >> 8;
  434. // Reset trie node.
  435. nodeIdx = 0; seqStart = i+1;
  436. }
  437. this.nodeIdx = nodeIdx;
  438. this.prevBuf = (seqStart >= 0) ? buf.slice(seqStart) : prevBuf.slice(seqStart + prevBufOffset);
  439. return newBuf.slice(0, j).toString('ucs2');
  440. }
  441. function decoderDBCSEnd() {
  442. var ret = '';
  443. // Try to parse all remaining chars.
  444. while (this.prevBuf.length > 0) {
  445. // Skip 1 character in the buffer.
  446. ret += this.defaultCharUnicode;
  447. var buf = this.prevBuf.slice(1);
  448. // Parse remaining as usual.
  449. this.prevBuf = new Buffer(0);
  450. this.nodeIdx = 0;
  451. if (buf.length > 0)
  452. ret += decoderDBCSWrite.call(this, buf);
  453. }
  454. this.nodeIdx = 0;
  455. return ret;
  456. }
  457. // Binary search for GB18030. Returns largest i such that table[i] <= val.
  458. function findIdx(table, val) {
  459. if (table[0] > val)
  460. return -1;
  461. var l = 0, r = table.length;
  462. while (l < r-1) { // always table[l] <= val < table[r]
  463. var mid = l + Math.floor((r-l+1)/2);
  464. if (table[mid] <= val)
  465. l = mid;
  466. else
  467. r = mid;
  468. }
  469. return l;
  470. }