1# Lint as: python2, python3 2# Copyright 2020 The TensorFlow Authors. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# ============================================================================== 16"""Simple script to convert binary file to C++ source code for embedding.""" 17 18# This is a version of //tensorflow/lite/python/convert_file_to_c_source.py 19# with minimal dependencies to reduce build times. See b/158254039. 20 21from __future__ import absolute_import 22from __future__ import division 23from __future__ import print_function 24 25import argparse 26import datetime 27import sys 28 29 30# Cribbed from //tensorflow/lite/python/util.py 31# Changed: 32# - Alignment from 4 to 16 for generality (16 can be required for SIMD) 33# - Added 'extern' to source for building on C++ target platforms 34# - Changed comments to refer to this script, and C++ rather than C 35def _convert_bytes_to_cc_source(data, 36 array_name, 37 max_line_width=80, 38 include_guard=None, 39 include_path=None, 40 use_tensorflow_license=False): 41 """Returns strings representing a C++ constant array containing `data`. 42 43 Args: 44 data: Byte array that will be converted into a C++ constant. 45 array_name: String to use as the variable name for the constant array. 46 max_line_width: The longest line length, for formatting purposes. 47 include_guard: Name to use for the include guard macro definition. 48 include_path: Optional path to include in the source file. 49 use_tensorflow_license: Whether to include the standard TensorFlow Apache2 50 license in the generated files. 51 52 Returns: 53 Text that can be compiled as a C++ source file to link in the data as a 54 literal array of values. 55 Text that can be used as a C++ header file to reference the literal array. 56 """ 57 58 starting_pad = " " 59 array_lines = [] 60 array_line = starting_pad 61 for value in bytearray(data): 62 if (len(array_line) + 4) > max_line_width: 63 array_lines.append(array_line + "\n") 64 array_line = starting_pad 65 array_line += " 0x%02x," % value 66 if len(array_line) > len(starting_pad): 67 array_lines.append(array_line + "\n") 68 array_values = "".join(array_lines) 69 70 if include_guard is None: 71 include_guard = "TENSORFLOW_LITE_UTIL_" + array_name.upper() + "_DATA_H_" 72 73 if include_path is not None: 74 include_line = "#include \"{include_path}\"\n".format( 75 include_path=include_path) 76 else: 77 include_line = "" 78 79 if use_tensorflow_license: 80 license_text = """ 81/* Copyright {year} The TensorFlow Authors. All Rights Reserved. 82 83Licensed under the Apache License, Version 2.0 (the "License"); 84you may not use this file except in compliance with the License. 85You may obtain a copy of the License at 86 87 http://www.apache.org/licenses/LICENSE-2.0 88 89Unless required by applicable law or agreed to in writing, software 90distributed under the License is distributed on an "AS IS" BASIS, 91WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 92See the License for the specific language governing permissions and 93limitations under the License. 94==============================================================================*/ 95""".format(year=datetime.date.today().year) 96 else: 97 license_text = "" 98 99 source_template = """{license_text} 100// This is a binary file that has been converted into a C++ data array using the 101// //tensorflow/lite/experimental/acceleration/compatibility/convert_binary_to_cc_source.py 102// script. This form is useful for compiling into a binary to simplify 103// deployment on mobile devices 104 105{include_line} 106// We need to keep the data array aligned on some architectures. 107#ifdef __has_attribute 108#define HAVE_ATTRIBUTE(x) __has_attribute(x) 109#else 110#define HAVE_ATTRIBUTE(x) 0 111#endif 112#if HAVE_ATTRIBUTE(aligned) || (defined(__GNUC__) && !defined(__clang__)) 113#define DATA_ALIGN_ATTRIBUTE __attribute__((aligned(16))) 114#else 115#define DATA_ALIGN_ATTRIBUTE 116#endif 117 118extern const unsigned char {array_name}[] DATA_ALIGN_ATTRIBUTE = {{ 119{array_values}}}; 120extern const int {array_name}_len = {array_length}; 121""" 122 123 source_text = source_template.format( 124 array_name=array_name, 125 array_length=len(data), 126 array_values=array_values, 127 license_text=license_text, 128 include_line=include_line) 129 130 header_template = """ 131{license_text} 132 133// This is a binary file that has been converted into a C++ data array using the 134// //tensorflow/lite/experimental/acceleration/compatibility/convert_binary_to_cc_source.py 135// script. This form is useful for compiling into a binary to simplify 136// deployment on mobile devices 137 138#ifndef {include_guard} 139#define {include_guard} 140 141extern const unsigned char {array_name}[]; 142extern const int {array_name}_len; 143 144#endif // {include_guard} 145""" 146 147 header_text = header_template.format( 148 array_name=array_name, 149 include_guard=include_guard, 150 license_text=license_text) 151 152 return source_text, header_text 153 154 155def main(): 156 parser = argparse.ArgumentParser( 157 description=("Binary to C++ source converter")) 158 parser.add_argument( 159 "--input_binary_file", 160 type=str, 161 help="Full filepath of input binary.", 162 required=True) 163 parser.add_argument( 164 "--output_header_file", 165 type=str, 166 help="Full filepath of output header.", 167 required=True) 168 parser.add_argument( 169 "--array_variable_name", 170 type=str, 171 help="Full filepath of output source.", 172 required=True) 173 parser.add_argument( 174 "--output_source_file", 175 type=str, 176 help="Name of global variable that will contain the binary data.", 177 required=True) 178 flags, _ = parser.parse_known_args(args=sys.argv[1:]) 179 with open(flags.input_binary_file, "rb") as input_handle: 180 input_data = input_handle.read() 181 182 source, header = _convert_bytes_to_cc_source( 183 data=input_data, 184 array_name=flags.array_variable_name, 185 use_tensorflow_license=True) 186 187 with open(flags.output_source_file, "w") as source_handle: 188 source_handle.write(source) 189 190 with open(flags.output_header_file, "w") as header_handle: 191 header_handle.write(header) 192 193 194if __name__ == "__main__": 195 main() 196