| # 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 + ";" |