/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This designates the format for the "alt-data" metadata. // When the format changes we need to update the version. static uint32_t const kAltDataVersion = 1; constchar* kAltDataKey = "alt-data";
namespace {
/** * A simple recursive descent parser for the mapping key.
*/ class KeyParser : protected Tokenizer { public: explicit KeyParser(nsACString const& aInput)
: Tokenizer(aInput),
isAnonymous(false) // Initialize the cache key to a zero length by default
,
lastTag(0) {}
// Keeps the last tag name, used for alphabetical sort checking char lastTag;
// Classifier for the 'tag' character valid range. // Explicitly using unsigned char as 127 is -1 when signed and it would only // produce a warning. staticbool TagChar(constchar aChar) { unsignedchar c = static_cast<unsignedchar>(aChar); return c >= ' ' && c <= '\x7f';
}
bool ParseTags() { // Expects to be at the tag name or at the end if (CheckEOF()) { returntrue;
}
char tag; if (!ReadChar(&TagChar, &tag)) { returnfalse;
}
// Check the alphabetical order, hard-fail on disobedience if (!(lastTag < tag || tag == ':')) { returnfalse;
}
lastTag = tag;
switch (tag) { case':': // last possible tag, when present there is the cacheKey following, // not terminated with ',' and no need to unescape.
cacheKey.Rebind(mCursor, mEnd - mCursor); returntrue; case'O': {
nsAutoCString originSuffix; if (!ParseValue(&originSuffix) ||
!originAttribs.PopulateFromSuffix(originSuffix)) { returnfalse;
} break;
} case'p':
originAttribs.SyncAttributesWithPrivateBrowsing(true); break; case'b': // Leaving to be able to read and understand oldformatted entries break; case'a':
isAnonymous = true; break; case'i': { // Leaving to be able to read and understand oldformatted entries
uint32_t deprecatedAppId = 0; if (!ReadInteger(&deprecatedAppId)) { returnfalse; // not a valid 32-bit integer
} break;
} case'~': if (!ParseValue(&idEnhance)) { returnfalse;
} break; default: if (!ParseValue()) { // skip any tag values, optional returnfalse;
} break;
}
// We expect a comma after every tag if (!CheckChar(',')) { returnfalse;
}
// Recurse to the next tag return ParseTags();
}
bool ParseValue(nsACString* result = nullptr) { // If at the end, fail since we expect a comma ; value may be empty tho if (CheckEOF()) { returnfalse;
}
Token t; while (Next(t)) { if (!Token::Char(',').Equals(t)) { if (result) {
result->Append(t.Fragment());
} continue;
}
if (CheckChar(',')) { // Two commas in a row, escaping if (result) {
result->Append(',');
} continue;
}
// We must give the comma back since the upper calls expect it
Rollback(); returntrue;
}
returnfalse;
}
public:
already_AddRefed<LoadContextInfo> Parse() {
RefPtr<LoadContextInfo> info; if (ParseTags()) {
info = GetLoadContextInfo(isAnonymous, originAttribs);
}
if (info) { if (aIdEnhance) parser.IdEnhance(*aIdEnhance); if (aURISpec) parser.URISpec(*aURISpec);
}
return info.forget();
}
void AppendKeyPrefix(nsILoadContextInfo* aInfo, nsACString& _retval) { /** * This key is used to salt file hashes. When form of the key is changed * cache entries will fail to find on disk. * * IMPORTANT NOTE: * Keep the attributes list sorted according their ASCII code.
*/
if (!aInfo) { return;
}
OriginAttributes const* oa = aInfo->OriginAttributesPtr();
nsAutoCString suffix;
oa->CreateSuffix(suffix); if (!suffix.IsEmpty()) {
AppendTagWithValue(_retval, 'O', suffix);
}
if (aInfo->IsAnonymous()) {
_retval.AppendLiteral("a,");
}
if (aInfo->IsPrivate()) {
_retval.AppendLiteral("p,");
}
}
// First check the value string to save some memory copying // for cases we don't need to escape at all (most likely). if (!aValue.IsEmpty()) { if (!aValue.Contains(',')) { // No need to escape
aTarget.Append(aValue);
} else {
nsAutoCString escapedValue(aValue);
escapedValue.ReplaceSubstring(","_ns, ",,"_ns);
aTarget.Append(escapedValue);
}
}
bool ValidityPair::CanBeMerged(const ValidityPair& aOther) const { // The pairs can be merged into a single one if the start of one of the pairs // is placed anywhere in the validity interval of other pair or exactly after // its end. return IsInOrFollows(aOther.mOffset) || aOther.IsInOrFollows(mOffset);
}
if (mMap.Length() == 0) {
mMap.AppendElement(pair); return;
}
// Find out where to place this pair into the map, it can overlap only with // one preceding pair and all subsequent pairs.
uint32_t pos = 0; for (pos = mMap.Length(); pos > 0;) {
--pos;
if (mMap[pos].LessThan(pair)) { // The new pair should be either inserted after pos or merged with it. if (mMap[pos].CanBeMerged(pair)) { // Merge with the preceding pair
mMap[pos].Merge(pair);
} else { // They don't overlap, element must be placed after pos element
++pos; if (pos == mMap.Length()) {
mMap.AppendElement(pair);
} else {
mMap.InsertElementAt(pos, pair);
}
}
break;
}
if (pos == 0) { // The new pair should be placed in front of all existing pairs.
mMap.InsertElementAt(0, pair);
}
}
// pos now points to merged or inserted pair, check whether it overlaps with // subsequent pairs. while (pos + 1 < mMap.Length()) { if (mMap[pos].CanBeMerged(mMap[pos + 1])) {
mMap[pos].Merge(mMap[pos + 1]);
mMap.RemoveElementAt(pos + 1);
} else { break;
}
}
}
// static void DetailedCacheHitTelemetry::AddRecord(ERecType aType,
TimeStamp aLoadStart) { bool isUpToDate = false;
CacheIndex::IsUpToDate(&isUpToDate); if (!isUpToDate) { // Ignore the record when the entry file count might be incorrect return;
}
uint32_t avg = GetAverage();
uint64_t avgSq = static_cast<uint64_t>(avg) * avg;
uint64_t variance = mSumSq / mCnt; if (variance < avgSq) { // Due to rounding error when using integer data type, it can happen that // average of squares of the values is smaller than square of the average // of the values. In this case fix mSumSq.
variance = avgSq;
mSumSq = variance * mCnt;
}
// Compare mShortAvg with mFilteredAvg to find out whether cache is getting // slower. Use only data about single IO operations because ENTRY_OPEN can be // affected by more factors than a slow disk. for (uint32_t i = 0; i < ENTRY_OPEN; ++i) { if (i == IO_WRITE) { // Skip this data type. IsCacheSlow is used for determining cache slowness // when opening entries. Writes have low priority and it's normal that // they are delayed a lot, but this doesn't necessarily affect opening // cache entries. continue;
}
uint32_t avgLong = sData[i].GetAverage(true); if (avgLong == 0) { // We have no perf data yet, skip this data type. continue;
}
uint32_t avgShort = sData[i].GetAverage(false);
uint32_t stddevLong = sData[i].GetStdDev(true);
uint32_t maxdiff = avgLong + (3 * stddevLong);
if (avgShort > avgLong + maxdiff) {
LOG(
("CachePerfStats::IsCacheSlow() - result is slow based on perf " "type %u [avgShort=%u, avgLong=%u, stddevLong=%u]",
i, avgShort, avgLong, stddevLong));
++sCacheSlowCnt; returntrue;
}
}
nsresult ParseAlternativeDataInfo(constchar* aInfo, int64_t* _offset,
nsACString* _type) { // The format is: "1;12345,javascript/binary" // <version>;<offset>,<type>
mozilla::Tokenizer p(aInfo, nullptr, "/");
uint32_t altDataVersion = 0;
int64_t altDataOffset = -1;
// The metadata format has a wrong version number. if (!p.ReadInteger(&altDataVersion) || altDataVersion != kAltDataVersion) {
LOG(
("ParseAlternativeDataInfo() - altDataVersion=%u, " "expectedVersion=%u",
altDataVersion, kAltDataVersion)); return NS_ERROR_NOT_AVAILABLE;
}
if (!p.CheckChar(';') || !p.ReadInteger(&altDataOffset) ||
!p.CheckChar(',')) { return NS_ERROR_NOT_AVAILABLE;
}
// The requested alt-data representation is not available if (altDataOffset < 0) { return NS_ERROR_NOT_AVAILABLE;
}
if (_offset) {
*_offset = altDataOffset;
}
if (_type) {
mozilla::Unused << p.ReadUntil(Tokenizer::Token::EndOfFile(), *_type);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.