docs: def _resume(self):
          # type: (Descriptor) -> int
          """Resume upload
          :param Descriptor self: this
          :rtype: int
          :return: resume bytes
          """
          if self._resume_mgr is None or self._offset > 0:
              return None
          # check if path exists in resume db
          rr = self._resume_mgr.get_record(self._ase)
          if rr is None:
              logger.debug('no resume record for {}'.format(self._ase.path))
              return None
          # ensure lengths are the same
          if rr.length != self._ase.size:
              logger.warning('resume length mismatch {} -> {}'.format(
                  rr.length, self._ase.size))
              return None
          # compute replica factor
          if blobxfer.util.is_not_empty(self._ase.replica_targets):
              replica_factor = 1 + len(self._ase.replica_targets)
          else:
              replica_factor = 1
          # set offsets if completed
          if rr.completed:
              with self._meta_lock:
                  logger.debug('{} upload already completed'.format(
                      self._ase.path))
                  self._offset = rr.total_chunks * rr.chunk_size
                  self._chunk_num = rr.total_chunks
                  self._chunk_size = rr.chunk_size
                  self._total_chunks = rr.total_chunks
                  self._completed_chunks.int = rr.completed_chunks
                  self._outstanding_ops = 0
                  return self._ase.size * replica_factor
          # encrypted files are not resumable due to hmac requirement
          if self._ase.is_encrypted:
              logger.debug('cannot resume encrypted entity {}'.format(
                  self._ase.path))
              return None
          # check if path exists
          if not pathlib.Path(rr.local_path).exists():
              logger.warning('resume from local path {} does not exist'.format(
                  rr.local_path))
              return None
          # re-hash from 0 to offset if needed
          _cc = bitstring.BitArray(length=rr.total_chunks)
          _cc.int = rr.completed_chunks
          curr_chunk = _cc.find('0b0')[0]
          del _cc
          _fd_offset = 0
          _end_offset = min((curr_chunk * rr.chunk_size, rr.length))
          if self.md5 is not None and curr_chunk > 0:
              _blocksize = blobxfer.util.MEGABYTE << 2
              logger.debug(
                  'integrity checking existing file {} offset {} -> {}'.format(
                      self._ase.path,
                      self.local_path.view.fd_start,
                      self.local_path.view.fd_start + _end_offset)
              )
              with self._hasher_lock:
                  with self.local_path.absolute_path.open('rb') as filedesc:
                      filedesc.seek(self.local_path.view.fd_start, 0)
                      while _fd_offset < _end_offset:
                          if (_fd_offset + _blocksize) > _end_offset:
                              _blocksize = _end_offset - _fd_offset
                          _buf = filedesc.read(_blocksize)
                          self.md5.update(_buf)
                          _fd_offset += _blocksize
              del _blocksize
              # compare hashes
              hexdigest = self.md5.hexdigest()
              if rr.md5hexdigest != hexdigest:
                  logger.warning(
                      'MD5 mismatch resume={} computed={} for {}'.format(
                          rr.md5hexdigest, hexdigest, self._ase.path))
                  # reset hasher
                  self.md5 = blobxfer.util.new_md5_hasher()
                  return None
          # set values from resume
          with self._meta_lock:
              self._offset = _end_offset
              self._chunk_num = curr_chunk
              self._chunk_size = rr.chunk_size
              self._total_chunks = rr.total_chunks
              self._completed_chunks = bitstring.BitArray(length=rr.total_chunks)
              self._completed_chunks.set(True, range(0, curr_chunk + 1))
              self._outstanding_ops = (
                  (rr.total_chunks - curr_chunk) * replica_factor
              )
              logger.debug(
                  ('resuming file {} from byte={} chunk={} chunk_size={} '
                   'total_chunks={} outstanding_ops={}').format(
                       self._ase.path, self._offset, self._chunk_num,
                       self._chunk_size, self._total_chunks,
                       self._outstanding_ops))
              return _end_offset * replica_factor