#!/usr/bin/env python3
# Copyright 2026 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Generates a brotli-compressed version of a libphonenumber metadata source.

Upstream libphonenumber ships its serialized metadata as a large uncompressed
``static const unsigned char data[]`` array (e.g. lite_metadata.cc). That array
lands uncompressed in .rodata. This script extracts those bytes, brotli-
compresses them, and emits a .cc that keeps the (much smaller) compressed blob
in the binary and reconstitutes the original bytes lazily on first use via the
shared helper in phonenumber_brotli.{h,cc}.

The generated accessor keeps the exact upstream signature
(``MetadataBytes <fn>()``) so the rest of the library is unchanged. The
returned MetadataBytes owns the decompressed buffer and frees it once the
caller (PhoneNumberUtil) has parsed it.
"""

import argparse
import os
import re
import subprocess
import sys


def _extract_bytes(source_text):
  match = re.search(r'data\[\]\s*=\s*\{(.*?)\}\s*;', source_text, re.DOTALL)
  if not match:
    raise ValueError('could not find a "data[] = { ... };" array')
  hex_bytes = re.findall(r'0[xX]([0-9A-Fa-f]{2})', match.group(1))
  if not hex_bytes:
    raise ValueError('found data[] but it contained no byte literals')
  return bytes(int(b, 16) for b in hex_bytes)


def main():
  parser = argparse.ArgumentParser()
  parser.add_argument('--input', required=True,
                      help='Path to the upstream metadata .cc file.')
  parser.add_argument('--output', required=True,
                      help='Path to the generated .cc file to write.')
  parser.add_argument('--brotli', required=True,
                      help='Path to the brotli host executable.')
  parser.add_argument('--header', required=True,
                      help='Library header declaring the accessor, e.g. '
                           '"phonenumbers/metadata.h".')
  parser.add_argument('--fn', required=True,
                      help='Accessor returning the metadata, e.g. GetMetadata.')
  parser.add_argument('--symbol', required=True,
                      help='C identifier for the embedded compressed array.')
  args = parser.parse_args()

  with open(args.input, 'r', encoding='utf-8') as f:
    raw = _extract_bytes(f.read())

  # GN passes the brotli path relative to the build directory (this script's
  # cwd). When the host toolchain is the default toolchain that can be a bare
  # filename with no directory component, which subprocess would look up on
  # PATH instead of in the build directory; resolve it to an absolute path.
  brotli = os.path.abspath(args.brotli)
  compressed = subprocess.run(
      [brotli, '-', '-f', '-q', '11'],
      input=raw, stdout=subprocess.PIPE, check=True).stdout

  encoded_bytes = '\n'.join(
      '    ' + ' '.join(f'0x{byte:02X},' for byte in compressed[i:i + 16])
      for i in range(0, len(compressed), 16))

  # The generated GetMetadata() returns an owning MetadataBytes whose
  # destructor frees the decompressed bytes once the caller has finished
  # parsing them.
  contents = f'''\
// Generated by //third_party/libphonenumber/gen_compressed_metadata.py.
// Do not edit. Source: {args.input}

#include <cstddef>

#include "{args.header}"
#include "third_party/libphonenumber/phonenumber_brotli.h"

namespace i18n::phonenumbers {{
namespace {{

constexpr int {args.symbol}UncompressedSize = {len(raw)};
const unsigned char {args.symbol}[] = {{
{encoded_bytes}
}};

}}  // namespace

MetadataBytes {args.fn}() {{
  return PhonenumberBrotliDecompress({args.symbol}, sizeof({args.symbol}),
                                     {args.symbol}UncompressedSize);
}}

}}  // namespace i18n::phonenumbers
'''

  with open(args.output, 'w', encoding='utf-8') as f:
    f.write(contents)

  return 0


if __name__ == '__main__':
  sys.exit(main())
