def _generate_policy_map_items(self, policies_map: PoliciesMap) -> str: policy_map_items = '' for name, rules in sorted(policies_map.items(), key=lambda x: normalize_name(x[0])): policy_map_items += self.ZONE_POLICY_MAP_ITEM.format( policyName=normalize_name(name)) return policy_map_items
def _generate_link_item(self, link_name: str, zone_name: str) -> str: return self.ZONE_INFOS_CPP_LINK_ITEM.format( scope=self.scope, linkFullName=link_name, linkNormalizedName=normalize_name(link_name), zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name))
def _generate_info_map_items(self, zones_map: ZonesMap) -> str: """Generate a map of (zone_name -> zoneInfo), shorted by name. """ info_map_items = '' for zone_name, zones in sorted(zones_map.items(), key=lambda x: normalize_name(x[0])): info_map_items += self.ZONE_INFO_MAP_ITEM.format( zoneNormalizedName=normalize_name(zone_name), zoneFullName=zone_name) return info_map_items
def _generate_link_item( self, link_name: str, zone_name: str, ) -> str: """Return the Link item. """ ZONE_INFOS_CPP_LINK_ITEM = """\ //--------------------------------------------------------------------------- // Link name: {linkFullName} -> {zoneFullName} // Strings (bytes): {stringSize} (originally {originalSize}) // Memory (8-bit): {memory8} // Memory (32-bit): {memory32} //--------------------------------------------------------------------------- static const char kZoneName{linkNormalizedName}[] {progmem} = \ {compressedName}; const {scope}::ZoneInfo kZone{linkNormalizedName} {progmem} = {{ kZoneName{linkNormalizedName} /*name*/, 0x{linkId:08x} /*zoneId*/, &kZoneContext /*zoneContext*/, {numEras} /*numEras*/, kZoneEra{zoneNormalizedName} /*eras*/, }}; """ compressed_name = self.compressed_names[link_name] rendered_name = _compressed_name_to_c_string(compressed_name) link_name_size = len(compressed_name) + 1 original_size = len(link_name) + 1 memory8 = link_name_size + self.SIZEOF_ZONE_INFO_8 memory32 = link_name_size + self.SIZEOF_ZONE_INFO_32 link_item = ZONE_INFOS_CPP_LINK_ITEM.format( scope=self.scope, linkFullName=link_name, linkNormalizedName=normalize_name(link_name), compressedName=rendered_name, linkId=self.link_ids[link_name], zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name), stringSize=link_name_size, originalSize=original_size, memory8=memory8, memory32=memory32, numEras=len(self.zones_map[zone_name]), progmem='ACE_TIME_PROGMEM', ) return link_item
def _generate_info_item( self, zone_name: str, eras: List[ZoneEraRaw], ) -> Tuple[str, int]: era_items = '' string_length = 0 for era in eras: (era_item, length) = self._generate_era_item(zone_name, era) era_items += era_item string_length += length string_length += len(zone_name) + 1 num_eras = len(eras) memory8 = (string_length + num_eras * self.SIZEOF_ZONE_ERA_8 + 1 * self.SIZEOF_ZONE_INFO_8) memory32 = (string_length + num_eras * self.SIZEOF_ZONE_ERA_32 + 1 * self.SIZEOF_ZONE_INFO_32) transition_buf_size = self.buf_sizes[zone_name] info_item = self.ZONE_INFOS_CPP_INFO_ITEM.format( scope=self.scope, zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name), zoneId=self.zone_ids[zone_name], transitionBufSize=transition_buf_size, numEras=num_eras, stringLength=string_length, memory8=memory8, memory32=memory32, eraItems=era_items, progmem='ACE_TIME_PROGMEM') return (info_item, string_length)
def generate_policies_h(self) -> str: policy_items = '' for name, rules in sorted(self.policies_map.items()): policy_items += self.ZONE_POLICIES_H_POLICY_ITEM.format( policyName=normalize_name(name), scope=self.scope) removed_policy_items = '' for name, reasons in sorted(self.removed_policies.items()): removed_policy_items += \ self.ZONE_POLICIES_H_REMOVED_POLICY_ITEM.format( policyName=name, policyReason=', '.join(reasons)) notable_policy_items = '' for name, reasons in sorted(self.notable_policies.items()): notable_policy_items += \ self.ZONE_POLICIES_H_NOTABLE_POLICY_ITEM.format( policyName=name, policyReason=', '.join(reasons)) return self.ZONE_POLICIES_H_FILE.format( invocation=self.invocation, tz_files=self.tz_files, tz_version=self.tz_version, dbNamespace=self.db_namespace, dbHeaderNamespace=self.db_header_namespace, numPolicies=len(self.policies_map), policyItems=policy_items, numRemovedPolicies=len(self.removed_policies), removedPolicyItems=removed_policy_items, numNotablePolicies=len(self.notable_policies), notablePolicyItems=notable_policy_items)
def _generate_test_cases(self, test_data: TestData) -> str: has_valid_abbrev = self.validation_data['has_valid_abbrev'] has_valid_dst = self.validation_data['has_valid_dst'] test_cases = '' for zone_name, _ in sorted(test_data.items()): normalized_name = normalize_name(zone_name) ( dst_validation_scope, dst_validation_comment, ) = _get_validation_scope( has_valid_dst, self.blacklist.get(zone_name), ) ( abbrev_validation_scope, abbrev_validation_comment, ) = _get_validation_scope( has_valid_abbrev, self.blacklist.get(zone_name), ) test_case = f"""\ testF({self.test_class}, {normalized_name}) {{ assertValid( &kZone{normalized_name}, &kValidationData{normalized_name}, {dst_validation_scope} /*dstValidationScope{dst_validation_comment}*/, {abbrev_validation_scope} \ /*abbrevValidationScope{abbrev_validation_comment}*/); }} """ test_cases += test_case return test_cases
def _generate_infos(self) -> None: for zone_name, eras in self.zones_map.items(): zone_eras: List[ZoneEra] = [] for era in eras: policy_name = era['rules'] zone_policy: Union[ZonePolicy, str] if policy_name in ['-', ':']: zone_policy = policy_name else: policy_name = normalize_name(policy_name) zone_policy = self.zone_policies[policy_name] # yapf: disable zone_eras.append({ 'offset_seconds': era['offset_seconds_truncated'], 'zone_policy': zone_policy, 'rules_delta_seconds': era['rules_delta_seconds_truncated'], 'format': era['format'], 'until_year': era['until_year'], 'until_month': era['until_month'], 'until_day': era['until_day'], 'until_seconds': era['until_seconds_truncated'], 'until_time_suffix': era['until_time_suffix'], }) # yapf: enable self.zone_infos[zone_name] = {'name': zone_name, 'eras': zone_eras}
def _generate_validation_data_cpp_items(self, test_data: TestData) -> str: validation_items = '' for zone_name, test_items in sorted(test_data.items()): test_items_string = self._generate_validation_data_cpp_test_items( zone_name, test_items) normalized_name = normalize_name(zone_name) validation_item = f"""\ //--------------------------------------------------------------------------- // Zone name: {zone_name} //--------------------------------------------------------------------------- static const testing::ValidationItem kValidationItems{normalized_name}[] = {{ // epoch, utc, dst, y, m, d, h, m, s, abbrev, type {test_items_string} }}; const testing::ValidationData kValidationData{normalized_name} = {{ {len(test_items)} /*numItems*/, kValidationItems{normalized_name} /*items*/, }}; """ validation_items += validation_item return validation_items
def _generate_validation_data_h_items(self, test_data: TestData) -> str: validation_items = '' for zone_name, test_items in sorted(test_data.items()): validation_items += self.VALIDATION_DATA_H_ITEM.format( validationDataClass=self.validation_data_class, zoneNormalizedName=normalize_name(zone_name)) return validation_items
def _generate_validation_data_h_items(self, test_data: TestData) -> str: validation_items = '' for zone_name, test_items in sorted(test_data.items()): normalized_name = normalize_name(zone_name) validation_items += f"""\ extern const testing::ValidationData kValidationData{normalized_name}; """ return validation_items
def _generate_notable_policy_items( self, notable_policies: CommentsMap, ) -> str: notable_policy_items = '' for name, reason in sorted(notable_policies.items()): notable_policy_items += (self.ZONE_NOTABLE_POLICY_ITEM.format( policyName=normalize_name(name), policyReason=reason)) return notable_policy_items
def _generate_removed_policy_items( self, removed_policies: CommentsMap, ) -> str: removed_policy_items = '' for name, reason in sorted(removed_policies.items()): removed_policy_items += (self.ZONE_REMOVED_POLICY_ITEM.format( policyName=normalize_name(name), policyReason=reason)) return removed_policy_items
def _generate_test_cases(self, test_data: TestData) -> str: test_cases = '' for zone_name, _ in sorted(test_data.items()): test_case = self.TEST_CASE.format( dbNamespace=self.db_namespace, testClass=self.test_class, zoneNormalizedName=normalize_name(zone_name)) test_cases += test_case return test_cases
def _generate_policy_item(self, name: str, rules: List[ZoneRuleRaw]) -> str: rule_items = '' for rule in rules: rule_items += self.ZONE_RULE_ITEM.format( policyName=normalize_name(name), raw_line=normalize_raw(rule['raw_line']), from_year=rule['from_year'], to_year=rule['to_year'], in_month=rule['in_month'], on_day_of_week=rule['on_day_of_week'], on_day_of_month=rule['on_day_of_month'], at_seconds=rule['at_seconds_truncated'], at_time_suffix=rule['at_time_suffix'], delta_seconds=rule['delta_seconds_truncated'], letter=rule['letter']) return self.ZONE_POLICY_ITEM.format(policyName=normalize_name(name), numRules=len(rules), ruleItems=rule_items)
def _generate_validation_data_cpp_items(self, test_data: TestData) -> str: validation_items = '' for zone_name, test_items in sorted(test_data.items()): test_items_string = self._generate_validation_data_cpp_test_items( zone_name, test_items) validation_item = self.VALIDATION_DATA_CPP_ITEM.format( validationDataClass=self.validation_data_class, zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name), testItems=test_items_string) validation_items += validation_item return validation_items
def _generate_info_item( self, zone_name: str, eras: List[ZoneEraRaw], ) -> str: era_items = '' for era in eras: era_items += self._generate_era_item(era) return self.ZONE_INFO_ITEM.format( zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name), numEras=len(eras), eraItems=era_items)
def generate_registry_cpp(self) -> str: zone_registry_items = '' for zone_name, eras in sorted(self.zones_map.items()): name = normalize_name(zone_name) zone_registry_items += f' &kZone{name}, // {zone_name}\n' return self.ZONE_REGISTRY_CPP_FILE.format( invocation=self.invocation, tz_files=self.tz_files, tz_version=self.tz_version, scope=self.scope, dbNamespace=self.db_namespace, dbHeaderNamespace=self.db_header_namespace, numZones=len(self.zones_map), zoneRegistryItems=zone_registry_items, progmem='ACE_TIME_PROGMEM')
def _generate_test_cases(self, test_data: TestData) -> str: has_valid_abbrev = self.validation_data['has_valid_abbrev'] has_valid_dst = self.validation_data['has_valid_dst'] test_cases = '' for zone_name, _ in sorted(test_data.items()): normalized_name = normalize_name(zone_name) ( dst_validation_scope, dst_validation_comment, ) = _get_validation_scope( has_valid_dst, self.blacklist.get(zone_name), ) ( abbrev_validation_scope, abbrev_validation_comment, ) = _get_validation_scope( has_valid_abbrev, self.blacklist.get(zone_name), ) zone_key = ( f'&kZone{normalized_name}' if self.zone_key_type == 'zoneInfo' else f'0x{hash_name(zone_name):08x}' ) test_case = f"""\ testF({self.test_class}, {normalized_name}) {{ assertValid( {zone_key}, &kValidationData{normalized_name}, {dst_validation_scope} /*dstValidationScope{dst_validation_comment}*/, {abbrev_validation_scope} \ /*abbrevValidationScope{abbrev_validation_comment}*/, kZoneBufSize{normalized_name} /*bufSize*/ ); }} """ test_cases += test_case return test_cases
def _generate_info_item( self, zone_name: str, eras: List[ZoneEraRaw], ) -> str: era_items = '' for era in eras: era_item = self._generate_era_item(zone_name, era) era_items += era_item compressed_name = self.compressed_names[zone_name] rendered_name = _compressed_name_to_c_string(compressed_name) # Calculate memory sizes zone_name_size = len(compressed_name) + 1 format_size = 0 for era in eras: format_size += len(era['format_short']) + 1 num_eras = len(eras) data_size8 = (num_eras * self.SIZEOF_ZONE_ERA_8 + self.SIZEOF_ZONE_INFO_8) data_size32 = (num_eras * self.SIZEOF_ZONE_ERA_32 + self.SIZEOF_ZONE_INFO_32) string_size = zone_name_size + format_size original_size = len(zone_name) + 1 + format_size info_item = self.ZONE_INFOS_CPP_INFO_ITEM.format( scope=self.scope, zoneFullName=zone_name, zoneNormalizedName=normalize_name(zone_name), compressedName=rendered_name, zoneId=self.zone_ids[zone_name], numEras=num_eras, stringSize=string_size, originalSize=original_size, memory8=data_size8 + string_size, memory32=data_size32 + string_size, eraItems=era_items, progmem='ACE_TIME_PROGMEM') return info_item
def _generate_policies(self) -> None: for name, rules in self.policies_map.items(): policy_rules: List[ZoneRule] = [] for rule in rules: # yapf: disable policy_rules.append({ 'from_year': rule['from_year'], 'to_year': rule['to_year'], 'in_month': rule['in_month'], 'on_day_of_week': rule['on_day_of_week'], 'on_day_of_month': rule['on_day_of_month'], 'at_seconds': rule['at_seconds_truncated'], 'at_time_suffix': rule['at_time_suffix'], 'delta_seconds': rule['delta_seconds_truncated'], 'letter': rule['letter'] }) # yapf: enable normalized_name = normalize_name(name) self.zone_policies[normalized_name] = { 'name': name, # policy name 'rules': policy_rules }
def generate_infos_h(self) -> str: info_items = '' info_zone_ids = '' for zone_name, eras in sorted(self.zones_map.items()): info_items += self.ZONE_INFOS_H_INFO_ITEM.format( scope=self.scope, zoneNormalizedName=normalize_name(zone_name), zoneFullName=zone_name, ) info_zone_ids += self.ZONE_INFOS_H_INFO_ZONE_ID.format( scope=self.scope, zoneNormalizedName=normalize_name(zone_name), zoneFullName=zone_name, zoneId=self.zone_ids[zone_name], ) removed_info_items = '' for zone_name, reasons in sorted(self.removed_zones.items()): removed_info_items += self.ZONE_INFOS_H_REMOVED_INFO_ITEM.format( zoneFullName=zone_name, reason=', '.join(reasons)) notable_info_items = '' for zone_name, reasons in sorted(self.notable_zones.items()): notable_info_items += self.ZONE_INFOS_H_NOTABLE_INFO_ITEM.format( zoneFullName=zone_name, reason=', '.join(reasons)) link_items = '' for link_name, zone_name in sorted(self.links_map.items()): link_items += self.ZONE_INFOS_H_LINK_ITEM.format( scope=self.scope, linkNormalizedName=normalize_name(link_name), linkFullName=link_name, zoneFullName=zone_name) removed_link_items = '' for link_name, reasons in sorted(self.removed_links.items()): removed_link_items += self.ZONE_INFOS_H_REMOVED_LINK_ITEM.format( linkFullName=link_name, reason=', '.join(reasons)) notable_link_items = '' for link_name, reasons in sorted(self.notable_links.items()): notable_link_items += self.ZONE_INFOS_H_NOTABLE_LINK_ITEM.format( linkFullName=link_name, reason=', '.join(reasons)) return self.ZONE_INFOS_H_FILE.format( invocation=self.invocation, tz_files=self.tz_files, tz_version=self.tz_version, scope=self.scope, dbNamespace=self.db_namespace, dbHeaderNamespace=self.db_header_namespace, numInfos=len(self.zones_map), infoItems=info_items, infoZoneIds=info_zone_ids, numLinks=len(self.links_map), linkItems=link_items, numRemovedInfos=len(self.removed_zones), removedInfoItems=removed_info_items, numNotableInfos=len(self.notable_zones), notableInfoItems=notable_info_items, numRemovedLinks=len(self.removed_links), removedLinkItems=removed_link_items, numNotableLinks=len(self.notable_links), notableLinkItems=notable_link_items)
def generate_registry_cpp(self) -> str: # Generate only Zones, sorted by zoneId to enable # ZoneRegistrar::binarySearchById(). zone_registry_items = '' for zone_name in sorted( self.zones_map.keys(), key=lambda x: self.zone_ids[x], ): normalized_name = normalize_name(zone_name) zone_id = self.zone_ids[zone_name] zone_registry_items += f"""\ &kZone{normalized_name}, // 0x{zone_id:08x}, {zone_name} """ # Generate Zones and Links, sorted by zoneId. zone_and_link_registry_items = '' num_zones_and_links = len(self.zones_and_links) for zone_name in sorted( self.zones_and_links, key=lambda x: self.zone_and_link_ids[x], ): normalized_name = normalize_name(zone_name) zone_id = self.zone_and_link_ids[zone_name] target_name = self.links_map.get(zone_name) if target_name: desc_name = f'{zone_name} -> {target_name}' else: desc_name = zone_name zone_and_link_registry_items += f"""\ &kZone{normalized_name}, // 0x{zone_id:08x}, {desc_name} """ # Generate Link table, sorted by linkId. link_registry_items = '' for link_name in sorted( self.links_map, key=lambda x: self.link_ids[x], ): zone_name = self.links_map[link_name] link_id = self.link_ids[link_name] zone_id = self.zone_ids[zone_name] normalized_link_name = normalize_name(link_name) normalized_zone_name = normalize_name(zone_name) link_registry_items += f"""\ {{ kZoneId{normalized_link_name}, kZoneId{normalized_zone_name} }}, \ // 0x{link_id:08x} -> 0x{zone_id:08x} """ return self.ZONE_REGISTRY_CPP_FILE.format( invocation=self.invocation, tz_files=self.tz_files, tz_version=self.tz_version, scope=self.scope, dbNamespace=self.db_namespace, dbHeaderNamespace=self.db_header_namespace, numZones=len(self.zones_map), numZonesAndLinks=num_zones_and_links, numLinks=len(self.links_map), zoneRegistryItems=zone_registry_items, zoneAndLinkRegistryItems=zone_and_link_registry_items, linkRegistryItems=link_registry_items, progmem='ACE_TIME_PROGMEM', )
def generate_infos_h(self) -> str: ZONE_INFOS_H_INFO_ITEM = """\ extern const {scope}::ZoneInfo kZone{zoneNormalizedName}; // {zoneFullName} """ ZONE_INFOS_H_INFO_ZONE_ID = """\ const uint32_t kZoneId{zoneNormalizedName} = 0x{zoneId:08x}; // {zoneFullName} """ ZONE_INFOS_H_BUF_SIZE = """\ const uint8_t kZoneBufSize{zoneNormalizedName} = {bufSize}; // {zoneFullName} """ info_items = '' info_zone_ids = '' info_buf_sizes = '' for zone_name, eras in sorted(self.zones_map.items()): normalized_name = normalize_name(zone_name) info_items += ZONE_INFOS_H_INFO_ITEM.format( scope=self.scope, zoneNormalizedName=normalized_name, zoneFullName=zone_name, ) info_zone_ids += ZONE_INFOS_H_INFO_ZONE_ID.format( zoneNormalizedName=normalized_name, zoneFullName=zone_name, zoneId=self.zone_ids[zone_name], ) info_buf_sizes += ZONE_INFOS_H_BUF_SIZE.format( zoneNormalizedName=normalized_name, zoneFullName=zone_name, bufSize=self.buf_sizes[zone_name], ) ZONE_INFOS_H_LINK_ITEM = """\ extern const {scope}::ZoneInfo kZone{linkNormalizedName}; \ // {linkFullName} -> {zoneFullName} """ ZONE_INFOS_H_LINK_ID = """\ const uint32_t kZoneId{linkNormalizedName} = 0x{linkId:08x}; // {linkFullName} """ link_items = '' link_ids = '' for link_name, zone_name in sorted(self.links_map.items()): link_items += ZONE_INFOS_H_LINK_ITEM.format( scope=self.scope, linkNormalizedName=normalize_name(link_name), linkFullName=link_name, zoneFullName=zone_name) link_ids += ZONE_INFOS_H_LINK_ID.format( linkNormalizedName=normalize_name(link_name), linkFullName=link_name, linkId=self.link_ids[link_name], ) ZONE_INFOS_H_REMOVED_INFO_ITEM = """\ // {zoneFullName} ({reason}) """ removed_info_items = '' for zone_name, reasons in sorted(self.removed_zones.items()): removed_info_items += ZONE_INFOS_H_REMOVED_INFO_ITEM.format( zoneFullName=zone_name, reason=', '.join(reasons)) ZONE_INFOS_H_NOTABLE_INFO_ITEM = """\ // {zoneFullName} ({reason}) """ notable_info_items = '' for zone_name, reasons in sorted(self.notable_zones.items()): notable_info_items += ZONE_INFOS_H_NOTABLE_INFO_ITEM.format( zoneFullName=zone_name, reason=', '.join(reasons)) ZONE_INFOS_H_REMOVED_LINK_ITEM = """\ // {linkFullName} ({reason}) """ removed_link_items = '' for link_name, reasons in sorted(self.removed_links.items()): removed_link_items += ZONE_INFOS_H_REMOVED_LINK_ITEM.format( linkFullName=link_name, reason=', '.join(reasons)) ZONE_INFOS_H_NOTABLE_LINK_ITEM = """\ // {linkFullName} ({reason}) """ notable_link_items = '' for link_name, reasons in sorted(self.notable_links.items()): notable_link_items += ZONE_INFOS_H_NOTABLE_LINK_ITEM.format( linkFullName=link_name, reason=', '.join(reasons)) return self.ZONE_INFOS_H_FILE.format( invocation=self.invocation, tz_files=self.tz_files, tz_version=self.tz_version, scope=self.scope, dbNamespace=self.db_namespace, dbHeaderNamespace=self.db_header_namespace, numInfos=len(self.zones_map), infoItems=info_items, zoneIds=info_zone_ids, numLinks=len(self.links_map), linkItems=link_items, linkIds=link_ids, numRemovedInfos=len(self.removed_zones), removedInfoItems=removed_info_items, numNotableInfos=len(self.notable_zones), notableInfoItems=notable_info_items, numRemovedLinks=len(self.removed_links), removedLinkItems=removed_link_items, numNotableLinks=len(self.notable_links), notableLinkItems=notable_link_items, bufSizes=info_buf_sizes, )
def _generate_policy_item( self, name: str, rules: List[ZoneRuleRaw], indexed_letters: Optional[IndexMap], ) -> Tuple[str, int, int]: ZONE_POLICIES_CPP_RULE_ITEM = """\ // {raw_line} {{ {from_year_tiny} /*fromYearTiny*/, {to_year_tiny} /*toYearTiny*/, {in_month} /*inMonth*/, {on_day_of_week} /*onDayOfWeek*/, {on_day_of_month} /*onDayOfMonth*/, {at_time_code} /*atTimeCode*/, {at_time_modifier} /*atTimeModifier ({at_time_modifier_comment})*/, {delta_code} /*deltaCode ({delta_code_comment})*/, {letter} /*letter{letterComment}*/, }}, """ ZONE_POLICIES_LETTER_ARRAY = """\ static const char* const kLetters{policyName}[] {progmem} = {{ {letterItems} }}; """ # Generate kZoneRules*[] rule_items = '' for rule in rules: at_time_code = rule['at_time_code'] at_time_modifier = rule['at_time_modifier'] at_time_modifier_comment = _get_time_modifier_comment( time_seconds=rule['at_seconds_truncated'], suffix=rule['at_time_suffix'], ) delta_code = rule['delta_code_encoded'] delta_code_comment = _get_rule_delta_code_comment( delta_seconds=rule['delta_seconds_truncated'], scope=self.scope, ) from_year_tiny = rule['from_year_tiny'] to_year_tiny = rule['to_year_tiny'] # Single-character 'letter' values are represented as themselves # using the C++ 'char' type ('A'-'Z'). But some 'letter' fields hold # a multi-character string. We can encode these multi-character # strings as an index into an array of NUL-terminated strings. # ASCII codes less than 32 (space) are non-printable control # characters so they will not collide with the printable characters # 'A' - 'Z'. Therefore we can hold to up to 31 multi-character # strings per-zone. In practice, for a single zone, the maximum # number of multi-character strings that I've seen is 2. letter = rule['letter'] if len(letter) == 1: letterComment = '' letter = f"'{letter}'" elif len(letter) > 1: letterComment = f' (index to "{letter}")' letter = str(rule['letter_index_per_policy']) else: raise Exception('len(%s) == 0; should not happen' % rule['letter']) rule_items += ZONE_POLICIES_CPP_RULE_ITEM.format( raw_line=normalize_raw(rule['raw_line']), from_year_tiny=from_year_tiny, to_year_tiny=to_year_tiny, in_month=rule['in_month'], on_day_of_week=rule['on_day_of_week'], on_day_of_month=rule['on_day_of_month'], at_time_code=at_time_code, at_time_modifier=at_time_modifier, at_time_modifier_comment=at_time_modifier_comment, delta_code=delta_code, delta_code_comment=delta_code_comment, letter=letter, letterComment=letterComment) # Generate kLetters*[] policy_name = normalize_name(name) num_letters = len(indexed_letters) if indexed_letters else 0 memory_letters8 = 0 memory_letters32 = 0 if num_letters: assert indexed_letters is not None letter_array_ref = f'kLetters{policy_name}' letterItems = '' for name, index in indexed_letters.items(): letterItems += f' /*{index}*/ "{name}",\n' memory_letters8 += len(name) + 1 + 2 # NUL terminated memory_letters32 += len(name) + 1 + 4 # NUL terminated letter_array = ZONE_POLICIES_LETTER_ARRAY.format( policyName=policy_name, letterItems=letterItems, progmem='ACE_TIME_PROGMEM') else: letter_array_ref = 'nullptr' letter_array = '' # Calculate the memory consumed by structs and arrays num_rules = len(rules) memory8 = (1 * self.SIZEOF_ZONE_POLICY_8 + num_rules * self.SIZEOF_ZONE_RULE_8 + memory_letters8) memory32 = (1 * self.SIZEOF_ZONE_POLICY_32 + num_rules * self.SIZEOF_ZONE_RULE_32 + memory_letters32) policy_item = self.ZONE_POLICIES_CPP_POLICY_ITEM.format( scope=self.scope, policyName=policy_name, numRules=num_rules, memory8=memory8, memory32=memory32, ruleItems=rule_items, numLetters=num_letters, letterArrayRef=letter_array_ref, letterArray=letter_array, progmem='ACE_TIME_PROGMEM') return (policy_item, memory8, memory32)