1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-28 21:53:48 +00:00

Merge branch 'integration' into memory_api

This commit is contained in:
J. Nick Koston
2025-10-13 18:32:24 -10:00
11 changed files with 106 additions and 50 deletions

View File

@@ -1519,7 +1519,7 @@ message BluetoothGATTCharacteristic {
repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true]; repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true];
uint32 handle = 2; uint32 handle = 2;
uint32 properties = 3; uint32 properties = 3;
repeated BluetoothGATTDescriptor descriptors = 4; repeated BluetoothGATTDescriptor descriptors = 4 [(fixed_vector) = true];
// New field for efficient UUID (v1.12+) // New field for efficient UUID (v1.12+)
// Only one of uuid or short_uuid will be set. // Only one of uuid or short_uuid will be set.
@@ -1531,7 +1531,7 @@ message BluetoothGATTCharacteristic {
message BluetoothGATTService { message BluetoothGATTService {
repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true]; repeated uint64 uuid = 1 [(fixed_array_size) = 2, (fixed_array_skip_zero) = true];
uint32 handle = 2; uint32 handle = 2;
repeated BluetoothGATTCharacteristic characteristics = 3; repeated BluetoothGATTCharacteristic characteristics = 3 [(fixed_vector) = true];
// New field for efficient UUID (v1.12+) // New field for efficient UUID (v1.12+)
// Only one of uuid or short_uuid will be set. // Only one of uuid or short_uuid will be set.

View File

@@ -64,4 +64,10 @@ extend google.protobuf.FieldOptions {
// This is typically done through methods returning const T& or special accessor // This is typically done through methods returning const T& or special accessor
// methods like get_options() or supported_modes_for_api_(). // methods like get_options() or supported_modes_for_api_().
optional string container_pointer = 50001; optional string container_pointer = 50001;
// fixed_vector: Use FixedVector instead of std::vector for repeated fields
// When set, the repeated field will use FixedVector<T> which requires calling
// init(size) before adding elements. This eliminates std::vector template overhead
// and is ideal when the exact size is known before populating the array.
optional bool fixed_vector = 50013 [default=false];
} }

View File

@@ -1923,7 +1923,7 @@ class BluetoothGATTCharacteristic final : public ProtoMessage {
std::array<uint64_t, 2> uuid{}; std::array<uint64_t, 2> uuid{};
uint32_t handle{0}; uint32_t handle{0};
uint32_t properties{0}; uint32_t properties{0};
std::vector<BluetoothGATTDescriptor> descriptors{}; FixedVector<BluetoothGATTDescriptor> descriptors{};
uint32_t short_uuid{0}; uint32_t short_uuid{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(ProtoSize &size) const override; void calculate_size(ProtoSize &size) const override;
@@ -1937,7 +1937,7 @@ class BluetoothGATTService final : public ProtoMessage {
public: public:
std::array<uint64_t, 2> uuid{}; std::array<uint64_t, 2> uuid{};
uint32_t handle{0}; uint32_t handle{0};
std::vector<BluetoothGATTCharacteristic> characteristics{}; FixedVector<BluetoothGATTCharacteristic> characteristics{};
uint32_t short_uuid{0}; uint32_t short_uuid{0};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(ProtoSize &size) const override; void calculate_size(ProtoSize &size) const override;

View File

@@ -749,15 +749,31 @@ class ProtoSize {
template<typename MessageType> template<typename MessageType>
inline void add_repeated_message(uint32_t field_id_size, const std::vector<MessageType> &messages) { inline void add_repeated_message(uint32_t field_id_size, const std::vector<MessageType> &messages) {
// Skip if the vector is empty // Skip if the vector is empty
if (messages.empty()) { if (!messages.empty()) {
return;
}
// Use the force version for all messages in the repeated field // Use the force version for all messages in the repeated field
for (const auto &message : messages) { for (const auto &message : messages) {
add_message_object_force(field_id_size, message); add_message_object_force(field_id_size, message);
} }
} }
}
/**
* @brief Calculates and adds the sizes of all messages in a repeated field to the total message size (FixedVector
* version)
*
* @tparam MessageType The type of the nested messages in the FixedVector
* @param messages FixedVector of message objects
*/
template<typename MessageType>
inline void add_repeated_message(uint32_t field_id_size, const FixedVector<MessageType> &messages) {
// Skip if the fixed vector is empty
if (!messages.empty()) {
// Use the force version for all messages in the repeated field
for (const auto &message : messages) {
add_message_object_force(field_id_size, message);
}
}
}
}; };
// Implementation of encode_message - must be after ProtoMessage is defined // Implementation of encode_message - must be after ProtoMessage is defined

View File

@@ -230,8 +230,8 @@ void BluetoothConnection::send_service_for_discovery_() {
service_resp.handle = service_result.start_handle; service_resp.handle = service_result.start_handle;
if (total_char_count > 0) { if (total_char_count > 0) {
// Reserve space and process characteristics // Initialize FixedVector with exact count and process characteristics
service_resp.characteristics.reserve(total_char_count); service_resp.characteristics.init(total_char_count);
uint16_t char_offset = 0; uint16_t char_offset = 0;
esp_gattc_char_elem_t char_result; esp_gattc_char_elem_t char_result;
while (true) { // characteristics while (true) { // characteristics
@@ -253,9 +253,7 @@ void BluetoothConnection::send_service_for_discovery_() {
service_resp.characteristics.emplace_back(); service_resp.characteristics.emplace_back();
auto &characteristic_resp = service_resp.characteristics.back(); auto &characteristic_resp = service_resp.characteristics.back();
fill_gatt_uuid(characteristic_resp.uuid, characteristic_resp.short_uuid, char_result.uuid, use_efficient_uuids); fill_gatt_uuid(characteristic_resp.uuid, characteristic_resp.short_uuid, char_result.uuid, use_efficient_uuids);
characteristic_resp.handle = char_result.char_handle; characteristic_resp.handle = char_result.char_handle;
characteristic_resp.properties = char_result.properties; characteristic_resp.properties = char_result.properties;
char_offset++; char_offset++;
@@ -271,12 +269,11 @@ void BluetoothConnection::send_service_for_discovery_() {
return; return;
} }
if (total_desc_count == 0) { if (total_desc_count == 0) {
// No descriptors, continue to next characteristic
continue; continue;
} }
// Reserve space and process descriptors // Initialize FixedVector with exact count and process descriptors
characteristic_resp.descriptors.reserve(total_desc_count); characteristic_resp.descriptors.init(total_desc_count);
uint16_t desc_offset = 0; uint16_t desc_offset = 0;
esp_gattc_descr_elem_t desc_result; esp_gattc_descr_elem_t desc_result;
while (true) { // descriptors while (true) { // descriptors
@@ -297,9 +294,7 @@ void BluetoothConnection::send_service_for_discovery_() {
characteristic_resp.descriptors.emplace_back(); characteristic_resp.descriptors.emplace_back();
auto &descriptor_resp = characteristic_resp.descriptors.back(); auto &descriptor_resp = characteristic_resp.descriptors.back();
fill_gatt_uuid(descriptor_resp.uuid, descriptor_resp.short_uuid, desc_result.uuid, use_efficient_uuids); fill_gatt_uuid(descriptor_resp.uuid, descriptor_resp.short_uuid, desc_result.uuid, use_efficient_uuids);
descriptor_resp.handle = desc_result.handle; descriptor_resp.handle = desc_result.handle;
desc_offset++; desc_offset++;
} }

View File

@@ -108,8 +108,13 @@ class BTLoggers(Enum):
"""ESP32 WiFi provisioning over Bluetooth""" """ESP32 WiFi provisioning over Bluetooth"""
# Set to track which loggers are needed by components # Key for storing required loggers in CORE.data
_required_loggers: set[BTLoggers] = set() ESP32_BLE_REQUIRED_LOGGERS_KEY = "esp32_ble_required_loggers"
def _get_required_loggers() -> set[BTLoggers]:
"""Get the set of required Bluetooth loggers from CORE.data."""
return CORE.data.setdefault(ESP32_BLE_REQUIRED_LOGGERS_KEY, set())
# Dataclass for handler registration counts # Dataclass for handler registration counts
@@ -170,12 +175,13 @@ def register_bt_logger(*loggers: BTLoggers) -> None:
Args: Args:
*loggers: One or more BTLoggers enum members *loggers: One or more BTLoggers enum members
""" """
required_loggers = _get_required_loggers()
for logger in loggers: for logger in loggers:
if not isinstance(logger, BTLoggers): if not isinstance(logger, BTLoggers):
raise TypeError( raise TypeError(
f"Logger must be a BTLoggers enum member, got {type(logger)}" f"Logger must be a BTLoggers enum member, got {type(logger)}"
) )
_required_loggers.add(logger) required_loggers.add(logger)
CONF_BLE_ID = "ble_id" CONF_BLE_ID = "ble_id"
@@ -427,7 +433,7 @@ def final_validation(config):
FINAL_VALIDATE_SCHEMA = final_validation FINAL_VALIDATE_SCHEMA = final_validation
# This needs to be run as a job with very low priority so that all components have # This needs to be run as a job with CoroPriority.FINAL priority so that all components have
# a chance to register their handlers before the counts are added to defines. # a chance to register their handlers before the counts are added to defines.
@coroutine_with_priority(CoroPriority.FINAL) @coroutine_with_priority(CoroPriority.FINAL)
async def _add_ble_handler_defines(): async def _add_ble_handler_defines():
@@ -479,8 +485,9 @@ async def to_code(config):
# Apply logger settings if log disabling is enabled # Apply logger settings if log disabling is enabled
if config.get(CONF_DISABLE_BT_LOGS, False): if config.get(CONF_DISABLE_BT_LOGS, False):
# Disable all Bluetooth loggers that are not required # Disable all Bluetooth loggers that are not required
required_loggers = _get_required_loggers()
for logger in BTLoggers: for logger in BTLoggers:
if logger not in _required_loggers: if logger not in required_loggers:
add_idf_sdkconfig_option(f"{logger.value}_NONE", True) add_idf_sdkconfig_option(f"{logger.value}_NONE", True)
# Set BLE connection establishment timeout to match aioesphomeapi/bleak-retry-connector # Set BLE connection establishment timeout to match aioesphomeapi/bleak-retry-connector

View File

@@ -60,11 +60,21 @@ class RegistrationCounts:
clients: int = 0 clients: int = 0
# Set to track which features are needed by components # CORE.data keys for state management
_required_features: set[BLEFeatures] = set() ESP32_BLE_TRACKER_REQUIRED_FEATURES_KEY = "esp32_ble_tracker_required_features"
ESP32_BLE_TRACKER_REGISTRATION_COUNTS_KEY = "esp32_ble_tracker_registration_counts"
# Track registration counts for StaticVector sizing
_registration_counts = RegistrationCounts() def _get_required_features() -> set[BLEFeatures]:
"""Get the set of required BLE features from CORE.data."""
return CORE.data.setdefault(ESP32_BLE_TRACKER_REQUIRED_FEATURES_KEY, set())
def _get_registration_counts() -> RegistrationCounts:
"""Get the registration counts from CORE.data."""
return CORE.data.setdefault(
ESP32_BLE_TRACKER_REGISTRATION_COUNTS_KEY, RegistrationCounts()
)
def register_ble_features(features: set[BLEFeatures]) -> None: def register_ble_features(features: set[BLEFeatures]) -> None:
@@ -73,7 +83,7 @@ def register_ble_features(features: set[BLEFeatures]) -> None:
Args: Args:
features: Set of BLEFeatures enum members features: Set of BLEFeatures enum members
""" """
_required_features.update(features) _get_required_features().update(features)
esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker") esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker")
@@ -267,15 +277,17 @@ async def to_code(config):
): ):
register_ble_features({BLEFeatures.ESP_BT_DEVICE}) register_ble_features({BLEFeatures.ESP_BT_DEVICE})
registration_counts = _get_registration_counts()
for conf in config.get(CONF_ON_BLE_ADVERTISE, []): for conf in config.get(CONF_ON_BLE_ADVERTISE, []):
_registration_counts.listeners += 1 registration_counts.listeners += 1
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if CONF_MAC_ADDRESS in conf: if CONF_MAC_ADDRESS in conf:
addr_list = [it.as_hex for it in conf[CONF_MAC_ADDRESS]] addr_list = [it.as_hex for it in conf[CONF_MAC_ADDRESS]]
cg.add(trigger.set_addresses(addr_list)) cg.add(trigger.set_addresses(addr_list))
await automation.build_automation(trigger, [(ESPBTDeviceConstRef, "x")], conf) await automation.build_automation(trigger, [(ESPBTDeviceConstRef, "x")], conf)
for conf in config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, []): for conf in config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, []):
_registration_counts.listeners += 1 registration_counts.listeners += 1
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if len(conf[CONF_SERVICE_UUID]) == len(bt_uuid16_format): if len(conf[CONF_SERVICE_UUID]) == len(bt_uuid16_format):
cg.add(trigger.set_service_uuid16(as_hex(conf[CONF_SERVICE_UUID]))) cg.add(trigger.set_service_uuid16(as_hex(conf[CONF_SERVICE_UUID])))
@@ -288,7 +300,7 @@ async def to_code(config):
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf)
for conf in config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, []): for conf in config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, []):
_registration_counts.listeners += 1 registration_counts.listeners += 1
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid16_format): if len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid16_format):
cg.add(trigger.set_manufacturer_uuid16(as_hex(conf[CONF_MANUFACTURER_ID]))) cg.add(trigger.set_manufacturer_uuid16(as_hex(conf[CONF_MANUFACTURER_ID])))
@@ -301,7 +313,7 @@ async def to_code(config):
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex)) cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf) await automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf)
for conf in config.get(CONF_ON_SCAN_END, []): for conf in config.get(CONF_ON_SCAN_END, []):
_registration_counts.listeners += 1 registration_counts.listeners += 1
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
@@ -331,19 +343,21 @@ async def to_code(config):
@coroutine_with_priority(CoroPriority.FINAL) @coroutine_with_priority(CoroPriority.FINAL)
async def _add_ble_features(): async def _add_ble_features():
# Add feature-specific defines based on what's needed # Add feature-specific defines based on what's needed
if BLEFeatures.ESP_BT_DEVICE in _required_features: required_features = _get_required_features()
if BLEFeatures.ESP_BT_DEVICE in required_features:
cg.add_define("USE_ESP32_BLE_DEVICE") cg.add_define("USE_ESP32_BLE_DEVICE")
cg.add_define("USE_ESP32_BLE_UUID") cg.add_define("USE_ESP32_BLE_UUID")
# Add defines for StaticVector sizing based on registration counts # Add defines for StaticVector sizing based on registration counts
# Only define if count > 0 to avoid allocating unnecessary memory # Only define if count > 0 to avoid allocating unnecessary memory
if _registration_counts.listeners > 0: registration_counts = _get_registration_counts()
if registration_counts.listeners > 0:
cg.add_define( cg.add_define(
"ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT", _registration_counts.listeners "ESPHOME_ESP32_BLE_TRACKER_LISTENER_COUNT", registration_counts.listeners
) )
if _registration_counts.clients > 0: if registration_counts.clients > 0:
cg.add_define( cg.add_define(
"ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT", _registration_counts.clients "ESPHOME_ESP32_BLE_TRACKER_CLIENT_COUNT", registration_counts.clients
) )
@@ -395,7 +409,7 @@ async def register_ble_device(
var: cg.SafeExpType, config: ConfigType var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType: ) -> cg.SafeExpType:
register_ble_features({BLEFeatures.ESP_BT_DEVICE}) register_ble_features({BLEFeatures.ESP_BT_DEVICE})
_registration_counts.listeners += 1 _get_registration_counts().listeners += 1
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_listener(var)) cg.add(paren.register_listener(var))
return var return var
@@ -403,7 +417,7 @@ async def register_ble_device(
async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExpType: async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExpType:
register_ble_features({BLEFeatures.ESP_BT_DEVICE}) register_ble_features({BLEFeatures.ESP_BT_DEVICE})
_registration_counts.clients += 1 _get_registration_counts().clients += 1
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_client(var)) cg.add(paren.register_client(var))
return var return var
@@ -417,7 +431,7 @@ async def register_raw_ble_device(
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
will not be compiled in if this is the only registration method used. will not be compiled in if this is the only registration method used.
""" """
_registration_counts.listeners += 1 _get_registration_counts().listeners += 1
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_listener(var)) cg.add(paren.register_listener(var))
return var return var
@@ -431,7 +445,7 @@ async def register_raw_client(
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
will not be compiled in if this is the only registration method used. will not be compiled in if this is the only registration method used.
""" """
_registration_counts.clients += 1 _get_registration_counts().clients += 1
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID]) paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_client(var)) cg.add(paren.register_client(var))
return var return var

View File

@@ -143,7 +143,18 @@ def validate_mclk_divisible_by_3(config):
return config return config
_use_legacy_driver = None # Key for storing legacy driver setting in CORE.data
I2S_USE_LEGACY_DRIVER_KEY = "i2s_use_legacy_driver"
def _get_use_legacy_driver():
"""Get the legacy driver setting from CORE.data."""
return CORE.data.get(I2S_USE_LEGACY_DRIVER_KEY)
def _set_use_legacy_driver(value: bool) -> None:
"""Set the legacy driver setting in CORE.data."""
CORE.data[I2S_USE_LEGACY_DRIVER_KEY] = value
def i2s_audio_component_schema( def i2s_audio_component_schema(
@@ -209,17 +220,15 @@ async def register_i2s_audio_component(var, config):
def validate_use_legacy(value): def validate_use_legacy(value):
global _use_legacy_driver # noqa: PLW0603
if CONF_USE_LEGACY in value: if CONF_USE_LEGACY in value:
if (_use_legacy_driver is not None) and ( existing_value = _get_use_legacy_driver()
_use_legacy_driver != value[CONF_USE_LEGACY] if (existing_value is not None) and (existing_value != value[CONF_USE_LEGACY]):
):
raise cv.Invalid( raise cv.Invalid(
f"All i2s_audio components must set {CONF_USE_LEGACY} to the same value." f"All i2s_audio components must set {CONF_USE_LEGACY} to the same value."
) )
if (not value[CONF_USE_LEGACY]) and (CORE.using_arduino): if (not value[CONF_USE_LEGACY]) and (CORE.using_arduino):
raise cv.Invalid("Arduino supports only the legacy i2s driver") raise cv.Invalid("Arduino supports only the legacy i2s driver")
_use_legacy_driver = value[CONF_USE_LEGACY] _set_use_legacy_driver(value[CONF_USE_LEGACY])
return value return value
@@ -249,7 +258,8 @@ def _final_validate(_):
def use_legacy(): def use_legacy():
return not (CORE.using_esp_idf and not _use_legacy_driver) legacy_driver = _get_use_legacy_driver()
return not (CORE.using_esp_idf and not legacy_driver)
FINAL_VALIDATE_SCHEMA = _final_validate FINAL_VALIDATE_SCHEMA = _final_validate

View File

@@ -202,7 +202,11 @@ template<typename T> class FixedVector {
~FixedVector() { cleanup_(); } ~FixedVector() { cleanup_(); }
// Enable move semantics for use in containers // Disable copy operations (avoid accidental expensive copies)
FixedVector(const FixedVector &) = delete;
FixedVector &operator=(const FixedVector &) = delete;
// Enable move semantics (allows use in move-only containers like std::vector)
FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) { FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
other.reset_(); other.reset_();
} }

View File

@@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
esptool==5.1.0 esptool==5.1.0
click==8.1.7 click==8.1.7
esphome-dashboard==20251013.0 esphome-dashboard==20251013.0
aioesphomeapi==41.16.0 aioesphomeapi==41.16.1
zeroconf==0.148.0 zeroconf==0.148.0
puremagic==1.30 puremagic==1.30
ruamel.yaml==0.18.15 # dashboard_import ruamel.yaml==0.18.15 # dashboard_import

View File

@@ -1415,6 +1415,8 @@ class RepeatedTypeInfo(TypeInfo):
# Check if this is a pointer field by looking for container_pointer option # Check if this is a pointer field by looking for container_pointer option
self._container_type = get_field_opt(field, pb.container_pointer, "") self._container_type = get_field_opt(field, pb.container_pointer, "")
self._use_pointer = bool(self._container_type) self._use_pointer = bool(self._container_type)
# Check if this should use FixedVector instead of std::vector
self._use_fixed_vector = get_field_opt(field, pb.fixed_vector, False)
# For repeated fields, we need to get the base type info # For repeated fields, we need to get the base type info
# but we can't call create_field_type_info as it would cause recursion # but we can't call create_field_type_info as it would cause recursion
@@ -1438,6 +1440,8 @@ class RepeatedTypeInfo(TypeInfo):
if "<" in self._container_type and ">" in self._container_type: if "<" in self._container_type and ">" in self._container_type:
return f"const {self._container_type}*" return f"const {self._container_type}*"
return f"const {self._container_type}<{self._ti.cpp_type}>*" return f"const {self._container_type}<{self._ti.cpp_type}>*"
if self._use_fixed_vector:
return f"FixedVector<{self._ti.cpp_type}>"
return f"std::vector<{self._ti.cpp_type}>" return f"std::vector<{self._ti.cpp_type}>"
@property @property