All files / json-pack/src/msgpack shallow-read.ts

77.46% Statements 55/71
45.83% Branches 11/24
100% Functions 4/4
75.38% Lines 49/65

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114  2x             2x 76x 76x 76x 76x 160x 160x 160x 160x                                               76x     2x 54x             54x 98x 98x   76x 76x 76x 76x 76x 76x 76x 76x 76x 76x 76x 76x 98x 12x 48x 12x         86x 26x 52x 26x           60x 60x             76x 76x 76x 76x 76x     22x 22x 22x 22x 22x               54x    
import type {Path} from '@jsonjoy.com/json-pointer';
import {Codegen} from '@jsonjoy.com/codegen/lib/Codegen';
import type {MsgPackDecoder} from './MsgPackDecoder';
 
type Decoder = Pick<MsgPackDecoder, 'readObjHdr' | 'readStrHdr' | 'readArrHdr' | 'skipAny'>;
 
type Fn = (decoder: Decoder) => number;
 
const toUtf8 = (str: string) => {
  const arr: number[] = [];
  const length = str.length;
  let curr = 0;
  while (curr < length) {
    let value = str.charCodeAt(curr++);
    if ((value & 0xffffff80) === 0) {
      arr.push(value);
      continue;
    } else Eif ((value & 0xfffff800) === 0) {
      arr.push(((value >> 6) & 0x1f) | 0xc0);
    } else {
      Iif (value >= 0xd800 && value <= 0xdbff) {
        Iif (curr < length) {
          const extra = str.charCodeAt(curr);
          Iif ((extra & 0xfc00) === 0xdc00) {
            curr++;
            value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
          }
        }
      }
      if ((value & 0xffff0000) === 0) {
        arr.push(((value >> 12) & 0x0f) | 0xe0);
        arr.push(((value >> 6) & 0x3f) | 0x80);
      } else {
        arr.push(((value >> 18) & 0x07) | 0xf0);
        arr.push(((value >> 12) & 0x3f) | 0x80);
        arr.push(((value >> 6) & 0x3f) | 0x80);
      }
    }
    arr.push((value & 0x3f) | 0x80);
  }
  return arr;
};
 
export const genShallowReader = (path: Path): Fn => {
  const codegen = new Codegen<Fn>({
    args: ['dec'],
    name: 'readShallow',
    prologue: 'var r = dec.reader;',
    epilogue: 'return r.x;',
  });
 
  for (let i = 0; i < path.length; i++) {
    const step = path[i];
    switch (typeof step) {
      case 'string': {
        const rObj = codegen.getRegister();
        const rIter = codegen.getRegister();
        const rFound = codegen.getRegister();
        codegen.js(/* js */ `var ${rObj} = dec.readObjHdr();`);
        codegen.js(/* js */ `var ${rFound} = false;`);
        codegen.js(`for(var ${rIter} = 0; ${rIter} < ${rObj}; ${rIter}++) {`);
        const utf8Arr = toUtf8(step);
        const length = utf8Arr.length;
        const rKey = codegen.getRegister();
        codegen.js(/* js */ `var ${rKey} = dec.readStrHdr();`);
        codegen.js(/* js */ `if (${rKey} !== ${length}) { r.x += ${rKey}; dec.skipAny(); continue; };`);
        while (utf8Arr.length > 0) {
          if (utf8Arr.length >= 4) {
            const word = utf8Arr.splice(0, 4);
            const utf8Chunk = '0x' + word.map((x) => x.toString(16)).join('');
            codegen.js(
              `if (r.u32() !== ${utf8Chunk}) { ${
                utf8Arr.length ? `r.x += ${utf8Arr.length}; ` : ''
              }dec.skipAny(); continue; }`,
            );
          } else if (utf8Arr.length >= 2) {
            const word = utf8Arr.splice(0, 2);
            const utf8Chunk = '0x' + word.map((x) => x.toString(16)).join('');
            codegen.js(
              `if (r.u16() !== ${utf8Chunk}) { ${
                utf8Arr.length ? `r.x += ${utf8Arr.length}; ` : ''
              }dec.skipAny(); continue; }`,
            );
          } else {
            const [octet] = utf8Arr.splice(0, 1);
            codegen.js(
              `if (r.u8() !== ${octet}) { ${
                utf8Arr.length ? `r.x += ${utf8Arr.length}; ` : ''
              }dec.skipAny(); continue; }`,
            );
          }
        }
        codegen.js(`${rFound} = true;`);
        codegen.js(`break;`);
        codegen.js(`}`);
        codegen.js(`if (!${rFound}) throw new Error('KEY_NOT_FOUND');`);
        break;
      }
      case 'number': {
        const rObj = codegen.getRegister();
        codegen.js(/* js */ `var ${rObj} = dec.readArrHdr();`);
        codegen.js(/* js */ `if(${rObj} <= ${step}) throw new Error('INDEX_OUT_OF_BOUNDS');`);
        for (let i = 0; i < step; i++) codegen.js(/* js */ `dec.skipAny();`);
        break;
      }
      default: {
        throw new Error('INVALID_PATH_STEP');
      }
    }
  }
 
  return codegen.compile();
};