| 1 | const v4Regex = /^(\d{1,3}\.){3,3}\d{1,3}$/
|
|---|
| 2 | const v4Size = 4
|
|---|
| 3 | const v6Regex = /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i
|
|---|
| 4 | const v6Size = 16
|
|---|
| 5 |
|
|---|
| 6 | export const v4 = {
|
|---|
| 7 | name: 'v4',
|
|---|
| 8 | size: v4Size,
|
|---|
| 9 | isFormat: ip => v4Regex.test(ip),
|
|---|
| 10 | encode (ip, buff, offset) {
|
|---|
| 11 | offset = ~~offset
|
|---|
| 12 | buff = buff || new Uint8Array(offset + v4Size)
|
|---|
| 13 | const max = ip.length
|
|---|
| 14 | let n = 0
|
|---|
| 15 | for (let i = 0; i < max;) {
|
|---|
| 16 | const c = ip.charCodeAt(i++)
|
|---|
| 17 | if (c === 46) { // "."
|
|---|
| 18 | buff[offset++] = n
|
|---|
| 19 | n = 0
|
|---|
| 20 | } else {
|
|---|
| 21 | n = n * 10 + (c - 48)
|
|---|
| 22 | }
|
|---|
| 23 | }
|
|---|
| 24 | buff[offset] = n
|
|---|
| 25 | return buff
|
|---|
| 26 | },
|
|---|
| 27 | decode (buff, offset) {
|
|---|
| 28 | offset = ~~offset
|
|---|
| 29 | return `${buff[offset++]}.${buff[offset++]}.${buff[offset++]}.${buff[offset]}`
|
|---|
| 30 | }
|
|---|
| 31 | }
|
|---|
| 32 |
|
|---|
| 33 | export const v6 = {
|
|---|
| 34 | name: 'v6',
|
|---|
| 35 | size: v6Size,
|
|---|
| 36 | isFormat: ip => ip.length > 0 && v6Regex.test(ip),
|
|---|
| 37 | encode (ip, buff, offset) {
|
|---|
| 38 | offset = ~~offset
|
|---|
| 39 | let end = offset + v6Size
|
|---|
| 40 | let fill = -1
|
|---|
| 41 | let hexN = 0
|
|---|
| 42 | let decN = 0
|
|---|
| 43 | let prevColon = true
|
|---|
| 44 | let useDec = false
|
|---|
| 45 | buff = buff || new Uint8Array(offset + v6Size)
|
|---|
| 46 | // Note: This algorithm needs to check if the offset
|
|---|
| 47 | // could exceed the buffer boundaries as it supports
|
|---|
| 48 | // non-standard compliant encodings that may go beyond
|
|---|
| 49 | // the boundary limits. if (offset < end) checks should
|
|---|
| 50 | // not be necessary...
|
|---|
| 51 | for (let i = 0; i < ip.length; i++) {
|
|---|
| 52 | let c = ip.charCodeAt(i)
|
|---|
| 53 | if (c === 58) { // :
|
|---|
| 54 | if (prevColon) {
|
|---|
| 55 | if (fill !== -1) {
|
|---|
| 56 | // Not Standard! (standard doesn't allow multiple ::)
|
|---|
| 57 | // We need to treat
|
|---|
| 58 | if (offset < end) buff[offset] = 0
|
|---|
| 59 | if (offset < end - 1) buff[offset + 1] = 0
|
|---|
| 60 | offset += 2
|
|---|
| 61 | } else if (offset < end) {
|
|---|
| 62 | // :: in the middle
|
|---|
| 63 | fill = offset
|
|---|
| 64 | }
|
|---|
| 65 | } else {
|
|---|
| 66 | // : ends the previous number
|
|---|
| 67 | if (useDec === true) {
|
|---|
| 68 | // Non-standard! (ipv4 should be at end only)
|
|---|
| 69 | // A ipv4 address should not be found anywhere else but at
|
|---|
| 70 | // the end. This codec also support putting characters
|
|---|
| 71 | // after the ipv4 address..
|
|---|
| 72 | if (offset < end) buff[offset] = decN
|
|---|
| 73 | offset++
|
|---|
| 74 | } else {
|
|---|
| 75 | if (offset < end) buff[offset] = hexN >> 8
|
|---|
| 76 | if (offset < end - 1) buff[offset + 1] = hexN & 0xff
|
|---|
| 77 | offset += 2
|
|---|
| 78 | }
|
|---|
| 79 | hexN = 0
|
|---|
| 80 | decN = 0
|
|---|
| 81 | }
|
|---|
| 82 | prevColon = true
|
|---|
| 83 | useDec = false
|
|---|
| 84 | } else if (c === 46) { // . indicates IPV4 notation
|
|---|
| 85 | if (offset < end) buff[offset] = decN
|
|---|
| 86 | offset++
|
|---|
| 87 | decN = 0
|
|---|
| 88 | hexN = 0
|
|---|
| 89 | prevColon = false
|
|---|
| 90 | useDec = true
|
|---|
| 91 | } else {
|
|---|
| 92 | prevColon = false
|
|---|
| 93 | if (c >= 97) {
|
|---|
| 94 | c -= 87 // a-f ... 97~102 -87 => 10~15
|
|---|
| 95 | } else if (c >= 65) {
|
|---|
| 96 | c -= 55 // A-F ... 65~70 -55 => 10~15
|
|---|
| 97 | } else {
|
|---|
| 98 | c -= 48 // 0-9 ... starting from charCode 48
|
|---|
| 99 | decN = decN * 10 + c
|
|---|
| 100 | }
|
|---|
| 101 | // We don't know yet if its a dec or hex number
|
|---|
| 102 | hexN = (hexN << 4) + c
|
|---|
| 103 | }
|
|---|
| 104 | }
|
|---|
| 105 | if (prevColon === false) {
|
|---|
| 106 | // Commiting last number
|
|---|
| 107 | if (useDec === true) {
|
|---|
| 108 | if (offset < end) buff[offset] = decN
|
|---|
| 109 | offset++
|
|---|
| 110 | } else {
|
|---|
| 111 | if (offset < end) buff[offset] = hexN >> 8
|
|---|
| 112 | if (offset < end - 1) buff[offset + 1] = hexN & 0xff
|
|---|
| 113 | offset += 2
|
|---|
| 114 | }
|
|---|
| 115 | } else if (fill === 0) {
|
|---|
| 116 | // Not Standard! (standard doesn't allow multiple ::)
|
|---|
| 117 | // This means that a : was found at the start AND end which means the
|
|---|
| 118 | // end needs to be treated as 0 entry...
|
|---|
| 119 | if (offset < end) buff[offset] = 0
|
|---|
| 120 | if (offset < end - 1) buff[offset + 1] = 0
|
|---|
| 121 | offset += 2
|
|---|
| 122 | } else if (fill !== -1) {
|
|---|
| 123 | // Non-standard! (standard doens't allow multiple ::)
|
|---|
| 124 | // Here we find that there has been a :: somewhere in the middle
|
|---|
| 125 | // and the end. To treat the end with priority we need to move all
|
|---|
| 126 | // written data two bytes to the right.
|
|---|
| 127 | offset += 2
|
|---|
| 128 | for (let i = Math.min(offset - 1, end - 1); i >= fill + 2; i--) {
|
|---|
| 129 | buff[i] = buff[i - 2]
|
|---|
| 130 | }
|
|---|
| 131 | buff[fill] = 0
|
|---|
| 132 | buff[fill + 1] = 0
|
|---|
| 133 | fill = offset
|
|---|
| 134 | }
|
|---|
| 135 | if (fill !== offset && fill !== -1) {
|
|---|
| 136 | // Move the written numbers to the end while filling the everything
|
|---|
| 137 | // "fill" to the bytes with zeros.
|
|---|
| 138 | if (offset > end - 2) {
|
|---|
| 139 | // Non Standard support, when the cursor exceeds bounds.
|
|---|
| 140 | offset = end - 2
|
|---|
| 141 | }
|
|---|
| 142 | while (end > fill) {
|
|---|
| 143 | buff[--end] = offset < end && offset > fill ? buff[--offset] : 0
|
|---|
| 144 | }
|
|---|
| 145 | } else {
|
|---|
| 146 | // Fill the rest with zeros
|
|---|
| 147 | while (offset < end) {
|
|---|
| 148 | buff[offset++] = 0
|
|---|
| 149 | }
|
|---|
| 150 | }
|
|---|
| 151 | return buff
|
|---|
| 152 | },
|
|---|
| 153 | decode (buff, offset) {
|
|---|
| 154 | offset = ~~offset
|
|---|
| 155 | let result = ''
|
|---|
| 156 | for (let i = 0; i < v6Size; i += 2) {
|
|---|
| 157 | if (i !== 0) {
|
|---|
| 158 | result += ':'
|
|---|
| 159 | }
|
|---|
| 160 | result += (buff[offset + i] << 8 | buff[offset + i + 1]).toString(16)
|
|---|
| 161 | }
|
|---|
| 162 | return result
|
|---|
| 163 | .replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3')
|
|---|
| 164 | .replace(/:{3,4}/, '::')
|
|---|
| 165 | }
|
|---|
| 166 | }
|
|---|
| 167 |
|
|---|
| 168 | export const name = 'ip'
|
|---|
| 169 | export function sizeOf (ip) {
|
|---|
| 170 | if (v4.isFormat(ip)) return v4.size
|
|---|
| 171 | if (v6.isFormat(ip)) return v6.size
|
|---|
| 172 | throw Error(`Invalid ip address: ${ip}`)
|
|---|
| 173 | }
|
|---|
| 174 |
|
|---|
| 175 | export function familyOf (string) {
|
|---|
| 176 | return sizeOf(string) === v4.size ? 1 : 2
|
|---|
| 177 | }
|
|---|
| 178 |
|
|---|
| 179 | export function encode (ip, buff, offset) {
|
|---|
| 180 | offset = ~~offset
|
|---|
| 181 | const size = sizeOf(ip)
|
|---|
| 182 | if (typeof buff === 'function') {
|
|---|
| 183 | buff = buff(offset + size)
|
|---|
| 184 | }
|
|---|
| 185 | if (size === v4.size) {
|
|---|
| 186 | return v4.encode(ip, buff, offset)
|
|---|
| 187 | }
|
|---|
| 188 | return v6.encode(ip, buff, offset)
|
|---|
| 189 | }
|
|---|
| 190 |
|
|---|
| 191 | export function decode (buff, offset, length) {
|
|---|
| 192 | offset = ~~offset
|
|---|
| 193 | length = length || (buff.length - offset)
|
|---|
| 194 | if (length === v4.size) {
|
|---|
| 195 | return v4.decode(buff, offset, length)
|
|---|
| 196 | }
|
|---|
| 197 | if (length === v6.size) {
|
|---|
| 198 | return v6.decode(buff, offset, length)
|
|---|
| 199 | }
|
|---|
| 200 | throw Error(`Invalid buffer size needs to be ${v4.size} for v4 or ${v6.size} for v6.`)
|
|---|
| 201 | }
|
|---|