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 184 185 186 187 188 189 190 191 | 937x 2785x 1x 1075x 138x 937x 937x 937x 937x 708x 708x 708x 690x 18x 4x 14x 14x 14x 14x 14x 229x 222x 218x 4x 933x 1x 142x 142x 142x 142x 578x 1x 328x 328x 328x 328x 328x 2785x 2785x 1726x 1059x 1037x 184x 853x 22x 14x 2785x 328x 2113x 2113x 5x 5x 5x 30x 30x 30x 26x 26x 30x 5x 5x 328x 2113x 328x 328x 328x 328x 328x 2x 328x 96x 96x 3x 96x 96x 3x 3x 1x 328x 2112x 328x | /**
* Copyright (c) Siemens 2016 - 2026
* 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; cursorDelta: number } => {
const { type, input, pos, zeroCompression, cidr } = options;
const sections: Section[] = [{ value: '' }];
let cursorDelta = 0;
Iif (!input) {
return { value: '', cursorDelta: 0 };
}
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[] = [];
let charsProcessed = 0;
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: ':' });
Iif (current && pos >= charsProcessed) {
cursorDelta++;
}
}
charsProcessed += 4;
}
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, cursorDelta };
};
|