| const v4Regex = /^(\d{1,3}\.){3,3}\d{1,3}$/ |
| const v4Size = 4 |
| const v6Regex = /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i |
| const v6Size = 16 |
|
|
| export const v4 = { |
| name: 'v4', |
| size: v4Size, |
| isFormat: ip => v4Regex.test(ip), |
| encode (ip, buff, offset) { |
| offset = ~~offset |
| buff = buff || new Uint8Array(offset + v4Size) |
| const max = ip.length |
| let n = 0 |
| for (let i = 0; i < max;) { |
| const c = ip.charCodeAt(i++) |
| if (c === 46) { |
| buff[offset++] = n |
| n = 0 |
| } else { |
| n = n * 10 + (c - 48) |
| } |
| } |
| buff[offset] = n |
| return buff |
| }, |
| decode (buff, offset) { |
| offset = ~~offset |
| return `${buff[offset++]}.${buff[offset++]}.${buff[offset++]}.${buff[offset]}` |
| } |
| } |
|
|
| export const v6 = { |
| name: 'v6', |
| size: v6Size, |
| isFormat: ip => ip.length > 0 && v6Regex.test(ip), |
| encode (ip, buff, offset) { |
| offset = ~~offset |
| let end = offset + v6Size |
| let fill = -1 |
| let hexN = 0 |
| let decN = 0 |
| let prevColon = true |
| let useDec = false |
| buff = buff || new Uint8Array(offset + v6Size) |
| |
| |
| |
| |
| |
| for (let i = 0; i < ip.length; i++) { |
| let c = ip.charCodeAt(i) |
| if (c === 58) { |
| if (prevColon) { |
| if (fill !== -1) { |
| |
| |
| if (offset < end) buff[offset] = 0 |
| if (offset < end - 1) buff[offset + 1] = 0 |
| offset += 2 |
| } else if (offset < end) { |
| |
| fill = offset |
| } |
| } else { |
| |
| if (useDec === true) { |
| |
| |
| |
| |
| if (offset < end) buff[offset] = decN |
| offset++ |
| } else { |
| if (offset < end) buff[offset] = hexN >> 8 |
| if (offset < end - 1) buff[offset + 1] = hexN & 0xff |
| offset += 2 |
| } |
| hexN = 0 |
| decN = 0 |
| } |
| prevColon = true |
| useDec = false |
| } else if (c === 46) { |
| if (offset < end) buff[offset] = decN |
| offset++ |
| decN = 0 |
| hexN = 0 |
| prevColon = false |
| useDec = true |
| } else { |
| prevColon = false |
| if (c >= 97) { |
| c -= 87 |
| } else if (c >= 65) { |
| c -= 55 |
| } else { |
| c -= 48 |
| decN = decN * 10 + c |
| } |
| |
| hexN = (hexN << 4) + c |
| } |
| } |
| if (prevColon === false) { |
| |
| if (useDec === true) { |
| if (offset < end) buff[offset] = decN |
| offset++ |
| } else { |
| if (offset < end) buff[offset] = hexN >> 8 |
| if (offset < end - 1) buff[offset + 1] = hexN & 0xff |
| offset += 2 |
| } |
| } else if (fill === 0) { |
| |
| |
| |
| if (offset < end) buff[offset] = 0 |
| if (offset < end - 1) buff[offset + 1] = 0 |
| offset += 2 |
| } else if (fill !== -1) { |
| |
| |
| |
| |
| offset += 2 |
| for (let i = Math.min(offset - 1, end - 1); i >= fill + 2; i--) { |
| buff[i] = buff[i - 2] |
| } |
| buff[fill] = 0 |
| buff[fill + 1] = 0 |
| fill = offset |
| } |
| if (fill !== offset && fill !== -1) { |
| |
| |
| if (offset > end - 2) { |
| |
| offset = end - 2 |
| } |
| while (end > fill) { |
| buff[--end] = offset < end && offset > fill ? buff[--offset] : 0 |
| } |
| } else { |
| |
| while (offset < end) { |
| buff[offset++] = 0 |
| } |
| } |
| return buff |
| }, |
| decode (buff, offset) { |
| offset = ~~offset |
| let result = '' |
| for (let i = 0; i < v6Size; i += 2) { |
| if (i !== 0) { |
| result += ':' |
| } |
| result += (buff[offset + i] << 8 | buff[offset + i + 1]).toString(16) |
| } |
| return result |
| .replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3') |
| .replace(/:{3,4}/, '::') |
| } |
| } |
|
|
| export const name = 'ip' |
| export function sizeOf (ip) { |
| if (v4.isFormat(ip)) return v4.size |
| if (v6.isFormat(ip)) return v6.size |
| throw Error(`Invalid ip address: ${ip}`) |
| } |
|
|
| export function familyOf (string) { |
| return sizeOf(string) === v4.size ? 1 : 2 |
| } |
|
|
| export function encode (ip, buff, offset) { |
| offset = ~~offset |
| const size = sizeOf(ip) |
| if (typeof buff === 'function') { |
| buff = buff(offset + size) |
| } |
| if (size === v4.size) { |
| return v4.encode(ip, buff, offset) |
| } |
| return v6.encode(ip, buff, offset) |
| } |
|
|
| export function decode (buff, offset, length) { |
| offset = ~~offset |
| length = length || (buff.length - offset) |
| if (length === v4.size) { |
| return v4.decode(buff, offset, length) |
| } |
| if (length === v6.size) { |
| return v6.decode(buff, offset, length) |
| } |
| throw Error(`Invalid buffer size needs to be ${v4.size} for v4 or ${v6.size} for v6.`) |
| } |
|
|