Module:IP: Difference between revisions

From the Croc Wiki, the Croc encyclopedia
Jump to navigationJump to search
some more style changes (it's still broken but I'll fix it later; also, sorry for the edit conflict)
(Collection is good (I've always meant to fix it) but in case you don't have other plans, here is a change to make it work)
(some more style changes (it's still broken but I'll fix it later; also, sorry for the edit conflict))
Line 10:
local V4 = 'IPv4'
local V6 = 'IPv6'
 
--------------------------------------------------------------------------------
-- Collection class
-- A table to hold items. Used internally.
--------------------------------------------------------------------------------
 
local Collection
do
local mt = {
__index = {
add = function (self, item)
self.n = self.n + 1
self[self.n] = item
end,
join = function (self, sep)
return table.concat(self, sep)
end,
}
}
Collection = function ()
return setmetatable({n = 0}, mt)
end
end
 
--------------------------------------------------------------------------------
Line 45 ⟶ 22:
RawIP.__index = RawIP
 
do
function RawIP.newFromIPv4(ipStr)
-- Collection class.
-- If ipStr is a valid IPv4 string, return a collection of its parts.
-- This is a table used to hold items.
-- Otherwise, return nil.
local Collection
-- This representation is for compatibility with IPv6 addresses.
do
local octets = Collection()
local mt = {
local s = ipStr:match('^%s*(.-)%s*$') .. '.'
__index = {
for item in s:gmatch('(.-)%.') do
octets: add = function (self, item)
self.n = self.n + 1
self[self.n] = item
end,
join = function (self, sep)
return table.concat(self, sep)
end,
}
}
Collection = function ()
return setmetatable({n = 0}, mt)
end
end
 
if octets.n == 4 then
-- Constructors
for i, s in ipairs(octets) do
function RawIP.newFromIPv4(ipStr)
if s:match('^%d+$') then
-- If ipStr is a valid IPv4 string, return a collection of its parts.
local num = tonumber(s)
-- Otherwise, return nil.
if 0 <= num and num <= 255 then
-- This representation is for compatibility with IPv6 addresses.
if num > 0 and s:match('^0') then
local octets = Collection()
-- A redundant leading zero is for an IP in octal.
local s = ipStr:match('^%s*(.-)%s*$') .. '.'
for item in s:gmatch('(.-)%.') do
octets:add(item)
end
if octets.n == 4 then
for i, s in ipairs(octets) do
if s:match('^%d+$') then
local num = tonumber(s)
if 0 <= num and num <= 255 then
if num > 0 and s:match('^0') then
-- A redundant leading zero is for an IP in octal.
return false
end
octets[i] = num
else
return false
end
octets[i] = num
else
return false
end
else
return false
end
local parts = Collection()
for i = 1, 3, 2 do
parts:add(octets[i] * 256 + octets[i+1])
end
return setmetatable(parts, RawIP)
end
return nil
end
function RawIP.newFromIPv6(ipStr)
-- If ipStr is a valid IPv6 string, return a collection of its parts.
-- Otherwise, return nil.
ipStr = ipStr:match('^%s*(.-)%s*$')
local _, n = ipStr:gsub(':', ':')
if n < 7 then
ipStr, n = ipStr:gsub('::', string.rep(':', 9 - n))
end
local parts = Collection()
for iitem =in 1,(ipStr 3,.. 2':'):gmatch('(.-):') do
parts:add(octets[i] * 256 + octets[i+1]item)
end
returnif setmetatable(parts,.n RawIP)== 8 then
for i, s in ipairs(parts) do
end
if s == '' then
return nil
parts[i] = 0
end
 
function RawIP.newFromIPv6(ipStr)
-- If ipStr is a valid IPv6 string, return a collection of its parts.
-- Otherwise, return nil.
ipStr = ipStr:match('^%s*(.-)%s*$')
local _, n = ipStr:gsub(':', ':')
if n < 7 then
ipStr, n = ipStr:gsub('::', string.rep(':', 9 - n))
end
local parts = Collection()
for item in (ipStr .. ':'):gmatch('(.-):') do
parts:add(item)
end
if parts.n == 8 then
for i, s in ipairs(parts) do
if s == '' then
parts[i] = 0
else
local num = tonumber('0x' .. s)
if num and 0 <= num and num <= 65535 then
parts[i] = num
else
local num = tonumber('0x' .. s)
return false
if num and 0 <= num and num <= 65535 then
parts[i] = num
else
return false
end
end
end
return setmetatable(parts, RawIP)
end
return setmetatable(parts, RawIP)nil
end
return nil
function RawIP:getAdjacent(previous)
end
-- Return a RawIP object for an adjacent IP address. If previous is true
 
-- then the previous IP is returned; otherwise the next IP is returned.
function RawIP:copyChanged(down)
-- Will wraparound:
-- Return a copy of IPv4 or IPv6 parts, incremented or decremented.
-- next 255.255.255.255 → 0.0.0.0
-- Will wraparound:
-- ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff → ::
-- increment 255.255.255.255 → 0.0.0.0
-- previous 0.0.0.0 → 255.255.255.255
-- ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff → ::
-- :: → ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
-- decrement 0.0.0.0 → 255.255.255.255
local result = Collection()
-- :: → ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
local result.n = Collection()self.n
local carry = down and 0xffff or 1
result.n = self.n
for i = self.n, 1, -1 do
local carry = down and 0xffff or 1
for local isum = self.n, 1,[i] -1+ docarry
local if sum >= self[i]0x10000 + carrythen
if carry sum >= down and 0x10000 thenor 1
carry sum = downsum and- 0x10000 or 1
else
sum = sum - 0x10000
carry = down and 0xffff or 0
else
end
carry = down and 0xffff or 0
result[i] = sum
end
return setmetatable(result, RawIP)
result[i] = sum
end
return setmetatable(result, RawIP)
end
 
-- Methods
function RawIP:copyPrefix(length)
function RawIP:getPrefix(length)
-- Return a copy of IPv4 or IPv6 parts, masked to length.
-- Return a copy of IPv4 or IPv6 parts, masked to length.
local result = Collection()
local result.n = self.nCollection()
for i result.n = 1, self.n do
iffor lengthi >= 01, thenself.n do
if length >= 160 then
result[i]if length >= self[i]16 then
length result[i] = length - 16self[i]
length = length - 16
else
result[i] = bit32.band(self[i],
bit32.arshift(0xffff8000, length - 1))
length = 0
end
else
result[i] = bit32.band(self[i],0
bit32.arshift(0xffff8000, length - 1))
length = 0
end
else
result[i] = 0
end
return setmetatable(result, RawIP)
end
return setmetatable(result, RawIP)
end
 
function RawIP:setHostBits(length)
-- Return a copy of IPv4 or IPv6 parts, with the least-significant bits
-- (host bits) set to 1.
-- The most-significant length bits identify the network.
local bits = self.n * 16
local width
if length <= 0 then
width = bits
elseif length >= bits then
width = 0
else
width = bits - length
end
local result = Collection()
result.n = self.n
for i = self.n, 1, -1 do
if width > 0 then
if width >= 16 then
result[i] = 0xffff
width = width - 16
else
result[i] = bit32.replace(self[i], 0xffff, width - 1, width)
width = 0
end
else
result[i] = bit32.replace(self[i], 0xffff, width - 1, width)
width = 0
end
else
result[i] = self[i]
end
return setmetatable(result, rawIP)
end
return setmetatable(result, rawIP)
end
 
function RawIP:_makeIPv6String()
-- Return an IPv6 string representation of the object. Behavior is undefined
-- if the current object is IPv4.
local z1, z2 -- indices of run of zeros to be displayed as "::"
local zstart, zcount
for i = 1, 9 do
-- Find left-most occurrence of longest run of two or more zeros.
if i < 9 and self[i] == 0 then
if zstart then
zcount = zcount + 1
else
zstart = i
zcount = 1
end
else
if zcount and zcount > 1 then
zstart = i
if not z1 or zcount => z2 - z1 + 1 then
z1 = zstart
end
z2 = zstart + zcount - 1
else
end
if zcount and zcount > 1 then
if not z1 or zcount > z2 - z1 + 1 then
z1 = zstart
z2 = zstart + zcount - 1
end
zstart = nil
zcount = nil
end
zstart = nil
zcount = nil
end
local parts = Collection()
end
for i = 1, 8 do
local parts = Collection()
if z1 and z1 <= i and i <= z2 then
for i = 1, 8 do
if z1 and z1 <= i and i <== z2z1 then
if iz1 == z11 or z2 == 8 then
if z1 == 1 orand z2 == 8 then
return '::'
if z1 == 1 and z2 == 8 then
return '::'end
parts:add(':')
else
parts:add('')
end
parts:add(':')
else
parts:add('')
end
else
parts:add(string.format('%x', self[i]))
end
end
return parts:join(':')
end
 
function RawIP:_makeIPv4String()
-- Return an IPv4 string representation of the object. Behavior is undefined
-- if the current object is IPv6.
local parts = Collection()
for i = 1, 2 do
local w = self[i]
parts:add(math.floor(w / 256))
parts:add(w % 256)
end
return parts:join('.')
end
 
function RawIP:__tostring()
-- Return a string equivalent to given IP address (IPv4 or IPv6).
if self.n == 2 then
return self:_makeIPv4String()
else
return self:_makeIPv6String()
parts:add(string.format('%x', self[i]))
end
end
return parts:join(':')
end
 
function RawIP:_makeIPv4String__lt(obj)
if self.n == obj.n then
-- Return an IPv4 string representation of the object. Behavior is undefined
for i = 1, self.n do
-- if the current object is IPv6.
if self[i] ~= obj[i] then
local parts = Collection()
return self[i] < obj[i]
for i = 1, 2 do
end
local w = self[i]
end
parts:add(math.floor(w / 256))
return false
parts:add(w % 256)
end
return self.n < obj.n
end
return parts:join('.')
end
 
function RawIP:__tostring__eq(obj)
if self.n == obj.n then
-- Return a string equivalent to given IP address (IPv4 or IPv6).
for i = 1, self.n do
if self.n == 2 then
return if self:_makeIPv4String()[i] ~= obj[i] then
return false
else
end
return self:_makeIPv6String()
end
return true
end
return false
end
end
Line 258 ⟶ 284:
local IPAddress = {}
 
local newIPFromRaw
-- newIPFromParts constructs a new IPAddress object from its parts. It needs to
-- benewIPFromRaw accessibleconstructs froma thenew SubnetIPAddress object butfrom ita shouldrawIP notobject. beIt public.needs
-- to be accessible from the Subnet class but it should not be public.
local newIPFromParts
 
do
Line 267 ⟶ 293:
-- Metamethods that don't need upvalues
local function ipEquals(ip1, ip2)
localreturn lhsip1[dataKey].rawIP == ip1ip2[dataKey].partsrawIP
local rhs = ip2[dataKey].parts
if lhs.n == rhs.n then
for i = 1, lhs.n do
if lhs[i] ~= rhs[i] then
return false
end
end
return true
end
return false
end
 
local function ipLessThan(ip1, ip2)
localreturn lhsip1[dataKey].rawIP =< ip1ip2[dataKey].partsrawIP
local rhs = ip2[dataKey].parts
if lhs.n == rhs.n then
for i = 1, lhs.n do
if lhs[i] ~= rhs[i] then
return lhs[i] < rhs[i]
end
end
return false
end
return lhs.n < rhs.n
end
 
local function concatIPs(ip1, ip2)
return tostring(ip1:getIP() .. tostring(ip2:getIP()
end
 
local function ipToString(ip)
return ipString(ip[dataKey].parts:getIP()
end
 
-- Constructors
newIPFromPartsnewIPFromRaw = function (partsrawIP)
-- Constructs a new IPAddress object from itsa partsrawIP object. This function is
-- is for internal use; it is called by IPAddress.new and canfrom be calledother
-- fromIPAddress the Subnet classmethods, butand itshould is notbe available to otherthe modules.Subnet class, but
-- should not be available to other modules.
assert(type(parts) == 'table', 'parts was type ' .. type(parts) .. '; expected type table')
assert(type(rawIP) == 'table', 'rawIP was type ' .. type(rawIP) .. '; expected type table')
 
-- Set up structure
local obj = {}
local data = {
partsrawIP = partsrawIP,
version = partsrawIP.n == 2 and V4 or V6,
}
 
-- Public methods
function obj:getIP()
return ipStringtostring(data.partsrawIP)
end
 
Line 326 ⟶ 333:
 
function obj:getHighestIP(bitLength)
return newIPFromPartsnewIPFromRaw(setHostBits(data.partsrawIP, bitLength))
end
 
function obj:getPrefix(bitLength)
return newIPFromPartsnewIPFromRaw(copyPrefix(data.partsrawIP, bitLength))
end
 
Line 352 ⟶ 359:
 
function obj:getNextIP()
return newIPFromParts(copyChangednewIPFromRaw(data.partsrawIP:getAdjacent())
end
 
function obj:getPreviousIP()
return newIPFromParts(copyChangednewIPFromRaw(data.parts, rawIP:getAdjacent(true))
end
 
Line 375 ⟶ 382:
function IPAddress.new(ip)
checkType('IPAddress.new', 1, ip, 'string')
local partsrawIP = parseIPv4RawIP.newFromIPv4String(ip) or parseIPv6RawIP.newFromIPv6String(ip)
if not partsrawIP then
error('invalid IP', 2)
end
return newIPFromPartsnewIPFromRaw(partsrawIP)
end
end
Cookies help us deliver our services. By using our services, you agree to our use of cookies.

Navigation menu