[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/firebase/firebase-cpp-sdk/main/binary_to_array.py [Back]  [Original]

#!/usr/grte/v4/bin/python2.7

# Copyright 2016 Google LLC
#
# 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 to convert binary data into a C/C++ array.

Usage: %s --input=input_file.bin [--output_source=output_source.cc]
          [--output_header=output_header.h] [--cpp_namespace=namespace]
          [--header_guard=HEADER_GUARD_TEXT] [--array=array_c_identifier]
          [--array_size=array_size_c_identifier] [--filename=override_filename]
          [--filename_identifier=filename_c_identifier]

By default, the output source file will be named the same as the input file,
but with .cc as the extension; the output header file will be named the
same as the input file but with .h as the extension.

By default, the data will be in an array named $NAME_data and the size will
be in a constant named $NAME_length, and the filename will be stored in
$NAME_filename. In all these cases, $NAME is the input filename (sans path and
extension) with runs of non-alphanumeric characters changed to underscores. The
header guard will be generated from the output header filename in a similar way.

By default, the data will be placed in the root namespace. If the data is placed
in the root namespace, it will be declared as a C array (using extern "C" if
compiled in C++ mode).

The actual size of $NAME_data is $NAME_length + 1, where it contains an extra
0x00 at the end. When data is actually text, $NAME_data can be used as a valid C
string directly.
"""

from os import path
from re import sub
from absl import app
from absl import flags
from absl import logging

FLAGS = flags.FLAGS

flags.DEFINE_string("input", None, "Input file containing binary data to embed")
flags.DEFINE_string("output_source", None,
                    "Output source file, defining the array data.")
flags.DEFINE_string("output_header", None,
                    "Output header file, declaring the array data.")
flags.DEFINE_string("array", None, "Identifier for the array.")
flags.DEFINE_string("array_size", None, "Identifier for the array size.")
flags.DEFINE_string("filename", None, "Override file name in code.")
flags.DEFINE_string("filename_identifier", None, "Where to put the filename.")
flags.DEFINE_string("header_guard", None,
                    "Header guard to #define in the output header.")
flags.DEFINE_string("cpp_namespace", None,
                    "C++ namespace to use. If blank, will generate a C array.")

# How many hex bytes to display in a line. Each "0x00, " takes 6 characters, so
# a width of 12 lets us fit within 80 characters.
WIDTH = 12


class Error(Exception):
  """Exception raised by methods in this module."""
  pass


def header(header_guard, namespaces, array_name, array_size_name, fileid):
  """Return a C/C++ header for the given array.

  Args:
    header_guard: Name of the HEADER_GUARD to define.
    namespaces: List of namespaces, outer to inner.
    array_name: Name of the array.
    array_size_name: Name of the array size constant.
    fileid: Name of the identifier containing the file name.

  Returns:
    A list of strings containing the C/C++ header file, line-by-line.
  """

  data = []
  data.extend([
      "// Copyright 2016 Google Inc. All Rights Reserved.",
      "",
      "#ifndef %s" % header_guard,
      "#define %s" % header_guard,
      "",
      "#include ",
      ""
  ])
  if namespaces:
    data.extend([
        "namespace %s {" % ns for ns in namespaces
    ])
  else:
    data.extend([
        "#if defined(__cplusplus)",
        "extern \"C\" {",
        "#endif  // defined(__cplusplus)"])

  data.extend([
      "",
      "extern const size_t %s;" % array_size_name,
      "extern const unsigned char %s[];" % array_name,
      "extern const char %s[];" % fileid,
  ])

  data.extend([
      ""
  ])
  if namespaces:
    data.extend([
        "}  // namespace %s" % ns for ns in namespaces
    ][::-1])  # close namespaces in reverse order
  else:
    data.extend([
        "#if defined(__cplusplus)",
        "}  // extern \"C\"",
        "#endif  // defined(__cplusplus)"
    ])
  data.extend([
      "",
      "#endif  // %s" % header_guard,
      ""
  ])
  return data


def source(namespaces, array_name, array_size_name, fileid, filename,
           input_bytes):
  """Return a C/C++ source file for the given array.

  Args:
    namespaces: List of namespaces, outer to inner.
    array_name: Name of the array.
    array_size_name: Name of the array size constant.
    fileid: Name of the identifier containing the filename.
    filename: The original data filename itself.
    input_bytes: Binary data to put into the array.

  Returns:
    A string containing the C/C++ source file.
  """
  data = []
  data.extend([
      "// Copyright 2016 Google Inc. All Rights Reserved.",
      "",
      "#include ",
      ""
  ])
  if namespaces:
    data.extend([
        "namespace %s {" % ns for ns in namespaces
    ])
  else:
    data.extend([
        "#if defined(__cplusplus)",
        "extern \"C\" {",
        "#endif  // defined(__cplusplus)"])

  data.extend([
      "",
      "extern const size_t %s;" % array_size_name,
      "extern const char %s[];" % fileid,
      "extern const unsigned char %s[];" % array_name, "",
      "const unsigned char %s[] = {" % array_name
  ])
  length = len(input_bytes)
  line = ""
  for idx in range(0, length):
    if idx % WIDTH == 0:
      line += "  "
    else:
      line += " "
    line += "0x%02x," % input_bytes[idx]
    if idx % WIDTH == WIDTH - 1:
      data.append(line)
      line = ""
  data.append(line)
  data.append("  0x00  // Extra \\0 to make it a C string")

  data.extend([
      "};",
      "",
      "const size_t %s =" % array_size_name,
      "  sizeof(%s) - 1;" % array_name,
      "",
      "const char %s[] =" % fileid,
      "  \"%s\";" % filename,
      "",
  ])

  if namespaces:
    data.extend([
        "}  // namespace %s" % ns for ns in namespaces
    ][::-1])  # close namespaces in reverse order
  else:
    data.extend([
        "#if defined(__cplusplus)",
        "}  // extern \"C\"",
        "#endif  // defined(__cplusplus)"
    ])
  data.extend([
      ""
  ])
  return data


def main(unused_argv):
  """Read an binary input file and output to a C/C++ source file as an array.

  Raises:
    Error: If an input file is not specified.
  """

  input_file = FLAGS.input
  if not input_file:
    raise Error("No input file specified.")
  input_file_base = FLAGS.input.rsplit(".", 1)[0]

  output_source = FLAGS.output_source
  if not output_source:
    output_source = input_file_base + ".cc"
    logging.debug("Using default --output_source='%s'", output_source)

  output_header = FLAGS.output_header
  if not output_header:
    output_header = input_file_base + ".h"
    logging.debug("Using default --output_header='%s'", output_header)

  identifier_base = sub("[^0-9a-zA-Z]+", "_", path.basename(input_file_base))
  array_name = FLAGS.array
  if not array_name:
    array_name = identifier_base + "_data"
    logging.debug("Using default --array='%s'", array_name)

  array_size_name = FLAGS.array_size
  if not array_size_name:
    array_size_name = identifier_base + "_size"
    logging.debug("Using default --array_size='%s'", array_size_name)

  fileid = FLAGS.filename_identifier
  if not fileid:
    fileid = identifier_base + "_filename"
    logging.debug("Using default --filename_identifier='%s'", fileid)

  filename = FLAGS.filename
  if filename is None:  # but not if it's the empty string
    filename = path.basename(input_file)
    logging.debug("Using default --filename='%s'", filename)

  header_guard = FLAGS.header_guard
  if not header_guard:
    header_guard = sub("[^0-9a-zA-Z]+", "_", "_" + output_header).upper()
    logging.debug("Using default --header_guard='%s'", header_guard)

  namespace = FLAGS.cpp_namespace
  namespaces = namespace.split("::") if namespace else []

  with open(input_file, "rb") as infile:
    input_bytes = bytearray(infile.read())
    logging.debug("Read %d bytes from %s", len(input_bytes), input_file)

  header_text = "\n".join(header(header_guard, namespaces, array_name,
                                 array_size_name, fileid))
  source_text = "\n".join(source(namespaces, array_name, array_size_name,
                                 fileid, filename, input_bytes))

  with open(output_header, "w") as hdr:
    hdr.write(header_text)
    logging.debug("Wrote header file %s", output_header)

  with open(output_source, "w") as src:
    src.write(source_text)
    logging.debug("Wrote source file %s", output_source)

if __name__ == "__main__":
  app.run(main)

Web Proxy Viewer  |  New URL  |  Original Page