Overview
これ の python3 と javascript 版。
javascript でブラウザで動かせるやつが欲しかったのだけど、意外と手こずったので、一度 python で書いてから js で書き直した。
検証はこの辺で。
python3 版
netaddr っていうお手本があるので、これを使うと楽。
ちなみに netaddr.iprange_to_cidrs
っていうそのまんまな実装がすでにあるけど、今回は手書きしたかったので使いません。
a.py
import netaddr
import math
def iprange2cidr(start, end):
iMask = lambda s: (2**32 - 2**(32-s))
while end >= start:
max_size = 32
while max_size > 0:
assert 0 < max_size <= 32
mask = iMask(max_size - 1)
if (start & mask) != start:
break
max_size -= 1
x = math.log(end - start + 1) / math.log(2)
max_diff = math.floor(32 - math.floor(x))
if max_size < max_diff:
max_size = max_diff
yield "{}/{}".format(netaddr.IPAddress(start), max_size)
start += 2**(32-max_size)
if __name__ == "__main__":
expect = [
"192.0.2.46/31",
"192.0.2.48/28",
"192.0.2.64/26",
"192.0.2.128/25",
"192.0.3.0/24",
"192.0.4.0/22",
"192.0.8.0/21",
"192.0.16.0/20",
"192.0.32.0/24",
"192.0.33.0/29",
"192.0.33.8/30",
"192.0.33.12/31",
"192.0.33.14/32",
]
a1 = netaddr.IPAddress('192.0.2.46')
b1 = netaddr.IPAddress('192.0.33.14')
actual = list(iprange2cidr(int(a1), int(b1)))
print(actual)
assert actual == expect
$ python a.py
['192.0.2.46/31', '192.0.2.48/28', '192.0.2.64/26', '192.0.2.128/25', '192.0.3.0/24', '192.0.4.0/22', '192.0.8.0/21', '192.0.16.0/20', '192.0.32.0/24', '192.0.33.0/29', '192.0.33.8/30', '192.0.33.12/31', '192.0.33.14/32']
javascript 版
unsigned が明示できないのが罠でちょっとハマった。
a.js
class IPv4 {
static OctetsToInt(octets) {
const ret = (octets[0] << 24) + (octets[1] << 16) + (octets[2] << 8) + octets[3];
return ret >>> 0; // cast to unsigned
}
static IntToOctets(i) {
return [
(i >>> 24) & 0xFF,
(i >>> 16) & 0xFF,
(i >>> 8) & 0xFF,
(i >>> 0) & 0xFF,
];
}
static IntToString(i) {
return this.OctetsToString(this.IntToOctets(i));
}
static StringToOctets(s) {
return s.split(".").map((x) => parseInt(x, 10))
}
static OctetsToString(octets) {
return octets.join(".")
}
static IntToBits(i) {
const mask = [...Array(32).keys()].reverse().map((x) => 1<<x);
const bits = mask.map((x) => i & x).map((x) => x != 0).map(Number);
return bits.join("");
}
}
function iprange2cidr(start, end) {
const ret = [];
const iMask = function(s) {
return (2**32 - 2**(32-s));
}
while (end >= start) {
let maxSize = 32;
while (maxSize > 0) {
const mask = iMask(maxSize-1);
const maskBase = (start & mask)>>>0; // cast to unsigned
if (maskBase != start) {
break;
}
maxSize -= 1;
}
const x = Math.log(end - start + 1) / Math.log(2);
const maxDiff = Math.floor(32 - Math.floor(x));
if (maxSize < maxDiff) {
maxSize = maxDiff;
}
ret.push([IPv4.IntToString(start), maxSize].join("/"));
start += 2**(32-maxSize);
}
return ret;
}
a1 = '192.0.2.46';
b1 = '192.0.33.14';
a2 = IPv4.OctetsToInt(IPv4.StringToOctets(a1));
b2 = IPv4.OctetsToInt(IPv4.StringToOctets(b1));
c = iprange2cidr(a2, b2);
console.log(c);
$ node a.js
[
'192.0.2.46/31', '192.0.2.48/28',
'192.0.2.64/26', '192.0.2.128/25',
'192.0.3.0/24', '192.0.4.0/22',
'192.0.8.0/21', '192.0.16.0/20',
'192.0.32.0/24', '192.0.33.0/29',
'192.0.33.8/30', '192.0.33.12/31',
'192.0.33.14/32'
]
用途
CIDR計算がいちいちめんどくさいのでchrome拡張作りたかった時とか。
おわり。