/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 *
 * @format
 * @oncall react_native
 */

"use strict";

// The magic number is used as a header for bytecode.
// It represents a Metro tunnel in binary.
//
// 11111111
// 11100111
// 11000011
// 11000011
const MAGIC_NUMBER = 0xffe7c3c3;
function getFileHeader(moduleCount) {
  const buffer = Buffer.alloc(8);
  buffer.writeUInt32LE(MAGIC_NUMBER, 0);
  buffer.writeUInt32LE(moduleCount, 4);
  return buffer;
}
function addModuleHeader(buffer) {
  const { getFileLength } = require("metro-hermes-compiler");
  const fileLength = getFileLength(buffer, 0);
  const header = Buffer.alloc(4);
  header.writeUInt32LE(fileLength, 0);
  return [header, buffer];
}

/**
 * A bytecode bundle has the following format:
 *
 * 4 bytes MAGIC_NUMBER
 * 4 bytes Module count
 * 4 bytes Module length N + N bytes
 * ...
 *
 */
function bundleToBytecode(bundle) {
  const buffers = [];
  if (bundle.pre.length) {
    buffers.push(...bundle.pre);
  }
  const modules = [];
  const sortedModules = bundle.modules
    .slice()
    // In a JS bundle, the order of modules needs to be deterministic for source
    // maps to work. This constraint is not necessary for bytecode bundles but
    // is kept for consistency.
    .sort((a, b) => a[0] - b[0]);
  for (const [id, bytecodeBundle] of sortedModules) {
    buffers.push(...bytecodeBundle);
    // Use the size of the last item in `bytecodeBundle` which is always
    // the actual module without headers.
    modules.push([id, bytecodeBundle[bytecodeBundle.length - 1].length]);
  }
  if (bundle.post.length) {
    buffers.push(...bundle.post);
  }
  return {
    bytecode: Buffer.concat([
      getFileHeader(buffers.length),
      ...buffers.flatMap(addModuleHeader),
    ]),
    metadata: {
      pre: bundle.pre ? bundle.pre.length : 0,
      post: bundle.post.length,
      modules,
    },
  };
}
bundleToBytecode.MAGIC_NUMBER = MAGIC_NUMBER;
module.exports = bundleToBytecode;
