Skip to content

Commit 8659024

Browse files
committed
feat(new tool): Integers to IP
Fix CorentinTh#1187
1 parent 318fb6e commit 8659024

File tree

8 files changed

+170
-25
lines changed

8 files changed

+170
-25
lines changed

components.d.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ declare module '@vue/runtime-core' {
103103
IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
104104
InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
105105
IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
106+
IntegersToIp: typeof import('./src/tools/integers-to-ip/integers-to-ip.vue')['default']
106107
Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
107108
Ipv4RangeExpander: typeof import('./src/tools/ipv4-range-expander/ipv4-range-expander.vue')['default']
108109
Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default']
@@ -129,11 +130,10 @@ declare module '@vue/runtime-core' {
129130
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
130131
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
131132
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
132-
NCode: typeof import('naive-ui')['NCode']
133133
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
134134
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
135+
NDivider: typeof import('naive-ui')['NDivider']
135136
NEllipsis: typeof import('naive-ui')['NEllipsis']
136-
NForm: typeof import('naive-ui')['NForm']
137137
NFormItem: typeof import('naive-ui')['NFormItem']
138138
NH1: typeof import('naive-ui')['NH1']
139139
NH3: typeof import('naive-ui')['NH3']
@@ -142,9 +142,6 @@ declare module '@vue/runtime-core' {
142142
NLayout: typeof import('naive-ui')['NLayout']
143143
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
144144
NMenu: typeof import('naive-ui')['NMenu']
145-
NScrollbar: typeof import('naive-ui')['NScrollbar']
146-
NSlider: typeof import('naive-ui')['NSlider']
147-
NSwitch: typeof import('naive-ui')['NSwitch']
148145
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
149146
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
150147
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"highlight.js": "^11.7.0",
6666
"iarna-toml-esm": "^3.0.5",
6767
"ibantools": "^4.3.3",
68+
"ip-bigint": "^8.2.0",
6869
"js-base64": "^3.7.6",
6970
"json5": "^2.2.3",
7071
"jwt-decode": "^3.1.2",

pnpm-lock.yaml

Lines changed: 15 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter';
22
import { tool as base64StringConverter } from './base64-string-converter';
33
import { tool as basicAuthGenerator } from './basic-auth-generator';
44
import { tool as emailNormalizer } from './email-normalizer';
5+
import { tool as integersToIp } from './integers-to-ip';
56

67
import { tool as asciiTextDrawer } from './ascii-text-drawer';
78

@@ -158,7 +159,15 @@ export const toolsByCategory: ToolCategory[] = [
158159
},
159160
{
160161
name: 'Network',
161-
components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator],
162+
components: [
163+
ipv4SubnetCalculator,
164+
ipv4AddressConverter,
165+
ipv4RangeExpander,
166+
macAddressLookup,
167+
macAddressGenerator,
168+
ipv6UlaGenerator,
169+
integersToIp,
170+
],
162171
},
163172
{
164173
name: 'Math',

src/tools/integer-base-converter/integer-base-converter.model.test.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,21 @@ describe('integer-base-converter', () => {
1111
expect(convertBase({ value: '10100101', fromBase: 2, toBase: 16 })).toEqual('a5');
1212
expect(convertBase({ value: '192654', fromBase: 10, toBase: 8 })).toEqual('570216');
1313
expect(convertBase({ value: 'zz', fromBase: 64, toBase: 10 })).toEqual('2275');
14-
expect(convertBase({ value: '42540766411283223938465490632011909384', fromBase: 10, toBase: 10 })).toEqual('42540766411283223938465490632011909384');
15-
expect(convertBase({ value: '42540766411283223938465490632011909384', fromBase: 10, toBase: 16 })).toEqual('20010db8000085a300000000ac1f8908');
16-
expect(convertBase({ value: '20010db8000085a300000000ac1f8908', fromBase: 16, toBase: 10 })).toEqual('42540766411283223938465490632011909384');
14+
expect(convertBase({ value: 'AA', fromBase: 16, toBase: 10 })).toEqual('170');
15+
expect(convertBase({ value: 'aa', fromBase: 16, toBase: 10 })).toEqual('170');
16+
expect(convertBase({ value: '0xAA', fromBase: -1, toBase: 10 })).toEqual('170');
17+
expect(convertBase({ value: '&HAA', fromBase: -1, toBase: 10 })).toEqual('170');
18+
expect(convertBase({ value: '0xAAUL', fromBase: -1, toBase: 10 })).toEqual('170');
19+
expect(convertBase({ value: '0XAAUL', fromBase: -1, toBase: 10 })).toEqual('170');
20+
expect(convertBase({ value: '10UL', fromBase: 10, toBase: 10 })).toEqual('10');
21+
expect(convertBase({ value: '10n', fromBase: 10, toBase: 10 })).toEqual('10');
22+
expect(convertBase({ value: '0o252', fromBase: -1, toBase: 10 })).toEqual('170');
23+
expect(convertBase({ value: '&O252', fromBase: -1, toBase: 10 })).toEqual('170');
24+
expect(convertBase({ value: '192 654', fromBase: 10, toBase: 8 })).toEqual('570216');
25+
expect(convertBase({ value: '192.654', fromBase: 10, toBase: 8 })).toEqual('570216');
26+
expect(convertBase({ value: '0b10101010', fromBase: -1, toBase: 10 })).toEqual('170');
27+
expect(convertBase({ value: '0b_1010_1010', fromBase: -1, toBase: 10 })).toEqual('170');
28+
expect(convertBase({ value: '192,654', fromBase: 10, toBase: 8 })).toEqual('570216');
1729
});
1830
});
1931
});
Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,61 @@
1-
export function convertBase({ value, fromBase, toBase }: { value: string; fromBase: number; toBase: number }) {
1+
export function hasNumberPrefix(value: string) {
2+
return (value ?? '').trim().match(/^(0[xob].|&[hob].)/i);
3+
}
4+
5+
export function convertBase(
6+
{
7+
value, fromBase, toBase,
8+
ignorePunctuationsRegexChars = ' \u00A0_\.,-',
9+
handlePrefixSuffix = true,
10+
ignoreCase = true,
11+
}: {
12+
value: string
13+
fromBase: number
14+
toBase: number
15+
ignorePunctuationsRegexChars?: string
16+
handlePrefixSuffix?: boolean
17+
ignoreCase?: boolean
18+
}) {
19+
let cleanedValue = (value ?? '0').trim();
20+
if (ignorePunctuationsRegexChars) {
21+
cleanedValue = cleanedValue.replace(new RegExp(`[${ignorePunctuationsRegexChars}]`, 'g'), '');
22+
}
23+
let finalFromBase = fromBase;
24+
if (handlePrefixSuffix) {
25+
for (const regBase of [
26+
{ base: 2, regex: /^(&b|0b)?([01]+)([IULZn]*)$/i },
27+
{ base: 8, regex: /^(&o|0o)?([0-7]+)([IULZn]*)$/i },
28+
{ base: 16, regex: /^(&h|0x)?([a-f0-9]+)([IULZn]*)$/i },
29+
]) {
30+
const match = cleanedValue.match(regBase.regex);
31+
if (match) {
32+
if (match[1]) {
33+
finalFromBase = regBase.base;
34+
}
35+
cleanedValue = match[2];
36+
break;
37+
}
38+
}
39+
}
40+
if (ignoreCase && finalFromBase <= 36) {
41+
cleanedValue = cleanedValue.toLowerCase();
42+
}
243
const range = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/'.split('');
3-
const fromRange = range.slice(0, fromBase);
44+
const fromRange = range.slice(0, finalFromBase);
445
const toRange = range.slice(0, toBase);
5-
let decValue = value
46+
let decValue = cleanedValue
647
.split('')
748
.reverse()
8-
.reduce((carry: bigint, digit: string, index: number) => {
49+
.reduce((carry: number, digit: string, index: number) => {
950
if (!fromRange.includes(digit)) {
10-
throw new Error(`Invalid digit "${digit}" for base ${fromBase}.`);
51+
throw new Error(`Invalid digit "${digit}" for base ${finalFromBase}.`);
1152
}
12-
return (carry += BigInt(fromRange.indexOf(digit)) * BigInt(fromBase) ** BigInt(index));
13-
}, 0n);
53+
return (carry += fromRange.indexOf(digit) * finalFromBase ** index);
54+
}, 0);
1455
let newValue = '';
1556
while (decValue > 0) {
16-
newValue = toRange[Number(decValue % BigInt(toBase))] + newValue;
17-
decValue = (decValue - (decValue % BigInt(toBase))) / BigInt(toBase);
57+
newValue = toRange[decValue % toBase] + newValue;
58+
decValue = (decValue - (decValue % toBase)) / toBase;
1859
}
1960
return newValue || '0';
2061
}

src/tools/integers-to-ip/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Binary } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Integers to IPv4/IPv6',
6+
path: '/integers-to-ip',
7+
description: 'Convert integers to IP',
8+
keywords: ['integers', 'ip', 'ipv4', 'ipv6'],
9+
component: () => import('./integers-to-ip.vue'),
10+
icon: Binary,
11+
createdAt: new Date('2024-07-14'),
12+
});
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<script setup lang="ts">
2+
import { stringifyIp } from 'ip-bigint';
3+
import InputCopyable from '../../components/InputCopyable.vue';
4+
import { convertBase, hasNumberPrefix } from '../integer-base-converter/integer-base-converter.model';
5+
6+
const input = ref('3232235777');
7+
const inputBase = ref(10);
8+
9+
const hasInputNumberPrefix = computed(() => hasNumberPrefix(input.value));
10+
11+
function convertToIP({ value, fromBase, version }: { value: string; fromBase: number; version: 6 | 4 }): string {
12+
try {
13+
return stringifyIp({
14+
number: BigInt(convertBase({
15+
value,
16+
fromBase,
17+
toBase: 10,
18+
})),
19+
version,
20+
}) ?? 'Invalid IP';
21+
}
22+
catch (err) {
23+
return err?.toString() ?? 'Invalid IP';
24+
}
25+
}
26+
</script>
27+
28+
<template>
29+
<div>
30+
<c-card>
31+
<c-input-text v-model:value="input" label="Input number" placeholder="Put your number here (ex: 3232235777)" label-position="left" label-width="110px" mb-2 label-align="right" />
32+
33+
<n-form-item v-if="!hasInputNumberPrefix" label="Input base" label-placement="left" label-width="110" :show-feedback="false">
34+
<c-select
35+
v-model:value="inputBase"
36+
:options="[{ value: 2, label: 'Binary' }, { value: 8, label: 'Octal' }, { value: 10, label: 'Decimal' }, { value: 16, label: 'Hexadecimal' }]"
37+
placeholder="Select a base"
38+
w-100px
39+
/>
40+
</n-form-item>
41+
42+
<n-divider />
43+
44+
<InputCopyable
45+
label="Formatted IPv4"
46+
label-position="left" label-width="110px" mb-2 label-align="right"
47+
:value="convertToIP({ value: input, fromBase: inputBase, version: 4 })"
48+
placeholder="Formatted IPv4 will be here..."
49+
/>
50+
51+
<InputCopyable
52+
label="Formatted IPv6"
53+
label-position="left" label-width="110px" mb-2 label-align="right"
54+
:value="convertToIP({ value: input, fromBase: inputBase, version: 6 })"
55+
placeholder="Formatted IPv6 will be here..."
56+
/>
57+
</c-card>
58+
</div>
59+
</template>
60+
61+
<style lang="less" scoped>
62+
.n-input-group:not(:first-child) {
63+
margin-top: 5px;
64+
}
65+
</style>

0 commit comments

Comments
 (0)