348 lines
12 KiB
C++
348 lines
12 KiB
C++
// Copyright 2020 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/win/variant_vector.h"
|
|
|
|
#include "base/check_op.h"
|
|
#include "base/notreached.h"
|
|
#include "base/numerics/checked_math.h"
|
|
#include "base/process/memory.h"
|
|
#include "base/win/scoped_safearray.h"
|
|
#include "base/win/scoped_variant.h"
|
|
#include "third_party/abseil-cpp/absl/types/optional.h"
|
|
|
|
namespace base {
|
|
namespace win {
|
|
|
|
namespace {
|
|
|
|
// Lexicographical comparison between the contents of |vector| and |safearray|.
|
|
template <VARTYPE ElementVartype>
|
|
int CompareAgainstSafearray(const std::vector<ScopedVariant>& vector,
|
|
const ScopedSafearray& safearray,
|
|
bool ignore_case) {
|
|
absl::optional<ScopedSafearray::LockScope<ElementVartype>> lock_scope =
|
|
safearray.CreateLockScope<ElementVartype>();
|
|
// If we fail to create a lock scope, then arbitrarily treat |this| as
|
|
// greater. This should only happen when the SAFEARRAY fails to be locked,
|
|
// so we cannot compare the contents of the SAFEARRAY.
|
|
if (!lock_scope)
|
|
return 1;
|
|
|
|
// Create a temporary VARIANT which does not own its contents, and is
|
|
// populated with values from the |lock_scope| so it can be compared against.
|
|
VARIANT non_owning_temp;
|
|
V_VT(&non_owning_temp) = ElementVartype;
|
|
|
|
auto vector_iter = vector.begin();
|
|
auto scope_iter = lock_scope->begin();
|
|
for (; vector_iter != vector.end() && scope_iter != lock_scope->end();
|
|
++vector_iter, ++scope_iter) {
|
|
internal::VariantConverter<ElementVartype>::RawSet(&non_owning_temp,
|
|
*scope_iter);
|
|
int compare_result = vector_iter->Compare(non_owning_temp, ignore_case);
|
|
// If there is a difference in values, return the difference.
|
|
if (compare_result)
|
|
return compare_result;
|
|
}
|
|
// There are more elements in |vector|, so |vector| is
|
|
// greater than |safearray|.
|
|
if (vector_iter != vector.end())
|
|
return 1;
|
|
// There are more elements in |safearray|, so |vector| is
|
|
// less than |safearray|.
|
|
if (scope_iter != lock_scope->end())
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
VariantVector::VariantVector() = default;
|
|
|
|
VariantVector::VariantVector(VariantVector&& other)
|
|
: vartype_(std::exchange(other.vartype_, VT_EMPTY)),
|
|
vector_(std::move(other.vector_)) {}
|
|
|
|
VariantVector& VariantVector::operator=(VariantVector&& other) {
|
|
DCHECK_NE(this, &other);
|
|
vartype_ = std::exchange(other.vartype_, VT_EMPTY);
|
|
vector_ = std::move(other.vector_);
|
|
return *this;
|
|
}
|
|
|
|
VariantVector::~VariantVector() {
|
|
Reset();
|
|
}
|
|
|
|
bool VariantVector::operator==(const VariantVector& other) const {
|
|
return !Compare(other);
|
|
}
|
|
|
|
bool VariantVector::operator!=(const VariantVector& other) const {
|
|
return !VariantVector::operator==(other);
|
|
}
|
|
|
|
void VariantVector::Reset() {
|
|
vector_.clear();
|
|
vartype_ = VT_EMPTY;
|
|
}
|
|
|
|
VARIANT VariantVector::ReleaseAsScalarVariant() {
|
|
ScopedVariant scoped_variant;
|
|
|
|
if (!Empty()) {
|
|
DCHECK_EQ(Size(), 1U);
|
|
scoped_variant = std::move(vector_[0]);
|
|
Reset();
|
|
}
|
|
|
|
return scoped_variant.Release();
|
|
}
|
|
|
|
VARIANT VariantVector::ReleaseAsSafearrayVariant() {
|
|
ScopedVariant scoped_variant;
|
|
|
|
switch (Type()) {
|
|
case VT_EMPTY:
|
|
break;
|
|
case VT_BOOL:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_BOOL>());
|
|
break;
|
|
case VT_I1:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_I1>());
|
|
break;
|
|
case VT_UI1:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_UI1>());
|
|
break;
|
|
case VT_I2:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_I2>());
|
|
break;
|
|
case VT_UI2:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_UI2>());
|
|
break;
|
|
case VT_I4:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_I4>());
|
|
break;
|
|
case VT_UI4:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_UI4>());
|
|
break;
|
|
case VT_I8:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_I8>());
|
|
break;
|
|
case VT_UI8:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_UI8>());
|
|
break;
|
|
case VT_R4:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_R4>());
|
|
break;
|
|
case VT_R8:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_R8>());
|
|
break;
|
|
case VT_DATE:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_DATE>());
|
|
break;
|
|
case VT_BSTR:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_BSTR>());
|
|
break;
|
|
case VT_DISPATCH:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_DISPATCH>());
|
|
break;
|
|
case VT_UNKNOWN:
|
|
scoped_variant.Set(CreateAndPopulateSafearray<VT_UNKNOWN>());
|
|
break;
|
|
// The default case shouldn't be reachable, but if we added support for more
|
|
// VARTYPEs to base::win::internal::VariantConverter<> and they were
|
|
// inserted into a VariantVector then it would be possible to reach the
|
|
// default case for those new types until implemented.
|
|
//
|
|
// Because the switch is against VARTYPE (unsigned short) and not VARENUM,
|
|
// removing the default case will not result in build warnings/errors if
|
|
// there are missing cases. It is important that this uses VARTYPE rather
|
|
// than VARENUM, because in the future we may want to support complex
|
|
// VARTYPES. For example a value within VT_TYPEMASK that's joined something
|
|
// outside the typemask like VT_ARRAY or VT_BYREF.
|
|
default:
|
|
NOTREACHED();
|
|
break;
|
|
}
|
|
|
|
// CreateAndPopulateSafearray handles resetting |this| to VT_EMPTY because it
|
|
// transfers ownership of each element to the SAFEARRAY.
|
|
return scoped_variant.Release();
|
|
}
|
|
|
|
int VariantVector::Compare(const VARIANT& other, bool ignore_case) const {
|
|
// If the element variant types are different, compare against the types.
|
|
if (Type() != (V_VT(&other) & VT_TYPEMASK))
|
|
return (Type() < (V_VT(&other) & VT_TYPEMASK)) ? (-1) : 1;
|
|
|
|
// Both have an empty variant type so they are the same.
|
|
if (Type() == VT_EMPTY)
|
|
return 0;
|
|
|
|
int compare_result = 0;
|
|
if (V_ISARRAY(&other)) {
|
|
compare_result = Compare(V_ARRAY(&other), ignore_case);
|
|
} else {
|
|
compare_result = vector_[0].Compare(other, ignore_case);
|
|
// If the first element is equal to |other|, and |vector_|
|
|
// has more than one element, then |vector_| is greater.
|
|
if (!compare_result && Size() > 1)
|
|
compare_result = 1;
|
|
}
|
|
return compare_result;
|
|
}
|
|
|
|
int VariantVector::Compare(const VariantVector& other, bool ignore_case) const {
|
|
// If the element variant types are different, compare against the types.
|
|
if (Type() != other.Type())
|
|
return (Type() < other.Type()) ? (-1) : 1;
|
|
|
|
// Both have an empty variant type so they are the same.
|
|
if (Type() == VT_EMPTY)
|
|
return 0;
|
|
|
|
auto iter1 = vector_.begin();
|
|
auto iter2 = other.vector_.begin();
|
|
for (; (iter1 != vector_.end()) && (iter2 != other.vector_.end());
|
|
++iter1, ++iter2) {
|
|
int compare_result = iter1->Compare(*iter2, ignore_case);
|
|
if (compare_result)
|
|
return compare_result;
|
|
}
|
|
// There are more elements in |this|, so |this| is greater than |other|.
|
|
if (iter1 != vector_.end())
|
|
return 1;
|
|
// There are more elements in |other|, so |this| is less than |other|.
|
|
if (iter2 != other.vector_.end())
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
int VariantVector::Compare(SAFEARRAY* safearray, bool ignore_case) const {
|
|
VARTYPE safearray_vartype;
|
|
// If we fail to get the element variant type for the SAFEARRAY, then
|
|
// arbitrarily treat |this| as greater.
|
|
if (FAILED(SafeArrayGetVartype(safearray, &safearray_vartype)))
|
|
return 1;
|
|
|
|
// If the element variant types are different, compare against the types.
|
|
if (Type() != safearray_vartype)
|
|
return (Type() < safearray_vartype) ? (-1) : 1;
|
|
|
|
ScopedSafearray scoped_safearray(safearray);
|
|
int compare_result = 0;
|
|
switch (Type()) {
|
|
case VT_BOOL:
|
|
compare_result = CompareAgainstSafearray<VT_BOOL>(
|
|
vector_, scoped_safearray, ignore_case);
|
|
break;
|
|
case VT_I1:
|
|
compare_result = CompareAgainstSafearray<VT_I1>(vector_, scoped_safearray,
|
|
ignore_case);
|
|
break;
|
|
case VT_UI1:
|
|
compare_result = CompareAgainstSafearray<VT_UI1>(
|
|
vector_, scoped_safearray, ignore_case);
|
|
break;
|
|
case VT_I2:
|
|
compare_result = CompareAgainstSafearray<VT_I2>(vector_, scoped_safearray,
|
|
ignore_case);
|
|
break;
|
|
case VT_UI2:
|
|
compare_result = CompareAgainstSafearray<VT_UI2>(
|
|
vector_, scoped_safearray, ignore_case);
|
|
break;
|
|
case VT_I4:
|
|
compare_result = CompareAgainstSafearray<VT_I4>(vector_, scoped_safearray,
|
|
ignore_case);
|
|
break;
|
|
case VT_UI4:
|
|
compare_result = CompareAgainstSafearray<VT_UI4>(
|
|
vector_, scoped_safearray, ignore_case);
|
|
break;
|
|
case VT_I8:
|
|
compare_result = CompareAgainstSafearray<VT_I8>(vector_, scoped_safearray,
|
|
ignore_case);
|
|
break;
|
|
case VT_UI8:
|
|
compare_result = CompareAgainstSafearray<VT_UI8>(
|
|
vector_, scoped_safearray, ignore_case);
|
|
break;
|
|
case VT_R4:
|
|
compare_result = CompareAgainstSafearray<VT_R4>(vector_, scoped_safearray,
|
|
ignore_case);
|
|
break;
|
|
case VT_R8:
|
|
compare_result = CompareAgainstSafearray<VT_R8>(vector_, scoped_safearray,
|
|
ignore_case);
|
|
break;
|
|
case VT_DATE:
|
|
compare_result = CompareAgainstSafearray<VT_DATE>(
|
|
vector_, scoped_safearray, ignore_case);
|
|
break;
|
|
case VT_BSTR:
|
|
compare_result = CompareAgainstSafearray<VT_BSTR>(
|
|
vector_, scoped_safearray, ignore_case);
|
|
break;
|
|
case VT_DISPATCH:
|
|
compare_result = CompareAgainstSafearray<VT_DISPATCH>(
|
|
vector_, scoped_safearray, ignore_case);
|
|
break;
|
|
case VT_UNKNOWN:
|
|
compare_result = CompareAgainstSafearray<VT_UNKNOWN>(
|
|
vector_, scoped_safearray, ignore_case);
|
|
break;
|
|
// The default case shouldn't be reachable, but if we added support for more
|
|
// VARTYPEs to base::win::internal::VariantConverter<> and they were
|
|
// inserted into a VariantVector then it would be possible to reach the
|
|
// default case for those new types until implemented.
|
|
//
|
|
// Because the switch is against VARTYPE (unsigned short) and not VARENUM,
|
|
// removing the default case will not result in build warnings/errors if
|
|
// there are missing cases. It is important that this uses VARTYPE rather
|
|
// than VARENUM, because in the future we may want to support complex
|
|
// VARTYPES. For example a value within VT_TYPEMASK that's joined something
|
|
// outside the typemask like VT_ARRAY or VT_BYREF.
|
|
default:
|
|
NOTREACHED();
|
|
compare_result = 1;
|
|
break;
|
|
}
|
|
|
|
scoped_safearray.Release();
|
|
return compare_result;
|
|
}
|
|
|
|
template <VARTYPE ElementVartype>
|
|
SAFEARRAY* VariantVector::CreateAndPopulateSafearray() {
|
|
DCHECK(!Empty());
|
|
|
|
ScopedSafearray scoped_safearray(
|
|
SafeArrayCreateVector(ElementVartype, 0, checked_cast<ULONG>(Size())));
|
|
if (!scoped_safearray.Get()) {
|
|
constexpr size_t kElementSize =
|
|
sizeof(typename internal::VariantConverter<ElementVartype>::Type);
|
|
base::TerminateBecauseOutOfMemory(sizeof(SAFEARRAY) +
|
|
(Size() * kElementSize));
|
|
}
|
|
|
|
absl::optional<ScopedSafearray::LockScope<ElementVartype>> lock_scope =
|
|
scoped_safearray.CreateLockScope<ElementVartype>();
|
|
DCHECK(lock_scope);
|
|
|
|
for (size_t i = 0; i < Size(); ++i) {
|
|
VARIANT element = vector_[i].Release();
|
|
(*lock_scope)[i] =
|
|
internal::VariantConverter<ElementVartype>::RawGet(element);
|
|
}
|
|
Reset();
|
|
|
|
return scoped_safearray.Release();
|
|
}
|
|
|
|
} // namespace win
|
|
} // namespace base
|