Backed out changeset b462e7b742d8 (bug 1908261) for causing multiple reftest failures...
[gecko.git] / storage / Variant.h
blob23ae7f4db271800e477103bd9c1a80ecac281bd3
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
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/. */
7 #ifndef mozilla_storage_Variant_h__
8 #define mozilla_storage_Variant_h__
10 #include "nsIInterfaceRequestor.h"
11 #include "nsIVariant.h"
12 #include "nsCOMPtr.h"
13 #include "nsString.h"
15 #define VARIANT_BASE_IID \
16 { /* 78888042-0fa3-4f7a-8b19-7996f99bf1aa */ \
17 0x78888042, 0x0fa3, 0x4f7a, { \
18 0x8b, 0x19, 0x79, 0x96, 0xf9, 0x9b, 0xf1, 0xaa \
19 } \
22 /**
23 * This class is used by the storage module whenever an nsIVariant needs to be
24 * returned. We provide traits for the basic sqlite types to make use easier.
25 * The following types map to the indicated sqlite type:
26 * int64_t -> INTEGER (use IntegerVariant)
27 * double -> FLOAT (use FloatVariant)
28 * nsString -> TEXT (use TextVariant)
29 * nsCString -> TEXT (use UTF8TextVariant)
30 * uint8_t[] -> BLOB (use BlobVariant)
31 * nullptr -> NULL (use NullVariant)
32 * int64_t[] -> ARRAY (use ArrayOfIntegersVariant)
33 * double[] -> ARRAY (use ArrayOfDoublesVariant)
34 * nsCString[] -> ARRAY (use ArrayOfUTF8StringsVariant)
36 * The kvstore component also reuses this class as a common implementation
37 * of a simple threadsafe variant for the storage of primitive values only.
38 * The BooleanVariant type has been introduced for kvstore use cases and should
39 * be enhanced to provide full boolean variant support for mozStorage.
41 * Bug 1494102 tracks that work.
44 namespace mozilla::storage {
46 ////////////////////////////////////////////////////////////////////////////////
47 //// Base Class
49 class Variant_base : public nsIVariant, public nsIInterfaceRequestor {
50 public:
51 NS_DECL_THREADSAFE_ISUPPORTS
52 NS_DECL_NSIVARIANT
53 NS_DECLARE_STATIC_IID_ACCESSOR(VARIANT_BASE_IID)
55 NS_IMETHOD
56 GetInterface(const nsIID& aIID, void** aResult) override {
57 NS_ENSURE_ARG_POINTER(aResult);
58 *aResult = nullptr;
60 // This is used to recognize nsIVariant instances derived from Variant_base
61 // from other implementations like XPCVariant that may not be thread-safe.
62 if (aIID.Equals(VARIANT_BASE_IID) || aIID.Equals(NS_GET_IID(nsIVariant))) {
63 nsCOMPtr<nsIVariant> result(static_cast<nsIVariant*>(this));
64 result.forget(aResult);
65 return NS_OK;
68 return NS_NOINTERFACE;
71 protected:
72 virtual ~Variant_base() = default;
75 NS_DEFINE_STATIC_IID_ACCESSOR(Variant_base, VARIANT_BASE_IID)
77 ////////////////////////////////////////////////////////////////////////////////
78 //// Traits
80 /**
81 * Generics
84 template <typename DataType>
85 struct variant_traits {
86 static inline uint16_t type() { return nsIDataType::VTYPE_EMPTY; }
89 template <typename DataType, bool Adopting = false>
90 struct variant_storage_traits {
91 using ConstructorType = DataType;
92 using StorageType = DataType;
93 static inline void storage_conversion(const ConstructorType aData,
94 StorageType* _storage) {
95 *_storage = aData;
98 static inline void destroy(const StorageType& _storage) {}
101 #define NO_CONVERSION return NS_ERROR_CANNOT_CONVERT_DATA;
103 template <typename DataType, bool Adopting = false>
104 struct variant_boolean_traits {
105 using StorageType =
106 typename variant_storage_traits<DataType, Adopting>::StorageType;
107 static inline nsresult asBool(const StorageType&, bool*) { NO_CONVERSION }
110 template <typename DataType, bool Adopting = false>
111 struct variant_integer_traits {
112 using StorageType =
113 typename variant_storage_traits<DataType, Adopting>::StorageType;
114 static inline nsresult asInt32(const StorageType&, int32_t*) { NO_CONVERSION }
115 static inline nsresult asInt64(const StorageType&, int64_t*) { NO_CONVERSION }
118 template <typename DataType, bool Adopting = false>
119 struct variant_float_traits {
120 using StorageType =
121 typename variant_storage_traits<DataType, Adopting>::StorageType;
122 static inline nsresult asDouble(const StorageType&, double*) { NO_CONVERSION }
125 template <typename DataType, bool Adopting = false>
126 struct variant_text_traits {
127 using StorageType =
128 typename variant_storage_traits<DataType, Adopting>::StorageType;
129 static inline nsresult asUTF8String(const StorageType&, nsACString&) {
130 NO_CONVERSION
132 static inline nsresult asString(const StorageType&, nsAString&) {
133 NO_CONVERSION
137 template <typename DataType, bool Adopting = false>
138 struct variant_array_traits {
139 using StorageType =
140 typename variant_storage_traits<DataType, Adopting>::StorageType;
141 static inline nsresult asArray(const StorageType&, uint16_t*, uint32_t*,
142 void**) {
143 NO_CONVERSION
147 #undef NO_CONVERSION
150 * BOOLEAN type
153 template <>
154 struct variant_traits<bool> {
155 static inline uint16_t type() { return nsIDataType::VTYPE_BOOL; }
157 template <>
158 struct variant_boolean_traits<bool> {
159 static inline nsresult asBool(bool aValue, bool* _result) {
160 *_result = aValue;
161 return NS_OK;
164 // NB: It might be worth also providing conversions to int types.
166 // NB: It'd be nice to implement asBool conversions for 0 and 1, too.
167 // That would let us clean up some conversions in Places, such as:
168 // https://searchfox.org/mozilla-central/rev/0640ea80fbc8d48f8b197cd363e2535c95a15eb3/toolkit/components/places/SQLFunctions.cpp#564-565
169 // https://searchfox.org/mozilla-central/rev/0640ea80fbc8d48f8b197cd363e2535c95a15eb3/toolkit/components/places/SQLFunctions.cpp#1057
170 // https://searchfox.org/mozilla-central/rev/0640ea80fbc8d48f8b197cd363e2535c95a15eb3/toolkit/components/places/nsNavHistory.cpp#3189
174 * INTEGER types
177 template <>
178 struct variant_traits<int64_t> {
179 static inline uint16_t type() { return nsIDataType::VTYPE_INT64; }
181 template <>
182 struct variant_integer_traits<int64_t> {
183 static inline nsresult asInt32(int64_t aValue, int32_t* _result) {
184 if (aValue > INT32_MAX || aValue < INT32_MIN) {
185 return NS_ERROR_CANNOT_CONVERT_DATA;
188 *_result = static_cast<int32_t>(aValue);
189 return NS_OK;
191 static inline nsresult asInt64(int64_t aValue, int64_t* _result) {
192 *_result = aValue;
193 return NS_OK;
196 // xpcvariant just calls get double for integers...
197 template <>
198 struct variant_float_traits<int64_t> {
199 static inline nsresult asDouble(int64_t aValue, double* _result) {
200 *_result = double(aValue);
201 return NS_OK;
206 * FLOAT types
209 template <>
210 struct variant_traits<double> {
211 static inline uint16_t type() { return nsIDataType::VTYPE_DOUBLE; }
213 template <>
214 struct variant_float_traits<double> {
215 static inline nsresult asDouble(double aValue, double* _result) {
216 *_result = aValue;
217 return NS_OK;
222 * TEXT types
225 template <>
226 struct variant_traits<nsString> {
227 static inline uint16_t type() { return nsIDataType::VTYPE_ASTRING; }
229 template <>
230 struct variant_storage_traits<nsString> {
231 using ConstructorType = const nsAString&;
232 using StorageType = nsString;
233 static inline void storage_conversion(ConstructorType aText,
234 StorageType* _outData) {
235 *_outData = aText;
237 static inline void destroy(const StorageType& _outData) {}
239 template <>
240 struct variant_text_traits<nsString> {
241 static inline nsresult asUTF8String(const nsString& aValue,
242 nsACString& _result) {
243 CopyUTF16toUTF8(aValue, _result);
244 return NS_OK;
246 static inline nsresult asString(const nsString& aValue, nsAString& _result) {
247 _result = aValue;
248 return NS_OK;
252 template <>
253 struct variant_traits<nsCString> {
254 static inline uint16_t type() { return nsIDataType::VTYPE_UTF8STRING; }
256 template <>
257 struct variant_storage_traits<nsCString> {
258 using ConstructorType = const nsACString&;
259 using StorageType = nsCString;
260 static inline void storage_conversion(ConstructorType aText,
261 StorageType* _outData) {
262 *_outData = aText;
264 static inline void destroy(const StorageType& aData) {}
266 template <>
267 struct variant_text_traits<nsCString> {
268 static inline nsresult asUTF8String(const nsCString& aValue,
269 nsACString& _result) {
270 _result = aValue;
271 return NS_OK;
273 static inline nsresult asString(const nsCString& aValue, nsAString& _result) {
274 CopyUTF8toUTF16(aValue, _result);
275 return NS_OK;
280 * ARRAY types
283 // NOLINTBEGIN(bugprone-macro-parentheses)
285 #define SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(Type, DataType) \
286 template <> \
287 struct variant_traits<Type[]> { \
288 static inline uint16_t type() { return nsIDataType::VTYPE_ARRAY; } \
289 }; \
291 template <> \
292 struct variant_storage_traits<Type[], false> { \
293 using ConstructorType = std::pair<const void*, int>; \
294 using StorageType = FallibleTArray<Type>; \
295 static inline void storage_conversion(ConstructorType aArrayAndLength, \
296 StorageType* _outData) { \
297 _outData->Clear(); \
298 MOZ_ALWAYS_TRUE(_outData->AppendElements( \
299 static_cast<const Type*>(aArrayAndLength.first), \
300 aArrayAndLength.second, fallible)); \
302 static inline void destroy(const StorageType& _outData) {} \
303 }; \
305 template <> \
306 struct variant_storage_traits<Type[], true> { \
307 using ConstructorType = std::pair<Type*, int>; \
308 using StorageType = std::pair<Type*, int>; \
309 static inline void storage_conversion(ConstructorType aArrayAndLength, \
310 StorageType* _outData) { \
311 *_outData = aArrayAndLength; \
313 static inline void destroy(StorageType& aArrayAndLength) { \
314 if (aArrayAndLength.first) { \
315 free(aArrayAndLength.first); \
316 aArrayAndLength.first = nullptr; \
319 }; \
321 template <> \
322 struct variant_array_traits<Type[], false> { \
323 static inline nsresult asArray(FallibleTArray<Type>& aData, \
324 uint16_t* _type, uint32_t* _size, \
325 void** _result) { \
326 /* For empty arrays, we return nullptr. */ \
327 if (aData.Length() == 0) { \
328 *_result = nullptr; \
329 *_type = DataType; \
330 *_size = 0; \
331 return NS_OK; \
333 /* Otherwise, we copy the array. */ \
334 *_result = moz_xmemdup(aData.Elements(), aData.Length() * sizeof(Type)); \
335 *_type = DataType; \
336 *_size = aData.Length(); \
337 return NS_OK; \
339 }; \
341 template <> \
342 struct variant_array_traits<Type[], true> { \
343 static inline nsresult asArray(std::pair<Type*, int>& aData, \
344 uint16_t* _type, uint32_t* _size, \
345 void** _result) { \
346 /* For empty arrays, we return nullptr. */ \
347 if (aData.second == 0) { \
348 *_result = nullptr; \
349 *_type = DataType; \
350 *_size = 0; \
351 return NS_OK; \
353 /* Otherwise, transfer the data out. */ \
354 *_result = aData.first; \
355 aData.first = nullptr; \
356 /* If we asked for it twice, better not use adopting! */ \
357 MOZ_ASSERT(*_result); \
358 *_type = DataType; \
359 *_size = aData.second; \
360 return NS_OK; \
364 // NOLINTEND(bugprone-macro-parentheses)
366 SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(uint8_t, nsIDataType::VTYPE_UINT8);
367 SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(int64_t, nsIDataType::VTYPE_INT64);
368 SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(double, nsIDataType::VTYPE_DOUBLE);
370 template <>
371 struct variant_traits<nsCString[]> {
372 static inline uint16_t type() { return nsIDataType::VTYPE_ARRAY; }
375 template <>
376 struct variant_storage_traits<nsCString[], false> {
377 using ConstructorType = std::pair<const void*, int>;
378 using StorageType = FallibleTArray<nsCString>;
379 static inline void storage_conversion(ConstructorType aArrayAndLength,
380 StorageType* _outData) {
381 _outData->Clear();
382 if (!_outData->SetCapacity(aArrayAndLength.second, fallible)) {
383 MOZ_ASSERT_UNREACHABLE("Cannot allocate.");
384 return;
386 // We can avoid copying the strings as we're asking SQLite to do it on bind
387 // by using SQLITE_TRANSIENT.
388 const nsCString* str = static_cast<const nsCString*>(aArrayAndLength.first);
389 for (int32_t i = 0; i < aArrayAndLength.second; ++i, str++) {
390 MOZ_ALWAYS_TRUE(_outData->AppendElement(*str, fallible));
394 static inline void destroy(const StorageType& _outData) {}
397 template <>
398 struct variant_array_traits<nsCString[], false> {
399 static inline nsresult asArray(FallibleTArray<nsCString>& aData,
400 uint16_t* _type, uint32_t* _size,
401 void** _result) {
402 // For empty arrays, we return nullptr.
403 if (aData.Length() == 0) {
404 *_result = nullptr;
405 *_type = nsIDataType::VTYPE_UTF8STRING;
406 *_size = 0;
407 return NS_OK;
409 // Otherwise, we copy the array.
410 // This memory will be freed up after Sqlite made its own copy in
411 // sqlite3_T_array. The string buffers are owned by mData.
412 const char** strings =
413 (const char**)moz_xmalloc(sizeof(char*) * aData.Length());
414 const char** iter = strings;
415 for (const nsCString& str : aData) {
416 *iter = str.get();
417 iter++;
419 *_result = strings;
420 *_type = nsIDataType::VTYPE_UTF8STRING;
421 *_size = aData.Length();
422 return NS_OK;
427 * nullptr type
430 class NullVariant : public Variant_base {
431 public:
432 uint16_t GetDataType() override { return nsIDataType::VTYPE_EMPTY; }
434 NS_IMETHOD GetAsAUTF8String(nsACString& _str) override {
435 // Return a void string.
436 _str.SetIsVoid(true);
437 return NS_OK;
440 NS_IMETHOD GetAsAString(nsAString& _str) override {
441 // Return a void string.
442 _str.SetIsVoid(true);
443 return NS_OK;
447 ////////////////////////////////////////////////////////////////////////////////
448 //// Template Implementation
450 template <typename DataType, bool Adopting = false>
451 class Variant final : public Variant_base {
452 ~Variant() { variant_storage_traits<DataType, Adopting>::destroy(mData); }
454 public:
455 explicit Variant(
456 const typename variant_storage_traits<DataType, Adopting>::ConstructorType
457 aData) {
458 variant_storage_traits<DataType, Adopting>::storage_conversion(aData,
459 &mData);
462 uint16_t GetDataType() override { return variant_traits<DataType>::type(); }
464 NS_IMETHOD GetAsBool(bool* _boolean) override {
465 return variant_boolean_traits<DataType, Adopting>::asBool(mData, _boolean);
468 NS_IMETHOD GetAsInt32(int32_t* _integer) override {
469 return variant_integer_traits<DataType, Adopting>::asInt32(mData, _integer);
472 NS_IMETHOD GetAsInt64(int64_t* _integer) override {
473 return variant_integer_traits<DataType, Adopting>::asInt64(mData, _integer);
476 NS_IMETHOD GetAsDouble(double* _double) override {
477 return variant_float_traits<DataType, Adopting>::asDouble(mData, _double);
480 NS_IMETHOD GetAsAUTF8String(nsACString& _str) override {
481 return variant_text_traits<DataType, Adopting>::asUTF8String(mData, _str);
484 NS_IMETHOD GetAsAString(nsAString& _str) override {
485 return variant_text_traits<DataType, Adopting>::asString(mData, _str);
488 NS_IMETHOD GetAsArray(uint16_t* _type, nsIID*, uint32_t* _size,
489 void** _data) override {
490 return variant_array_traits<DataType, Adopting>::asArray(mData, _type,
491 _size, _data);
494 protected:
495 typename variant_storage_traits<DataType, Adopting>::StorageType mData;
498 ////////////////////////////////////////////////////////////////////////////////
499 //// Handy typedefs! Use these for the right mapping.
501 // Currently, BooleanVariant is only useful for kvstore.
502 // Bug 1494102 tracks implementing full boolean variant support for mozStorage.
503 using BooleanVariant = Variant<bool>;
505 using IntegerVariant = Variant<int64_t>;
506 using FloatVariant = Variant<double>;
507 using TextVariant = Variant<nsString>;
508 using UTF8TextVariant = Variant<nsCString>;
509 using BlobVariant = Variant<uint8_t[], false>;
510 using AdoptedBlobVariant = Variant<uint8_t[], true>;
511 using ArrayOfIntegersVariant = Variant<int64_t[], false>;
512 using AdoptedArrayOfIntegersVariant = Variant<int64_t[], true>;
513 using ArrayOfDoublesVariant = Variant<double[], false>;
514 using AdoptedArrayOfDoublesVariant = Variant<double[], true>;
515 using ArrayOfUTF8StringsVariant = Variant<nsCString[], false>;
517 } // namespace mozilla::storage
519 #include "Variant_inl.h"
521 #endif // mozilla_storage_Variant_h__