Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 4 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #ifndef MOZILLA_TIMEVARYING_H_ |
michael@0 | 7 | #define MOZILLA_TIMEVARYING_H_ |
michael@0 | 8 | |
michael@0 | 9 | #include "nsTArray.h" |
michael@0 | 10 | |
michael@0 | 11 | namespace mozilla { |
michael@0 | 12 | |
michael@0 | 13 | /** |
michael@0 | 14 | * This is just for CTOR/DTOR tracking. We can't put this in |
michael@0 | 15 | * TimeVarying directly because the different template instances have |
michael@0 | 16 | * different sizes and that confuses things. |
michael@0 | 17 | */ |
michael@0 | 18 | class TimeVaryingBase { |
michael@0 | 19 | protected: |
michael@0 | 20 | TimeVaryingBase() |
michael@0 | 21 | { |
michael@0 | 22 | MOZ_COUNT_CTOR(TimeVaryingBase); |
michael@0 | 23 | } |
michael@0 | 24 | ~TimeVaryingBase() |
michael@0 | 25 | { |
michael@0 | 26 | MOZ_COUNT_DTOR(TimeVaryingBase); |
michael@0 | 27 | } |
michael@0 | 28 | }; |
michael@0 | 29 | |
michael@0 | 30 | /** |
michael@0 | 31 | * Objects of this class represent values that can change over time --- |
michael@0 | 32 | * a mathematical function of time. |
michael@0 | 33 | * Time is the type of time values, T is the value that changes over time. |
michael@0 | 34 | * There are a finite set of "change times"; at each change time, the function |
michael@0 | 35 | * instantly changes to a new value. ReservedChanges should be set to the |
michael@0 | 36 | * expected number of change events that the object is likely to contain. |
michael@0 | 37 | * This value should be 0 for all consumers unless you know that a higher value |
michael@0 | 38 | * would be a benefit. |
michael@0 | 39 | * There is also a "current time" which must always advance (not go backward). |
michael@0 | 40 | * The function is constant for all times less than the current time. |
michael@0 | 41 | * When the current time is advanced, the value of the function at the new |
michael@0 | 42 | * current time replaces the values for all previous times. |
michael@0 | 43 | * |
michael@0 | 44 | * The implementation records a mCurrent (the value at the current time) |
michael@0 | 45 | * and an array of "change times" (greater than the current time) and the |
michael@0 | 46 | * new value for each change time. This is a simple but dumb implementation. |
michael@0 | 47 | * We maintain the invariant that each change entry in the array must have |
michael@0 | 48 | * a different value to the value in the previous change entry (or, for |
michael@0 | 49 | * the first change entry, mCurrent). |
michael@0 | 50 | */ |
michael@0 | 51 | template <typename Time, typename T, uint32_t ReservedChanges> |
michael@0 | 52 | class TimeVarying : public TimeVaryingBase { |
michael@0 | 53 | public: |
michael@0 | 54 | TimeVarying(const T& aInitial) : mCurrent(aInitial) {} |
michael@0 | 55 | /** |
michael@0 | 56 | * This constructor can only be called if mCurrent has a no-argument |
michael@0 | 57 | * constructor. |
michael@0 | 58 | */ |
michael@0 | 59 | TimeVarying() : mCurrent() {} |
michael@0 | 60 | |
michael@0 | 61 | /** |
michael@0 | 62 | * Sets the value for all times >= aTime to aValue. |
michael@0 | 63 | */ |
michael@0 | 64 | void SetAtAndAfter(Time aTime, const T& aValue) |
michael@0 | 65 | { |
michael@0 | 66 | for (int32_t i = mChanges.Length() - 1; i >= 0; --i) { |
michael@0 | 67 | NS_ASSERTION(i == int32_t(mChanges.Length() - 1), |
michael@0 | 68 | "Always considering last element of array"); |
michael@0 | 69 | if (aTime > mChanges[i].mTime) { |
michael@0 | 70 | if (mChanges[i].mValue != aValue) { |
michael@0 | 71 | mChanges.AppendElement(Entry(aTime, aValue)); |
michael@0 | 72 | } |
michael@0 | 73 | return; |
michael@0 | 74 | } |
michael@0 | 75 | if (aTime == mChanges[i].mTime) { |
michael@0 | 76 | if ((i > 0 ? mChanges[i - 1].mValue : mCurrent) == aValue) { |
michael@0 | 77 | mChanges.RemoveElementAt(i); |
michael@0 | 78 | return; |
michael@0 | 79 | } |
michael@0 | 80 | mChanges[i].mValue = aValue; |
michael@0 | 81 | return; |
michael@0 | 82 | } |
michael@0 | 83 | mChanges.RemoveElementAt(i); |
michael@0 | 84 | } |
michael@0 | 85 | if (mCurrent == aValue) { |
michael@0 | 86 | return; |
michael@0 | 87 | } |
michael@0 | 88 | mChanges.InsertElementAt(0, Entry(aTime, aValue)); |
michael@0 | 89 | } |
michael@0 | 90 | /** |
michael@0 | 91 | * Returns the final value of the function. If aTime is non-null, |
michael@0 | 92 | * sets aTime to the time at which the function changes to that final value. |
michael@0 | 93 | * If there are no changes after the current time, returns INT64_MIN in aTime. |
michael@0 | 94 | */ |
michael@0 | 95 | const T& GetLast(Time* aTime = nullptr) const |
michael@0 | 96 | { |
michael@0 | 97 | if (mChanges.IsEmpty()) { |
michael@0 | 98 | if (aTime) { |
michael@0 | 99 | *aTime = INT64_MIN; |
michael@0 | 100 | } |
michael@0 | 101 | return mCurrent; |
michael@0 | 102 | } |
michael@0 | 103 | if (aTime) { |
michael@0 | 104 | *aTime = mChanges[mChanges.Length() - 1].mTime; |
michael@0 | 105 | } |
michael@0 | 106 | return mChanges[mChanges.Length() - 1].mValue; |
michael@0 | 107 | } |
michael@0 | 108 | /** |
michael@0 | 109 | * Returns the value of the function just before time aTime. |
michael@0 | 110 | */ |
michael@0 | 111 | const T& GetBefore(Time aTime) const |
michael@0 | 112 | { |
michael@0 | 113 | if (mChanges.IsEmpty() || aTime <= mChanges[0].mTime) { |
michael@0 | 114 | return mCurrent; |
michael@0 | 115 | } |
michael@0 | 116 | int32_t changesLength = mChanges.Length(); |
michael@0 | 117 | if (mChanges[changesLength - 1].mTime < aTime) { |
michael@0 | 118 | return mChanges[changesLength - 1].mValue; |
michael@0 | 119 | } |
michael@0 | 120 | for (uint32_t i = 1; ; ++i) { |
michael@0 | 121 | if (aTime <= mChanges[i].mTime) { |
michael@0 | 122 | NS_ASSERTION(mChanges[i].mValue != mChanges[i - 1].mValue, |
michael@0 | 123 | "Only changed values appear in array"); |
michael@0 | 124 | return mChanges[i - 1].mValue; |
michael@0 | 125 | } |
michael@0 | 126 | } |
michael@0 | 127 | } |
michael@0 | 128 | /** |
michael@0 | 129 | * Returns the value of the function at time aTime. |
michael@0 | 130 | * If aEnd is non-null, sets *aEnd to the time at which the function will |
michael@0 | 131 | * change from the returned value to a new value, or INT64_MAX if that |
michael@0 | 132 | * never happens. |
michael@0 | 133 | * If aStart is non-null, sets *aStart to the time at which the function |
michael@0 | 134 | * changed to the returned value, or INT64_MIN if that happened at or |
michael@0 | 135 | * before the current time. |
michael@0 | 136 | * |
michael@0 | 137 | * Currently uses a linear search, but could use a binary search. |
michael@0 | 138 | */ |
michael@0 | 139 | const T& GetAt(Time aTime, Time* aEnd = nullptr, Time* aStart = nullptr) const |
michael@0 | 140 | { |
michael@0 | 141 | if (mChanges.IsEmpty() || aTime < mChanges[0].mTime) { |
michael@0 | 142 | if (aStart) { |
michael@0 | 143 | *aStart = INT64_MIN; |
michael@0 | 144 | } |
michael@0 | 145 | if (aEnd) { |
michael@0 | 146 | *aEnd = mChanges.IsEmpty() ? INT64_MAX : mChanges[0].mTime; |
michael@0 | 147 | } |
michael@0 | 148 | return mCurrent; |
michael@0 | 149 | } |
michael@0 | 150 | int32_t changesLength = mChanges.Length(); |
michael@0 | 151 | if (mChanges[changesLength - 1].mTime <= aTime) { |
michael@0 | 152 | if (aEnd) { |
michael@0 | 153 | *aEnd = INT64_MAX; |
michael@0 | 154 | } |
michael@0 | 155 | if (aStart) { |
michael@0 | 156 | *aStart = mChanges[changesLength - 1].mTime; |
michael@0 | 157 | } |
michael@0 | 158 | return mChanges[changesLength - 1].mValue; |
michael@0 | 159 | } |
michael@0 | 160 | |
michael@0 | 161 | for (uint32_t i = 1; ; ++i) { |
michael@0 | 162 | if (aTime < mChanges[i].mTime) { |
michael@0 | 163 | if (aEnd) { |
michael@0 | 164 | *aEnd = mChanges[i].mTime; |
michael@0 | 165 | } |
michael@0 | 166 | if (aStart) { |
michael@0 | 167 | *aStart = mChanges[i - 1].mTime; |
michael@0 | 168 | } |
michael@0 | 169 | NS_ASSERTION(mChanges[i].mValue != mChanges[i - 1].mValue, |
michael@0 | 170 | "Only changed values appear in array"); |
michael@0 | 171 | return mChanges[i - 1].mValue; |
michael@0 | 172 | } |
michael@0 | 173 | } |
michael@0 | 174 | } |
michael@0 | 175 | /** |
michael@0 | 176 | * Advance the current time to aTime. |
michael@0 | 177 | */ |
michael@0 | 178 | void AdvanceCurrentTime(Time aTime) |
michael@0 | 179 | { |
michael@0 | 180 | for (uint32_t i = 0; i < mChanges.Length(); ++i) { |
michael@0 | 181 | if (aTime < mChanges[i].mTime) { |
michael@0 | 182 | mChanges.RemoveElementsAt(0, i); |
michael@0 | 183 | return; |
michael@0 | 184 | } |
michael@0 | 185 | mCurrent = mChanges[i].mValue; |
michael@0 | 186 | } |
michael@0 | 187 | mChanges.Clear(); |
michael@0 | 188 | } |
michael@0 | 189 | /** |
michael@0 | 190 | * Make all currently pending changes happen aDelta later than their |
michael@0 | 191 | * current change times. |
michael@0 | 192 | */ |
michael@0 | 193 | void InsertTimeAtStart(Time aDelta) |
michael@0 | 194 | { |
michael@0 | 195 | for (uint32_t i = 0; i < mChanges.Length(); ++i) { |
michael@0 | 196 | mChanges[i].mTime += aDelta; |
michael@0 | 197 | } |
michael@0 | 198 | } |
michael@0 | 199 | |
michael@0 | 200 | /** |
michael@0 | 201 | * Replace the values of this function at aTimeOffset and later with the |
michael@0 | 202 | * values of aOther taken from zero, so if aOther is V at time T >= 0 |
michael@0 | 203 | * then this function will be V at time T + aTimeOffset. aOther's current |
michael@0 | 204 | * time must be >= 0. |
michael@0 | 205 | */ |
michael@0 | 206 | void Append(const TimeVarying& aOther, Time aTimeOffset) |
michael@0 | 207 | { |
michael@0 | 208 | NS_ASSERTION(aOther.mChanges.IsEmpty() || aOther.mChanges[0].mTime >= 0, |
michael@0 | 209 | "Negative time not allowed here"); |
michael@0 | 210 | NS_ASSERTION(&aOther != this, "Can't self-append"); |
michael@0 | 211 | SetAtAndAfter(aTimeOffset, aOther.mCurrent); |
michael@0 | 212 | for (uint32_t i = 0; i < aOther.mChanges.Length(); ++i) { |
michael@0 | 213 | const Entry& e = aOther.mChanges[i]; |
michael@0 | 214 | SetAtAndAfter(aTimeOffset + e.mTime, e.mValue); |
michael@0 | 215 | } |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
michael@0 | 219 | { |
michael@0 | 220 | return mChanges.SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 221 | } |
michael@0 | 222 | |
michael@0 | 223 | private: |
michael@0 | 224 | struct Entry { |
michael@0 | 225 | Entry(Time aTime, const T& aValue) : mTime(aTime), mValue(aValue) {} |
michael@0 | 226 | |
michael@0 | 227 | // The time at which the value changes to mValue |
michael@0 | 228 | Time mTime; |
michael@0 | 229 | T mValue; |
michael@0 | 230 | }; |
michael@0 | 231 | nsAutoTArray<Entry, ReservedChanges> mChanges; |
michael@0 | 232 | T mCurrent; |
michael@0 | 233 | }; |
michael@0 | 234 | |
michael@0 | 235 | } |
michael@0 | 236 | |
michael@0 | 237 | #endif /* MOZILLA_TIMEVARYING_H_ */ |