paraemu
Version:
An working enviroment that provides a simplified interface for developers who want to fully parallelize node execution
215 lines (168 loc) • 5.67 kB
JavaScript
(()=>{
"use strict";
const util = require( 'util' );
const crypto = require( 'crypto' );
// See http://www.isthe.com/chongo/tech/comp/fnv/#FNV-param for the definition of these parameters;
const FNV_PRIME_HIGH = 0x0100, FNV_PRIME_LOW = 0x0193; // 16777619 0x01000193
const OFFSET_BASIS = Buffer.from([0xC5, 0x9D, 0x1C, 0x81]); // 2166136261 [0x81, 0x1C, 0x9D, 0xC5]
const FNV1A32 = ___FNV1A32.bind(null, Buffer.alloc(4));
const FNV1A24 = ___FNV1A24.bind(null, Buffer.alloc(4));
const HOST_NAME = require('os').hostname;
const MACHINE_ID = FNV1A24(HOST_NAME);
const HEX_FMT_CHECK = /^[0-9a-fA-F]{24}$/;
let SEQ_NUMBER = (crypto.randomBytes(4).readUInt32BE(0) & 0xFFFFFF);
const _PROPS = new WeakMap();
class ObjectId {
constructor(id=null) {
const PROPS = {};
_PROPS.set(this, PROPS);
// region [ Default Constructor ]
if ( id === null || typeof id === 'number' ) {
PROPS._rawId = ___GEN_OBJECT_ID(id);
PROPS._hexId = PROPS._rawId.toString( 'hex' );
PROPS._b64urlId = PROPS._rawId.toString( 'base64' ).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
return;
}
// endregion
// region [ Copy Constructor ]
if ( id instanceof ObjectId ) {
const OTHER = _PROPS.get(id);
PROPS._rawId = Buffer.from(OTHER._rawId);
PROPS._hexId = OTHER._hexId;
PROPS._b64urlId = OTHER._b64urlId;
return;
}
// endregion
// region [ Cast other type or error... ]
const RAW_ID = ___CAST_OBJECT_ID(id);
if ( RAW_ID === null ) {
throw new TypeError(
'Argument passed in must be either a buffer, a number, a date object, a binary string of 12 bytes or a hex string with 24 characters.'
);
}
PROPS._rawId = RAW_ID;
PROPS._hexId = PROPS._rawId.toString( 'hex' );
PROPS._b64urlId = PROPS._rawId.toString( 'base64' ).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
// endregion
}
toString(type='hex') {
const PROPS = _PROPS.get(this);
switch ( type ) {
case "raw":
return this.buffer;
case "base64":
return PROPS._rawId.toString('base64');
case "base64url":
return PROPS._b64urlId;
case "hex":
default:
return PROPS._hexId;
}
}
toJSON(type='hex') {
return this.toString(type);
}
equals(other) {
const type = typeof other;
if ( other === null || type === "number" || other instanceof Date ) {
return false;
}
const PROPS = _PROPS.get(this);
if ( other instanceof ObjectId) {
const OTHER = _PROPS.get(other);
return (OTHER._rawId.compare(PROPS._rawId) === 0);
}
const OTHER = ___CAST_OBJECT_ID(other);
if ( other === null ) { return false; }
return (OTHER.compare(PROPS._rawId) === 0);
}
get time() {
const PROPS = _PROPS.get(this);
return _PROPS._rawId.readUInt32BE(0);
}
set time(time) {
if ( time instanceof Date) {
time = (time.getTime()/1000)|0;
}
if ( typeof time !== "number" ) {
throw new TypeError(
"time property accepts only a number or date object"
);
}
const PROPS = _PROPS.get(this);
PROPS._rawId.writeUInt32BE(time, 0);
}
get buffer() {
const PROPS = _PROPS.get(this);
return Buffer.from(PROPS._rawId);
}
[util.inspect.custom]() {
return `ObjectID( ${this.toString('hex')} )`;
}
}
module.exports = ObjectId;
Object.assign(ObjectId, {
fnv1a24: FNV1A24,
fnv1a32: FNV1A32,
GenObjectId:(id=null)=>{
return new ObjectId(id);
}
});
function ___FNV1A32(HASH_RESULT, input, encoding='utf8'){
let octets = input;
if ( !Buffer.isBuffer(input) ) {
octets = Buffer.from(input, encoding);
}
OFFSET_BASIS.copy(HASH_RESULT);
for( let i = 0; i < octets.length; i += 1 ) {
HASH_RESULT[0] = (HASH_RESULT[0] ^ octets[i]) >>> 0;
let hash_low = HASH_RESULT.readUInt16LE(0);
let hash_high = HASH_RESULT.readUInt16LE(2);
let new_low = hash_low * FNV_PRIME_LOW;
let new_high = hash_low * FNV_PRIME_HIGH + hash_high * FNV_PRIME_LOW + + (new_low>>>16);
HASH_RESULT.writeUInt16LE((new_low & 0xFFFF)>>>0, 0);
HASH_RESULT.writeUInt16LE((new_high & 0xFFFF)>>>0, 2);
}
return HASH_RESULT.readUInt32LE(0);
}
function ___FNV1A24(HASH_RESULT, input, encoding){
const _32bit = ___FNV1A32(HASH_RESULT, input, encoding);
const base = _32bit & 0xFFFFFF;
const top = (_32bit >>> 24) & 0xFF;
return (base ^ top) & 0xFFFFFF;
}
function ___GEN_OBJECT_ID(init=null) {
const time = init || ((Date.now()/1000)|0);
const pid = process.pid % 0xFFFF;
const inc = (SEQ_NUMBER=(SEQ_NUMBER+1) % 0xffffff);
const buffer = Buffer.alloc(12);
// region [ Fill in the id information ]
// Fill in the info in reversed order to prevent byte-wise assignments
buffer.writeUInt32BE(inc, 8); // [9-11] seq
buffer.writeUInt16BE(pid, 7); // [7-8] pid
buffer.writeUInt32BE(MACHINE_ID, 3); // [4-6] machine id
buffer.writeUInt32BE(time, 0); // [0-3] epoch time
// endregion
return buffer;
}
function ___CAST_OBJECT_ID(candidate) {
if ( Buffer.isBuffer(candidate) && candidate.length === 12 ) {
return Buffer.from(candidate.slice(0, 12));
}
const type = typeof candidate;
if ( type === "string" ) {
if ( candidate.length === 12 ) {
return Buffer.from(candidate, 'binary');
}
else
if ( candidate.length === 24 && HEX_FMT_CHECK.test(id) ) {
return Buffer.from(candidate, 'hex');
}
return null;
}
if ( candidate instanceof Date ) { candidate = candidate.getTime(); }
if ( candidate === null || type === "number" ) {
return ___GEN_OBJECT_ID(candidate);
}
}
})();