-
-
Save mikelehen/3596a30bd69384624c11 to your computer and use it in GitHub Desktop.
/** | |
* Fancy ID generator that creates 20-character string identifiers with the following properties: | |
* | |
* 1. They're based on timestamp so that they sort *after* any existing ids. | |
* 2. They contain 72-bits of random data after the timestamp so that IDs won't collide with other clients' IDs. | |
* 3. They sort *lexicographically* (so the timestamp is converted to characters that will sort properly). | |
* 4. They're monotonically increasing. Even if you generate more than one in the same timestamp, the | |
* latter ones will sort after the former ones. We do this by using the previous random bits | |
* but "incrementing" them by 1 (only in the case of a timestamp collision). | |
*/ | |
generatePushID = (function() { | |
// Modeled after base64 web-safe chars, but ordered by ASCII. | |
var PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz'; | |
// Timestamp of last push, used to prevent local collisions if you push twice in one ms. | |
var lastPushTime = 0; | |
// We generate 72-bits of randomness which get turned into 12 characters and appended to the | |
// timestamp to prevent collisions with other clients. We store the last characters we | |
// generated because in the event of a collision, we'll use those same characters except | |
// "incremented" by one. | |
var lastRandChars = []; | |
return function() { | |
var now = new Date().getTime(); | |
var duplicateTime = (now === lastPushTime); | |
lastPushTime = now; | |
var timeStampChars = new Array(8); | |
for (var i = 7; i >= 0; i--) { | |
timeStampChars[i] = PUSH_CHARS.charAt(now % 64); | |
// NOTE: Can't use << here because javascript will convert to int and lose the upper bits. | |
now = Math.floor(now / 64); | |
} | |
if (now !== 0) throw new Error('We should have converted the entire timestamp.'); | |
var id = timeStampChars.join(''); | |
if (!duplicateTime) { | |
for (i = 0; i < 12; i++) { | |
lastRandChars[i] = Math.floor(Math.random() * 64); | |
} | |
} else { | |
// If the timestamp hasn't changed since last push, use the same random number, except incremented by 1. | |
for (i = 11; i >= 0 && lastRandChars[i] === 63; i--) { | |
lastRandChars[i] = 0; | |
} | |
lastRandChars[i]++; | |
} | |
for (i = 0; i < 12; i++) { | |
id += PUSH_CHARS.charAt(lastRandChars[i]); | |
} | |
if(id.length != 20) throw new Error('Length should be 20.'); | |
return id; | |
}; | |
})(); |
SublimeText 3 plugin (no numpy)
Hi, after the code piece:
for (i = 11; i >= 0 && lastRandChars[i] === 63; i--) { lastRandChars[i] = 0; }
The var i could be -1 if all the lastRandChars is 63, and lead to index out of range error
Thread safe Java port with bug fixes and jmh benchmark:
https://gist.github.com/longkerdandy/a296b7a084fd191af6e4176eed168812
Could have taken me weeks to work this out on my own... thanks a bunch
I created a Rust version here: https://github.com/silverbeak/rust-push-id
Crate here: https://crates.io/crates/pushid
I will get to documentation soon, hopefully.
The reason I didn't turn it into a Gist is because Rust doesn't have native support for Random. It needs an external Crate for that, which makes it clumsy as a Gist.
Some nice submissions in this thread. Kudos, all!
@SVincent, you have your calculations wrong I think:
// 64 = 2^6
// So it actually uses Math.pow ( 64, 8 ) === Math.pow ( 2, 48 )
const maxTime = Math.pow ( 2, 48 ) - 1
let now = maxTime
for ( let i = 7; i >= 0; i-- ) {
now = Math.floor ( now / 64 )
}
// ==> now === 0
const date = new Date ()
date.setTime ( maxTime )
d.toISOString ()
// ==> "+010889-08-02T05:31:50.655Z"
// This date is greater then year 10889
// It is actually somewhere in 10895
Im so grateful finding this ❤️ thank you
port in elixir https://hex.pm/packages/firebase_pushid
Did pushid changed in firebase lately? None of the thousands of ids generated contains dash (-) or underscore (_). Or anything apart from alphanumeric characters.
Swift 4: https://gist.github.com/AhmedOS/512531b74da37f34331ecb206c77c20a
Modified version of pgherveou's one.
Made a JSFiddle: https://jsfiddle.net/feLys02r/1/
It's the original script with a button to copy to clipboard.
Port in Unity C#: https://gist.github.com/bhupiister/05dbb860e9064c43e8176b591f11e555
Just paste this script into unity and assign text field and function on a button.
Thank you to the original content maker
here is the codesandox for this code!
https://codesandbox.io/s/firebase-push-id-generator-080cb
Thanks @mikelehen & @Flyclops , for sharing the code.
My usecase is that we are transforming a firebase application (which uses firebase auth) and generates an uuid based on the above algorithm, however, we needed to convert it to a W3C DID, here is a simple code
var gb64 = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
func genB32(m string) (string, error) {
encb64 := base64.NewEncoding(gb64)
dec, err := encb64.DecodeString(m)
if err != nil {
return "", err
}
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(dec), nil
}
see line Line 77 of https://play.golang.org/p/vnCZTqOjZp9
Thank you for sharing <3
Best way:
`
function generatePushID() {
// Modeled after base64 web-safe chars, but ordered by ASCII.
var PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
// Timestamp of last push, used to prevent local collisions if you push twice in one ms.
var lastPushTime = 0;
// We generate 72-bits of randomness which get turned into 12 characters and appended to the
// timestamp to prevent collisions with other clients. We store the last characters we
// generated because in the event of a collision, we'll use those same characters except
// "incremented" by one.
var lastRandChars = [];
var now = new Date().getTime();
var duplicateTime = (now === lastPushTime);
lastPushTime = now;
var timeStampChars = new Array(8);
for (var i = 7; i >= 0; i--) {
timeStampChars[i] = PUSH_CHARS.charAt(now % 64);
// NOTE: Can't use << here because javascript will convert to int and lose the upper bits.
now = Math.floor(now / 64);
}
if (now !== 0) throw new Error('We should have converted the entire timestamp.');
var id = timeStampChars.join('');
if (!duplicateTime) {
for (i = 0; i < 12; i++) {
lastRandChars[i] = Math.floor(Math.random() * 64);
}
} else {
// If the timestamp hasn't changed since last push, use the same random number, except incremented by 1.
for (i = 11; i >= 0 && lastRandChars[i] === 63; i--) {
lastRandChars[i] = 0;
}
lastRandChars[i]++;
}
for (i = 0; i < 12; i++) {
id += PUSH_CHARS.charAt(lastRandChars[i]);
}
if(id.length != 20) throw new Error('Length should be 20.');
return id;
}
console.log(generatePushID());
`
Hi All,
Is there a possibility to have a pure numeric version?
Hello, here you can find a porting for PostgreSQL:
https://github.com/marcopeg/amazing-postgresql/tree/main/projects/firebase-pushid
Oh nice! Thanks for noting this here @marcopeg. :)
The beginning of the push ID is based on the current timestamp. This is what makes push IDs sort chronologically. Around the year 2110 the first character will change from a -
to a 0
.
The beginning of the push ID is based on the current timestamp. This is what makes push IDs sort chronologically. Around the year 2110 the first character will change from a
-
to a0
.
thanks that makes sense, now do we know why isn't Firebase applying the same methodology?
just to weigh the pros and cons as it seems a bit weird Google would not apply the most rational approach no?
Elm 0.18.0 effect manager version.