static KeyValueMap retrieveKeyValuePairs(WebCore::SharedBufferChunkReader* buffer) { KeyValueMap keyValuePairs; String line; String key; StringBuilder value; while (!(line = buffer->nextChunkAsUTF8StringWithLatin1Fallback()).isNull()) { if (line.isEmpty()) break; // Empty line means end of key/value section. if (line[0] == '\t') { ASSERT(!key.isEmpty()); value.append(line.substring(1)); continue; } // New key/value, store the previous one if any. if (!key.isEmpty()) { if (keyValuePairs.find(key) != keyValuePairs.end()) LOG_ERROR("Key duplicate found in MIME header. Key is '%s', previous value replaced.", key.ascii().data()); keyValuePairs.add(key, value.toString().stripWhiteSpace()); key = String(); value.clear(); } size_t semiColonIndex = line.find(':'); if (semiColonIndex == notFound) { // This is not a key value pair, ignore. continue; } key = line.substring(0, semiColonIndex).lower().stripWhiteSpace(); value.append(line.substring(semiColonIndex + 1)); } // Store the last property if there is one. if (!key.isEmpty()) keyValuePairs.set(key, value.toString().stripWhiteSpace()); return keyValuePairs; }
static void commitLiteralToken(StringBuilder& literalBuffer, StringBuilder& converted) { if (literalBuffer.length() <= 0) return; DateTimeFormat::quoteAndappend(literalBuffer.toString(), converted); literalBuffer.clear(); }
TEST_F(SecurityOriginTest, SuboriginsParsing) { RuntimeEnabledFeatures::setSuboriginsEnabled(true); String host, realHost, suborigin; host = "test.com"; EXPECT_FALSE(SecurityOrigin::deserializeSuboriginAndHost(host, suborigin, realHost)); host = "foobar_test.com"; EXPECT_TRUE(SecurityOrigin::deserializeSuboriginAndHost(host, suborigin, realHost)); EXPECT_EQ("test.com", realHost); EXPECT_EQ("foobar", suborigin); RefPtr<SecurityOrigin> origin; StringBuilder builder; origin = SecurityOrigin::createFromString("https://foobar_test.com"); origin->buildRawString(builder); EXPECT_EQ("https://foobar_test.com", builder.toString()); builder.clear(); origin = SecurityOrigin::createFromString("https://test.com"); origin->addSuborigin("foobar"); origin->buildRawString(builder); EXPECT_EQ("https://foobar_test.com", builder.toString()); }
String WTFLoggingAccumulator::getAndResetAccumulatedLogs() { Locker<Lock> locker(accumulatorLock); String result = loggingAccumulator.toString(); loggingAccumulator.clear(); return result; }
TEST(StringBuilderTest, Clear) { StringBuilder builder; builder.append("0123456789"); builder.clear(); expectEmpty(builder); }
static void commitLiteralToken(StringBuilder& literalBuffer, Vector<DateFormatToken>& tokens) { if (literalBuffer.length() <= 0) return; tokens.append(DateFormatToken(literalBuffer.toString())); literalBuffer.clear(); }
void XmlContext::flush() { if (text.length() > 0) { exprs.addAtEnd(ALLOC(LiteralString, (text.str(), text_start))); /* char buf[500]; getn(buf, text.str(), 500); puts(buf); */ text.clear(); } }
TEST_F(SecurityOriginTest, SuboriginsParsing) { RuntimeEnabledFeatures::setSuboriginsEnabled(true); String protocol, realProtocol, host, realHost, suborigin; protocol = "https"; host = "test.com"; EXPECT_FALSE(SecurityOrigin::deserializeSuboriginAndProtocolAndHost( protocol, host, suborigin, realProtocol, realHost)); protocol = "https-so"; host = "foobar.test.com"; EXPECT_TRUE(SecurityOrigin::deserializeSuboriginAndProtocolAndHost( protocol, host, suborigin, realProtocol, realHost)); EXPECT_EQ("https", realProtocol); EXPECT_EQ("test.com", realHost); EXPECT_EQ("foobar", suborigin); RefPtr<SecurityOrigin> origin; StringBuilder builder; origin = SecurityOrigin::createFromString("https-so://foobar.test.com"); origin->buildRawString(builder, true); EXPECT_EQ("https-so://foobar.test.com", builder.toString()); EXPECT_EQ("https-so://foobar.test.com", origin->toString()); builder.clear(); origin->buildRawString(builder, false); EXPECT_EQ("https://test.com", builder.toString()); EXPECT_EQ("https://test.com", origin->toPhysicalOriginString()); Suborigin suboriginObj; suboriginObj.setName("foobar"); builder.clear(); origin = SecurityOrigin::createFromString("https://test.com"); origin->addSuborigin(suboriginObj); origin->buildRawString(builder, true); EXPECT_EQ("https-so://foobar.test.com", builder.toString()); EXPECT_EQ("https-so://foobar.test.com", origin->toString()); builder.clear(); origin->buildRawString(builder, false); EXPECT_EQ("https://test.com", builder.toString()); EXPECT_EQ("https://test.com", origin->toPhysicalOriginString()); }
static bool consumeNamedEntity(SegmentedString& source, StringBuilder& decodedEntity, bool& notEnoughCharacters, UChar additionalAllowedCharacter, UChar& cc) { StringBuilder consumedCharacters; HTMLEntitySearch entitySearch; while (!source.isEmpty()) { cc = source.currentChar(); entitySearch.advance(cc); if (!entitySearch.isEntityPrefix()) break; consumedCharacters.append(cc); source.advance(); } notEnoughCharacters = source.isEmpty(); if (notEnoughCharacters) { // We can't an entity because there might be a longer entity // that we could match if we had more data. unconsumeCharacters(source, consumedCharacters); return false; } if (!entitySearch.mostRecentMatch()) { unconsumeCharacters(source, consumedCharacters); return false; } if (entitySearch.mostRecentMatch()->length != entitySearch.currentLength()) { // We've consumed too many characters. We need to walk the // source back to the point at which we had consumed an // actual entity. unconsumeCharacters(source, consumedCharacters); consumedCharacters.clear(); const int length = entitySearch.mostRecentMatch()->length; const LChar* reference = entitySearch.mostRecentMatch()->entity; for (int i = 0; i < length; ++i) { cc = source.currentChar(); ASSERT_UNUSED(reference, cc == *reference++); consumedCharacters.append(cc); source.advance(); ASSERT(!source.isEmpty()); } cc = source.currentChar(); } if (entitySearch.mostRecentMatch()->lastCharacter() == ';' || !additionalAllowedCharacter || !(isASCIIAlphanumeric(cc) || cc == '=')) { decodedEntity.append(entitySearch.mostRecentMatch()->firstValue); if (entitySearch.mostRecentMatch()->secondValue) decodedEntity.append(entitySearch.mostRecentMatch()->secondValue); return true; } unconsumeCharacters(source, consumedCharacters); return false; }
void MarkupAccumulator::generateUniquePrefix(QualifiedName& prefixedName, const Namespaces& namespaces) { // http://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#normalizeDocumentAlgo // Find a prefix following the pattern "NS" + index (starting at 1) and make sure this // prefix is not declared in the current scope. StringBuilder builder; do { builder.clear(); builder.append("NS"); builder.appendNumber(++m_prefixLevel); const AtomicString& name = builder.toAtomicString(); if (!namespaces.get(name.impl())) { prefixedName.setPrefix(name); return; } } while (true); }
TEST(StringBuilderTest, ToAtomicStringOnEmpty) { { // Default constructed. StringBuilder builder; AtomicString atomicString = builder.toAtomicString(); EXPECT_EQ(emptyAtom, atomicString); } { // With capacity. StringBuilder builder; builder.reserveCapacity(64); AtomicString atomicString = builder.toAtomicString(); EXPECT_EQ(emptyAtom, atomicString); } { // AtomicString constructed from a null string. StringBuilder builder; builder.append(String()); AtomicString atomicString = builder.toAtomicString(); EXPECT_EQ(emptyAtom, atomicString); } { // AtomicString constructed from an empty string. StringBuilder builder; builder.append(emptyString()); AtomicString atomicString = builder.toAtomicString(); EXPECT_EQ(emptyAtom, atomicString); } { // AtomicString constructed from an empty StringBuilder. StringBuilder builder; StringBuilder emptyBuilder; builder.append(emptyBuilder); AtomicString atomicString = builder.toAtomicString(); EXPECT_EQ(emptyAtom, atomicString); } { // AtomicString constructed from an empty char* string. StringBuilder builder; builder.append("", 0); AtomicString atomicString = builder.toAtomicString(); EXPECT_EQ(emptyAtom, atomicString); } { // Cleared StringBuilder. StringBuilder builder; builder.appendLiteral("WebKit"); builder.clear(); AtomicString atomicString = builder.toAtomicString(); EXPECT_EQ(emptyAtom, atomicString); } }
void FileReader::convertToDataURL(const Vector<char>& rawData, const String& fileType, StringBuilder& builder) { builder.clear(); builder.append("data:"); if (!rawData.size()) return; if (!fileType.isEmpty()) { builder.append(fileType); builder.append(";base64,"); } else builder.append("base64,"); Vector<char> out; base64Encode(rawData, out); out.append('\0'); builder.append(out.data()); }
static void fillContainerFromString(ContainerNode* paragraph, const String& string) { Document& document = paragraph->document(); if (string.isEmpty()) { paragraph->appendChild(HTMLBRElement::create(document)); return; } DCHECK_EQ(string.find('\n'), kNotFound) << string; Vector<String> tabList; string.split('\t', true, tabList); StringBuilder tabText; bool first = true; size_t numEntries = tabList.size(); for (size_t i = 0; i < numEntries; ++i) { const String& s = tabList[i]; // append the non-tab textual part if (!s.isEmpty()) { if (!tabText.isEmpty()) { paragraph->appendChild( createTabSpanElement(document, tabText.toString())); tabText.clear(); } Text* textNode = document.createTextNode( stringWithRebalancedWhitespace(s, first, i + 1 == numEntries)); paragraph->appendChild(textNode); } // there is a tab after every entry, except the last entry // (if the last character is a tab, the list gets an extra empty entry) if (i + 1 != numEntries) tabText.append('\t'); else if (!tabText.isEmpty()) paragraph->appendChild( createTabSpanElement(document, tabText.toString())); first = false; } }
/** * Destructor */ STM32F7USB::~STM32F7USB() { _accumulator.clear(); }
void ProcessDiskInfo(wstring apiKey, wstring envKey, SOCKET socketHandle, sockaddr_in remoteServAddr, int interval) { StringBuilder sb; int sleepTime = interval * 1000; wchar_t driveLetter[_MAX_PATH + 1]; HRESULT hr; ULARGE_INTEGER freeBytesAvailable; ULARGE_INTEGER totalNumberOfBytes; ULARGE_INTEGER totalNumberOfFreeBytes; while (true) { if (g_servicePaused == FALSE) { sb.clear(); sb.push_back(L"{"); sb.push_back(L"\"" + StorageInfo::Members::disk + L"\":"); sb.push_back(L"["); DWORD dwNeedLength = ::GetLogicalDriveStrings(0, NULL); wchar_t *pWchBuf = nullptr; wchar_t *pHead = nullptr; do { if (dwNeedLength != 0) { pWchBuf = new wchar_t[dwNeedLength + 1]; if (pWchBuf == nullptr) { break; } pHead = pWchBuf; DWORD dwResult = ::GetLogicalDriveStrings(dwNeedLength, pWchBuf); while (dwResult > 0) { hr = StringCchPrintf(driveLetter, _MAX_PATH, L"%s", pWchBuf); if (FAILED(hr) == TRUE) { break; } int length = wcslen(driveLetter) + 1; if (length < 2) { break; } dwResult -= length; pWchBuf += length; if (::GetDiskFreeSpaceEx(driveLetter, &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes) == FALSE) { break; } driveLetter[1] = '\0'; sb.push_back(L"{ \"" + DiskInfo::Members::name + L"\": \""); sb.push_back(driveLetter); sb.push_back(L"\", "); sb.push_back(L"\"" + DiskInfo::Members::size + L"\": "); sb.push_back((__int64)totalNumberOfBytes.QuadPart); sb.push_back(L", "); sb.push_back(L"\"" + DiskInfo::Members::current + L"\": "); sb.push_back((__int64)(totalNumberOfBytes.QuadPart - totalNumberOfFreeBytes.QuadPart)); sb.push_back(L"}"); if (dwResult > 0) { sb.push_back(L","); } } } } while (false); if (pHead != nullptr) { delete[] pHead; } sb.push_back(L"],"); sb.push_back(L"\"" + PacketBase::Members::groupKey + L"\":"); sb.push_back(L"\""); sb.push_back(apiKey); sb.push_back(L"\","); sb.push_back(L"\"" + PacketBase::Members::machineId + L"\":"); sb.push_back(L"\""); sb.push_back(envKey); sb.push_back(L"\""); sb.push_back(L"}"); } #if defined(_DEBUG) // ::OutputDebugString(sb.ToString().c_str()); #endif SendToServer(socketHandle, remoteServAddr, sb); if (g_isConsoleApp == TRUE) { printf(":"); } DWORD dwEventResult = ::WaitForSingleObject(g_killServiceEvent, sleepTime); if (dwEventResult == WAIT_TIMEOUT) { continue; } #if defined(_DEBUG) ::OutputDebugString(L"ProcessDiskInfo-thread exited."); #endif break; } }
void ProcessCpuMemInfo(wstring apiKey, wstring envKey, SOCKET socketHandle, sockaddr_in remoteServAddr, int interval) { StringBuilder sb; int sleepTime = interval * 1000; while (true) { if (g_servicePaused == FALSE) { sb.clear(); sb.push_back(L"{"); { sb.push_back(L"\"" + SystemInfo::Members::cpuUsage + L"\":"); sb.push_back(L"{"); { float totalUsage = 0.0f; sb.push_back(L"\"" + CpuInfo::Members::unit + L"\":["); if (RetrieveCpuInfo(sb, &totalUsage) == false) { Sleep(1000); continue; } sb.push_back(L"]"); wchar_t buf[40]; StringCchPrintf(buf, 40, L", \"%s\": %.2f", CpuInfo::Members::total.c_str(), totalUsage / 100); sb.push_back(buf); } sb.push_back(L"},"); __int64 maxMemory; __int64 currentUsage; GetMemoryInfo(&maxMemory, ¤tUsage); sb.push_back(L"\"" + SystemInfo::Members::memoryUsage + L"\":"); { sb.push_back(L"{\"" + MemoryInfo::Members::max + L"\":"); sb.push_back(maxMemory); sb.push_back(L", \"" + MemoryInfo::Members::current + L"\":"); sb.push_back(currentUsage); sb.push_back(L"},"); } sb.push_back(L"\"" + PacketBase::Members::groupKey + L"\":"); sb.push_back(L"\""); sb.push_back(apiKey); sb.push_back(L"\","); sb.push_back(L"\"" + PacketBase::Members::machineId + L"\":"); sb.push_back(L"\""); sb.push_back(envKey); sb.push_back(L"\""); } sb.push_back(L"}"); SendToServer(socketHandle, remoteServAddr, sb); if (g_isConsoleApp == TRUE) { printf("."); } } if (::WaitForSingleObject(g_killServiceEvent, sleepTime) == WAIT_TIMEOUT) { continue; } #if defined(_DEBUG) ::OutputDebugString(L"ProcessCpuMemInfo-thread exited."); #endif break; } }
void WTFLoggingAccumulator::resetAccumulatedLogs() { Locker<Lock> locker(accumulatorLock); loggingAccumulator.clear(); }
bool DateTimeFormat::parse(const String& source, TokenHandler& tokenHandler) { enum State { StateInQuote, StateInQuoteQuote, StateLiteral, StateQuote, StateSymbol, } state = StateLiteral; FieldType fieldType = FieldTypeLiteral; StringBuilder literalBuffer; int fieldCounter = 0; for (unsigned index = 0; index < source.length(); ++index) { const UChar ch = source[index]; switch (state) { case StateInQuote: if (ch == '\'') { state = StateInQuoteQuote; break; } literalBuffer.append(ch); break; case StateInQuoteQuote: if (ch == '\'') { literalBuffer.append('\''); state = StateInQuote; break; } fieldType = mapCharacterToFieldType(ch); if (fieldType == FieldTypeInvalid) return false; if (fieldType == FieldTypeLiteral) { literalBuffer.append(ch); state = StateLiteral; break; } if (literalBuffer.length()) { tokenHandler.visitLiteral(literalBuffer.toString()); literalBuffer.clear(); } fieldCounter = 1; state = StateSymbol; break; case StateLiteral: if (ch == '\'') { state = StateQuote; break; } fieldType = mapCharacterToFieldType(ch); if (fieldType == FieldTypeInvalid) return false; if (fieldType == FieldTypeLiteral) { literalBuffer.append(ch); break; } if (literalBuffer.length()) { tokenHandler.visitLiteral(literalBuffer.toString()); literalBuffer.clear(); } fieldCounter = 1; state = StateSymbol; break; case StateQuote: literalBuffer.append(ch); state = ch == '\'' ? StateLiteral : StateInQuote; break; case StateSymbol: { ASSERT(fieldType != FieldTypeInvalid); ASSERT(fieldType != FieldTypeLiteral); ASSERT(literalBuffer.isEmpty()); FieldType fieldType2 = mapCharacterToFieldType(ch); if (fieldType2 == FieldTypeInvalid) return false; if (fieldType == fieldType2) { ++fieldCounter; break; } tokenHandler.visitField(fieldType, fieldCounter); if (fieldType2 == FieldTypeLiteral) { if (ch == '\'') { state = StateQuote; } else { literalBuffer.append(ch); state = StateLiteral; } break; } fieldCounter = 1; fieldType = fieldType2; break; } } } ASSERT(fieldType != FieldTypeInvalid); switch (state) { case StateLiteral: case StateInQuoteQuote: if (literalBuffer.length()) tokenHandler.visitLiteral(literalBuffer.toString()); return true; case StateQuote: case StateInQuote: if (literalBuffer.length()) tokenHandler.visitLiteral(literalBuffer.toString()); return false; case StateSymbol: ASSERT(fieldType != FieldTypeLiteral); ASSERT(!literalBuffer.length()); tokenHandler.visitField(fieldType, fieldCounter); return true; } ASSERT_NOT_REACHED(); return false; }
PassRefPtr<SharedBuffer> MHTMLArchive::generateMHTMLData(Page* page, bool useBinaryEncoding) { Vector<PageSerializer::Resource> resources; PageSerializer pageSerializer(&resources); pageSerializer.serialize(page); String boundary = generateRandomBoundary(); String endOfResourceBoundary = makeString("--", boundary, "\r\n"); tm localTM; getCurrentLocalTime(&localTM); String dateString = makeRFC2822DateString(localTM.tm_wday, localTM.tm_mday, localTM.tm_mon, 1900 + localTM.tm_year, localTM.tm_hour, localTM.tm_min, localTM.tm_sec, calculateUTCOffset() / (1000 * 60)); StringBuilder stringBuilder; stringBuilder.append("From: <Saved by WebKit>\r\n"); stringBuilder.append("Subject: "); // We replace non ASCII characters with '?' characters to match IE's behavior. stringBuilder.append(replaceNonPrintableCharacters(page->mainFrame()->document()->title())); stringBuilder.append("\r\nDate: "); stringBuilder.append(dateString); stringBuilder.append("\r\nMIME-Version: 1.0\r\n"); stringBuilder.append("Content-Type: multipart/related;\r\n"); stringBuilder.append("\ttype=\""); stringBuilder.append(page->mainFrame()->document()->suggestedMIMEType()); stringBuilder.append("\";\r\n"); stringBuilder.append("\tboundary=\""); stringBuilder.append(boundary); stringBuilder.append("\"\r\n\r\n"); // We use utf8() below instead of ascii() as ascii() replaces CRLFs with ?? (we still only have put ASCII characters in it). ASSERT(stringBuilder.toString().containsOnlyASCII()); CString asciiString = stringBuilder.toString().utf8(); RefPtr<SharedBuffer> mhtmlData = SharedBuffer::create(); mhtmlData->append(asciiString.data(), asciiString.length()); for (size_t i = 0; i < resources.size(); ++i) { const PageSerializer::Resource& resource = resources[i]; stringBuilder.clear(); stringBuilder.append(endOfResourceBoundary); stringBuilder.append("Content-Type: "); stringBuilder.append(resource.mimeType); const char* contentEncoding = 0; if (useBinaryEncoding) contentEncoding = binary; else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(resource.mimeType) || MIMETypeRegistry::isSupportedNonImageMIMEType(resource.mimeType)) contentEncoding = quotedPrintable; else contentEncoding = base64; stringBuilder.append("\r\nContent-Transfer-Encoding: "); stringBuilder.append(contentEncoding); stringBuilder.append("\r\nContent-Location: "); stringBuilder.append(resource.url); stringBuilder.append("\r\n\r\n"); asciiString = stringBuilder.toString().utf8(); mhtmlData->append(asciiString.data(), asciiString.length()); if (!strcmp(contentEncoding, binary)) { const char* data; size_t position = 0; while (size_t length = resource.data->getSomeData(data, position)) { mhtmlData->append(data, length); position += length; } } else { // FIXME: ideally we would encode the content as a stream without having to fetch it all. const char* data = resource.data->data(); size_t dataLength = resource.data->size(); Vector<char> encodedData; if (!strcmp(contentEncoding, quotedPrintable)) { quotedPrintableEncode(data, dataLength, encodedData); mhtmlData->append(encodedData.data(), encodedData.size()); mhtmlData->append("\r\n", 2); } else { ASSERT(!strcmp(contentEncoding, base64)); // We are not specifying insertLFs = true below as it would cut the lines with LFs and MHTML requires CRLFs. base64Encode(data, dataLength, encodedData); const size_t maximumLineLength = 76; size_t index = 0; size_t encodedDataLength = encodedData.size(); do { size_t lineLength = std::min(encodedDataLength - index, maximumLineLength); mhtmlData->append(encodedData.data() + index, lineLength); mhtmlData->append("\r\n", 2); index += maximumLineLength; } while (index < encodedDataLength); } } } asciiString = makeString("--", boundary, "--\r\n").utf8(); mhtmlData->append(asciiString.data(), asciiString.length()); return mhtmlData.release(); }
void RotLibConvert_Dunbrack_BBDep::readLib( const std::string& _LibraryFilename, RotamerLibrary& _RotLib ) { StringBuilder sb; std::ifstream* p_torsionFile; try { if( _RotLib.isFinalised() ) throw ProcedureException("readLib() is not allowed, the rotamer library has been finalised, no further import can occur"); // Mappings for things like HIS -> HIE HID HIP _RotLib.addIonisationAliasWorkingDefaults(); // Make sure Alanine and Glycine are defined non-rotamer residues (the library itself ignores them) _RotLib.addAsBlankRotamer("ALA"); _RotLib.addAsBlankRotamer("GLY"); p_torsionFile = new std::ifstream(_LibraryFilename.c_str(), std::ifstream::in); std::ifstream& torsionFile = *p_torsionFile; if( !torsionFile.is_open() ) throw(IOException( "Dunbrack BB-independent torsional definition file not found: '" + _LibraryFilename + "'!" )); ParseData pd; ContainerType data; StringBuilder prevResName(3); StringBuilder currResName(4); StringBuilder cacheLine; while( true ) { if( cacheLine.size() > 0 ) { sb.setTo(cacheLine); cacheLine.clear(); } else if( !(sb << torsionFile) ) { break; } sb.Trim(); if( sb.size() == 0 ) continue; // blank line currResName.setTo(sb,0,3); if( prevResName.size() == 0 ) { prevResName.setTo( currResName ); ASSERT( pd.parse( sb ), ParseException, "Malformed line"); data.push_back( pd ); } else if( prevResName.compare( currResName, 0, 3, 0 ) ) { // Woooo - we found one :-D ASSERT( pd.parse( sb ), ParseException, "Malformed line"); data.push_back( pd ); } else { if( data.size() > 0 ) { processResData( prevResName.toString(), data, _RotLib, switchImportMode ); ASSERT( data.size() == 0, CodeException, "CodeFail"); } prevResName.setTo( currResName ); cacheLine.setTo( sb ); // we havent actually processed the current line! Store it. } } if( data.size() > 0 ) { processResData( prevResName.toString(), data, _RotLib, switchImportMode ); ASSERT( data.size() == 0, CodeException, "CodeFail"); } // Ensure file-handle cleanup p_torsionFile->close(); delete p_torsionFile; } catch( ExceptionBase ex ) { // Ensure file-handle cleanup p_torsionFile->close(); delete p_torsionFile; throw ex; } }
void predict(Options::PredictEnum mode, ReportWriter *writer, const RomAccessor &rom, Trace &trace, const AnnotationResolver &annotations) { if (mode == Options::PRD_NEVER) return; bool limit_to_functions = mode == Options::PRD_FUNCTIONS; Profile profile("Predict", true); struct PredictBranch { const Annotation *annotation; Pointer from_pc; Pointer pc; uint16_t DP, P; uint8_t DB; }; std::vector<PredictBranch> predict_brances; LargeBitfield has_op(256*64*1024); LargeBitfield inside_op(256 * 64 * 1024); for (auto opsit : trace.ops) { const Pointer pc = opsit.first; const Trace::OpVariantLookup &vl = opsit.second; const OpInfo &example = trace.variant(vl, 0); const uint8_t* data = rom.evalPtr(pc); const uint8_t opcode = data[0]; uint32_t op_size = instruction_size(opcode, is_memory_accumulator_wide(example.P), is_index_wide(example.P)); for (uint32_t i = 0; i < op_size; ++i) { has_op.set_bit(bank_add(pc, i)); if (i!=0) inside_op.set_bit(bank_add(pc, i)); } StringBuilder sb; Pointer target_jump, target_no_jump; bool branch_or_jump = decode_static_jump(opcode, rom, pc, &target_jump, &target_no_jump); const Hint *hint = annotations.hint(pc); if (hint && hint->has_hint(Hint::BRANCH_ALWAYS)) { target_no_jump = INVALID_POINTER; } if (hint && hint->has_hint(Hint::BRANCH_NEVER)) { target_jump = INVALID_POINTER; } if (!branch_or_jump) continue; const Annotation *source_annotation = nullptr, *target_annotation = nullptr; annotations.resolve_annotation(pc, &source_annotation); if (target_jump != INVALID_POINTER) { trace.labels.set_bit(target_jump); annotations.resolve_annotation(target_jump, &target_annotation); } if (source_annotation || !limit_to_functions) { PredictBranch p; p.annotation = source_annotation; p.from_pc = pc; p.DB = example.DB; p.DP = example.DP; p.P = example.P; if (target_jump != INVALID_POINTER && (target_annotation == source_annotation || !limit_to_functions)) { p.pc = target_jump; CUSTOM_ASSERT(target_jump != INVALID_POINTER); predict_brances.push_back(p); } if (target_no_jump != INVALID_POINTER && (!limit_to_functions || (target_no_jump >= source_annotation->startOfRange && target_no_jump <= source_annotation->endOfRange))) { // BRA,BRL and the jumps always branches/jumps p.pc = target_no_jump; CUSTOM_ASSERT(target_no_jump != INVALID_POINTER); predict_brances.push_back(p); } } } StringBuilder sb; sb.clear(); if (writer) writer->writeSeperator("Prediction diagnostics"); for (int pbi=0; pbi<(int)predict_brances.size(); ++pbi) { const PredictBranch pb = predict_brances[pbi]; Pointer pc = pb.pc; Pointer r0 = limit_to_functions ? pb.annotation->startOfRange : 0, r1 = limit_to_functions ? pb.annotation->endOfRange : 0xFFFFFF; uint16_t P = pb.P, DP = pb.DP; uint8_t DB = pb.DB; bool P_unknown = false; if (inside_op[pc]) { if (writer) { sb.clear(); sb.format("Predicted jump at %06X jumped inside instruction at %06X. Consider adding a \"hint branch_always/branch_never %06X\" annotation.", pc, pb.from_pc, pb.from_pc); writer->writeComment(sb); } printf("Warning; predicted jump went inside instruction at %06X (from %06X)\n", pc, pb.from_pc); } while (!has_op[pc] && pc >= r0 && pc <= r1) { // Make sure we don't run into a data scope const Annotation *data_scope = nullptr, *function_scope = nullptr; annotations.resolve_annotation(pc, &function_scope, &data_scope); if (data_scope) continue; if (function_scope && function_scope != pb.annotation) continue; uint8_t opcode = rom.evalByte(pc); bool abort_unknown_P = P_unknown; if (P_unknown) { switch(opcode) { case 0x02: // COP const case 0x40: // RTI case 0x6B: // RTL case 0x60: // RTS case 0x3B: // TSC case 0xBA: // TSX case 0x8A: // TXA case 0x9A: // TXS case 0x9B: // TXY case 0x98: // TYA case 0xBB: // TYX case 0xCB: // WAI case 0xEB: // XBA case 0xFB: // XCE case 0xAA: // TAX case 0xA8: // TAY case 0x5B: // TCD case 0x1B: // TCS case 0x7B: // TDC case 0xDB: // STP case 0x38: // SEC case 0xF8: // SED case 0x78: // SEI case 0xE2: // SEP case 0xC2: // REP case 0x18: // CLC case 0xD8: // CLD case 0x58: // CLI case 0xB8: // CLV case 0xCA: // DEX case 0x88: // DEY case 0xE8: // INX case 0xC8: // INY case 0xEA: // NOP case 0x8B: // PHB case 0x0B: // PHD case 0x4B: // PHK case 0x08: // PHP case 0xDA: // PHX case 0x5A: // PHY case 0x68: // PLA case 0xAB: // PLB case 0x2B: // PLD case 0x28: // PLP case 0xFA: // PLX case 0x7A: // PLY abort_unknown_P = false; } } if (abort_unknown_P) { if (writer) { sb.clear(); sb.format("Aborting trace at %06X due to unknown processor status", pc); if (pb.annotation) sb.format("(in %s)", pb.annotation->name.c_str()); writer->writeComment(sb); } break; } uint8_t operand = rom.evalByte(pc + 1); Pointer target_jump, target_no_jump; bool is_jump_or_branch = decode_static_jump(opcode, rom, pc, &target_jump, &target_no_jump); { Trace::OpVariantLookup l; l.count = 1; l.offset = (uint32_t)trace.ops_variants.size(); trace.ops[pc] = l; OpInfo info; info.P = P_unknown ? 0 : P; info.DB = DB; info.DP = DP; info.X = info.Y = 0; info.jump_target = target_jump; info.indirect_base_pointer = INVALID_POINTER; // TODO: We should be able to set this one sometimes trace.ops_variants.push_back(info); // Note that DB and DP here represent a lie :) } const int op_size = instruction_size(rom.evalByte(pc), is_memory_accumulator_wide(P), is_index_wide(P)); for (int i=0; i<op_size; ++i) { // TODO: We should do overlap test for entire range we are "using" now // Also first might not always be best! trace.is_predicted.set_bit(bank_add(pc, i)); has_op.set_bit(bank_add(pc, i)); if (i != 0) inside_op.set_bit(bank_add(pc, i)); } const Hint *hint = annotations.hint(pc); if (hint && hint->has_hint(Hint::BRANCH_NEVER)) { target_jump = INVALID_POINTER; } bool is_jsr = opcode == 0x20||opcode==0x22||opcode==0xFC; if (hint && hint->has_hint(Hint::JUMP_IS_JSR)) { is_jump_or_branch = false; is_jsr = true; } if (is_jump_or_branch) { const Annotation *function = nullptr; if (target_jump != INVALID_POINTER) { annotations.resolve_annotation(target_jump, &function); trace.labels.set_bit(target_jump); } if (limit_to_functions && writer && (!function || function != pb.annotation)) { sb.clear(); sb.format("Branch going out of %s to ", pb.annotation->name.c_str()); if (function) { sb.format("%s [%06X]", function->name.c_str(), target_jump); } else { sb.format("%06X", target_jump); } sb.format(". Not following due to range restriction."); writer->writeComment(sb); } if (target_jump != INVALID_POINTER) { PredictBranch npb = pb; npb.from_pc = pc; npb.pc = target_jump; predict_brances.push_back(npb); } if (hint && hint->has_hint(Hint::BRANCH_ALWAYS)) { // Never continoue after this op since it always diverges control flow continue; } } else if (opcode == 0xE2) { // TODO: // * When we get a REP or SEP parts or P become known again. We could track unknown per the three flags and update. // * Updating DB/DP might be interesting if (operand & 0x10) P |= 0x0010; if (operand & 0x20) P |= 0x0020; } else if (opcode == 0xC2) { if (operand & 0x10) P &= ~0x0010; if (operand & 0x20) P &= ~0x0020; } else if (opcode == 0x28 || opcode == 0xFB) { P_unknown = true; // PLP or XCE // A jump or BRA, stop execution flow (not JSR or non-BRA-branch) } else if (opcode == 0x4C||opcode==0x5C||opcode==0x6C||opcode==0x7C||opcode==0x80) { if (writer && opcode != 0x80) { sb.clear(); sb.format("Not following jump (opcode %02X) at %06X in %s. Only absolute jumps supported.", opcode, pc, pb.annotation->name.c_str()); writer->writeComment(sb); } // TODO: if there is a trace annotation about jmp being jsr we could go on continue; } else if (is_jsr) { if (writer) { sb.clear(); sb.format("Not following jump with subroutine (opcode %02X) at %06X", opcode, pc); if (pb.annotation) sb.format(" in %s.", pb.annotation->name.c_str()); writer->writeComment(sb); } } else if (opcode == 0x40 || opcode == 0x6B || opcode == 0x60) { // some sort of return, stop execution flow continue; } pc += op_size; } } }