Get File/Blob header (signature) to parse for MIME-type or any magic numbers
@param {File|Blob} blob
@returns {Promise} callback(str:signature)
774201
function getSignature(blob) {
        return new Promise(function (callback) {
            var reader = new FileReader();
            reader.onloadend = function () {
                var arr = new Uint8Array(reader.result).subarray(0, 4);
                var signature = '';
                for (var i = 0; i < arr.length; i++) {
                    signature += arr[i].toString(16);
                }
                callback(signature);
            };
            reader.readAsArrayBuffer(blob);
        });
    }
990797
function parse_Window1(blob) {
	var xWn = blob.read_shift(2), yWn = blob.read_shift(2), dxWn = blob.read_shift(2), dyWn = blob.read_shift(2);
	var flags = blob.read_shift(2), iTabCur = blob.read_shift(2), iTabFirst = blob.read_shift(2);
	var ctabSel = blob.read_shift(2), wTabRatio = blob.read_shift(2);
	return { Pos: [xWn, yWn], Dim: [dxWn, dyWn], Flags: flags, CurTab: iTabCur,
		FirstTab: iTabFirst, Selected: ctabSel, TabRatio: wTabRatio };
}
272589
async function hash (file) {
  const filesize = await fileUtil.getFileSize(file)

  const chunkSize = 64 * 1024

  const firstBytesPromise = fileUtil.readBytes({
    file,
    chunkSize,
    start: 0
  })

  const lastBytesPromise = fileUtil.readBytes({
    file,
    chunkSize,
    start: filesize - chunkSize
  })

  const [ firstBytes, lastBytes ] = await Promise.all([firstBytesPromise, lastBytesPromise])

  return crypto
    .createHash('md5')
    .update(firstBytes)
    .update(lastBytes)
    .digest('hex')
}
936509
function parseFile(target) {
  return new Promise((resolve, reject) => {
    fs.readFile(target, 'utf8', (err, data) => {
      if (err) reject(err)
      // const doc = parser.parseFromString(data, 'application/xml')
      const doc = cheerio.load(data, { xmlMode: true })
      resolve(doc)
    })
  })
}
1174747
function getFileMIME(file) {
  var idx = file.lastIndexOf('.')
  var ext = idx > -1 ? file.slice(idx) : null
  if (ext) ext = ext.toLowerCase().substring(1)
  return MIMES[ext] || MIMES.txt
}
514889
function extractBlobData (blob) {
  // todo: add tests for non-zlib cases
  switch (true) {
    // error cases:

    // * lzma compressed data (support for this kind of data is not required by the specs)
    case blob.lzma_data !== null:
      throw new Error('unsupported osmpbf blob data type: lzma_data')
    // * formerly used for bzip2 compressed data, deprecated since 2010
    case blob.OBSOLETE_bzip2_data !== null:
      throw new Error('unsupported osmpbf blob data type: OBSOLETE_bzip2_data')
    // * empty data blob??
    default:
      throw new Error('unsupported osmpbf blob data type: <empty blob>')

    // supported data formats:

    // * uncompressed data
    case blob.raw !== null:
      return blob.raw
    // * zlib "deflate" compressed data
    case blob.zlib_data !== null:
      var blobData = new Buffer(blob.raw_size)
      inflate(blob.zlib_data.slice(2), blobData)
      return blobData
  }
}
494054
function parse_ExternName(blob, length, opts) {
	var flags = blob.read_shift(2);
	var body;
	var o = ({
		fBuiltIn: flags & 0x01,
		fWantAdvise: (flags >>> 1) & 0x01,
		fWantPict: (flags >>> 2) & 0x01,
		fOle: (flags >>> 3) & 0x01,
		fOleLink: (flags >>> 4) & 0x01,
		cf: (flags >>> 5) & 0x3FF,
		fIcon: flags >>> 15 & 0x01
	});
	if(opts.sbcch === 0x3A01) body = parse_AddinUdf(blob, length-2, opts);
	//else throw new Error("unsupported SupBook cch: " + opts.sbcch);
	o.body = body || blob.read_shift(length-2);
	if(typeof body === "string") o.Name = body;
	return o;
}
689654
function parse_EncryptionVerifier(blob, length/*:number*/) {
	var o = {}, tgt = blob.l + length;
	blob.l += 4; // SaltSize must be 0x10
	o.Salt = blob.slice(blob.l, blob.l+16); blob.l += 16;
	o.Verifier = blob.slice(blob.l, blob.l+16); blob.l += 16;
	/*var sz = */blob.read_shift(4);
	o.VerifierHash = blob.slice(blob.l, tgt); blob.l = tgt;
	return o;
}
519703
function blobToBase64(blob) {
        var reader = new FileReader();
        var p = new Promise(function (success, fail) {
            reader.onloadend = function () {
                var base64 = reader.result;
                success(base64);
            };
            reader.onerror = function (e) {
                fail(e);
            };
        });

        reader.readAsDataURL(blob);

        return p;
    }
756648
function parse_FileMoniker(blob) {
	blob.l += 2; //var cAnti = blob.read_shift(2);
	var ansiPath = blob.read_shift(0, 'lpstr-ansi');
	blob.l += 2; //var endServer = blob.read_shift(2);
	if(blob.read_shift(2) != 0xDEAD) throw new Error("Bad FileMoniker");
	var sz = blob.read_shift(4);
	if(sz === 0) return ansiPath.replace(/\\/g,"/");
	var bytes = blob.read_shift(4);
	if(blob.read_shift(2) != 3) throw new Error("Bad FileMoniker");
	var unicodePath = blob.read_shift(bytes>>1, 'utf16le').replace(chr0,"");
	return unicodePath;
}
1328130
function parse_Font(blob, length, opts) {
	var o = {
		dyHeight: blob.read_shift(2),
		fl: blob.read_shift(2)
	};
	switch((opts && opts.biff) || 8) {
		case 2: break;
		case 3: case 4: blob.l += 2; break;
		default: blob.l += 10; break;
	}
	o.name = parse_ShortXLUnicodeString(blob, 0, opts);
	return o;
}
599330
function parse_ExternSheet(blob, length, opts) {
	if(opts.biff < 8) return parse_BIFF5ExternSheet(blob, length, opts);
	var o = [], target = blob.l + length, len = blob.read_shift(opts.biff > 8 ? 4 : 2);
	while(len-- !== 0) o.push(parse_XTI(blob, opts.biff > 8 ? 12 : 6, opts));
		// [iSupBook, itabFirst, itabLast];
	if(blob.l != target) throw new Error("Bad ExternSheet: " + blob.l + " != " + target);
	return o;
}
1001986
function parse_FormulaValue(blob) {
	var b;
	if(__readUInt16LE(blob,blob.l + 6) !== 0xFFFF) return [parse_Xnum(blob),'n'];
	switch(blob[blob.l]) {
		case 0x00: blob.l += 8; return ["String", 's'];
		case 0x01: b = blob[blob.l+2] === 0x1; blob.l += 8; return [b,'b'];
		case 0x02: b = blob[blob.l+2]; blob.l += 8; return [b,'e'];
		case 0x03: blob.l += 8; return ["",'s'];
	}
	return [];
}
1194268
function BlobReader(blob, opts) {
  stream.Readable.call(this);
  opts = opts || {};

  this._batchSize = opts.batchSize || 65536;
  this._blob = blob;
  this._pos = 0;
}
50401
function parse_RgceArea(blob, length, opts) {
	var w = 2;
	if(opts) {
		if(opts.biff >= 2 && opts.biff <= 5) return parse_RgceArea_BIFF2(blob, length, opts);
		else if(opts.biff == 12) w = 4;
	}
	var r=blob.read_shift(w), R=blob.read_shift(w);
	var c=parse_ColRelU(blob, 2);
	var C=parse_ColRelU(blob, 2);
	return { s:{r:r, c:c[0], cRel:c[1], rRel:c[2]}, e:{r:R, c:C[0], cRel:C[1], rRel:C[2]} };
}
181816
function parse_PtgMemFunc(blob, length, opts) {
	var type = (blob.read_shift(1) >>> 5) & 0x03;
	var cce = blob.read_shift(opts && opts.biff == 2 ? 1 : 2);
	return [type, cce];
}
1073517
function parse_EncInfoStd(blob) {
	var flags = blob.read_shift(4);
	if((flags & 0x3F) != 0x24) throw new Error("EncryptionInfo mismatch");
	var sz = blob.read_shift(4);
	//var tgt = blob.l + sz;
	var hdr = parse_EncryptionHeader(blob, sz);
	var verifier = parse_EncryptionVerifier(blob, blob.length - blob.l);
	return { t:"Std", h:hdr, v:verifier };
}