sentry-react-native icon indicating copy to clipboard operation
sentry-react-native copied to clipboard

Use CPP Base64 Encoder for better performance (>10x)

Open krystofwoldrich opened this issue 7 months ago • 2 comments

Description

Hermes offers builtin btoa, using it or vendoring react-native-quick-base64, will bring 10x+ improvement in the event processing time.

Things to consider

krystofwoldrich avatar Jun 03 '25 11:06 krystofwoldrich

There is also function utf8ToBytes that is very slow. This function could possibly use native TextEncoder which should speed it up significantly. Here is implementation I used (10x - 15x faster than JS implementation):

/**
 * Converts a UTF-8 string to an array of bytes using native TextEncoder.
 * This function is intended as a more performant alternative to the
 * utf8ToBytes function found in some Buffer polyfills.
 *
 * @param {string} string The string to convert.
 * @param {number} [unitsInput] Optional. Similar to the 'units' in the polyfill's
 * utf8ToBytes, this aims to limit the number of bytes.
 * If 0, undefined, null, or NaN, it's treated as Infinity
 * (no limit), mimicking the polyfill's `|| Infinity` behavior.
 * Otherwise, the output byte array will be truncated to this length.
 * @returns {number[]} An array of numbers, where each number represents a byte.
 */
export function utf8ToBytes(string, unitsInput) {
  let units;

  // Mimic the polyfill's `units = units || Infinity` behavior for common falsy values
  // that would lead to `Infinity` in the original.
  if (unitsInput === undefined ||
      unitsInput === null ||
      unitsInput === 0 || // Original `|| Infinity` makes 0 effectively Infinity
      (typeof unitsInput === 'number' && Number.isNaN(unitsInput))) {
    units = Infinity;
  } else {
    units = Number(unitsInput); // Coerce to number (e.g., if it was a string "10")
    if (Number.isNaN(units)) {  // If coercion results in NaN (e.g., from "abc")
      units = Infinity;
    }
  }

  const encoder = new TextEncoder(); // UTF-8 by default
  const uint8Array = encoder.encode(string);

  let finalUint8Array;

  if (units < 0) {
    // If units was specified as a negative number not caught by the Infinity conditions.
    // The polyfill would also likely break immediately or produce no bytes.
    finalUint8Array = new Uint8Array(0);
  } else if (units !== Infinity && uint8Array.length > units) {
    // Truncate if the encoded length is greater than the specified units.
    // Ensure units is not negative for slice, though above logic should handle most.
    finalUint8Array = uint8Array.slice(0, Math.max(0, units));
  } else {
    // No truncation needed or units is Infinity
    finalUint8Array = uint8Array;
  }

  return finalUint8Array;
}

Nodonisko avatar Jun 04 '25 11:06 Nodonisko

Totally agree. utf8ToBytes is too slow

huuphat1908 avatar Oct 07 '25 06:10 huuphat1908