| # Copyright 2023 The TensorFlow Authors. All Rights Reserved. | |
| # | |
| # 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. | |
| # ============================================================================== | |
| """ Utility functions and classes for code generation. """ | |
| from typing import Any, Generator, Iterable, List, Optional, Sequence, Tuple | |
| import string | |
| import textwrap | |
| import itertools | |
| def to_pascal_case(s: str) -> str: | |
| """ Basic function for converting snake_case to PascalCase. """ | |
| # This isn't perfect, as there might be some cases where we want underscores | |
| # to remain if they are used as number separators. | |
| return s.title().replace('_', '') | |
| def bool_to_c_str(b: bool) -> str: | |
| """ Convert a python bool value to a C bool string. Ie, False -> 'false' """ | |
| return str(b).lower() | |
| def split_into_chunks( | |
| data: Iterable[Any], | |
| chunk_size: int) -> Generator[Tuple[Any, ...], None, None]: | |
| """Splits an iterable into chunks of a given size.""" | |
| data_iterator = iter(data) | |
| while True: | |
| chunk = tuple(itertools.islice(data_iterator, chunk_size)) | |
| if not chunk: | |
| break | |
| yield chunk | |
| def generate_c_int_array(indent: str, int_type: str, name: str, | |
| ints: Sequence[int]) -> str: | |
| int_strs = ['{}'.format(i) for i in ints] | |
| # Try to do it on a single line first | |
| single_line_array_template = string.Template( | |
| "constexpr ${int_type} ${name}[${size}] = {${data}};") | |
| single_line = textwrap.indent( | |
| single_line_array_template.substitute(int_type=int_type, | |
| name=name, | |
| size=len(int_strs), | |
| data=', '.join(int_strs)), indent) | |
| if len(single_line) < 81: | |
| return single_line | |
| # Couldn't fit, so split it across multiple lines | |
| multi_line_array_template = string.Template( | |
| "constexpr ${int_type} ${name}[${size}] = {\n" | |
| "${body}\n" | |
| "};\n") | |
| lines = [] | |
| for int_strs_for_line in split_into_chunks(int_strs, 12): | |
| ints_segment = ', '.join(int_strs_for_line) | |
| lines.append(f' {ints_segment},') | |
| return textwrap.indent( | |
| multi_line_array_template.substitute(int_type=int_type, | |
| name=name, | |
| size=len(ints), | |
| body='\n'.join(lines)), indent) | |
| class IntArray(object): | |
| """ A helper class for generating int arrays that can be used to provide the | |
| backing storage for a TfLiteIntArray. """ | |
| def __init__(self, data: List[int]): | |
| self._data = data | |
| def generate_c_struct(self, type_name: str, | |
| variable_name: Optional[str]) -> str: | |
| struct_template = string.Template("struct ${type_name} {\n" | |
| " int size = ${size};\n" | |
| " int data[${size}] = {${data}};\n" | |
| "}") | |
| # TODO(rjascani): Make this pretty print in multi-line chunks | |
| int_strs = ['{}'.format(i) for i in self._data] | |
| c_struct_str = struct_template.substitute(type_name=type_name, | |
| size=len(int_strs), | |
| data=', '.join(int_strs)) | |
| if variable_name: | |
| return c_struct_str + " {};".format(variable_name) | |
| return c_struct_str + ";" |