tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Variant.h (20348B)


      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/. */
      6 
      7 #ifndef mozilla_storage_Variant_h__
      8 #define mozilla_storage_Variant_h__
      9 
     10 #include "nsIInterfaceRequestor.h"
     11 #include "nsIVariant.h"
     12 #include "nsCOMPtr.h"
     13 #include "nsString.h"
     14 
     15 #define VARIANT_BASE_IID                      \
     16  {/* 78888042-0fa3-4f7a-8b19-7996f99bf1aa */ \
     17   0x78888042,                                \
     18   0x0fa3,                                    \
     19   0x4f7a,                                    \
     20   {0x8b, 0x19, 0x79, 0x96, 0xf9, 0x9b, 0xf1, 0xaa}}
     21 
     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)
     35 *
     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.
     40 *
     41 * Bug 1494102 tracks that work.
     42 */
     43 
     44 namespace mozilla::storage {
     45 
     46 ////////////////////////////////////////////////////////////////////////////////
     47 //// Base Class
     48 
     49 class Variant_base : public nsIVariant, public nsIInterfaceRequestor {
     50 public:
     51  NS_DECL_THREADSAFE_ISUPPORTS
     52  NS_DECL_NSIVARIANT
     53  NS_INLINE_DECL_STATIC_IID(VARIANT_BASE_IID)
     54 
     55  NS_IMETHOD
     56  GetInterface(const nsIID& aIID, void** aResult) override {
     57    NS_ENSURE_ARG_POINTER(aResult);
     58    *aResult = nullptr;
     59 
     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;
     66    }
     67 
     68    return NS_NOINTERFACE;
     69  }
     70 
     71 protected:
     72  virtual ~Variant_base() = default;
     73 };
     74 
     75 ////////////////////////////////////////////////////////////////////////////////
     76 //// Traits
     77 
     78 /**
     79 * Generics
     80 */
     81 
     82 template <typename DataType>
     83 struct variant_traits {
     84  static inline uint16_t type() { return nsIDataType::VTYPE_EMPTY; }
     85 };
     86 
     87 template <typename DataType, bool Adopting = false>
     88 struct variant_storage_traits {
     89  using ConstructorType = DataType;
     90  using StorageType = DataType;
     91  static inline void storage_conversion(const ConstructorType aData,
     92                                        StorageType* _storage) {
     93    *_storage = aData;
     94  }
     95 
     96  static inline void destroy(const StorageType& _storage) {}
     97 };
     98 
     99 #define NO_CONVERSION return NS_ERROR_CANNOT_CONVERT_DATA;
    100 
    101 template <typename DataType, bool Adopting = false>
    102 struct variant_boolean_traits {
    103  using StorageType =
    104      typename variant_storage_traits<DataType, Adopting>::StorageType;
    105  static inline nsresult asBool(const StorageType&, bool*) { NO_CONVERSION }
    106 };
    107 
    108 template <typename DataType, bool Adopting = false>
    109 struct variant_integer_traits {
    110  using StorageType =
    111      typename variant_storage_traits<DataType, Adopting>::StorageType;
    112  static inline nsresult asInt32(const StorageType&, int32_t*) { NO_CONVERSION }
    113  static inline nsresult asInt64(const StorageType&, int64_t*) { NO_CONVERSION }
    114 };
    115 
    116 template <typename DataType, bool Adopting = false>
    117 struct variant_float_traits {
    118  using StorageType =
    119      typename variant_storage_traits<DataType, Adopting>::StorageType;
    120  static inline nsresult asDouble(const StorageType&, double*) { NO_CONVERSION }
    121 };
    122 
    123 template <typename DataType, bool Adopting = false>
    124 struct variant_text_traits {
    125  using StorageType =
    126      typename variant_storage_traits<DataType, Adopting>::StorageType;
    127  static inline nsresult asUTF8String(const StorageType&, nsACString&) {
    128    NO_CONVERSION
    129  }
    130  static inline nsresult asString(const StorageType&, nsAString&) {
    131    NO_CONVERSION
    132  }
    133 };
    134 
    135 template <typename DataType, bool Adopting = false>
    136 struct variant_array_traits {
    137  using StorageType =
    138      typename variant_storage_traits<DataType, Adopting>::StorageType;
    139  static inline nsresult asArray(const StorageType&, uint16_t*, uint32_t*,
    140                                 void**) {
    141    NO_CONVERSION
    142  }
    143 };
    144 
    145 #undef NO_CONVERSION
    146 
    147 /**
    148 * BOOLEAN type
    149 */
    150 
    151 template <>
    152 struct variant_traits<bool> {
    153  static inline uint16_t type() { return nsIDataType::VTYPE_BOOL; }
    154 };
    155 template <>
    156 struct variant_boolean_traits<bool> {
    157  static inline nsresult asBool(bool aValue, bool* _result) {
    158    *_result = aValue;
    159    return NS_OK;
    160  }
    161 
    162  // NB: It might be worth also providing conversions to int types.
    163 
    164  // NB: It'd be nice to implement asBool conversions for 0 and 1, too.
    165  // That would let us clean up some conversions in Places, such as:
    166  // https://searchfox.org/mozilla-central/rev/0640ea80fbc8d48f8b197cd363e2535c95a15eb3/toolkit/components/places/SQLFunctions.cpp#564-565
    167  // https://searchfox.org/mozilla-central/rev/0640ea80fbc8d48f8b197cd363e2535c95a15eb3/toolkit/components/places/SQLFunctions.cpp#1057
    168  // https://searchfox.org/mozilla-central/rev/0640ea80fbc8d48f8b197cd363e2535c95a15eb3/toolkit/components/places/nsNavHistory.cpp#3189
    169 };
    170 
    171 /**
    172 * INTEGER types
    173 */
    174 
    175 template <>
    176 struct variant_traits<int64_t> {
    177  static inline uint16_t type() { return nsIDataType::VTYPE_INT64; }
    178 };
    179 template <>
    180 struct variant_integer_traits<int64_t> {
    181  static inline nsresult asInt32(int64_t aValue, int32_t* _result) {
    182    if (aValue > INT32_MAX || aValue < INT32_MIN) {
    183      return NS_ERROR_CANNOT_CONVERT_DATA;
    184    }
    185 
    186    *_result = static_cast<int32_t>(aValue);
    187    return NS_OK;
    188  }
    189  static inline nsresult asInt64(int64_t aValue, int64_t* _result) {
    190    *_result = aValue;
    191    return NS_OK;
    192  }
    193 };
    194 // xpcvariant just calls get double for integers...
    195 template <>
    196 struct variant_float_traits<int64_t> {
    197  static inline nsresult asDouble(int64_t aValue, double* _result) {
    198    *_result = double(aValue);
    199    return NS_OK;
    200  }
    201 };
    202 
    203 /**
    204 * FLOAT types
    205 */
    206 
    207 template <>
    208 struct variant_traits<double> {
    209  static inline uint16_t type() { return nsIDataType::VTYPE_DOUBLE; }
    210 };
    211 template <>
    212 struct variant_float_traits<double> {
    213  static inline nsresult asDouble(double aValue, double* _result) {
    214    *_result = aValue;
    215    return NS_OK;
    216  }
    217 };
    218 
    219 /**
    220 * TEXT types
    221 */
    222 
    223 template <>
    224 struct variant_traits<nsString> {
    225  static inline uint16_t type() { return nsIDataType::VTYPE_ASTRING; }
    226 };
    227 template <>
    228 struct variant_storage_traits<nsString> {
    229  using ConstructorType = const nsAString&;
    230  using StorageType = nsString;
    231  static inline void storage_conversion(ConstructorType aText,
    232                                        StorageType* _outData) {
    233    *_outData = aText;
    234  }
    235  static inline void destroy(const StorageType& _outData) {}
    236 };
    237 template <>
    238 struct variant_text_traits<nsString> {
    239  static inline nsresult asUTF8String(const nsString& aValue,
    240                                      nsACString& _result) {
    241    CopyUTF16toUTF8(aValue, _result);
    242    return NS_OK;
    243  }
    244  static inline nsresult asString(const nsString& aValue, nsAString& _result) {
    245    _result = aValue;
    246    return NS_OK;
    247  }
    248 };
    249 
    250 template <>
    251 struct variant_traits<nsCString> {
    252  static inline uint16_t type() { return nsIDataType::VTYPE_UTF8STRING; }
    253 };
    254 template <>
    255 struct variant_storage_traits<nsCString> {
    256  using ConstructorType = const nsACString&;
    257  using StorageType = nsCString;
    258  static inline void storage_conversion(ConstructorType aText,
    259                                        StorageType* _outData) {
    260    *_outData = aText;
    261  }
    262  static inline void destroy(const StorageType& aData) {}
    263 };
    264 template <>
    265 struct variant_text_traits<nsCString> {
    266  static inline nsresult asUTF8String(const nsCString& aValue,
    267                                      nsACString& _result) {
    268    _result = aValue;
    269    return NS_OK;
    270  }
    271  static inline nsresult asString(const nsCString& aValue, nsAString& _result) {
    272    CopyUTF8toUTF16(aValue, _result);
    273    return NS_OK;
    274  }
    275 };
    276 
    277 /**
    278 * ARRAY types
    279 */
    280 
    281 // NOLINTBEGIN(bugprone-macro-parentheses)
    282 
    283 #define SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(Type, DataType)                    \
    284  template <>                                                                  \
    285  struct variant_traits<Type[]> {                                              \
    286    static inline uint16_t type() { return nsIDataType::VTYPE_ARRAY; }         \
    287  };                                                                           \
    288                                                                               \
    289  template <>                                                                  \
    290  struct variant_storage_traits<Type[], false> {                               \
    291    using ConstructorType = std::pair<const void*, int>;                       \
    292    using StorageType = FallibleTArray<Type>;                                  \
    293    static inline void storage_conversion(ConstructorType aArrayAndLength,     \
    294                                          StorageType* _outData) {             \
    295      _outData->Clear();                                                       \
    296      MOZ_ALWAYS_TRUE(_outData->AppendElements(                                \
    297          static_cast<const Type*>(aArrayAndLength.first),                     \
    298          aArrayAndLength.second, fallible));                                  \
    299    }                                                                          \
    300    static inline void destroy(const StorageType& _outData) {}                 \
    301  };                                                                           \
    302                                                                               \
    303  template <>                                                                  \
    304  struct variant_storage_traits<Type[], true> {                                \
    305    using ConstructorType = std::pair<Type*, int>;                             \
    306    using StorageType = std::pair<Type*, int>;                                 \
    307    static inline void storage_conversion(ConstructorType aArrayAndLength,     \
    308                                          StorageType* _outData) {             \
    309      *_outData = aArrayAndLength;                                             \
    310    }                                                                          \
    311    static inline void destroy(StorageType& aArrayAndLength) {                 \
    312      if (aArrayAndLength.first) {                                             \
    313        free(aArrayAndLength.first);                                           \
    314        aArrayAndLength.first = nullptr;                                       \
    315      }                                                                        \
    316    }                                                                          \
    317  };                                                                           \
    318                                                                               \
    319  template <>                                                                  \
    320  struct variant_array_traits<Type[], false> {                                 \
    321    static inline nsresult asArray(FallibleTArray<Type>& aData,                \
    322                                   uint16_t* _type, uint32_t* _size,           \
    323                                   void** _result) {                           \
    324      /* For empty arrays, we return nullptr. */                               \
    325      if (aData.Length() == 0) {                                               \
    326        *_result = nullptr;                                                    \
    327        *_type = DataType;                                                     \
    328        *_size = 0;                                                            \
    329        return NS_OK;                                                          \
    330      }                                                                        \
    331      /* Otherwise, we copy the array. */                                      \
    332      *_result = moz_xmemdup(aData.Elements(), aData.Length() * sizeof(Type)); \
    333      *_type = DataType;                                                       \
    334      *_size = aData.Length();                                                 \
    335      return NS_OK;                                                            \
    336    }                                                                          \
    337  };                                                                           \
    338                                                                               \
    339  template <>                                                                  \
    340  struct variant_array_traits<Type[], true> {                                  \
    341    static inline nsresult asArray(std::pair<Type*, int>& aData,               \
    342                                   uint16_t* _type, uint32_t* _size,           \
    343                                   void** _result) {                           \
    344      /* For empty arrays, we return nullptr. */                               \
    345      if (aData.second == 0) {                                                 \
    346        *_result = nullptr;                                                    \
    347        *_type = DataType;                                                     \
    348        *_size = 0;                                                            \
    349        return NS_OK;                                                          \
    350      }                                                                        \
    351      /* Otherwise, transfer the data out. */                                  \
    352      *_result = aData.first;                                                  \
    353      aData.first = nullptr;                                                   \
    354      /* If we asked for it twice, better not use adopting! */                 \
    355      MOZ_ASSERT(*_result);                                                    \
    356      *_type = DataType;                                                       \
    357      *_size = aData.second;                                                   \
    358      return NS_OK;                                                            \
    359    }                                                                          \
    360  }
    361 
    362 // NOLINTEND(bugprone-macro-parentheses)
    363 
    364 SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(uint8_t, nsIDataType::VTYPE_UINT8);
    365 SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(int64_t, nsIDataType::VTYPE_INT64);
    366 SPECIALIZE_ARRAY_TO_NUMERIC_VARIANT(double, nsIDataType::VTYPE_DOUBLE);
    367 
    368 template <>
    369 struct variant_traits<nsCString[]> {
    370  static inline uint16_t type() { return nsIDataType::VTYPE_ARRAY; }
    371 };
    372 
    373 template <>
    374 struct variant_storage_traits<nsCString[], false> {
    375  using ConstructorType = std::pair<const void*, int>;
    376  using StorageType = FallibleTArray<nsCString>;
    377  static inline void storage_conversion(ConstructorType aArrayAndLength,
    378                                        StorageType* _outData) {
    379    _outData->Clear();
    380    if (!_outData->SetCapacity(aArrayAndLength.second, fallible)) {
    381      MOZ_ASSERT_UNREACHABLE("Cannot allocate.");
    382      return;
    383    }
    384    // We can avoid copying the strings as we're asking SQLite to do it on bind
    385    // by using SQLITE_TRANSIENT.
    386    const nsCString* str = static_cast<const nsCString*>(aArrayAndLength.first);
    387    for (int32_t i = 0; i < aArrayAndLength.second; ++i, str++) {
    388      MOZ_ALWAYS_TRUE(_outData->AppendElement(*str, fallible));
    389    }
    390  }
    391 
    392  static inline void destroy(const StorageType& _outData) {}
    393 };
    394 
    395 template <>
    396 struct variant_array_traits<nsCString[], false> {
    397  static inline nsresult asArray(FallibleTArray<nsCString>& aData,
    398                                 uint16_t* _type, uint32_t* _size,
    399                                 void** _result) {
    400    // For empty arrays, we return nullptr.
    401    if (aData.Length() == 0) {
    402      *_result = nullptr;
    403      *_type = nsIDataType::VTYPE_UTF8STRING;
    404      *_size = 0;
    405      return NS_OK;
    406    }
    407    // Otherwise, we copy the array.
    408    // This memory will be freed up after Sqlite made its own copy in
    409    // sqlite3_T_array. The string buffers are owned by mData.
    410    const char** strings =
    411        (const char**)moz_xmalloc(sizeof(char*) * aData.Length());
    412    const char** iter = strings;
    413    for (const nsCString& str : aData) {
    414      *iter = str.get();
    415      iter++;
    416    }
    417    *_result = strings;
    418    *_type = nsIDataType::VTYPE_UTF8STRING;
    419    *_size = aData.Length();
    420    return NS_OK;
    421  }
    422 };
    423 
    424 /**
    425 * nullptr type
    426 */
    427 
    428 class NullVariant : public Variant_base {
    429 public:
    430  uint16_t GetDataType() override { return nsIDataType::VTYPE_EMPTY; }
    431 
    432  NS_IMETHOD GetAsAUTF8String(nsACString& _str) override {
    433    // Return a void string.
    434    _str.SetIsVoid(true);
    435    return NS_OK;
    436  }
    437 
    438  NS_IMETHOD GetAsAString(nsAString& _str) override {
    439    // Return a void string.
    440    _str.SetIsVoid(true);
    441    return NS_OK;
    442  }
    443 };
    444 
    445 ////////////////////////////////////////////////////////////////////////////////
    446 //// Template Implementation
    447 
    448 template <typename DataType, bool Adopting = false>
    449 class Variant final : public Variant_base {
    450  ~Variant() { variant_storage_traits<DataType, Adopting>::destroy(mData); }
    451 
    452 public:
    453  explicit Variant(
    454      const typename variant_storage_traits<DataType, Adopting>::ConstructorType
    455          aData) {
    456    variant_storage_traits<DataType, Adopting>::storage_conversion(aData,
    457                                                                   &mData);
    458  }
    459 
    460  uint16_t GetDataType() override { return variant_traits<DataType>::type(); }
    461 
    462  NS_IMETHOD GetAsBool(bool* _boolean) override {
    463    return variant_boolean_traits<DataType, Adopting>::asBool(mData, _boolean);
    464  }
    465 
    466  NS_IMETHOD GetAsInt32(int32_t* _integer) override {
    467    return variant_integer_traits<DataType, Adopting>::asInt32(mData, _integer);
    468  }
    469 
    470  NS_IMETHOD GetAsInt64(int64_t* _integer) override {
    471    return variant_integer_traits<DataType, Adopting>::asInt64(mData, _integer);
    472  }
    473 
    474  NS_IMETHOD GetAsDouble(double* _double) override {
    475    return variant_float_traits<DataType, Adopting>::asDouble(mData, _double);
    476  }
    477 
    478  NS_IMETHOD GetAsAUTF8String(nsACString& _str) override {
    479    return variant_text_traits<DataType, Adopting>::asUTF8String(mData, _str);
    480  }
    481 
    482  NS_IMETHOD GetAsAString(nsAString& _str) override {
    483    return variant_text_traits<DataType, Adopting>::asString(mData, _str);
    484  }
    485 
    486  NS_IMETHOD GetAsArray(uint16_t* _type, nsIID*, uint32_t* _size,
    487                        void** _data) override {
    488    return variant_array_traits<DataType, Adopting>::asArray(mData, _type,
    489                                                             _size, _data);
    490  }
    491 
    492 protected:
    493  typename variant_storage_traits<DataType, Adopting>::StorageType mData;
    494 };
    495 
    496 ////////////////////////////////////////////////////////////////////////////////
    497 //// Handy typedefs!  Use these for the right mapping.
    498 
    499 // Currently, BooleanVariant is only useful for kvstore.
    500 // Bug 1494102 tracks implementing full boolean variant support for mozStorage.
    501 using BooleanVariant = Variant<bool>;
    502 
    503 using IntegerVariant = Variant<int64_t>;
    504 using FloatVariant = Variant<double>;
    505 using TextVariant = Variant<nsString>;
    506 using UTF8TextVariant = Variant<nsCString>;
    507 using BlobVariant = Variant<uint8_t[], false>;
    508 using AdoptedBlobVariant = Variant<uint8_t[], true>;
    509 using ArrayOfIntegersVariant = Variant<int64_t[], false>;
    510 using AdoptedArrayOfIntegersVariant = Variant<int64_t[], true>;
    511 using ArrayOfDoublesVariant = Variant<double[], false>;
    512 using AdoptedArrayOfDoublesVariant = Variant<double[], true>;
    513 using ArrayOfUTF8StringsVariant = Variant<nsCString[], false>;
    514 
    515 }  // namespace mozilla::storage
    516 
    517 #include "Variant_inl.h"
    518 
    519 #endif  // mozilla_storage_Variant_h__