Files
core/src/utils/ip-utils-fast.js
T

166 lines
4.1 KiB
JavaScript
Raw Normal View History

/**
* Optimized IP Address Utilities
*
* High-performance versions for hot paths
*/
// CIDR cache for repeated lookups
const cidrCache = new Map();
const CIDR_CACHE_SIZE = 1000;
/**
* Fast IPv4 check - less strict but much faster
* Only validates format, not strict numeric ranges
*/
export function isIPv4Fast(ip) {
if (typeof ip !== 'string') return false;
// Quick length check (min: 7 for "0.0.0.0", max: 15 for "255.255.255.255")
if (ip.length < 7 || ip.length > 15) return false;
let dots = 0;
for (let i = 0; i < ip.length; i++) {
const c = ip.charCodeAt(i);
if (c === 46) { // '.'
dots++;
} else if (c < 48 || c > 57) { // not 0-9
return false;
}
}
return dots === 3;
}
/**
* Ultra-fast IP to integer conversion
* Direct character parsing, no string splitting
*/
export function ipToIntFast(ip) {
let result = 0;
let octet = 0;
let shift = 24;
for (let i = 0; i < ip.length; i++) {
const c = ip.charCodeAt(i);
if (c === 46) { // '.'
result |= (octet << shift);
octet = 0;
shift -= 8;
} else {
octet = octet * 10 + (c - 48);
}
}
return (result | octet) >>> 0;
}
/**
* Fast CIDR parsing with caching
*/
export function parseCidrCached(cidr) {
// Check cache first
let cached = cidrCache.get(cidr);
if (cached) return cached;
// Parse and cache
const slashIdx = cidr.indexOf('/');
if (slashIdx === -1) return null;
const ip = cidr.slice(0, slashIdx);
const prefix = parseInt(cidr.slice(slashIdx + 1), 10);
const mask = -1 << (32 - prefix);
cached = {
network: ipToIntFast(ip),
mask,
prefix
};
// Simple LRU - clear if too big
if (cidrCache.size >= CIDR_CACHE_SIZE) {
cidrCache.clear();
}
cidrCache.set(cidr, cached);
return cached;
}
/**
* Ultra-fast IP in CIDR check
* Uses caching and optimized parsing
*/
export function isIpInCidrFast(ip, cidr) {
const cached = parseCidrCached(cidr);
if (!cached) return false;
const ipInt = ipToIntFast(ip);
return (ipInt & cached.mask) === (cached.network & cached.mask);
}
/**
* Fast private IP check using bit manipulation
*/
export function isPrivateIpFast(ip) {
const ipInt = ipToIntFast(ip);
// 10.0.0.0/8: 0x0A000000 to 0x0AFFFFFF
if ((ipInt >>> 24) === 10) return true;
// 172.16.0.0/12: 0xAC100000 to 0xAC1FFFFF
const high16 = ipInt >>> 16;
if (high16 >= 0xAC10 && high16 <= 0xAC1F) return true;
// 192.168.0.0/16: 0xC0A80000 to 0xC0A8FFFF
if (high16 === 0xC0A8) return true;
// 127.0.0.0/8: 0x7F000000 to 0x7FFFFFFF
if ((ipInt >>> 24) === 127) return true;
// 169.254.0.0/16: 0xA9FE0000 to 0xA9FEFFFF
if (high16 === 0xA9FE) return true;
return false;
}
/**
* Optimized built-in function evaluator
* Direct dispatch without object lookups
*/
export function evaluateBuiltInFast(name, args) {
switch (name) {
case 'ip_in_cidr':
return isIpInCidrFast(args[0], args[1]);
case 'ip_is_private':
return isPrivateIpFast(args[0]);
case 'ip_is_loopback':
return (ipToIntFast(args[0]) >>> 24) === 127;
case 'ip_version':
return isIPv4Fast(args[0]) ? 4 : (args[0].includes(':') ? 6 : null);
case 'ip_equals':
return args[0] === args[1];
case 'contains':
return String(args[0]).includes(String(args[1]));
case 'starts_with':
return String(args[0]).startsWith(String(args[1]));
case 'ends_with':
return String(args[0]).endsWith(String(args[1]));
case 'equals':
return args[0] === args[1];
case 'greater_than':
return args[0] > args[1];
case 'less_than':
return args[0] < args[1];
case 'in_range':
return args[0] >= args[1] && args[0] <= args[2];
case 'hour_of_day':
return new Date(args[0]).getHours();
case 'day_of_week':
return new Date(args[0]).getDay();
default:
throw new Error(`Unknown: ${name}`);
}
}
// Re-export original functions for compatibility
export { isIPv4, isIPv6, isLoopbackIp, getIpVersion, normalizeIp } from './ip-utils.js';
export { isIpInCidr, isPrivateIp } from './ip-utils.js';