CoolFace
Apppublic

AK-21/Graphite-Industrial-Intelligence

sourceHugging Faceupdated 3mo agoView on Hugging Face
0likes
index.js454 linesDownload Raw Back to object-hash
1'use strict';2 3var crypto = require('crypto');4 5/**6 * Exported function7 *8 * Options:9 *10 *  - `algorithm` hash algo to be used by this instance: *'sha1', 'md5'11 *  - `excludeValues` {true|*false} hash object keys, values ignored12 *  - `encoding` hash encoding, supports 'buffer', '*hex', 'binary', 'base64'13 *  - `ignoreUnknown` {true|*false} ignore unknown object types14 *  - `replacer` optional function that replaces values before hashing15 *  - `respectFunctionProperties` {*true|false} consider function properties when hashing16 *  - `respectFunctionNames` {*true|false} consider 'name' property of functions for hashing17 *  - `respectType` {*true|false} Respect special properties (prototype, constructor)18 *    when hashing to distinguish between types19 *  - `unorderedArrays` {true|*false} Sort all arrays before hashing20 *  - `unorderedSets` {*true|false} Sort `Set` and `Map` instances before hashing21 *  * = default22 *23 * @param {object} object value to hash24 * @param {object} options hashing options25 * @return {string} hash value26 * @api public27 */28exports = module.exports = objectHash;29 30function objectHash(object, options){31  options = applyDefaults(object, options);32 33  return hash(object, options);34}35 36/**37 * Exported sugar methods38 *39 * @param {object} object value to hash40 * @return {string} hash value41 * @api public42 */43exports.sha1 = function(object){44  return objectHash(object);45};46exports.keys = function(object){47  return objectHash(object, {excludeValues: true, algorithm: 'sha1', encoding: 'hex'});48};49exports.MD5 = function(object){50  return objectHash(object, {algorithm: 'md5', encoding: 'hex'});51};52exports.keysMD5 = function(object){53  return objectHash(object, {algorithm: 'md5', encoding: 'hex', excludeValues: true});54};55 56// Internals57var hashes = crypto.getHashes ? crypto.getHashes().slice() : ['sha1', 'md5'];58hashes.push('passthrough');59var encodings = ['buffer', 'hex', 'binary', 'base64'];60 61function applyDefaults(object, sourceOptions){62  sourceOptions = sourceOptions || {};63 64  // create a copy rather than mutating65  var options = {};66  options.algorithm = sourceOptions.algorithm || 'sha1';67  options.encoding = sourceOptions.encoding || 'hex';68  options.excludeValues = sourceOptions.excludeValues ? true : false;69  options.algorithm = options.algorithm.toLowerCase();70  options.encoding = options.encoding.toLowerCase();71  options.ignoreUnknown = sourceOptions.ignoreUnknown !== true ? false : true; // default to false72  options.respectType = sourceOptions.respectType === false ? false : true; // default to true73  options.respectFunctionNames = sourceOptions.respectFunctionNames === false ? false : true;74  options.respectFunctionProperties = sourceOptions.respectFunctionProperties === false ? false : true;75  options.unorderedArrays = sourceOptions.unorderedArrays !== true ? false : true; // default to false76  options.unorderedSets = sourceOptions.unorderedSets === false ? false : true; // default to false77  options.unorderedObjects = sourceOptions.unorderedObjects === false ? false : true; // default to true78  options.replacer = sourceOptions.replacer || undefined;79  options.excludeKeys = sourceOptions.excludeKeys || undefined;80 81  if(typeof object === 'undefined') {82    throw new Error('Object argument required.');83  }84 85  // if there is a case-insensitive match in the hashes list, accept it86  // (i.e. SHA256 for sha256)87  for (var i = 0; i < hashes.length; ++i) {88    if (hashes[i].toLowerCase() === options.algorithm.toLowerCase()) {89      options.algorithm = hashes[i];90    }91  }92 93  if(hashes.indexOf(options.algorithm) === -1){94    throw new Error('Algorithm "' + options.algorithm + '"  not supported. ' +95      'supported values: ' + hashes.join(', '));96  }97 98  if(encodings.indexOf(options.encoding) === -1 &&99     options.algorithm !== 'passthrough'){100    throw new Error('Encoding "' + options.encoding + '"  not supported. ' +101      'supported values: ' + encodings.join(', '));102  }103 104  return options;105}106 107/** Check if the given function is a native function */108function isNativeFunction(f) {109  if ((typeof f) !== 'function') {110    return false;111  }112  var exp = /^function\s+\w*\s*\(\s*\)\s*{\s+\[native code\]\s+}$/i;113  return exp.exec(Function.prototype.toString.call(f)) != null;114}115 116function hash(object, options) {117  var hashingStream;118 119  if (options.algorithm !== 'passthrough') {120    hashingStream = crypto.createHash(options.algorithm);121  } else {122    hashingStream = new PassThrough();123  }124 125  if (typeof hashingStream.write === 'undefined') {126    hashingStream.write = hashingStream.update;127    hashingStream.end   = hashingStream.update;128  }129 130  var hasher = typeHasher(options, hashingStream);131  hasher.dispatch(object);132  if (!hashingStream.update) {133    hashingStream.end('');134  }135 136  if (hashingStream.digest) {137    return hashingStream.digest(options.encoding === 'buffer' ? undefined : options.encoding);138  }139 140  var buf = hashingStream.read();141  if (options.encoding === 'buffer') {142    return buf;143  }144 145  return buf.toString(options.encoding);146}147 148/**149 * Expose streaming API150 *151 * @param {object} object  Value to serialize152 * @param {object} options  Options, as for hash()153 * @param {object} stream  A stream to write the serializiation to154 * @api public155 */156exports.writeToStream = function(object, options, stream) {157  if (typeof stream === 'undefined') {158    stream = options;159    options = {};160  }161 162  options = applyDefaults(object, options);163 164  return typeHasher(options, stream).dispatch(object);165};166 167function typeHasher(options, writeTo, context){168  context = context || [];169  var write = function(str) {170    if (writeTo.update) {171      return writeTo.update(str, 'utf8');172    } else {173      return writeTo.write(str, 'utf8');174    }175  };176 177  return {178    dispatch: function(value){179      if (options.replacer) {180        value = options.replacer(value);181      }182 183      var type = typeof value;184      if (value === null) {185        type = 'null';186      }187 188      //console.log("[DEBUG] Dispatch: ", value, "->", type, " -> ", "_" + type);189 190      return this['_' + type](value);191    },192    _object: function(object) {193      var pattern = (/\[object (.*)\]/i);194      var objString = Object.prototype.toString.call(object);195      var objType = pattern.exec(objString);196      if (!objType) { // object type did not match [object ...]197        objType = 'unknown:[' + objString + ']';198      } else {199        objType = objType[1]; // take only the class name200      }201 202      objType = objType.toLowerCase();203 204      var objectNumber = null;205 206      if ((objectNumber = context.indexOf(object)) >= 0) {207        return this.dispatch('[CIRCULAR:' + objectNumber + ']');208      } else {209        context.push(object);210      }211 212      if (typeof Buffer !== 'undefined' && Buffer.isBuffer && Buffer.isBuffer(object)) {213        write('buffer:');214        return write(object);215      }216 217      if(objType !== 'object' && objType !== 'function' && objType !== 'asyncfunction') {218        if(this['_' + objType]) {219          this['_' + objType](object);220        } else if (options.ignoreUnknown) {221          return write('[' + objType + ']');222        } else {223          throw new Error('Unknown object type "' + objType + '"');224        }225      }else{226        var keys = Object.keys(object);227        if (options.unorderedObjects) {228          keys = keys.sort();229        }230        // Make sure to incorporate special properties, so231        // Types with different prototypes will produce232        // a different hash and objects derived from233        // different functions (`new Foo`, `new Bar`) will234        // produce different hashes.235        // We never do this for native functions since some236        // seem to break because of that.237        if (options.respectType !== false && !isNativeFunction(object)) {238          keys.splice(0, 0, 'prototype', '__proto__', 'constructor');239        }240 241        if (options.excludeKeys) {242          keys = keys.filter(function(key) { return !options.excludeKeys(key); });243        }244 245        write('object:' + keys.length + ':');246        var self = this;247        return keys.forEach(function(key){248          self.dispatch(key);249          write(':');250          if(!options.excludeValues) {251            self.dispatch(object[key]);252          }253          write(',');254        });255      }256    },257    _array: function(arr, unordered){258      unordered = typeof unordered !== 'undefined' ? unordered :259        options.unorderedArrays !== false; // default to options.unorderedArrays260 261      var self = this;262      write('array:' + arr.length + ':');263      if (!unordered || arr.length <= 1) {264        return arr.forEach(function(entry) {265          return self.dispatch(entry);266        });267      }268 269      // the unordered case is a little more complicated:270      // since there is no canonical ordering on objects,271      // i.e. {a:1} < {a:2} and {a:1} > {a:2} are both false,272      // we first serialize each entry using a PassThrough stream273      // before sorting.274      // also: we can’t use the same context array for all entries275      // since the order of hashing should *not* matter. instead,276      // we keep track of the additions to a copy of the context array277      // and add all of them to the global context array when we’re done278      var contextAdditions = [];279      var entries = arr.map(function(entry) {280        var strm = new PassThrough();281        var localContext = context.slice(); // make copy282        var hasher = typeHasher(options, strm, localContext);283        hasher.dispatch(entry);284        // take only what was added to localContext and append it to contextAdditions285        contextAdditions = contextAdditions.concat(localContext.slice(context.length));286        return strm.read().toString();287      });288      context = context.concat(contextAdditions);289      entries.sort();290      return this._array(entries, false);291    },292    _date: function(date){293      return write('date:' + date.toJSON());294    },295    _symbol: function(sym){296      return write('symbol:' + sym.toString());297    },298    _error: function(err){299      return write('error:' + err.toString());300    },301    _boolean: function(bool){302      return write('bool:' + bool.toString());303    },304    _string: function(string){305      write('string:' + string.length + ':');306      write(string.toString());307    },308    _function: function(fn){309      write('fn:');310      if (isNativeFunction(fn)) {311        this.dispatch('[native]');312      } else {313        this.dispatch(fn.toString());314      }315 316      if (options.respectFunctionNames !== false) {317        // Make sure we can still distinguish native functions318        // by their name, otherwise String and Function will319        // have the same hash320        this.dispatch("function-name:" + String(fn.name));321      }322 323      if (options.respectFunctionProperties) {324        this._object(fn);325      }326    },327    _number: function(number){328      return write('number:' + number.toString());329    },330    _xml: function(xml){331      return write('xml:' + xml.toString());332    },333    _null: function() {334      return write('Null');335    },336    _undefined: function() {337      return write('Undefined');338    },339    _regexp: function(regex){340      return write('regex:' + regex.toString());341    },342    _uint8array: function(arr){343      write('uint8array:');344      return this.dispatch(Array.prototype.slice.call(arr));345    },346    _uint8clampedarray: function(arr){347      write('uint8clampedarray:');348      return this.dispatch(Array.prototype.slice.call(arr));349    },350    _int8array: function(arr){351      write('int8array:');352      return this.dispatch(Array.prototype.slice.call(arr));353    },354    _uint16array: function(arr){355      write('uint16array:');356      return this.dispatch(Array.prototype.slice.call(arr));357    },358    _int16array: function(arr){359      write('int16array:');360      return this.dispatch(Array.prototype.slice.call(arr));361    },362    _uint32array: function(arr){363      write('uint32array:');364      return this.dispatch(Array.prototype.slice.call(arr));365    },366    _int32array: function(arr){367      write('int32array:');368      return this.dispatch(Array.prototype.slice.call(arr));369    },370    _float32array: function(arr){371      write('float32array:');372      return this.dispatch(Array.prototype.slice.call(arr));373    },374    _float64array: function(arr){375      write('float64array:');376      return this.dispatch(Array.prototype.slice.call(arr));377    },378    _arraybuffer: function(arr){379      write('arraybuffer:');380      return this.dispatch(new Uint8Array(arr));381    },382    _url: function(url) {383      return write('url:' + url.toString(), 'utf8');384    },385    _map: function(map) {386      write('map:');387      var arr = Array.from(map);388      return this._array(arr, options.unorderedSets !== false);389    },390    _set: function(set) {391      write('set:');392      var arr = Array.from(set);393      return this._array(arr, options.unorderedSets !== false);394    },395    _file: function(file) {396      write('file:');397      return this.dispatch([file.name, file.size, file.type, file.lastModfied]);398    },399    _blob: function() {400      if (options.ignoreUnknown) {401        return write('[blob]');402      }403 404      throw Error('Hashing Blob objects is currently not supported\n' +405        '(see https://github.com/puleos/object-hash/issues/26)\n' +406        'Use "options.replacer" or "options.ignoreUnknown"\n');407    },408    _domwindow: function() { return write('domwindow'); },409    _bigint: function(number){410      return write('bigint:' + number.toString());411    },412    /* Node.js standard native objects */413    _process: function() { return write('process'); },414    _timer: function() { return write('timer'); },415    _pipe: function() { return write('pipe'); },416    _tcp: function() { return write('tcp'); },417    _udp: function() { return write('udp'); },418    _tty: function() { return write('tty'); },419    _statwatcher: function() { return write('statwatcher'); },420    _securecontext: function() { return write('securecontext'); },421    _connection: function() { return write('connection'); },422    _zlib: function() { return write('zlib'); },423    _context: function() { return write('context'); },424    _nodescript: function() { return write('nodescript'); },425    _httpparser: function() { return write('httpparser'); },426    _dataview: function() { return write('dataview'); },427    _signal: function() { return write('signal'); },428    _fsevent: function() { return write('fsevent'); },429    _tlswrap: function() { return write('tlswrap'); },430  };431}432 433// Mini-implementation of stream.PassThrough434// We are far from having need for the full implementation, and we can435// make assumptions like "many writes, then only one final read"436// and we can ignore encoding specifics437function PassThrough() {438  return {439    buf: '',440 441    write: function(b) {442      this.buf += b;443    },444 445    end: function(b) {446      this.buf += b;447    },448 449    read: function() {450      return this.buf;451    }452  };453}454