mdict-parser.js
function Scanner(buf, len) {
      var offset = 0, dv = new DataView(buf);
      var methods = {
        // target data size in bytes
        size:     function() { return len || buf.byteLength; },
        // update offset to new position
        forward:  function(len) { return offset += len; },
        // return current offset
        offset:   function() { return offset; },
        
        // MDict file format uses big endian to store number
        
        // 32-bit unsigned int
        readInt:    function() { return conseq(dv.getUint32(offset, false), this.forward(4)); },
        readUint16: function() { return conseq(dv.getUint16(offset, false), this.forward(2)); },
        readUint8:  function() { return conseq(dv.getUint8(offset, false),  this.forward(1)); },
        
        // Read a "short" number representing keyword text size, 8-bit for version < 2, 16-bit for version >= 2
        readShort:  function() { return _readShort(this); },
        // Read a number representing offset or data block size, 16-bit for version < 2, 32-bit for version >= 2
        readNum:    function() { return _readNum(this); },

        readUTF16:  function(len) { return conseq(UTF_16LE.decode(newUint8Array(buf, offset, len)), this.forward(len)); },
        
        // Read data to an Uint8Array and decode it to text with specified encoding.
        // Text length in bytes is determined by searching terminated NUL.
        // NOTE: After decoding the text, it is need to forward extra "tail" bytes according to specified encoding. 
        readText: function() {
          var len = _searchTextLen(dv, offset);
          return conseq(_decoder.decode(newUint8Array(buf, offset, len)), this.forward(len + _bpu));
        },
        // Read data to an Uint8Array and decode it to text with specified encoding.
        // @param len length in basic unit, need to multiply byte per unit to get length in bytes
        // NOTE: After decoding the text, it is need to forward extra "tail" bytes according to specified encoding. 
        readTextSized: function(len) {
          len *= _bpu;
          var read = conseq(_decoder.decode(newUint8Array(buf, offset, len)), this.forward(len + _tail));
          return read;
        },
        
        // Skip checksum, just ignore it anyway.
        checksum:    function() { this.forward(4); },
        // Version >= 2.0 only
        checksum_v2: function() { return _checksum_v2(this); },

        // Read data block of keyword index, key block or record content.
        // These data block are maybe in compressed (gzip or lzo) format, while keyword index maybe be encrypted.
        // @see https://github.com/zhansliu/writemdict/blob/master/fileformat.md#compression (with typo mistake)
        readBlock: function(len, expectedBufSize, decryptor) {
          var comp_type = dv.getUint8(offset, false);  // compression type, 0 = non, 1 = lzo, 2 = gzip
          if (comp_type === 0) {
            if (_v2) {
              this.forward(8);  // for version >= 2, skip comp_type (4 bytes with tailing \x00) and checksum (4 bytes)
            }
            return this;
          } else {
            // skip comp_type (4 bytes with tailing \x00) and checksum (4 bytes)
            offset += 8; len -= 8;
            var tmp = new Uint8Array(len);
            buf.copy(tmp, 0, offset, offset + len);
            if (decryptor) {
              var passkey = new Uint8Array(8);
              var q = new Buffer(4);
              buf.copy(passkey, 0, offset-4, offset);
              // var q = new Buffer(4);
              passkey.set([0x95, 0x36, 0x00, 0x00], 4);         // key part 2: fixed data
              tmp = decryptor(tmp, passkey);
            }            
            tmp = comp_type === 2 ? pako.inflate(tmp) : lzo.decompress(tmp, expectedBufSize, 1308672);
            this.forward(len);
            var d = new Buffer(tmp);
            return Scanner(d, tmp.length);
          }
        },
        
        // Read raw data as Uint8Array from current offset with specified length in bytes
        readRaw: function(len) {
          return conseq(newUint8Array(buf, offset, len), this.forward(len === UNDEFINED ? buf.length - offset : len));
        },
      };

      return Object.create(methods);
    }
function Scanner(buf, len) {
      var offset = 0, dv = new DataView(buf);
      var methods = {
        // target data size in bytes
        size:     function() { return len || buf.byteLength; },
        // update offset to new position
        forward:  function(len) { return offset += len; },
        // return current offset
        offset:   function() { return offset; },
        
        // MDict file format uses big endian to store number
        
        // 32-bit unsigned int
        readInt:    function() { return conseq(dv.getUint32(offset, false), this.forward(4)); },
        readUint16: function() { return conseq(dv.getUint16(offset, false), this.forward(2)); },
        readUint8:  function() { return conseq(dv.getUint8(offset, false),  this.forward(1)); },
        
        // Read a "short" number representing keyword text size, 8-bit for version < 2, 16-bit for version >= 2
        readShort:  function() { return _readShort(this); },
        // Read a number representing offset or data block size, 16-bit for version < 2, 32-bit for version >= 2
        readNum:    function() { return _readNum(this); },

        readUTF16:  function(len) { return conseq(UTF_16LE.decode(newUint8Array(buf, offset, len)), this.forward(len)); },
        
        // Read data to an Uint8Array and decode it to text with specified encoding.
        // Text length in bytes is determined by searching terminated NUL.
        // NOTE: After decoding the text, it is need to forward extra "tail" bytes according to specified encoding. 
        readText: function() {
          var len = _searchTextLen(dv, offset);
          return conseq(_decoder.decode(newUint8Array(buf, offset, len)), this.forward(len + _bpu));
        },
        // Read data to an Uint8Array and decode it to text with specified encoding.
        // @param len length in basic unit, need to multiply byte per unit to get length in bytes
        // NOTE: After decoding the text, it is need to forward extra "tail" bytes according to specified encoding. 
        readTextSized: function(len) {
          len *= _bpu;
          var read = conseq(_decoder.decode(newUint8Array(buf, offset, len)), this.forward(len + _tail));
          return read;
        },
        
        // Skip checksum, just ignore it anyway.
        checksum:    function() { this.forward(4); },
        // Version >= 2.0 only
        checksum_v2: function() { return _checksum_v2(this); },

        // Read data block of keyword index, key block or record content.
        // These data block are maybe in compressed (gzip or lzo) format, while keyword index maybe be encrypted.
        // @see https://github.com/zhansliu/writemdict/blob/master/fileformat.md#compression (with typo mistake)
        readBlock: function(len, expectedBufSize, decryptor) {
          var comp_type = dv.getUint8(offset, false);  // compression type, 0 = non, 1 = lzo, 2 = gzip
          if (comp_type === 0) {
            if (_v2) {
              this.forward(8);  // for version >= 2, skip comp_type (4 bytes with tailing \x00) and checksum (4 bytes)
            }
            return this;
          } else {
            // skip comp_type (4 bytes with tailing \x00) and checksum (4 bytes)
            offset += 8; len -= 8;
            var tmp = new Uint8Array(len);
            buf.copy(tmp, 0, offset, offset + len);
            if (decryptor) {
              var passkey = new Uint8Array(8);
              var q = new Buffer(4);
              buf.copy(passkey, 0, offset-4, offset);
              // var q = new Buffer(4);
              passkey.set([0x95, 0x36, 0x00, 0x00], 4);         // key part 2: fixed data
              tmp = decryptor(tmp, passkey);
            }            
            tmp = comp_type === 2 ? pako.inflate(tmp) : lzo.decompress(tmp, expectedBufSize, 1308672);
            this.forward(len);
            var d = new Buffer(tmp);
            return Scanner(d, tmp.length);
          }
        },
        
        // Read raw data as Uint8Array from current offset with specified length in bytes
        readRaw: function(len) {
          return conseq(newUint8Array(buf, offset, len), this.forward(len === UNDEFINED ? buf.length - offset : len));
        },
      };

      return Object.create(methods);
    }
Read data in current offset from target data ArrayBuffer