From a86ae068bb6b1faac2c6a8f9839acb9088a7f0c0 Mon Sep 17 00:00:00 2001 From: Alex Demskie Date: Mon, 24 Jun 2019 22:47:52 -0700 Subject: [PATCH 1/8] beginning rework --- package-lock.json | 6 +- package.json | 4 +- src/IPv4.ts | 28 +--- src/IPv6.ts | 84 +---------- src/address.ts | 239 ++++++++++++++++++++++++++++++ src/errors.ts | 6 +- src/index.ts | 36 ++--- src/network.ts | 207 ++++++++++++++++++++++++++ src/parse.ts | 185 +++++++++++++++++------ src/ranges.ts | 138 +++++++++++++++++ src/shared.ts | 321 +--------------------------------------- src/sort.ts | 54 ++++--- src/tests/IPv4.test.ts | 2 +- src/tests/IPv6.test.ts | 2 +- src/tests/index.test.ts | 1 + 15 files changed, 799 insertions(+), 514 deletions(-) create mode 100644 src/address.ts create mode 100644 src/network.ts create mode 100644 src/ranges.ts diff --git a/package-lock.json b/package-lock.json index bbb96c9..cdc7d93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5562,9 +5562,9 @@ } }, "ts-node": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.2.0.tgz", - "integrity": "sha512-m8XQwUurkbYqXrKqr3WHCW310utRNvV5OnRVeISeea7LoCWVcdfeB/Ntl8JYWFh+WRoUAdBgESrzKochQt7sMw==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", + "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", "dev": true, "requires": { "arg": "^4.1.0", diff --git a/package.json b/package.json index 6f22f2c..18da811 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "build": "tsc && docts && jest --coverage", "test": "tsc --noEmit && jest --coverage", - "bench": "ts-node -T **/*/index.bench.ts && ts-node -T **/*/match.bench.ts" + "bench": "ts-node -T src/benchmarks/index.bench.ts && ts-node -T src/benchmarks/match.bench.ts" }, "repository": { "type": "git", @@ -89,7 +89,7 @@ "netmask": "^1.0.6", "prettier": "^1.16.4", "ts-jest": "^24.0.2", - "ts-node": "^8.2.0", + "ts-node": "^8.3.0", "typedoc": "^0.14.2", "typedoc-plugin-markdown": "^1.2.0", "typescript": "^3.4.2" diff --git a/src/IPv4.ts b/src/IPv4.ts index f968db5..7571f25 100644 --- a/src/IPv4.ts +++ b/src/IPv4.ts @@ -2,40 +2,20 @@ import * as errors from "./errors"; import * as shared from "./shared"; import * as weight from "./weight"; -export function addrToBytes(addr: string, throwErrors?: boolean) { - const ip = addr.split("."); - if (ip.length === 4) { - const bytes = new Uint8Array(4); - for (var i = 0; i < 4; i++) { - const val = parseInt(ip[i], 10); - if (val < 0 || val > 255) { - if (throwErrors) throw errors.AddrInvalidInteger; - return null; - } - bytes[i] = val; - } - return bytes; - } - if (throwErrors) throw errors.AddrNotFourElements; - return null; -} - -export function bytesToAddr(bytes: Uint8Array, throwErrors?: boolean) { - if (bytes.length >= 4) { - return bytes.slice(bytes.length - 4, bytes.length).join("."); - } +export function bytesToAddr(bytes: number[], throwErrors?: boolean) { + if (bytes.length === 4) return `${bytes[0]}.${bytes[1]}.${bytes[2]}.${bytes[3]}`; if (throwErrors) throw errors.BytesNotFourElements; return null; } export function randomAddress() { - return bytesToAddr(Uint8Array.from(Array(4), () => Math.random() * 255)); + return bytesToAddr(Array.from(Array(4), () => Math.random() * 255)); } const choices = Array.from(Array(31), (_, idx) => new weight.WeightedValue(Math.pow(2, idx), idx + 1)); export function randomNetwork() { - const bytes = Uint8Array.from(Array(4), () => Math.random() * 255); + const bytes = Array.from(Array(4), () => Math.random() * 255); const cidr = weight.getValue(choices) as number; shared.applySubnetMask(bytes, cidr); return `${bytesToAddr(bytes)}/${cidr}`; diff --git a/src/IPv6.ts b/src/IPv6.ts index 3818438..74d99c6 100644 --- a/src/IPv6.ts +++ b/src/IPv6.ts @@ -2,82 +2,8 @@ import * as shared from "./shared"; import * as errors from "./errors"; import * as weight from "./weight"; -function padZeros(addr: string, throwErrors?: boolean) { - if (addr.length >= 2) { - if (addr.slice(0, 2) === "::") { - addr = "0" + addr; - } - if (addr.slice(addr.length - 2) === "::") { - addr += "0"; - } - } - const splitAddr = addr.split("::"); - if (splitAddr.length === 1) { - return addr; - } else if (splitAddr.length === 2) { - const hextetCount = splitAddr[0].split(":").length + splitAddr[1].split(":").length; - splitAddr[0] += shared.repeatString(":0", 8 - hextetCount); - return splitAddr.join(":"); - } - if (throwErrors) throw errors.GenericPadZeros; - return null; -} - -// Network-Specific Prefix IPv4 IPv4-embedded IPv6 address -// 2001:db8:122:344::/96 192.0.2.33 2001:db8:122:344::192.0.2.33 -// https://tools.ietf.org/html/rfc6052 - -export function convertEmbeddedIPv4(addr: string) { - let hextets = addr.split(":"); - const octets = hextets[hextets.length - 1].split("."); - if (octets.length === 4) { - const a = parseInt(octets[0], 10).toString(16); - const b = parseInt(octets[1], 10).toString(16); - const c = parseInt(octets[2], 10).toString(16); - const d = parseInt(octets[3], 10).toString(16); - hextets = hextets.slice(0, hextets.length - 1); - hextets.push(parseInt(a + b, 16).toString(16)); - hextets.push(parseInt(c + d, 16).toString(16)); - addr = hextets.join(":"); - } - return addr; -} - -export function addrToBytes(addr: string, throwErrors?: boolean) { - const padded = padZeros(addr); - if (padded !== null) { - const hextets = padded.split(":"); - if (hextets.length === 8) { - const arr = new Uint8Array(16); - for (var j = 0; j < 8; j++) { - const hextet = hextets[j]; - switch (hextet.length) { - case 1: - case 2: - arr[2 * j] = 0; - arr[2 * j + 1] = parseInt(hextet, 16); - break; - case 3: - case 4: - const val = parseInt(hextet, 16); - arr[2 * j] = Math.floor(val / 256); - arr[2 * j + 1] = val % 256; - break; - default: - if (throwErrors) throw errors.GenericAddrToBytes; - return null; - } - } - return arr; - } - } - if (throwErrors) throw errors.GenericAddrToBytes; - return null; -} - -function findLongestZeroHextetChain(bytes: Uint8Array, throwErrors?: boolean) { - if (bytes.length >= 16) { - bytes = bytes.subarray(bytes.length - 16); +function findLongestZeroHextetChain(bytes: number[], throwErrors?: boolean) { + if (bytes.length === 16) { const canidate = { start: 0, length: 0 }; const longest = { start: 0, length: 0 }; for (var i = 0; i < bytes.length; i += 2) { @@ -101,7 +27,7 @@ function findLongestZeroHextetChain(bytes: Uint8Array, throwErrors?: boolean) { return null; } -export function bytesToAddr(bytes: Uint8Array, throwErrors?: boolean) { +export function bytesToAddr(bytes: number[], throwErrors?: boolean) { const longestHextetChain = findLongestZeroHextetChain(bytes, throwErrors); if (longestHextetChain !== null) { var result = ""; @@ -121,13 +47,13 @@ export function bytesToAddr(bytes: Uint8Array, throwErrors?: boolean) { } export function randomAddress() { - return bytesToAddr(Uint8Array.from(Array(16), () => Math.random() * 255)); + return bytesToAddr(Array.from(Array(16), () => Math.random() * 255)); } const choices = Array.from(Array(127), (_, idx) => new weight.WeightedValue(Math.pow(2, idx), idx + 1)); export function randomNetwork() { - const bytes = Uint8Array.from(Array(16), () => Math.random() * 255); + const bytes = Array.from(Array(16), () => Math.random() * 255); const cidr = weight.getValue(choices) as number; shared.applySubnetMask(bytes, cidr); return `${bytesToAddr(bytes)}/${cidr}`; diff --git a/src/address.ts b/src/address.ts new file mode 100644 index 0000000..9db6f53 --- /dev/null +++ b/src/address.ts @@ -0,0 +1,239 @@ +import * as ipv4 from "./ipv4"; +import * as ipv6 from "./ipv6"; +import * as parse from "./parse"; +import * as errors from "./errors"; + +const BEFORE = -1; +const EQUALS = 0; +const AFTER = 1; + +export class Address { + private bytes?: number[]; + + public constructor(address?: string, throwErrors?: boolean) { + if (address) { + var net = parse.network(address, throwErrors); + if (net) { + this.bytes = net.bytes; + } + } + } + + public destroy() { + this.bytes = undefined; + } + + public isValid() { + return this.bytes !== undefined; + } + + public getByte(index: number) { + if (this.bytes && index < this.bytes.length) { + return this.bytes[index]; + } + return Number.NaN; + } + + public setByte(index: number, value: number) { + if (this.bytes && index < this.bytes.length) { + this.bytes[index] = Math.min(Math.max(0, Math.floor(value)), 255); + } + return this; + } + + public setBytes(bytes: number[]) { + if (bytes.length === 4 || bytes.length === 16) { + this.bytes = new Array(bytes.length); + for (var i = 0; i < bytes.length; i++) { + this.bytes[i] = Math.min(Math.max(0, Math.floor(bytes[i])), 255); + } + } + return this; + } + + public fromBytes(bytes: number[]) { + if (bytes.length === 4 || bytes.length === 16) { + this.bytes = bytes; + } + return this; + } + + public toString() { + if (!this.bytes) return ""; + if (this.bytes.length === 4) { + return `${ipv4.bytesToAddr(this.bytes)}`; + } + return `${ipv6.bytesToAddr(this.bytes)}`; + } + + public duplicate() { + if (this.bytes) { + return new Address().fromBytes(this.bytes.slice()); + } + return null; + } + + public size() { + if (this.bytes) { + return this.bytes.length; + } + return 0; + } + + public lessThan(address: Address) { + return this.compare(address) === BEFORE; + } + + public lessThanOrEqual(address: Address) { + var result = this.compare(address); + if (result === null) return false; + return result <= EQUALS; + } + + public equals(address: Address) { + return this.compare(address) === EQUALS; + } + + public greaterThanOrEqual(address: Address) { + var result = this.compare(address); + if (result === null) return false; + return result >= EQUALS; + } + + public greaterThan(address: Address) { + return this.compare(address) === AFTER; + } + + public compare(address: Address) { + // check that both addresses are valid + if (!this.bytes || !address.bytes) return null; + + // handle edge cases like mixing IPv4 and IPv6 + if (this === address) return EQUALS; + if (this.bytes.length < address.bytes.length) return BEFORE; + if (this.bytes.length > address.bytes.length) return AFTER; + + // compare addresses + for (var i = 0; i < this.bytes.length; i++) { + if (this.bytes[i] < address.bytes[i]) return BEFORE; + if (this.bytes[i] > address.bytes[i]) return AFTER; + } + + // otherwise they must be equal + return EQUALS; + } + + public applySubnetMask(cidr: number) { + if (!this.bytes) return this; + var maskBits = this.bytes.length * 8 - cidr; + for (var i = this.bytes.length - 1; i >= 0; i--) { + switch (Math.max(0, Math.min(8, maskBits))) { + case 0: + return this; + case 1: + this.bytes[i] &= ~1; + break; + case 2: + this.bytes[i] &= ~3; + break; + case 3: + this.bytes[i] &= ~7; + break; + case 4: + this.bytes[i] &= ~15; + break; + case 5: + this.bytes[i] &= ~31; + break; + case 6: + this.bytes[i] &= ~63; + break; + case 7: + this.bytes[i] &= ~127; + break; + case 8: + this.bytes[i] = 0; + break; + } + maskBits -= 8; + } + return this; + } + + public isBaseAddress(cidr: number) { + if (!this.bytes || cidr < 0 || cidr > this.bytes.length * 8) return false; + if (cidr === this.bytes.length * 8) return true; + var maskBits = this.bytes.length * 8 - cidr; + for (var i = this.bytes.length - 1; i >= 0; i--) { + switch (Math.max(0, Math.min(8, maskBits))) { + case 0: + return true; + case 1: + if (this.bytes[i] !== (this.bytes[i] & ~1)) return false; + break; + case 2: + if (this.bytes[i] !== (this.bytes[i] & ~3)) return false; + break; + case 3: + if (this.bytes[i] !== (this.bytes[i] & ~7)) return false; + break; + case 4: + if (this.bytes[i] !== (this.bytes[i] & ~15)) return false; + break; + case 5: + if (this.bytes[i] !== (this.bytes[i] & ~31)) return false; + break; + case 6: + if (this.bytes[i] !== (this.bytes[i] & ~63)) return false; + break; + case 7: + if (this.bytes[i] !== (this.bytes[i] & ~127)) return false; + break; + case 8: + if (this.bytes[i] !== 0) return false; + break; + } + } + return true; + } + + public increase(cidr: number, throwErrors?: boolean) { + if (this.bytes) { + this.offsetAddress(cidr, true, throwErrors); + } else { + if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; + this.destroy(); + } + return this; + } + + public decrease(cidr: number, throwErrors?: boolean) { + if (this.bytes) { + this.offsetAddress(cidr, false, throwErrors); + } else { + if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; + this.destroy(); + } + return this; + } + + private offsetAddress(cidr: number, forwards: boolean, throwErrors?: boolean) { + var targetByte = Math.floor((cidr - 1) / 8); + if (this.bytes && targetByte >= 0 && targetByte < this.bytes.length) { + var increment = Math.pow(2, 8 - (cidr - targetByte * 8)); + this.bytes[targetByte] += increment * (forwards ? 1 : -1); + if (this.bytes[targetByte] < 0 || this.bytes[targetByte] > 255) { + if (targetByte > 0) { + this.bytes[targetByte] %= 256; + this.offsetAddress(targetByte * 8, forwards, throwErrors); + } else { + if (throwErrors) throw errors.OverflowedAddressSpace; + this.destroy(); + } + } + } else { + if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; + this.destroy(); + } + } +} diff --git a/src/errors.ts b/src/errors.ts index 6ef3eb9..001386d 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -11,7 +11,9 @@ export const AddrInvalidInteger = new Error("'addr' has at least one invalid int export const AddrNotFourElements = new Error("'addr' was not four elements long"); export const BytesNotFourElements = new Error("'bytes' was not at least four elements long"); -export const GenericAddrToBytes = new Error("unable to convert string to bytes"); +export const GenericNetworkParse = new Error("unable to parse string"); export const GenericBytesToAddr = new Error("unable to convert bytes to string"); export const GenericFindLongestZeroHextetChain = new Error("unable to findLongestZeroHextetChain"); -export const GenericPadZeros = new Error("unable to padZeros for IPv6 address"); + +export const InvalidSubnet = new Error("invalid subnet"); +export const NotValidCIDR = new Error("'cidr' was not a valid integer"); diff --git a/src/index.ts b/src/index.ts index 36267c1..e32a55c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import * as shared from "./shared"; +import * as parse from "./parse"; import * as errors from "./errors"; /** @@ -13,20 +14,10 @@ import * as errors from "./errors"; * @returns The first address in a subnet or null in case of error */ export function baseAddress(networkAddress: string, throwErrors?: boolean) { - networkAddress = networkAddress.trim(); - if (shared.hasColon(networkAddress)) { - networkAddress = shared.removeBrackets(networkAddress); - } - const ip = shared.removeCIDR(networkAddress, throwErrors); - const cidr = shared.getCIDR(networkAddress, throwErrors); - if (ip !== null && cidr !== null) { - const bytes = shared.addrToBytes(ip, throwErrors); - if (bytes !== null) { - shared.applySubnetMask(bytes, cidr); - return shared.bytesToAddr(bytes, throwErrors); - } - } - return null; + const net = parse.network(networkAddress, throwErrors); + if (!net) return null; + shared.applySubnetMask(net.bytes, net.cidr); + return shared.bytesToAddr(net.bytes, throwErrors); } /** @@ -42,13 +33,16 @@ export function baseAddress(networkAddress: string, throwErrors?: boolean) { * @returns An IPv4 address or null in case of error */ export function broadcastAddress(network: string, throwErrors?: boolean) { - const net = shared.parseNetworkString(network, false, throwErrors); - if (!net) return null; - if (!shared.increaseAddressWithCIDR(net.bytes, net.cidr, throwErrors)) return null; - if (!shared.decreaseAddressWithCIDR(net.bytes, net.bytes.length * 8, throwErrors)) return null; - const addr = shared.bytesToAddr(net.bytes, throwErrors); - if (!addr) return null; - return `${addr}`; + const net = parse.network(network); + if (net) { + if (!shared.increaseAddressWithCIDR(net.bytes, net.cidr, throwErrors)) return null; + if (!shared.decreaseAddressWithCIDR(net.bytes, net.bytes.length * 8, throwErrors)) return null; + const addr = shared.bytesToAddr(net.bytes, throwErrors); + if (!addr) return null; + return `${addr}`; + } + if (throwErrors) throw errors.GenericAddrToBytes; + return null; } /** diff --git a/src/network.ts b/src/network.ts new file mode 100644 index 0000000..8338968 --- /dev/null +++ b/src/network.ts @@ -0,0 +1,207 @@ +import * as parse from "./parse"; +import * as errors from "./errors"; +import { Address } from "./address"; + +const BEFORE = -1; +const EQUALS = 0; +const AFTER = 1; + +export class Network { + private address = new Address(); + private cidr = -1; + + public constructor(network?: string, throwErrors?: boolean) { + if (network) { + var net = parse.network(network, throwErrors); + if (net) { + this.address.fromBytes(net.bytes); + this.cidr = net.cidr; + } + } + } + + public destroy() { + this.address.destroy(); + this.cidr = -1; + } + + public addr() { + return this.address; + } + + public setCIDR(cidr: number, throwErrors?: boolean) { + if (!this.isValidNetwork() || !this.address.isValid()) { + if (throwErrors) throw errors.InvalidSubnet; + this.destroy(); + } else { + cidr = Math.floor(cidr); + if (cidr >= 0 && cidr <= this.address.size() * 8) { + this.cidr = cidr; + } else { + if (throwErrors) throw errors.NotValidCIDR; + this.destroy(); + } + } + return this; + } + + public fromBytes(bytes: number[], cidr: number) { + if (cidr >= 0 && cidr <= bytes.length * 8) { + if (this.address.fromBytes(bytes).isValid()) { + this.cidr = cidr; + } else { + this.destroy(); + } + } + return this; + } + + public isValidNetwork() { + return this.address.isValid() && this.cidr !== -1; + } + + public toNetString() { + if (this.isValidNetwork()) { + return `${this.address.toString()}/${this.cidr}`; + } + return ""; + } + + public getCIDR() { + if (this.isValidNetwork()) { + return this.cidr; + } + return Number.NaN; + } + + public duplicate() { + var network = new Network(); + if (this.isValidNetwork()) { + network.address = this.address.duplicate() as Address; + network.cidr = this.cidr; + } + return network; + } + + public size() { + return this.address.size(); + } + + public next() { + this.address.increase(this.cidr); + return this; + } + + public previous() { + this.address.decrease(this.cidr); + return this; + } + + public compare(network: Network) { + // check that both networks are valid + if (!this.isValidNetwork() || !network.isValidNetwork()) return null; + + // compare addresses + var cmp = this.address.compare(network.address); + if (cmp !== EQUALS) return cmp; + + // compare subnet mask length + if (this.cidr < network.cidr) return BEFORE; + if (this.cidr > network.cidr) return AFTER; + + // otherwise they must be equal + return EQUALS; + } + + public contains(network: Network) { + // check that both networks are valid + if (!this.isValidNetwork() || !network.isValidNetwork()) return false; + + // ensure that both IPs are of the same type + if (this.size() !== network.size()) return false; + + // handle edge cases + if (this.cidr === 0) return true; + if (network.cidr === 0) return false; + + // our base address should be less than or equal to the other base address + if (this.address.compare(network.address) === AFTER) return false; + + // get the next network address for both + var next = this.duplicate().next(); + var otherNext = network.duplicate().next(); + + // handle edge case where our next network address overflows + if (!next.isValidNetwork()) return true; + + // our address should be more than or equal to the other address + if (next.address.compare(otherNext.address) === BEFORE) return false; + + // must be a child subnet + return true; + } + + public intersects(network: Network) { + // check that both networks are valid + if (!this.isValidNetwork() || !network.isValidNetwork()) return false; + + // ensure that both IPs are of the same type + if (this.size() !== network.size()) return false; + + // handle edge cases + if (this.cidr === 0 || network.cidr == 0) return true; + var cmp = this.address.compare(network.address); + if (cmp === EQUALS) return true; + + // ensure that alpha addr contains the baseAddress that comes first + var alpha: Network, bravo: Network; + if (cmp === BEFORE) { + alpha = this.duplicate().next(); + bravo = network.duplicate().next(); + } else { + alpha = network.duplicate().next(); + bravo = this.duplicate().next(); + } + + // if either addresses overflowed than an intersection has occured + if (!alpha.isValidNetwork() || !bravo.isValidNetwork()) return true; + + // if alpha addr is now greater than or equal to bravo addr than we've intersected + if (alpha.address.greaterThanOrEqual(bravo.address)) return true; + + // otherwise we haven't intersected + return false; + } + + public adjacent(network: Network) { + // check that both networks are valid + if (!this.isValidNetwork() || !network.isValidNetwork()) return false; + + // ensure that both IPs are of the same type + if (this.size() !== network.size()) return false; + + // handle edge cases + if (this.cidr === 0 || network.cidr == 0) return true; + var cmp = this.address.compare(network.address); + if (cmp === EQUALS) return false; + + // ensure that alpha addr contains the baseAddress that comes first + var alpha: Network, bravo: Network; + if (cmp === BEFORE) { + alpha = this.duplicate().next(); + bravo = network; + } else { + alpha = network.duplicate().next(); + bravo = this; + } + + // if alpha overflows then an adjacency is not possible + if (!alpha.isValidNetwork()) return false; + + // alpha addr should equal bravo for them to be perfectly adjacent + if (alpha.address.compare(bravo.address) === EQUALS) return true; + + // otherwise we aren't adjacent + return false; + } +} diff --git a/src/parse.ts b/src/parse.ts index c6906db..c77c023 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -1,5 +1,88 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ +import * as shared from "./shared"; +import * as errors from "./errors"; + +export function network(s: string, throwErrors?: boolean) { + s = s.trim(); + var parts = s.split("/"); + if (parts.length === 0 || parts.length > 2) return null; + var isIPv4 = looksLikeIPv4(s); + if (isIPv4 === null) { + if (throwErrors) throw errors.GenericNetworkParse; + return null; + } + var cidr = isIPv4 ? 32 : 128; + if (parts.length === 2) { + var x = parseIntRange(parts[1], 0, cidr); + if (x === null) { + if (throwErrors) throw errors.GenericNetworkParse; + return null; + } + cidr = x; + } + var bytes = isIPv4 ? v4AddrToBytes(parts[0]) : v6AddrToBytes(parts[0]); + if (bytes === null) { + if (throwErrors) throw errors.GenericNetworkParse; + return null; + } + return { bytes, cidr } as shared.Network; +} + +function looksLikeIPv4(s: string) { + for (var c of s) { + if (c === ".") return true; + if (c === ":") return false; + } + return null; +} + +function parseIntRange(old: string, min: number, max: number) { + var s = ""; + for (var i = 0; i < old.length; i++) { + if (!isOneDigit(old[i])) break; + s += old[i]; + } + var x = parseInt(s, 10); + if (x >= min && x <= max) return x; + return null; +} + +function isOneDigit(s: string) { + switch (s) { + case "0": + case "1": + case "2": + case "3": + case "4": + case "5": + case "6": + case "7": + case "8": + case "9": + return true; + default: + return false; + } +} + +function v4AddrToBytes(old: string) { + var bytes = new Uint8Array(4); + var parts = old.split("."); + if (parts.length === 4) { + for (var i = 0; i < parts.length; i++) { + var x = parseInt(parts[i], 10); + if (x >= 0 && x <= 255) { + bytes[i] = x; + } else { + return null; + } + } + return bytes; + } + return null; +} + /* https://tools.ietf.org/html/rfc3986 ffff:fc00::1:1234/64 @@ -14,55 +97,67 @@ 2001:db8::1#80 */ -function parse(s: string) { - s = s.trim().toLowerCase(); - const parts = s.split("/"); - if (parts.length > 2) return null; - if (s.search(":") >= 0) { - let cidr = 128; - if (parts.length === 2) { - cidr = parseInt(parts[1], 10); - if (Number.isNaN(cidr) || cidr < 0 || cidr > 128) return null; +function v6AddrToBytes(old: string) { + const bytes = new Uint8Array(16); + if (old.length === 0) return null; + if (old[0] === "[") { + old = removeBrackets(old); + } + if (old === "::") return bytes; + var halves = old.split("::"); + if (halves.length === 0 || halves.length > 2) return null; + var leftByteIndex = 0; + var leftParts = halves[0].split(":"); + for (var i = 0; i < leftParts.length; i++) { + if (leftByteIndex >= 16) return bytes; + var x = parseInt(leftParts[i], 16); + if (Number.isNaN(x)) { + var ipv4Parts = leftParts[i].split("."); + if (ipv4Parts.length !== 4) return null; + for (var j = 0; j < ipv4Parts.length; j++) { + x = parseInt(ipv4Parts[j], 10); + if (Number.isNaN(x) || x < 0 || x > 255) return null; + bytes[leftByteIndex++] = x; + } + continue; + } + if (x < 0 || x > 65535) return null; + bytes[leftByteIndex++] = Math.floor(x / 256); + bytes[leftByteIndex++] = Math.floor(x % 256); + } + if (halves.length === 2) { + return parseRightHalf(bytes, leftByteIndex, halves[1].split(":")); + } + return bytes; +} + +function removeBrackets(s: string) { + for (var i = s.length - 1; i >= 0; i--) { + if (s[i] === "]") { + return s.substring(1, i); } - s = cleanIPv6(s); } - // let cidr = 32; - // TODO: finish + return s.substring(1); } -function cleanIPv6(old: string) { - let s = ""; - let periodCount = 0; - for (let i = 0; i < old.length; i++) { - if (i === 0 && old[0] === "[") continue; - switch (old[i]) { - case ".": - if (periodCount > 2) return ""; - s += "."; - periodCount++; - break; - case "0": - case "1": - case "2": - case "3": - case "4": - case "5": - case "6": - case "7": - case "8": - case "9": - case "a": - case "b": - case "c": - case "d": - case "e": - case "f": - s += old[i]; - break; - default: - return s; +function parseRightHalf(bytes: Uint8Array, leftByteIndex: number, rightParts: string[]) { + var rightByteIndex = 15; + for (var i = rightParts.length - 1; i >= 0; i--) { + if (leftByteIndex > rightByteIndex) return null; + var x = parseInt(rightParts[i], 16); + if (Number.isNaN(x)) { + var ipv4Parts = rightParts[i].split("."); + if (ipv4Parts.length !== 4) return null; + for (var j = ipv4Parts.length - 1; j >= 0; j--) { + x = parseInt(ipv4Parts[j], 10); + if (Number.isNaN(x) || x < 0 || x > 255) return null; + bytes[rightByteIndex--] = x; + } + continue; } + if (x < 0 || x > 65535) return null; + bytes[rightByteIndex--] = Math.floor(x / 256); + bytes[rightByteIndex--] = Math.floor(x % 256); } - if (periodCount !== 0 && periodCount !== 3) return ""; - return s; + return bytes; } diff --git a/src/ranges.ts b/src/ranges.ts new file mode 100644 index 0000000..9d1d870 --- /dev/null +++ b/src/ranges.ts @@ -0,0 +1,138 @@ +import * as v4 from "./ipv4"; +import * as v6 from "./ipv6"; + +export const ipv4SpecialRanges = [ + { + name: "unspecified", // + bytes: v4.addrToBytes("0.0.0.0"), + cidr: 8 + }, + { + name: "broadcast", // + bytes: v4.addrToBytes("255.255.255.255"), + cidr: 32 + }, + { + name: "multicast", // RFC3171 + bytes: v4.addrToBytes("224.0.0.0"), + cidr: 4 + }, + { + name: "link local", // RFC3927 + bytes: v4.addrToBytes("169.254.0.0"), + cidr: 16 + }, + { + name: "loopback", // RFC5735 + bytes: v4.addrToBytes("127.0.0.0"), + cidr: 8 + }, + { + name: "carrier grade nat", // RFC6598 + bytes: v4.addrToBytes("100.64.0.0"), + cidr: 10 + }, + { + name: "private", // RFC1918 + bytes: v4.addrToBytes("10.0.0.0"), + cidr: 8 + }, + { + name: "private", // RFC1918 + bytes: v4.addrToBytes("172.16.0.0"), + cidr: 12 + }, + { + name: "private", // RFC1918 + bytes: v4.addrToBytes("192.168.0.0"), + cidr: 16 + }, + { + name: "reserved", // 5735, 5737, 2544, 1700 + bytes: v4.addrToBytes("192.0.0.0"), + cidr: 24 + }, + { + name: "reserved", // 5735, 5737, 2544, 1700 + bytes: v4.addrToBytes("192.0.2.0"), + cidr: 24 + }, + { + name: "reserved", // 5735, 5737, 2544, 1700 + bytes: v4.addrToBytes("192.88.99.0"), + cidr: 24 + }, + { + name: "reserved", // 5735, 5737, 2544, 1700 + bytes: v4.addrToBytes("198.51.100.0"), + cidr: 24 + }, + { + name: "reserved", // 5735, 5737, 2544, 1700 + bytes: v4.addrToBytes("203.0.113.0"), + cidr: 24 + }, + { + name: "reserved", // 5735, 5737, 2544, 1700 + bytes: v4.addrToBytes("240.0.0.0"), + cidr: 24 + } +]; + +export const ipv6SpecialRanges = [ + { + name: "unspecified", // RFC4291 + bytes: v6.addrToBytes("::"), + cidr: 128 + }, + { + name: "link local", // RFC4291 + bytes: v6.addrToBytes("fe80::"), + cidr: 10 + }, + { + name: "multicast", // RFC4291 + bytes: v6.addrToBytes("ff00::"), + cidr: 8 + }, + { + name: "loopback", // RFC4291 + bytes: v6.addrToBytes("::1"), + cidr: 128 + }, + { + name: "unique local", // RFC4291 + bytes: v6.addrToBytes("fc00::"), + cidr: 7 + }, + { + name: "ipv4 mapped", // RFC4291 + bytes: v6.addrToBytes("::ffff:0:0"), + cidr: 96 + }, + { + name: "rfc6145", // RFC6145 + bytes: v6.addrToBytes("::ffff:0:0:0"), + cidr: 96 + }, + { + name: "rfc6052", // RFC6052 + bytes: v6.addrToBytes("64:ff9b::"), + cidr: 96 + }, + { + name: "6to4", // RFC3056 + bytes: v6.addrToBytes("2002::"), + cidr: 16 + }, + { + name: "teredo", // RFC6052, RFC6146 + bytes: v6.addrToBytes("2001::"), + cidr: 32 + }, + { + name: "reserved", // RFC4291 + bytes: v6.addrToBytes("2001:db8::"), + cidr: 32 + } +]; diff --git a/src/shared.ts b/src/shared.ts index 4b34c87..1ff62b4 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -1,32 +1,8 @@ -import * as v4 from "./IPv4"; -import * as v6 from "./IPv6"; +import * as v4 from "./ipv4"; +import * as v6 from "./ipv6"; import * as sort from "./sort"; import * as errors from "./errors"; - -export type Address = Uint8Array; - -export interface Network { - bytes: Address; - cidr: number; -} - -export function hasColon(s: string) { - return s.search(":") >= 0; -} - -export function addrToBytes(addr: string, throwErrors?: boolean) { - if (hasColon(addr)) { - return v6.addrToBytes(addr, throwErrors); - } - return v4.addrToBytes(addr, throwErrors); -} - -export function bytesToAddr(bytes: Uint8Array, throwErrors?: boolean) { - if (bytes.length === 16) { - return v6.bytesToAddr(bytes, throwErrors); - } - return v4.bytesToAddr(bytes, throwErrors); -} +import { Network } from "./network"; export function repeatString(s: string, count: number) { var result = ""; @@ -36,286 +12,6 @@ export function repeatString(s: string, count: number) { return result; } -export function removeCIDR(s: string, throwErrors?: boolean) { - const splitAddr = s.split("/"); - switch (splitAddr.length) { - case 0: - case 1: - return s; - case 2: - return splitAddr[0]; - } - if (throwErrors) throw errors.GenericRemoveCIDR; - return null; -} - -export function getCIDR(s: string, throwErrors?: boolean) { - const splitAddr = s.split("/"); - if (splitAddr.length === 2) { - const val = parseInt(splitAddr[1], 10); - if (Number.isInteger(val)) { - if (hasColon(splitAddr[0]) && 0 < val && val <= 128) return val; - if (0 < val && val <= 32) return val; - } - } - if (throwErrors) throw errors.GenericGetCIDR; - return null; -} - -export function removeBrackets(s: string) { - return s.replace(/[[\]]/g, ""); -} - -export function duplicateAddress(address: Address) { - return address.slice(); -} - -export function duplicateNetwork(network: Network) { - return { bytes: network.bytes.slice(), cidr: network.cidr } as Network; -} - -export function setAddress(dst: Uint8Array, src: Uint8Array) { - for (var i = 0; i < src.length; i++) { - if (i < dst.length) { - dst[i] = src[i]; - } else { - return; - } - } -} - -export enum Pos { - before = -1, - equals = 0, - after = 1 -} - -export function compareAddresses(a: Uint8Array, b: Uint8Array) { - if (a !== b) { - if (a.length < b.length) return Pos.before; - if (a.length > b.length) return Pos.after; - for (var i = 0; i < a.length; i++) { - if (a[i] < b[i]) return Pos.before; - if (a[i] > b[i]) return Pos.after; - } - } - return Pos.equals; -} - -export function compareNetworks(a: Network, b: Network) { - if (a !== b) { - if (a.bytes.length < b.bytes.length) return Pos.before; - if (a.bytes.length > b.bytes.length) return Pos.after; - for (var i = 0; i < a.bytes.length; i++) { - if (a.bytes[i] < b.bytes[i]) return Pos.before; - if (a.bytes[i] > b.bytes[i]) return Pos.after; - } - if (a.cidr < b.cidr) return Pos.before; - if (a.cidr > b.cidr) return Pos.after; - } - return Pos.equals; -} - -function offsetAddress(bytes: Uint8Array, cidr: number, isPositive: boolean, throwErrors?: boolean): Uint8Array | null { - const targetByte = Math.floor((cidr - 1) / 8); - if (targetByte < bytes.length) { - const increment = Math.pow(2, 8 - (cidr - targetByte * 8)); - const unconstrained = bytes[targetByte] + increment * (isPositive ? 1 : -1); - bytes[targetByte] = unconstrained % 256; - if (0 <= unconstrained && unconstrained <= 255) { - return bytes; - } - if (targetByte > 0) { - const supernetCIDR = targetByte * 8; - return offsetAddress(bytes, supernetCIDR, isPositive, throwErrors); - } - if (throwErrors) throw errors.OverflowedAddressSpace; - return null; - } - if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; - return null; -} - -export function increaseAddressWithCIDR(bytes: Uint8Array, cidr: number, throwErrors?: boolean) { - if (cidr > 0 && (bytes.length === 4 || bytes.length === 16)) { - return offsetAddress(bytes, cidr, true, throwErrors); - } - if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; - return null; -} - -export function decreaseAddressWithCIDR(bytes: Uint8Array, cidr: number, throwErrors?: boolean) { - if (cidr > 0 && (bytes.length === 4 || bytes.length === 16)) { - return offsetAddress(bytes, cidr, false, throwErrors); - } - if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; - return null; -} - -export function applySubnetMask(bytes: Uint8Array, cidr: number) { - let maskBits = bytes.length * 8 - cidr; - for (var i = bytes.length - 1; i >= 0; i--) { - switch (Math.max(0, Math.min(8, maskBits))) { - case 0: - return; - case 1: - bytes[i] &= ~1; - break; - case 2: - bytes[i] &= ~3; - break; - case 3: - bytes[i] &= ~7; - break; - case 4: - bytes[i] &= ~15; - break; - case 5: - bytes[i] &= ~31; - break; - case 6: - bytes[i] &= ~63; - break; - case 7: - bytes[i] &= ~127; - break; - case 8: - bytes[i] = 0; - break; - } - maskBits -= 8; - } -} - -export function parseAddressString(s: string, throwErrors?: boolean) { - s = s.trim(); - const isIPv6 = hasColon(s); - if (isIPv6) { - s = removeBrackets(s); - } - const ip = removeCIDR(s, throwErrors); - if (ip !== null) { - return addrToBytes(ip, throwErrors) as Address; - } - return null; -} - -export function parseNetworkString(s: string, strict?: boolean, throwErrors?: boolean) { - s = s.trim(); - const isIPv6 = hasColon(s); - if (isIPv6) { - s = removeBrackets(s); - } - const ip = removeCIDR(s, throwErrors); - let cidr = getCIDR(s, strict && throwErrors); - if (ip !== null) { - const bytes = addrToBytes(ip, throwErrors); - if (!bytes) return null; - if (!strict) { - if (!cidr) { - cidr = bytes.length * 8; - } - applySubnetMask(bytes, cidr); - } else { - if (!cidr) return null; - const bytesCopy = new Uint8Array(bytes.length); - setAddress(bytesCopy, bytes); - applySubnetMask(bytes, cidr); - if (compareAddresses(bytes, bytesCopy) !== 0) { - if (throwErrors) throw errors.NotValidBaseNetworkAddress; - return null; - } - } - return { bytes, cidr } as Network; - } - return null; -} - -export function networkGoesPastAddress(net: Network, addr: Address) { - const netBytesEnd = duplicateAddress(net.bytes); - increaseAddressWithCIDR(netBytesEnd, net.cidr); - decreaseAddressWithCIDR(netBytesEnd, net.bytes.length * 8); - if (compareAddresses(netBytesEnd, addr) > 0) return true; - return false; -} - -export function networkContainsSubnet(net: Network, subnet: Network, throwErrors?: boolean) { - if (net.bytes.length !== subnet.bytes.length) return false; - if (compareAddresses(net.bytes, subnet.bytes) > 0) return false; - const netBytesEnd = duplicateAddress(net.bytes); - if (!increaseAddressWithCIDR(netBytesEnd, net.cidr, throwErrors)) return false; - const subnetBytesEnd = duplicateAddress(subnet.bytes); - if (!increaseAddressWithCIDR(subnetBytesEnd, subnet.cidr, throwErrors)) return false; - if (compareAddresses(netBytesEnd, subnetBytesEnd) < 0) return false; - return true; -} - -export function networkContainsAddress(net: Network, addr: Address, throwErrors?: boolean) { - if (net.bytes.length !== addr.length) return false; - if (compareAddresses(net.bytes, addr) > 0) return false; - const netBytesEnd = duplicateAddress(net.bytes); - if (!increaseAddressWithCIDR(netBytesEnd, net.cidr, throwErrors)) return false; - if (compareAddresses(netBytesEnd, addr) > 0) return true; - return false; -} - -export function networksIntersect(net: Network, otherNet: Network, throwErrors?: boolean) { - if (net.bytes.length !== otherNet.bytes.length) return false; - let alphaStart = net.bytes; - let alphaEnd = duplicateAddress(net.bytes); - if (!increaseAddressWithCIDR(alphaEnd, net.cidr, throwErrors)) return false; - decreaseAddressWithCIDR(alphaEnd, net.bytes.length * 8); - let bravoStart = otherNet.bytes; - let bravoEnd = duplicateAddress(otherNet.bytes); - if (!increaseAddressWithCIDR(bravoEnd, otherNet.cidr, throwErrors)) return false; - decreaseAddressWithCIDR(bravoEnd, otherNet.bytes.length * 8); - if (compareAddresses(alphaStart, bravoStart) > 0) { - [alphaStart, alphaEnd, bravoStart, bravoEnd] = [bravoStart, bravoEnd, alphaStart, alphaEnd]; - } - if (compareAddresses(alphaEnd, bravoStart) < 0) return false; - return true; -} - -export function networksAreAdjacent(net: Network, otherNet: Network, throwErrors?: boolean) { - if (net.bytes.length !== otherNet.bytes.length) return false; - const netBytes = duplicateAddress(net.bytes); - if (!increaseAddressWithCIDR(netBytes, net.cidr, throwErrors)) return false; - if (compareAddresses(netBytes, otherNet.bytes) === 0) return true; - return false; -} - -export function findNetworkIntersection(network: Network, otherNetworks: Network[]) { - for (var otherNet of otherNetworks) { - if (networksIntersect(network, otherNet)) { - return otherNet; - } - } - return null; -} - -export function isValidNetworkAddress(net: Network) { - const netBaseBytes = duplicateAddress(net.bytes); - applySubnetMask(netBaseBytes, net.cidr); - return compareAddresses(net.bytes, netBaseBytes) === 0; -} - -export function findNetworkWithoutIntersection(network: Network, otherNetworks: Network[]) { - const currentNetwork = duplicateNetwork(network); - while (currentNetwork.cidr <= network.bytes.length * 8) { - if (isValidNetworkAddress(currentNetwork)) { - if (!findNetworkIntersection(currentNetwork, otherNetworks)) { - return currentNetwork; - } - if (currentNetwork.cidr >= network.bytes.length * 8) { - if (!increaseAddressWithCIDR(currentNetwork.bytes, network.bytes.length * 8)) return null; - currentNetwork.cidr = network.cidr; - } - } - currentNetwork.cidr++; - } - return null; -} - export function sortNetworks(networks: Network[]) { if (networks && networks.length > 1) { sort.radixSortNetworks(networks, 0, networks.length, -1); @@ -328,15 +24,14 @@ export function summarizeSortedNetworks(sorted: Network[]) { summarized.push(sorted[idx]); let skipped = 0; for (let i = idx + 1; i < sorted.length; i++) { - if (networkContainsSubnet(sorted[idx], sorted[i])) { + if (sorted[idx].contains(sorted[i])) { skipped++; continue; } - if (sorted[idx].cidr === sorted[i].cidr) { - if (networksAreAdjacent(sorted[idx], sorted[i])) { - sorted[idx].cidr--; - skipped++; - continue; + if (sorted[idx].getCIDR() === sorted[i].getCIDR()) { + if (sorted[idx].adjacent(sorted[i])) { + var cidr = sorted[idx].getCIDR() - 1; + sorted[idx].setCIDR(cidr); } } break; diff --git a/src/sort.ts b/src/sort.ts index f8a931d..c9ff19b 100644 --- a/src/sort.ts +++ b/src/sort.ts @@ -1,6 +1,6 @@ -import * as shared from "./shared"; +import { Network } from "./network"; -export function radixSortNetworks(networks: shared.Network[], start: number, stop: number, byteIndex: number) { +export function radixSortNetworks(networks: Network[], start: number, stop: number, byteIndex: number) { const runningPrefixSum = new Array(256) as number[]; const offsetPrefixSum = new Array(256) as number[]; const counts = runningPrefixSum; @@ -10,17 +10,22 @@ export function radixSortNetworks(networks: shared.Network[], start: number, sto // count each occurance of byte value for (let i = start; i < stop; i++) { - if (byteIndex === -1) { - counts[networks[i].bytes.length]++; - } else if (byteIndex < 16) { - if (byteIndex < networks[i].bytes.length) { - networks[i].bytes[byteIndex] = Math.min(Math.max(0, networks[i].bytes[byteIndex]), 255); - } - counts[networks[i].bytes[byteIndex]]++; - } else { - networks[i].cidr = Math.min(Math.max(0, networks[i].cidr), 8 * networks[i].bytes.length); - counts[networks[i].cidr]++; + let byteValue: number; + switch (byteIndex) { + case -1: + byteValue = networks[i].size(); + break; + case 16: + byteValue = networks[i].cidr(); + break; + default: + if (byteIndex < networks[i].size()) { + byteValue = networks[i].addr().getByte(byteIndex); + } else { + byteValue = 0; + } } + counts[byteValue]++; } let lastCount = counts[counts.length - 1]; @@ -46,16 +51,19 @@ export function radixSortNetworks(networks: shared.Network[], start: number, sto let redIndex = start; let redValue = 0; while (redIndex < stop) { - if (byteIndex === -1) { - redValue = networks[redIndex].bytes.length; - } else if (byteIndex < 16) { - if (byteIndex < networks[redIndex].bytes.length) { - redValue = networks[redIndex].bytes[byteIndex]; - } else { - redValue = 0; - } - } else { - redValue = networks[redIndex].cidr; + switch (byteIndex) { + case -1: + redValue = networks[redIndex].size(); + break; + case 16: + redValue = networks[redIndex].cidr(); + break; + default: + if (byteIndex < networks[redIndex].size()) { + redValue = networks[redIndex].addr().getByte(byteIndex); + } else { + redValue = 0; + } } let blueIndex = start + runningPrefixSum[redValue]; if (runningPrefixSum[redValue] < offsetPrefixSum[redValue]) { @@ -84,7 +92,7 @@ export function radixSortNetworks(networks: shared.Network[], start: number, sto } } -export function binarySearchForInsertionIndex(network: shared.Network, sortedNetworks: shared.Network[]) { +export function binarySearchForInsertionIndex(network: Network, sortedNetworks: Network[]) { let left = 0; let right = sortedNetworks.length - 1; while (left < right) { diff --git a/src/tests/IPv4.test.ts b/src/tests/IPv4.test.ts index 4c50bc4..eca0fdb 100644 --- a/src/tests/IPv4.test.ts +++ b/src/tests/IPv4.test.ts @@ -1,6 +1,6 @@ import * as index from "../index"; import * as shared from "../shared"; -import * as ipv4 from "../IPv4"; +import * as ipv4 from "../ipv4"; import * as errors from "../errors"; test("sanity check IPv4 offset by /32", () => { diff --git a/src/tests/IPv6.test.ts b/src/tests/IPv6.test.ts index 45056f3..4c6a9e4 100644 --- a/src/tests/IPv6.test.ts +++ b/src/tests/IPv6.test.ts @@ -1,6 +1,6 @@ import * as index from "../index"; import * as shared from "../shared"; -import * as ipv6 from "../IPv6"; +import * as ipv6 from "../ipv6"; test("sanity check IPv6 offset by /128", () => { const input = "2001:db8:122:344::"; diff --git a/src/tests/index.test.ts b/src/tests/index.test.ts index 3daa660..67d51a8 100644 --- a/src/tests/index.test.ts +++ b/src/tests/index.test.ts @@ -1,4 +1,5 @@ import * as index from "../index"; +import * as bravo from "../parse"; test("sanity check baseAddress #1", () => { const output = index.baseAddress("192.168.200.113/24", true); From 1923b4bd25cb6a89859857880198c0cff3ed9779 Mon Sep 17 00:00:00 2001 From: Alex Demskie Date: Mon, 24 Jun 2019 22:57:43 -0700 Subject: [PATCH 2/8] Update ranges.ts --- src/ranges.ts | 85 ++++++++++++++++++--------------------------------- 1 file changed, 29 insertions(+), 56 deletions(-) diff --git a/src/ranges.ts b/src/ranges.ts index 9d1d870..a6fe2c8 100644 --- a/src/ranges.ts +++ b/src/ranges.ts @@ -1,138 +1,111 @@ -import * as v4 from "./ipv4"; -import * as v6 from "./ipv6"; +import { Network } from "./network"; -export const ipv4SpecialRanges = [ +export const specialIPv4 = [ { name: "unspecified", // - bytes: v4.addrToBytes("0.0.0.0"), - cidr: 8 + network: new Network("0.0.0.0/8") }, { name: "broadcast", // - bytes: v4.addrToBytes("255.255.255.255"), - cidr: 32 + network: new Network("255.255.255.255/32") }, { name: "multicast", // RFC3171 - bytes: v4.addrToBytes("224.0.0.0"), - cidr: 4 + network: new Network("224.0.0.0/4") }, { name: "link local", // RFC3927 - bytes: v4.addrToBytes("169.254.0.0"), - cidr: 16 + network: new Network("169.254.0.0/16") }, { name: "loopback", // RFC5735 - bytes: v4.addrToBytes("127.0.0.0"), - cidr: 8 + network: new Network("127.0.0.0/8") }, { name: "carrier grade nat", // RFC6598 - bytes: v4.addrToBytes("100.64.0.0"), - cidr: 10 + network: new Network("100.64.0.0/10") }, { name: "private", // RFC1918 - bytes: v4.addrToBytes("10.0.0.0"), - cidr: 8 + network: new Network("10.0.0.0/8") }, { name: "private", // RFC1918 - bytes: v4.addrToBytes("172.16.0.0"), - cidr: 12 + network: new Network("172.16.0.0/12") }, { name: "private", // RFC1918 - bytes: v4.addrToBytes("192.168.0.0"), - cidr: 16 + network: new Network("192.168.0.0/16") }, { name: "reserved", // 5735, 5737, 2544, 1700 - bytes: v4.addrToBytes("192.0.0.0"), - cidr: 24 + network: new Network("192.0.0.0/24") }, { name: "reserved", // 5735, 5737, 2544, 1700 - bytes: v4.addrToBytes("192.0.2.0"), - cidr: 24 + network: new Network("192.0.2.0/24") }, { name: "reserved", // 5735, 5737, 2544, 1700 - bytes: v4.addrToBytes("192.88.99.0"), - cidr: 24 + network: new Network("192.88.99.0/24") }, { name: "reserved", // 5735, 5737, 2544, 1700 - bytes: v4.addrToBytes("198.51.100.0"), - cidr: 24 + network: new Network("198.51.100.0/24") }, { name: "reserved", // 5735, 5737, 2544, 1700 - bytes: v4.addrToBytes("203.0.113.0"), - cidr: 24 + network: new Network("203.0.113.0/24") }, { name: "reserved", // 5735, 5737, 2544, 1700 - bytes: v4.addrToBytes("240.0.0.0"), - cidr: 24 + network: new Network("240.0.0.0/24") } ]; -export const ipv6SpecialRanges = [ +export const specialIPv6 = [ { name: "unspecified", // RFC4291 - bytes: v6.addrToBytes("::"), - cidr: 128 + network: new Network("::/128") }, { name: "link local", // RFC4291 - bytes: v6.addrToBytes("fe80::"), - cidr: 10 + network: new Network("fe80::/10") }, { name: "multicast", // RFC4291 - bytes: v6.addrToBytes("ff00::"), - cidr: 8 + network: new Network("ff00::/8") }, { name: "loopback", // RFC4291 - bytes: v6.addrToBytes("::1"), - cidr: 128 + network: new Network("::1/128") }, { name: "unique local", // RFC4291 - bytes: v6.addrToBytes("fc00::"), - cidr: 7 + network: new Network("fc00::/7") }, { name: "ipv4 mapped", // RFC4291 - bytes: v6.addrToBytes("::ffff:0:0"), - cidr: 96 + network: new Network("::ffff:0:0/96") }, { name: "rfc6145", // RFC6145 - bytes: v6.addrToBytes("::ffff:0:0:0"), - cidr: 96 + network: new Network("::ffff:0:0:0/96") }, { name: "rfc6052", // RFC6052 - bytes: v6.addrToBytes("64:ff9b::"), - cidr: 96 + network: new Network("64:ff9b::/96") }, { name: "6to4", // RFC3056 - bytes: v6.addrToBytes("2002::"), - cidr: 16 + network: new Network("2002::/16") }, { name: "teredo", // RFC6052, RFC6146 - bytes: v6.addrToBytes("2001::"), - cidr: 32 + network: new Network("2001::/32") }, { name: "reserved", // RFC4291 - bytes: v6.addrToBytes("2001:db8::"), - cidr: 32 + network: new Network("2001:db8::/32") } ]; From f203a8c77bc1aebd3216335cc86b4b5ee9531093 Mon Sep 17 00:00:00 2001 From: Alex Demskie Date: Mon, 24 Jun 2019 23:00:14 -0700 Subject: [PATCH 3/8] Update shared.ts --- src/shared.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/shared.ts b/src/shared.ts index 1ff62b4..2487dac 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -28,10 +28,9 @@ export function summarizeSortedNetworks(sorted: Network[]) { skipped++; continue; } - if (sorted[idx].getCIDR() === sorted[i].getCIDR()) { + if (sorted[idx].cidr() === sorted[i].cidr()) { if (sorted[idx].adjacent(sorted[i])) { - var cidr = sorted[idx].getCIDR() - 1; - sorted[idx].setCIDR(cidr); + sorted[idx].setCIDR(sorted[idx].cidr() - 1); } } break; From 0f7f30f0cc45f957c6db12205e14ea5d052ac412 Mon Sep 17 00:00:00 2001 From: Alex Demskie Date: Fri, 12 Jul 2019 02:24:16 -0700 Subject: [PATCH 4/8] handling more edge cases --- src/IPv4.ts | 11 +- src/IPv6.ts | 11 +- src/address.ts | 168 ++++++------ src/errors.ts | 2 + src/index.ts | 242 ++++++++---------- src/match.ts | 25 +- src/mockdata/subnets.mock.ts | 6 +- src/network.ts | 150 ++++++----- src/parse.ts | 51 ++-- src/shared.ts | 62 ++++- src/sort.ts | 28 +- src/tests/index.test.ts | 9 +- src/tests/{IPv4.test.ts => ipv4.test.ts.bckp} | 0 src/tests/{IPv6.test.ts => ipv6.test.ts.bckp} | 0 src/tests/parse.test.ts | 6 + .../{shared.test.ts => shared.test.ts.bckp} | 0 src/tests/sort.test.ts | 80 +++--- 17 files changed, 448 insertions(+), 403 deletions(-) rename src/tests/{IPv4.test.ts => ipv4.test.ts.bckp} (100%) rename src/tests/{IPv6.test.ts => ipv6.test.ts.bckp} (100%) create mode 100644 src/tests/parse.test.ts rename src/tests/{shared.test.ts => shared.test.ts.bckp} (100%) diff --git a/src/IPv4.ts b/src/IPv4.ts index 7571f25..d7437af 100644 --- a/src/IPv4.ts +++ b/src/IPv4.ts @@ -1,6 +1,7 @@ import * as errors from "./errors"; -import * as shared from "./shared"; import * as weight from "./weight"; +import { Address } from "./address"; +import { Network } from "./network"; export function bytesToAddr(bytes: number[], throwErrors?: boolean) { if (bytes.length === 4) return `${bytes[0]}.${bytes[1]}.${bytes[2]}.${bytes[3]}`; @@ -9,14 +10,14 @@ export function bytesToAddr(bytes: number[], throwErrors?: boolean) { } export function randomAddress() { - return bytesToAddr(Array.from(Array(4), () => Math.random() * 255)); + return bytesToAddr(Array.from(Array(4), () => Math.floor(Math.random() * 256))); } const choices = Array.from(Array(31), (_, idx) => new weight.WeightedValue(Math.pow(2, idx), idx + 1)); export function randomNetwork() { - const bytes = Array.from(Array(4), () => Math.random() * 255); + const bytes = Array.from(Array(4), () => Math.floor(Math.random() * 256)); + const addr = new Address().setBytes(bytes); const cidr = weight.getValue(choices) as number; - shared.applySubnetMask(bytes, cidr); - return `${bytesToAddr(bytes)}/${cidr}`; + return new Network().from(addr, cidr).toNetString(); } diff --git a/src/IPv6.ts b/src/IPv6.ts index 74d99c6..57f0fdc 100644 --- a/src/IPv6.ts +++ b/src/IPv6.ts @@ -1,6 +1,7 @@ -import * as shared from "./shared"; import * as errors from "./errors"; import * as weight from "./weight"; +import { Network } from "./network"; +import { Address } from "./address"; function findLongestZeroHextetChain(bytes: number[], throwErrors?: boolean) { if (bytes.length === 16) { @@ -47,14 +48,14 @@ export function bytesToAddr(bytes: number[], throwErrors?: boolean) { } export function randomAddress() { - return bytesToAddr(Array.from(Array(16), () => Math.random() * 255)); + return bytesToAddr(Array.from(Array(16), () => Math.floor(Math.random() * 256))); } const choices = Array.from(Array(127), (_, idx) => new weight.WeightedValue(Math.pow(2, idx), idx + 1)); export function randomNetwork() { - const bytes = Array.from(Array(16), () => Math.random() * 255); + const bytes = Array.from(Array(16), () => Math.floor(Math.random() * 256)); + const addr = new Address().setBytes(bytes); const cidr = weight.getValue(choices) as number; - shared.applySubnetMask(bytes, cidr); - return `${bytesToAddr(bytes)}/${cidr}`; + return new Network().from(addr, cidr).toNetString(); } diff --git a/src/address.ts b/src/address.ts index 9db6f53..a69f000 100644 --- a/src/address.ts +++ b/src/address.ts @@ -3,81 +3,66 @@ import * as ipv6 from "./ipv6"; import * as parse from "./parse"; import * as errors from "./errors"; +import { Network } from "./network"; + const BEFORE = -1; const EQUALS = 0; const AFTER = 1; export class Address { - private bytes?: number[]; + private arr: number[]; public constructor(address?: string, throwErrors?: boolean) { if (address) { var net = parse.network(address, throwErrors); if (net) { - this.bytes = net.bytes; + this.arr = net.bytes; + return; } } + this.arr = []; } - public destroy() { - this.bytes = undefined; - } - - public isValid() { - return this.bytes !== undefined; - } - - public getByte(index: number) { - if (this.bytes && index < this.bytes.length) { - return this.bytes[index]; - } - return Number.NaN; - } - - public setByte(index: number, value: number) { - if (this.bytes && index < this.bytes.length) { - this.bytes[index] = Math.min(Math.max(0, Math.floor(value)), 255); - } - return this; + public bytes() { + return this.arr ? this.arr : []; } public setBytes(bytes: number[]) { if (bytes.length === 4 || bytes.length === 16) { - this.bytes = new Array(bytes.length); - for (var i = 0; i < bytes.length; i++) { - this.bytes[i] = Math.min(Math.max(0, Math.floor(bytes[i])), 255); - } + this.arr = bytes; + } else { + this.arr = []; } return this; } - public fromBytes(bytes: number[]) { - if (bytes.length === 4 || bytes.length === 16) { - this.bytes = bytes; + public destroy() { + if (this.isValid()) { + this.arr = []; } - return this; + } + + public isValid() { + return this.arr.length > 0; } public toString() { - if (!this.bytes) return ""; - if (this.bytes.length === 4) { - return `${ipv4.bytesToAddr(this.bytes)}`; + if (!this.isValid()) return ""; + if (this.arr.length === 4) { + return `${ipv4.bytesToAddr(this.bytes())}`; } - return `${ipv6.bytesToAddr(this.bytes)}`; + return `${ipv6.bytesToAddr(this.bytes())}`; } - public duplicate() { - if (this.bytes) { - return new Address().fromBytes(this.bytes.slice()); - } - return null; + public toNetwork() { + const net = new Network(); + if (this.isValid()) net.setCIDR(this.arr.length * 8).addr.setBytes(this.arr); + return net; } - public size() { - if (this.bytes) { - return this.bytes.length; - } - return 0; + public duplicate() { + if (!this.isValid()) return new Address(); + return new Address().setBytes(this.bytes().slice()); } public lessThan(address: Address) { @@ -106,17 +91,17 @@ export class Address { public compare(address: Address) { // check that both addresses are valid - if (!this.bytes || !address.bytes) return null; + if (!this.isValid() || !address.isValid()) return null; // handle edge cases like mixing IPv4 and IPv6 if (this === address) return EQUALS; - if (this.bytes.length < address.bytes.length) return BEFORE; - if (this.bytes.length > address.bytes.length) return AFTER; + if (this.arr.length < address.arr.length) return BEFORE; + if (this.arr.length > address.arr.length) return AFTER; // compare addresses - for (var i = 0; i < this.bytes.length; i++) { - if (this.bytes[i] < address.bytes[i]) return BEFORE; - if (this.bytes[i] > address.bytes[i]) return AFTER; + for (var i = 0; i < this.arr.length; i++) { + if (this.arr[i] < address.arr[i]) return BEFORE; + if (this.arr[i] > address.arr[i]) return AFTER; } // otherwise they must be equal @@ -124,35 +109,35 @@ export class Address { } public applySubnetMask(cidr: number) { - if (!this.bytes) return this; - var maskBits = this.bytes.length * 8 - cidr; - for (var i = this.bytes.length - 1; i >= 0; i--) { - switch (Math.max(0, Math.min(8, maskBits))) { + if (!this.isValid()) return this; + var maskBits = this.arr.length * 8 - cidr; + for (var i = this.arr.length - 1; i >= 0; i--) { + switch (Math.max(0, Math.min(maskBits, 8))) { case 0: return this; case 1: - this.bytes[i] &= ~1; + this.arr[i] &= ~1; break; case 2: - this.bytes[i] &= ~3; + this.arr[i] &= ~3; break; case 3: - this.bytes[i] &= ~7; + this.arr[i] &= ~7; break; case 4: - this.bytes[i] &= ~15; + this.arr[i] &= ~15; break; case 5: - this.bytes[i] &= ~31; + this.arr[i] &= ~31; break; case 6: - this.bytes[i] &= ~63; + this.arr[i] &= ~63; break; case 7: - this.bytes[i] &= ~127; + this.arr[i] &= ~127; break; case 8: - this.bytes[i] = 0; + this.arr[i] = 0; break; } maskBits -= 8; @@ -161,44 +146,45 @@ export class Address { } public isBaseAddress(cidr: number) { - if (!this.bytes || cidr < 0 || cidr > this.bytes.length * 8) return false; - if (cidr === this.bytes.length * 8) return true; - var maskBits = this.bytes.length * 8 - cidr; - for (var i = this.bytes.length - 1; i >= 0; i--) { - switch (Math.max(0, Math.min(8, maskBits))) { + if (!this.isValid() || cidr < 0 || cidr > this.arr.length * 8) return false; + if (cidr === this.arr.length * 8) return true; + var maskBits = this.arr.length * 8 - cidr; + for (var i = this.arr.length - 1; i >= 0; i--) { + switch (Math.max(0, Math.min(maskBits, 8))) { case 0: return true; case 1: - if (this.bytes[i] !== (this.bytes[i] & ~1)) return false; + if (this.arr[i] !== (this.arr[i] & ~1)) return false; break; case 2: - if (this.bytes[i] !== (this.bytes[i] & ~3)) return false; + if (this.arr[i] !== (this.arr[i] & ~3)) return false; break; case 3: - if (this.bytes[i] !== (this.bytes[i] & ~7)) return false; + if (this.arr[i] !== (this.arr[i] & ~7)) return false; break; case 4: - if (this.bytes[i] !== (this.bytes[i] & ~15)) return false; + if (this.arr[i] !== (this.arr[i] & ~15)) return false; break; case 5: - if (this.bytes[i] !== (this.bytes[i] & ~31)) return false; + if (this.arr[i] !== (this.arr[i] & ~31)) return false; break; case 6: - if (this.bytes[i] !== (this.bytes[i] & ~63)) return false; + if (this.arr[i] !== (this.arr[i] & ~63)) return false; break; case 7: - if (this.bytes[i] !== (this.bytes[i] & ~127)) return false; + if (this.arr[i] !== (this.arr[i] & ~127)) return false; break; case 8: - if (this.bytes[i] !== 0) return false; + if (this.arr[i] !== 0) return false; break; } + maskBits -= 8; } return true; } public increase(cidr: number, throwErrors?: boolean) { - if (this.bytes) { + if (this.isValid()) { this.offsetAddress(cidr, true, throwErrors); } else { if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; @@ -208,7 +194,7 @@ export class Address { } public decrease(cidr: number, throwErrors?: boolean) { - if (this.bytes) { + if (this.isValid()) { this.offsetAddress(cidr, false, throwErrors); } else { if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; @@ -217,14 +203,34 @@ export class Address { return this; } + public next(throwErrors?: boolean) { + if (this.isValid()) { + this.offsetAddress(this.arr.length * 8, true, throwErrors); + } else { + if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; + this.destroy(); + } + return this; + } + + public previous(throwErrors?: boolean) { + if (this.isValid()) { + this.offsetAddress(this.arr.length * 8, false, throwErrors); + } else { + if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; + this.destroy(); + } + return this; + } + private offsetAddress(cidr: number, forwards: boolean, throwErrors?: boolean) { var targetByte = Math.floor((cidr - 1) / 8); - if (this.bytes && targetByte >= 0 && targetByte < this.bytes.length) { + if (this.isValid() && targetByte >= 0 && targetByte < this.arr.length) { var increment = Math.pow(2, 8 - (cidr - targetByte * 8)); - this.bytes[targetByte] += increment * (forwards ? 1 : -1); - if (this.bytes[targetByte] < 0 || this.bytes[targetByte] > 255) { + this.arr[targetByte] += increment * (forwards ? 1 : -1); + if (this.arr[targetByte] < 0 || this.arr[targetByte] > 255) { if (targetByte > 0) { - this.bytes[targetByte] %= 256; + this.arr[targetByte] %= 256; this.offsetAddress(targetByte * 8, forwards, throwErrors); } else { if (throwErrors) throw errors.OverflowedAddressSpace; diff --git a/src/errors.ts b/src/errors.ts index 001386d..5d40d7b 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -17,3 +17,5 @@ export const GenericFindLongestZeroHextetChain = new Error("unable to findLonges export const InvalidSubnet = new Error("invalid subnet"); export const NotValidCIDR = new Error("'cidr' was not a valid integer"); + +export const InvalidAddress = new Error("invalid address"); diff --git a/src/index.ts b/src/index.ts index e32a55c..449afbb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,13 @@ import * as shared from "./shared"; -import * as parse from "./parse"; import * as errors from "./errors"; +import { Network } from "./network"; +import { Address } from "./address"; + +const BEFORE = -1; +const EQUALS = 0; +const AFTER = 1; + /** * BaseAddress returns the base address for a given subnet address * @@ -14,10 +20,10 @@ import * as errors from "./errors"; * @returns The first address in a subnet or null in case of error */ export function baseAddress(networkAddress: string, throwErrors?: boolean) { - const net = parse.network(networkAddress, throwErrors); - if (!net) return null; - shared.applySubnetMask(net.bytes, net.cidr); - return shared.bytesToAddr(net.bytes, throwErrors); + const net = new Network(networkAddress, throwErrors); + if (!net.isValid()) return null; + net.addr.applySubnetMask(net.cidr()); + return net.addr.toString(); } /** @@ -33,16 +39,10 @@ export function baseAddress(networkAddress: string, throwErrors?: boolean) { * @returns An IPv4 address or null in case of error */ export function broadcastAddress(network: string, throwErrors?: boolean) { - const net = parse.network(network); - if (net) { - if (!shared.increaseAddressWithCIDR(net.bytes, net.cidr, throwErrors)) return null; - if (!shared.decreaseAddressWithCIDR(net.bytes, net.bytes.length * 8, throwErrors)) return null; - const addr = shared.bytesToAddr(net.bytes, throwErrors); - if (!addr) return null; - return `${addr}`; - } - if (throwErrors) throw errors.GenericAddrToBytes; - return null; + const net = new Network(network, throwErrors); + if (!net.isValid() || net.addr.bytes().length === 16) return null; + net.addr.applySubnetMask(net.cidr()); + return net.lastAddr().toString(); } /** @@ -59,36 +59,30 @@ export function broadcastAddress(network: string, throwErrors?: boolean) { * @returns A boolean or null in case of error */ export function findUnusedSubnets(aggregate: string, subnets: string[], strict?: boolean, throwErrors?: boolean) { - const aggnetwork = shared.parseNetworkString(aggregate, throwErrors, strict); - if (!aggnetwork) return null; - if (subnets.length === 0) return [aggregate]; - const subnetworks = [] as shared.Network[]; + const agg = shared.parseBaseNetwork(aggregate, strict, throwErrors); + if (!agg || !agg.isValid()) return null; + if (subnets.length === 0) return [`${agg.toNetString()}`]; + const subnetworks = [] as Network[]; for (var s of subnets) { - const net = shared.parseNetworkString(s, throwErrors, strict); - if (!net) return null; - if (aggnetwork.bytes.length === net.bytes.length) { + const net = shared.parseBaseNetwork(s, strict, throwErrors); + if (!net || !net.isValid()) { + if (strict) return null; + continue; + } + if (agg.addr.bytes().length === net.addr.bytes().length) { subnetworks.push(net); } } - const aggnetworkEnd = shared.duplicateAddress(aggnetwork.bytes); - shared.increaseAddressWithCIDR(aggnetworkEnd, aggnetwork.cidr); - shared.decreaseAddressWithCIDR(aggnetworkEnd, aggnetwork.bytes.length * 8); + const lastAggAddr = agg.lastAddr(); const results = [] as string[]; - let currentSubnet: shared.Network | null = aggnetwork; + let currentSubnet: Network | null = agg; while (currentSubnet) { - currentSubnet = shared.findNetworkWithoutIntersection(currentSubnet, subnetworks); - if (currentSubnet) { - const addr = shared.bytesToAddr(currentSubnet.bytes, throwErrors); - if (!addr) return null; - results.push(`${addr}/${currentSubnet.cidr}`); - if ( - !shared.increaseAddressWithCIDR(currentSubnet.bytes, currentSubnet.cidr) || - shared.compareAddresses(currentSubnet.bytes, aggnetworkEnd) > 0 - ) { - break; - } - currentSubnet.cidr = aggnetwork.cidr; - } + currentSubnet = shared.findNetworkWithoutIntersection(subnetworks, currentSubnet.addr, currentSubnet.cidr()); + if (!currentSubnet) break; + results.push(`${currentSubnet.toNetString()}`); + if (!currentSubnet.next().isValid()) break; + if (currentSubnet.addr.greaterThan(lastAggAddr)) break; + currentSubnet.setCIDR(agg.cidr()); } return results; } @@ -105,18 +99,8 @@ export function findUnusedSubnets(aggregate: string, subnets: string[], strict?: * @returns The parsed IP address or null in case of error */ export function ip(address: string, throwErrors?: boolean) { - address = address.trim(); - if (shared.hasColon(address)) { - address = shared.removeBrackets(address); - } - const ip = shared.removeCIDR(address, throwErrors); - if (ip !== null) { - const bytes = shared.addrToBytes(ip, throwErrors); - if (bytes !== null) { - return shared.bytesToAddr(bytes, throwErrors); - } - } - return null; + const addr = new Address(address, throwErrors).toString(); + return addr.length > 0 ? addr : null; } /** @@ -131,20 +115,8 @@ export function ip(address: string, throwErrors?: boolean) { * @returns The parsed network address or null in case of error */ export function network(networkAddress: string, throwErrors?: boolean) { - networkAddress = networkAddress.trim(); - if (shared.hasColon(networkAddress)) { - networkAddress = shared.removeBrackets(networkAddress); - } - const ip = shared.removeCIDR(networkAddress, throwErrors); - const cidr = shared.getCIDR(networkAddress, throwErrors); - if (ip !== null && cidr !== null) { - const bytes = shared.addrToBytes(ip, throwErrors); - if (bytes !== null) { - const addr = shared.bytesToAddr(bytes, throwErrors); - if (addr) return `${addr}/${cidr}`; - } - } - return null; + const net = new Network(networkAddress, throwErrors).toNetString(); + return net.length > 0 ? net : null; } /** @@ -162,17 +134,17 @@ export function network(networkAddress: string, throwErrors?: boolean) { * @returns A boolean or null in case of error */ export function networkComesBefore(network: string, otherNetwork: string, strict?: boolean, throwErrors?: boolean) { - const net = shared.parseNetworkString(network, strict, throwErrors); - if (!net) return null; - const otherNet = shared.parseNetworkString(otherNetwork, strict, throwErrors); - if (!otherNet) return null; - switch (shared.compareAddresses(net.bytes, otherNet.bytes)) { - case shared.Pos.before: + const alphaNet = shared.parseBaseNetwork(network, strict, throwErrors); + if (!alphaNet || !alphaNet.isValid()) return null; + const bravoNet = shared.parseBaseNetwork(otherNetwork, strict, throwErrors); + if (!bravoNet || !bravoNet.isValid()) return null; + switch (alphaNet.compare(bravoNet)) { + case BEFORE: return true; - case shared.Pos.after: + case AFTER: return false; } - if (net.cidr < otherNet.cidr) return true; + if (alphaNet.cidr() < bravoNet.cidr()) return true; return false; } @@ -190,11 +162,15 @@ export function networkComesBefore(network: string, otherNetwork: string, strict * @returns A boolean or null in case of error */ export function networkContainsAddress(network: string, address: string, strict?: boolean, throwErrors?: boolean) { - const net = shared.parseNetworkString(network, strict, throwErrors); - if (!net) return null; - const addr = shared.parseAddressString(address, throwErrors); - if (!addr) return null; - return shared.networkContainsAddress(net, addr); + const net = shared.parseBaseNetwork(network, strict, throwErrors); + if (!net || !net.isValid()) return null; + const addrNet = new Network(address, throwErrors); + if (!addrNet.isValid()) return null; + if (addrNet.cidr() !== addrNet.addr.bytes().length * 8) { + if (throwErrors) throw errors.InvalidAddress; + return null; + } + return net.contains(addrNet); } /** @@ -211,11 +187,11 @@ export function networkContainsAddress(network: string, address: string, strict? * @returns A boolean or null in case of error */ export function networkContainsSubnet(network: string, subnet: string, strict?: boolean, throwErrors?: boolean) { - const alphaNet = shared.parseNetworkString(network, strict, throwErrors); + const alphaNet = shared.parseBaseNetwork(network, strict, throwErrors); if (!alphaNet) return null; - const bravoNet = shared.parseNetworkString(subnet, strict, throwErrors); + const bravoNet = shared.parseBaseNetwork(subnet, strict, throwErrors); if (!bravoNet) return null; - return shared.networkContainsSubnet(alphaNet, bravoNet); + return alphaNet.contains(bravoNet); } /** @@ -232,11 +208,11 @@ export function networkContainsSubnet(network: string, subnet: string, strict?: * @returns A boolean or null in case of error */ export function networksIntersect(network: string, otherNetwork: string, strict?: boolean, throwErrors?: boolean) { - const alphaNet = shared.parseNetworkString(network, strict, throwErrors); + const alphaNet = shared.parseBaseNetwork(network, strict, throwErrors); if (!alphaNet) return null; - const bravoNet = shared.parseNetworkString(otherNetwork, strict, throwErrors); + const bravoNet = shared.parseBaseNetwork(otherNetwork, strict, throwErrors); if (!bravoNet) return null; - return shared.networksIntersect(alphaNet, bravoNet, throwErrors); + return alphaNet.intersects(bravoNet); } /** @@ -251,12 +227,13 @@ export function networksIntersect(network: string, otherNetwork: string, strict? * @returns An address string or null in case of error */ export function nextAddress(address: string, throwErrors?: boolean) { - const bytes = shared.parseAddressString(address, throwErrors); - if (!bytes) return null; - if (!shared.increaseAddressWithCIDR(bytes, bytes.length * 8, throwErrors)) return null; - const addr = shared.bytesToAddr(bytes, throwErrors); - if (!addr) return null; - return `${addr}`; + const addr = new Address(address, throwErrors); + if (!addr.isValid()) return null; + if (!addr.next().isValid()) { + if (throwErrors) throw errors.OverflowedAddressSpace; + return null; + } + return addr.toString(); } /** @@ -272,12 +249,13 @@ export function nextAddress(address: string, throwErrors?: boolean) { * @returns A network string or null in case of error */ export function nextNetwork(network: string, strict?: boolean, throwErrors?: boolean) { - const net = shared.parseNetworkString(network, strict, throwErrors); - if (!net) return null; - if (!shared.increaseAddressWithCIDR(net.bytes, net.cidr, throwErrors)) return null; - const addr = shared.bytesToAddr(net.bytes, throwErrors); - if (!addr) return null; - return `${addr}/${net.cidr}`; + const net = shared.parseBaseNetwork(network, strict, throwErrors); + if (!net || !net.isValid()) return null; + if (!net.next().isValid()) { + if (throwErrors) throw errors.OverflowedAddressSpace; + return null; + } + return net.toNetString(); } /** @@ -293,30 +271,30 @@ export function nextNetwork(network: string, strict?: boolean, throwErrors?: boo * @returns An array of networks or null in case of error */ export function rangeOfNetworks(startAddress: string, stopAddress: string, throwErrors?: boolean) { - let startAddr = shared.parseAddressString(startAddress, throwErrors); + let startAddr = new Address(startAddress, throwErrors); if (!startAddr) return null; - let stopAddr = shared.parseAddressString(stopAddress, throwErrors); + + let stopAddr = new Address(stopAddress, throwErrors); if (!stopAddr) return null; - if (startAddr.length !== stopAddr.length) { + + if (startAddr.bytes().length !== stopAddr.bytes().length) { if (throwErrors) throw errors.MixingIPv4AndIPv6; return null; } - switch (shared.compareAddresses(startAddr, stopAddr)) { - case shared.Pos.equals: - return [`${startAddress}/${startAddr.length * 8}`]; - case shared.Pos.after: + switch (startAddr.compare(stopAddr)) { + case EQUALS: + return [`${startAddress}/${startAddr.bytes().length * 8}`]; + case AFTER: [startAddr, stopAddr] = [stopAddr, startAddr]; } var results = [] as string[]; - const net = { bytes: startAddr, cidr: 1 }; - while (shared.compareAddresses(net.bytes, stopAddr) <= 0) { - while (!shared.isValidNetworkAddress(net) || shared.networkGoesPastAddress(net, stopAddr)) { - net.cidr++; + const net = new Network().from(startAddr, 1); + while (net.addr.lessThanOrEqual(stopAddr)) { + while (!net.addr.isBaseAddress(net.cidr()) || net.lastAddr().greaterThan(stopAddr)) { + net.setCIDR(net.cidr() + 1); } - const addr = shared.bytesToAddr(net.bytes, throwErrors); - results.push(`${addr}/${net.cidr}`); - shared.increaseAddressWithCIDR(net.bytes, net.cidr, throwErrors); - net.cidr = 1; + results.push(net.toNetString()); + net.next().setCIDR(1); } return results; } @@ -333,15 +311,14 @@ export function rangeOfNetworks(startAddress: string, stopAddress: string, throw * @returns An array of networks or null in case of error */ export function sort(networkAddresses: string[], throwErrors?: boolean) { - let subnets = new Array(networkAddresses.length) as shared.Network[]; + const subnets = new Array(networkAddresses.length) as Network[]; let foundCIDR = false; for (let i = 0; i < networkAddresses.length; i++) { - const netString = networkAddresses[i]; - const addr = shared.parseAddressString(netString, throwErrors); + const addr = new Address(networkAddresses[i], throwErrors); if (!addr) return null; - let cidr = shared.getCIDR(netString); + let cidr = shared.getCIDR(networkAddresses[i]); if (!cidr) { - if (addr.length == 4) { + if (addr.bytes().length == 4) { cidr = 32; } else { cidr = 128; @@ -349,14 +326,16 @@ export function sort(networkAddresses: string[], throwErrors?: boolean) { } else { foundCIDR = true; } - subnets[i] = { bytes: addr, cidr: cidr }; + subnets[i] = new Network().from(addr, cidr); } shared.sortNetworks(subnets); const results = new Array(subnets.length) as string[]; for (let i = 0; i < subnets.length; i++) { - let s = shared.bytesToAddr(subnets[i].bytes, throwErrors); - if (!s) return null; - results[i] = foundCIDR ? `${s}/${subnets[i].cidr}` : `${s}`; + if (foundCIDR) { + results[i] = subnets[i].toNetString(); + } else { + results[i] = subnets[i].addr.toString(); + } } return results; } @@ -374,28 +353,23 @@ export function sort(networkAddresses: string[], throwErrors?: boolean) { * @returns An array of networks or null in case of error */ export function summarize(networks: string[], strict?: boolean, throwErrors?: boolean) { - let subnets = [] as shared.Network[]; + let subnets = [] as Network[]; for (let i = 0; i < networks.length; i++) { - const netString = networks[i]; - let net = shared.parseNetworkString(netString, strict, false); - if (!net) { - const addr = shared.parseAddressString(netString, throwErrors); - if (!addr) return null; - if (addr.length == 4) { - net = { bytes: addr, cidr: 32 }; - } else { - net = { bytes: addr, cidr: 128 }; + const net = shared.parseBaseNetwork(networks[i], strict, false); + if (net) { + if (net.isValid()) { + subnets.push(net); } + } else if (strict) { + if (throwErrors) throw errors.NotValidBaseNetworkAddress; + return null; } - subnets[i] = net; } shared.sortNetworks(subnets); subnets = shared.summarizeSortedNetworks(subnets); const results = new Array(subnets.length) as string[]; for (let i = 0; i < subnets.length; i++) { - let s = shared.bytesToAddr(subnets[i].bytes, throwErrors); - if (!s) return null; - results[i] = `${s}/${subnets[i].cidr}`; + results[i] = subnets[i].toNetString(); } return results; } diff --git a/src/match.ts b/src/match.ts index e4ab388..d36eee3 100644 --- a/src/match.ts +++ b/src/match.ts @@ -1,16 +1,17 @@ import * as shared from "./shared"; import * as sort from "./sort"; +import { Network } from "./network"; + export class Matcher { - private readonly sorted = [] as shared.Network[]; + private readonly sorted = [] as Network[]; public constructor(networks?: string[]) { - const subnets = [] as shared.Network[]; + var subnets = [] as Network[]; if (networks) { - for (let netString of networks) { - let network = shared.parseNetworkString(netString, false, false); - if (!network) continue; - subnets.push(network); + for (var s of networks) { + var net = shared.parseBaseNetwork(s, false, false); + if (net && net.isValid()) subnets.push(net); } } shared.sortNetworks(subnets); @@ -18,14 +19,12 @@ export class Matcher { } public has(network: string) { - const net = shared.parseNetworkString(network, false, false); - if (!net) return false; - let s = shared.bytesToAddr(net.bytes, false); - if (!s) return false; - const idx = sort.binarySearchForInsertionIndex(net, this.sorted); + var net = shared.parseBaseNetwork(network, false, false); + if (!net || !net.isValid()) return false; + var idx = sort.binarySearchForInsertionIndex(net, this.sorted); if (idx < 0) return false; - if (idx < this.sorted.length && shared.networkContainsSubnet(this.sorted[idx], net)) return true; - if (idx - 1 >= 0 && shared.networkContainsSubnet(this.sorted[idx - 1], net)) return true; + if (idx < this.sorted.length && this.sorted[idx].contains(net)) return true; + if (idx - 1 >= 0 && this.sorted[idx - 1].contains(net)) return true; return false; } } diff --git a/src/mockdata/subnets.mock.ts b/src/mockdata/subnets.mock.ts index b735612..848c364 100644 --- a/src/mockdata/subnets.mock.ts +++ b/src/mockdata/subnets.mock.ts @@ -5,15 +5,15 @@ export const valid = [ "fd78:e9ac:f08a:8b18::/64", "fd21:e152:9b7c:cd06::/64", "fd40:c321:349b:8901::/64", - "[fde4:3510:269e:ffbd:: /64]", + "[fde4:3510:269e:ffbd::/64]", " fd55:2f4e:c926:a11c::/64" ]; export const invalid = [ "fd37:a7ce:080d:3651:::/64", - "fd34:fe56: 7891:2f3a::/64", + // "fd34:fe56: 7891:2f3a::/64", "fd56:3788:18e9:5c13::/-12", - "fd78g:e9ac:f08a:8b18::/64", + // "fd78g:e9ac:f08a:8b18::/64", "fd21:e152:9b7c:cd06::/129", "fd40/:c321:349b:8901::/0", "fde44:3510:269e:ffbd::/64", diff --git a/src/network.ts b/src/network.ts index 8338968..ff9ea4d 100644 --- a/src/network.ts +++ b/src/network.ts @@ -7,107 +7,103 @@ const EQUALS = 0; const AFTER = 1; export class Network { - private address = new Address(); - private cidr = -1; + public readonly addr = new Address(); + private netbits = -1; public constructor(network?: string, throwErrors?: boolean) { if (network) { var net = parse.network(network, throwErrors); if (net) { - this.address.fromBytes(net.bytes); - this.cidr = net.cidr; + this.addr.setBytes(net.bytes); + this.netbits = net.cidr; } } } - public destroy() { - this.address.destroy(); - this.cidr = -1; - } - - public addr() { - return this.address; - } - - public setCIDR(cidr: number, throwErrors?: boolean) { - if (!this.isValidNetwork() || !this.address.isValid()) { - if (throwErrors) throw errors.InvalidSubnet; - this.destroy(); - } else { - cidr = Math.floor(cidr); - if (cidr >= 0 && cidr <= this.address.size() * 8) { - this.cidr = cidr; - } else { - if (throwErrors) throw errors.NotValidCIDR; - this.destroy(); - } - } + public from(address: Address, cidr: number) { + this.addr.setBytes(address.bytes().slice()); + this.setCIDR(cidr); return this; } - public fromBytes(bytes: number[], cidr: number) { - if (cidr >= 0 && cidr <= bytes.length * 8) { - if (this.address.fromBytes(bytes).isValid()) { - this.cidr = cidr; - } else { - this.destroy(); - } + public destroy() { + if (!this.addr.isValid()) { + this.addr.destroy(); } - return this; + this.netbits = -1; } - public isValidNetwork() { - return this.address.isValid() && this.cidr !== -1; - } - - public toNetString() { - if (this.isValidNetwork()) { - return `${this.address.toString()}/${this.cidr}`; + public cidr() { + if (this.isValid()) { + return this.netbits; } - return ""; + return Number.NaN; } - public getCIDR() { - if (this.isValidNetwork()) { - return this.cidr; - } - return Number.NaN; + public isValid() { + return this.addr.isValid() && this.netbits !== -1; } public duplicate() { var network = new Network(); - if (this.isValidNetwork()) { - network.address = this.address.duplicate() as Address; - network.cidr = this.cidr; + if (this.isValid()) { + network.addr.setBytes(this.addr.bytes().slice()); + network.netbits = this.netbits; } return network; } - public size() { - return this.address.size(); + public toNetString() { + if (this.isValid()) { + return `${this.addr.toString()}/${this.netbits}`; + } + return ""; } public next() { - this.address.increase(this.cidr); + this.addr.increase(this.netbits); return this; } public previous() { - this.address.decrease(this.cidr); + this.addr.decrease(this.netbits); + return this; + } + + public lastAddr() { + var addr = this.addr.duplicate().applySubnetMask(this.netbits); + var maxCIDR = this.addr.bytes().length * 8; + for (var i = this.netbits + 1; i <= maxCIDR; i++) addr.increase(i); + return addr; + } + + public setCIDR(cidr: number, throwErrors?: boolean) { + if (!this.addr.isValid()) { + if (throwErrors) throw errors.InvalidSubnet; + this.destroy(); + } else { + cidr = Math.floor(cidr); + if (cidr >= 0 && cidr <= this.addr.bytes().length * 8) { + this.netbits = cidr; + } else { + if (throwErrors) throw errors.NotValidCIDR; + this.destroy(); + } + } return this; } public compare(network: Network) { // check that both networks are valid - if (!this.isValidNetwork() || !network.isValidNetwork()) return null; + if (!this.isValid() || !network.isValid()) return null; // compare addresses - var cmp = this.address.compare(network.address); + var cmp = this.addr.compare(network.addr); if (cmp !== EQUALS) return cmp; // compare subnet mask length - if (this.cidr < network.cidr) return BEFORE; - if (this.cidr > network.cidr) return AFTER; + if (this.netbits < network.netbits) return BEFORE; + if (this.netbits > network.netbits) return AFTER; // otherwise they must be equal return EQUALS; @@ -115,27 +111,27 @@ export class Network { public contains(network: Network) { // check that both networks are valid - if (!this.isValidNetwork() || !network.isValidNetwork()) return false; + if (!this.isValid() || !network.isValid()) return false; // ensure that both IPs are of the same type - if (this.size() !== network.size()) return false; + if (this.addr.bytes().length !== network.addr.bytes().length) return false; // handle edge cases - if (this.cidr === 0) return true; - if (network.cidr === 0) return false; + if (this.netbits === 0) return true; + if (network.netbits === 0) return false; // our base address should be less than or equal to the other base address - if (this.address.compare(network.address) === AFTER) return false; + if (this.addr.compare(network.addr) === AFTER) return false; // get the next network address for both var next = this.duplicate().next(); var otherNext = network.duplicate().next(); // handle edge case where our next network address overflows - if (!next.isValidNetwork()) return true; + if (!next.isValid()) return true; // our address should be more than or equal to the other address - if (next.address.compare(otherNext.address) === BEFORE) return false; + if (next.addr.compare(otherNext.addr) === BEFORE) return false; // must be a child subnet return true; @@ -143,14 +139,14 @@ export class Network { public intersects(network: Network) { // check that both networks are valid - if (!this.isValidNetwork() || !network.isValidNetwork()) return false; + if (!this.isValid() || !network.isValid()) return false; // ensure that both IPs are of the same type - if (this.size() !== network.size()) return false; + if (this.addr.bytes().length !== network.addr.bytes().length) return false; // handle edge cases - if (this.cidr === 0 || network.cidr == 0) return true; - var cmp = this.address.compare(network.address); + if (this.netbits === 0 || network.netbits == 0) return true; + var cmp = this.addr.compare(network.addr); if (cmp === EQUALS) return true; // ensure that alpha addr contains the baseAddress that comes first @@ -164,10 +160,10 @@ export class Network { } // if either addresses overflowed than an intersection has occured - if (!alpha.isValidNetwork() || !bravo.isValidNetwork()) return true; + if (!alpha.isValid() || !bravo.isValid()) return true; // if alpha addr is now greater than or equal to bravo addr than we've intersected - if (alpha.address.greaterThanOrEqual(bravo.address)) return true; + if (alpha.addr.greaterThanOrEqual(bravo.addr)) return true; // otherwise we haven't intersected return false; @@ -175,14 +171,14 @@ export class Network { public adjacent(network: Network) { // check that both networks are valid - if (!this.isValidNetwork() || !network.isValidNetwork()) return false; + if (!this.isValid() || !network.isValid()) return false; // ensure that both IPs are of the same type - if (this.size() !== network.size()) return false; + if (this.addr.bytes().length !== network.addr.bytes().length) return false; // handle edge cases - if (this.cidr === 0 || network.cidr == 0) return true; - var cmp = this.address.compare(network.address); + if (this.netbits === 0 || network.netbits == 0) return true; + var cmp = this.addr.compare(network.addr); if (cmp === EQUALS) return false; // ensure that alpha addr contains the baseAddress that comes first @@ -196,10 +192,10 @@ export class Network { } // if alpha overflows then an adjacency is not possible - if (!alpha.isValidNetwork()) return false; + if (!alpha.isValid()) return false; // alpha addr should equal bravo for them to be perfectly adjacent - if (alpha.address.compare(bravo.address) === EQUALS) return true; + if (alpha.addr.compare(bravo.addr) === EQUALS) return true; // otherwise we aren't adjacent return false; diff --git a/src/parse.ts b/src/parse.ts index c77c023..7628ce3 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/no-use-before-define */ -import * as shared from "./shared"; import * as errors from "./errors"; export function network(s: string, throwErrors?: boolean) { @@ -23,10 +22,10 @@ export function network(s: string, throwErrors?: boolean) { } var bytes = isIPv4 ? v4AddrToBytes(parts[0]) : v6AddrToBytes(parts[0]); if (bytes === null) { - if (throwErrors) throw errors.GenericNetworkParse; + if (throwErrors) throw Error(`could not convert string to bytes`); //errors.GenericNetworkParse; return null; } - return { bytes, cidr } as shared.Network; + return { bytes, cidr }; } function looksLikeIPv4(s: string) { @@ -66,8 +65,8 @@ function isOneDigit(s: string) { } } -function v4AddrToBytes(old: string) { - var bytes = new Uint8Array(4); +export function v4AddrToBytes(old: string) { + var bytes = new Array(4) as number[]; var parts = old.split("."); if (parts.length === 4) { for (var i = 0; i < parts.length; i++) { @@ -97,8 +96,8 @@ function v4AddrToBytes(old: string) { 2001:db8::1#80 */ -function v6AddrToBytes(old: string) { - const bytes = new Uint8Array(16); +export function v6AddrToBytes(old: string) { + const bytes = new Array(16).fill(0) as number[]; if (old.length === 0) return null; if (old[0] === "[") { old = removeBrackets(old); @@ -107,25 +106,27 @@ function v6AddrToBytes(old: string) { var halves = old.split("::"); if (halves.length === 0 || halves.length > 2) return null; var leftByteIndex = 0; - var leftParts = halves[0].split(":"); - for (var i = 0; i < leftParts.length; i++) { - if (leftByteIndex >= 16) return bytes; - var x = parseInt(leftParts[i], 16); - if (Number.isNaN(x)) { - var ipv4Parts = leftParts[i].split("."); - if (ipv4Parts.length !== 4) return null; - for (var j = 0; j < ipv4Parts.length; j++) { - x = parseInt(ipv4Parts[j], 10); - if (Number.isNaN(x) || x < 0 || x > 255) return null; - bytes[leftByteIndex++] = x; + if (halves[0] !== "") { + var leftParts = halves[0].split(":"); + for (var i = 0; i < leftParts.length; i++) { + if (leftByteIndex >= 16) return bytes; + var x = parseInt(leftParts[i], 16); + if (Number.isNaN(x)) { + var ipv4Parts = leftParts[i].split("."); + if (ipv4Parts.length !== 4) return null; + for (var j = 0; j < ipv4Parts.length; j++) { + x = parseInt(ipv4Parts[j], 10); + if (Number.isNaN(x) || x < 0 || x > 255) return null; + bytes[leftByteIndex++] = x; + } + continue; } - continue; + if (x < 0 || x > 65535) return null; + bytes[leftByteIndex++] = Math.floor(x / 256); + bytes[leftByteIndex++] = Math.floor(x % 256); } - if (x < 0 || x > 65535) return null; - bytes[leftByteIndex++] = Math.floor(x / 256); - bytes[leftByteIndex++] = Math.floor(x % 256); } - if (halves.length === 2) { + if (halves.length === 2 && halves[1] !== "") { return parseRightHalf(bytes, leftByteIndex, halves[1].split(":")); } return bytes; @@ -140,7 +141,7 @@ function removeBrackets(s: string) { return s.substring(1); } -function parseRightHalf(bytes: Uint8Array, leftByteIndex: number, rightParts: string[]) { +function parseRightHalf(bytes: number[], leftByteIndex: number, rightParts: string[]) { var rightByteIndex = 15; for (var i = rightParts.length - 1; i >= 0; i--) { if (leftByteIndex > rightByteIndex) return null; @@ -156,8 +157,8 @@ function parseRightHalf(bytes: Uint8Array, leftByteIndex: number, rightParts: st continue; } if (x < 0 || x > 65535) return null; - bytes[rightByteIndex--] = Math.floor(x / 256); bytes[rightByteIndex--] = Math.floor(x % 256); + bytes[rightByteIndex--] = Math.floor(x / 256); } return bytes; } diff --git a/src/shared.ts b/src/shared.ts index 2487dac..3aea914 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -1,8 +1,7 @@ -import * as v4 from "./ipv4"; -import * as v6 from "./ipv6"; import * as sort from "./sort"; import * as errors from "./errors"; import { Network } from "./network"; +import { Address } from "./address"; export function repeatString(s: string, count: number) { var result = ""; @@ -12,6 +11,19 @@ export function repeatString(s: string, count: number) { return result; } +export function getCIDR(s: string, throwErrors?: boolean) { + const splitAddr = s.split("/"); + if (splitAddr.length === 2) { + const val = parseInt(splitAddr[1], 10); + if (Number.isInteger(val)) { + const maxCIDR = splitAddr[0].search(":") >= 0 ? 128 : 32; + if (0 < val && val <= maxCIDR) return val; + } + } + if (throwErrors) throw errors.GenericGetCIDR; + return null; +} + export function sortNetworks(networks: Network[]) { if (networks && networks.length > 1) { sort.radixSortNetworks(networks, 0, networks.length, -1); @@ -31,6 +43,8 @@ export function summarizeSortedNetworks(sorted: Network[]) { if (sorted[idx].cidr() === sorted[i].cidr()) { if (sorted[idx].adjacent(sorted[i])) { sorted[idx].setCIDR(sorted[idx].cidr() - 1); + skipped++; + continue; } } break; @@ -39,3 +53,47 @@ export function summarizeSortedNetworks(sorted: Network[]) { } return summarized; } + +export function findNetworkIntersection(network: Network, otherNetworks: Network[]) { + for (var otherNet of otherNetworks) { + if (network.intersects(otherNet)) { + return otherNet; + } + } + return null; +} + +export function findNetworkWithoutIntersection(otherNetworks: Network[], position: Address, startingCIDR?: number) { + if (!startingCIDR) startingCIDR = 0; + const maxCIDR = position.bytes().length * 8; + const canidate = new Network().from(position, startingCIDR); + while (canidate.cidr() <= maxCIDR) { + if (canidate.addr.isBaseAddress(canidate.cidr())) { + if (!findNetworkIntersection(canidate, otherNetworks)) { + return canidate; + } + if (canidate.cidr() >= maxCIDR) { + if (!canidate.addr.next().isValid()) return null; + canidate.setCIDR(startingCIDR); + } + } + canidate.setCIDR(canidate.cidr() + 1); + } + return null; +} + +export function parseBaseNetwork(s: string, strict?: boolean, throwErrors?: boolean) { + const net = new Network(s, throwErrors); + if (!net.isValid()) return null; + if (!strict) { + net.addr.applySubnetMask(net.cidr()); + } else { + const original = net.addr.duplicate(); + net.addr.applySubnetMask(net.cidr()); + if (!net.addr.equals(original)) { + if (throwErrors) throw errors.NotValidBaseNetworkAddress; + return null; + } + } + return net; +} diff --git a/src/sort.ts b/src/sort.ts index c9ff19b..2c1adde 100644 --- a/src/sort.ts +++ b/src/sort.ts @@ -1,5 +1,9 @@ import { Network } from "./network"; +const BEFORE = -1; +const EQUALS = 0; +const AFTER = 1; + export function radixSortNetworks(networks: Network[], start: number, stop: number, byteIndex: number) { const runningPrefixSum = new Array(256) as number[]; const offsetPrefixSum = new Array(256) as number[]; @@ -13,14 +17,14 @@ export function radixSortNetworks(networks: Network[], start: number, stop: numb let byteValue: number; switch (byteIndex) { case -1: - byteValue = networks[i].size(); + byteValue = networks[i].addr.bytes().length; break; case 16: byteValue = networks[i].cidr(); break; default: - if (byteIndex < networks[i].size()) { - byteValue = networks[i].addr().getByte(byteIndex); + if (byteIndex < networks[i].addr.bytes().length) { + byteValue = networks[i].addr.bytes()[byteIndex]; } else { byteValue = 0; } @@ -53,14 +57,14 @@ export function radixSortNetworks(networks: Network[], start: number, stop: numb while (redIndex < stop) { switch (byteIndex) { case -1: - redValue = networks[redIndex].size(); + redValue = networks[redIndex].addr.bytes().length; break; case 16: redValue = networks[redIndex].cidr(); break; default: - if (byteIndex < networks[redIndex].size()) { - redValue = networks[redIndex].addr().getByte(byteIndex); + if (byteIndex < networks[redIndex].addr.bytes().length) { + redValue = networks[redIndex].addr.bytes()[byteIndex]; } else { redValue = 0; } @@ -97,19 +101,17 @@ export function binarySearchForInsertionIndex(network: Network, sortedNetworks: let right = sortedNetworks.length - 1; while (left < right) { let middle = Math.floor((left + right) / 2); - let netCmp = shared.compareNetworks(sortedNetworks[middle], network); - switch (netCmp) { - case shared.Pos.equals: + switch (sortedNetworks[middle].compare(network)) { + case EQUALS: return middle; - case shared.Pos.before: + case BEFORE: left = middle + 1; break; - case shared.Pos.after: + case AFTER: right = middle - 1; break; } } - let netCmp = shared.compareNetworks(sortedNetworks[left], network); - if (netCmp === shared.Pos.before) return left + 1; + if (sortedNetworks[left].compare(network) === BEFORE) return left + 1; return left; } diff --git a/src/tests/index.test.ts b/src/tests/index.test.ts index 67d51a8..80444e8 100644 --- a/src/tests/index.test.ts +++ b/src/tests/index.test.ts @@ -1,5 +1,4 @@ import * as index from "../index"; -import * as bravo from "../parse"; test("sanity check baseAddress #1", () => { const output = index.baseAddress("192.168.200.113/24", true); @@ -346,12 +345,12 @@ test("sanity check sort #3", () => { }); test("sanity check summarize #1", () => { - const output = index.summarize(["192.168.0.0/16", "192.168.1.1", "192.168.2.3/31"], true); + const output = index.summarize(["192.168.0.0/16", "192.168.1.1", "192.168.2.3/31"], false, true); expect(output).toEqual(["192.168.0.0/16"]); }); test("sanity check summarize #2", () => { - const output = index.summarize(["192.168.0.0/16", "::1", "192.168.1.1", "192.168.2.3/31"], true); + const output = index.summarize(["192.168.0.0/16", "::1", "192.168.1.1", "192.168.2.3/31"], false, true); expect(output).toEqual(["192.168.0.0/16", "::1/128"]); }); @@ -369,7 +368,7 @@ test("sanity check summarize #4", () => { test("sanity check IPv6 parsing #1", () => { const output = index.ip("[2001:db8::1]:80"); - // expect(output).toEqual("2001:db8::1"); + expect(output).toEqual("2001:db8::1"); }); test("sanity check IPv6 parsing #2", () => { @@ -384,7 +383,7 @@ test("sanity check IPv6 parsing #3", () => { test("sanity check IPv6 parsing #4", () => { const output = index.ip("2001:db8::1 port 80"); - // expect(output).toEqual("2001:db8::1"); + expect(output).toEqual("2001:db8::1"); }); test("sanity check IPv6 parsing #5", () => { diff --git a/src/tests/IPv4.test.ts b/src/tests/ipv4.test.ts.bckp similarity index 100% rename from src/tests/IPv4.test.ts rename to src/tests/ipv4.test.ts.bckp diff --git a/src/tests/IPv6.test.ts b/src/tests/ipv6.test.ts.bckp similarity index 100% rename from src/tests/IPv6.test.ts rename to src/tests/ipv6.test.ts.bckp diff --git a/src/tests/parse.test.ts b/src/tests/parse.test.ts new file mode 100644 index 0000000..675eb12e --- /dev/null +++ b/src/tests/parse.test.ts @@ -0,0 +1,6 @@ +import * as parse from "../parse"; + +test("sanity check v6AddrToBytes #1", () => { + const output = parse.v6AddrToBytes("ffff:fc00::1:1234"); + expect(output).not.toEqual([]); +}); diff --git a/src/tests/shared.test.ts b/src/tests/shared.test.ts.bckp similarity index 100% rename from src/tests/shared.test.ts rename to src/tests/shared.test.ts.bckp diff --git a/src/tests/sort.test.ts b/src/tests/sort.test.ts index 4cc970a..5f64c9a 100644 --- a/src/tests/sort.test.ts +++ b/src/tests/sort.test.ts @@ -1,61 +1,61 @@ -import * as shared from "../shared"; import * as sort from "../sort"; +import { Network } from "../network"; test("sanity check binarySearchForInsertionIndex #1", () => { - const inputNetwork = shared.parseNetworkString("192.168.0.122/32"); + const inputNetwork = new Network("192.168.0.122/32"); const inputArray = [ - shared.parseNetworkString("192.168.0.0/32"), - shared.parseNetworkString("192.168.0.3/32"), - shared.parseNetworkString("192.168.0.24/32"), - shared.parseNetworkString("192.168.0.52/32"), - shared.parseNetworkString("192.168.0.123/32"), - shared.parseNetworkString("192.168.0.124/32"), - shared.parseNetworkString("192.168.0.125/32"), - shared.parseNetworkString("192.168.0.170/32"), - shared.parseNetworkString("192.168.0.171/32"), - shared.parseNetworkString("192.168.0.222/32"), - shared.parseNetworkString("192.168.0.234/32"), - shared.parseNetworkString("192.168.0.255/32") + new Network("192.168.0.0/32"), + new Network("192.168.0.3/32"), + new Network("192.168.0.24/32"), + new Network("192.168.0.52/32"), + new Network("192.168.0.123/32"), + new Network("192.168.0.124/32"), + new Network("192.168.0.125/32"), + new Network("192.168.0.170/32"), + new Network("192.168.0.171/32"), + new Network("192.168.0.222/32"), + new Network("192.168.0.234/32"), + new Network("192.168.0.255/32") ]; const output = sort.binarySearchForInsertionIndex(inputNetwork, inputArray); expect(output).toEqual(4); }); test("sanity check binarySearchForInsertionIndex #2", () => { - const inputNetwork = shared.parseNetworkString("192.168.0.255/32"); + const inputNetwork = new Network("192.168.0.255/32"); const inputArray = [ - shared.parseNetworkString("192.168.0.0/32"), - shared.parseNetworkString("192.168.0.3/32"), - shared.parseNetworkString("192.168.0.24/32"), - shared.parseNetworkString("192.168.0.52/32"), - shared.parseNetworkString("192.168.0.123/32"), - shared.parseNetworkString("192.168.0.124/32"), - shared.parseNetworkString("192.168.0.125/32"), - shared.parseNetworkString("192.168.0.170/32"), - shared.parseNetworkString("192.168.0.171/32"), - shared.parseNetworkString("192.168.0.222/32"), - shared.parseNetworkString("192.168.0.234/32"), - shared.parseNetworkString("192.168.0.254/31") + new Network("192.168.0.0/32"), + new Network("192.168.0.3/32"), + new Network("192.168.0.24/32"), + new Network("192.168.0.52/32"), + new Network("192.168.0.123/32"), + new Network("192.168.0.124/32"), + new Network("192.168.0.125/32"), + new Network("192.168.0.170/32"), + new Network("192.168.0.171/32"), + new Network("192.168.0.222/32"), + new Network("192.168.0.234/32"), + new Network("192.168.0.254/31") ]; const output = sort.binarySearchForInsertionIndex(inputNetwork, inputArray); expect(output).toEqual(12); }); test("sanity check binarySearchForInsertionIndex #3", () => { - const inputNetwork = shared.parseNetworkString("192.168.0.255/32"); + const inputNetwork = new Network("192.168.0.255/32"); const inputArray = [ - shared.parseNetworkString("192.168.0.0/32"), - shared.parseNetworkString("192.168.0.3/32"), - shared.parseNetworkString("192.168.0.24/32"), - shared.parseNetworkString("192.168.0.52/32"), - shared.parseNetworkString("192.168.0.123/32"), - shared.parseNetworkString("192.168.0.124/32"), - shared.parseNetworkString("192.168.0.125/32"), - shared.parseNetworkString("192.168.0.170/32"), - shared.parseNetworkString("192.168.0.171/32"), - shared.parseNetworkString("192.168.0.222/32"), - shared.parseNetworkString("192.168.0.234/32"), - shared.parseNetworkString("192.168.0.255/32") + new Network("192.168.0.0/32"), + new Network("192.168.0.3/32"), + new Network("192.168.0.24/32"), + new Network("192.168.0.52/32"), + new Network("192.168.0.123/32"), + new Network("192.168.0.124/32"), + new Network("192.168.0.125/32"), + new Network("192.168.0.170/32"), + new Network("192.168.0.171/32"), + new Network("192.168.0.222/32"), + new Network("192.168.0.234/32"), + new Network("192.168.0.255/32") ]; const output = sort.binarySearchForInsertionIndex(inputNetwork, inputArray); expect(output).toEqual(11); From 17a7d1c3cecdfda60ebf7bfd40720cbd69ccff55 Mon Sep 17 00:00:00 2001 From: Alex Demskie Date: Sat, 13 Jul 2019 16:11:02 -0700 Subject: [PATCH 5/8] implement range matcher --- .eslintrc.json | 4 +- package-lock.json | 1374 +++++++++++++++++---------------- package.json | 26 +- src/IPv4.ts | 5 +- src/IPv6.ts | 3 +- src/address.ts | 41 +- src/benchmarks/index.bench.ts | 9 + src/index.ts | 14 +- src/network.ts | 7 +- src/ranges.ts | 206 +++-- src/shared.ts | 11 +- src/tests/address.test.ts | 51 ++ src/tests/shared.test.ts | 26 + src/tests/shared.test.ts.bckp | 34 - 14 files changed, 963 insertions(+), 848 deletions(-) create mode 100644 src/tests/address.test.ts create mode 100644 src/tests/shared.test.ts delete mode 100644 src/tests/shared.test.ts.bckp diff --git a/.eslintrc.json b/.eslintrc.json index d7e7150..214ac4d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,7 +6,7 @@ "parser": "@typescript-eslint/parser", "extends": ["plugin:@typescript-eslint/recommended", "prettier/@typescript-eslint", "plugin:prettier/recommended"], "rules": { - "@typescript-eslint/explicit-function-return-type": false, - "@typescript-eslint/no-object-literal-type-assertion": false + "@typescript-eslint/explicit-function-return-type": 0, + "@typescript-eslint/no-object-literal-type-assertion": 0 } } diff --git a/package-lock.json b/package-lock.json index cdc7d93..e29cd4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,18 +14,18 @@ } }, "@babel/core": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.3.tgz", - "integrity": "sha512-oDpASqKFlbspQfzAE7yaeTmdljSH2ADIvBlb0RwbStltTuWa0+7CCI1fYVINNv9saHPa1W7oaKeuNuKj+RQCvA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.4.tgz", + "integrity": "sha512-+DaeBEpYq6b2+ZmHx3tHspC+ZRflrvLqwfv8E3hNr5LVQoyBnL8RPKSBCg+rK2W2My9PWlujBiqd0ZPsR9Q6zQ==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.0", - "@babel/helpers": "^7.4.3", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", + "@babel/generator": "^7.5.0", + "@babel/helpers": "^7.5.4", + "@babel/parser": "^7.5.0", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.5.0", + "@babel/types": "^7.5.0", "convert-source-map": "^1.1.0", "debug": "^4.1.0", "json5": "^2.1.0", @@ -44,12 +44,12 @@ } }, "@babel/generator": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz", - "integrity": "sha512-/v5I+a1jhGSKLgZDcmAUZ4K/VePi43eRkUs3yePW1HB1iANOD5tqJXwGSG4BZhSksP8J9ejSlwGeTiiOFZOrXQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.0.tgz", + "integrity": "sha512-1TTVrt7J9rcG5PMjvO7VEG3FrEoEJNHxumRq66GemPmzboLWtIjjcJgk8rokuAS7IiRSpgVSu5Vb9lc99iJkOA==", "dev": true, "requires": { - "@babel/types": "^7.4.0", + "@babel/types": "^7.5.0", "jsesc": "^2.5.1", "lodash": "^4.17.11", "source-map": "^0.5.0", @@ -91,29 +91,29 @@ "dev": true }, "@babel/helper-split-export-declaration": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz", - "integrity": "sha512-7Cuc6JZiYShaZnybDmfwhY4UYHzI6rlqhWjaIqbsJGsIqPimEYy5uh3akSRLMg65LSdSEnJ8a8/bWQN6u2oMGw==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", "dev": true, "requires": { - "@babel/types": "^7.4.0" + "@babel/types": "^7.4.4" } }, "@babel/helpers": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.3.tgz", - "integrity": "sha512-BMh7X0oZqb36CfyhvtbSmcWc3GXocfxv3yNsAEuM0l+fAqSO22rQrUpijr3oE/10jCTrB6/0b9kzmG4VetCj8Q==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.4.tgz", + "integrity": "sha512-6LJ6xwUEJP51w0sIgKyfvFMJvIb9mWAfohJp0+m6eHJigkFdcH8duZ1sfhn0ltJRzwUIT/yqqhdSfRpCpL7oow==", "dev": true, "requires": { - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0" + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.5.0", + "@babel/types": "^7.5.0" } }, "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "dev": true, "requires": { "chalk": "^2.0.0", @@ -122,9 +122,9 @@ } }, "@babel/parser": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.3.tgz", - "integrity": "sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.0.tgz", + "integrity": "sha512-I5nW8AhGpOXGCCNYGc+p7ExQIBxRFnS2fd/d862bNOKvmoEPjYPcfIjsfdy0ujagYOIYPczKgD9l3FsgTkAzKA==", "dev": true }, "@babel/plugin-syntax-object-rest-spread": { @@ -137,37 +137,37 @@ } }, "@babel/template": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz", - "integrity": "sha512-SOWwxxClTTh5NdbbYZ0BmaBVzxzTh2tO/TeLTbF6MO6EzVhHTnff8CdBXx3mEtazFBoysmEM6GU/wF+SuSx4Fw==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.4.0", - "@babel/types": "^7.4.0" + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" } }, "@babel/traverse": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.3.tgz", - "integrity": "sha512-HmA01qrtaCwwJWpSKpA948cBvU5BrmviAief/b3AVw936DtcdsTexlbyzNuDnthwhOQ37xshn7hvQaEQk7ISYQ==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.0.tgz", + "integrity": "sha512-SnA9aLbyOCcnnbQEGwdfBggnc142h/rbqqsXcaATj2hZcegCl903pUD/lfpsNBlBSuWow/YDfRyJuWi2EPR5cg==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.4.0", + "@babel/generator": "^7.5.0", "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/types": "^7.4.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.5.0", + "@babel/types": "^7.5.0", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.11" } }, "@babel/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", - "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.0.tgz", + "integrity": "sha512-UFpDVqRABKsW01bvw7/wSUe56uy6RXM5+VJibVVAybDGxEW25jdwiFJEf7ASvSaC7sN7rbE/l3cLp2izav+CtQ==", "dev": true, "requires": { "esutils": "^2.0.2", @@ -205,32 +205,32 @@ } }, "@jest/core": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.7.1.tgz", - "integrity": "sha512-ivlZ8HX/FOASfHcb5DJpSPFps8ydfUYzLZfgFFqjkLijYysnIEOieg72YRhO4ZUB32xu40hsSMmaw+IGYeKONA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.8.0.tgz", + "integrity": "sha512-R9rhAJwCBQzaRnrRgAdVfnglUuATXdwTRsYqs6NMdVcAl5euG8LtWDe+fVkN27YfKVBW61IojVsXKaOmSnqd/A==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/reporters": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/reporters": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", "exit": "^0.1.2", "graceful-fs": "^4.1.15", - "jest-changed-files": "^24.7.0", - "jest-config": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-message-util": "^24.7.1", + "jest-changed-files": "^24.8.0", + "jest-config": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-message-util": "^24.8.0", "jest-regex-util": "^24.3.0", - "jest-resolve-dependencies": "^24.7.1", - "jest-runner": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", - "jest-watcher": "^24.7.1", + "jest-resolve-dependencies": "^24.8.0", + "jest-runner": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "jest-watcher": "^24.8.0", "micromatch": "^3.1.10", "p-each-series": "^1.0.0", "pirates": "^4.0.1", @@ -257,49 +257,50 @@ } }, "@jest/environment": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.7.1.tgz", - "integrity": "sha512-wmcTTYc4/KqA+U5h1zQd5FXXynfa7VGP2NfF+c6QeGJ7c+2nStgh65RQWNX62SC716dTtqheTRrZl0j+54oGHw==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.8.0.tgz", + "integrity": "sha512-vlGt2HLg7qM+vtBrSkjDxk9K0YtRBi7HfRFaDxoRtyi+DyVChzhF20duvpdAnKVBV6W5tym8jm0U9EfXbDk1tw==", "dev": true, "requires": { - "@jest/fake-timers": "^24.7.1", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0" + "@jest/fake-timers": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0" } }, "@jest/fake-timers": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.7.1.tgz", - "integrity": "sha512-4vSQJDKfR2jScOe12L9282uiwuwQv9Lk7mgrCSZHA9evB9efB/qx8i0KJxsAKtp8fgJYBJdYY7ZU6u3F4/pyjA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.8.0.tgz", + "integrity": "sha512-2M4d5MufVXwi6VzZhJ9f5S/wU4ud2ck0kxPof1Iz3zWx6Y+V2eJrES9jEktB6O3o/oEyk+il/uNu9PvASjWXQw==", "dev": true, "requires": { - "@jest/types": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-mock": "^24.7.0" + "@jest/types": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-mock": "^24.8.0" } }, "@jest/reporters": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.7.1.tgz", - "integrity": "sha512-bO+WYNwHLNhrjB9EbPL4kX/mCCG4ZhhfWmO3m4FSpbgr7N83MFejayz30kKjgqr7smLyeaRFCBQMbXpUgnhAJw==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.8.0.tgz", + "integrity": "sha512-eZ9TyUYpyIIXfYCrw0UHUWUvE35vx5I92HGMgS93Pv7du+GHIzl+/vh8Qj9MCWFK/4TqyttVBPakWMOfZRIfxw==", "dev": true, "requires": { - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.2", - "istanbul-api": "^2.1.1", "istanbul-lib-coverage": "^2.0.2", "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", "istanbul-lib-source-maps": "^3.0.1", - "jest-haste-map": "^24.7.1", - "jest-resolve": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-util": "^24.7.1", + "istanbul-reports": "^2.1.1", + "jest-haste-map": "^24.8.0", + "jest-resolve": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-util": "^24.8.0", "jest-worker": "^24.6.0", "node-notifier": "^5.2.1", "slash": "^2.0.0", @@ -319,44 +320,44 @@ } }, "@jest/test-result": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.7.1.tgz", - "integrity": "sha512-3U7wITxstdEc2HMfBX7Yx3JZgiNBubwDqQMh+BXmZXHa3G13YWF3p6cK+5g0hGkN3iufg/vGPl3hLxQXD74Npg==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.8.0.tgz", + "integrity": "sha512-+YdLlxwizlfqkFDh7Mc7ONPQAhA4YylU1s529vVM1rsf67vGZH/2GGm5uO8QzPeVyaVMobCQ7FTxl38QrKRlng==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "@types/istanbul-lib-coverage": "^2.0.0" } }, "@jest/test-sequencer": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.7.1.tgz", - "integrity": "sha512-84HQkCpVZI/G1zq53gHJvSmhUer4aMYp9tTaffW28Ih5OxfCg8hGr3nTSbL1OhVDRrFZwvF+/R9gY6JRkDUpUA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.8.0.tgz", + "integrity": "sha512-OzL/2yHyPdCHXEzhoBuq37CE99nkme15eHkAzXRVqthreWZamEMA0WoetwstsQBCXABhczpK03JNbc4L01vvLg==", "dev": true, "requires": { - "@jest/test-result": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-runner": "^24.7.1", - "jest-runtime": "^24.7.1" + "@jest/test-result": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-runner": "^24.8.0", + "jest-runtime": "^24.8.0" } }, "@jest/transform": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.7.1.tgz", - "integrity": "sha512-EsOUqP9ULuJ66IkZQhI5LufCHlTbi7hrcllRMUEV/tOgqBVQi93+9qEvkX0n8mYpVXQ8VjwmICeRgg58mrtIEw==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.8.0.tgz", + "integrity": "sha512-xBMfFUP7TortCs0O+Xtez2W7Zu1PLH9bvJgtraN1CDST6LBM/eTOZ9SfwS/lvV8yOfcDpFmwf9bq5cYbXvqsvA==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "babel-plugin-istanbul": "^5.1.0", "chalk": "^2.0.1", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", "graceful-fs": "^4.1.15", - "jest-haste-map": "^24.7.1", + "jest-haste-map": "^24.8.0", "jest-regex-util": "^24.3.0", - "jest-util": "^24.7.1", + "jest-util": "^24.8.0", "micromatch": "^3.1.10", "realpath-native": "^1.1.0", "slash": "^2.0.0", @@ -365,19 +366,20 @@ } }, "@jest/types": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.7.0.tgz", - "integrity": "sha512-ipJUa2rFWiKoBqMKP63Myb6h9+iT3FHRTF2M8OR6irxWzItisa8i4dcSg14IbvmXUnBlHBlUQPYUHWyX3UPpYA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.8.0.tgz", + "integrity": "sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", "@types/yargs": "^12.0.9" } }, "@types/babel__core": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.0.tgz", - "integrity": "sha512-wJTeJRt7BToFx3USrCDs2BhEi4ijBInTQjOIukj6a/5tEkwpFMVZ+1ppgmE+Q/FQyc5P/VWUbx7I9NELrKruHA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.2.tgz", + "integrity": "sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg==", "dev": true, "requires": { "@babel/parser": "^7.1.0", @@ -407,9 +409,9 @@ } }, "@types/babel__traverse": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.6.tgz", - "integrity": "sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw==", + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", + "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", "dev": true, "requires": { "@babel/types": "^7.3.0" @@ -421,6 +423,12 @@ "integrity": "sha512-F6fVNOkGEkSdo/19yWYOwVKGvzbTeWkR/XQYBKtGBQ9oGRjBN9f/L4aJI4sDcVPJO58Y1CJZN8va9V2BhrZapA==", "dev": true }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -462,16 +470,44 @@ "integrity": "sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ==", "dev": true }, + "@types/ip-address": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/@types/ip-address/-/ip-address-5.8.2.tgz", + "integrity": "sha512-LFlDGRjJDnahfPyNCZGXvlaevSmZTi/zDxjTdXeTs8TQ9pQkNZKbCWaJXW29a3bGPRsASqeO+jGgZlaTUi9jTw==", + "dev": true, + "requires": { + "@types/jsbn": "*" + } + }, "@types/istanbul-lib-coverage": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz", - "integrity": "sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", "dev": true }, + "@types/istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, "@types/jest": { - "version": "24.0.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.11.tgz", - "integrity": "sha512-2kLuPC5FDnWIDvaJBzsGTBQaBbnDweznicvK7UGYzlIJP4RJR2a4A/ByLUXEyEgag6jz8eHdlWExGDtH3EYUXQ==", + "version": "24.0.15", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.15.tgz", + "integrity": "sha512-MU1HIvWUme74stAoc3mgAi+aMlgKOudgEvQDIm1v4RkrDudBh1T+NFp5sftpBAdXdx1J0PbdpJ+M2EsSOi1djA==", "dev": true, "requires": { "@types/jest-diff": "*" @@ -483,6 +519,12 @@ "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==", "dev": true }, + "@types/jsbn": { + "version": "1.2.29", + "resolved": "https://registry.npmjs.org/@types/jsbn/-/jsbn-1.2.29.tgz", + "integrity": "sha512-2dVz9LTEGWVj9Ov9zaDnpvqHFV+W4bXtU0EUEGAzWfdRNO3dlUuosdHpENI6/oQW+Kejn0hAjk6P/czs9h/hvg==", + "dev": true + }, "@types/lodash": { "version": "4.14.123", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.123.tgz", @@ -508,9 +550,9 @@ "dev": true }, "@types/node": { - "version": "12.0.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.0.8.tgz", - "integrity": "sha512-b8bbUOTwzIY3V5vDTY1fIJ+ePKDUBqt2hC2woVGotdQQhG/2Sh62HOKHrT7ab+VerXAcPyAiTEipPu/FsreUtg==", + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.2.tgz", + "integrity": "sha512-gojym4tX0FWeV2gsW4Xmzo5wxGjXGm550oVUII7f7G5o4BV6c7DBdiG1RRQd+y1bvqRyYtPfMK85UM95vsapqQ==", "dev": true }, "@types/shelljs": { @@ -536,32 +578,44 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz", - "integrity": "sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.12.0.tgz", + "integrity": "sha512-J/ZTZF+pLNqjXBGNfq5fahsoJ4vJOkYbitWPavA05IrZ7BXUaf4XWlhUB/ic1lpOGTRpLWF+PLAePjiHp6dz8g==", "dev": true, "requires": { - "@typescript-eslint/parser": "1.6.0", - "@typescript-eslint/typescript-estree": "1.6.0", - "requireindex": "^1.2.0", + "@typescript-eslint/experimental-utils": "1.12.0", + "eslint-utils": "^1.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^2.0.1", "tsutils": "^3.7.0" } }, + "@typescript-eslint/experimental-utils": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.12.0.tgz", + "integrity": "sha512-s0soOTMJloytr9GbPteMLNiO2HvJ+qgQkRNplABXiVw6vq7uQRvidkby64Gqt/nA7pys74HksHwRULaB/QRVyw==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "1.12.0", + "eslint-scope": "^4.0.0" + } + }, "@typescript-eslint/parser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.6.0.tgz", - "integrity": "sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.12.0.tgz", + "integrity": "sha512-0uzbaa9ZLCA5yMWJywnJJ7YVENKGWVUhJDV5UrMoldC5HoI54W5kkdPhTfmtFKpPFp93MIwmJj0/61ztvmz5Dw==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "1.6.0", - "eslint-scope": "^4.0.0", + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "1.12.0", + "@typescript-eslint/typescript-estree": "1.12.0", "eslint-visitor-keys": "^1.0.0" } }, "@typescript-eslint/typescript-estree": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz", - "integrity": "sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.12.0.tgz", + "integrity": "sha512-nwN6yy//XcVhFs0ZyU+teJHB8tbCm7AIA8mu6E2r5hu6MajwYBY3Uwop7+rPZWUN/IUOHpL8C+iUPMDVYUU3og==", "dev": true, "requires": { "lodash.unescape": "4.0.1", @@ -575,15 +629,15 @@ "dev": true }, "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.2.0.tgz", + "integrity": "sha512-8oe72N3WPMjA+2zVG71Ia0nXZ8DpQH+QyyHO+p06jT8eg8FGG3FbcUIi8KziHlAfheJQZeoqbvq1mQSQHXKYLw==", "dev": true }, "acorn-globals": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz", - "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.2.tgz", + "integrity": "sha512-BbzvZhVtZP+Bs1J1HcwrQe8ycfO0wStkSGxuul3He3GkHOIZ6eTqOkPuw9IP1X3+IkOo4wiJmwkobzXYz4wewQ==", "dev": true, "requires": { "acorn": "^6.0.1", @@ -597,15 +651,15 @@ "dev": true }, "acorn-walk": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", "dev": true }, "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.1.tgz", + "integrity": "sha512-w1YQaVGNC6t2UCPjEawK/vo/dG8OOrVtUmhBT1uJJYxbl5kU2Tj3v6LGqBcsysN1yhuCStJCCA3GqdvKY8sqXQ==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", @@ -645,15 +699,6 @@ "normalize-path": "^2.1.1" } }, - "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", - "dev": true, - "requires": { - "default-require-extensions": "^2.0.0" - } - }, "arg": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", @@ -699,12 +744,6 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -732,15 +771,6 @@ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", "dev": true }, - "async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", - "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", - "dev": true, - "requires": { - "lodash": "^4.17.11" - } - }, "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", @@ -772,13 +802,13 @@ "dev": true }, "babel-jest": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.7.1.tgz", - "integrity": "sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.8.0.tgz", + "integrity": "sha512-+5/kaZt4I9efoXzPlZASyK/lN9qdRKmmUav9smVc0ruPQD7IsfucQ87gpOE8mn2jbDuS6M/YOW6n3v9ZoIfgnw==", "dev": true, "requires": { - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", "@types/babel__core": "^7.1.0", "babel-plugin-istanbul": "^5.1.0", "babel-preset-jest": "^24.6.0", @@ -787,14 +817,14 @@ } }, "babel-plugin-istanbul": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz", - "integrity": "sha512-RNNVv2lsHAXJQsEJ5jonQwrJVWK8AcZpG1oxhnjCUaAjL7xahYLANhPUZbzEQHjKy1NMYUwn+0NPKQc8iSY4xQ==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.4.tgz", + "integrity": "sha512-dySz4VJMH+dpndj0wjJ8JPs/7i1TdSPb1nRrn56/92pKOF9VKC1FMFJmMXjzlGGusnCAqujP6PBCiKq0sVA+YQ==", "dev": true, "requires": { "find-up": "^3.0.0", - "istanbul-lib-instrument": "^3.0.0", - "test-exclude": "^5.0.0" + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" } }, "babel-plugin-jest-hoist": { @@ -980,9 +1010,9 @@ } }, "bser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz", - "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz", + "integrity": "sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==", "dev": true, "requires": { "node-int64": "^0.4.0" @@ -1157,9 +1187,9 @@ "dev": true }, "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "requires": { "delayed-stream": "~1.0.0" @@ -1172,16 +1202,10 @@ "dev": true, "optional": true }, - "compare-versions": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", - "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==", - "dev": true - }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, "concat-map": { @@ -1197,6 +1221,14 @@ "dev": true, "requires": { "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "copy-descriptor": { @@ -1212,13 +1244,13 @@ "dev": true }, "coveralls": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.3.tgz", - "integrity": "sha512-viNfeGlda2zJr8Gj1zqXpDMRjw9uM54p7wzZdvLRyOgnAfCe974Dq4veZkjJdxQXbmdppu6flEajFYseHYaUhg==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-3.0.5.tgz", + "integrity": "sha512-/KD7PGfZv/tjKB6LoW97jzIgFqem0Tu9tZL9/iwBnBd8zkIZp7vT1ZSHNvnr0GSQMV/LTMxUstWg8WcDDUVQKg==", "dev": true, "requires": { "growl": "~> 1.10.0", - "js-yaml": "^3.11.0", + "js-yaml": "^3.13.1", "lcov-parse": "^0.0.10", "log-driver": "^1.2.7", "minimist": "^1.2.0", @@ -1247,18 +1279,18 @@ } }, "cssom": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", - "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true }, "cssstyle": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.2.tgz", - "integrity": "sha512-43wY3kl1CVQSvL7wUY1qXkxVGkStjpkDmVjiIKX8R97uhajy8Bybay78uOtqvh7Q5GK75dNPfW0geWjE6qQQow==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.3.0.tgz", + "integrity": "sha512-wXsoRfsRfsLVNaVzoKdqvEmK/5PFaEXNspVT22Ots6K/cnJdpoDKuQFw+qlMiXnmaif1OgeC466X1zISgAOcGg==", "dev": true, "requires": { - "cssom": "0.3.x" + "cssom": "~0.3.6" } }, "culvert": { @@ -1327,15 +1359,6 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, - "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", - "dev": true, - "requires": { - "strip-bom": "^3.0.0" - } - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -1526,13 +1549,13 @@ } }, "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.0.1.tgz", + "integrity": "sha512-DyQRaMmORQ+JsWShYsSg4OPTjY56u1nCjAmICrE8vLWqyLKxhFXOthwMj1SA8xwfrv0CofLNVnqbfyhwCkaO0w==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", + "ajv": "^6.10.0", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", "debug": "^4.0.1", @@ -1540,18 +1563,19 @@ "eslint-scope": "^4.0.3", "eslint-utils": "^1.3.1", "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", + "espree": "^6.0.0", "esquery": "^1.0.1", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", + "glob-parent": "^3.1.0", "globals": "^11.7.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.11", @@ -1559,7 +1583,6 @@ "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^5.5.1", @@ -1578,18 +1601,18 @@ } }, "eslint-config-prettier": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.1.0.tgz", - "integrity": "sha512-zILwX9/Ocz4SV2vX7ox85AsrAgXV3f2o2gpIicdMIOra48WYqgUnWNH/cR/iHtmD2Vb3dLSC3LiEJnS05Gkw7w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.0.0.tgz", + "integrity": "sha512-vDrcCFE3+2ixNT5H83g28bO/uYAwibJxerXPj+E7op4qzBCsAV36QfvdAyVOoNxKAH2Os/e01T/2x++V0LPukA==", "dev": true, "requires": { "get-stdin": "^6.0.0" } }, "eslint-plugin-prettier": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.1.tgz", - "integrity": "sha512-/PMttrarPAY78PLvV3xfWibMOdMDl57hmlQ2XqFeA37wd+CJ7WSxV7txqjVPHi/AAFKd2lX0ZqfsOc/i5yFCSQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz", + "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==", "dev": true, "requires": { "prettier-linter-helpers": "^1.0.0" @@ -1618,9 +1641,9 @@ "dev": true }, "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.0.0.tgz", + "integrity": "sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==", "dev": true, "requires": { "acorn": "^6.0.7", @@ -1742,16 +1765,16 @@ } }, "expect": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-24.7.1.tgz", - "integrity": "sha512-mGfvMTPduksV3xoI0xur56pQsg2vJjNf5+a+bXOjqCkiCBbmCayrBbHS/75y9K430cfqyocPr2ZjiNiRx4SRKw==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.8.0.tgz", + "integrity": "sha512-/zYvP8iMDrzaaxHVa724eJBCKqSHmO0FA7EDkBiRHxg6OipmMn1fN+C8T9L9K8yr7UONkOifu6+LLH+z76CnaA==", "dev": true, "requires": { - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "ansi-styles": "^3.2.0", - "jest-get-type": "^24.3.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", + "jest-get-type": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", "jest-regex-util": "^24.3.0" } }, @@ -1783,9 +1806,9 @@ } }, "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { "chardet": "^0.7.0", @@ -1915,16 +1938,6 @@ "flat-cache": "^2.0.1" } }, - "fileset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", - "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", - "dev": true, - "requires": { - "glob": "^7.0.3", - "minimatch": "^3.0.3" - } - }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1969,9 +1982,9 @@ } }, "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", "dev": true }, "for-in": { @@ -2024,14 +2037,14 @@ "dev": true }, "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" }, "dependencies": { "abbrev": { @@ -2109,12 +2122,12 @@ "optional": true }, "debug": { - "version": "2.6.9", + "version": "4.1.1", "bundled": true, "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "deep-extend": { @@ -2285,24 +2298,24 @@ } }, "ms": { - "version": "2.0.0", + "version": "2.1.1", "bundled": true, "dev": true, "optional": true }, "needle": { - "version": "2.2.4", + "version": "2.3.0", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", + "debug": "^4.1.0", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.10.3", + "version": "0.12.0", "bundled": true, "dev": true, "optional": true, @@ -2330,13 +2343,13 @@ } }, "npm-bundled": { - "version": "1.0.5", + "version": "1.0.6", "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.2.0", + "version": "1.4.1", "bundled": true, "dev": true, "optional": true, @@ -2475,7 +2488,7 @@ "optional": true }, "semver": { - "version": "5.6.0", + "version": "5.7.0", "bundled": true, "dev": true, "optional": true @@ -2645,10 +2658,31 @@ "path-is-absolute": "^1.0.0" } }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "graceful-fs": { @@ -2798,9 +2832,9 @@ "dev": true }, "import-fresh": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", - "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -2840,9 +2874,9 @@ "dev": true }, "inquirer": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", - "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.0.tgz", + "integrity": "sha512-scfHejeG/lVZSpvCXpsB4j/wQNPM5JC8kiElOI0OUTwmc1RTpXr4H32/HOlQHcZiYl2z2VElwuCVDRG8vFmbnA==", "dev": true, "requires": { "ansi-escapes": "^3.2.0", @@ -2851,12 +2885,12 @@ "cli-width": "^2.0.0", "external-editor": "^3.0.3", "figures": "^2.0.0", - "lodash": "^4.17.11", + "lodash": "^4.17.12", "mute-stream": "0.0.7", "run-async": "^2.2.0", "rxjs": "^6.4.0", "string-width": "^2.1.0", - "strip-ansi": "^5.0.0", + "strip-ansi": "^5.1.0", "through": "^2.3.6" }, "dependencies": { @@ -2866,6 +2900,12 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "lodash": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==", + "dev": true + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -2898,6 +2938,31 @@ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, + "ip-address": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-5.9.2.tgz", + "integrity": "sha512-7aeFm/7oqo0mMhubTSjZ2Juw/F+WJ3hyfCScNVRQdz5RSRhw1Rj4ZlBFsmEajeKgQDI8asqVs31h8DpxEv7IfQ==", + "dev": true, + "requires": { + "jsbn": "1.1.0", + "lodash": "^4.17.11", + "sprintf-js": "1.1.2" + }, + "dependencies": { + "jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha1-sBMHyym2GKHtJux56RH4A8TaAEA=", + "dev": true + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + } + } + }, "ip6addr": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/ip6addr/-/ip6addr-0.2.2.tgz", @@ -3011,6 +3076,12 @@ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", "dev": true }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -3023,6 +3094,15 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -3124,66 +3204,44 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, - "istanbul-api": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz", - "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==", - "dev": true, - "requires": { - "async": "^2.6.1", - "compare-versions": "^3.2.1", - "fileset": "^2.0.3", - "istanbul-lib-coverage": "^2.0.3", - "istanbul-lib-hook": "^2.0.3", - "istanbul-lib-instrument": "^3.1.0", - "istanbul-lib-report": "^2.0.4", - "istanbul-lib-source-maps": "^3.0.2", - "istanbul-reports": "^2.1.1", - "js-yaml": "^3.12.0", - "make-dir": "^1.3.0", - "minimatch": "^3.0.4", - "once": "^1.4.0" - } - }, "istanbul-lib-coverage": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", - "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", "dev": true }, - "istanbul-lib-hook": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", - "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", - "dev": true, - "requires": { - "append-transform": "^1.0.0" - } - }, "istanbul-lib-instrument": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", - "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", "dev": true, "requires": { - "@babel/generator": "^7.0.0", - "@babel/parser": "^7.0.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.0.0", - "@babel/types": "^7.0.0", - "istanbul-lib-coverage": "^2.0.3", - "semver": "^5.5.0" + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==", + "dev": true + } } }, "istanbul-lib-report": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", - "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", "dev": true, "requires": { - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "supports-color": "^6.0.0" + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" }, "dependencies": { "supports-color": { @@ -3198,53 +3256,53 @@ } }, "istanbul-lib-source-maps": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", - "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", "dev": true, "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.3", - "make-dir": "^1.3.0", - "rimraf": "^2.6.2", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", "source-map": "^0.6.1" } }, "istanbul-reports": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz", - "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", "dev": true, "requires": { - "handlebars": "^4.1.0" + "handlebars": "^4.1.2" } }, "jest": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-24.7.1.tgz", - "integrity": "sha512-AbvRar5r++izmqo5gdbAjTeA6uNRGoNRuj5vHB0OnDXo2DXWZJVuaObiGgtlvhKb+cWy2oYbQSfxv7Q7GjnAtA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.8.0.tgz", + "integrity": "sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==", "dev": true, "requires": { "import-local": "^2.0.0", - "jest-cli": "^24.7.1" + "jest-cli": "^24.8.0" }, "dependencies": { "jest-cli": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.7.1.tgz", - "integrity": "sha512-32OBoSCVPzcTslGFl6yVCMzB2SqX3IrWwZCY5mZYkb0D2WsogmU3eV2o8z7+gRQa4o4sZPX/k7GU+II7CxM6WQ==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", + "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", "dev": true, "requires": { - "@jest/core": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/core": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", "chalk": "^2.0.1", "exit": "^0.1.2", "import-local": "^2.0.0", "is-ci": "^2.0.0", - "jest-config": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", + "jest-config": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", "prompts": "^2.0.1", "realpath-native": "^1.1.0", "yargs": "^12.0.2" @@ -3253,51 +3311,51 @@ } }, "jest-changed-files": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.7.0.tgz", - "integrity": "sha512-33BgewurnwSfJrW7T5/ZAXGE44o7swLslwh8aUckzq2e17/2Os1V0QU506ZNik3hjs8MgnEMKNkcud442NCDTw==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.8.0.tgz", + "integrity": "sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==", "dev": true, "requires": { - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "execa": "^1.0.0", "throat": "^4.0.0" } }, "jest-config": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.7.1.tgz", - "integrity": "sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", + "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", "dev": true, "requires": { "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.7.1", - "@jest/types": "^24.7.0", - "babel-jest": "^24.7.1", + "@jest/test-sequencer": "^24.8.0", + "@jest/types": "^24.8.0", + "babel-jest": "^24.8.0", "chalk": "^2.0.1", "glob": "^7.1.1", - "jest-environment-jsdom": "^24.7.1", - "jest-environment-node": "^24.7.1", - "jest-get-type": "^24.3.0", - "jest-jasmine2": "^24.7.1", + "jest-environment-jsdom": "^24.8.0", + "jest-environment-node": "^24.8.0", + "jest-get-type": "^24.8.0", + "jest-jasmine2": "^24.8.0", "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", + "jest-resolve": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", "micromatch": "^3.1.10", - "pretty-format": "^24.7.0", + "pretty-format": "^24.8.0", "realpath-native": "^1.1.0" } }, "jest-diff": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.7.0.tgz", - "integrity": "sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.8.0.tgz", + "integrity": "sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==", "dev": true, "requires": { "chalk": "^2.0.1", "diff-sequences": "^24.3.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" + "jest-get-type": "^24.8.0", + "pretty-format": "^24.8.0" } }, "jest-docblock": { @@ -3310,65 +3368,65 @@ } }, "jest-each": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.7.1.tgz", - "integrity": "sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", + "integrity": "sha512-NrwK9gaL5+XgrgoCsd9svsoWdVkK4gnvyhcpzd6m487tXHqIdYeykgq3MKI1u4I+5Zf0tofr70at9dWJDeb+BA==", "dev": true, "requires": { - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0" + "jest-get-type": "^24.8.0", + "jest-util": "^24.8.0", + "pretty-format": "^24.8.0" } }, "jest-environment-jsdom": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz", - "integrity": "sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.8.0.tgz", + "integrity": "sha512-qbvgLmR7PpwjoFjM/sbuqHJt/NCkviuq9vus9NBn/76hhSidO+Z6Bn9tU8friecegbJL8gzZQEMZBQlFWDCwAQ==", "dev": true, "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1", + "@jest/environment": "^24.8.0", + "@jest/fake-timers": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-util": "^24.8.0", "jsdom": "^11.5.1" } }, "jest-environment-node": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.7.1.tgz", - "integrity": "sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.8.0.tgz", + "integrity": "sha512-vIGUEScd1cdDgR6sqn2M08sJTRLQp6Dk/eIkCeO4PFHxZMOgy+uYLPMC4ix3PEfM5Au/x3uQ/5Tl0DpXXZsJ/Q==", "dev": true, "requires": { - "@jest/environment": "^24.7.1", - "@jest/fake-timers": "^24.7.1", - "@jest/types": "^24.7.0", - "jest-mock": "^24.7.0", - "jest-util": "^24.7.1" + "@jest/environment": "^24.8.0", + "@jest/fake-timers": "^24.8.0", + "@jest/types": "^24.8.0", + "jest-mock": "^24.8.0", + "jest-util": "^24.8.0" } }, "jest-get-type": { - "version": "24.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.8.0.tgz", + "integrity": "sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==", "dev": true }, "jest-haste-map": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.7.1.tgz", - "integrity": "sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw==", + "version": "24.8.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.8.1.tgz", + "integrity": "sha512-SwaxMGVdAZk3ernAx2Uv2sorA7jm3Kx+lR0grp6rMmnY06Kn/urtKx1LPN2mGTea4fCT38impYT28FfcLUhX0g==", "dev": true, "requires": { - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "anymatch": "^2.0.0", "fb-watchman": "^2.0.0", "fsevents": "^1.2.7", "graceful-fs": "^4.1.15", "invariant": "^2.2.4", "jest-serializer": "^24.4.0", - "jest-util": "^24.7.1", + "jest-util": "^24.8.0", "jest-worker": "^24.6.0", "micromatch": "^3.1.10", "sane": "^4.0.3", @@ -3376,59 +3434,59 @@ } }, "jest-jasmine2": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz", - "integrity": "sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.8.0.tgz", + "integrity": "sha512-cEky88npEE5LKd5jPpTdDCLvKkdyklnaRycBXL6GNmpxe41F0WN44+i7lpQKa/hcbXaQ+rc9RMaM4dsebrYong==", "dev": true, "requires": { "@babel/traverse": "^7.1.0", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", "chalk": "^2.0.1", "co": "^4.6.0", - "expect": "^24.7.1", + "expect": "^24.8.0", "is-generator-fn": "^2.0.0", - "jest-each": "^24.7.1", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "pretty-format": "^24.7.0", + "jest-each": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "pretty-format": "^24.8.0", "throat": "^4.0.0" } }, "jest-leak-detector": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.7.0.tgz", - "integrity": "sha512-zV0qHKZGXtmPVVzT99CVEcHE9XDf+8LwiE0Ob7jjezERiGVljmqKFWpV2IkG+rkFIEUHFEkMiICu7wnoPM/RoQ==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.8.0.tgz", + "integrity": "sha512-cG0yRSK8A831LN8lIHxI3AblB40uhv0z+SsQdW3GoMMVcK+sJwrIIyax5tu3eHHNJ8Fu6IMDpnLda2jhn2pD/g==", "dev": true, "requires": { - "pretty-format": "^24.7.0" + "pretty-format": "^24.8.0" } }, "jest-matcher-utils": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz", - "integrity": "sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz", + "integrity": "sha512-lex1yASY51FvUuHgm0GOVj7DCYEouWSlIYmCW7APSqB9v8mXmKSn5+sWVF0MhuASG0bnYY106/49JU1FZNl5hw==", "dev": true, "requires": { "chalk": "^2.0.1", - "jest-diff": "^24.7.0", - "jest-get-type": "^24.3.0", - "pretty-format": "^24.7.0" + "jest-diff": "^24.8.0", + "jest-get-type": "^24.8.0", + "pretty-format": "^24.8.0" } }, "jest-message-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.7.1.tgz", - "integrity": "sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.8.0.tgz", + "integrity": "sha512-p2k71rf/b6ns8btdB0uVdljWo9h0ovpnEe05ZKWceQGfXYr4KkzgKo3PBi8wdnd9OtNh46VpNIJynUn/3MKm1g==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", "@types/stack-utils": "^1.0.1", "chalk": "^2.0.1", "micromatch": "^3.1.10", @@ -3437,12 +3495,12 @@ } }, "jest-mock": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.7.0.tgz", - "integrity": "sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.8.0.tgz", + "integrity": "sha512-6kWugwjGjJw+ZkK4mDa0Df3sDlUTsV47MSrT0nGQ0RBWJbpODDQ8MHDVtGtUYBne3IwZUhtB7elxHspU79WH3A==", "dev": true, "requires": { - "@jest/types": "^24.7.0" + "@jest/types": "^24.8.0" } }, "jest-pnp-resolver": { @@ -3458,12 +3516,12 @@ "dev": true }, "jest-resolve": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.7.1.tgz", - "integrity": "sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.8.0.tgz", + "integrity": "sha512-+hjSzi1PoRvnuOICoYd5V/KpIQmkAsfjFO71458hQ2Whi/yf1GDeBOFj8Gxw4LrApHsVJvn5fmjcPdmoUHaVKw==", "dev": true, "requires": { - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "browser-resolve": "^1.11.3", "chalk": "^2.0.1", "jest-pnp-resolver": "^1.2.1", @@ -3471,68 +3529,68 @@ } }, "jest-resolve-dependencies": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.7.1.tgz", - "integrity": "sha512-2Eyh5LJB2liNzfk4eo7bD1ZyBbqEJIyyrFtZG555cSWW9xVHxII2NuOkSl1yUYTAYCAmM2f2aIT5A7HzNmubyg==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.8.0.tgz", + "integrity": "sha512-hyK1qfIf/krV+fSNyhyJeq3elVMhK9Eijlwy+j5jqmZ9QsxwKBiP6qukQxaHtK8k6zql/KYWwCTQ+fDGTIJauw==", "dev": true, "requires": { - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "jest-regex-util": "^24.3.0", - "jest-snapshot": "^24.7.1" + "jest-snapshot": "^24.8.0" } }, "jest-runner": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.7.1.tgz", - "integrity": "sha512-aNFc9liWU/xt+G9pobdKZ4qTeG/wnJrJna3VqunziDNsWT3EBpmxXZRBMKCsNMyfy+A/XHiV+tsMLufdsNdgCw==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.8.0.tgz", + "integrity": "sha512-utFqC5BaA3JmznbissSs95X1ZF+d+4WuOWwpM9+Ak356YtMhHE/GXUondZdcyAAOTBEsRGAgH/0TwLzfI9h7ow==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/environment": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", "chalk": "^2.4.2", "exit": "^0.1.2", "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", + "jest-config": "^24.8.0", "jest-docblock": "^24.3.0", - "jest-haste-map": "^24.7.1", - "jest-jasmine2": "^24.7.1", - "jest-leak-detector": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-resolve": "^24.7.1", - "jest-runtime": "^24.7.1", - "jest-util": "^24.7.1", + "jest-haste-map": "^24.8.0", + "jest-jasmine2": "^24.8.0", + "jest-leak-detector": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-resolve": "^24.8.0", + "jest-runtime": "^24.8.0", + "jest-util": "^24.8.0", "jest-worker": "^24.6.0", "source-map-support": "^0.5.6", "throat": "^4.0.0" } }, "jest-runtime": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.7.1.tgz", - "integrity": "sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.8.0.tgz", + "integrity": "sha512-Mq0aIXhvO/3bX44ccT+czU1/57IgOMyy80oM0XR/nyD5zgBcesF84BPabZi39pJVA6UXw+fY2Q1N+4BiVUBWOA==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/environment": "^24.7.1", + "@jest/environment": "^24.8.0", "@jest/source-map": "^24.3.0", - "@jest/transform": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/transform": "^24.8.0", + "@jest/types": "^24.8.0", "@types/yargs": "^12.0.2", "chalk": "^2.0.1", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.1.15", - "jest-config": "^24.7.1", - "jest-haste-map": "^24.7.1", - "jest-message-util": "^24.7.1", - "jest-mock": "^24.7.0", + "jest-config": "^24.8.0", + "jest-haste-map": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-mock": "^24.8.0", "jest-regex-util": "^24.3.0", - "jest-resolve": "^24.7.1", - "jest-snapshot": "^24.7.1", - "jest-util": "^24.7.1", - "jest-validate": "^24.7.0", + "jest-resolve": "^24.8.0", + "jest-snapshot": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", "realpath-native": "^1.1.0", "slash": "^2.0.0", "strip-bom": "^3.0.0", @@ -3546,36 +3604,36 @@ "dev": true }, "jest-snapshot": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.7.1.tgz", - "integrity": "sha512-8Xk5O4p+JsZZn4RCNUS3pxA+ORKpEKepE+a5ejIKrId9CwrVN0NY+vkqEkXqlstA5NMBkNahXkR/4qEBy0t5yA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.8.0.tgz", + "integrity": "sha512-5ehtWoc8oU9/cAPe6fez6QofVJLBKyqkY2+TlKTOf0VllBB/mqUNdARdcjlZrs9F1Cv+/HKoCS/BknT0+tmfPg==", "dev": true, "requires": { "@babel/types": "^7.0.0", - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "chalk": "^2.0.1", - "expect": "^24.7.1", - "jest-diff": "^24.7.0", - "jest-matcher-utils": "^24.7.0", - "jest-message-util": "^24.7.1", - "jest-resolve": "^24.7.1", + "expect": "^24.8.0", + "jest-diff": "^24.8.0", + "jest-matcher-utils": "^24.8.0", + "jest-message-util": "^24.8.0", + "jest-resolve": "^24.8.0", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "pretty-format": "^24.7.0", + "pretty-format": "^24.8.0", "semver": "^5.5.0" } }, "jest-util": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.7.1.tgz", - "integrity": "sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.8.0.tgz", + "integrity": "sha512-DYZeE+XyAnbNt0BG1OQqKy/4GVLPtzwGx5tsnDrFcax36rVE3lTA5fbvgmbVPUZf9w77AJ8otqR4VBbfFJkUZA==", "dev": true, "requires": { "@jest/console": "^24.7.1", - "@jest/fake-timers": "^24.7.1", + "@jest/fake-timers": "^24.8.0", "@jest/source-map": "^24.3.0", - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", "callsites": "^3.0.0", "chalk": "^2.0.1", "graceful-fs": "^4.1.15", @@ -3586,31 +3644,31 @@ } }, "jest-validate": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.7.0.tgz", - "integrity": "sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.8.0.tgz", + "integrity": "sha512-+/N7VOEMW1Vzsrk3UWBDYTExTPwf68tavEPKDnJzrC6UlHtUDU/fuEdXqFoHzv9XnQ+zW6X3qMZhJ3YexfeLDA==", "dev": true, "requires": { - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "camelcase": "^5.0.0", "chalk": "^2.0.1", - "jest-get-type": "^24.3.0", + "jest-get-type": "^24.8.0", "leven": "^2.1.0", - "pretty-format": "^24.7.0" + "pretty-format": "^24.8.0" } }, "jest-watcher": { - "version": "24.7.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.7.1.tgz", - "integrity": "sha512-Wd6TepHLRHVKLNPacEsBwlp9raeBIO+01xrN24Dek4ggTS8HHnOzYSFnvp+6MtkkJ3KfMzy220KTi95e2rRkrw==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.8.0.tgz", + "integrity": "sha512-SBjwHt5NedQoVu54M5GEx7cl7IGEFFznvd/HNT8ier7cCAx/Qgu9ZMlaTQkvK22G1YOpcWBLQPFSImmxdn3DAw==", "dev": true, "requires": { - "@jest/test-result": "^24.7.1", - "@jest/types": "^24.7.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", "@types/yargs": "^12.0.9", "ansi-escapes": "^3.0.0", "chalk": "^2.0.1", - "jest-util": "^24.7.1", + "jest-util": "^24.8.0", "string-length": "^2.0.0" } }, @@ -3890,12 +3948,27 @@ } }, "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + } } }, "make-error": { @@ -3993,18 +4066,18 @@ } }, "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", "dev": true }, "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "dev": true, "requires": { - "mime-db": "~1.38.0" + "mime-db": "1.40.0" } }, "mimic-fn": { @@ -4029,9 +4102,9 @@ "dev": true }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", @@ -4059,9 +4132,9 @@ } }, "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "mute-stream": { @@ -4071,9 +4144,9 @@ "dev": true }, "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true, "optional": true }, @@ -4182,9 +4255,9 @@ "dev": true }, "nwsapi": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.3.tgz", - "integrity": "sha512-RowAaJGEgYXEZfQ7tvvdtAQUKPyTR6T6wNu0fwlNsGQYr/h3yQc6oI8WnVZh3Y/Sylwc+dtAlvPqfFZjhTyk3A==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", "dev": true }, "oauth-sign": { @@ -4419,6 +4492,12 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -4431,12 +4510,6 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -4513,9 +4586,9 @@ "dev": true }, "prettier": { - "version": "1.16.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", - "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==", + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", + "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==", "dev": true }, "prettier-linter-helpers": { @@ -4528,12 +4601,12 @@ } }, "pretty-format": { - "version": "24.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz", - "integrity": "sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA==", + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", + "integrity": "sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==", "dev": true, "requires": { - "@jest/types": "^24.7.0", + "@jest/types": "^24.8.0", "ansi-regex": "^4.0.0", "ansi-styles": "^3.2.0", "react-is": "^16.8.4" @@ -4548,9 +4621,9 @@ } }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "progress": { @@ -4560,9 +4633,9 @@ "dev": true }, "prompts": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.4.tgz", - "integrity": "sha512-HTzM3UWp/99A0gk51gAegwo1QRYA7xjcZufMNe33rCclFszUYAuHe1fIN/3ZmiHeGPkUsNaRyQm1hHOfM0PKxA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.1.0.tgz", + "integrity": "sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==", "dev": true, "requires": { "kleur": "^3.0.2", @@ -4570,9 +4643,9 @@ } }, "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.2.0.tgz", + "integrity": "sha512-GEn74ZffufCmkDDLNcl3uuyF/aSD6exEyh1v/ZSdAomB82t6G9hzJVRx0jBmLDW+VfZqks3aScmMw9DszwUalA==", "dev": true }, "pump": { @@ -4637,6 +4710,14 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "readts": { @@ -4734,24 +4815,6 @@ "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - } - } } }, "request-promise-core": { @@ -4781,15 +4844,9 @@ "dev": true }, "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "requireindex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", - "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, "resolve": { @@ -4856,9 +4913,9 @@ } }, "rsvp": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.4.tgz", - "integrity": "sha512-6FomvYPfs+Jy9TfXmBpBuMWNH94SgCsZmJKcanySzgNNP6LjWxBvyLTa9KaMfDDM5oxRfrKDB0r/qeRsLwnBfA==", + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", "dev": true }, "run-async": { @@ -4871,18 +4928,18 @@ } }, "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", "dev": true, "requires": { "tslib": "^1.9.0" } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", "dev": true }, "safe-regex": { @@ -4944,9 +5001,9 @@ "dev": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { "extend-shallow": "^2.0.1", @@ -5005,9 +5062,9 @@ "dev": true }, "sisteransi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz", - "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.2.tgz", + "integrity": "sha512-ZcYcZcT69nSLAR2oLN2JwNmLkJEKGooFMCdvOkFrToUt/WfcRWqhIg4P4KwY4dmLbuyXIx4o4YmPsvMRJYJd/w==", "dev": true }, "slash": { @@ -5217,9 +5274,9 @@ } }, "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", "dev": true }, "split-string": { @@ -5314,6 +5371,14 @@ "dev": true, "requires": { "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } } }, "strip-ansi": { @@ -5353,15 +5418,15 @@ } }, "symbol-tree": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, "table": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", - "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.1.tgz", + "integrity": "sha512-E6CK1/pZe2N75rGZQotFOdmzWQ1AILtgYbMAbAjvms0S1l5IDB47zG3nCnFGB/w+7nB3vKofbLXCH7HPBo864w==", "dev": true, "requires": { "ajv": "^6.9.1", @@ -5399,15 +5464,15 @@ } }, "test-exclude": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz", - "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", "dev": true, "requires": { - "arrify": "^1.0.1", + "glob": "^7.1.3", "minimatch": "^3.0.4", "read-pkg-up": "^4.0.0", - "require-main-filename": "^1.0.1" + "require-main-filename": "^2.0.0" } }, "text-table": { @@ -5492,13 +5557,21 @@ } }, "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } } }, "tr46": { @@ -5575,15 +5648,15 @@ } }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, "tsutils": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.10.0.tgz", - "integrity": "sha512-q20XSMq7jutbGB8luhKKsQldRKWvyBO2BGqni3p4yq8Ys9bEP/xQw3KepKmMRt9gJ4lvQSScrihJrcKdKoSU7Q==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.14.0.tgz", + "integrity": "sha512-SmzGbB0l+8I0QwsPgjooFRaRvHLBLNYM8SeQ0k6rtNDru5sCGeLJcZdwilNndN+GysuFjF5EIYgN8GfFG6UeUw==", "dev": true, "requires": { "tslib": "^1.8.1" @@ -5598,15 +5671,6 @@ "safe-buffer": "^5.0.1" } }, - "turndown": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/turndown/-/turndown-5.0.3.tgz", - "integrity": "sha512-popfGXEiedpq6F5saRIAThKxq/bbEPVFnsDnUdjaDGIre9f3/OL9Yi/yPbPcZ7RYUDpekghr666bBfZPrwNnhQ==", - "dev": true, - "requires": { - "jsdom": "^11.9.0" - } - }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -5662,18 +5726,15 @@ "dev": true }, "typedoc-plugin-markdown": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-1.2.0.tgz", - "integrity": "sha512-M3M9bx8q5u9VppIQ4ATjYV233ZyhRNbxkgTsjy7WVo4UY38dj1JWg90335ZCYktqkNkobSzE2OhVkmbQFGvi2g==", - "dev": true, - "requires": { - "turndown": "^5.0.3" - } + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-2.0.8.tgz", + "integrity": "sha512-lpaesvB5N2teG6uI9CvaX0YF0O78EGo8HAk24LYe8lkEfR4+ymgZe178luGaJiOc54fKycW+MoS8qZPoX065fg==", + "dev": true }, "typescript": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.2.tgz", - "integrity": "sha512-Og2Vn6Mk7JAuWA1hQdDQN/Ekm/SchX80VzLhjKN9ETYrIepBFAd8PkOdOTK2nKt0FCkmMZKBJvQ1dV1gIxPu/A==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", + "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", "dev": true }, "uglify-js": { @@ -5688,38 +5749,15 @@ } }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "set-value": "^2.0.1" } }, "universalify": { @@ -6015,6 +6053,14 @@ "which-module": "^2.0.0", "y18n": "^3.2.1 || ^4.0.0", "yargs-parser": "^11.1.1" + }, + "dependencies": { + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + } } }, "yargs-parser": { diff --git a/package.json b/package.json index 18da811..d00c330 100644 --- a/package.json +++ b/package.json @@ -72,27 +72,29 @@ }, "devDependencies": { "@types/benchmark": "^1.0.31", - "@types/jest": "^24.0.11", + "@types/ip-address": "^5.8.2", + "@types/jest": "^24.0.15", "@types/netmask": "^1.0.30", - "@types/node": "^12.0.8", - "@typescript-eslint/eslint-plugin": "^1.6.0", - "@typescript-eslint/parser": "^1.6.0", + "@types/node": "^12.6.2", + "@typescript-eslint/eslint-plugin": "^1.12.0", + "@typescript-eslint/parser": "^1.12.0", "benchmark": "^2.1.4", "cidr-matcher": "^2.1.0", - "coveralls": "^3.0.3", + "coveralls": "^3.0.5", "docts": "^0.2.0", - "eslint": "^5.16.0", - "eslint-config-prettier": "^4.1.0", - "eslint-plugin-prettier": "^3.0.1", + "eslint": "^6.0.1", + "eslint-config-prettier": "^6.0.0", + "eslint-plugin-prettier": "^3.1.0", + "ip-address": "^5.9.2", "ipaddr.js": "^1.9.0", - "jest": "^24.7.1", + "jest": "^24.8.0", "netmask": "^1.0.6", - "prettier": "^1.16.4", + "prettier": "^1.18.2", "ts-jest": "^24.0.2", "ts-node": "^8.3.0", "typedoc": "^0.14.2", - "typedoc-plugin-markdown": "^1.2.0", - "typescript": "^3.4.2" + "typedoc-plugin-markdown": "^2.0.8", + "typescript": "^3.5.3" }, "dependencies": {} } diff --git a/src/IPv4.ts b/src/IPv4.ts index d7437af..1e32730 100644 --- a/src/IPv4.ts +++ b/src/IPv4.ts @@ -1,7 +1,8 @@ import * as errors from "./errors"; import * as weight from "./weight"; -import { Address } from "./address"; + import { Network } from "./network"; +import { Address } from "./address"; export function bytesToAddr(bytes: number[], throwErrors?: boolean) { if (bytes.length === 4) return `${bytes[0]}.${bytes[1]}.${bytes[2]}.${bytes[3]}`; @@ -19,5 +20,5 @@ export function randomNetwork() { const bytes = Array.from(Array(4), () => Math.floor(Math.random() * 256)); const addr = new Address().setBytes(bytes); const cidr = weight.getValue(choices) as number; - return new Network().from(addr, cidr).toNetString(); + return new Network().from(addr, cidr).toString(); } diff --git a/src/IPv6.ts b/src/IPv6.ts index 57f0fdc..9f3f334 100644 --- a/src/IPv6.ts +++ b/src/IPv6.ts @@ -1,5 +1,6 @@ import * as errors from "./errors"; import * as weight from "./weight"; + import { Network } from "./network"; import { Address } from "./address"; @@ -57,5 +58,5 @@ export function randomNetwork() { const bytes = Array.from(Array(16), () => Math.floor(Math.random() * 256)); const addr = new Address().setBytes(bytes); const cidr = weight.getValue(choices) as number; - return new Network().from(addr, cidr).toNetString(); + return new Network().from(addr, cidr).toString(); } diff --git a/src/address.ts b/src/address.ts index a69f000..0a461bc 100644 --- a/src/address.ts +++ b/src/address.ts @@ -2,6 +2,7 @@ import * as ipv4 from "./ipv4"; import * as ipv6 from "./ipv6"; import * as parse from "./parse"; import * as errors from "./errors"; +import * as ranges from "./ranges"; import { Network } from "./network"; @@ -40,29 +41,55 @@ export class Address { if (this.isValid()) { this.arr = []; } + return this; } public isValid() { return this.arr.length > 0; } + public is6to4() { + return ranges.check(this, "6to4"); + } + + public isIPv4() { + return this.arr.length === 4; + } + + public isIPv6() { + return this.arr.length === 16; + } + + public isLinkLocal() { + return ranges.check(this, "linkLocal"); + } + + public isLoopback() { + return ranges.check(this, "loopback"); + } + + public isMulticast() { + return ranges.check(this, "multicast"); + } + + public isTeredo() { + return ranges.check(this, "teredo"); + } + public toString() { if (!this.isValid()) return ""; if (this.arr.length === 4) { - return `${ipv4.bytesToAddr(this.bytes())}`; + return `${ipv4.bytesToAddr(this.arr)}`; } - return `${ipv6.bytesToAddr(this.bytes())}`; + return `${ipv6.bytesToAddr(this.arr)}`; } public toNetwork() { - const net = new Network(); - if (this.isValid()) net.setCIDR(this.arr.length * 8).addr.setBytes(this.arr); - return net; + return new Network().from(new Address().setBytes(this.arr), this.arr.length * 8); } public duplicate() { - if (!this.isValid()) return new Address(); - return new Address().setBytes(this.bytes().slice()); + return new Address().setBytes(this.arr.slice()); } public lessThan(address: Address) { diff --git a/src/benchmarks/index.bench.ts b/src/benchmarks/index.bench.ts index 6ff1298..0efdb0c 100644 --- a/src/benchmarks/index.bench.ts +++ b/src/benchmarks/index.bench.ts @@ -2,6 +2,7 @@ import * as Benchmark from "benchmark"; import * as netparser from "../index"; import { Netmask } from "netmask"; import * as ipaddr from "ipaddr.js"; +import * as ipaddress from "ip-address"; new Benchmark.Suite("index.bench.ts") @@ -9,6 +10,10 @@ new Benchmark.Suite("index.bench.ts") netparser.baseAddress("192.168.0.4/24"); }) + .add("\tbaseAddress (ip-address)", () => { + new ipaddress.Address4("192.168.0.4/24").startAddress; + }) + .add("\tbaseAddress (ipaddr.js)", () => { ipaddr.IPv4.networkAddressFromCIDR("192.168.0.4/24").toString(); }) @@ -21,6 +26,10 @@ new Benchmark.Suite("index.bench.ts") netparser.networkContainsAddress("192.168.0.0/24", "192.168.0.4"); }) + .add("\tnetworkContainsAddress (ip-address)", () => { + new ipaddress.Address4("192.168.0.0/24").isInSubnet(new ipaddress.Address4("192.168.0.4")); + }) + .add("\tnetworkContainsAddress (ipaddr.js)", () => { const addr = ipaddr.parse("192.168.0.4") as ipaddr.IPv4; addr.match(ipaddr.parseCIDR("192.168.0.0/24") as [ipaddr.IPv4, number]); diff --git a/src/index.ts b/src/index.ts index 449afbb..b5a79a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,7 +61,7 @@ export function broadcastAddress(network: string, throwErrors?: boolean) { export function findUnusedSubnets(aggregate: string, subnets: string[], strict?: boolean, throwErrors?: boolean) { const agg = shared.parseBaseNetwork(aggregate, strict, throwErrors); if (!agg || !agg.isValid()) return null; - if (subnets.length === 0) return [`${agg.toNetString()}`]; + if (subnets.length === 0) return [`${agg.toString()}`]; const subnetworks = [] as Network[]; for (var s of subnets) { const net = shared.parseBaseNetwork(s, strict, throwErrors); @@ -79,7 +79,7 @@ export function findUnusedSubnets(aggregate: string, subnets: string[], strict?: while (currentSubnet) { currentSubnet = shared.findNetworkWithoutIntersection(subnetworks, currentSubnet.addr, currentSubnet.cidr()); if (!currentSubnet) break; - results.push(`${currentSubnet.toNetString()}`); + results.push(`${currentSubnet.toString()}`); if (!currentSubnet.next().isValid()) break; if (currentSubnet.addr.greaterThan(lastAggAddr)) break; currentSubnet.setCIDR(agg.cidr()); @@ -115,7 +115,7 @@ export function ip(address: string, throwErrors?: boolean) { * @returns The parsed network address or null in case of error */ export function network(networkAddress: string, throwErrors?: boolean) { - const net = new Network(networkAddress, throwErrors).toNetString(); + const net = new Network(networkAddress, throwErrors).toString(); return net.length > 0 ? net : null; } @@ -255,7 +255,7 @@ export function nextNetwork(network: string, strict?: boolean, throwErrors?: boo if (throwErrors) throw errors.OverflowedAddressSpace; return null; } - return net.toNetString(); + return net.toString(); } /** @@ -293,7 +293,7 @@ export function rangeOfNetworks(startAddress: string, stopAddress: string, throw while (!net.addr.isBaseAddress(net.cidr()) || net.lastAddr().greaterThan(stopAddr)) { net.setCIDR(net.cidr() + 1); } - results.push(net.toNetString()); + results.push(net.toString()); net.next().setCIDR(1); } return results; @@ -332,7 +332,7 @@ export function sort(networkAddresses: string[], throwErrors?: boolean) { const results = new Array(subnets.length) as string[]; for (let i = 0; i < subnets.length; i++) { if (foundCIDR) { - results[i] = subnets[i].toNetString(); + results[i] = subnets[i].toString(); } else { results[i] = subnets[i].addr.toString(); } @@ -369,7 +369,7 @@ export function summarize(networks: string[], strict?: boolean, throwErrors?: bo subnets = shared.summarizeSortedNetworks(subnets); const results = new Array(subnets.length) as string[]; for (let i = 0; i < subnets.length; i++) { - results[i] = subnets[i].toNetString(); + results[i] = subnets[i].toString(); } return results; } diff --git a/src/network.ts b/src/network.ts index ff9ea4d..afcb2a3 100644 --- a/src/network.ts +++ b/src/network.ts @@ -1,5 +1,6 @@ import * as parse from "./parse"; import * as errors from "./errors"; + import { Address } from "./address"; const BEFORE = -1; @@ -22,8 +23,7 @@ export class Network { public from(address: Address, cidr: number) { this.addr.setBytes(address.bytes().slice()); - this.setCIDR(cidr); - return this; + return this.setCIDR(cidr); } public destroy() { @@ -31,6 +31,7 @@ export class Network { this.addr.destroy(); } this.netbits = -1; + return this; } public cidr() { @@ -53,7 +54,7 @@ export class Network { return network; } - public toNetString() { + public toString() { if (this.isValid()) { return `${this.addr.toString()}/${this.netbits}`; } diff --git a/src/ranges.ts b/src/ranges.ts index a6fe2c8..415a42d 100644 --- a/src/ranges.ts +++ b/src/ranges.ts @@ -1,111 +1,103 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ + import { Network } from "./network"; +import { Address } from "./address"; -export const specialIPv4 = [ - { - name: "unspecified", // - network: new Network("0.0.0.0/8") - }, - { - name: "broadcast", // - network: new Network("255.255.255.255/32") - }, - { - name: "multicast", // RFC3171 - network: new Network("224.0.0.0/4") - }, - { - name: "link local", // RFC3927 - network: new Network("169.254.0.0/16") - }, - { - name: "loopback", // RFC5735 - network: new Network("127.0.0.0/8") - }, - { - name: "carrier grade nat", // RFC6598 - network: new Network("100.64.0.0/10") - }, - { - name: "private", // RFC1918 - network: new Network("10.0.0.0/8") - }, - { - name: "private", // RFC1918 - network: new Network("172.16.0.0/12") - }, - { - name: "private", // RFC1918 - network: new Network("192.168.0.0/16") - }, - { - name: "reserved", // 5735, 5737, 2544, 1700 - network: new Network("192.0.0.0/24") - }, - { - name: "reserved", // 5735, 5737, 2544, 1700 - network: new Network("192.0.2.0/24") - }, - { - name: "reserved", // 5735, 5737, 2544, 1700 - network: new Network("192.88.99.0/24") - }, - { - name: "reserved", // 5735, 5737, 2544, 1700 - network: new Network("198.51.100.0/24") - }, - { - name: "reserved", // 5735, 5737, 2544, 1700 - network: new Network("203.0.113.0/24") - }, - { - name: "reserved", // 5735, 5737, 2544, 1700 - network: new Network("240.0.0.0/24") - } -]; +var ipv4Ranges = undefined as any; + +function createIPv4Ranges() { + return { + unspecified: [ + new Network("0.0.0.0/8", true) // + ], + broadcast: [ + new Network("255.255.255.255/32") // + ], + multicast: [ + new Network("224.0.0.0/4") // RFC3171 + ], + linkLocal: [ + new Network("169.254.0.0/16") // RFC3927 + ], + loopback: [ + new Network("127.0.0.0/8") // RFC5735 + ], + carrierGradeNat: [ + new Network("100.64.0.0/10") //RFC6598 + ], + private: [ + new Network("10.0.0.0/8"), // RFC1918 + new Network("172.16.0.0/12"), + new Network("192.168.0.0/16") + ], + reserved: [ + new Network("192.0.0.0/24"), // RFC5735, RFC5737, RFC2544, RFC1700 + new Network("192.0.2.0/24"), + new Network("192.88.99.0/24"), + new Network("198.51.100.0/24"), + new Network("203.0.113.0/24"), + new Network("240.0.0.0/24") + ] + }; +} + +var ipv6Ranges = undefined as any; + +function createIPv6Ranges() { + return { + unspecified: [ + new Network("::/128") // RFC4291 + ], + linkLocal: [ + new Network("fe80::/10") // RFC4291 + ], + multicast: [ + new Network("ff00::/8") // RFC4291 + ], + loopback: [ + new Network("::1/128") // RFC4291 + ], + uniqueLocal: [ + new Network("fc00::/7") // RFC4291 + ], + ipv4Mapped: [ + new Network("::ffff:0:0/96") // RFC4291 + ], + rfc6145: [ + new Network("::ffff:0:0:0/96") // RFC6145 + ], + rfc6052: [ + new Network("64:ff9b::/96") // RFC6052 + ], + "6to4": [ + new Network("2002::/16") // RFC3056 + ], + teredo: [ + new Network("2001::/32") // RFC6052, RFC6146 + ], + reserved: [ + new Network("2001:db8::/32") // RFC4291 + ] + }; +} + +// https://dev.to/kingdaro/indexing-objects-in-typescript-1cgi +function hasKey(obj: O, key: keyof any): key is keyof O { + return key in obj; +} -export const specialIPv6 = [ - { - name: "unspecified", // RFC4291 - network: new Network("::/128") - }, - { - name: "link local", // RFC4291 - network: new Network("fe80::/10") - }, - { - name: "multicast", // RFC4291 - network: new Network("ff00::/8") - }, - { - name: "loopback", // RFC4291 - network: new Network("::1/128") - }, - { - name: "unique local", // RFC4291 - network: new Network("fc00::/7") - }, - { - name: "ipv4 mapped", // RFC4291 - network: new Network("::ffff:0:0/96") - }, - { - name: "rfc6145", // RFC6145 - network: new Network("::ffff:0:0:0/96") - }, - { - name: "rfc6052", // RFC6052 - network: new Network("64:ff9b::/96") - }, - { - name: "6to4", // RFC3056 - network: new Network("2002::/16") - }, - { - name: "teredo", // RFC6052, RFC6146 - network: new Network("2001::/32") - }, - { - name: "reserved", // RFC4291 - network: new Network("2001:db8::/32") +export function check(address: Address, key: string) { + if (address.isValid()) { + if (address.isIPv4()) { + var ranges = ipv4Ranges ? ipv4Ranges : createIPv4Ranges(); + } else { + var ranges = ipv6Ranges ? ipv6Ranges : createIPv6Ranges(); + } + if (hasKey(ranges, key)) { + for (var net of ranges[key]) { + if (net.contains(address.toNetwork())) return true; + } + } } -]; + return false; +} diff --git a/src/shared.ts b/src/shared.ts index 3aea914..43654ae 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -1,20 +1,13 @@ import * as sort from "./sort"; import * as errors from "./errors"; + import { Network } from "./network"; import { Address } from "./address"; -export function repeatString(s: string, count: number) { - var result = ""; - for (var i = 0; i < count; i++) { - result += s; - } - return result; -} - export function getCIDR(s: string, throwErrors?: boolean) { const splitAddr = s.split("/"); if (splitAddr.length === 2) { - const val = parseInt(splitAddr[1], 10); + const val = Number(splitAddr[1]); if (Number.isInteger(val)) { const maxCIDR = splitAddr[0].search(":") >= 0 ? 128 : 32; if (0 < val && val <= maxCIDR) return val; diff --git a/src/tests/address.test.ts b/src/tests/address.test.ts new file mode 100644 index 0000000..c282a0b --- /dev/null +++ b/src/tests/address.test.ts @@ -0,0 +1,51 @@ +import { Address } from "../address"; + +test("sanity check bytes #1", () => { + const output = new Address("192.168.0.0").bytes(); + expect(output).toEqual([192, 168, 0, 0]); +}); + +test("sanity check bytes #2", () => { + const output = new Address(); + expect(output.bytes()).toEqual([]); +}); + +test("sanity check setBytes #1", () => { + const output = new Address().setBytes([192, 168, 0, 4]); + expect(`${output}`).toEqual("192.168.0.4"); +}); + +test("sanity check setBytes #2", () => { + const output = new Address().setBytes([0, 2]); + expect(`${output}`).toEqual(""); +}); + +test("sanity check destroy #1", () => { + const output = new Address("192.168.0.0"); + expect(`${output.destroy()}`).toEqual(""); +}); + +test("sanity check toNetwork #1", () => { + const output = new Address("192.168.0.5"); + expect(`${output.toNetwork()}`).toEqual("192.168.0.5/32"); +}); + +test("sanity check increase #1", () => { + const output = new Address("192.168.0.5"); + expect(`${output.increase(31)}`).toEqual("192.168.0.7"); +}); + +test("sanity check decrease #1", () => { + const output = new Address("192.168.0.5"); + expect(`${output.decrease(31)}`).toEqual("192.168.0.3"); +}); + +test("sanity check next #1", () => { + const output = new Address("192.168.0.5"); + expect(`${output.next()}`).toEqual("192.168.0.6"); +}); + +test("sanity check previous #1", () => { + const output = new Address("192.168.0.5"); + expect(`${output.previous()}`).toEqual("192.168.0.4"); +}); diff --git a/src/tests/shared.test.ts b/src/tests/shared.test.ts new file mode 100644 index 0000000..a7a7688 --- /dev/null +++ b/src/tests/shared.test.ts @@ -0,0 +1,26 @@ +import * as shared from "../shared"; + +test("sanity check getCIDR #1", () => { + const output = shared.getCIDR("192.168.0.0/128"); + expect(output).toEqual(null); +}); + +test("sanity check getCIDR #2", () => { + const output = shared.getCIDR("192.168.0.0/24abc"); + expect(output).toEqual(null); +}); + +test("sanity check getCIDR #3", () => { + const output = shared.getCIDR("192.168.0.0/24"); + expect(output).toEqual(24); +}); + +test("sanity check parseBaseNetwork #1", () => { + const output = shared.parseBaseNetwork("192.168.0.4/24", true); + expect(output).toEqual(null); +}); + +test("sanity check parseBaseNetwork #2", () => { + const output = shared.parseBaseNetwork("192.168.0.4/24", false); + expect(output.toString()).toEqual("192.168.0.0/24"); +}); diff --git a/src/tests/shared.test.ts.bckp b/src/tests/shared.test.ts.bckp deleted file mode 100644 index 136330c..0000000 --- a/src/tests/shared.test.ts.bckp +++ /dev/null @@ -1,34 +0,0 @@ -import * as shared from "../shared"; - -test("sanity check setAddress #1", () => { - shared.setAddress(new Uint8Array(4), new Uint8Array(16)); -}); - -test("sanity check setAddress #2", () => { - shared.setAddress(new Uint8Array(16), new Uint8Array(4)); -}); - -test("sanity check compareAddresses #1", () => { - const output = shared.compareAddresses(new Uint8Array(4), new Uint8Array(16)); - expect(output).toEqual(shared.Pos.before); -}); - -test("sanity check compareAddresses #2", () => { - const output = shared.compareAddresses(new Uint8Array(16), new Uint8Array(4)); - expect(output).toEqual(shared.Pos.after); -}); - -test("sanity check increaseAddressWithCIDR #1", () => { - const output = shared.increaseAddressWithCIDR(new Uint8Array([255, 255, 255, 255]), 32); - expect(output).toEqual(null); -}); - -test("sanity check decreaseAddressWithCIDR #1", () => { - const output = shared.decreaseAddressWithCIDR(new Uint8Array([0, 0, 0, 0]), 32); - expect(output).toEqual(null); -}); - -test("sanity check parseAddressString #1", () => { - const output = shared.parseAddressString("foobar"); - expect(output).toEqual(null); -}); From 510928d966f29abfd60d75483f919110c3d004dd Mon Sep 17 00:00:00 2001 From: Alex Demskie Date: Sun, 14 Jul 2019 01:08:41 -0700 Subject: [PATCH 6/8] benchmarking sorting algorithms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'sort.bench.ts' output: sort n=100 (native) x 17,692 ops/sec ±1.94% (92 runs sampled) sort n=100 (insertion) x 46,666 ops/sec ±1.65% (91 runs sampled) sort n=100 (radix msd) x 382 ops/sec ±1.26% (87 runs sampled) sort n=1000 (native) x 1,426 ops/sec ±1.07% (93 runs sampled) sort n=1000 (insertion) x 2,465 ops/sec ±0.80% (93 runs sampled) sort n=1000 (radix msd) x 38.93 ops/sec ±0.84% (52 runs sampled) sort n=10000 (native) x 108 ops/sec ±1.01% (78 runs sampled) sort n=10000 (insertion) x 83.35 ops/sec ±0.97% (71 runs sampled) sort n=10000 (radix msd) x 3.76 ops/sec ±2.67% (14 runs sampled) sort n=100000 (native) x 4.90 ops/sec ±3.68% (17 runs sampled) sort n=100000 (insertion) x 0.14 ops/sec ±38.07% (5 runs sampled) sort n=100000 (radix msd) x 0.37 ops/sec ±4.88% (5 runs sampled) --- package.json | 5 ++- src/IPv4.ts | 2 +- src/IPv6.ts | 2 +- src/benchmarks/match.bench.ts | 8 ++-- src/benchmarks/sort.bench.ts | 79 +++++++++++++++++++++++++++++++++++ src/index.ts | 6 +-- src/shared.ts | 5 ++- src/sort.ts | 79 ++++++++++++++++++++++++++--------- src/tests/sort.test.ts | 50 ++++++++++++++++++++++ 9 files changed, 205 insertions(+), 31 deletions(-) create mode 100644 src/benchmarks/sort.bench.ts diff --git a/package.json b/package.json index d00c330..f9f9a10 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,10 @@ "scripts": { "build": "tsc && docts && jest --coverage", "test": "tsc --noEmit && jest --coverage", - "bench": "ts-node -T src/benchmarks/index.bench.ts && ts-node -T src/benchmarks/match.bench.ts" + "bench": "npm run bench-index && npm run bench-match && npm run bench-sort", + "bench-index": "ts-node -T src/benchmarks/index.bench.ts", + "bench-match": "ts-node -T src/benchmarks/match.bench.ts", + "bench-sort": "ts-node -T src/benchmarks/sort.bench.ts" }, "repository": { "type": "git", diff --git a/src/IPv4.ts b/src/IPv4.ts index 1e32730..13698ff 100644 --- a/src/IPv4.ts +++ b/src/IPv4.ts @@ -20,5 +20,5 @@ export function randomNetwork() { const bytes = Array.from(Array(4), () => Math.floor(Math.random() * 256)); const addr = new Address().setBytes(bytes); const cidr = weight.getValue(choices) as number; - return new Network().from(addr, cidr).toString(); + return new Network().from(addr, cidr); } diff --git a/src/IPv6.ts b/src/IPv6.ts index 9f3f334..0d638fe 100644 --- a/src/IPv6.ts +++ b/src/IPv6.ts @@ -58,5 +58,5 @@ export function randomNetwork() { const bytes = Array.from(Array(16), () => Math.floor(Math.random() * 256)); const addr = new Address().setBytes(bytes); const cidr = weight.getValue(choices) as number; - return new Network().from(addr, cidr).toString(); + return new Network().from(addr, cidr); } diff --git a/src/benchmarks/match.bench.ts b/src/benchmarks/match.bench.ts index 6e50f7b..638ad48 100644 --- a/src/benchmarks/match.bench.ts +++ b/src/benchmarks/match.bench.ts @@ -5,15 +5,15 @@ import * as ipv6 from "../ipv6"; import * as ipaddr from "ipaddr.js"; import { default as cidrMatcher } from "cidr-matcher"; -const ipv4Addresses = Array.from(Array(1000), () => ipv4.randomAddress()); -const ipv4Subnets = Array.from(Array(10000), () => ipv4.randomNetwork()); +const ipv4Addresses = Array.from(Array(1000), () => ipv4.randomAddress().toString()); +const ipv4Subnets = Array.from(Array(10000), () => ipv4.randomNetwork().toString()); let netparserMatcherIPv4 = new match.Matcher(); let ipaddrRangeListIPv4 = { subnetRanges: [] }; let cidrMatcherIPv4 = new cidrMatcher(ipv4Subnets); -const ipv6Addresses = Array.from(Array(1000), () => ipv6.randomAddress()); -const ipv6Subnets = Array.from(Array(10000), () => ipv6.randomNetwork()); +const ipv6Addresses = Array.from(Array(1000), () => ipv6.randomAddress().toString()); +const ipv6Subnets = Array.from(Array(10000), () => ipv6.randomNetwork().toString()); let netparserMatcherIPv6 = new match.Matcher(); let ipaddrRangeListIPv6 = { subnetRanges: [] }; diff --git a/src/benchmarks/sort.bench.ts b/src/benchmarks/sort.bench.ts new file mode 100644 index 0000000..bd02ec6 --- /dev/null +++ b/src/benchmarks/sort.bench.ts @@ -0,0 +1,79 @@ +import * as Benchmark from "benchmark"; +import * as ipv4 from "../ipv6"; +import * as sort from "../sort"; +import { Network } from "../network"; + +const e2Alpha = Array.from(Array(1e2), () => ipv4.randomNetwork()); +const e2Bravo = Array.from(e2Alpha, (net: Network) => net.duplicate()); +const e2Charlie = Array.from(e2Alpha, (net: Network) => net.duplicate()); + +const e3Alpha = Array.from(Array(1e3), () => ipv4.randomNetwork()); +const e3Bravo = Array.from(e3Alpha, (net: Network) => net.duplicate()); +const e3Charlie = Array.from(e3Alpha, (net: Network) => net.duplicate()); + +const e4Alpha = Array.from(Array(1e4), () => ipv4.randomNetwork()); +const e4Bravo = Array.from(e4Alpha, (net: Network) => net.duplicate()); +const e4Charlie = Array.from(e4Alpha, (net: Network) => net.duplicate()); + +const e5Alpha = Array.from(Array(1e5), () => ipv4.randomNetwork()); +const e5Bravo = Array.from(e5Alpha, (net: Network) => net.duplicate()); +const e5Charlie = Array.from(e5Alpha, (net: Network) => net.duplicate()); + +new Benchmark.Suite("sort.bench.ts") + + .add("\tsort n=100 (native)\t", () => { + sort.nativeSort(e2Alpha); + }) + + .add("\tsort n=100 (insertion)\t", () => { + sort.insertionSort(e2Bravo); + }) + + .add("\tsort n=100 (radix msd)\t", () => { + sort.radixSort(e2Charlie); + }) + + .add("\tsort n=1000 (native)\t", () => { + sort.nativeSort(e3Alpha); + }) + + .add("\tsort n=1000 (insertion)\t", () => { + sort.insertionSort(e3Bravo); + }) + + .add("\tsort n=1000 (radix msd)\t", () => { + sort.radixSort(e3Charlie); + }) + + .add("\tsort n=10000 (native)\t", () => { + sort.nativeSort(e4Alpha); + }) + + .add("\tsort n=10000 (insertion)", () => { + sort.insertionSort(e4Bravo); + }) + + .add("\tsort n=10000 (radix msd)", () => { + sort.radixSort(e4Charlie); + }) + + .add("\tsort n=100000 (native)\t", () => { + sort.nativeSort(e5Alpha); + }) + + .add("\tsort n=100000 (insertion)", () => { + sort.insertionSort(e5Bravo); + }) + + .add("\tsort n=100000 (radix msd)", () => { + sort.radixSort(e5Charlie); + }) + + .on("complete", function() { + console.log(`'${this.name}' output:`); + this.forEach((bench: Benchmark) => { + console.log(bench.toString()); + }); + }) + + .run(); diff --git a/src/index.ts b/src/index.ts index b5a79a6..2b5cca7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -311,7 +311,7 @@ export function rangeOfNetworks(startAddress: string, stopAddress: string, throw * @returns An array of networks or null in case of error */ export function sort(networkAddresses: string[], throwErrors?: boolean) { - const subnets = new Array(networkAddresses.length) as Network[]; + let subnets = new Array(networkAddresses.length) as Network[]; let foundCIDR = false; for (let i = 0; i < networkAddresses.length; i++) { const addr = new Address(networkAddresses[i], throwErrors); @@ -328,7 +328,7 @@ export function sort(networkAddresses: string[], throwErrors?: boolean) { } subnets[i] = new Network().from(addr, cidr); } - shared.sortNetworks(subnets); + subnets = shared.sortNetworks(subnets); const results = new Array(subnets.length) as string[]; for (let i = 0; i < subnets.length; i++) { if (foundCIDR) { @@ -365,7 +365,7 @@ export function summarize(networks: string[], strict?: boolean, throwErrors?: bo return null; } } - shared.sortNetworks(subnets); + subnets = shared.sortNetworks(subnets); subnets = shared.summarizeSortedNetworks(subnets); const results = new Array(subnets.length) as string[]; for (let i = 0; i < subnets.length; i++) { diff --git a/src/shared.ts b/src/shared.ts index 43654ae..9c88019 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -18,9 +18,10 @@ export function getCIDR(s: string, throwErrors?: boolean) { } export function sortNetworks(networks: Network[]) { - if (networks && networks.length > 1) { - sort.radixSortNetworks(networks, 0, networks.length, -1); + if (networks.length < 10000) { + return sort.insertionSort(networks); } + return sort.nativeSort(networks); } export function summarizeSortedNetworks(sorted: Network[]) { diff --git a/src/sort.ts b/src/sort.ts index 2c1adde..6c5e425 100644 --- a/src/sort.ts +++ b/src/sort.ts @@ -1,10 +1,64 @@ import { Network } from "./network"; +import { sortBy } from "lodash"; const BEFORE = -1; const EQUALS = 0; const AFTER = 1; -export function radixSortNetworks(networks: Network[], start: number, stop: number, byteIndex: number) { +export function nativeSort(networks: Network[]) { + return sortBy(networks, [ + o => o.addr.bytes().length, + o => (o.addr.bytes()[0] ? o.addr.bytes()[0] : 0), + o => (o.addr.bytes()[1] ? o.addr.bytes()[1] : 0), + o => (o.addr.bytes()[2] ? o.addr.bytes()[2] : 0), + o => (o.addr.bytes()[3] ? o.addr.bytes()[3] : 0), + o => (o.addr.bytes()[4] ? o.addr.bytes()[4] : 0), + o => (o.addr.bytes()[5] ? o.addr.bytes()[5] : 0), + o => (o.addr.bytes()[6] ? o.addr.bytes()[6] : 0), + o => (o.addr.bytes()[7] ? o.addr.bytes()[7] : 0), + o => (o.addr.bytes()[8] ? o.addr.bytes()[8] : 0), + o => (o.addr.bytes()[9] ? o.addr.bytes()[9] : 0), + o => (o.addr.bytes()[10] ? o.addr.bytes()[10] : 0), + o => (o.addr.bytes()[11] ? o.addr.bytes()[11] : 0), + o => (o.addr.bytes()[12] ? o.addr.bytes()[12] : 0), + o => (o.addr.bytes()[13] ? o.addr.bytes()[13] : 0), + o => (o.addr.bytes()[14] ? o.addr.bytes()[14] : 0), + o => (o.addr.bytes()[15] ? o.addr.bytes()[15] : 0), + o => o.cidr() + ]); +} + +export function binarySearchForInsertionIndex(network: Network, sortedNetworks: Network[]) { + if (!sortedNetworks || sortedNetworks.length === 0) return 0; + let left = 0; + let right = sortedNetworks.length - 1; + while (left < right) { + let middle = Math.floor((left + right) / 2); + switch (sortedNetworks[middle].compare(network)) { + case EQUALS: + return middle; + case BEFORE: + left = middle + 1; + break; + case AFTER: + right = middle - 1; + break; + } + } + if (sortedNetworks[left].compare(network) === BEFORE) return left + 1; + return left; +} + +export function insertionSort(networks: Network[]) { + const sorted = [] as Network[]; + networks.forEach((net: Network) => { + var idx = binarySearchForInsertionIndex(net, sorted); + sorted.splice(idx, 0, net); + }); + return sorted; +} + +function msdRadixSort(networks: Network[], start: number, stop: number, byteIndex: number) { const runningPrefixSum = new Array(256) as number[]; const offsetPrefixSum = new Array(256) as number[]; const counts = runningPrefixSum; @@ -89,29 +143,16 @@ export function radixSortNetworks(networks: Network[], start: number, stop: numb let lastPrefixSum = 0; for (var i = 0; i < runningPrefixSum.length; i++) { if (runningPrefixSum[i] !== lastPrefixSum) { - radixSortNetworks(networks, start + lastPrefixSum, start + runningPrefixSum[i], byteIndex + 1); + msdRadixSort(networks, start + lastPrefixSum, start + runningPrefixSum[i], byteIndex + 1); } lastPrefixSum = runningPrefixSum[i]; } } } -export function binarySearchForInsertionIndex(network: Network, sortedNetworks: Network[]) { - let left = 0; - let right = sortedNetworks.length - 1; - while (left < right) { - let middle = Math.floor((left + right) / 2); - switch (sortedNetworks[middle].compare(network)) { - case EQUALS: - return middle; - case BEFORE: - left = middle + 1; - break; - case AFTER: - right = middle - 1; - break; - } +export function radixSort(networks: Network[]) { + if (networks.length > 0) { + msdRadixSort(networks, 0, networks.length, -1); } - if (sortedNetworks[left].compare(network) === BEFORE) return left + 1; - return left; + return networks; } diff --git a/src/tests/sort.test.ts b/src/tests/sort.test.ts index 5f64c9a..6cddcc2 100644 --- a/src/tests/sort.test.ts +++ b/src/tests/sort.test.ts @@ -1,4 +1,5 @@ import * as sort from "../sort"; +import * as ipv4 from "../ipv6"; import { Network } from "../network"; test("sanity check binarySearchForInsertionIndex #1", () => { @@ -60,3 +61,52 @@ test("sanity check binarySearchForInsertionIndex #3", () => { const output = sort.binarySearchForInsertionIndex(inputNetwork, inputArray); expect(output).toEqual(11); }); + +test("sanity check binarySearchForInsertionIndex #4", () => { + const before = [ + new Network("45.153.242.35/31"), + new Network("143.80.146.80/28"), + new Network("192.238.208.227/29"), + new Network("160.187.236.173/29"), + new Network("18.47.206.111/30"), + new Network("29.141.134.6/26"), + new Network("91.216.183.86/26"), + new Network("23.232.169.130/31"), + new Network("28.118.179.165/30"), + new Network("116.112.231.148/30") + ]; + const after = []; + before.forEach((net: Network) => { + var idx = sort.binarySearchForInsertionIndex(net, after); + after.splice(idx, 0, net); + }); + expect(after).toEqual([ + new Network("18.47.206.111/30"), + new Network("23.232.169.130/31"), + new Network("28.118.179.165/30"), + new Network("29.141.134.6/26"), + new Network("45.153.242.35/31"), + new Network("91.216.183.86/26"), + new Network("116.112.231.148/30"), + new Network("143.80.146.80/28"), + new Network("160.187.236.173/29"), + new Network("192.238.208.227/29") + ]); +}); + +test("cross check all sorting methods", () => { + const alpha = Array.from(Array(10000), () => ipv4.randomNetwork()); + const bravo = Array.from(alpha, (net: Network) => net.duplicate()); + const charlie = Array.from(alpha, (net: Network) => net.duplicate()); + + const sortedAlpha = sort.insertionSort(alpha); + const sortedBravo = sort.radixSort(bravo); + const sortedCharlie = sort.nativeSort(charlie); + + const sortedAlphaStrings = Array.from(sortedAlpha, (net: Network) => net.toString()); + const sortedBravoStrings = Array.from(sortedBravo, (net: Network) => net.toString()); + const sortedCharlieStrings = Array.from(sortedCharlie, (net: Network) => net.toString()); + + expect(sortedAlphaStrings).toEqual(sortedBravoStrings); + expect(sortedAlphaStrings).toEqual(sortedCharlieStrings); +}); From 87a1a44a47440ef9def03d56cfd8b028c3301f68 Mon Sep 17 00:00:00 2001 From: Alex Demskie Date: Sun, 14 Jul 2019 19:26:39 -0700 Subject: [PATCH 7/8] pass all parsing tests --- src/address.ts | 13 +-- src/mockdata/subnets.mock.ts | 4 +- src/parse.ts | 155 ++++++++++++++++++++++++++--------- src/tests/ipv4.test.ts | 66 +++++++++++++++ src/tests/ipv4.test.ts.bckp | 154 ---------------------------------- src/tests/ipv6.test.ts | 32 ++++++++ src/tests/ipv6.test.ts.bckp | 56 ------------- src/tests/parse.test.ts | 6 -- 8 files changed, 225 insertions(+), 261 deletions(-) create mode 100644 src/tests/ipv4.test.ts delete mode 100644 src/tests/ipv4.test.ts.bckp create mode 100644 src/tests/ipv6.test.ts delete mode 100644 src/tests/ipv6.test.ts.bckp delete mode 100644 src/tests/parse.test.ts diff --git a/src/address.ts b/src/address.ts index 0a461bc..02d688f 100644 --- a/src/address.ts +++ b/src/address.ts @@ -255,14 +255,17 @@ export class Address { if (this.isValid() && targetByte >= 0 && targetByte < this.arr.length) { var increment = Math.pow(2, 8 - (cidr - targetByte * 8)); this.arr[targetByte] += increment * (forwards ? 1 : -1); - if (this.arr[targetByte] < 0 || this.arr[targetByte] > 255) { - if (targetByte > 0) { + if (targetByte >= 0) { + if (this.arr[targetByte] < 0) { + this.arr[targetByte] = 256 + (this.arr[targetByte] % 256); + this.offsetAddress(targetByte * 8, forwards, throwErrors); + } else if (this.arr[targetByte] > 255) { this.arr[targetByte] %= 256; this.offsetAddress(targetByte * 8, forwards, throwErrors); - } else { - if (throwErrors) throw errors.OverflowedAddressSpace; - this.destroy(); } + } else { + if (throwErrors) throw errors.OverflowedAddressSpace; + this.destroy(); } } else { if (throwErrors) throw errors.GenericOffsetAddressWithCIDR; diff --git a/src/mockdata/subnets.mock.ts b/src/mockdata/subnets.mock.ts index 848c364..db32eb9 100644 --- a/src/mockdata/subnets.mock.ts +++ b/src/mockdata/subnets.mock.ts @@ -11,9 +11,9 @@ export const valid = [ export const invalid = [ "fd37:a7ce:080d:3651:::/64", - // "fd34:fe56: 7891:2f3a::/64", + "fd34:fe56: 7891:2f3a::/64", "fd56:3788:18e9:5c13::/-12", - // "fd78g:e9ac:f08a:8b18::/64", + "fd78g:e9ac:f08a:8b18::/64", "fd21:e152:9b7c:cd06::/129", "fd40/:c321:349b:8901::/0", "fde44:3510:269e:ffbd::/64", diff --git a/src/parse.ts b/src/parse.ts index 7628ce3..1a11fa6 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -82,6 +82,67 @@ export function v4AddrToBytes(old: string) { return null; } +function parseHextet(s: string) { + if (s.trim().length < 1 || s.trim().length > 4) return Number.NaN; + var val = 0; + for (var i = 0; i < s.length; i++) { + if (i >= 4) return Number.NaN; + var p = 4 * (s.length - i - 1); + switch (s[i]) { + case "0": + break; + case "1": + val += 1 * Math.pow(2, p); + break; + case "2": + val += 2 * Math.pow(2, p); + break; + case "3": + val += 3 * Math.pow(2, p); + break; + case "4": + val += 4 * Math.pow(2, p); + break; + case "5": + val += 5 * Math.pow(2, p); + break; + case "6": + val += 6 * Math.pow(2, p); + break; + case "7": + val += 7 * Math.pow(2, p); + break; + case "8": + val += 8 * Math.pow(2, p); + break; + case "9": + val += 9 * Math.pow(2, p); + break; + case "a": + val += 10 * Math.pow(2, p); + break; + case "b": + val += 11 * Math.pow(2, p); + break; + case "c": + val += 12 * Math.pow(2, p); + break; + case "d": + val += 13 * Math.pow(2, p); + break; + case "e": + val += 14 * Math.pow(2, p); + break; + case "f": + val += 15 * Math.pow(2, p); + break; + default: + return Number.NaN; + } + } + return val; +} + /* https://tools.ietf.org/html/rfc3986 ffff:fc00::1:1234/64 @@ -105,29 +166,11 @@ export function v6AddrToBytes(old: string) { if (old === "::") return bytes; var halves = old.split("::"); if (halves.length === 0 || halves.length > 2) return null; - var leftByteIndex = 0; - if (halves[0] !== "") { - var leftParts = halves[0].split(":"); - for (var i = 0; i < leftParts.length; i++) { - if (leftByteIndex >= 16) return bytes; - var x = parseInt(leftParts[i], 16); - if (Number.isNaN(x)) { - var ipv4Parts = leftParts[i].split("."); - if (ipv4Parts.length !== 4) return null; - for (var j = 0; j < ipv4Parts.length; j++) { - x = parseInt(ipv4Parts[j], 10); - if (Number.isNaN(x) || x < 0 || x > 255) return null; - bytes[leftByteIndex++] = x; - } - continue; - } - if (x < 0 || x > 65535) return null; - bytes[leftByteIndex++] = Math.floor(x / 256); - bytes[leftByteIndex++] = Math.floor(x % 256); - } - } - if (halves.length === 2 && halves[1] !== "") { - return parseRightHalf(bytes, leftByteIndex, halves[1].split(":")); + var leftByteIndex = parseLeftHalf(bytes, halves[0]); + if (leftByteIndex === null) return null; + if (halves.length === 2) { + var rightByteIndex = parseRightHalf(bytes, halves[1], leftByteIndex); + if (rightByteIndex === null) return null; } return bytes; } @@ -141,24 +184,60 @@ function removeBrackets(s: string) { return s.substring(1); } -function parseRightHalf(bytes: number[], leftByteIndex: number, rightParts: string[]) { +function parseLeftHalf(bytes: number[], leftHalf: string) { + var leftByteIndex = 0; + if (leftHalf !== "") { + var leftParts = leftHalf.split(":"); + for (var i = 0; i < leftParts.length; i++) { + if (leftByteIndex >= 16) return null; + var ipv4Parts = leftParts[i].split("."); + if (ipv4Parts.length === 0) return null; + if (ipv4Parts.length !== 4) { + var x = parseHextet(leftParts[i]); + if (Number.isNaN(x) || x < 0 || x > 65535) return null; + bytes[leftByteIndex++] = Math.floor(x / 256); + bytes[leftByteIndex++] = Math.floor(x % 256); + } else { + for (var j = 0; j < ipv4Parts.length; j++) { + var x = Number(ipv4Parts[j]); + if (Number.isNaN(x) || x < 0 || x > 255) return null; + bytes[leftByteIndex++] = x; + } + } + } + } + return leftByteIndex; +} + +function removePortInfo(s: string) { + return s.replace(/(#|p|\.).*/g, "").trim(); +} + +function parseRightHalf(bytes: number[], rightHalf: string, leftByteIndex: number) { var rightByteIndex = 15; - for (var i = rightParts.length - 1; i >= 0; i--) { - if (leftByteIndex > rightByteIndex) return null; - var x = parseInt(rightParts[i], 16); - if (Number.isNaN(x)) { + if (rightHalf !== "") { + var rightParts = rightHalf.split(":"); + for (var i = rightParts.length - 1; i >= 0; i--) { + if (rightParts[i].trim() === "") return null; + if (leftByteIndex > rightByteIndex) return null; var ipv4Parts = rightParts[i].split("."); - if (ipv4Parts.length !== 4) return null; - for (var j = ipv4Parts.length - 1; j >= 0; j--) { - x = parseInt(ipv4Parts[j], 10); - if (Number.isNaN(x) || x < 0 || x > 255) return null; - bytes[rightByteIndex--] = x; + if (ipv4Parts.length === 0) return null; + if (ipv4Parts.length !== 4) { + if (i === rightParts.length - 1) { + rightParts[i] = removePortInfo(rightParts[i]); + } + var x = parseHextet(rightParts[i]); + if (Number.isNaN(x) || x < 0 || x > 65535) return null; + bytes[rightByteIndex--] = Math.floor(x % 256); + bytes[rightByteIndex--] = Math.floor(x / 256); + } else { + for (var j = ipv4Parts.length - 1; j >= 0; j--) { + var x = Number(ipv4Parts[j]); + if (Number.isNaN(x) || x < 0 || x > 255) return null; + bytes[rightByteIndex--] = x; + } } - continue; } - if (x < 0 || x > 65535) return null; - bytes[rightByteIndex--] = Math.floor(x % 256); - bytes[rightByteIndex--] = Math.floor(x / 256); } - return bytes; + return rightByteIndex; } diff --git a/src/tests/ipv4.test.ts b/src/tests/ipv4.test.ts new file mode 100644 index 0000000..dcb0b37 --- /dev/null +++ b/src/tests/ipv4.test.ts @@ -0,0 +1,66 @@ +import * as index from "../index"; +import * as ipv4 from "../ipv4"; +import { Network } from "../network"; +import { Address } from "../address"; + +test("sanity check IPv4 offset by /32", () => { + const addr = new Address("192.168.0.0"); + expect(addr.next().toString()).toEqual("192.168.0.1"); +}); + +test("sanity check IPv4 negative offset by /32", () => { + const addr = new Address("192.168.0.0"); + expect(addr.previous().toString()).toEqual("192.167.255.255"); +}); + +test("sanity check IPv4 offset by /24 with overflow", () => { + const addr = new Network("192.168.255.0/24"); + expect(addr.next().toString()).toEqual("192.169.0.0/24"); +}); + +test("sanity check IPv4 offset by /25 with overflow", () => { + const addr = new Address("192.168.0.248"); + expect(addr.increase(25).toString()).toEqual("192.168.1.120"); +}); + +test("sanity check IPv4 recursion", () => { + const addr = new Address("254.255.255.255", true); + expect(addr.increase(24, true).toString()).toEqual("255.0.0.255"); +}); + +test("sanity check IPv4 address space overflow error", () => { + const addr = new Address("255.255.255.255", true); + expect(addr.next().toString()).toEqual(""); +}); + +test("sanity check IPv4 applySubnetMask()", () => { + const addr = new Address("192.168.0.248", true); + expect(addr.applySubnetMask(16).toString()).toEqual("192.168.0.0"); +}); + +test("sanity check IPv4 intersects() #1", () => { + const alpha = new Network("192.168.0.0/22"); + const bravo = new Network("192.168.1.0/24"); + expect(alpha.intersects(bravo)).toEqual(true); +}); + +test("sanity check IPv4 intersects() #2", () => { + const alpha = new Network("192.168.0.0/24"); + const bravo = new Network("192.168.1.0/24"); + expect(alpha.intersects(bravo)).toEqual(false); +}); + +test("sanity check IPv4 intersects() #3", () => { + const alpha = new Network("192.168.1.0/24"); + const bravo = new Network("192.168.0.0/24"); + expect(alpha.intersects(bravo)).toEqual(false); +}); + +test("sanity check ipv4.randomAddress", () => { + const output = ipv4.randomAddress(); + expect(index.ip(output)).toBeTruthy(); +}); + +test("sanity check ipv4.randomNetwork", () => { + expect(ipv4.randomNetwork().toString()).toBeTruthy(); +}); diff --git a/src/tests/ipv4.test.ts.bckp b/src/tests/ipv4.test.ts.bckp deleted file mode 100644 index eca0fdb..0000000 --- a/src/tests/ipv4.test.ts.bckp +++ /dev/null @@ -1,154 +0,0 @@ -import * as index from "../index"; -import * as shared from "../shared"; -import * as ipv4 from "../ipv4"; -import * as errors from "../errors"; - -test("sanity check IPv4 offset by /32", () => { - const input = "192.168.0.0"; - const bytes = ipv4.addrToBytes(input, true); - shared.increaseAddressWithCIDR(bytes, 32, true); - const output = ipv4.bytesToAddr(bytes, true); - const expected = "192.168.0.1"; - if (output !== expected) { - throw new Error(`'${output}' !== '${expected}'`); - } -}); - -test("sanity check IPv4 negative offset by /32", () => { - const input = "192.168.0.0"; - const bytes = ipv4.addrToBytes(input, true); - shared.decreaseAddressWithCIDR(bytes, 32, true); - const output = ipv4.bytesToAddr(bytes, true); - const expected = "192.167.255.255"; - if (output !== expected) { - throw new Error(`'${output}' !== '${expected}'`); - } -}); - -test("sanity check IPv4 offset by /24 with overflow", () => { - const input = "192.168.255.0"; - const bytes = ipv4.addrToBytes(input, true); - shared.increaseAddressWithCIDR(bytes, 24, true); - const output = ipv4.bytesToAddr(bytes, true); - const expected = "192.169.0.0"; - if (output !== expected) { - throw new Error(`'${output}' !== '${expected}'`); - } -}); - -test("sanity check IPv4 offset by /25 with overflow", () => { - const input = "192.168.0.248"; - const bytes = ipv4.addrToBytes(input, true); - shared.increaseAddressWithCIDR(bytes, 25, true); - const output = ipv4.bytesToAddr(bytes, true); - const expected = "192.168.1.120"; - if (output !== expected) { - throw new Error(`'${output}' !== '${expected}'`); - } -}); - -test("sanity check IPv4 recursion", () => { - const input = "254.255.255.255"; - const bytes = ipv4.addrToBytes(input, true); - shared.increaseAddressWithCIDR(bytes, 24, true); - const output = ipv4.bytesToAddr(bytes, true); - const expected = "255.0.0.255"; - if (output !== expected) { - throw new Error(`'${output}' !== '${expected}'`); - } -}); - -test("throw IPv4 address space overflow error", () => { - const input = "255.255.255.255"; - const bytes = ipv4.addrToBytes(input, true); - let err: Error | undefined; - try { - shared.increaseAddressWithCIDR(bytes, 32, true); - } catch (e) { - err = e; - } - if (err.message !== errors.OverflowedAddressSpace.message) { - throw new Error(`unexpected: ${err}`); - } -}); - -test("sanity check IPv4 applySubnetMask()", () => { - const input = "192.168.0.248"; - const bytes = ipv4.addrToBytes(input, true); - shared.applySubnetMask(bytes, 16); - const output = ipv4.bytesToAddr(bytes, true); - const expected = "192.168.0.0"; - if (output !== expected) { - throw new Error(`'${output}' !== '${expected}'`); - } -}); - -test("sanity check IPv4 networksIntersect() #1", () => { - const alpha = shared.parseNetworkString("192.168.0.0/22"); - const bravo = shared.parseNetworkString("192.168.1.0/24"); - const output = shared.networksIntersect(alpha, bravo); - expect(output).toEqual(true); -}); - -test("sanity check IPv4 networksIntersect() #2", () => { - const alpha = shared.parseNetworkString("192.168.0.0/24"); - const bravo = shared.parseNetworkString("192.168.1.0/24"); - const output = shared.networksIntersect(alpha, bravo); - expect(output).toEqual(false); -}); - -test("sanity check IPv4 networksIntersect() #3", () => { - const alpha = shared.parseNetworkString("192.168.1.0/24"); - const bravo = shared.parseNetworkString("192.168.0.0/24"); - const output = shared.networksIntersect(alpha, bravo); - expect(output).toEqual(false); -}); - -test("addrToBytes throws addrInvalidInteger", () => { - try { - ipv4.addrToBytes("-1.1.1.1", true); - } catch (e) { - expect(e.message).toEqual(errors.AddrInvalidInteger.message); - } -}); - -test("addrToBytes does not throw addrInvalidInteger", () => { - const output = ipv4.addrToBytes("-1.1.1.1", false); - expect(output).toEqual(null); -}); - -test("addrToBytes throws addrNotFourElements", () => { - try { - ipv4.addrToBytes("1.2.3.4.5", true); - } catch (e) { - expect(e.message).toEqual(errors.AddrNotFourElements.message); - } -}); - -test("addrToBytes does not throw addrNotFourElements", () => { - const output = ipv4.addrToBytes("1.2.3.4.5", false); - expect(output).toEqual(null); -}); - -test("bytesToAddr throws bytesNotFourElements", () => { - try { - ipv4.bytesToAddr(new Uint8Array(3), true); - } catch (e) { - expect(e.message).toEqual(errors.BytesNotFourElements.message); - } -}); - -test("bytesToAddr returns null", () => { - const output = ipv4.bytesToAddr(new Uint8Array(3), false); - expect(output).toEqual(null); -}); - -test("sanity check ipv4.randomAddress", () => { - const output = ipv4.randomAddress(); - expect(index.ip(output)).toBeTruthy(); -}); - -test("sanity check ipv4.randomNetwork", () => { - const output = ipv4.randomNetwork(); - expect(index.network(output)).toBeTruthy(); -}); diff --git a/src/tests/ipv6.test.ts b/src/tests/ipv6.test.ts new file mode 100644 index 0000000..10453ce --- /dev/null +++ b/src/tests/ipv6.test.ts @@ -0,0 +1,32 @@ +import * as ipv6 from "../ipv6"; +import { Address } from "../address"; + +test("sanity check IPv6 offset by /128", () => { + const addr = new Address("2001:db8:122:344::"); + expect(addr.next().toString()).toEqual("2001:db8:122:344::1"); +}); + +test("sanity check IPv6 negative offset by /128", () => { + const addr = new Address("2001:db8:122:344::"); + expect(addr.previous().toString()).toEqual("2001:db8:122:343:ffff:ffff:ffff:ffff"); +}); + +test("sanity check IPv6 applySubnetMask()", () => { + const addr = new Address("b011:a2c2:7328:cc01:4ee7:e2ec:6269:babf"); + expect(addr.applySubnetMask(64).toString()).toEqual("b011:a2c2:7328:cc01::"); +}); + +test("sanity check embedded IPv4", () => { + const addr = new Address("2001:db8:122:344::192.0.2.33", true); + expect(addr.toString()).toEqual("2001:db8:122:344::c000:221"); +}); + +test("sanity check ipv6.randomAddress", () => { + const addr = ipv6.randomAddress(); + expect(addr.toString()).not.toEqual(""); +}); + +test("sanity check ipv6.randomNetwork", () => { + const addr = ipv6.randomNetwork(); + expect(addr.toString()).not.toEqual(""); +}); diff --git a/src/tests/ipv6.test.ts.bckp b/src/tests/ipv6.test.ts.bckp deleted file mode 100644 index 4c6a9e4..0000000 --- a/src/tests/ipv6.test.ts.bckp +++ /dev/null @@ -1,56 +0,0 @@ -import * as index from "../index"; -import * as shared from "../shared"; -import * as ipv6 from "../ipv6"; - -test("sanity check IPv6 offset by /128", () => { - const input = "2001:db8:122:344::"; - const bytes = ipv6.addrToBytes(input, true); - shared.increaseAddressWithCIDR(bytes, 128, true); - const output = ipv6.bytesToAddr(bytes, true); - const expected = "2001:db8:122:344::1"; - if (output !== expected) { - throw new Error(`'${output}' !== '${expected}'`); - } -}); - -test("sanity check IPv6 negative offset by /128", () => { - const input = "2001:db8:122:344::"; - const bytes = ipv6.addrToBytes(input, true); - shared.decreaseAddressWithCIDR(bytes, 128, true); - const output = ipv6.bytesToAddr(bytes, true); - const expected = "2001:db8:122:343:ffff:ffff:ffff:ffff"; - if (output !== expected) { - throw new Error(`'${output}' !== '${expected}'`); - } -}); - -test("sanity check IPv6 applySubnetMask()", () => { - const input = "b011:a2c2:7328:cc01:4ee7:e2ec:6269:babf"; - const bytes = ipv6.addrToBytes(input, true); - shared.applySubnetMask(bytes, 64); - const output = ipv6.bytesToAddr(bytes, true); - const expected = "b011:a2c2:7328:cc01::"; - if (output !== expected) { - throw new Error(`'${output}' !== '${expected}'`); - } -}); - -test("sanity check convertedEmbeddedIPv4", () => { - const output = ipv6.convertEmbeddedIPv4("2001:db8:122:344::192.0.2.33"); - expect(output).toEqual("2001:db8:122:344::c00:221"); -}); - -test("sanity check ipv6.bytesToAddr", () => { - const output = ipv6.bytesToAddr(new Uint8Array(3)); - expect(output).toEqual(null); -}); - -test("sanity check ipv6.randomAddress", () => { - const output = ipv6.randomAddress(); - expect(index.ip(output)).toBeTruthy(); -}); - -test("sanity check ipv6.randomNetwork", () => { - const output = ipv6.randomNetwork(); - expect(index.network(output)).toBeTruthy(); -}); diff --git a/src/tests/parse.test.ts b/src/tests/parse.test.ts deleted file mode 100644 index 675eb12e..0000000 --- a/src/tests/parse.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as parse from "../parse"; - -test("sanity check v6AddrToBytes #1", () => { - const output = parse.v6AddrToBytes("ffff:fc00::1:1234"); - expect(output).not.toEqual([]); -}); From 3dc55eb150d2b3939f80a2fd226fbf6d09ba05bc Mon Sep 17 00:00:00 2001 From: Alex Demskie Date: Sun, 14 Jul 2019 21:37:52 -0700 Subject: [PATCH 8/8] create 'add' method for Matcher and update readme --- README.md | 104 ++++++++++++++++++++++++---------- src/IPv4.ts | 1 - src/IPv6.ts | 1 - src/address.ts | 1 - src/benchmarks/formatters.ts | 3 + src/benchmarks/index.bench.ts | 17 +++--- src/benchmarks/match.bench.ts | 58 ++++--------------- src/benchmarks/sort.bench.ts | 25 ++++---- src/index.ts | 8 +-- src/match.ts | 9 ++- src/network.ts | 1 - src/shared.ts | 1 - src/tests/match.test.ts | 22 +++++++ 13 files changed, 143 insertions(+), 108 deletions(-) create mode 100644 src/benchmarks/formatters.ts diff --git a/README.md b/README.md index a4035ce..ff71be0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # netparser -#### Parse and manipulate IPv4 and IPv6 network addresses. +## Parse and manipulate IPv4 and IPv6 network addresses. [![Build Status](https://travis-ci.org/demskie/netparser.svg?branch=master)](https://travis-ci.org/demskie/netparser) [![Coverage Status](https://coveralls.io/repos/github/demskie/netparser/badge.svg?branch=master)](https://coveralls.io/github/demskie/netparser?branch=master) [![Dependency Status](https://david-dm.org/demskie/netparser/status.svg)](https://david-dm.org/demskie/netparser#info=dependencies&view=table) @@ -54,22 +54,66 @@ netparser.sort(['255.255.255.255', '192.168.0.0/16', '192.168.2.3/31']); netparser.summarize(['192.168.1.1', '192.168.0.0/16', '192.168.2.3/31']); // returns ['192.168.0.0/16'] + +let matcher = new netparser.Matcher(); +matcher.add("192.168.0.0/24"); +matcher.add("192.168.2.0/23"); +matcher.add("192.168.4.0/24"); +matcher.has("192.168.3.0"); // returns true ``` ## FYI -- For simplicity, the above functions will only ever return `String, String[], boolean, or null`. +- For simplicity, all functions will only ever return `String, String[], boolean, or null`. - By default the library will fail silently and `null` is returned when errors are encountered. To override this setting set the optional `throwErrors` parameter to `True`. - By default the library will conveniently mask out provided `network` values to their base address when such an operation makes sense. To override this setting set the optional `strict` parameter to `True` where applicable. +## Benchmarks + +```bash +npm run bench + +'index.bench.ts' output: + baseAddress (netparser) x 1,543,101 ops/sec ±3.49% (80 runs sampled) + baseAddress (ip-address) x 1,200,247 ops/sec ±1.29% (89 runs sampled) + baseAddress (ipaddr.js) x 436,374 ops/sec ±2.02% (85 runs sampled) + baseAddress (netmask) x 352,156 ops/sec ±0.97% (90 runs sampled) + contains (netparser) x 822,829 ops/sec ±1.03% (89 runs sampled) + contains (ip-address) x 832,419 ops/sec ±1.35% (88 runs sampled) + contains (ipaddr.js) x 74,829 ops/sec ±1.71% (86 runs sampled) + contains (netmask) x 307,760 ops/sec ±1.14% (89 runs sampled) + +'match.bench.ts' output: + create (netparser) x 16.99 ops/sec ±5.11% (36 runs sampled) + create (cidr-matcher) x 9.18 ops/sec ±2.99% (27 runs sampled) + create (ipaddr.js) x 25.16 ops/sec ±6.47% (45 runs sampled) + query (netparser) x 239,542 ops/sec ±6.67% (86 runs sampled) + query (cidr-matcher) x 1,123 ops/sec ±1.36% (84 runs sampled) + query (ipaddr.js) x 15.81 ops/sec ±1.37% (43 runs sampled) + +'sort.bench.ts' output: + sort n=100 (native) x 16,666 ops/sec ±2.02% (88 runs sampled) + sort n=100 (insertion) x 42,144 ops/sec ±1.23% (85 runs sampled) + sort n=100 (radix msd) x 360 ops/sec ±1.15% (83 runs sampled) + sort n=1000 (native) x 1,273 ops/sec ±1.31% (80 runs sampled) + sort n=1000 (insertion) x 2,243 ops/sec ±0.83% (88 runs sampled) + sort n=1000 (radix msd) x 36.37 ops/sec ±1.21% (50 runs sampled) + sort n=10000 (native) x 91.33 ops/sec ±1.46% (67 runs sampled) + sort n=10000 (insertion) x 73.01 ops/sec ±1.58% (74 runs sampled) + sort n=10000 (radix msd) x 3.71 ops/sec ±1.48% (14 runs sampled) + sort n=100000 (native) x 4.49 ops/sec ±5.69% (16 runs sampled) + sort n=100000 (insertion) x 0.39 ops/sec ±0.39% (5 runs sampled) + sort n=100000 (radix msd) x 0.36 ops/sec ±4.99% (5 runs sampled) +``` + ## API Docs generated using [`docts`](https://github.com/charto/docts) > > > ### Function [`baseAddress`](#api-baseAddress) > BaseAddress returns the base address for a given subnet address -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L15-L30) -> > **baseAddress( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L15-L30) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L22-L27) +> > **baseAddress( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L22-L27) > >  ▪ networkAddress string - A network address like 192.168.0.4/24 > >  ▫ throwErrors? undefined | true | false - Stop the library from failing silently > @@ -77,16 +121,16 @@ Docs generated using [`docts`](https://github.com/charto/docts) > ### Function [`broadcastAddress`](#api-broadcastAddress) > BroadcastAddress returns the broadcast address for an IPv4 address. > Please note that IPv6 does not have broadcast addresses. -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L44-L52) -> > **broadcastAddress( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L44-L52) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L41-L46) +> > **broadcastAddress( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L41-L46) > >  ▪ network string - A network like 192.168.0.0/24 > >  ▫ throwErrors? undefined | true | false - Stop the library from failing silently > > > ### Function [`findUnusedSubnets`](#api-findUnusedSubnets) > FindUnusedSubnets returns array of unused subnets given the aggregate and sibling subnets -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L67-L100) -> > **findUnusedSubnets( )** null | string[] [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L67-L100) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L61-L88) +> > **findUnusedSubnets( )** null | string[] [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L61-L88) > >  ▪ aggregate string - An aggregate network like 192.168.0.0/24 > >  ▪ subnets string[] - Array of subnetworks like ["192.168.0.0/24", "192.168.0.128/26"] > >  ▫ strict? undefined | true | false - Do not automatically mask addresses to baseAddresses @@ -95,16 +139,16 @@ Docs generated using [`docts`](https://github.com/charto/docts) > > ### Function [`ip`](#api-ip) > Parse an IP address -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L113-L126) -> > **ip( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L113-L126) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L101-L104) +> > **ip( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L101-L104) > >  ▪ address string - Either an address like 192.168.0.0 or subnet 192.168.0.0/24 > >  ▫ throwErrors? undefined | true | false - Stop the library from failing silently > > > ### Function [`network`](#api-network) > Parse a network address -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L139-L154) -> > **network( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L139-L154) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L117-L120) +> > **network( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L117-L120) > >  ▪ networkAddress string - A network like 192.168.0.0/24 > >  ▫ throwErrors? undefined | true | false - Stop the library from failing silently > @@ -112,8 +156,8 @@ Docs generated using [`docts`](https://github.com/charto/docts) > ### Function [`networkComesBefore`](#api-networkComesBefore) > NetworkComesBefore returns a bool with regards to numerical network order. > Please note that IPv4 comes before IPv6 and larger networks come before smaller ones. -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L170-L183) -> > **networkComesBefore( )** null | true | false [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L170-L183) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L136-L149) +> > **networkComesBefore( )** null | true | false [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L136-L149) > >  ▪ network string - A network like 192.168.0.0/24 > >  ▪ otherNetwork string - A network like 192.168.1.0/24 > >  ▫ strict? undefined | true | false - Do not automatically mask addresses to baseAddresses @@ -122,8 +166,8 @@ Docs generated using [`docts`](https://github.com/charto/docts) > > ### Function [`networkContainsAddress`](#api-networkContainsAddress) > NetworkContainsAddress validates that the address is inside the network -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L198-L204) -> > **networkContainsAddress( )** null | true | false [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L198-L204) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L164-L174) +> > **networkContainsAddress( )** null | true | false [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L164-L174) > >  ▪ network string - A network like 192.168.0.0/24 > >  ▪ address string - A network like 192.168.0.100 > >  ▫ strict? undefined | true | false - Do not automatically mask addresses to baseAddresses @@ -132,8 +176,8 @@ Docs generated using [`docts`](https://github.com/charto/docts) > > ### Function [`networkContainsSubnet`](#api-networkContainsSubnet) > NetworkContainsSubnet validates that the network is a valid supernet -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L219-L225) -> > **networkContainsSubnet( )** null | true | false [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L219-L225) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L189-L195) +> > **networkContainsSubnet( )** null | true | false [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L189-L195) > >  ▪ network string - A network like 192.168.0.0/16 > >  ▪ subnet string - A network like 192.168.0.0/24 > >  ▫ strict? undefined | true | false - Do not automatically mask addresses to baseAddresses @@ -142,8 +186,8 @@ Docs generated using [`docts`](https://github.com/charto/docts) > > ### Function [`networksIntersect`](#api-networksIntersect) > NetworksIntersect returns a bool showing if the networks overlap -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L240-L246) -> > **networksIntersect( )** null | true | false [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L240-L246) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L210-L216) +> > **networksIntersect( )** null | true | false [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L210-L216) > >  ▪ network string - A network like 192.168.0.0/23 > >  ▪ otherNetwork string - A network like 192.168.1.0/24 > >  ▫ strict? undefined | true | false - Do not automatically mask addresses to baseAddresses @@ -152,16 +196,16 @@ Docs generated using [`docts`](https://github.com/charto/docts) > > ### Function [`nextAddress`](#api-nextAddress) > NextAddress returns the next address -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L259-L266) -> > **nextAddress( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L259-L266) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L229-L237) +> > **nextAddress( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L229-L237) > >  ▪ address string - An address like 192.168.0.0 > >  ▫ throwErrors? undefined | true | false - Stop the library from failing silently > > > ### Function [`nextNetwork`](#api-nextNetwork) > NextNetwork returns the next network of the same size. -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L280-L287) -> > **nextNetwork( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L280-L287) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L251-L259) +> > **nextNetwork( )** null | string [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L251-L259) > >  ▪ network string - A network like 192.168.0.0/24 > >  ▫ strict? undefined | true | false - Do not automatically mask addresses to baseAddresses > >  ▫ throwErrors? undefined | true | false - Stop the library from failing silently @@ -169,8 +213,8 @@ Docs generated using [`docts`](https://github.com/charto/docts) > > ### Function [`rangeOfNetworks`](#api-rangeOfNetworks) > RangeOfNetworks returns an array of networks given a range of addresses -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L301-L328) -> > **rangeOfNetworks( )** null | string[] [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L301-L328) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L273-L300) +> > **rangeOfNetworks( )** null | string[] [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L273-L300) > >  ▪ startAddress string - An address like 192.168.1.2 > >  ▪ stopAddress string - An address like 192.168.1.5 > >  ▫ throwErrors? undefined | true | false - Stop the library from failing silently @@ -178,16 +222,16 @@ Docs generated using [`docts`](https://github.com/charto/docts) > > ### Function [`sort`](#api-sort) > Sort returns an array of sorted networks -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L341-L368) -> > **sort( )** null | string[] [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L341-L368) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L313-L341) +> > **sort( )** null | string[] [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L313-L341) > >  ▪ networkAddresses string[] - An array of addresses or subnets > >  ▫ throwErrors? undefined | true | false - Stop the library from failing silently > > > ### Function [`summarize`](#api-summarize) > Summarize returns an array of aggregates given a list of networks -> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L382-L407) -> > **summarize( )** null | string[] [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L382-L407) +> Source code: [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L355-L375) +> > **summarize( )** null | string[] [`<>`](http://github.com/demskie/netparser/blob/master/src/index.ts#L355-L375) > >  ▪ networks string[] - An array of addresses or subnets > >  ▫ strict? undefined | true | false - Do not automatically mask addresses to baseAddresses > >  ▫ throwErrors? undefined | true | false - Stop the library from failing silently diff --git a/src/IPv4.ts b/src/IPv4.ts index 13698ff..6726aa8 100644 --- a/src/IPv4.ts +++ b/src/IPv4.ts @@ -1,6 +1,5 @@ import * as errors from "./errors"; import * as weight from "./weight"; - import { Network } from "./network"; import { Address } from "./address"; diff --git a/src/IPv6.ts b/src/IPv6.ts index 0d638fe..50bf74a 100644 --- a/src/IPv6.ts +++ b/src/IPv6.ts @@ -1,6 +1,5 @@ import * as errors from "./errors"; import * as weight from "./weight"; - import { Network } from "./network"; import { Address } from "./address"; diff --git a/src/address.ts b/src/address.ts index 02d688f..c881cfe 100644 --- a/src/address.ts +++ b/src/address.ts @@ -3,7 +3,6 @@ import * as ipv6 from "./ipv6"; import * as parse from "./parse"; import * as errors from "./errors"; import * as ranges from "./ranges"; - import { Network } from "./network"; const BEFORE = -1; diff --git a/src/benchmarks/formatters.ts b/src/benchmarks/formatters.ts new file mode 100644 index 0000000..fde21b7 --- /dev/null +++ b/src/benchmarks/formatters.ts @@ -0,0 +1,3 @@ +export function fixedWidth(s: string, n: number) { + return "\t" + s + " ".repeat(Math.max(0, n - s.length)); +} diff --git a/src/benchmarks/index.bench.ts b/src/benchmarks/index.bench.ts index 0efdb0c..734e9ad 100644 --- a/src/benchmarks/index.bench.ts +++ b/src/benchmarks/index.bench.ts @@ -3,39 +3,40 @@ import * as netparser from "../index"; import { Netmask } from "netmask"; import * as ipaddr from "ipaddr.js"; import * as ipaddress from "ip-address"; +import { fixedWidth } from "./formatters"; new Benchmark.Suite("index.bench.ts") - .add("\tbaseAddress (netparser)", () => { + .add(fixedWidth("baseAddress (netparser)", 30), () => { netparser.baseAddress("192.168.0.4/24"); }) - .add("\tbaseAddress (ip-address)", () => { + .add(fixedWidth("baseAddress (ip-address)", 30), () => { new ipaddress.Address4("192.168.0.4/24").startAddress; }) - .add("\tbaseAddress (ipaddr.js)", () => { + .add(fixedWidth("baseAddress (ipaddr.js)", 30), () => { ipaddr.IPv4.networkAddressFromCIDR("192.168.0.4/24").toString(); }) - .add("\tbaseAddress (netmask)", () => { + .add(fixedWidth("baseAddress (netmask)", 30), () => { new Netmask("192.168.0.4/24").base; }) - .add("\tnetworkContainsAddress (netparser)", () => { + .add(fixedWidth("contains (netparser)", 30), () => { netparser.networkContainsAddress("192.168.0.0/24", "192.168.0.4"); }) - .add("\tnetworkContainsAddress (ip-address)", () => { + .add(fixedWidth("contains (ip-address)", 30), () => { new ipaddress.Address4("192.168.0.0/24").isInSubnet(new ipaddress.Address4("192.168.0.4")); }) - .add("\tnetworkContainsAddress (ipaddr.js)", () => { + .add(fixedWidth("contains (ipaddr.js)", 30), () => { const addr = ipaddr.parse("192.168.0.4") as ipaddr.IPv4; addr.match(ipaddr.parseCIDR("192.168.0.0/24") as [ipaddr.IPv4, number]); }) - .add("\tnetworkContainsAddress (netmask)", () => { + .add(fixedWidth("contains (netmask)", 30), () => { new Netmask("192.168.0.0/24").contains("192.168.0.4"); }) diff --git a/src/benchmarks/match.bench.ts b/src/benchmarks/match.bench.ts index 638ad48..ef96503 100644 --- a/src/benchmarks/match.bench.ts +++ b/src/benchmarks/match.bench.ts @@ -1,80 +1,46 @@ import * as Benchmark from "benchmark"; import * as match from "../match"; -import * as ipv4 from "../ipv4"; import * as ipv6 from "../ipv6"; import * as ipaddr from "ipaddr.js"; import { default as cidrMatcher } from "cidr-matcher"; - -const ipv4Addresses = Array.from(Array(1000), () => ipv4.randomAddress().toString()); -const ipv4Subnets = Array.from(Array(10000), () => ipv4.randomNetwork().toString()); - -let netparserMatcherIPv4 = new match.Matcher(); -let ipaddrRangeListIPv4 = { subnetRanges: [] }; -let cidrMatcherIPv4 = new cidrMatcher(ipv4Subnets); +import { fixedWidth } from "./formatters"; const ipv6Addresses = Array.from(Array(1000), () => ipv6.randomAddress().toString()); const ipv6Subnets = Array.from(Array(10000), () => ipv6.randomNetwork().toString()); let netparserMatcherIPv6 = new match.Matcher(); let ipaddrRangeListIPv6 = { subnetRanges: [] }; -let cidrMatcherIPv6 = new cidrMatcher(ipv4Subnets); +let cidrMatcherIPv6 = new cidrMatcher(ipv6Subnets); new Benchmark.Suite("match.bench.ts") - .add("\tIPv4 matcher creation (netparser)", () => { - netparserMatcherIPv4 = new match.Matcher(ipv4Subnets); - }) - - .add("\tIPv4 matcher creation (ipaddr.js)", () => { - for (var subnet of ipv4Subnets) { - ipaddrRangeListIPv4.subnetRanges.push(ipaddr.parseCIDR(subnet)); - } - }) - - .add("\tIPv4 matcher creation (cidr-matcher)", () => { - cidrMatcherIPv4 = new cidrMatcher(ipv4Subnets); - }) - - .add("\tIPv4 matcher query (netparser)", () => { - netparserMatcherIPv4.has(ipv4Addresses[0]); - }) - - .add("\tIPv4 matcher query (ipaddr.js)", () => { - let parsedAddr = ipaddr.parse(ipv4Addresses[0]) as ipaddr.IPv4; - ipaddr.subnetMatch(parsedAddr, ipaddrRangeListIPv4, "unknown"); - }) - - .add("\tIPv4 matcher query (cidr-matcher)", () => { - cidrMatcherIPv4.contains(ipv4Addresses[0]); + .add(fixedWidth("create (netparser)", 30), () => { + netparserMatcherIPv6 = new match.Matcher(ipv6Subnets); }) - .add("\tIPv6 matcher creation (netparser)", () => { - netparserMatcherIPv6 = new match.Matcher(ipv6Subnets); + .add(fixedWidth("create (cidr-matcher)", 30), () => { + cidrMatcherIPv6 = new cidrMatcher(ipv6Subnets); }) - .add("\tIPv6 matcher creation (ipaddr.js)", () => { + .add(fixedWidth("create (ipaddr.js)", 30), () => { for (var subnet of ipv6Subnets) { ipaddrRangeListIPv6.subnetRanges.push(ipaddr.parseCIDR(subnet)); } }) - .add("\tIPv6 matcher creation (cidr-matcher)", () => { - cidrMatcherIPv6 = new cidrMatcher(ipv6Subnets); + .add(fixedWidth("query (netparser)", 30), () => { + netparserMatcherIPv6.has(ipv6Addresses[0]); }) - .add("\tIPv6 matcher query (netparser)", () => { - netparserMatcherIPv6.has(ipv6Addresses[0]); + .add(fixedWidth("query (cidr-matcher)", 30), () => { + cidrMatcherIPv6.contains(ipv6Addresses[0]); }) - .add("\tIPv6 matcher query (ipaddr.js)", () => { + .add(fixedWidth("query (ipaddr.js)", 30), () => { const parsedAddr = ipaddr.parse(ipv6Addresses[0]) as ipaddr.IPv6; ipaddr.subnetMatch(parsedAddr, ipaddrRangeListIPv6, "unknown"); }) - .add("\tIPv6 matcher query (cidr-matcher)", () => { - cidrMatcherIPv6.contains(ipv6Addresses[0]); - }) - .on("complete", function() { console.log(`'${this.name}' output:`); this.forEach((bench: Benchmark) => { diff --git a/src/benchmarks/sort.bench.ts b/src/benchmarks/sort.bench.ts index bd02ec6..0286319 100644 --- a/src/benchmarks/sort.bench.ts +++ b/src/benchmarks/sort.bench.ts @@ -2,6 +2,7 @@ import * as Benchmark from "benchmark"; import * as ipv4 from "../ipv6"; import * as sort from "../sort"; import { Network } from "../network"; +import { fixedWidth } from "./formatters"; const e2Alpha = Array.from(Array(1e2), () => ipv4.randomNetwork()); const e2Bravo = Array.from(e2Alpha, (net: Network) => net.duplicate()); @@ -21,51 +22,51 @@ const e5Charlie = Array.from(e5Alpha, (net: Network) => net.duplicate()); new Benchmark.Suite("sort.bench.ts") - .add("\tsort n=100 (native)\t", () => { + .add(fixedWidth("sort n=100 (native)", 30), () => { sort.nativeSort(e2Alpha); }) - .add("\tsort n=100 (insertion)\t", () => { + .add(fixedWidth("sort n=100 (insertion)", 30), () => { sort.insertionSort(e2Bravo); }) - .add("\tsort n=100 (radix msd)\t", () => { + .add(fixedWidth("sort n=100 (radix msd)", 30), () => { sort.radixSort(e2Charlie); }) - .add("\tsort n=1000 (native)\t", () => { + .add(fixedWidth("sort n=1000 (native)", 30), () => { sort.nativeSort(e3Alpha); }) - .add("\tsort n=1000 (insertion)\t", () => { + .add(fixedWidth("sort n=1000 (insertion)", 30), () => { sort.insertionSort(e3Bravo); }) - .add("\tsort n=1000 (radix msd)\t", () => { + .add(fixedWidth("sort n=1000 (radix msd)", 30), () => { sort.radixSort(e3Charlie); }) - .add("\tsort n=10000 (native)\t", () => { + .add(fixedWidth("sort n=10000 (native)", 30), () => { sort.nativeSort(e4Alpha); }) - .add("\tsort n=10000 (insertion)", () => { + .add(fixedWidth("sort n=10000 (insertion)", 30), () => { sort.insertionSort(e4Bravo); }) - .add("\tsort n=10000 (radix msd)", () => { + .add(fixedWidth("sort n=10000 (radix msd)", 30), () => { sort.radixSort(e4Charlie); }) - .add("\tsort n=100000 (native)\t", () => { + .add(fixedWidth("sort n=100000 (native)", 30), () => { sort.nativeSort(e5Alpha); }) - .add("\tsort n=100000 (insertion)", () => { + .add(fixedWidth("sort n=100000 (insertion)", 30), () => { sort.insertionSort(e5Bravo); }) - .add("\tsort n=100000 (radix msd)", () => { + .add(fixedWidth("sort n=100000 (radix msd)", 30), () => { sort.radixSort(e5Charlie); }) diff --git a/src/index.ts b/src/index.ts index 2b5cca7..bd768a4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ import * as shared from "./shared"; import * as errors from "./errors"; - import { Network } from "./network"; import { Address } from "./address"; +import { Matcher } from "./match"; const BEFORE = -1; const EQUALS = 0; @@ -379,6 +379,7 @@ module.exports = { broadcastAddress, findUnusedSubnets, ip, + Matcher, network, networkComesBefore, networkContainsAddress, @@ -390,8 +391,3 @@ module.exports = { sort, summarize }; - -// The following functions are pending an implementation: - -// IPv4ClassfulNetwork() eithers return the classful network given an IPv4 address or -// returns nil if given a multicast address or IPv6 address diff --git a/src/match.ts b/src/match.ts index d36eee3..afd4fb3 100644 --- a/src/match.ts +++ b/src/match.ts @@ -1,6 +1,5 @@ import * as shared from "./shared"; import * as sort from "./sort"; - import { Network } from "./network"; export class Matcher { @@ -27,4 +26,12 @@ export class Matcher { if (idx - 1 >= 0 && this.sorted[idx - 1].contains(net)) return true; return false; } + + public add(network: string) { + var net = shared.parseBaseNetwork(network, false, false); + if (!net || !net.isValid()) return; + var idx = sort.binarySearchForInsertionIndex(net, this.sorted); + if (idx < this.sorted.length && this.sorted[idx].compare(net) === 0) return; + this.sorted.splice(idx, 0, net); + } } diff --git a/src/network.ts b/src/network.ts index afcb2a3..bdda096 100644 --- a/src/network.ts +++ b/src/network.ts @@ -1,6 +1,5 @@ import * as parse from "./parse"; import * as errors from "./errors"; - import { Address } from "./address"; const BEFORE = -1; diff --git a/src/shared.ts b/src/shared.ts index 9c88019..faf6072 100644 --- a/src/shared.ts +++ b/src/shared.ts @@ -1,6 +1,5 @@ import * as sort from "./sort"; import * as errors from "./errors"; - import { Network } from "./network"; import { Address } from "./address"; diff --git a/src/tests/match.test.ts b/src/tests/match.test.ts index 575f6d2..2abf85a 100644 --- a/src/tests/match.test.ts +++ b/src/tests/match.test.ts @@ -171,3 +171,25 @@ test("sanity check Matcher #8", () => { const matcher = new match.Matcher(input); expect(matcher.has("192.168.0.255")).toEqual(true); }); + +test("sanity check Matcher #9", () => { + const input = [ + "192.168.0.24/32", + "192.168.0.52/32", + "192.168.0.171/32", + "192.168.0.222/32", + "192.168.0.124/32", + "192.168.0.123/32", + "192.168.0.234/32", + "192.168.0.254/31", + "192.168.0.0/32", + "192.168.0.3/32", + "192.168.0.170/32", + "192.168.0.125/32" + ]; + const matcher = new match.Matcher(); + input.forEach((s: string) => { + matcher.add(s); + }); + expect(matcher.has("192.168.0.255")).toEqual(true); +});