|
| 1 | +/* |
| 2 | + * Copyright (C) 2026 The Android Open Source Project |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | + * you may not use this file except in compliance with the License. |
| 6 | + * You may obtain a copy of the License at |
| 7 | + * |
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | + * |
| 10 | + * Unless required by applicable law or agreed to in writing, software |
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | + * See the License for the specific language governing permissions and |
| 14 | + * limitations under the License. |
| 15 | + */ |
| 16 | + |
| 17 | +#include "src/trace_processor/core/exec/assert_type.h" |
| 18 | + |
| 19 | +#include <cmath> |
| 20 | +#include <cstdint> |
| 21 | +#include <memory> |
| 22 | +#include <string> |
| 23 | +#include <utility> |
| 24 | + |
| 25 | +#include "perfetto/base/status.h" |
| 26 | +#include "src/trace_processor/containers/string_pool.h" |
| 27 | +#include "src/trace_processor/core/common/storage_types.h" |
| 28 | +#include "src/trace_processor/core/exec/column_view.h" |
| 29 | +#include "src/trace_processor/core/exec/operator.h" |
| 30 | +#include "src/trace_processor/core/exec/row_batch.h" |
| 31 | +#include "src/trace_processor/core/exec/row_selection.h" |
| 32 | +#include "src/trace_processor/core/exec/variant.h" |
| 33 | +#include "src/trace_processor/core/util/flex_vector.h" |
| 34 | + |
| 35 | +namespace perfetto::trace_processor::core::exec { |
| 36 | +namespace { |
| 37 | + |
| 38 | +const char* Name(Variant::Type type) { |
| 39 | + switch (type) { |
| 40 | + case Variant::Type::kNull: |
| 41 | + return "a null"; |
| 42 | + case Variant::Type::kInt64: |
| 43 | + return "an integer"; |
| 44 | + case Variant::Type::kDouble: |
| 45 | + return "a float"; |
| 46 | + case Variant::Type::kString: |
| 47 | + return "a string"; |
| 48 | + } |
| 49 | + return "something"; |
| 50 | +} |
| 51 | + |
| 52 | +const char* Name(StorageType type) { |
| 53 | + if (type.Is<Int64>()) { |
| 54 | + return "an integer"; |
| 55 | + } |
| 56 | + return type.Is<Double>() ? "a float" : "a string"; |
| 57 | +} |
| 58 | + |
| 59 | +// `resolve` is a template parameter so the loop carries no per-row branch on |
| 60 | +// how a row is reached. |
| 61 | +template <typename Resolve, typename Write> |
| 62 | +bool Walk(Resolve resolve, uint32_t count, Write write) { |
| 63 | + for (uint32_t i = 0; i < count; ++i) { |
| 64 | + if (!write(i, resolve(i))) { |
| 65 | + return false; |
| 66 | + } |
| 67 | + } |
| 68 | + return true; |
| 69 | +} |
| 70 | + |
| 71 | +// Reads `count` values of a narrower integer column into `values.ints`. |
| 72 | +template <typename T> |
| 73 | +void WidenAs(const ColumnView& column, |
| 74 | + uint32_t count, |
| 75 | + FlexVector<int64_t>& out) { |
| 76 | + const auto* data = static_cast<const T*>(column.data()); |
| 77 | + RowSelection selection = column.selection(); |
| 78 | + int64_t* dest = out.data(); |
| 79 | + if (selection.is_range()) { |
| 80 | + const T* from = data + selection.offset(); |
| 81 | + for (uint32_t i = 0; i < count; ++i) { |
| 82 | + dest[i] = from[i]; |
| 83 | + } |
| 84 | + return; |
| 85 | + } |
| 86 | + const uint32_t* rows = selection.data(); |
| 87 | + for (uint32_t i = 0; i < count; ++i) { |
| 88 | + dest[i] = data[rows[i]]; |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +} // namespace |
| 93 | + |
| 94 | +AssertType::AssertType(uint32_t column, StorageType type, std::string name) |
| 95 | + : column_(column), type_(type), name_(std::move(name)) {} |
| 96 | + |
| 97 | +AssertType::~AssertType() = default; |
| 98 | + |
| 99 | +void AssertType::Widen(const ColumnView& column, |
| 100 | + uint32_t count, |
| 101 | + Values& values) const { |
| 102 | + if (column.type().Is<Uint32>()) { |
| 103 | + WidenAs<uint32_t>(column, count, values.ints); |
| 104 | + } else { |
| 105 | + WidenAs<int32_t>(column, count, values.ints); |
| 106 | + } |
| 107 | +} |
| 108 | +AssertType::State::~State() = default; |
| 109 | + |
| 110 | +std::unique_ptr<OperatorState> AssertType::MakeState() const { |
| 111 | + auto state = std::make_unique<State>(); |
| 112 | + if (type_.Is<Int64>()) { |
| 113 | + state->values->ints.resize(kMaxBatchRows); |
| 114 | + } else if (type_.Is<Double>()) { |
| 115 | + state->values->doubles.resize(kMaxBatchRows); |
| 116 | + } else { |
| 117 | + state->values->strings.resize(kMaxBatchRows); |
| 118 | + } |
| 119 | + state->values->validity = BitVector::CreateWithSize(kMaxBatchRows); |
| 120 | + return state; |
| 121 | +} |
| 122 | + |
| 123 | +base::Status AssertType::status(const OperatorState& state) const { |
| 124 | + return state.Cast<const State>().status; |
| 125 | +} |
| 126 | + |
| 127 | +OpResult AssertType::Execute(const RowBatch& in, |
| 128 | + RowBatch& out, |
| 129 | + OperatorState& state) const { |
| 130 | + State& s = state.Cast<State>(); |
| 131 | + out.CopyFrom(in); |
| 132 | + const ColumnView& column = in.column(column_); |
| 133 | + if (column.kind() != ColumnView::Kind::kVariant) { |
| 134 | + if (column.type() == type_) { |
| 135 | + return OpResult::kNeedMoreInput; |
| 136 | + } |
| 137 | + // Widening a narrower integer to Int64 always loses nothing. |
| 138 | + if (type_.Is<Int64>() && |
| 139 | + (column.type().Is<Uint32>() || column.type().Is<Int32>())) { |
| 140 | + Widen(column, in.size(), *s.values); |
| 141 | + out.SetColumn(column_, |
| 142 | + ColumnView::Reference(type_, s.values->ints.data(), |
| 143 | + column.validity()), |
| 144 | + s.values); |
| 145 | + return OpResult::kNeedMoreInput; |
| 146 | + } |
| 147 | + s.status = base::ErrStatus("column '%s' is %s, not %s", name_.c_str(), |
| 148 | + Name(column.type()), Name(type_)); |
| 149 | + return OpResult::kError; |
| 150 | + } |
| 151 | + |
| 152 | + uint32_t count = in.size(); |
| 153 | + const auto* cells = static_cast<const Variant*>(column.data()); |
| 154 | + Values& values = *s.values; |
| 155 | + values.validity.ClearAllBits(); |
| 156 | + auto write = [&](uint32_t row, const Variant& cell) { |
| 157 | + if (cell.type == Variant::Type::kNull) { |
| 158 | + // Written even for a null row, so nothing downstream reads an |
| 159 | + // uninitialised slot. |
| 160 | + if (type_.Is<Int64>()) { |
| 161 | + values.ints[row] = 0; |
| 162 | + } else if (type_.Is<Double>()) { |
| 163 | + values.doubles[row] = 0; |
| 164 | + } else { |
| 165 | + values.strings[row] = StringPool::Id::Null(); |
| 166 | + } |
| 167 | + return true; |
| 168 | + } |
| 169 | + if (type_.Is<Int64>() && cell.type == Variant::Type::kInt64) { |
| 170 | + values.ints[row] = cell.AsInt64(); |
| 171 | + } else if (type_.Is<Double>() && cell.type == Variant::Type::kDouble) { |
| 172 | + values.doubles[row] = cell.AsDouble(); |
| 173 | + } else if (type_.Is<Double>() && cell.type == Variant::Type::kInt64) { |
| 174 | + // Only convert where the widening is exact. |
| 175 | + int64_t value = cell.AsInt64(); |
| 176 | + auto widened = static_cast<double>(value); |
| 177 | + if (static_cast<int64_t>(widened) != value) { |
| 178 | + s.status = base::ErrStatus( |
| 179 | + "column '%s' holds an integer too large to be a float", |
| 180 | + name_.c_str()); |
| 181 | + return false; |
| 182 | + } |
| 183 | + values.doubles[row] = widened; |
| 184 | + } else if (type_.Is<String>() && cell.type == Variant::Type::kString) { |
| 185 | + values.strings[row] = cell.AsString(); |
| 186 | + } else { |
| 187 | + s.status = base::ErrStatus("column '%s' holds %s, not %s", name_.c_str(), |
| 188 | + Name(cell.type), Name(type_)); |
| 189 | + return false; |
| 190 | + } |
| 191 | + values.validity.set(row); |
| 192 | + return true; |
| 193 | + }; |
| 194 | + |
| 195 | + RowSelection selection = column.selection(); |
| 196 | + bool ok; |
| 197 | + if (selection.is_range()) { |
| 198 | + const Variant* from = cells + selection.offset(); |
| 199 | + ok = Walk([from](uint32_t i) -> const Variant& { return from[i]; }, count, |
| 200 | + write); |
| 201 | + } else { |
| 202 | + const uint32_t* rows = selection.data(); |
| 203 | + ok = Walk( |
| 204 | + [cells, rows](uint32_t i) -> const Variant& { return cells[rows[i]]; }, |
| 205 | + count, write); |
| 206 | + } |
| 207 | + if (!ok) { |
| 208 | + return OpResult::kError; |
| 209 | + } |
| 210 | + |
| 211 | + const void* data = nullptr; |
| 212 | + if (type_.Is<Int64>()) { |
| 213 | + data = values.ints.data(); |
| 214 | + } else if (type_.Is<Double>()) { |
| 215 | + data = values.doubles.data(); |
| 216 | + } else { |
| 217 | + data = values.strings.data(); |
| 218 | + } |
| 219 | + out.SetColumn(column_, ColumnView::Reference(type_, data, &values.validity), |
| 220 | + s.values); |
| 221 | + return OpResult::kNeedMoreInput; |
| 222 | +} |
| 223 | + |
| 224 | +} // namespace perfetto::trace_processor::core::exec |
0 commit comments