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"
15 #define VARIANT_BASE_IID \
16 { /* 78888042-0fa3-4f7a-8b19-7996f99bf1aa */ \
17 0x78888042, 0x0fa3, 0x4f7a, { \
18 0x8b, 0x19, 0x79, 0x96, 0xf9, 0x9b, 0xf1, 0xaa \
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 ////////////////////////////////////////////////////////////////////////////////
49 class Variant_base
: public nsIVariant
, public nsIInterfaceRequestor
{
51 NS_DECL_THREADSAFE_ISUPPORTS
53 NS_DECLARE_STATIC_IID_ACCESSOR(VARIANT_BASE_IID
)
56 GetInterface(const nsIID
& aIID
, void** aResult
) override
{
57 NS_ENSURE_ARG_POINTER(aResult
);
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
);
68 return NS_NOINTERFACE
;
72 virtual ~Variant_base() = default;
75 NS_DEFINE_STATIC_IID_ACCESSOR(Variant_base
, VARIANT_BASE_IID
)
77 ////////////////////////////////////////////////////////////////////////////////
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
) {
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
{
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
{
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
{
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
{
128 typename variant_storage_traits
<DataType
, Adopting
>::StorageType
;
129 static inline nsresult
asUTF8String(const StorageType
&, nsACString
&) {
132 static inline nsresult
asString(const StorageType
&, nsAString
&) {
137 template <typename DataType
, bool Adopting
= false>
138 struct variant_array_traits
{
140 typename variant_storage_traits
<DataType
, Adopting
>::StorageType
;
141 static inline nsresult
asArray(const StorageType
&, uint16_t*, uint32_t*,
154 struct variant_traits
<bool> {
155 static inline uint16_t type() { return nsIDataType::VTYPE_BOOL
; }
158 struct variant_boolean_traits
<bool> {
159 static inline nsresult
asBool(bool aValue
, bool* _result
) {
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
178 struct variant_traits
<int64_t> {
179 static inline uint16_t type() { return nsIDataType::VTYPE_INT64
; }
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
);
191 static inline nsresult
asInt64(int64_t aValue
, int64_t* _result
) {
196 // xpcvariant just calls get double for integers...
198 struct variant_float_traits
<int64_t> {
199 static inline nsresult
asDouble(int64_t aValue
, double* _result
) {
200 *_result
= double(aValue
);
210 struct variant_traits
<double> {
211 static inline uint16_t type() { return nsIDataType::VTYPE_DOUBLE
; }
214 struct variant_float_traits
<double> {
215 static inline nsresult
asDouble(double aValue
, double* _result
) {
226 struct variant_traits
<nsString
> {
227 static inline uint16_t type() { return nsIDataType::VTYPE_ASTRING
; }
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
) {
237 static inline void destroy(const StorageType
& _outData
) {}
240 struct variant_text_traits
<nsString
> {
241 static inline nsresult
asUTF8String(const nsString
& aValue
,
242 nsACString
& _result
) {
243 CopyUTF16toUTF8(aValue
, _result
);
246 static inline nsresult
asString(const nsString
& aValue
, nsAString
& _result
) {
253 struct variant_traits
<nsCString
> {
254 static inline uint16_t type() { return nsIDataType::VTYPE_UTF8STRING
; }
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
) {
264 static inline void destroy(const StorageType
& aData
) {}
267 struct variant_text_traits
<nsCString
> {
268 static inline nsresult
asUTF8String(const nsCString
& aValue
,
269 nsACString
& _result
) {
273 static inline nsresult
asString(const nsCString
& aValue
, nsAString
& _result
) {
274 CopyUTF8toUTF16(aValue
, _result
);
283 // NOLINTBEGIN(bugprone-macro-parentheses)
285 #define SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(Type, DataType) \
287 struct variant_traits<Type[]> { \
288 static inline uint16_t type() { return nsIDataType::VTYPE_ARRAY; } \
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) { \
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) {} \
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; \
322 struct variant_array_traits<Type[], false> { \
323 static inline nsresult asArray(FallibleTArray<Type>& aData, \
324 uint16_t* _type, uint32_t* _size, \
326 /* For empty arrays, we return nullptr. */ \
327 if (aData.Length() == 0) { \
328 *_result = nullptr; \
333 /* Otherwise, we copy the array. */ \
334 *_result = moz_xmemdup(aData.Elements(), aData.Length() * sizeof(Type)); \
336 *_size = aData.Length(); \
342 struct variant_array_traits<Type[], true> { \
343 static inline nsresult asArray(std::pair<Type*, int>& aData, \
344 uint16_t* _type, uint32_t* _size, \
346 /* For empty arrays, we return nullptr. */ \
347 if (aData.second == 0) { \
348 *_result = nullptr; \
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); \
359 *_size = aData.second; \
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
);
371 struct variant_traits
<nsCString
[]> {
372 static inline uint16_t type() { return nsIDataType::VTYPE_ARRAY
; }
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
) {
382 if (!_outData
->SetCapacity(aArrayAndLength
.second
, fallible
)) {
383 MOZ_ASSERT_UNREACHABLE("Cannot allocate.");
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
) {}
398 struct variant_array_traits
<nsCString
[], false> {
399 static inline nsresult
asArray(FallibleTArray
<nsCString
>& aData
,
400 uint16_t* _type
, uint32_t* _size
,
402 // For empty arrays, we return nullptr.
403 if (aData
.Length() == 0) {
405 *_type
= nsIDataType::VTYPE_UTF8STRING
;
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
) {
420 *_type
= nsIDataType::VTYPE_UTF8STRING
;
421 *_size
= aData
.Length();
430 class NullVariant
: public Variant_base
{
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);
440 NS_IMETHOD
GetAsAString(nsAString
& _str
) override
{
441 // Return a void string.
442 _str
.SetIsVoid(true);
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
); }
456 const typename variant_storage_traits
<DataType
, Adopting
>::ConstructorType
458 variant_storage_traits
<DataType
, Adopting
>::storage_conversion(aData
,
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
,
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__