1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "CacheFileUtils.h"
7 #include "LoadContextInfo.h"
16 namespace CacheFileUtils
{
21 * A simple recursive descent parser for the mapping key.
26 KeyParser(nsACString::const_iterator aCaret
, nsACString::const_iterator aEnd
)
29 // Initialize attributes to their default values
30 , appId(nsILoadContextInfo::NO_APP_ID
)
34 // Initialize the cache key to a zero length by default
41 // Current character being parsed
42 nsACString::const_iterator caret
;
43 // The end of the buffer
44 nsACString::const_iterator
const end
;
52 // Position of the cache key, if present
53 nsACString::const_iterator cacheKey
;
55 // Keeps the last tag name, used for alphabetical sort checking
60 // Expects to be at the tag name or at the end
64 // 'Read' the tag name and move to the next char
65 char const tag
= *caret
++;
66 // Check the alphabetical order, hard-fail on disobedience
67 if (!(lastTag
< tag
|| tag
== ':'))
74 // last possible tag, when present there is the cacheKey following,
75 // not terminated with ',' and no need to unescape.
89 nsAutoCString appIdString
;
90 if (!ParseValue(&appIdString
))
94 int64_t appId64
= appIdString
.ToInteger64(&rv
);
96 return false; // appid value is mandatory
97 if (appId64
< 0 || appId64
> PR_UINT32_MAX
)
98 return false; // not in the range
99 appId
= static_cast<uint32_t>(appId64
);
104 if (!ParseValue(&idEnhance
))
108 if (!ParseValue()) // skip any tag values, optional
113 // Recurse to the next tag
114 return ParseNextTagOrEnd();
117 bool ParseNextTagOrEnd()
119 // We expect a comma after every tag
120 if (caret
== end
|| *caret
++ != ',')
127 bool ParseValue(nsACString
* result
= nullptr)
129 // If at the end, fail since we expect a comma ; value may be empty tho
133 // Remeber where the value starts
134 nsACString::const_iterator val
= caret
;
135 nsACString::const_iterator comma
= end
;
137 while (caret
!= end
) {
138 nsACString::const_iterator at
= caret
;
139 ++caret
; // we can safely break/continue the loop now
143 // another comma (we have found ",," -> escape)
153 // after a single comma
158 // At this point |comma| points to the last and lone ',' we've hit.
159 // If a lone comma was not found, |comma| is at the end of the buffer,
160 // that is not expected and we return failure.
165 // No ReplaceSubstring on nsACString..
166 nsAutoCString
_result(Substring(val
, caret
));
167 _result
.ReplaceSubstring(NS_LITERAL_CSTRING(",,"), NS_LITERAL_CSTRING(","));
168 result
->Assign(_result
);
170 result
->Assign(Substring(val
, caret
));
178 already_AddRefed
<LoadContextInfo
> Parse()
180 nsRefPtr
<LoadContextInfo
> info
;
182 info
= GetLoadContextInfo(isPrivate
, appId
, isInBrowser
, isAnonymous
);
184 return info
.forget();
187 void URISpec(nsACString
&result
)
189 // cacheKey is either pointing to end or the position where the cache key is.
190 result
.Assign(Substring(cacheKey
, end
));
193 void IdEnhance(nsACString
&result
)
195 result
.Assign(idEnhance
);
201 already_AddRefed
<nsILoadContextInfo
>
202 ParseKey(const nsCSubstring
&aKey
,
203 nsCSubstring
*aIdEnhance
,
204 nsCSubstring
*aURISpec
)
206 nsACString::const_iterator caret
, end
;
207 aKey
.BeginReading(caret
);
208 aKey
.EndReading(end
);
210 KeyParser
parser(caret
, end
);
211 nsRefPtr
<LoadContextInfo
> info
= parser
.Parse();
215 parser
.IdEnhance(*aIdEnhance
);
217 parser
.URISpec(*aURISpec
);
220 return info
.forget();
224 AppendKeyPrefix(nsILoadContextInfo
* aInfo
, nsACString
&_retval
)
227 * This key is used to salt file hashes. When form of the key is changed
228 * cache entries will fail to find on disk.
231 * Keep the attributes list sorted according their ASCII code.
234 if (aInfo
->IsAnonymous()) {
235 _retval
.AppendLiteral("a,");
238 if (aInfo
->IsInBrowserElement()) {
239 _retval
.AppendLiteral("b,");
242 if (aInfo
->AppId() != nsILoadContextInfo::NO_APP_ID
) {
244 _retval
.AppendInt(aInfo
->AppId());
248 if (aInfo
->IsPrivate()) {
249 _retval
.AppendLiteral("p,");
254 AppendTagWithValue(nsACString
& aTarget
, char const aTag
, nsCSubstring
const & aValue
)
256 aTarget
.Append(aTag
);
258 // First check the value string to save some memory copying
259 // for cases we don't need to escape at all (most likely).
260 if (!aValue
.IsEmpty()) {
261 if (aValue
.FindChar(',') == kNotFound
) {
263 aTarget
.Append(aValue
);
265 nsAutoCString
escapedValue(aValue
);
266 escapedValue
.ReplaceSubstring(
267 NS_LITERAL_CSTRING(","), NS_LITERAL_CSTRING(",,"));
268 aTarget
.Append(escapedValue
);
276 KeyMatchesLoadContextInfo(const nsACString
&aKey
, nsILoadContextInfo
*aInfo
,
279 nsCOMPtr
<nsILoadContextInfo
> info
= ParseKey(aKey
);
282 return NS_ERROR_FAILURE
;
285 *_retval
= info
->Equals(aInfo
);
289 ValidityPair::ValidityPair(uint32_t aOffset
, uint32_t aLen
)
290 : mOffset(aOffset
), mLen(aLen
)
294 ValidityPair::operator=(const ValidityPair
& aOther
)
296 mOffset
= aOther
.mOffset
;
302 ValidityPair::CanBeMerged(const ValidityPair
& aOther
) const
304 // The pairs can be merged into a single one if the start of one of the pairs
305 // is placed anywhere in the validity interval of other pair or exactly after
307 return IsInOrFollows(aOther
.mOffset
) || aOther
.IsInOrFollows(mOffset
);
311 ValidityPair::IsInOrFollows(uint32_t aOffset
) const
313 return mOffset
<= aOffset
&& mOffset
+ mLen
>= aOffset
;
317 ValidityPair::LessThan(const ValidityPair
& aOther
) const
319 if (mOffset
< aOther
.mOffset
) {
323 if (mOffset
== aOther
.mOffset
&& mLen
< aOther
.mLen
) {
331 ValidityPair::Merge(const ValidityPair
& aOther
)
333 MOZ_ASSERT(CanBeMerged(aOther
));
335 uint32_t offset
= std::min(mOffset
, aOther
.mOffset
);
336 uint32_t end
= std::max(mOffset
+ mLen
, aOther
.mOffset
+ aOther
.mLen
);
343 ValidityMap::Log() const
345 LOG(("ValidityMap::Log() - number of pairs: %u", mMap
.Length()));
346 for (uint32_t i
=0; i
<mMap
.Length(); i
++) {
347 LOG((" (%u, %u)", mMap
[i
].Offset() + 0, mMap
[i
].Len() + 0));
352 ValidityMap::Length() const
354 return mMap
.Length();
358 ValidityMap::AddPair(uint32_t aOffset
, uint32_t aLen
)
360 ValidityPair
pair(aOffset
, aLen
);
362 if (mMap
.Length() == 0) {
363 mMap
.AppendElement(pair
);
367 // Find out where to place this pair into the map, it can overlap only with
368 // one preceding pair and all subsequent pairs.
370 for (pos
= mMap
.Length(); pos
> 0; ) {
373 if (mMap
[pos
].LessThan(pair
)) {
374 // The new pair should be either inserted after pos or merged with it.
375 if (mMap
[pos
].CanBeMerged(pair
)) {
376 // Merge with the preceding pair
377 mMap
[pos
].Merge(pair
);
379 // They don't overlap, element must be placed after pos element
381 if (pos
== mMap
.Length()) {
382 mMap
.AppendElement(pair
);
384 mMap
.InsertElementAt(pos
, pair
);
392 // The new pair should be placed in front of all existing pairs.
393 mMap
.InsertElementAt(0, pair
);
397 // pos now points to merged or inserted pair, check whether it overlaps with
399 while (pos
+ 1 < mMap
.Length()) {
400 if (mMap
[pos
].CanBeMerged(mMap
[pos
+ 1])) {
401 mMap
[pos
].Merge(mMap
[pos
+ 1]);
402 mMap
.RemoveElementAt(pos
+ 1);
416 ValidityMap::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf
) const
418 return mMap
.SizeOfExcludingThis(mallocSizeOf
);
422 ValidityMap::operator[](uint32_t aIdx
)
424 return mMap
.ElementAt(aIdx
);