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