docs: def Run(self, args):
      """Search the file for the pattern.
  
      This implements the grep algorithm used to scan files. It reads
      the data in chunks of BUFF_SIZE (10 MB currently) and can use
      different functions to search for matching patterns. In every
      step, a buffer that is a bit bigger than the block size is used in
      order to return all the requested results. Specifically, a
      preamble is used in order to not miss any patterns that start in
      one block of data and end in the next and also a postscript buffer
      is kept such that the algorithm can return bytes trailing the
      pattern even if the pattern is at the end of one block.
  
      One block:
      -----------------------------
      | Pre | Data         | Post |
      -----------------------------
      Searching the pattern is done here:
      <------------------->
  
      The following block is constructed like this:
      -----------------------------
      | Pre | Data         | Post |
      -----------------------------
                           |
                     -----------------------------
                     | Pre | Data         | Post |
                     -----------------------------
  
      The preamble is filled from Data so every hit that happens to fall
      entirely into the preamble has to be discarded since it has
      already been discovered in the step before.
  
      Grepping for memory
  
      If this action is used to grep the memory of a client machine
      using one of the GRR memory acquisition drivers, we have to be
      very careful not to have any hits in the GRR process memory space
      itself. Therefore, if the input is a literal, it is XOR encoded
      and only visible in memory when the pattern is matched. This is
      done using bytearrays which guarantees in place updates and no
      leaking patterns. Also the returned data is encoded using a
      different XOR 'key'.
  
      This should guarantee that there are no hits when the pattern is
      not present in memory. However, since the data will be copied to
      the preamble and the postscript, a single pattern might in some
      cases produce multiple hits.
  
      Args:
        args: A protobuf describing the grep request.
  
      Raises:
        RuntimeError: No search pattern has been given in the request.
  
      """
      fd = vfs.VFSOpen(args.target, progress_callback=self.Progress)
      fd.Seek(args.start_offset)
      base_offset = args.start_offset
  
      self.xor_in_key = args.xor_in_key
      self.xor_out_key = args.xor_out_key
  
      if args.regex:
        find_func = functools.partial(self.FindRegex, args.regex.AsBytes())
      elif args.literal:
        find_func = functools.partial(self.FindLiteral, args.literal.AsBytes())
      else:
        raise RuntimeError("Grep needs a regex or a literal.")
  
      preamble_size = 0
      postscript_size = 0
      hits = 0
      data = b""
      while fd.Tell() < args.start_offset + args.length:
  
        # Base size to read is at most the buffer size.
        to_read = min(args.length, self.BUFF_SIZE,
                      args.start_offset + args.length - fd.Tell())
        # Read some more data for the snippet.
        to_read += self.ENVELOPE_SIZE - postscript_size
        read_data = fd.Read(to_read)
  
        data = data[-postscript_size - self.ENVELOPE_SIZE:] + read_data
  
        postscript_size = max(0, self.ENVELOPE_SIZE - (to_read - len(read_data)))
        data_size = len(data) - preamble_size - postscript_size
  
        if data_size == 0 and postscript_size == 0:
          break
  
        for (start, end) in find_func(data):
          # Ignore hits in the preamble.
          if end <= preamble_size:
            continue
  
          # Ignore hits in the postscript.
          if end > preamble_size + data_size:
            continue
  
          # Offset of file in the end after length.
          if end + base_offset - preamble_size > args.start_offset + args.length:
            break
  
          data_start = max(0, start - args.bytes_before)
          data_end = min(len(data), end + args.bytes_after)
          out_data = utils.Xor(data[data_start:data_end], self.xor_out_key)
  
          hits += 1
          self.SendReply(
              rdf_client.BufferReference(
                  offset=base_offset + start - preamble_size,
                  data=out_data,
                  length=len(out_data),
                  pathspec=fd.pathspec))
  
          if args.mode == rdf_client_fs.GrepSpec.Mode.FIRST_HIT:
            return
  
          if hits >= self.HIT_LIMIT:
            msg = utils.Xor(
                b"This Grep has reached the maximum number of hits"
                b" (%d)." % self.HIT_LIMIT, self.xor_out_key)
            self.SendReply(
                rdf_client.BufferReference(offset=0, data=msg, length=len(msg)))
            return
  
        self.Progress()
  
        base_offset += data_size
  
        # Allow for overlap with previous matches.
        preamble_size = min(len(data), self.ENVELOPE_SIZE)