def read_stream(cls, stream, validate=True):
        try:
            content = stream.read(cls.MAX_TORRENT_FILE_SIZE)
        except OSError as e:
            raise error.ReadError(e.errno)
        else:
            try:
                metainfo_enc = bdecode(content)
            except BTFailure as e:
                raise error.ParseError()
            if validate:
                if b'info' not in metainfo_enc:
                    raise error.MetainfoError("Missing 'info'")
                elif not isinstance(metainfo_enc[b'info'], abc.Mapping):
                    raise error.MetainfoError("'info' is not a dictionary")
                elif b'pieces' not in metainfo_enc[b'info']:
                    raise error.MetainfoError("Missing 'pieces' in ['info']")
            if b'info' in metainfo_enc and b'pieces' in metainfo_enc[b'info']:
                pieces = metainfo_enc[b'info'].pop(b'pieces')
                metainfo = utils.decode_dict(metainfo_enc)
                metainfo['info']['pieces'] = pieces
            else:
                metainfo = utils.decode_dict(metainfo_enc)
            torrent = cls()
            torrent._metainfo = metainfo
            for attr in ('creation_date', 'private'):
                setattr(torrent, attr, getattr(torrent, attr))
            info = torrent.metainfo['info']
            torrent.include_md5 = ('length' in info and 'md5sum' in info) or \
                                  ('files' in info and all('md5sum' in fileinfo
                                                           for fileinfo in info['files']))
            if validate:
                torrent.validate()
            return torrent