Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | 827x 2739x 1x 945x 118x 827x 827x 827x 827x 626x 626x 626x 608x 18x 4x 14x 14x 14x 14x 14x 201x 194x 190x 4x 823x 1x 122x 122x 122x 122x 502x 1x 319x 319x 319x 319x 2739x 2739x 1688x 1051x 1030x 182x 848x 21x 13x 2739x 319x 2090x 2090x 4x 4x 28x 28x 28x 25x 4x 4x 319x 2090x 319x 319x 319x 319x 319x 2x 319x 95x 95x 3x 95x 95x 3x 3x 1x 319x 2089x 319x | /**
* Copyright (c) Siemens 2016 - 2025
* SPDX-License-Identifier: MIT
*/
interface Section {
value: string;
current?: boolean;
partNo?: number;
/** Indicate this is a network mask. */
mask?: boolean;
}
export interface Ip4SplitOptions {
type?: 'insert' | 'delete' | 'paste';
input?: string | null;
pos: number;
cidr?: boolean;
}
export interface Ip6SplitOptions {
type?: 'insert' | 'delete' | 'paste';
input?: string | null;
pos: number;
zeroCompression?: boolean;
cidr?: boolean;
}
const isDigit = (c: string): boolean => c >= '0' && c <= '9';
const isHex = (c: string): boolean => (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F');
const recursiveSplitIpV4 = (
options: Ip4SplitOptions,
input: string,
sections: Section[] = [{ value: '', partNo: 0 }],
index: number = 0,
cursorDelta: number = 0
): { sections: Section[]; cursorDelta: number } => {
// Base case: no more input to process
if (input.length === 0) {
return { sections, cursorDelta };
}
const current = sections.at(-1)!;
const char = input[0];
input = input.substring(1);
if (isDigit(char)) {
const part = `${current.value}${char}`;
const limit = current.mask ? 32 : 255;
// Append digits to current part until the part limit exceeds
if (part.length <= 3 && parseInt(part, 10) <= limit) {
current.value = part;
} else {
// Base case: IP completely entered
if ((options.cidr && current.mask) || (!options.cidr && current.partNo === 3)) {
return { sections, cursorDelta };
}
// Force separators since the part exceeded his limit
Iif (current.partNo === 3) {
input = `/${char}${input.replace('.', '').replace('/', '')}`;
} else {
const dotIndex = input.indexOf('.');
Iif (dotIndex >= 0) {
input = input.substring(0, dotIndex) + input.substring(dotIndex + 1);
}
input = `.${char}${input}`;
}
// In case the cursor position is at the the position of the exceeded digit it is necessary
// to move the cursor one position forward since a separator will be added before the digit
Iif (index === options.pos) {
cursorDelta = 1;
}
}
} else if (char === '.' || char === '/') {
// Handle separators
if ('partNo' in current && current.partNo! < 3) {
sections.push({ value: '.' }, { value: '', partNo: current.partNo! + 1 });
} else Iif (options.cidr && !current.mask) {
sections.push({ value: '/' }, { value: '', mask: true });
}
}
return recursiveSplitIpV4(options, input, sections, index + 1, cursorDelta);
};
/**
* Parse IPv4 input string into IPv4 address section array.
*/
export const splitIpV4Sections = (
options: Ip4SplitOptions
): { value: string; cursorDelta: number } => {
const { input } = options;
Iif (!input) {
return { value: '', cursorDelta: 0 };
}
const { sections, cursorDelta } = recursiveSplitIpV4(options, input);
return {
value: sections.map(s => s.value).join(''),
cursorDelta
};
};
export const splitIpV6Sections = (options: Ip6SplitOptions): { value: string } => {
const { type, input, pos, zeroCompression, cidr } = options;
const sections: Section[] = [{ value: '' }];
Iif (!input) {
return { value: '' };
}
for (let i = 0; i < input.length; i++) {
const c = input.charAt(i).toUpperCase();
if (isHex(c)) {
sections.at(-1)!.value += c;
} else if (c === ':') {
if (input.charAt(i - 1) === c) {
// Merge :: characters
sections.at(-2)!.value += c;
} else {
sections.push({ value: c }, { value: '' });
}
} else if (cidr && c === '/') {
sections.push({ value: c }, { value: '', mask: true });
}
Iif (pos === i) {
sections.at(-1)!.current = true;
}
}
// Split values > FFFF in multiple sections:
// - 1FFFF will be split into 1FFF and F
for (let i = 0; i < sections.length; i++) {
const { value, current } = sections[i];
if (value.length > 4) {
const append: Section[] = [];
for (let p = 0; p < value.length; p += 4) {
const part = value.substring(p, p + 4);
append.push({ value: part });
if (part.length === 4) {
append.push({ value: ':' });
}
}
sections.splice(i, 1, ...append);
Iif (current) {
sections[i + append.length - 1].current = true;
}
}
}
// Drop invalid zero compression indicators '::'
const removeEnd = pos === input.length || type === 'paste';
let matches = sections.filter(s => s.value.startsWith('::'));
if (matches) {
matches = removeEnd ? matches : matches.reverse();
if (zeroCompression) {
matches.shift();
}
// Only allow one occurrence of ::
for (const drop of matches) {
drop.value = drop.value.substring(1);
}
}
// Ensure the that the CIDR divider is a slash
if (cidr) {
const startCidr = matches.length > 0 ? 13 : 15;
if (startCidr < sections.length && sections[startCidr].value === ':') {
sections[startCidr].value = '/';
}
const prefixPos = startCidr + 1;
if (prefixPos < sections.length) {
const prefixLength = sections[prefixPos].value;
if (parseInt(prefixLength, 10) > 128) {
sections[prefixPos].value = prefixLength.substring(0, 2);
}
}
}
const value = sections
.splice(0, cidr ? 17 : 15)
.map(s => s.value)
.join('');
return { value };
};
|