| # Copyright (C) 2022 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. | |
| from typing import List | |
| from typing import Optional | |
| from python.generators.trace_processor_table.public import Alias | |
| from python.generators.trace_processor_table.public import ColumnFlag | |
| from python.generators.trace_processor_table.public import CppAccess | |
| from python.generators.trace_processor_table.public import CppAccessDuration | |
| from python.generators.trace_processor_table.public import Purpose | |
| from python.generators.trace_processor_table.public import SqlAccess | |
| from python.generators.trace_processor_table.util import ParsedTable | |
| from python.generators.trace_processor_table.util import ParsedColumn | |
| from python.generators.trace_processor_table.util import parse_type | |
| class ColumnSerializer: | |
| def __init__(self, table: ParsedTable, column: ParsedColumn, col_index: int): | |
| self.col_index = col_index | |
| self.parsed_col = column | |
| self.col = self.parsed_col.column | |
| self.name = self.col.name | |
| self.flags = self.col.flags | |
| parsed_type = parse_type(table.table, self.col.type) | |
| self.cpp_type_non_optional = parsed_type.cpp_type() | |
| self.cpp_type_with_optionality = parsed_type.cpp_type_with_optionality() | |
| self.is_optional = parsed_type.is_optional | |
| self.is_string = self.cpp_type_non_optional == 'StringPool::Id' | |
| self.is_id_type = parsed_type.id_table | |
| self.is_no_transform_id = parsed_type.is_no_transform_id() | |
| self.is_implicit_id = self.parsed_col.is_implicit_id | |
| self.table_purpose = table.table.purpose | |
| def row_field(self) -> Optional[str]: | |
| if self.is_implicit_id: | |
| return None | |
| return f' {self.cpp_type_with_optionality} {self.name};' | |
| def row_insert_arg_value(self) -> Optional[str]: | |
| """Generates the C++ code to access this column's value from a Row object, | |
| for use in the dataframe::InsertUnchecked call.""" | |
| if self.is_implicit_id: | |
| return f'std::monostate()' | |
| if self.is_optional and self.is_id_type and not self.is_no_transform_id: | |
| return f'row.{self.name} ? std::make_optional(row.{self.name}->value) : std::nullopt' | |
| if self.is_optional and self.is_string: | |
| return f'row.{self.name}' | |
| if self.is_string: | |
| return f'std::make_optional(row.{self.name})' | |
| if self.is_id_type and not self.is_no_transform_id: | |
| return f'row.{self.name}.value' | |
| return f'row.{self.name}' | |
| def cursor_getter(self) -> Optional[str]: | |
| if self.col.cpp_access == CppAccess.NONE: | |
| return '' | |
| if self.col.cpp_access_duration == CppAccessDuration.PRE_FINALIZATION_ONLY: | |
| dcheck = 'PERFETTO_DCHECK(!dataframe_->finalized());' | |
| else: | |
| dcheck = '' | |
| if self.is_optional and self.is_id_type: | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| auto res = cursor_.GetCellUnchecked<ColumnIndex::{self.name}>(kSpec); | |
| return res ? std::make_optional({self.cpp_type_non_optional}{{*res}}) : std::nullopt; | |
| }}''' | |
| if self.is_optional and self.is_string: | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| return cursor_.GetCellUnchecked<ColumnIndex::{self.name}>(kSpec); | |
| }}''' | |
| if self.is_string: | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| auto res = cursor_.GetCellUnchecked<ColumnIndex::{self.name}>(kSpec); | |
| return res ? *res : StringPool::Id::Null(); | |
| }}''' | |
| if self.is_id_type: | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| return {self.cpp_type_non_optional}{{cursor_.GetCellUnchecked<ColumnIndex::{self.name}>(kSpec)}}; | |
| }}''' | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| return cursor_.GetCellUnchecked<ColumnIndex::{self.name}>(kSpec); | |
| }}''' | |
| def cursor_setter(self) -> Optional[str]: | |
| if self.col.cpp_access == CppAccess.NONE or self.col.cpp_access == CppAccess.READ: | |
| return '' | |
| if self.col.cpp_access_duration == CppAccessDuration.PRE_FINALIZATION_ONLY: | |
| dcheck = 'PERFETTO_DCHECK(!dataframe_->finalized());' | |
| else: | |
| dcheck = '' | |
| if self.is_optional and self.is_id_type and not self.is_no_transform_id: | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| auto res_value = res ? std::make_optional(res->value) : std::nullopt; | |
| cursor_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, res_value); | |
| }}''' | |
| if self.is_optional and self.is_string: | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| cursor_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, res); | |
| }}''' | |
| if self.is_string: | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| cursor_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, std::make_optional(res)); | |
| }}''' | |
| if self.is_id_type and not self.is_no_transform_id: | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| cursor_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, res.value); | |
| }}''' | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| cursor_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, res); | |
| }}''' | |
| def row_reference_getter(self) -> str: | |
| if self.col.cpp_access == CppAccess.NONE: | |
| return '' | |
| if self.col.cpp_access_duration == CppAccessDuration.PRE_FINALIZATION_ONLY: | |
| dcheck = 'PERFETTO_DCHECK(!table_->dataframe_.finalized());' | |
| else: | |
| dcheck = '' | |
| if self.is_optional and self.is_id_type: | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| auto res = table_->dataframe_.template GetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_); | |
| return res ? std::make_optional({self.cpp_type_non_optional}{{*res}}) : std::nullopt; | |
| }}''' | |
| if self.is_optional and self.is_string: | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| return table_->dataframe_.template GetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_); | |
| }}''' | |
| if self.is_string: | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| auto res = table_->dataframe_.template GetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_); | |
| return res ? *res : StringPool::Id::Null(); | |
| }}''' | |
| if self.is_id_type: | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| return {self.cpp_type_non_optional}{{table_->dataframe_.template GetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_)}}; | |
| }}''' | |
| return f''' | |
| {self.cpp_type_with_optionality} {self.name}() const {{ | |
| {dcheck} | |
| return table_->dataframe_.template GetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_); | |
| }}''' | |
| def row_reference_setter(self) -> Optional[str]: | |
| if self.col.cpp_access == CppAccess.NONE or self.col.cpp_access == CppAccess.READ: | |
| return '' | |
| if self.col.cpp_access_duration == CppAccessDuration.PRE_FINALIZATION_ONLY: | |
| dcheck = 'PERFETTO_DCHECK(!table_->dataframe_.finalized());' | |
| else: | |
| dcheck = '' | |
| if self.is_optional and self.is_id_type and not self.is_no_transform_id: | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| auto res_value = res ? std::make_optional(res->value) : std::nullopt; | |
| table_->dataframe_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_, res_value); | |
| }}''' | |
| if self.is_optional and self.is_string: | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| table_->dataframe_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_, res); | |
| }}''' | |
| if self.is_string: | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| table_->dataframe_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_, std::make_optional(res)); | |
| }}''' | |
| if self.is_id_type and not self.is_no_transform_id: | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| table_->dataframe_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_, res.value); | |
| }}''' | |
| return f''' | |
| void set_{self.name}({self.cpp_type_with_optionality} res) {{ | |
| {dcheck} | |
| table_->dataframe_.SetCellUnchecked<ColumnIndex::{self.name}>(kSpec, row_, res); | |
| }}''' | |
| def colindex_member(self) -> str: | |
| return f'static constexpr uint32_t {self.name} = {self.col_index}' | |
| def typespec_column_name_literal(self) -> str: | |
| return f'"{self.name}"' | |
| def _get_storage_type_tag(self) -> str: | |
| if self.is_implicit_id: | |
| return 'dataframe::Id' | |
| if self.is_string: | |
| return 'dataframe::String' | |
| if self.cpp_type_non_optional == 'uint32_t' or self.is_id_type: | |
| return 'dataframe::Uint32' | |
| if self.cpp_type_non_optional == 'int32_t': | |
| return 'dataframe::Int32' | |
| if self.cpp_type_non_optional == 'int64_t': | |
| return 'dataframe::Int64' | |
| if self.cpp_type_non_optional == 'double': | |
| return 'dataframe::Double' | |
| raise ValueError( | |
| f"Unknown cpp_type_non_optional '{self.cpp_type_non_optional}' for Dataframe StorageType for column '{self.name}'" | |
| ) | |
| def _get_nullability_tag(self) -> str: | |
| if self.is_optional or self.is_string: | |
| # StaticTableFunction outputs are read by random access (GetCell), which | |
| # the scan-only SparseNull layout does not support. Force DenseNull for | |
| # all nullable columns regardless of cpp_access. | |
| if self.table_purpose == Purpose.STATIC_TABLE_FUNCTION: | |
| return 'dataframe::DenseNull' | |
| if self.col.sql_access == SqlAccess.HIGH_PERF: | |
| return 'dataframe::DenseNull' | |
| if self.col.cpp_access == CppAccess.NONE: | |
| return 'dataframe::SparseNull' | |
| if self.col.cpp_access == CppAccess.READ_AND_HIGH_PERF_WRITE: | |
| return 'dataframe::DenseNull' | |
| assert self.col.cpp_access in ( | |
| CppAccess.READ, | |
| CppAccess.READ_AND_LOW_PERF_WRITE, | |
| ) | |
| # TODO(lalitm): we force this to dense for strings because in the | |
| # pre-dataframe days, string nullability was just represented by | |
| # StringPool::Id::Null(). There are hidden dependencies everywhere in the | |
| # codebase that expect O(1) writes to string columns. Fixing this is | |
| # non-trivial, so we just keep it dense for now. | |
| if self.is_string and self.col.cpp_access == CppAccess.READ_AND_LOW_PERF_WRITE: | |
| return 'dataframe::DenseNull' | |
| if self.col.cpp_access_duration == CppAccessDuration.PRE_FINALIZATION_ONLY: | |
| return 'dataframe::SparseNullWithPopcountUntilFinalization' | |
| assert self.col.cpp_access_duration == CppAccessDuration.POST_FINALIZATION | |
| return 'dataframe::SparseNullWithPopcountAlways' | |
| return 'dataframe::NonNull' | |
| def _get_sort_state_tag(self) -> str: | |
| if ColumnFlag.SORTED in self.flags: | |
| if self.is_implicit_id: | |
| return 'dataframe::IdSorted' | |
| if ColumnFlag.SET_ID in self.flags: | |
| return 'dataframe::SetIdSorted' | |
| return 'dataframe::Sorted' | |
| return 'dataframe::Unsorted' | |
| def _get_duplicate_state_tag(self) -> str: | |
| if self.is_implicit_id: | |
| return 'dataframe::NoDuplicates' | |
| return 'dataframe::HasDuplicates' | |
| def typespec_typed_column_spec_expr(self) -> str: | |
| storage_tag = self._get_storage_type_tag() | |
| null_tag = self._get_nullability_tag() | |
| sort_tag = self._get_sort_state_tag() | |
| dupe_tag = self._get_duplicate_state_tag() | |
| return ( | |
| f"dataframe::CreateTypedColumnSpec(" | |
| f"{storage_tag}{{}}, {null_tag}{{}}, {sort_tag}{{}}, {dupe_tag}{{}})") | |
| def column_spec_initializer(self) -> str: | |
| """Generate ColumnSpec initializer for non-templated array.""" | |
| storage_tag = self._get_storage_type_tag() | |
| null_tag = self._get_nullability_tag() | |
| sort_tag = self._get_sort_state_tag() | |
| dupe_tag = self._get_duplicate_state_tag() | |
| return f"dataframe::ColumnSpec{{{storage_tag}{{}}, {null_tag}{{}}, {sort_tag}{{}}, {dupe_tag}{{}}}}" | |
| class TableSerializer(object): | |
| def __init__(self, parsed: ParsedTable): | |
| self.table = parsed.table | |
| self.table_name = parsed.table.class_name | |
| self.column_serializers: List[ColumnSerializer] = [] | |
| # Filter out aliases first, then assign col_index | |
| temp_serializers = [] | |
| for c_parsed in parsed.columns: | |
| if isinstance(c_parsed.column.type, Alias): | |
| continue | |
| # col_index will be assigned when rebuilding self.column_serializers | |
| temp_serializers.append(ColumnSerializer(parsed, c_parsed, 0)) | |
| self.column_serializers = [ | |
| ColumnSerializer(parsed, cs.parsed_col, i) | |
| for i, cs in enumerate(temp_serializers) | |
| ] | |
| def foreach_col(self, serialize_fn, delimiter='\n') -> str: | |
| lines = [] | |
| for c_ser in self.column_serializers: | |
| serialized = serialize_fn(c_ser) | |
| if serialized: | |
| lines.append(serialized.lstrip('\n').rstrip()) | |
| return delimiter.join(lines).strip() | |
| def serialize_id_type(self) -> str: | |
| """Generate the Id type at namespace scope.""" | |
| return f'''struct {self.table_name}_Id : BaseId {{ | |
| {self.table_name}_Id() = default; | |
| explicit constexpr {self.table_name}_Id(uint32_t _value) : BaseId(_value) {{}} | |
| bool operator==(const {self.table_name}_Id& other) const {{ | |
| return value == other.value; | |
| }} | |
| }};''' | |
| def serialize_static_specs(self) -> str: | |
| """Generate non-templated column names and specs arrays.""" | |
| col_names = self.foreach_col( | |
| ColumnSerializer.typespec_column_name_literal, delimiter=', ') | |
| col_specs = self.foreach_col( | |
| ColumnSerializer.column_spec_initializer, delimiter=',\n ') | |
| col_count = len(self.column_serializers) | |
| return f''' | |
| inline constexpr const char* k{self.table_name}ColumnNames[] = {{{col_names}}}; | |
| inline constexpr dataframe::ColumnSpec k{self.table_name}ColumnSpecs[] = {{ | |
| {col_specs}}}; | |
| inline constexpr uint32_t k{self.table_name}ColumnCount = {col_count}; | |
| ''' | |
| def row_struct(self) -> str: | |
| fields = [] | |
| constructor_params_list = [] | |
| constructor_inits_list = [] | |
| for c_ser in self.column_serializers: | |
| if not c_ser.is_implicit_id: | |
| fields.append(c_ser.row_field()) | |
| constructor_params_list.append( | |
| f'{c_ser.cpp_type_with_optionality} _{c_ser.name} = {{}}') | |
| constructor_inits_list.append(f'{c_ser.name}(std::move(_{c_ser.name}))') | |
| fields_str = "\n".join(filter(None, fields)) | |
| params_str = ', '.join(constructor_params_list) | |
| inits_str = ( | |
| ': ' + | |
| ', '.join(constructor_inits_list)) if constructor_inits_list else '' | |
| return f''' | |
| struct Row {{ | |
| Row({params_str}) {inits_str} {{}} | |
| bool operator==(const Row& other) const {{ | |
| return {' && '.join([f'std::equal_to<>()({c_ser.name}, other.{c_ser.name})' for c_ser in self.column_serializers if not c_ser.is_implicit_id])}; | |
| }} | |
| {fields_str} | |
| }};''' | |
| def serialize(self) -> str: | |
| col_names = self.foreach_col( | |
| ColumnSerializer.typespec_column_name_literal, delimiter=',') | |
| col_specs = self.foreach_col( | |
| ColumnSerializer.typespec_typed_column_spec_expr, delimiter=',\n ') | |
| col_index = self.foreach_col( | |
| ColumnSerializer.colindex_member, delimiter=';\n ') | |
| row_ref_getter = self.foreach_col( | |
| ColumnSerializer.row_reference_getter, delimiter='\n ') | |
| row_ref_setter = self.foreach_col( | |
| ColumnSerializer.row_reference_setter, delimiter='\n ') | |
| cursor_getter = self.foreach_col( | |
| ColumnSerializer.cursor_getter, delimiter='\n') | |
| cursor_setter = self.foreach_col( | |
| ColumnSerializer.cursor_setter, delimiter='\n') | |
| insert_argvalue = self.foreach_col( | |
| ColumnSerializer.row_insert_arg_value, delimiter=', ') | |
| return f''' | |
| class {self.table_name} {{ | |
| public: | |
| static constexpr auto kSpec = dataframe::CreateTypedDataframeSpec( | |
| {{{col_names}}}, | |
| {col_specs}); | |
| using Id = {self.table_name}_Id; | |
| struct RowReference; | |
| struct ConstRowReference; | |
| struct RowNumber {{ | |
| public: | |
| explicit constexpr RowNumber(uint32_t value) : value_(value) {{}} | |
| uint32_t row_number() const {{ return value_; }} | |
| RowReference ToRowReference({self.table_name}* table) const {{ | |
| return RowReference(table, value_); | |
| }} | |
| ConstRowReference ToRowReference(const {self.table_name}& table) const {{ | |
| return ConstRowReference(&table, value_); | |
| }} | |
| bool operator==(const RowNumber& other) const {{ | |
| return value_ == other.value_; | |
| }} | |
| bool operator<(const RowNumber& other) const {{ | |
| return value_ < other.value_; | |
| }} | |
| private: | |
| uint32_t value_; | |
| }}; | |
| struct ColumnIndex {{ | |
| {col_index}; | |
| }}; | |
| struct RowReference {{ | |
| public: | |
| explicit RowReference({self.table_name}* table, uint32_t row) | |
| : table_(table), row_(row) {{ | |
| base::ignore_result(table_); | |
| }} | |
| {row_ref_getter} | |
| {row_ref_setter} | |
| RowNumber ToRowNumber() const {{ | |
| return RowNumber{{row_}}; | |
| }} | |
| private: | |
| friend struct ConstRowReference; | |
| {self.table_name}* table_; | |
| uint32_t row_; | |
| }}; | |
| struct ConstRowReference {{ | |
| public: | |
| explicit ConstRowReference(const {self.table_name}* table, uint32_t row) | |
| : table_(table), row_(row) {{ | |
| base::ignore_result(table_); | |
| }} | |
| ConstRowReference(const RowReference& other) | |
| : table_(other.table_), row_(other.row_) {{}} | |
| {row_ref_getter} | |
| RowNumber ToRowNumber() const {{ | |
| return RowNumber{{row_}}; | |
| }} | |
| private: | |
| const {self.table_name}* table_; | |
| uint32_t row_; | |
| }}; | |
| class ConstCursor {{ | |
| public: | |
| explicit ConstCursor(const dataframe::Dataframe& df, | |
| std::vector<dataframe::FilterSpec> filters, | |
| std::vector<dataframe::SortSpec> sorts) | |
| : dataframe_(&df), cursor_(&df, std::move(filters), std::move(sorts)) {{ | |
| base::ignore_result(dataframe_); | |
| }} | |
| PERFETTO_ALWAYS_INLINE void Execute() {{ cursor_.ExecuteUnchecked(); }} | |
| PERFETTO_ALWAYS_INLINE bool Eof() const {{ return cursor_.Eof(); }} | |
| PERFETTO_ALWAYS_INLINE void Next() {{ cursor_.Next(); }} | |
| template <typename C> | |
| PERFETTO_ALWAYS_INLINE void SetFilterValueUnchecked(uint32_t index, C value) {{ | |
| cursor_.SetFilterValueUnchecked(index, std::move(value)); | |
| }} | |
| PERFETTO_ALWAYS_INLINE void SetFilterValueListUnchecked( | |
| uint32_t index, | |
| const dataframe::TypedCursor::FilterValue* values, | |
| uint32_t count) {{ | |
| cursor_.SetFilterValueListUnchecked(index, values, count); | |
| }} | |
| RowNumber ToRowNumber() const {{ | |
| return RowNumber{{cursor_.RowIndex()}}; | |
| }} | |
| void Reset() {{ cursor_.Reset(); }} | |
| {cursor_getter} | |
| private: | |
| const dataframe::Dataframe* dataframe_; | |
| dataframe::TypedCursor cursor_; | |
| }}; | |
| class Cursor {{ | |
| public: | |
| explicit Cursor(dataframe::Dataframe& df, | |
| std::vector<dataframe::FilterSpec> filters, | |
| std::vector<dataframe::SortSpec> sorts) | |
| : dataframe_(&df), cursor_(&df, std::move(filters), std::move(sorts)) {{ | |
| base::ignore_result(dataframe_); | |
| }} | |
| PERFETTO_ALWAYS_INLINE void Execute() {{ cursor_.ExecuteUnchecked(); }} | |
| PERFETTO_ALWAYS_INLINE bool Eof() const {{ return cursor_.Eof(); }} | |
| PERFETTO_ALWAYS_INLINE void Next() {{ cursor_.Next(); }} | |
| template <typename C> | |
| PERFETTO_ALWAYS_INLINE void SetFilterValueUnchecked(uint32_t index, C value) {{ | |
| cursor_.SetFilterValueUnchecked(index, std::move(value)); | |
| }} | |
| PERFETTO_ALWAYS_INLINE void SetFilterValueListUnchecked( | |
| uint32_t index, | |
| const dataframe::TypedCursor::FilterValue* values, | |
| uint32_t count) {{ | |
| cursor_.SetFilterValueListUnchecked(index, values, count); | |
| }} | |
| RowNumber ToRowNumber() const {{ | |
| return RowNumber{{cursor_.RowIndex()}}; | |
| }} | |
| void Reset() {{ cursor_.Reset(); }} | |
| {cursor_getter} | |
| {cursor_setter} | |
| private: | |
| dataframe::Dataframe* dataframe_; | |
| dataframe::TypedCursor cursor_; | |
| }}; | |
| class Iterator {{ | |
| public: | |
| explicit Iterator({self.table_name}* table) : table_(table) {{ | |
| base::ignore_result(table_); | |
| }} | |
| explicit operator bool() const {{ return row_ < table_->row_count(); }} | |
| Iterator& operator++() {{ | |
| ++row_; | |
| return *this; | |
| }} | |
| RowNumber row_number() const {{ | |
| return RowNumber{{row_}}; | |
| }} | |
| RowReference ToRowReference() const {{ | |
| return RowReference(table_, row_); | |
| }} | |
| {row_ref_getter} | |
| {row_ref_setter} | |
| private: | |
| {self.table_name}* table_; | |
| uint32_t row_ = 0; | |
| }}; | |
| class ConstIterator {{ | |
| public: | |
| explicit ConstIterator(const {self.table_name}* table) : table_(table) {{ | |
| base::ignore_result(table_); | |
| }} | |
| explicit operator bool() const {{ return row_ < table_->row_count(); }} | |
| ConstIterator& operator++() {{ | |
| ++row_; | |
| return *this; | |
| }} | |
| RowNumber row_number() const {{ | |
| return RowNumber{{row_}}; | |
| }} | |
| ConstRowReference ToRowReference() const {{ | |
| return ConstRowReference(table_, row_); | |
| }} | |
| {row_ref_getter} | |
| private: | |
| const {self.table_name}* table_; | |
| uint32_t row_ = 0; | |
| }}; | |
| struct IdAndRow {{ | |
| Id id; | |
| RowNumber row_number; | |
| uint32_t row; | |
| RowReference row_reference; | |
| }}; | |
| {self.row_struct()} | |
| explicit {self.table_name}(StringPool* pool) | |
| : dataframe_( | |
| dataframe::Dataframe::CreateFromTypedSpec(kSpec, pool)) {{}} | |
| template <typename = void> | |
| IdAndRow Insert(const Row& row) {{ | |
| uint32_t row_count = dataframe_.row_count(); | |
| dataframe_.InsertUnchecked(kSpec, {insert_argvalue}); | |
| return IdAndRow{{Id{{row_count}}, RowNumber{{row_count}}, row_count, RowReference(this, row_count)}}; | |
| }} | |
| uint32_t row_count() const {{ | |
| return dataframe_.row_count(); | |
| }} | |
| uint64_t mutations() const {{ | |
| return dataframe_.mutations(); | |
| }} | |
| ConstRowReference operator[](Id id) const {{ | |
| return ConstRowReference(this, id.value); | |
| }} | |
| ConstRowReference operator[](uint32_t row) const {{ | |
| return ConstRowReference(this, row); | |
| }} | |
| RowReference operator[](Id id) {{ | |
| return RowReference(this, id.value); | |
| }} | |
| RowReference operator[](uint32_t row) {{ | |
| return RowReference(this, row); | |
| }} | |
| // Returns an `Id` for `value` if it refers to a valid row in this table, or | |
| // std::nullopt otherwise. Use this whenever turning an untrusted integer | |
| // (e.g. from SQL or a decoded proto) into a typed `Id`. | |
| std::optional<Id> TryCastId(int64_t value) const {{ | |
| if (value < 0 || value >= row_count()) {{ | |
| return std::nullopt; | |
| }} | |
| return Id{{static_cast<uint32_t>(value)}}; | |
| }} | |
| ConstCursor CreateCursor( | |
| std::vector<dataframe::FilterSpec> filters = {{}}, | |
| std::vector<dataframe::SortSpec> sorts = {{}}) const {{ | |
| return ConstCursor(dataframe_, std::move(filters), std::move(sorts)); | |
| }} | |
| Cursor CreateCursor( | |
| std::vector<dataframe::FilterSpec> filters = {{}}, | |
| std::vector<dataframe::SortSpec> sorts = {{}}) {{ | |
| return Cursor(dataframe_, std::move(filters), std::move(sorts)); | |
| }} | |
| Iterator IterateRows() {{ return Iterator(this); }} | |
| ConstIterator IterateRows() const {{ return ConstIterator(this); }} | |
| void Finalize() {{ dataframe_.Finalize(); }} | |
| void Clear() {{ dataframe_.Clear(); }} | |
| static const char* Name() {{ | |
| return "{self.table.sql_name}"; | |
| }} | |
| dataframe::Dataframe& dataframe() {{ | |
| return dataframe_; | |
| }} | |
| const dataframe::Dataframe& dataframe() const {{ | |
| return dataframe_; | |
| }} | |
| private: | |
| dataframe::Dataframe dataframe_; | |
| }}; | |
| static_assert(sizeof({self.table_name}) == sizeof(dataframe::Dataframe)); | |
| ''' | |
| def serialize_header(ifdef_guard: str, tables: List[ParsedTable], | |
| include_paths: List[str], fwd_header_path: str) -> str: | |
| """Serializes a table header file containing the given set of tables.""" | |
| # Replace the backslash with forward slash when building on Windows. | |
| # Caused b/327985369 without the replace. | |
| include_paths_str = '\n'.join([f'#include "{i}"' for i in include_paths | |
| ]).replace("\\", "/") | |
| tables_str = '\n\n'.join([TableSerializer(t).serialize() for t in tables]) | |
| fwd_header_str = fwd_header_path.replace("\\", "/") | |
| return f'''\ | |
| #ifndef {ifdef_guard} | |
| #define {ifdef_guard} | |
| #include <cstdint> | |
| #include <optional> | |
| #include <tuple> | |
| #include <type_traits> | |
| #include <utility> | |
| #include <variant> | |
| #include <vector> | |
| #include "perfetto/base/compiler.h" | |
| #include "perfetto/public/compiler.h" | |
| #include "src/trace_processor/containers/string_pool.h" | |
| #include "src/trace_processor/tables/macros_internal.h" | |
| #include "src/trace_processor/core/dataframe/dataframe.h" | |
| #include "src/trace_processor/core/dataframe/specs.h" | |
| #include "src/trace_processor/core/dataframe/typed_cursor.h" | |
| #include "{fwd_header_str}" | |
| {include_paths_str} | |
| namespace perfetto::trace_processor::tables {{ | |
| {tables_str} | |
| }} // namespace perfetto::trace_processor::tables | |
| #endif // {ifdef_guard} | |
| ''' | |
| def serialize_fwd_header(ifdef_guard: str, tables: List[ParsedTable], | |
| include_paths: List[str]) -> str: | |
| """Serializes a forward declaration header with Id types only.""" | |
| # Replace the backslash with forward slash when building on Windows. | |
| include_paths_str = '\n'.join([ | |
| f'#include "{i}" // IWYU pragma: export' for i in include_paths | |
| ]).replace("\\", "/") | |
| id_types = '\n\n'.join( | |
| [TableSerializer(t).serialize_id_type() for t in tables]) | |
| fwd_decls = '\n'.join([f'class {t.table.class_name};' for t in tables]) | |
| # Generate tuple of table classes for this header | |
| table_names = ', '.join([t.table.class_name for t in tables]) | |
| tuple_name = ifdef_guard.replace('_H_', '').replace( | |
| 'SRC_TRACE_PROCESSOR_TABLES_', '') + '_TABLES' | |
| return f'''\ | |
| #ifndef {ifdef_guard} | |
| #define {ifdef_guard} | |
| #include <cstdint> | |
| #include <tuple> | |
| #include "src/trace_processor/tables/macros_internal.h" // IWYU pragma: export | |
| {include_paths_str} | |
| namespace perfetto::trace_processor::tables {{ | |
| {id_types} | |
| {fwd_decls} | |
| // Tuple of all table types in this header | |
| using {tuple_name} = std::tuple<{table_names}>; | |
| }} // namespace perfetto::trace_processor::tables | |
| #endif // {ifdef_guard} | |
| ''' |