docs: def read_events(stream):
      '''
      Read and return as a generator a sequence of Event protos from
      file-like object `stream`.
      '''
      header_size = struct.calcsize('<QI')
      len_size = struct.calcsize('<Q')
      footer_size = struct.calcsize('<I')
  
      while True:
          header = stream.read(header_size)
          if len(header) == 0:
              break
          elif len(header) < header_size:
              raise SummaryReaderException('unexpected EOF (expected a %d-byte header, '
                                           'got %d bytes)' % (header_size, len(header)))
          data_len, len_crc = struct.unpack('<QI', header)
          len_crc_actual = masked_crc(header[:len_size])
          if len_crc_actual != len_crc:
              raise SummaryReaderException('incorrect length CRC (%d != %d)' %
                                           (len_crc_actual, len_crc))
  
          data = stream.read(data_len)
          if len(data) < data_len:
              raise SummaryReaderException('unexpected EOF (expected %d bytes, got %d)' %
                                           (data_len, len(data)))
          yield Event.FromString(data)
  
          footer = stream.read(footer_size)
          if len(footer) < footer_size:
              raise SummaryReaderException('unexpected EOF (expected a %d-byte footer, '
                                           'got %d bytes)' % (footer_size, len(footer)))
          data_crc, = struct.unpack('<I', footer)
          data_crc_actual = masked_crc(data)
          if data_crc_actual != data_crc:
              raise SummaryReaderException('incorrect data CRC (%d != %d)' %
                                           (data_crc_actual, data_crc))