Skip to content

Commit

Permalink
Faster ipv4 parsing (#3413)
Browse files Browse the repository at this point in the history
* Update ipv4 benchmark

* Update ipv4 regex
  • Loading branch information
colinhacks committed Apr 18, 2024
1 parent 0ca48b2 commit 27daef7
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 2 deletions.
4 changes: 4 additions & 0 deletions deno/lib/benchmarks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Benchmark from "benchmark";

import datetimeBenchmarks from "./datetime.ts";
import discriminatedUnionBenchmarks from "./discriminatedUnion.ts";
import ipv4Benchmarks from "./ipv4.ts";
import objectBenchmarks from "./object.ts";
import primitiveBenchmarks from "./primitives.ts";
import realworld from "./realworld.ts";
Expand Down Expand Up @@ -42,6 +43,9 @@ if (!argv.length) {
if (argv.includes("--datetime")) {
suites.push(...datetimeBenchmarks.suites);
}
if (argv.includes("--ipv4")) {
suites.push(...ipv4Benchmarks.suites);
}
}

for (const suite of suites) {
Expand Down
60 changes: 60 additions & 0 deletions deno/lib/benchmarks/ipv4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Benchmark from "benchmark";

const suite = new Benchmark.Suite("ipv4");

const DATA = "127.0.0.1";
const ipv4RegexA =
/^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
const ipv4RegexB =
/^(?:(?:(?=(25[0-5]))\1|(?=(2[0-4][0-9]))\2|(?=(1[0-9]{2}))\3|(?=([0-9]{1,2}))\4)\.){3}(?:(?=(25[0-5]))\5|(?=(2[0-4][0-9]))\6|(?=(1[0-9]{2}))\7|(?=([0-9]{1,2}))\8)$/;
const ipv4RegexC =
/^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
const ipv4RegexD =
/^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/;
const ipv4RegexE =
/^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)$/;
const ipv4RegexF = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/;
const ipv4RegexG = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/;
const ipv4RegexH = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/;
const ipv4RegexI =
/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;

suite
.add("A", () => {
return ipv4RegexA.test(DATA);
})
.add("B", () => {
return ipv4RegexB.test(DATA);
})
.add("C", () => {
return ipv4RegexC.test(DATA);
})
.add("D", () => {
return ipv4RegexD.test(DATA);
})
.add("E", () => {
return ipv4RegexE.test(DATA);
})
.add("F", () => {
return ipv4RegexF.test(DATA);
})
.add("G", () => {
return ipv4RegexG.test(DATA);
})
.add("H", () => {
return ipv4RegexH.test(DATA);
})
.add("I", () => {
return ipv4RegexI.test(DATA);
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${suite.name!}: ${e.target}`);
});

export default {
suites: [suite],
};

if (require.main === module) {
suite.run();
}
3 changes: 2 additions & 1 deletion deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,8 +605,9 @@ const emailRegex =
const _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;
let emojiRegex: RegExp;

// faster, simpler, safer
const ipv4Regex =
/^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;

const ipv6Regex =
/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;
Expand Down
4 changes: 4 additions & 0 deletions src/benchmarks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Benchmark from "benchmark";

import datetimeBenchmarks from "./datetime";
import discriminatedUnionBenchmarks from "./discriminatedUnion";
import ipv4Benchmarks from "./ipv4";
import objectBenchmarks from "./object";
import primitiveBenchmarks from "./primitives";
import realworld from "./realworld";
Expand Down Expand Up @@ -42,6 +43,9 @@ if (!argv.length) {
if (argv.includes("--datetime")) {
suites.push(...datetimeBenchmarks.suites);
}
if (argv.includes("--ipv4")) {
suites.push(...ipv4Benchmarks.suites);
}
}

for (const suite of suites) {
Expand Down
60 changes: 60 additions & 0 deletions src/benchmarks/ipv4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Benchmark from "benchmark";

const suite = new Benchmark.Suite("ipv4");

const DATA = "127.0.0.1";
const ipv4RegexA =
/^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
const ipv4RegexB =
/^(?:(?:(?=(25[0-5]))\1|(?=(2[0-4][0-9]))\2|(?=(1[0-9]{2}))\3|(?=([0-9]{1,2}))\4)\.){3}(?:(?=(25[0-5]))\5|(?=(2[0-4][0-9]))\6|(?=(1[0-9]{2}))\7|(?=([0-9]{1,2}))\8)$/;
const ipv4RegexC =
/^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/;
const ipv4RegexD =
/^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/;
const ipv4RegexE =
/^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.){3}(25[0-5]|(2[0-4]|1\d|[1-9]|)\d)$/;
const ipv4RegexF = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}$/;
const ipv4RegexG = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/;
const ipv4RegexH = /^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$/;
const ipv4RegexI =
/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;

suite
.add("A", () => {
return ipv4RegexA.test(DATA);
})
.add("B", () => {
return ipv4RegexB.test(DATA);
})
.add("C", () => {
return ipv4RegexC.test(DATA);
})
.add("D", () => {
return ipv4RegexD.test(DATA);
})
.add("E", () => {
return ipv4RegexE.test(DATA);
})
.add("F", () => {
return ipv4RegexF.test(DATA);
})
.add("G", () => {
return ipv4RegexG.test(DATA);
})
.add("H", () => {
return ipv4RegexH.test(DATA);
})
.add("I", () => {
return ipv4RegexI.test(DATA);
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${suite.name!}: ${e.target}`);
});

export default {
suites: [suite],
};

if (require.main === module) {
suite.run();
}
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,8 +605,9 @@ const emailRegex =
const _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;
let emojiRegex: RegExp;

// faster, simpler, safer
const ipv4Regex =
/^(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))$/;
/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;

const ipv6Regex =
/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;
Expand Down

0 comments on commit 27daef7

Please sign in to comment.