Skip to content

Commit ab3f00e

Browse files
committed
Merge branch 'feat/integer-to-ip' into chore/all-my-stuffs
# Conflicts: # components.d.ts # package.json # pnpm-lock.yaml # src/tools/index.ts
2 parents 8a4ebe2 + 8659024 commit ab3f00e

File tree

7 files changed

+153
-12
lines changed

7 files changed

+153
-12
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
"ical-generator": "^8.0.0",
121121
"ical.js": "^2.1.0",
122122
"image-in-browser": "^3.2.0",
123+
"ip-bigint": "^8.2.0",
123124
"js-base64": "^3.7.6",
124125
"image-in-browser": "^3.1.0",
125126
"js-base64": "^3.7.7",

pnpm-lock.yaml

Lines changed: 8 additions & 0 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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { tool as fileHasher } from './file-hasher';
1414
import { tool as hexFileConverter } from './hex-file-converter';
1515
import { tool as icalMerger } from './ical-merger';
1616
import { tool as imageConverter } from './image-converter';
17+
import { tool as integersToIp } from './integers-to-ip';
1718

1819
import { tool as cssXpathConverter } from './css-xpath-converter';
1920
import { tool as cssSelectorsMemo } from './css-selectors-memo';
@@ -251,6 +252,7 @@ export const toolsByCategory: ToolCategory[] = [
251252
dnsQueries,
252253
emailParser,
253254
outlookParser,
255+
integersToIp,
254256
],
255257
},
256258
{

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)