|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef mozilla_dom_indexeddb_key_h__ |
|
8 #define mozilla_dom_indexeddb_key_h__ |
|
9 |
|
10 #include "mozilla/dom/indexedDB/IndexedDatabase.h" |
|
11 |
|
12 #include "mozIStorageStatement.h" |
|
13 |
|
14 #include "js/Value.h" |
|
15 |
|
16 namespace IPC { |
|
17 template <typename T> struct ParamTraits; |
|
18 } // namespace IPC |
|
19 |
|
20 BEGIN_INDEXEDDB_NAMESPACE |
|
21 |
|
22 class Key |
|
23 { |
|
24 friend struct IPC::ParamTraits<Key>; |
|
25 |
|
26 public: |
|
27 Key() |
|
28 { |
|
29 Unset(); |
|
30 } |
|
31 |
|
32 Key& operator=(const nsAString& aString) |
|
33 { |
|
34 SetFromString(aString); |
|
35 return *this; |
|
36 } |
|
37 |
|
38 Key& operator=(int64_t aInt) |
|
39 { |
|
40 SetFromInteger(aInt); |
|
41 return *this; |
|
42 } |
|
43 |
|
44 bool operator==(const Key& aOther) const |
|
45 { |
|
46 NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), |
|
47 "Don't compare unset keys!"); |
|
48 |
|
49 return mBuffer.Equals(aOther.mBuffer); |
|
50 } |
|
51 |
|
52 bool operator!=(const Key& aOther) const |
|
53 { |
|
54 NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), |
|
55 "Don't compare unset keys!"); |
|
56 |
|
57 return !mBuffer.Equals(aOther.mBuffer); |
|
58 } |
|
59 |
|
60 bool operator<(const Key& aOther) const |
|
61 { |
|
62 NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), |
|
63 "Don't compare unset keys!"); |
|
64 |
|
65 return Compare(mBuffer, aOther.mBuffer) < 0; |
|
66 } |
|
67 |
|
68 bool operator>(const Key& aOther) const |
|
69 { |
|
70 NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), |
|
71 "Don't compare unset keys!"); |
|
72 |
|
73 return Compare(mBuffer, aOther.mBuffer) > 0; |
|
74 } |
|
75 |
|
76 bool operator<=(const Key& aOther) const |
|
77 { |
|
78 NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), |
|
79 "Don't compare unset keys!"); |
|
80 |
|
81 return Compare(mBuffer, aOther.mBuffer) <= 0; |
|
82 } |
|
83 |
|
84 bool operator>=(const Key& aOther) const |
|
85 { |
|
86 NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), |
|
87 "Don't compare unset keys!"); |
|
88 |
|
89 return Compare(mBuffer, aOther.mBuffer) >= 0; |
|
90 } |
|
91 |
|
92 void |
|
93 Unset() |
|
94 { |
|
95 mBuffer.SetIsVoid(true); |
|
96 } |
|
97 |
|
98 bool IsUnset() const |
|
99 { |
|
100 return mBuffer.IsVoid(); |
|
101 } |
|
102 |
|
103 bool IsFloat() const |
|
104 { |
|
105 return !IsUnset() && mBuffer.First() == eFloat; |
|
106 } |
|
107 |
|
108 bool IsDate() const |
|
109 { |
|
110 return !IsUnset() && mBuffer.First() == eDate; |
|
111 } |
|
112 |
|
113 bool IsString() const |
|
114 { |
|
115 return !IsUnset() && mBuffer.First() == eString; |
|
116 } |
|
117 |
|
118 bool IsArray() const |
|
119 { |
|
120 return !IsUnset() && mBuffer.First() >= eArray; |
|
121 } |
|
122 |
|
123 double ToFloat() const |
|
124 { |
|
125 NS_ASSERTION(IsFloat(), "Why'd you call this?"); |
|
126 const unsigned char* pos = BufferStart(); |
|
127 double res = DecodeNumber(pos, BufferEnd()); |
|
128 NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); |
|
129 return res; |
|
130 } |
|
131 |
|
132 double ToDateMsec() const |
|
133 { |
|
134 NS_ASSERTION(IsDate(), "Why'd you call this?"); |
|
135 const unsigned char* pos = BufferStart(); |
|
136 double res = DecodeNumber(pos, BufferEnd()); |
|
137 NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); |
|
138 return res; |
|
139 } |
|
140 |
|
141 void ToString(nsString& aString) const |
|
142 { |
|
143 NS_ASSERTION(IsString(), "Why'd you call this?"); |
|
144 const unsigned char* pos = BufferStart(); |
|
145 DecodeString(pos, BufferEnd(), aString); |
|
146 NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); |
|
147 } |
|
148 |
|
149 void SetFromString(const nsAString& aString) |
|
150 { |
|
151 mBuffer.Truncate(); |
|
152 EncodeString(aString, 0); |
|
153 TrimBuffer(); |
|
154 } |
|
155 |
|
156 void SetFromInteger(int64_t aInt) |
|
157 { |
|
158 mBuffer.Truncate(); |
|
159 EncodeNumber(double(aInt), eFloat); |
|
160 TrimBuffer(); |
|
161 } |
|
162 |
|
163 nsresult SetFromJSVal(JSContext* aCx, |
|
164 JS::Handle<JS::Value> aVal) |
|
165 { |
|
166 mBuffer.Truncate(); |
|
167 |
|
168 if (aVal.isNull() || aVal.isUndefined()) { |
|
169 Unset(); |
|
170 return NS_OK; |
|
171 } |
|
172 |
|
173 nsresult rv = EncodeJSVal(aCx, aVal, 0); |
|
174 if (NS_FAILED(rv)) { |
|
175 Unset(); |
|
176 return rv; |
|
177 } |
|
178 TrimBuffer(); |
|
179 |
|
180 return NS_OK; |
|
181 } |
|
182 |
|
183 nsresult ToJSVal(JSContext* aCx, |
|
184 JS::MutableHandle<JS::Value> aVal) const |
|
185 { |
|
186 if (IsUnset()) { |
|
187 aVal.setUndefined(); |
|
188 return NS_OK; |
|
189 } |
|
190 |
|
191 const unsigned char* pos = BufferStart(); |
|
192 nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal); |
|
193 NS_ENSURE_SUCCESS(rv, rv); |
|
194 |
|
195 NS_ASSERTION(pos >= BufferEnd(), |
|
196 "Didn't consume whole buffer"); |
|
197 |
|
198 return NS_OK; |
|
199 } |
|
200 |
|
201 nsresult ToJSVal(JSContext* aCx, |
|
202 JS::Heap<JS::Value>& aVal) const |
|
203 { |
|
204 JS::Rooted<JS::Value> value(aCx); |
|
205 nsresult rv = ToJSVal(aCx, &value); |
|
206 if (NS_SUCCEEDED(rv)) { |
|
207 aVal = value; |
|
208 } |
|
209 return rv; |
|
210 } |
|
211 |
|
212 nsresult AppendItem(JSContext* aCx, |
|
213 bool aFirstOfArray, |
|
214 JS::Handle<JS::Value> aVal) |
|
215 { |
|
216 nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); |
|
217 if (NS_FAILED(rv)) { |
|
218 Unset(); |
|
219 return rv; |
|
220 } |
|
221 |
|
222 return NS_OK; |
|
223 } |
|
224 |
|
225 void FinishArray() |
|
226 { |
|
227 TrimBuffer(); |
|
228 } |
|
229 |
|
230 const nsCString& GetBuffer() const |
|
231 { |
|
232 return mBuffer; |
|
233 } |
|
234 |
|
235 nsresult BindToStatement(mozIStorageStatement* aStatement, |
|
236 const nsACString& aParamName) const |
|
237 { |
|
238 nsresult rv = aStatement->BindBlobByName(aParamName, |
|
239 reinterpret_cast<const uint8_t*>(mBuffer.get()), mBuffer.Length()); |
|
240 |
|
241 return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; |
|
242 } |
|
243 |
|
244 nsresult SetFromStatement(mozIStorageStatement* aStatement, |
|
245 uint32_t aIndex) |
|
246 { |
|
247 uint8_t* data; |
|
248 uint32_t dataLength = 0; |
|
249 |
|
250 nsresult rv = aStatement->GetBlob(aIndex, &dataLength, &data); |
|
251 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); |
|
252 |
|
253 mBuffer.Adopt( |
|
254 reinterpret_cast<char*>(const_cast<uint8_t*>(data)), dataLength); |
|
255 |
|
256 return NS_OK; |
|
257 } |
|
258 |
|
259 static |
|
260 int16_t CompareKeys(Key& aFirst, Key& aSecond) |
|
261 { |
|
262 int32_t result = Compare(aFirst.mBuffer, aSecond.mBuffer); |
|
263 |
|
264 if (result < 0) { |
|
265 return -1; |
|
266 } |
|
267 |
|
268 if (result > 0) { |
|
269 return 1; |
|
270 } |
|
271 |
|
272 return 0; |
|
273 } |
|
274 |
|
275 private: |
|
276 const unsigned char* BufferStart() const |
|
277 { |
|
278 return reinterpret_cast<const unsigned char*>(mBuffer.BeginReading()); |
|
279 } |
|
280 |
|
281 const unsigned char* BufferEnd() const |
|
282 { |
|
283 return reinterpret_cast<const unsigned char*>(mBuffer.EndReading()); |
|
284 } |
|
285 |
|
286 enum { |
|
287 eTerminator = 0, |
|
288 eFloat = 1, |
|
289 eDate = 2, |
|
290 eString = 3, |
|
291 eArray = 4, |
|
292 eMaxType = eArray |
|
293 }; |
|
294 |
|
295 // Encoding helper. Trims trailing zeros off of mBuffer as a post-processing |
|
296 // step. |
|
297 void TrimBuffer() |
|
298 { |
|
299 const char* end = mBuffer.EndReading() - 1; |
|
300 while (!*end) { |
|
301 --end; |
|
302 } |
|
303 |
|
304 mBuffer.Truncate(end + 1 - mBuffer.BeginReading()); |
|
305 } |
|
306 |
|
307 // Encoding functions. These append the encoded value to the end of mBuffer |
|
308 inline nsresult EncodeJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, |
|
309 uint8_t aTypeOffset) |
|
310 { |
|
311 return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); |
|
312 } |
|
313 void EncodeString(const nsAString& aString, uint8_t aTypeOffset); |
|
314 void EncodeNumber(double aFloat, uint8_t aType); |
|
315 |
|
316 // Decoding functions. aPos points into mBuffer and is adjusted to point |
|
317 // past the consumed value. |
|
318 static inline nsresult DecodeJSVal(const unsigned char*& aPos, |
|
319 const unsigned char* aEnd, JSContext* aCx, |
|
320 uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal) |
|
321 { |
|
322 return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0); |
|
323 } |
|
324 |
|
325 static void DecodeString(const unsigned char*& aPos, |
|
326 const unsigned char* aEnd, |
|
327 nsString& aString); |
|
328 static double DecodeNumber(const unsigned char*& aPos, |
|
329 const unsigned char* aEnd); |
|
330 |
|
331 nsCString mBuffer; |
|
332 |
|
333 private: |
|
334 nsresult EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal, |
|
335 uint8_t aTypeOffset, uint16_t aRecursionDepth); |
|
336 |
|
337 static nsresult DecodeJSValInternal(const unsigned char*& aPos, |
|
338 const unsigned char* aEnd, |
|
339 JSContext* aCx, uint8_t aTypeOffset, |
|
340 JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth); |
|
341 }; |
|
342 |
|
343 END_INDEXEDDB_NAMESPACE |
|
344 |
|
345 #endif /* mozilla_dom_indexeddb_key_h__ */ |