Playlist Read2Sources (const QString& path) { QFile file (path); if (!file.open (QIODevice::ReadOnly)) { qWarning () << Q_FUNC_INFO << "unable to open" << path << file.errorString (); return {}; } const auto& m3uDir = QFileInfo (path).absoluteDir (); QVariantMap lastMetadata; Playlist result; while (!file.atEnd ()) { const auto& line = file.readLine ().trimmed (); if (line.startsWith ('#')) { const auto& pair = ParseMetadata (line); if (!pair.first.isEmpty ()) lastMetadata [pair.first] = pair.second; continue; } const auto& url = QUrl::fromEncoded (line); auto src = QString::fromUtf8 (line); const auto mdGuard = std::shared_ptr<void> (nullptr, [&lastMetadata] (void*) { lastMetadata.clear (); }); #ifdef Q_OS_WIN32 if (url.scheme ().size () > 1) #else if (!url.scheme ().isEmpty ()) #endif { result.Append ({ url, lastMetadata }); continue; } src.replace ('\\', '/'); const QFileInfo fi (src); if (fi.isRelative ()) src = m3uDir.absoluteFilePath (src); if (fi.suffix () == "m3u" || fi.suffix () == "m3u8") result += Read2Sources (src); else result.Append ({ src, lastMetadata }); } return result; }
SongList M3UParser::Load(QIODevice* device, const QString& playlist_path, const QDir& dir) const { SongList ret; M3UType type = STANDARD; Metadata current_metadata; QString data = QString::fromUtf8(device->readAll()); data.replace('\r', '\n'); data.replace("\n\n", "\n"); QByteArray bytes = data.toUtf8(); QBuffer buffer(&bytes); buffer.open(QIODevice::ReadOnly); QString line = QString::fromUtf8(buffer.readLine()).trimmed(); if (line.startsWith("#EXTM3U")) { // This is in extended M3U format. type = EXTENDED; line = QString::fromUtf8(buffer.readLine()).trimmed(); } forever { if (line.startsWith('#')) { // Extended info or comment. if (type == EXTENDED && line.startsWith("#EXT")) { if (!ParseMetadata(line, ¤t_metadata)) { qLog(Warning) << "Failed to parse metadata: " << line; } } } else if (!line.isEmpty()) { Song song = LoadSong(line, 0, dir); if (!current_metadata.title.isEmpty()) { song.set_title(current_metadata.title); } if (!current_metadata.artist.isEmpty()) { song.set_artist(current_metadata.artist); } if (current_metadata.length > 0) { song.set_length_nanosec(current_metadata.length); } ret << song; current_metadata = Metadata(); } if (buffer.atEnd()) { break; } line = QString::fromUtf8(buffer.readLine()).trimmed(); } return ret; }
nsresult CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) { LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]", this, aHandle, aResult)); MOZ_ASSERT(mListener); nsresult rv, retval; nsCOMPtr<CacheFileMetadataListener> listener; if (NS_FAILED(aResult)) { LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed" ", creating empty metadata. [this=%p, rv=0x%08x]", this, aResult)); InitEmptyMetadata(); retval = NS_OK; mListener.swap(listener); listener->OnMetadataRead(retval); return NS_OK; } // check whether we have read all necessary data uint32_t realOffset = NetworkEndian::readUint32(mBuf + mBufSize - sizeof(uint32_t)); int64_t size = mHandle->FileSize(); MOZ_ASSERT(size != -1); if (realOffset >= size) { LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating " "empty metadata. [this=%p, realOffset=%d, size=%lld]", this, realOffset, size)); InitEmptyMetadata(); retval = NS_OK; mListener.swap(listener); listener->OnMetadataRead(retval); return NS_OK; } uint32_t usedOffset = size - mBufSize; if (realOffset < usedOffset) { uint32_t missing = usedOffset - realOffset; // we need to read more data mBuf = static_cast<char *>(moz_xrealloc(mBuf, mBufSize + missing)); memmove(mBuf + missing, mBuf, mBufSize); mBufSize += missing; DoMemoryReport(MemoryUsage()); LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to " "have full metadata. [this=%p]", missing, this)); rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, true, this); if (NS_FAILED(rv)) { LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() " "failed synchronously, creating empty metadata. [this=%p, " "rv=0x%08x]", this, rv)); InitEmptyMetadata(); retval = NS_OK; mListener.swap(listener); listener->OnMetadataRead(retval); return NS_OK; } return NS_OK; } // We have all data according to offset information at the end of the entry. // Try to parse it. rv = ParseMetadata(realOffset, realOffset - usedOffset, true); if (NS_FAILED(rv)) { LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating " "empty metadata. [this=%p]", this)); InitEmptyMetadata(); retval = NS_OK; } else { retval = NS_OK; } mListener.swap(listener); listener->OnMetadataRead(retval); return NS_OK; }
nsresult CacheFileMetadata::SyncReadMetadata(nsIFile *aFile) { LOG(("CacheFileMetadata::SyncReadMetadata() [this=%p]", this)); MOZ_ASSERT(!mListener); MOZ_ASSERT(!mHandle); MOZ_ASSERT(!mHashArray); MOZ_ASSERT(!mBuf); MOZ_ASSERT(!mWriteBuf); MOZ_ASSERT(mKey.IsEmpty()); nsresult rv; int64_t fileSize; rv = aFile->GetFileSize(&fileSize); if (NS_FAILED(rv)) { // Don't bloat the console return rv; } PRFileDesc *fd; rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0600, &fd); NS_ENSURE_SUCCESS(rv, rv); int64_t offset = PR_Seek64(fd, fileSize - sizeof(uint32_t), PR_SEEK_SET); if (offset == -1) { PR_Close(fd); return NS_ERROR_FAILURE; } uint32_t metaOffset; int32_t bytesRead = PR_Read(fd, &metaOffset, sizeof(uint32_t)); if (bytesRead != sizeof(uint32_t)) { PR_Close(fd); return NS_ERROR_FAILURE; } metaOffset = NetworkEndian::readUint32(&metaOffset); if (metaOffset > fileSize) { PR_Close(fd); return NS_ERROR_FAILURE; } mBufSize = fileSize - metaOffset; mBuf = static_cast<char *>(moz_xmalloc(mBufSize)); DoMemoryReport(MemoryUsage()); offset = PR_Seek64(fd, metaOffset, PR_SEEK_SET); if (offset == -1) { PR_Close(fd); return NS_ERROR_FAILURE; } bytesRead = PR_Read(fd, mBuf, mBufSize); PR_Close(fd); if (bytesRead != static_cast<int32_t>(mBufSize)) { return NS_ERROR_FAILURE; } rv = ParseMetadata(metaOffset, 0, false); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; }
nsresult CacheFileMetadata::OnDataRead(CacheFileHandle *aHandle, char *aBuf, nsresult aResult) { LOG(("CacheFileMetadata::OnDataRead() [this=%p, handle=%p, result=0x%08x]", this, aHandle, aResult)); MOZ_ASSERT(mListener); nsresult rv; nsCOMPtr<CacheFileMetadataListener> listener; if (NS_FAILED(aResult)) { LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() failed" ", creating empty metadata. [this=%p, rv=0x%08x]", this, aResult)); InitEmptyMetadata(); mListener.swap(listener); listener->OnMetadataRead(NS_OK); return NS_OK; } if (mFirstRead) { Telemetry::AccumulateTimeDelta( Telemetry::NETWORK_CACHE_METADATA_FIRST_READ_TIME_MS, mReadStart); Telemetry::Accumulate( Telemetry::NETWORK_CACHE_METADATA_FIRST_READ_SIZE, mBufSize); } else { Telemetry::AccumulateTimeDelta( Telemetry::NETWORK_CACHE_METADATA_SECOND_READ_TIME_MS, mReadStart); } // check whether we have read all necessary data uint32_t realOffset = NetworkEndian::readUint32(mBuf + mBufSize - sizeof(uint32_t)); int64_t size = mHandle->FileSize(); MOZ_ASSERT(size != -1); if (realOffset >= size) { LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, creating " "empty metadata. [this=%p, realOffset=%u, size=%lld]", this, realOffset, size)); InitEmptyMetadata(); mListener.swap(listener); listener->OnMetadataRead(NS_OK); return NS_OK; } uint32_t maxHashCount = size / kChunkSize; uint32_t maxMetadataSize = CalcMetadataSize(kMaxElementsSize, maxHashCount); if (size - realOffset > maxMetadataSize) { LOG(("CacheFileMetadata::OnDataRead() - Invalid realOffset, metadata would " "be too big, creating empty metadata. [this=%p, realOffset=%u, " "maxMetadataSize=%u, size=%lld]", this, realOffset, maxMetadataSize, size)); InitEmptyMetadata(); mListener.swap(listener); listener->OnMetadataRead(NS_OK); return NS_OK; } uint32_t usedOffset = size - mBufSize; if (realOffset < usedOffset) { uint32_t missing = usedOffset - realOffset; // we need to read more data char *newBuf = static_cast<char *>(realloc(mBuf, mBufSize + missing)); if (!newBuf) { LOG(("CacheFileMetadata::OnDataRead() - Error allocating %d more bytes " "for the missing part of the metadata, creating empty metadata. " "[this=%p]", missing, this)); InitEmptyMetadata(); mListener.swap(listener); listener->OnMetadataRead(NS_OK); return NS_OK; } mBuf = newBuf; memmove(mBuf + missing, mBuf, mBufSize); mBufSize += missing; DoMemoryReport(MemoryUsage()); LOG(("CacheFileMetadata::OnDataRead() - We need to read %d more bytes to " "have full metadata. [this=%p]", missing, this)); mFirstRead = false; mReadStart = mozilla::TimeStamp::Now(); rv = CacheFileIOManager::Read(mHandle, realOffset, mBuf, missing, this); if (NS_FAILED(rv)) { LOG(("CacheFileMetadata::OnDataRead() - CacheFileIOManager::Read() " "failed synchronously, creating empty metadata. [this=%p, " "rv=0x%08x]", this, rv)); InitEmptyMetadata(); mListener.swap(listener); listener->OnMetadataRead(NS_OK); return NS_OK; } return NS_OK; } Telemetry::Accumulate(Telemetry::NETWORK_CACHE_METADATA_SIZE, size - realOffset); // We have all data according to offset information at the end of the entry. // Try to parse it. rv = ParseMetadata(realOffset, realOffset - usedOffset, true); if (NS_FAILED(rv)) { LOG(("CacheFileMetadata::OnDataRead() - Error parsing metadata, creating " "empty metadata. [this=%p]", this)); InitEmptyMetadata(); } else { // Shrink elements buffer. mBuf = static_cast<char *>(moz_xrealloc(mBuf, mElementsSize)); mBufSize = mElementsSize; // There is usually no or just one call to SetMetadataElement() when the // metadata is parsed from disk. Avoid allocating power of two sized buffer // which we do in case of newly created metadata. mAllocExactSize = true; } mListener.swap(listener); listener->OnMetadataRead(NS_OK); return NS_OK; }