RegExp IPv4 parsing vs. string parsing (v3)

Revision 3 of this benchmark created on


Description

Conventional wisdom is that RegExps can be slow. Is that true in this case?

Setup

// RegExp
const v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])";
const v4Str = `(${v4Seg}[.]){3}${v4Seg}`;
const IPv4Reg = new RegExp(`^${v4Str}$`);

// Function equivalent
function isIPv4(s) {
  if (!s || s.length < 7 || s.length > 15) return false;

  const octets = s.split(".");
  if (octets.length !== 4) return false;

  for (const octet of octets) {
    const num = parseInt(octet, 10);
    // Check if it's a valid number and in range
    if (isNaN(num) || num < 0 || num > 255 || (octet.length > 1 && octet[0] === "0")) {
      // No leading zeros
      return false;
    }
  }

  return true;
}

// Alternate function equivalent
function isIPv4_V2(s) {
  if (s.length < 7 || s.length > 15) return false; // Quick length check

  let dots = 0;
  let val = 0;
  let lastWasDot = true; // Start shouldn't have a digit

  for (let i = 0; i < s.length; i++) {
    const c = s.charCodeAt(i);

    if (c === 46) {
      // '.' character
      if (lastWasDot || val > 255) return false;
      dots++;
      val = 0;
      lastWasDot = true;
    } else if (c >= 48 && c <= 57) {
      // '0'-'9' characters
      val = val * 10 + (c - 48);
      lastWasDot = false;
    } else {
      return false; // Invalid character
    }
  }
}

const fixtures = [
  ["10.5.0.1", "0.0.0.0/0", true],
  ["10.5.0.1", "11.0.0.0/8", false],
  ["10.5.0.1", "10.0.0.0/8", true],
  ["10.5.0.1", "10.0.0.1/8", true],
  ["10.5.0.1", "10.0.0.10/8", true],
  ["10.5.0.1", "10.5.5.0/16", true],
  ["10.5.0.1", "10.4.5.0/16", false],
  ["10.5.0.1", "10.4.5.0/15", true],
  ["10.5.0.1", "10.5.0.2/32", false],
  ["10.5.0.1", "10.5.0.1/32", true],
  ["192.168.0.100", "192.168.0.0/24", true],
  ["192.168.1.100", "192.168.0.0/24", false],
  ["0.0.0.0", "0.0.0.0/0", true],
  ["1.1.1.1", "0.0.0.0/0", true],
  ["2.3.4.5", "0.0.0.0/0", true],
  ["0.0.0.1", "0.0.0.2/32", false],
  ["0.0.0.2", "0.0.0.2/32", true],
  ["0.0.0.3", "0.0.0.2/32", false],
  ["172.29.255.255", "172.30.0.0/16", false],
  ["172.30.0.0", "172.30.0.0/16", true],
  ["172.30.255.255", "172.30.0.0/16", true],
  ["172.31.0.0", "172.30.0.0/16", false],
];

Test runner

Ready to run.

Testing in
TestOps/sec
RegExp
let valid = 0;
let invalid = 0;
for (const fixture of fixtures) {
	if (IPv4Reg.test(fixture)) {
		valid += 1;
	} else {
		invalid += 1;
	}
}
ready
function
let valid = 0;
let invalid = 0;
for (const fixture of fixtures) {
	if (isIPv4(fixture)) {
		valid += 1;
	} else {
		invalid += 1;
	}
}
ready
function (alternate)
let valid = 0;
let invalid = 0;
for (const fixture of fixtures) {
	if (isIPv4_V2(fixture)) {
		valid += 1;
	} else {
		invalid += 1;
	}
}
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.