|  | /* | 
|  | * Copyright (C) 2019 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #ifndef SRC_TRACE_PROCESSOR_DB_COMPARE_H_ | 
|  | #define SRC_TRACE_PROCESSOR_DB_COMPARE_H_ | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <optional> | 
|  |  | 
|  | #include "perfetto/ext/base/string_view.h" | 
|  | #include "perfetto/trace_processor/basic_types.h" | 
|  |  | 
|  | namespace perfetto { | 
|  | namespace trace_processor { | 
|  | namespace compare { | 
|  |  | 
|  | // This file contains the de-facto impleemntation of all comparisions used by | 
|  | // trace processor in every setting. All of this is centralised in one file to | 
|  | // ensure both internal consistency with SQLite and consistency with SQLite. | 
|  |  | 
|  | // Compare a non-null numeric to a double; returns: | 
|  | //  * <0 if i < d, | 
|  | //  * >0 if i > d. | 
|  | //  *  0 otherwise | 
|  | // This code matches the behaviour of sqlite3IntFloatCompare. | 
|  | inline int LongToDouble(int64_t i, double d) { | 
|  | // First check if we are out of range for a int64_t. We use the constants | 
|  | // directly instead of using numeric_limits as the casts introduces rounding | 
|  | // in the doubles as a double cannot exactly represent int64::max(). | 
|  | if (d >= 9223372036854775808.0) | 
|  | return -1; | 
|  | if (d < -9223372036854775808.0) | 
|  | return 1; | 
|  |  | 
|  | // Then, try to compare in int64 space to try and keep as much precision as | 
|  | // possible. | 
|  | int64_t d_i = static_cast<int64_t>(d); | 
|  | if (i < d_i) | 
|  | return -1; | 
|  | if (i > d_i) | 
|  | return 1; | 
|  |  | 
|  | // Finally, try and compare in double space, sacrificing precision if | 
|  | // necessary. | 
|  | double i_d = static_cast<double>(i); | 
|  | return (i_d < d) ? -1 : (i_d > d ? 1 : 0); | 
|  | } | 
|  |  | 
|  | // Compares two non-null numeric values; returns: | 
|  | //  * <0 if a < b, | 
|  | //  * >0 if a > b. | 
|  | //  *  0 otherwise | 
|  | // This code matches the behaviour of the inline code in the comparision path of | 
|  | // sqlite3VdbeExec (for ints) and the behaviour of sqlite3MemCompare (for | 
|  | // doubles). | 
|  | template <typename T> | 
|  | inline int Numeric(T a, T b) { | 
|  | static_assert(std::is_arithmetic<T>::value, | 
|  | "Numeric comparision performed with non-numeric type"); | 
|  | return a < b ? -1 : (a > b ? 1 : 0); | 
|  | } | 
|  |  | 
|  | // Compares two non-null bytes values; returns: | 
|  | //  * <0 if a < b, | 
|  | //  * >0 if a > b. | 
|  | //  *  0 otherwise | 
|  | // This code matches the behaviour of sqlite3BlobCompare. | 
|  | inline int Bytes(const void* a, size_t a_n, const void* b, size_t b_n) { | 
|  | int res = memcmp(a, b, std::min(a_n, b_n)); | 
|  | return res != 0 ? res | 
|  | : static_cast<int>(static_cast<int64_t>(a_n) - | 
|  | static_cast<int64_t>(b_n)); | 
|  | } | 
|  |  | 
|  | // Compares two non-null string values; returns: | 
|  | //  * <0 if a < b, | 
|  | //  * >0 if a > b. | 
|  | //  *  0 otherwise | 
|  | // This code matches the behaviour of sqlite3BlobCompare which is called when | 
|  | // there is no collation sequence defined in sqlite3MemCompare. | 
|  | inline int String(base::StringView a, base::StringView b) { | 
|  | PERFETTO_DCHECK(a.data() != nullptr); | 
|  | PERFETTO_DCHECK(b.data() != nullptr); | 
|  | return Bytes(a.data(), a.size(), b.data(), b.size()); | 
|  | } | 
|  |  | 
|  | // Compares two nullable numeric values; returns: | 
|  | //  *  0 if both a and b are null | 
|  | //  * <0 if a is null and b is non null | 
|  | //  * >0 if a is non null and b is null | 
|  | //  * <0 if a < b (a and b both non null) | 
|  | //  * >0 if a > b (a and b both non null) | 
|  | //  *  0 otherwise | 
|  | // Should only be used for defining an ordering on value of type T. For filter | 
|  | // functions, compare::Numeric should be used above after checking if the value | 
|  | // is null. | 
|  | // This method was defined from observing the behaviour of SQLite when sorting | 
|  | // on columns containing nulls. | 
|  | template <typename T> | 
|  | inline int NullableNumeric(std::optional<T> a, std::optional<T> b) { | 
|  | if (!a) | 
|  | return b ? -1 : 0; | 
|  |  | 
|  | if (!b) | 
|  | return 1; | 
|  |  | 
|  | return compare::Numeric(*a, *b); | 
|  | } | 
|  |  | 
|  | // Compares two strings, either of which can be null; returns: | 
|  | //  *  0 if both a and b are null | 
|  | //  * <0 if a is null and b is non null | 
|  | //  * >0 if a is non null and b is null | 
|  | //  * <0 if a < b (a and b both non null) | 
|  | //  * >0 if a > b (a and b both non null) | 
|  | //  *  0 otherwise | 
|  | // Should only be used for defining an ordering on value of type T. For filter | 
|  | // functions, compare::String should be used above after checking if the value | 
|  | // is null. | 
|  | // This method was defined from observing the behaviour of SQLite when sorting | 
|  | // on columns containing nulls. | 
|  | inline int NullableString(base::StringView a, base::StringView b) { | 
|  | if (!a.data()) | 
|  | return b.data() ? -1 : 0; | 
|  |  | 
|  | if (!b.data()) | 
|  | return 1; | 
|  |  | 
|  | return compare::String(a, b); | 
|  | } | 
|  |  | 
|  | // Compares two SqlValue; returns: | 
|  | //  * <0 if a.type < b.type and a and b are not both numeric | 
|  | //  * >0 if a.type > b.type and a and b are not both numeric | 
|  | //  * <0 if a < b (a and b both non null and either of same type or numeric) | 
|  | //  * >0 if a > b (a and b both non null and either of same type or numeric) | 
|  | //  *  0 otherwise | 
|  | // This code roughly matches the behaviour of the code in the comparision path | 
|  | // of sqlite3VdbeExec except we are intentionally more strict than SQLite when | 
|  | // it comes to casting between strings and numerics - we disallow moving between | 
|  | // the two. We do allow comparing between doubles and longs however as doubles | 
|  | // can easily appear by using constants in SQL even when intending to use longs. | 
|  | inline int SqlValue(const SqlValue& a, const SqlValue& b) { | 
|  | if (a.type != b.type) { | 
|  | if (a.type == SqlValue::kLong && b.type == SqlValue::kDouble) | 
|  | return compare::LongToDouble(a.long_value, b.double_value); | 
|  |  | 
|  | if (a.type == SqlValue::kDouble && b.type == SqlValue::kLong) | 
|  | return -compare::LongToDouble(b.long_value, a.double_value); | 
|  |  | 
|  | return a.type - b.type; | 
|  | } | 
|  |  | 
|  | switch (a.type) { | 
|  | case SqlValue::Type::kLong: | 
|  | return compare::Numeric(a.long_value, b.long_value); | 
|  | case SqlValue::Type::kDouble: | 
|  | return compare::Numeric(a.double_value, b.double_value); | 
|  | case SqlValue::Type::kString: | 
|  | return compare::String(a.string_value, b.string_value); | 
|  | case SqlValue::Type::kBytes: | 
|  | return compare::Bytes(a.bytes_value, a.bytes_count, b.bytes_value, | 
|  | b.bytes_count); | 
|  | case SqlValue::Type::kNull: | 
|  | return 0; | 
|  | } | 
|  | PERFETTO_FATAL("For GCC"); | 
|  | } | 
|  |  | 
|  | // Implements a comparator for SqlValues to use for std algorithms. | 
|  | // See documentation of compare::SqlValue for details of how this function | 
|  | // works. | 
|  | inline int SqlValueComparator(const trace_processor::SqlValue& a, | 
|  | const trace_processor::SqlValue& b) { | 
|  | return compare::SqlValue(a, b) < 0; | 
|  | } | 
|  |  | 
|  | }  // namespace compare | 
|  | }  // namespace trace_processor | 
|  | }  // namespace perfetto | 
|  |  | 
|  | #endif  // SRC_TRACE_PROCESSOR_DB_COMPARE_H_ |