Files
genieacs/lib/common.coffee
T
Zaid Abdulla 6a9c62b639 Support time discretization in scripts
This can be used to ensure parameter refresh times are aligned (as
opposed to using Date.now() - X). It also allows for possible future
optimizations by exploiting the fact that the value of Date.now(X)
remains static for a given amount of time.

The variance value can be used to shift the discretization threshold
points based on the hash of the device ID for load distribution.

The djb2 hashing algorithm has been replaced with MD5 for better
distribution.
2017-11-10 14:34:24 -08:00

240 lines
5.9 KiB
CoffeeScript

###
# Copyright 2013-2017 Zaid Abdulla
#
# This file is part of GenieACS.
#
# GenieACS is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# GenieACS is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with GenieACS. If not, see <http://www.gnu.org/licenses/>.
###
buffer = require 'buffer'
UNDEFINED_TYPE = '[object Undefined]'
NULL_TYPE = '[object Null]'
BOOLEAN_TYPE = '[object Boolean]'
NUMBER_TYPE = '[object Number]'
STRING_TYPE = '[object String]'
ARRAY_TYPE = '[object Array]'
OBJECT_TYPE = '[object Object]'
REGEXP_TYPE = '[object RegExp]'
DATE_TYPE = '[object Date]'
typeOf = (obj) ->
Object.prototype.toString.call(obj)
exports.generateDeviceId = (deviceIdStruct) ->
# Percent escaping function. Escapes everything except alphanumerics and underscore
esc = (str) ->
str.replace(/[^A-Za-z0-9_]/g, (chr) ->
buf = new buffer.Buffer(chr, 'utf8')
rep = ''
rep += "%#{b.toString(16).toUpperCase()}" for b in buf
return rep
)
# Guaranteeing globally unique id as defined in TR-069
if deviceIdStruct['ProductClass']
return "#{esc(deviceIdStruct['OUI'])}-#{esc(deviceIdStruct['ProductClass'])}-#{esc(deviceIdStruct['SerialNumber'])}"
return "#{esc(deviceIdStruct['OUI'])}-#{esc(deviceIdStruct['SerialNumber'])}"
exports.getParamValueFromPath = (obj, path) ->
pp = path.split('.')
ref = obj
try
for p in pp
ref = ref[p]
return ref
catch err
return undefined
pathOverlap = (a, b, start) ->
if a.length == b.length
res = 3
else if a.length > b.length
res = 2
else
res = 1
for i in [(start ? 0)...Math.min(a.length, b.length)] by 1
if a[i]?
if not b[i]?
res &= 2
else if a[i] != b[i]
return 0
else if b[i]?
res &= 1
return 0 if not res
return res
parseAlias = (pattern, start, res) ->
aliases = []
i = start
while i < pattern.length and pattern[i] != ']'
alias = []
i = j = parsePath(pattern, i, alias) + 1
while pattern[j] not in [']', ',']
if pattern[j] == '"' and i == j
++ j
while pattern[j] != '"' or pattern[j - 1] == '\\'
if ++ j >= pattern.length
throw new Error('Invalid alias expression')
if ++ j >= pattern.length
throw new Error('Invalid alias expression')
value = pattern[i...j].trim()
i = j
if value[0] == '"'
try
value = JSON.parse(value)
catch err
throw new Error('Invalid alias expression')
alias.push(value)
aliases.push(alias)
++ i if pattern[i] == ','
# Need to sort to ensure identical expressions have idential string representation
srt = (a, b) ->
for j in [0...Math.min(a.length, b.length)] by 2
for k in [0...Math.min(a[j].length, b[j].length)] by 1
if Array.isArray(a[j][k])
if Array.isArray(b[j][k])
return srt(a[j][k], b[j][k])
else if not b[j][k]?
return -1
else
return 1
else if not a[j][k]?
if not b[j][k]?
return 0
else
return 1
else if not b[j][k]? or Array.isArray(b[j][k])
return -1
else if a[j][k] > b[j][k]
return 1
else if a[j][k] < b[j][k]
return -1
if a[j].length > b[j].length
return -1
else if a[j].length < b[j].length
return 1
if a[j + 1] > b[j + 1]
return -1
else if a[j + 1] < b[j + 1]
return 1
if a.length > b.length
return -1
else if a.length < b.length
return 1
return 0
aliases.sort(srt)
res.push([].concat.apply([], aliases))
return i
parsePath = (pattern, start, res) ->
path = []
path.wildcard = 0
path.alias = 0
i = start ? 0
# Colon separator is needed for parseAlias
if i < pattern.length and pattern[i] != ':'
while true
if pattern[i] == '['
path.alias |= 1 << path.length
i = parseAlias(pattern, i + 1, path) + 1
else
j = i
while i < pattern.length and pattern[i] != ':' and pattern[i] != '.'
++ i
n = pattern.slice(j, i)
path.wildcard |= 1 << path.length if n == '*'
path.push(n)
if i >= pattern.length or pattern[i] == ':'
break
else if pattern[i] != '.'
throw new Error('Invalid alias expression')
++ i
Object.freeze(path)
if not res?
return path
res.push(path)
return i
addPathMeta = (path) ->
return path if path.alias? or path.wildcard?
path.alias = 0
path.wildcard = 0
for p, i in path
if typeOf(p) is ARRAY_TYPE
path.alias |= 1 << i
for j in [0...p.length] by 2
addPathMeta(p[j])
else if p == '*'
path.wildcard |= 1 << i
Object.freeze(path)
return path
hammingWeight = (flags) ->
flags -= ((flags >> 1) & 0x55555555)
flags = (((flags >> 2) & 0x33333333) + (flags & 0x33333333))
flags = (((flags >> 4) + flags) & 0x0f0f0f0f)
flags += (flags >> 8)
flags += (flags >> 16)
return flags & 0x0000003f
# Source: http://stackoverflow.com/a/6969486
escapeRegExp = (str) ->
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")
exports.UNDEFINED_TYPE = UNDEFINED_TYPE
exports.NULL_TYPE = NULL_TYPE
exports.NUMBER_TYPE = NUMBER_TYPE
exports.STRING_TYPE = STRING_TYPE
exports.ARRAY_TYPE = ARRAY_TYPE
exports.OBJECT_TYPE = OBJECT_TYPE
exports.REGEXP_TYPE = REGEXP_TYPE
exports.typeOf = typeOf
exports.pathOverlap = pathOverlap
exports.parsePath = parsePath
exports.addPathMeta = addPathMeta
exports.hammingWeight = hammingWeight
exports.escapeRegExp = escapeRegExp