Skip to content

Commit

Permalink
Merge pull request #1 from ChainSafe/cayman/init-implementaiton
Browse files Browse the repository at this point in the history
feat: initial implementation
  • Loading branch information
wemeetagain authored Oct 29, 2022
2 parents d7dabd3 + c8bc591 commit 65211d9
Show file tree
Hide file tree
Showing 8 changed files with 558 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,5 @@ lib

# others
package-lock.json

benchmark_data
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,57 @@
npm install @chainsafe/is-ip
```

#### Example
## Example

```typescript
import { expect } from "chai";
import {
parseIPv4,
parseIPv6,
parseIP,
} from "@chainsafe/is-ip";

// parse a string into IPv4 bytes
const b1 = parseIPv4("127.0.0.1");
expect(b1).to.deep.equal(Uint8Array.from([127, 0, 0, 1]));

// parse a string into IPv6 bytes
const b2 = parseIPv6("::1");
expect(b2).to.deep.equal(Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]));

// parse a string into either IPv4 or IPv6 bytes
const b3 = parseIP("127.0.0.1");
expect(b3).to.deep.equal(Uint8Array.from([127, 0, 0, 1]));

const b4 = parseIP("::1");
expect(b4).to.deep.equal(Uint8Array.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]));

// parseIP* functions throw on invalid input
try {
parseIP("not an IP");
expect.fail("not reached");
} catch (e) {}

import {
isIPv4,
isIPv6,
isIP,
ipVersion,
} from "@chainsafe/is-ip";

// check if a string is a valid IPv4
expect(isIPv4("127.0.0.1")).to.equal(true);

// check if a string is a valid IPv6
expect(isIPv6("127.0.0.1")).to.equal(false);

// check if a string is a valid IPv4 or IPv6
expect(isIP("127.0.0.1")).to.equal(true);

// return 4, 6, or undefined
expect(ipVersion("127.0.0.1")).to.equal(4);
expect(ipVersion("1:2:3:4:5:6:7:8")).to.equal(6);
expect(ipVersion("invalid ip")).to.equal(undefined);
```

## License
Expand Down
90 changes: 90 additions & 0 deletions bench/comparison.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { itBench } from "@dapplion/benchmark";
import * as ours from "../src/index.js";
import * as net from "node:net";
import * as isIpLib from "is-ip";

const ipv4s = [
"0.0.0.0",
"192.168.0.1",
"255.255.255.255",
"0.0f.0.0",
"hahahahah",
"999.0.0.0",
"0000.0000.0000.0000",
];

const ipv6s = [
"1:2:3:4:5:6:7:8",
"::1",
"0000:0000:0000:0000:0000:0000:0000:0000",
"0000:0000:0000:0000:0000:ffff:192.168.100.228",
"2001:0dc5:72a3:0000:0000:802e:3370:73E4",
"hahahahahahaha",
"0000.0000:0000.0000",
];

describe("isIPv4", async function () {
this.timeout(5000);
for (const { name, fn } of [
{
name: "ours.isIPv4",
fn: ours.isIPv4,
},
{
name: "net.isIPv4",
fn: net.isIPv4,
},
{
name: "isIpLib.isIPv4",
fn: isIpLib.isIPv4,
},
]) {
for (const ipStr of ipv4s) {
itBench(`${name}("${ipStr}")`, () => fn(ipStr));
}
}
});

describe("isIPv6", async function () {
this.timeout(10000);
for (const { name, fn } of [
{
name: "ours.isIPv6",
fn: ours.isIPv6,
},
{
name: "net.isIPv6",
fn: net.isIPv6,
},
{
name: "isIpLib.isIPv6",
fn: isIpLib.isIPv6,
},
]) {
for (const ipStr of ipv6s) {
itBench(`${name}("${ipStr}")`, () => fn(ipStr));
}
}
});

describe("isIP", async function () {
this.timeout(10000);
for (const { name, fn } of [
{
name: "ours.isIP",
fn: ours.isIP,
},
{
name: "net.isIP",
fn: net.isIP,
},
{
name: "isIpLib.isIP",
fn: isIpLib.isIP,
},
]) {
for (const ipStr of [...ipv4s, ...ipv6s]) {
itBench(`${name}("${ipStr}")`, () => fn(ipStr));
}
}
});
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
"exports": {
".": {
"import": "./lib/index.js"
},
"./parser": {
"import": "./lib/parser.js"
}
},
"typesVersions": {
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./is-ip.js";
74 changes: 74 additions & 0 deletions src/is-ip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Parser, err } from "./parser.js";

// See https://stackoverflow.com/questions/166132/maximum-length-of-the-textual-representation-of-an-ipv6-address
const MAX_IPV6_LENGTH = 45;
const MAX_IPV4_LENGTH = 15;

const parser = new Parser();

/** Parse `input` into IPv4 bytes. */
export function parseIPv4(input: string): Uint8Array {
if (input.length > MAX_IPV4_LENGTH) {
throw err;
}
return parser.new(input).parseWith(() => parser.readIPv4Addr());
}

/** Parse `input` into IPv6 bytes. */
export function parseIPv6(input: string): Uint8Array {
if (input.length > MAX_IPV6_LENGTH) {
throw err;
}
return parser.new(input).parseWith(() => parser.readIPv6Addr());
}

/** Parse `input` into IPv4 or IPv6 bytes. */
export function parseIP(input: string): Uint8Array {
if (input.length > MAX_IPV6_LENGTH) {
throw err;
}
return parser.new(input).parseWith(() => parser.readIPAddr());
}

/** Check if `input` is IPv4. */
export function isIPv4(input: string): boolean {
try {
parseIPv4(input);
return true;
} catch (e) {
return false;
}
}

/** Check if `input` is IPv6. */
export function isIPv6(input: string): boolean {
try {
parseIPv6(input);
return true;
} catch (e) {
return false;
}
}

/** Check if `input` is IPv4 or IPv6. */
export function isIP(input: string): boolean {
try {
parseIP(input);
return true;
} catch (e) {
return false;
}
}

/**
* @returns `6` if `input` is IPv6, `4` if `input` is IPv4, or `undefined` if `input` is neither.
*/
export function ipVersion(input: string): 4 | 6 | undefined {
if (isIPv4(input)) {
return 4;
} else if (isIPv6(input)) {
return 6;
} else {
return undefined;
}
}
Loading

0 comments on commit 65211d9

Please sign in to comment.