Canonicalize CIDR: Difference between revisions

Add Swift implementation
(→‎{{header|TXR}}: New section.)
(Add Swift implementation)
 
(16 intermediate revisions by 12 users not shown)
Line 41:
| d < 0 | d > 255
R (0, 0)
V mask = (-)~((1 << (32 - m)) - 1)
V address = (a << 24) + (b << 16) + (c << 8) + d
address [&]= mask
Line 74:
184.232.176.184/18 -> 184.232.128.0/18
</pre>
 
=={{header|ALGOL 68}}==
<syntaxhighlight lang="algol68">BEGIN # show IPv4 addresses in CIDR notation in canonical form #
Line 218 ⟶ 219:
<syntaxhighlight lang="apl"> canonicalize '87.70.141.1/22'
87.70.140.0/22</syntaxhighlight>
 
=={{header|BASIC}}==
==={{header|Commodore BASIC}}===
<syntaxhighlight lang="gwbasic">100 REM THE BINARY OPS ONLY WORK ON SIGNED 16-BIT NUMBERS
110 REM SO WE STORE THE IP ADDRESS AS AN ARRAY OF FOUR OCTETS
120 DIM IP(3)
130 REM READ DEMO DATA
140 READ CI$
150 IF CI$="" THEN END
160 REM FIND /
170 SL=0
180 FOR I=LEN(CI$) TO 1 STEP -1
190 : IF MID$(CI$,I,1)="/" THEN SL=I:I=1
200 NEXT I
210 IF SL=0 THEN PRINT "INVALID CIDR STRING: '"CI$"'":GOTO 140
220 NW=VAL(MID$(CI$,SL+1))
230 IF NW < 1 OR NW > 32 THEN PRINT "INVALID NETWORK WIDTH:"NW:GOTO 140
240 REM PARSE OCTETS INTO IP ARRAY
250 BY=0:N=0
260 FOR I=1 TO SL-1
270 : C$=MID$(CI$,I,1):IF C$<>"." THEN 300
280 : IP(N)=BY:N=N+1:BY=0:IF IP(N-1)<256 THEN 310
290 : PRINT "INVALID OCTET VALUE:"IP(N-1):GOTO 140
300 : C=VAL(C$):IF C OR (C$="0") THEN BY=BY*10+C
310 NEXT I
320 IP(N)=BY:N=N+1:IF IP(N-1)>255 THEN 290
330 REM NUMBER OF COMPLETE OCTETS IN NETWORK PART
340 NB=INT(NW/8)
350 REM NUMBER OF NETWORK BITS IN PARTIAL OCTET
360 XB=NW AND 7
370 REM ZERO OUT HOST BITS IN PARTIAL OCTET
380 IP(NB) = IP(NB) AND (255 - 2^(8-XB) + 1)
390 REM AND SET ANY ALL-HOST OCTETS TO 0
400 IF NB<3 THEN FOR I=NB+1 TO 3:IP(I)=0 :NEXT I
410 REM PRINT OUT THE RESULT
420 PRINT MID$(STR$(IP(0)),2);
430 FOR I=1 TO 3: PRINT "."MID$(STR$(IP( I)),2);:NEXT I
440 PRINT MID$(CI$,SL)
450 REM AND GO BACK FOR NEXT INPUT
460 GOTO 140
500 DATA 87.70.141.1/22, 36.18.154.103/12, 62.62.197.11/29
510 DATA 67.137.119.181/4, 161.214.74.21/24, 184.232.176.184/18
520 REM SOME INVALID INPUTS
530 DATA 127.0.0.1, 123.45.67.89/0, 98.76.54.32/100, 123.456.789.0/12
540 DATA</syntaxhighlight>
 
{{Out}}<pre>READY.
RUN
87.70.140.0/22
36.16.0.0/12
62.62.197.8/29
64.0.0.0/4
161.214.74.0/24
184.232.128.0/18
INVALID CIDR STRING: '127.0.0.1'
INVALID NETWORK WIDTH: 0
INVALID NETWORK WIDTH: 100
INVALID OCTET VALUE: 456
 
READY.</pre>
 
==={{header|FreeBASIC}}===
{{trans|Commodore BASIC}}
<syntaxhighlight lang="vb">#lang "qb"
 
REM THE Binary OPS ONLY WORK On SIGNED 16-Bit NUMBERS
REM SO WE STORE THE IP ADDRESS As AN ARRAY OF FOUR OCTETS
Cls
Dim IP(3)
Do
REM Read DEMO Data
140 Read CI$
If CI$ = "" Then Exit Do 'Sleep: End
REM FIND /
SL = 0
For I = Len(CI$) To 1 Step -1
If Mid$(CI$,I,1) = "/" Then SL = I : I = 1
Next I
If SL = 0 Then Print "INVALID CIDR STRING: '"; CI$; "'": Goto 140
NW = Val(Mid$(CI$,SL+1))
If NW < 1 Or NW > 32 Then Print "INVALID NETWORK WIDTH:"; NW: Goto 140
REM PARSE OCTETS INTO IP ARRAY
BY = 0 : N = 0
For I = 1 To SL-1
C$ = Mid$(CI$,I,1)
If Not (C$ <> ".") Then
IP(N) = BY : N = N + 1
BY = 0
If IP(N-1) < 256 Then 310
Print "INVALID OCTET VALUE:"; IP(N-1): Goto 140
Else C = Val(C$):If C Or (C$="0") Then BY = BY*10+C
End If
310 '
Next I
IP(N) = BY : N = N + 1
If IP(N-1) > 255 Then Print "INVALID OCTET VALUE:"; IP(N-1): Goto 140
REM NUMBER OF COMPLETE OCTETS IN NETWORK PART
NB = Int(NW/8)
REM NUMBER OF NETWORK BITS IN PARTIAL OCTET
XB = NW And 7
REM ZERO Out HOST BITS IN PARTIAL OCTET
IP(NB) = IP(NB) And (255 - 2^(8-XB) + 1)
REM And SET Any ALL-HOST OCTETS To 0
If NB < 3 Then For I = NB +1 To 3 : IP(I) = 0 : Next I
REM Print Out THE RESULT
Print Mid$(Str$(IP(0)),2);
For I = 1 To 3
Print "."; Mid$(Str$(IP( I)),2);
Next I
Print Mid$(CI$,SL)
Loop
Data "87.70.141.1/22", "36.18.154.103/12", "62.62.197.11/29"
Data "67.137.119.181/4", "161.214.74.21/24", "184.232.176.184/18"
REM SOME INVALID INPUTS
Data "127.0.0.1", "123.45.67.89/0", "98.76.54.32/100", "123.456.789.0/12"
 
Sleep</syntaxhighlight>
{{out}}
<pre>Similar to Commodore BASIC entry.</pre>
 
==={{header|QBasic}}===
{{trans|Commodore BASIC}}
{{works with|QBasic|1.1}}
{{works with|QuickBasic|4.5}}
<syntaxhighlight lang="qbasic">CLS
DIM IP(3)
DO
REM Read DEMO Data
140 READ CI$
IF CI$ = "" THEN EXIT DO 'Sleep: End
REM FIND /
SL = 0
FOR I = LEN(CI$) TO 1 STEP -1
IF MID$(CI$, I, 1) = "/" THEN SL = I: I = 1
NEXT I
IF SL = 0 THEN PRINT "INVALID CIDR STRING: '"; CI$; "'": GOTO 140
NW = VAL(MID$(CI$, SL + 1))
IF NW < 1 OR NW > 32 THEN PRINT "INVALID NETWORK WIDTH:"; NW: GOTO 140
REM PARSE OCTETS INTO IP ARRAY
BY = 0: N = 0
FOR I = 1 TO SL - 1
C$ = MID$(CI$, I, 1): IF C$ <> "." THEN 300
IP(N) = BY: N = N + 1: BY = 0: IF IP(N - 1) < 256 THEN 310
290 PRINT "INVALID OCTET VALUE:"; IP(N - 1): GOTO 140
300 C = VAL(C$): IF C OR (C$ = "0") THEN BY = BY * 10 + C
310 '
NEXT I
IP(N) = BY: N = N + 1: IF IP(N - 1) > 255 THEN 290
REM NUMBER OF COMPLETE OCTETS IN NETWORK PART
NB = INT(NW / 8)
REM NUMBER OF NETWORK BITS IN PARTIAL OCTET
XB = NW AND 7
REM ZERO Out HOST BITS IN PARTIAL OCTET
IP(NB) = IP(NB) AND (255 - 2 ^ (8 - XB) + 1)
REM And SET Any ALL-HOST OCTETS To 0
IF NB < 3 THEN FOR I = NB + 1 TO 3: IP(I) = 0: NEXT I
REM Print Out THE RESULT
PRINT MID$(STR$(IP(0)), 2);
FOR I = 1 TO 3
PRINT "."; MID$(STR$(IP(I)), 2);
NEXT I
PRINT MID$(CI$, SL)
LOOP
DATA 87.70.141.1/22, 36.18.154.103/12, 62.62.197.11/29
DATA 67.137.119.181/4, 161.214.74.21/24, 184.232.176.184/18
REM SOME INVALID INPUTS
DATA 127.0.0.1, 123.45.67.89/0, 98.76.54.32/100, 123.456.789.0/12
DATA</syntaxhighlight>
{{out}}
<pre>Similar to Commodore BASIC entry.</pre>
 
==={{header|uBasic/4tH}}===
<syntaxhighlight lang="uBasic/4tH">If Try (_CanonicalizeCIDR ("36.18.154.103/12")) Then Print "Illegal IP"
If Try (_CanonicalizeCIDR ("62.62.197.11/29")) Then Print "Illegal IP"
If Try (_CanonicalizeCIDR ("67.137.119.181/4")) Then Print "Illegal IP"
If Try (_CanonicalizeCIDR ("161.214.74.0/24")) Then Print "Illegal IP"
If Try (_CanonicalizeCIDR ("184.232.176.184/18")) Then Print "Illegal IP"
End
 
_CanonicalizeCIDR ' do the whole shebang
Param (1) ' IP string
Local (3)
 
b@ = FUNC(_GetIP (a@)) ' get IP address
c@ = FUNC(_GetMask(a@)) ' get the mask
d@ = FUNC(_Canonicalize (b@, c@)) ' canonicalize IP address
' now print the report
Print Show(a@); Tab(20); " -> "; FUNC(_IP(d@, 24)); ".";
Print FUNC(_IP(d@, 16)); "."; FUNC(_IP(d@, 8)); "."; AND(d@, 255); "/"; c@
Return
_IP Param (2) : Return (AND(255, SHL(a@, -b@)))
_Canonicalize Param (2) : Return (AND(a@, FUNC(_MakeMask (b@))))
_GetMask Param (1) : Return (Val(Chop(a@, o)))
_MakeMask Param (1) : Return (NOT(SHL(1, 32 - a@) - 1))
 
_GetIP ' get the IP address
Param (1) ' IP string
Local (1) ' IP number
 
o = 0
b@ = SHL(FUNC(_Parse (a@, Ord("."))), 24)
b@ = OR(b@, SHL(FUNC(_Parse (a@, Ord("."))), 16))
b@ = OR(b@, SHL(FUNC(_Parse (a@, Ord("."))), 8))
b@ = OR(b@, FUNC(_Parse (a@, Ord("/"))))
Return (b@)
 
_Parse ' parse a token
Param (2) ' string, delimiter
Local (1) ' token
 
c@ = Dup ("") ' start with an empty token
 
For o = o to Len (a@) - 1 Until Peek (a@, o) = b@
c@ = Join (c@, Char (Peek (a@, o)))
Next
 
o = o + 1
Return (Val(c@))</syntaxhighlight>
{{Out}}
<pre>36.18.154.103/12 -> 36.16.0.0/12
62.62.197.11/29 -> 62.62.197.8/29
67.137.119.181/4 -> 64.0.0.0/4
161.214.74.0/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18
 
0 OK, 0:388</pre>
 
=={{header|C}}==
This solution uses only the standard library. On POSIX platforms one can use the functions
Line 448 ⟶ 677:
 
{{out}}
<pre>87.70.141.1/22 -> 87.70.140.0/22
<pre>
87.70.141.1/22 -> 87.70.140.0/22
36.18.154.103/12 -> 36.16.0.0/12
62.62.197.11/29 -> 62.62.197.8/29
67.137.119.181/4 -> 64.0.0.0/4
161.214.74.21/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18</pre>
</pre>
=={{header|Commodore BASIC}}==
<syntaxhighlight lang="gwbasic">100 REM THE BINARY OPS ONLY WORK ON SIGNED 16-BIT NUMBERS
110 REM SO WE STORE THE IP ADDRESS AS AN ARRAY OF FOUR OCTETS
120 DIM IP(3)
130 REM READ DEMO DATA
140 READ CI$
150 IF CI$="" THEN END
160 REM FIND /
170 SL=0
180 FOR I=LEN(CI$) TO 1 STEP -1
190 : IF MID$(CI$,I,1)="/" THEN SL=I:I=1
200 NEXT I
210 IF SL=0 THEN PRINT "INVALID CIDR STRING: '"CI$"'":GOTO 140
220 NW=VAL(MID$(CI$,SL+1))
230 IF NW < 1 OR NW > 32 THEN PRINT "INVALID NETWORK WIDTH:"NW:GOTO 140
240 REM PARSE OCTETS INTO IP ARRAY
250 BY=0:N=0
260 FOR I=1 TO SL-1
270 : C$=MID$(CI$,I,1):IF C$<>"." THEN 300
280 : IP(N)=BY:N=N+1:BY=0:IF IP(N-1)<256 THEN 310
290 : PRINT "INVALID OCTET VALUE:"IP(N-1):GOTO 140
300 : C=VAL(C$):IF C OR (C$="0") THEN BY=BY*10+C
310 NEXT I
320 IP(N)=BY:N=N+1:IF IP(N-1)>255 THEN 290
330 REM NUMBER OF COMPLETE OCTETS IN NETWORK PART
340 NB=INT(NW/8)
350 REM NUMBER OF NETWORK BITS IN PARTIAL OCTET
360 XB=NW AND 7
370 REM ZERO OUT HOST BITS IN PARTIAL OCTET
380 IP(NB) = IP(NB) AND (255 - 2^(8-XB) + 1)
390 REM AND SET ANY ALL-HOST OCTETS TO 0
400 IF NB<3 THEN FOR I=NB+1 TO 3:IP(I)=0 :NEXT I
410 REM PRINT OUT THE RESULT
420 PRINT MID$(STR$(IP(0)),2);
430 FOR I=1 TO 3: PRINT "."MID$(STR$(IP( I)),2);:NEXT I
440 PRINT MID$(CI$,SL)
450 REM AND GO BACK FOR NEXT INPUT
460 GOTO 140
500 DATA 87.70.141.1/22, 36.18.154.103/12, 62.62.197.11/29
510 DATA 67.137.119.181/4, 161.214.74.21/24, 184.232.176.184/18
520 REM SOME INVALID INPUTS
530 DATA 127.0.0.1, 123.45.67.89/0, 98.76.54.32/100, 123.456.789.0/12
540 DATA</syntaxhighlight>
 
{{Out}}<pre>READY.
RUN
87.70.140.0/22
36.16.0.0/12
62.62.197.8/29
64.0.0.0/4
161.214.74.0/24
184.232.128.0/18
INVALID CIDR STRING: '127.0.0.1'
INVALID NETWORK WIDTH: 0
INVALID NETWORK WIDTH: 100
INVALID OCTET VALUE: 456
 
READY.</pre>
=={{header|Common Lisp}}==
<syntaxhighlight lang="lisp">(defun ip->bit-vector (ip)
Line 643 ⟶ 813:
<pre>Before canonicalization: 87.70.141.1/22
After canonicalization: 87.70.140.0/22</pre>
=={{header|EasyLang}}==
<syntaxhighlight>
func$ can_cidr s$ .
n[] = number strsplit s$ "./"
if len n[] <> 5
return ""
.
for i to 4
if n[i] < 0 or n[i] > 255
return ""
.
ad = ad * 256 + n[i]
.
if n[5] > 31 or n[5] < 1
return ""
.
mask = bitnot (bitshift 1 (32 - n[5]) - 1)
ad = bitand ad mask
for i to 4
if r$ <> ""
r$ = "." & r$
.
r$ = ad mod 256 & r$
ad = ad div 256
.
return r$ & "/" & n[5]
.
repeat
s$ = input
until s$ = ""
print s$ & " -> " & can_cidr s$
.
#
input_data
87.70.141.1/22
36.18.154.103/12
62.62.197.11/29
67.137.119.181/4
161.214.74.21/24
184.232.176.184/18
 
</syntaxhighlight>
 
=={{header|Factor}}==
{{trans|Ruby}}
Line 673 ⟶ 886:
87.70.140.0/22
</pre>
 
=={{header|Go}}==
{{trans|Ruby}}
Line 1,060 ⟶ 1,274:
10.../8 -> 10.0.0.0/8
</pre>
=={{header|jq}}==
'''Adapted from [[#Wren|Wren]]'''
{{works with|jq}}
'''Also works with gojq, the Go implementation of jq, and with fq'''
 
'''Generic Utilities'''
<syntaxhighlight lang=jq>
# For gojq and fq
def _nwise($n):
def nw: if length <= $n then . else .[0:$n] , (.[$n:] | nw) end;
nw;
 
def lpad($len; $fill): tostring | ($len - length) as $l | ($fill * $l)[:$l] + .;
def lpad($len): lpad($len;" ");
 
# Convert the input integer to a string in the specified base (2 to 36 inclusive)
def convert(base):
def stream:
recurse(if . >= base then ./base|floor else empty end) | . % base ;
[stream] | reverse
| if base < 10 then map(tostring) | join("")
elif base <= 36 then map(if . < 10 then 48 + . else . + 87 end) | implode
else error("base too large")
end;
 
# input string is converted from "base" to an integer, within limits
# of the underlying arithmetic operations, and without error-checking:
def to_i(base):
explode
| reverse
| map(if . > 96 then . - 87 else . - 48 end) # "a" ~ 97 => 10 ~ 87
| reduce .[] as $c
# state: [power, ans]
([1,0]; (.[0] * base) as $b | [$b, .[1] + (.[0] * $c)])
| .[1];
 
</syntaxhighlight>
'''Canonicalize CIDR'''
<syntaxhighlight lang=jq>
# Canonicalize a CIDR block: make sure none of the host bits are set
def canonicalize:
# dotted-decimal / bits in network part
(. / "/") as [$dotted, $bits]
| ($bits | tonumber) as $size
 
# get IP as binary string
| {binary: (($dotted / ".") | map( tonumber | convert(2) | lpad(8; "0") ) | join("") )}
# replace the host part with all zeros
| .binary |= .[0:$size] + "0" * (32 - $size)
 
# convert back to dotted-decimal
| [.binary | explode | _nwise(8) | implode]
| (map( to_i(2) | tostring ) | join(".")) as $canon
 
| $canon + "/" + $bits;
 
def tests:
"87.70.141.1/22",
"36.18.154.103/12",
"62.62.197.11/29",
"67.137.119.181/4",
"161.214.74.21/24",
"184.232.176.184/18"
;
 
tests
| "\(lpad(18)) -> \(canonicalize)"
</syntaxhighlight>
{{output}}
<pre>
87.70.141.1/22 -> 87.70.140.0/22
36.18.154.103/12 -> 36.16.0.0/12
62.62.197.11/29 -> 62.62.197.8/29
67.137.119.181/4 -> 64.0.0.0/4
161.214.74.21/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18
</pre>
 
=={{header|Julia}}==
Julia has a Sockets library as a builtin, which has the types IPv4 and IPv6 for single IP addresses.
Line 1,092 ⟶ 1,385:
10.0.0.0/8
</pre>
 
=={{header|Lua}}==
{{libheader|inet}}
Line 1,148 ⟶ 1,442:
161.214.74.0/24
184.232.128.0/18</pre>
 
=={{header|MATLAB}}==
{{trans|Python}}
<syntaxhighlight lang="MATLAB">
clear all;close all;clc;
cidrCanonicalizer();
 
function cidrCanonicalizer
% Main function to test CIDR canonicalization
% Define test cases
testCases = {
'36.18.154.103/12', '36.16.0.0/12';
'62.62.197.11/29', '62.62.197.8/29';
'67.137.119.181/4', '64.0.0.0/4';
'161.214.74.21/24', '161.214.74.0/24';
'184.232.176.184/18', '184.232.128.0/18'
};
% Run test cases
for i = 1:size(testCases, 1)
ip = testCases{i, 1};
expected = testCases{i, 2};
result = canonicalize(ip);
fprintf('%s -> %s\n', ip, result);
assert(strcmp(result, expected));
end
end
 
function result = dottedToInt(dotted)
% Convert dotted IP to integer representation
parts = str2double(strsplit(dotted, '.'));
result = sum(parts .* (256 .^ (3:-1:0)));
end
 
function result = intToDotted(ip)
% Convert integer IP to dotted representation
result = strjoin(arrayfun(@(x) num2str(bitshift(bitand(ip, bitshift(255, x)), -x)), [24 16 8 0], 'UniformOutput', false), '.');
end
 
function result = networkMask(numberOfBits)
% Create a network mask for the given number of bits
result = bitshift((bitshift(1, numberOfBits) - 1), (32 - numberOfBits));
end
 
function result = canonicalize(ip)
% Canonicalize the given CIDR IP
[dotted, networkBits] = strtok(ip, '/');
networkBits = str2double(strrep(networkBits, '/', ''));
i = dottedToInt(dotted);
mask = networkMask(networkBits);
result = strcat(intToDotted(bitand(i, mask)), '/', num2str(networkBits));
end
</syntaxhighlight>
{{out}}
<pre>
36.18.154.103/12 -> 36.16.0.0/12
62.62.197.11/29 -> 62.62.197.8/29
67.137.119.181/4 -> 64.0.0.0/4
161.214.74.21/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18
</pre>
 
=={{header|Nim}}==
Using the IpAddress type from standard module “net”.
Line 1,215 ⟶ 1,572:
161.214.74.21/24 ⇢ 161.214.74.0/24
184.232.176.184/18 ⇢ 184.232.128.0/18</pre>
 
=={{header|OCaml}}==
<syntaxhighlight lang="ocaml">let mask = function
| _, 0 -> 0l, 0
| a, l -> Int32.(logand (shift_left minus_one (-l land 31)) a), l
 
let str_to_cidr s =
let (<<+) b a = Int32.(add (shift_left b 8) a) in
let recv d c b a l = d <<+ c <<+ b <<+ a, l in
Scanf.sscanf s "%3lu.%3lu.%3lu.%3lu/%2u" recv
 
let cidr_to_str (a, l) =
let addr n =
let dgt = function
| h :: t -> Int32.shift_right_logical h 8 :: Int32.logand h 255l :: t
| l -> l
in
dgt [n] |> dgt |> dgt |> List.map Int32.to_string |> String.concat "."
in
Printf.sprintf "%s/%u" (addr a) l
 
let () =
["87.70.141.1/22"; "36.18.154.103/12"; "62.62.197.11/29"; "67.137.119.181/4"; "161.214.74.21/24"; "184.232.176.184/18"; "10.207.219.251/32"]
|> List.iter (fun s -> str_to_cidr s |> mask |> cidr_to_str |> print_endline)</syntaxhighlight>
{{out}}
<pre>
87.70.140.0/22
36.16.0.0/12
62.62.197.8/29
64.0.0.0/4
161.214.74.0/24
184.232.128.0/18
10.207.219.251/32
</pre>
 
=={{header|Perl}}==
There's a CPAN module for IP address manipulation, <tt>Net::IP</tt>. Unfortunately, it requires a CIDR string to be already in canonical form; otherwise it fails with an "Invalid prefix" error. So we do it manually.
Line 1,596 ⟶ 1,988:
canonicalized address: 184.232.128.0/18
</pre>
=={{header|RPL}}==
RPL has hundreds of built-in functions, but strangely a few limited ones for bit handling. There is no <code>>></code> operator, so shifting left an integer by n bits shall be done through a loop: <code> 1 n '''START''' SL '''NEXT'''</code>
{| class="wikitable"
! RPL code
! Comment
|-
|
DUP SIZE "" 1 ROT '''FOR''' j
OVER j j SUB DUP "0" ≥ OVER "9" ≤ AND SWAP " "
IFTE + '''NEXT'''
SWAP DROP STR→ → nbits
≪ SWAP ROT 4 ROLL
32 STWS 1 3 '''START''' #100h * + '''NEXT'''
#FFFFFFFF 1 32 nbits - '''START''' SL '''NEXT''' AND
1 3 '''START''' DUP SRB SWAP OVER SLB - SWAP '''NEXT'''
B→R →STR 1 3 '''START''' "." + SWAP B→R →STR + '''NEXT'''
"/" + nbits →STR +
≫ ≫ ‘<span style="color:blue">CANON</span>’ STO
|
<span style="color:blue">CANON</span> ''( "nn.nn.nn.nn/bits" → "nn.nn.nn.nn/nbits" )
Scan input string
Replace non-numerical chars by spaces
Drop input string, parse addresses to stack - except # bits
Reverse stack order
Consolidate numbers into a 32-bit number
Set and apply mask
Break 32-bit number into 4 in the stack
Make a IP address with the 4 numbers
add # bits
|}
≪ { "36.18.154.103/12" "62.62.197.11/29" "67.137.119.181/4" "161.214.74.21/24" "184.232.176.184/18" } → t
≪ 1 5 '''FOR''' j t j GET <span style="color:blue">CANON</span> '''NEXT'''
≫ ≫ ‘<span style="color:blue">TASK</span>’ STO
{{out}}
<pre>
5: "36.16.0.0/12"
4: "62.62.197.8/29"
3: "64.0.0.0/4"
2: "161.214.74.0/24"
1: "184.232.128.0/18"
</pre>
 
=={{header|Ruby}}==
{{trans|Python}}
Line 1,649 ⟶ 2,086:
161.214.74.0/24
184.232.128.0/18</pre>
 
=={{header|Rust}}==
<syntaxhighlight lang="rust">use std::net::Ipv4Addr;
Line 1,689 ⟶ 2,127:
println!("{}", canonical_cidr("127.1.2.3/24").unwrap());
}</syntaxhighlight>
 
=={{header|Scala}}==
{{trans|Java}}
<syntaxhighlight lang="Scala">
import java.text.MessageFormat
 
object CanonicalizeCIDR extends App {
case class CIDR(address: Int, maskLength: Int) {
override def toString: String = {
val a = (address >> 24) & 0xFF
val b = (address >> 16) & 0xFF
val c = (address >> 8) & 0xFF
val d = address & 0xFF
MessageFormat.format(CIDR.format, a.asInstanceOf[AnyRef], b.asInstanceOf[AnyRef], c.asInstanceOf[AnyRef], d.asInstanceOf[AnyRef], maskLength.asInstanceOf[AnyRef])
}
}
 
object CIDR {
private val format = "{0,number,integer}.{1,number,integer}.{2,number,integer}.{3,number,integer}/{4,number,integer}"
 
def apply(str: String): CIDR = {
val args = new MessageFormat(format).parse(str)
val address = args.take(4).foldLeft(0) { (acc, arg) =>
val a = arg.asInstanceOf[Number].intValue()
require(a >= 0 && a <= 255, "Invalid IP address")
(acc << 8) + a
}
val maskLength = args(4).asInstanceOf[Number].intValue()
require(maskLength >= 1 && maskLength <= 32, "Invalid mask length")
val mask = ~((1 << (32 - maskLength)) - 1)
new CIDR(address & mask, maskLength)
}
}
 
val tests = Array(
"87.70.141.1/22",
"36.18.154.103/12",
"62.62.197.11/29",
"67.137.119.181/4",
"161.214.74.21/24",
"184.232.176.184/18"
)
 
tests.foreach { test =>
try {
val cidr = CIDR(test)
println(f"$test%-18s -> $cidr")
} catch {
case ex: Exception => println(s"Error parsing '$test': ${ex.getLocalizedMessage}")
}
}
}
</syntaxhighlight>
{{out}}
<pre>
87.70.141.1/22 -> 87.70.140.0/22
36.18.154.103/12 -> 36.16.0.0/12
62.62.197.11/29 -> 62.62.197.8/29
67.137.119.181/4 -> 64.0.0.0/4
161.214.74.21/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18
 
</pre>
 
=={{header|SNOBOL}}==
<syntaxhighlight lang="snobol">* Pattern to match any number of digits
Line 1,746 ⟶ 2,248:
161.214.74.0/24
184.232.128.0/18</pre>
 
=={{header|Swift}}==
{{trans|Python}}
<syntaxhighlight lang="Swift">
import Foundation
 
func dottedToInt(_ dotted: String) -> UInt32 {
let digits = dotted.split(separator: ".").map { UInt32($0)! }
return digits.enumerated().reduce(0) { $0 + ($1.element << (24 - $1.offset * 8)) }
}
 
func intToDotted(_ ip: UInt32) -> String {
let digits = [24, 16, 8, 0].map { (ip & (255 << $0)) >> $0 }
return digits.map { String($0) }.joined(separator: ".")
}
 
func networkMask(_ numberOfBits: Int) -> UInt32 {
// Explicitly use UInt32 for bitwise operations
return UInt32((1 << numberOfBits) - 1) << (32 - numberOfBits)
}
 
func canonicalize(_ ip: String) -> String {
let parts = ip.split(separator: "/")
let dotted = String(parts[0])
let networkBits = Int(parts[1])!
let i = dottedToInt(dotted)
let mask = networkMask(networkBits)
return "\(intToDotted(i & mask))/\(networkBits)"
}
 
let testCases = [
("36.18.154.103/12", "36.16.0.0/12"),
("62.62.197.11/29", "62.62.197.8/29"),
("67.137.119.181/4", "64.0.0.0/4"),
("161.214.74.21/24", "161.214.74.0/24"),
("184.232.176.184/18", "184.232.128.0/18"),
]
 
for testCase in testCases {
let (ip, expect) = testCase
let result = canonicalize(ip)
print("\(ip) -> \(result)")
assert(result == expect, "Test failed for \(ip)")
}
</syntaxhighlight>
{{out}}
<pre>
36.18.154.103/12 -> 36.16.0.0/12
62.62.197.11/29 -> 62.62.197.8/29
67.137.119.181/4 -> 64.0.0.0/4
161.214.74.21/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18
 
</pre>
 
 
=={{header|Tcl}}==
{{trans|Python}}
<syntaxhighlight lang="Tcl">
# Canonicalize CIDR in Tcl
 
# Convert dotted IP address to integer
proc dotted_to_int {dotted} {
set digits [split $dotted .]
set result 0
foreach digit $digits {
set result [expr {$result * 256 + $digit}]
}
return $result
}
 
# Convert integer IP address to dotted format
proc int_to_dotted {ip} {
set result {}
for {set i 3} {$i >= 0} {incr i -1} {
lappend result [expr {($ip >> ($i * 8)) & 0xFF}]
}
return [join $result .]
}
 
# Calculate network mask
proc network_mask {number_of_bits} {
return [expr {(1 << $number_of_bits) - 1 << (32 - $number_of_bits)}]
}
 
# Canonicalize IP address
proc canonicalize {ip} {
regexp {^(.*)/(.*)$} $ip -> dotted network_bits
set i [dotted_to_int $dotted]
set mask [network_mask $network_bits]
return [int_to_dotted [expr {$i & $mask}]]/$network_bits
}
 
# Test cases
set test_cases {
{"36.18.154.103/12" "36.16.0.0/12"}
{"62.62.197.11/29" "62.62.197.8/29"}
{"67.137.119.181/4" "64.0.0.0/4"}
{"161.214.74.21/24" "161.214.74.0/24"}
{"184.232.176.184/18" "184.232.128.0/18"}
}
 
# Main execution
foreach test $test_cases {
foreach {ip expect} $test {}
set rv [canonicalize $ip]
puts "$ip -> $rv"
if {$rv ne $expect} {
error "Test failed: $rv != $expect"
}
}
</syntaxhighlight>
{{out}}
<pre>
36.18.154.103/12 -> 36.16.0.0/12
62.62.197.11/29 -> 62.62.197.8/29
67.137.119.181/4 -> 64.0.0.0/4
161.214.74.21/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18
 
</pre>
 
 
=={{header|TXR}}==
 
The <code>inaddr-str</code> function in TXR Lisp parses IPv4 addresses, converting them to a <code>sockaddr-in</code> structure. If there is a slash notation present, it is recognized. The prefix value is validated and stored in the structure as the <code>prefix</code> value, and the numeric address is canonicalized to clear the irrelevant bits. Thus, the solution looks like this:
 
<syntaxhighlight lang="txrlisp">(defun cidr-canon (str)
(let ((a (inaddr-str str)))
(match `@dots/@bits` str
`@(let ((str-inaddr (inaddr-str dotsa.addr)/@{a.addrprefix}`))</syntaxhighlight>
 
(bits (int-str bits)))
Furthermore, the prefix notation can be condensed by removing unnecessary zeros. That is to say, <code>10.1.2.3/16</code> can be not just canonicalized to strip the irrelevant bits, but then shortened to <code>10.1/16</code>.
`@(str-inaddr (logand inaddr (ash -1 (- 32 bits))))/@bits`)))</syntaxhighlight>
 
The built-in function <code>inaddr-str-net</code> will produce this condensed prefix notation:
 
<syntaxhighlight lang="txrlisp">(defun cidr-canon (str)
(let ((a (inaddr-str str)))
(str-inaddr-net a.addr a.prefix)))</syntaxhighlight>
 
This can be written using the <code>flow</code> macro:
 
<syntaxhighlight lang="txrlisp">(defun cidr-canon (str)
(flow str
inaddr-str
(str-inaddr-net @1.addr @1.prefix)))</syntaxhighlight>
 
=={{header|UNIX Shell}}==
{{works with|Bourne Again SHell}}
Line 1,812 ⟶ 2,455:
{{libheader|Wren-fmt}}
{{libheader|Wren-str}}
<syntaxhighlight lang="ecmascriptwren">import "./fmt" for Fmt, Conv
import "./str" for Str
 
// canonicalize a CIDR block: make sure none of the host bits are set
Line 1,857 ⟶ 2,500:
161.214.74.21/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18
</pre>
 
=={{header|XPL0}}==
Device 8 is a 256-byte input/output buffer that provides a convenient
means for converting between ASCII and binary representations of numbers.
<syntaxhighlight lang "XPL0">proc Canonicalize(IPAddr);
char IPAddr;
int N, I, HostBits;
[Text(0, IPAddr);
Text(0, " ^i-> "); \^i = tab
OpenO(8);
Text(8, IPAddr); \ASCII out
OpenI(8);
N:= 0;
for I:= 0 to 3 do
N:= N<<8 + IntIn(8); \binary in
HostBits:= IntIn(8);
N:= N & -1<<(32-HostBits);
for I:= 3 downto 0 do
[IntOut(0, N>>(I*8) & $FF);
ChOut(0, if I = 0 then ^/ else ^.);
];
IntOut(0, HostBits);
CrLf(0);
];
 
int IPAddrs, I;
[IPAddrs:= [
"87.70.141.1/22",
"36.18.154.103/12",
"62.62.197.11/29",
"67.137.119.181/4",
"161.214.74.21/24",
"184.232.176.184/18" ];
for I:= 0 to 6-1 do
Canonicalize(IPAddrs(I));
]</syntaxhighlight>
{{out}}
<pre>
87.70.141.1/22 -> 87.70.140.0/22
36.18.154.103/12 -> 36.16.0.0/12
62.62.197.11/29 -> 62.62.197.8/29
67.137.119.181/4 -> 64.0.0.0/4
161.214.74.21/24 -> 161.214.74.0/24
184.232.176.184/18 -> 184.232.128.0/18
</pre>
337

edits