docs: def _resume(self):
          # type: (Descriptor) -> int
          """Resume a download, if possible
          :param Descriptor self: this
          :rtype: int or None
          :return: verified download offset
          """
          if self._resume_mgr is None or self._offset > 0 or self._finalized:
              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.final_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
          # calculate current chunk and offset
          if rr.next_integrity_chunk == 0:
              logger.debug('nothing to resume for {}'.format(self.final_path))
              return None
          curr_chunk = rr.next_integrity_chunk
          # set offsets if completed and the final path exists
          if rr.completed and self.final_path.exists():
              with self._meta_lock:
                  logger.debug('{} download already completed'.format(
                      self.final_path))
                  self._offset = self._ase.size
                  self._chunk_num = curr_chunk
                  self._chunk_size = rr.chunk_size
                  self._total_chunks = self._compute_total_chunks(rr.chunk_size)
                  self._next_integrity_chunk = rr.next_integrity_chunk
                  self._outstanding_ops = 0
                  self._finalized = True
              return self._ase.size
          # 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
          self._allocate_disk_space()
          # check if final path exists
          if not self.final_path.exists():  # noqa
              logger.warning('download path {} does not exist'.format(
                  self.final_path))
              return None
          if self.hmac is not None:
              raise RuntimeError(
                  'unexpected hmac object for entity {}'.format(self._ase.path))
          # re-hash from 0 to offset if needed
          _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.final_path,
                      self.view.fd_start,
                      self.view.fd_start + _end_offset)
              )
              with self._hasher_lock:
                  with self.final_path.open('rb') as filedesc:
                      filedesc.seek(self.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.final_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 = self._compute_total_chunks(rr.chunk_size)
              self._next_integrity_chunk = rr.next_integrity_chunk
              self._outstanding_ops = (
                  self._total_chunks - self._next_integrity_chunk
              )
              logger.debug(
                  ('resuming file {} from byte={} chunk={} chunk_size={} '
                   'total_chunks={} next_integrity_chunk={} '
                   'outstanding_ops={}').format(
                       self.final_path, self._offset, self._chunk_num,
                       self._chunk_size, self._total_chunks,
                       self._next_integrity_chunk, self._outstanding_ops))
          return _end_offset