// 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.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
#include "base/win/variant_vector.h"
#include <optional>
#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"
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) {
std::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();
}
// 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();
}
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));
}
std::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