boB Rudis
5 years ago
18 changed files with 1561 additions and 14 deletions
@ -1,24 +1,41 @@ |
|||||
Package: ulid |
Package: ulid |
||||
Type: Package |
Type: Package |
||||
Title: ulid title goes here otherwise CRAN checks fail |
Title: Generate Universally Unique Lexicographically Sortable Identifier |
||||
Version: 0.1.0 |
Version: 0.1.0 |
||||
Date: 2018-12-28 |
Date: 2018-12-28 |
||||
Authors@R: c( |
Authors@R: c( |
||||
person("Bob", "Rudis", email = "bob@rud.is", role = c("aut", "cre"), |
person("Bob", "Rudis", email = "bob@rud.is", role = c("aut", "cre"), |
||||
comment = c(ORCID = "0000-0001-5670-2640")) |
comment = c(ORCID = "0000-0001-5670-2640")), |
||||
|
person("suyash", role = c("aut"), |
||||
|
comment = "ULID C++ Port <https://github.com/suyash/ulid/>") |
||||
) |
) |
||||
Maintainer: Bob Rudis <bob@rud.is> |
Maintainer: Bob Rudis <bob@rud.is> |
||||
Description: A good description goes here otherwise CRAN checks fail. |
Description: Universally unique identifiers ('UUIDs') can be suboptimal for many |
||||
|
uses-cases because they aren't the most character efficient way of encoding |
||||
|
128 bits of randomness; v1/v2 versions are impractical in many environments, |
||||
|
as they require access to a unique, stable MAC address; v3/v5 versions require |
||||
|
a unique seed and produce randomly distributed IDs, which can cause fragmentation |
||||
|
in many data structures; v4 provides no other information than randomness which |
||||
|
can cause fragmentation in many data structures. 'ULIDs' (<https://github.com/ulid/spec>) |
||||
|
have 128-bit compatibility with 'UUID', 1.21e+24 unique ULIDs per millisecond, |
||||
|
are lexicographically sortable, canonically encoded as a 26 character string, |
||||
|
as opposed to the 36 character 'UUID', use Crockford's 'base32' for better |
||||
|
efficiency and readability (5 bits per character), are case insensitive, |
||||
|
have no special characters (i.e. are 'URL' safe) and have a onotonic sort order |
||||
|
(correctly detects and handles the same millisecond). |
||||
URL: https://gitlab.com/hrbrmstr/ulid |
URL: https://gitlab.com/hrbrmstr/ulid |
||||
BugReports: https://gitlab.com/hrbrmstr/ulid/issues |
BugReports: https://gitlab.com/hrbrmstr/ulid/issues |
||||
|
SystemRequirements: C++11 |
||||
|
NeedsCompilation: yes |
||||
Encoding: UTF-8 |
Encoding: UTF-8 |
||||
License: AGPL |
License: MIT + file LICENSE |
||||
Suggests: |
Suggests: |
||||
testthat, |
testthat, |
||||
covr |
covr |
||||
Depends: |
Depends: |
||||
R (>= 3.2.0) |
R (>= 3.2.0) |
||||
Imports: |
Imports: |
||||
httr, |
Rcpp |
||||
jsonlite |
|
||||
RoxygenNote: 6.1.1 |
RoxygenNote: 6.1.1 |
||||
|
LinkingTo: |
||||
|
Rcpp |
||||
|
@ -0,0 +1,2 @@ |
|||||
|
YEAR: 2018 |
||||
|
COPYRIGHT HOLDER: Bob Rudis |
@ -0,0 +1,21 @@ |
|||||
|
# MIT License |
||||
|
|
||||
|
Copyright (c) 2018 Bob Rudis |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
@ -1,4 +1,5 @@ |
|||||
# Generated by roxygen2: do not edit by hand |
# Generated by roxygen2: do not edit by hand |
||||
|
|
||||
import(httr) |
export(ULIDgenerate) |
||||
importFrom(jsonlite,fromJSON) |
importFrom(Rcpp,sourceCpp) |
||||
|
useDynLib(ulid, .registration = TRUE) |
||||
|
@ -0,0 +1,17 @@ |
|||||
|
# Generated by using Rcpp::compileAttributes() -> do not edit by hand |
||||
|
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 |
||||
|
|
||||
|
#' Generate ULID |
||||
|
#' |
||||
|
#' [ULIDgenerate()] generates a new []Universally Unique Lexicographically |
||||
|
#' Sortable Identifier](https://github.com/ulid/spec). |
||||
|
#' |
||||
|
#' @md |
||||
|
#' @param n number of id's to generate (default = `1`) |
||||
|
#' @export |
||||
|
#' @examples |
||||
|
#' ULIDgenerate() |
||||
|
ULIDgenerate <- function(n = 1L) { |
||||
|
.Call(`_ulid_ULIDgenerate`, n) |
||||
|
} |
||||
|
|
@ -1,2 +1,80 @@ |
|||||
|
|
||||
# ulid |
# ulid |
||||
|
|
||||
|
Universally Unique Lexicographically Sortable Identifier |
||||
|
|
||||
|
## Description |
||||
|
|
||||
|
(grifted from <https://github.com/ulid/spec>) |
||||
|
|
||||
|
UUID can be suboptimal for many uses-cases because: |
||||
|
|
||||
|
- It isn’t the most character efficient way of encoding 128 bits of |
||||
|
randomness |
||||
|
- UUID v1/v2 is impractical in many environments, as it requires |
||||
|
access to a unique, stable MAC address |
||||
|
- UUID v3/v5 requires a unique seed and produces randomly distributed |
||||
|
IDs, which can cause fragmentation in many data structures |
||||
|
- UUID v4 provides no other information than randomness which can |
||||
|
cause fragmentation in many data structures |
||||
|
|
||||
|
Instead, herein is proposed ULID: |
||||
|
|
||||
|
``` javascript |
||||
|
ulid() // 01ARZ3NDEKTSV4RRFFQ69G5FAV |
||||
|
``` |
||||
|
|
||||
|
- 128-bit compatibility with UUID |
||||
|
- 1.21e+24 unique ULIDs per millisecond |
||||
|
- Lexicographically sortable\! |
||||
|
- Canonically encoded as a 26 character string, as opposed to the 36 |
||||
|
character UUID |
||||
|
- Uses Crockford’s base32 for better efficiency and readability (5 |
||||
|
bits per character) |
||||
|
- Case insensitive |
||||
|
- No special characters (URL safe) |
||||
|
- Monotonic sort order (correctly detects and handles the same |
||||
|
millisecond) |
||||
|
|
||||
|
## What’s Inside The Tin |
||||
|
|
||||
|
The following functions are implemented: |
||||
|
|
||||
|
- `ULIDgenerate`: Generate a time-based ULID |
||||
|
|
||||
|
## Installation |
||||
|
|
||||
|
``` r |
||||
|
devtools::install_github("hrbrmstr/ulid") |
||||
|
``` |
||||
|
|
||||
|
## Usage |
||||
|
|
||||
|
``` r |
||||
|
library(ulid) |
||||
|
|
||||
|
# current verison |
||||
|
packageVersion("ulid") |
||||
|
``` |
||||
|
|
||||
|
## [1] '0.1.0' |
||||
|
|
||||
|
### One |
||||
|
|
||||
|
``` r |
||||
|
ulid::ULIDgenerate() |
||||
|
``` |
||||
|
|
||||
|
## [1] "0001E2CNHEB71VBCP6Y1M3RAGR" |
||||
|
|
||||
|
### Many |
||||
|
|
||||
|
``` r |
||||
|
ulid::ULIDgenerate(20) |
||||
|
``` |
||||
|
|
||||
|
## [1] "0001E2CNHEVGN75257V2TZBSBD" "0001E2CNHE7WRWS5HB52XHCK0X" "0001E2CNHEATMJ0TNJK0D7R9NV" "0001E2CNHE3GS9WDYF9V45YRVG" |
||||
|
## [5] "0001E2CNHEJ4NZPP9TB4K53Z8B" "0001E2CNHE383ZMKDRPNR8N9B0" "0001E2CNHEYPRFKC6BBXXGZ8TK" "0001E2CNHEQK1M8FZKDGPG32TS" |
||||
|
## [9] "0001E2CNHECVSY4VD69NMT8JNK" "0001E2CNHEEVPNZTXZ9QA66DRK" "0001E2CNHESEDDC41ETPJJWN8C" "0001E2CNHEZ4D0D9XGAV2AK5TA" |
||||
|
## [13] "0001E2CNHEZF13W414M0AKYKX8" "0001E2CNHEVKZ4WQZTFH4FBX4D" "0001E2CNHE69545C9DN9A29EP8" "0001E2CNHEJMDHYHFW3FBTZG5X" |
||||
|
## [17] "0001E2CNHEN9EZ4HHZCS3NQ9C0" "0001E2CNHEWR1SWBEPG69PDHXN" "0001E2CNHEVEPE5P2K5DNHB6CT" "0001E2CNHE8WX898BTPG3EJMQN" |
||||
|
@ -0,0 +1,18 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/RcppExports.R |
||||
|
\name{ULIDgenerate} |
||||
|
\alias{ULIDgenerate} |
||||
|
\title{Generate ULID} |
||||
|
\usage{ |
||||
|
ULIDgenerate(n = 1L) |
||||
|
} |
||||
|
\arguments{ |
||||
|
\item{n}{number of id's to generate (default = \code{1})} |
||||
|
} |
||||
|
\description{ |
||||
|
\code{\link[=ULIDgenerate]{ULIDgenerate()}} generates a new []Universally Unique Lexicographically |
||||
|
Sortable Identifier](https://github.com/ulid/spec). |
||||
|
} |
||||
|
\examples{ |
||||
|
ULIDgenerate() |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
*.o |
||||
|
*.so |
||||
|
*.dll |
@ -0,0 +1,2 @@ |
|||||
|
CXX_STD = CXX11 |
||||
|
PKG_LIBS = -L. -lz -lpthread -pthread -std=c++11 |
@ -0,0 +1,28 @@ |
|||||
|
// Generated by using Rcpp::compileAttributes() -> do not edit by hand
|
||||
|
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
|
||||
|
|
||||
|
#include <Rcpp.h> |
||||
|
|
||||
|
using namespace Rcpp; |
||||
|
|
||||
|
// ULIDgenerate
|
||||
|
CharacterVector ULIDgenerate(long n); |
||||
|
RcppExport SEXP _ulid_ULIDgenerate(SEXP nSEXP) { |
||||
|
BEGIN_RCPP |
||||
|
Rcpp::RObject rcpp_result_gen; |
||||
|
Rcpp::RNGScope rcpp_rngScope_gen; |
||||
|
Rcpp::traits::input_parameter< long >::type n(nSEXP); |
||||
|
rcpp_result_gen = Rcpp::wrap(ULIDgenerate(n)); |
||||
|
return rcpp_result_gen; |
||||
|
END_RCPP |
||||
|
} |
||||
|
|
||||
|
static const R_CallMethodDef CallEntries[] = { |
||||
|
{"_ulid_ULIDgenerate", (DL_FUNC) &_ulid_ULIDgenerate, 1}, |
||||
|
{NULL, NULL, 0} |
||||
|
}; |
||||
|
|
||||
|
RcppExport void R_init_ulid(DllInfo *dll) { |
||||
|
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); |
||||
|
R_useDynamicSymbols(dll, FALSE); |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
#include <Rcpp.h> |
||||
|
|
||||
|
#include "ulid.h" |
||||
|
|
||||
|
using namespace Rcpp; |
||||
|
|
||||
|
//' Generate ULID
|
||||
|
//'
|
||||
|
//' [ULIDgenerate()] generates a new []Universally Unique Lexicographically
|
||||
|
//' Sortable Identifier](https://github.com/ulid/spec).
|
||||
|
//'
|
||||
|
//' @md
|
||||
|
//' @param n number of id's to generate (default = `1`)
|
||||
|
//' @export
|
||||
|
//' @examples
|
||||
|
//' ULIDgenerate()
|
||||
|
// [[Rcpp::export]]
|
||||
|
CharacterVector ULIDgenerate(long n=1) { |
||||
|
CharacterVector c(n); |
||||
|
for (long i=0; i<=(n-1); i++) { |
||||
|
c[i] = ulid::Marshal(ulid::CreateNowRand()); |
||||
|
} |
||||
|
return(c); |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
// http://stackoverflow.com/a/23981011
|
||||
|
#ifdef __SIZEOF_INT128__ |
||||
|
#define ULIDUINT128 |
||||
|
#endif |
||||
|
|
||||
|
#ifdef ULIDUINT128 |
||||
|
#include "ulid_uint128.h" |
||||
|
#else |
||||
|
#include "ulid_struct.h" |
||||
|
#endif // ULIDUINT128
|
@ -0,0 +1,699 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <chrono> |
||||
|
#include <cstdlib> |
||||
|
#include <ctime> |
||||
|
#include <functional> |
||||
|
#include <random> |
||||
|
#include <vector> |
||||
|
|
||||
|
namespace ulid { |
||||
|
|
||||
|
/**
|
||||
|
* ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier |
||||
|
* */ |
||||
|
struct ULID { |
||||
|
uint8_t data[16]; |
||||
|
|
||||
|
ULID() { |
||||
|
// for (int i = 0 ; i < 16 ; i++) {
|
||||
|
// data[i] = 0;
|
||||
|
// }
|
||||
|
|
||||
|
// unrolled loop
|
||||
|
data[0] = 0; |
||||
|
data[1] = 0; |
||||
|
data[2] = 0; |
||||
|
data[3] = 0; |
||||
|
data[4] = 0; |
||||
|
data[5] = 0; |
||||
|
data[6] = 0; |
||||
|
data[7] = 0; |
||||
|
data[8] = 0; |
||||
|
data[9] = 0; |
||||
|
data[10] = 0; |
||||
|
data[11] = 0; |
||||
|
data[12] = 0; |
||||
|
data[13] = 0; |
||||
|
data[14] = 0; |
||||
|
data[15] = 0; |
||||
|
} |
||||
|
|
||||
|
ULID(uint64_t val) { |
||||
|
// for (int i = 0 ; i < 16 ; i++) {
|
||||
|
// data[15 - i] = static_cast<uint8_t>(val);
|
||||
|
// val >>= 8;
|
||||
|
// }
|
||||
|
|
||||
|
// unrolled loop
|
||||
|
data[15] = static_cast<uint8_t>(val); |
||||
|
|
||||
|
val >>= 8; |
||||
|
data[14] = static_cast<uint8_t>(val); |
||||
|
|
||||
|
val >>= 8; |
||||
|
data[13] = static_cast<uint8_t>(val); |
||||
|
|
||||
|
val >>= 8; |
||||
|
data[12] = static_cast<uint8_t>(val); |
||||
|
|
||||
|
val >>= 8; |
||||
|
data[11] = static_cast<uint8_t>(val); |
||||
|
|
||||
|
val >>= 8; |
||||
|
data[10] = static_cast<uint8_t>(val); |
||||
|
|
||||
|
val >>= 8; |
||||
|
data[9] = static_cast<uint8_t>(val); |
||||
|
|
||||
|
val >>= 8; |
||||
|
data[8] = static_cast<uint8_t>(val); |
||||
|
|
||||
|
data[7] = 0; |
||||
|
data[6] = 0; |
||||
|
data[5] = 0; |
||||
|
data[4] = 0; |
||||
|
data[3] = 0; |
||||
|
data[2] = 0; |
||||
|
data[1] = 0; |
||||
|
data[0] = 0; |
||||
|
} |
||||
|
|
||||
|
ULID(const ULID& other) { |
||||
|
// for (int i = 0 ; i < 16 ; i++) {
|
||||
|
// data[i] = other.data[i];
|
||||
|
// }
|
||||
|
|
||||
|
// unrolled loop
|
||||
|
data[0] = other.data[0]; |
||||
|
data[1] = other.data[1]; |
||||
|
data[2] = other.data[2]; |
||||
|
data[3] = other.data[3]; |
||||
|
data[4] = other.data[4]; |
||||
|
data[5] = other.data[5]; |
||||
|
data[6] = other.data[6]; |
||||
|
data[7] = other.data[7]; |
||||
|
data[8] = other.data[8]; |
||||
|
data[9] = other.data[9]; |
||||
|
data[10] = other.data[10]; |
||||
|
data[11] = other.data[11]; |
||||
|
data[12] = other.data[12]; |
||||
|
data[13] = other.data[13]; |
||||
|
data[14] = other.data[14]; |
||||
|
data[15] = other.data[15]; |
||||
|
} |
||||
|
|
||||
|
ULID& operator=(const ULID& other) { |
||||
|
// for (int i = 0 ; i < 16 ; i++) {
|
||||
|
// data[i] = other.data[i];
|
||||
|
// }
|
||||
|
|
||||
|
// unrolled loop
|
||||
|
data[0] = other.data[0]; |
||||
|
data[1] = other.data[1]; |
||||
|
data[2] = other.data[2]; |
||||
|
data[3] = other.data[3]; |
||||
|
data[4] = other.data[4]; |
||||
|
data[5] = other.data[5]; |
||||
|
data[6] = other.data[6]; |
||||
|
data[7] = other.data[7]; |
||||
|
data[8] = other.data[8]; |
||||
|
data[9] = other.data[9]; |
||||
|
data[10] = other.data[10]; |
||||
|
data[11] = other.data[11]; |
||||
|
data[12] = other.data[12]; |
||||
|
data[13] = other.data[13]; |
||||
|
data[14] = other.data[14]; |
||||
|
data[15] = other.data[15]; |
||||
|
|
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
ULID(ULID&& other) { |
||||
|
// for (int i = 0 ; i < 16 ; i++) {
|
||||
|
// data[i] = other.data[i];
|
||||
|
// other.data[i] = 0;
|
||||
|
// }
|
||||
|
|
||||
|
// unrolled loop
|
||||
|
data[0] = other.data[0]; |
||||
|
other.data[0] = 0; |
||||
|
|
||||
|
data[1] = other.data[1]; |
||||
|
other.data[1] = 0; |
||||
|
|
||||
|
data[2] = other.data[2]; |
||||
|
other.data[2] = 0; |
||||
|
|
||||
|
data[3] = other.data[3]; |
||||
|
other.data[3] = 0; |
||||
|
|
||||
|
data[4] = other.data[4]; |
||||
|
other.data[4] = 0; |
||||
|
|
||||
|
data[5] = other.data[5]; |
||||
|
other.data[5] = 0; |
||||
|
|
||||
|
data[6] = other.data[6]; |
||||
|
other.data[6] = 0; |
||||
|
|
||||
|
data[7] = other.data[7]; |
||||
|
other.data[7] = 0; |
||||
|
|
||||
|
data[8] = other.data[8]; |
||||
|
other.data[8] = 0; |
||||
|
|
||||
|
data[9] = other.data[9]; |
||||
|
other.data[9] = 0; |
||||
|
|
||||
|
data[10] = other.data[10]; |
||||
|
other.data[10] = 0; |
||||
|
|
||||
|
data[11] = other.data[11]; |
||||
|
other.data[11] = 0; |
||||
|
|
||||
|
data[12] = other.data[12]; |
||||
|
other.data[12] = 0; |
||||
|
|
||||
|
data[13] = other.data[13]; |
||||
|
other.data[13] = 0; |
||||
|
|
||||
|
data[14] = other.data[14]; |
||||
|
other.data[14] = 0; |
||||
|
|
||||
|
data[15] = other.data[15]; |
||||
|
other.data[15] = 0; |
||||
|
} |
||||
|
|
||||
|
ULID& operator=(ULID&& other) { |
||||
|
// for (int i = 0 ; i < 16 ; i++) {
|
||||
|
// data[i] = other.data[i];
|
||||
|
// other.data[i] = 0;
|
||||
|
// }
|
||||
|
|
||||
|
// unrolled loop
|
||||
|
data[0] = other.data[0]; |
||||
|
other.data[0] = 0; |
||||
|
|
||||
|
data[1] = other.data[1]; |
||||
|
other.data[1] = 0; |
||||
|
|
||||
|
data[2] = other.data[2]; |
||||
|
other.data[2] = 0; |
||||
|
|
||||
|
data[3] = other.data[3]; |
||||
|
other.data[3] = 0; |
||||
|
|
||||
|
data[4] = other.data[4]; |
||||
|
other.data[4] = 0; |
||||
|
|
||||
|
data[5] = other.data[5]; |
||||
|
other.data[5] = 0; |
||||
|
|
||||
|
data[6] = other.data[6]; |
||||
|
other.data[6] = 0; |
||||
|
|
||||
|
data[7] = other.data[7]; |
||||
|
other.data[7] = 0; |
||||
|
|
||||
|
data[8] = other.data[8]; |
||||
|
other.data[8] = 0; |
||||
|
|
||||
|
data[9] = other.data[9]; |
||||
|
other.data[9] = 0; |
||||
|
|
||||
|
data[10] = other.data[10]; |
||||
|
other.data[10] = 0; |
||||
|
|
||||
|
data[11] = other.data[11]; |
||||
|
other.data[11] = 0; |
||||
|
|
||||
|
data[12] = other.data[12]; |
||||
|
other.data[12] = 0; |
||||
|
|
||||
|
data[13] = other.data[13]; |
||||
|
other.data[13] = 0; |
||||
|
|
||||
|
data[14] = other.data[14]; |
||||
|
other.data[14] = 0; |
||||
|
|
||||
|
data[15] = other.data[15]; |
||||
|
other.data[15] = 0; |
||||
|
|
||||
|
return *this; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/**
|
||||
|
* EncodeTime will encode the first 6 bytes of a uint8_t array to the passed |
||||
|
* timestamp |
||||
|
* */ |
||||
|
void EncodeTime(time_t timestamp, ULID& ulid) { |
||||
|
ulid.data[0] = static_cast<uint8_t>(timestamp >> 40); |
||||
|
ulid.data[1] = static_cast<uint8_t>(timestamp >> 32); |
||||
|
ulid.data[2] = static_cast<uint8_t>(timestamp >> 24); |
||||
|
ulid.data[3] = static_cast<uint8_t>(timestamp >> 16); |
||||
|
ulid.data[4] = static_cast<uint8_t>(timestamp >> 8); |
||||
|
ulid.data[5] = static_cast<uint8_t>(timestamp); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeTimeNow will encode a ULID using the time obtained using std::time(nullptr) |
||||
|
* */ |
||||
|
void EncodeTimeNow(ULID& ulid) { |
||||
|
EncodeTime(std::time(nullptr), ulid); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeTimeSystemClockNow will encode a ULID using the time obtained using |
||||
|
* std::chrono::system_clock::now() by taking the timestamp in milliseconds. |
||||
|
* */ |
||||
|
void EncodeTimeSystemClockNow(ULID& ulid) { |
||||
|
auto now = std::chrono::system_clock::now(); |
||||
|
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()); |
||||
|
EncodeTime(ms.count(), ulid); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeEntropy will encode the last 10 bytes of the passed uint8_t array with |
||||
|
* the values generated using the passed random number generator. |
||||
|
* */ |
||||
|
void EncodeEntropy(const std::function<uint8_t()>& rng, ULID& ulid) { |
||||
|
ulid.data[6] = rng(); |
||||
|
ulid.data[7] = rng(); |
||||
|
ulid.data[8] = rng(); |
||||
|
ulid.data[9] = rng(); |
||||
|
ulid.data[10] = rng(); |
||||
|
ulid.data[11] = rng(); |
||||
|
ulid.data[12] = rng(); |
||||
|
ulid.data[13] = rng(); |
||||
|
ulid.data[14] = rng(); |
||||
|
ulid.data[15] = rng(); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeEntropyRand will encode a ulid using std::rand |
||||
|
* |
||||
|
* std::rand returns values in [0, RAND_MAX] |
||||
|
* */ |
||||
|
void EncodeEntropyRand(ULID& ulid) { |
||||
|
ulid.data[6] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
ulid.data[7] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
ulid.data[8] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
ulid.data[9] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
ulid.data[10] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
ulid.data[11] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
ulid.data[12] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
ulid.data[13] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
ulid.data[14] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
ulid.data[15] = (std::rand() * 255ull) / RAND_MAX; |
||||
|
} |
||||
|
|
||||
|
std::uniform_int_distribution<uint8_t> Distribution_0_255(0, 255); |
||||
|
|
||||
|
/**
|
||||
|
* EncodeEntropyMt19937 will encode a ulid using std::mt19937 |
||||
|
* |
||||
|
* It also creates a std::uniform_int_distribution to generate values in [0, 255] |
||||
|
* */ |
||||
|
void EncodeEntropyMt19937(std::mt19937& generator, ULID& ulid) { |
||||
|
ulid.data[6] = Distribution_0_255(generator); |
||||
|
ulid.data[7] = Distribution_0_255(generator); |
||||
|
ulid.data[8] = Distribution_0_255(generator); |
||||
|
ulid.data[9] = Distribution_0_255(generator); |
||||
|
ulid.data[10] = Distribution_0_255(generator); |
||||
|
ulid.data[11] = Distribution_0_255(generator); |
||||
|
ulid.data[12] = Distribution_0_255(generator); |
||||
|
ulid.data[13] = Distribution_0_255(generator); |
||||
|
ulid.data[14] = Distribution_0_255(generator); |
||||
|
ulid.data[15] = Distribution_0_255(generator); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Encode will create an encoded ULID with a timestamp and a generator. |
||||
|
* */ |
||||
|
void Encode(time_t timestamp, const std::function<uint8_t()>& rng, ULID& ulid) { |
||||
|
EncodeTime(timestamp, ulid); |
||||
|
EncodeEntropy(rng, ulid); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeNowRand = EncodeTimeNow + EncodeEntropyRand. |
||||
|
* */ |
||||
|
void EncodeNowRand(ULID& ulid) { |
||||
|
EncodeTimeNow(ulid); |
||||
|
EncodeEntropyRand(ulid); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Create will create a ULID with a timestamp and a generator. |
||||
|
* */ |
||||
|
ULID Create(time_t timestamp, const std::function<uint8_t()>& rng) { |
||||
|
ULID ulid; |
||||
|
Encode(timestamp, rng, ulid); |
||||
|
return ulid; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* CreateNowRand:EncodeNowRand = Create:Encode. |
||||
|
* */ |
||||
|
ULID CreateNowRand() { |
||||
|
ULID ulid; |
||||
|
EncodeNowRand(ulid); |
||||
|
return ulid; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Crockford's Base32 |
||||
|
* */ |
||||
|
const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; |
||||
|
|
||||
|
/**
|
||||
|
* MarshalTo will marshal a ULID to the passed character array. |
||||
|
* |
||||
|
* Implementation taken directly from oklog/ulid |
||||
|
* (https://sourcegraph.com/github.com/oklog/ulid@0774f81f6e44af5ce5e91c8d7d76cf710e889ebb/-/blob/ulid.go#L162-190)
|
||||
|
* |
||||
|
* timestamp:<br> |
||||
|
* dst[0]: first 3 bits of data[0]<br> |
||||
|
* dst[1]: last 5 bits of data[0]<br> |
||||
|
* dst[2]: first 5 bits of data[1]<br> |
||||
|
* dst[3]: last 3 bits of data[1] + first 2 bits of data[2]<br> |
||||
|
* dst[4]: bits 3-7 of data[2]<br> |
||||
|
* dst[5]: last bit of data[2] + first 4 bits of data[3]<br> |
||||
|
* dst[6]: last 4 bits of data[3] + first bit of data[4]<br> |
||||
|
* dst[7]: bits 2-6 of data[4]<br> |
||||
|
* dst[8]: last 2 bits of data[4] + first 3 bits of data[5]<br> |
||||
|
* dst[9]: last 5 bits of data[5]<br> |
||||
|
* |
||||
|
* entropy: |
||||
|
* follows similarly, except now all components are set to 5 bits. |
||||
|
* */ |
||||
|
void MarshalTo(const ULID& ulid, char dst[26]) { |
||||
|
// 10 byte timestamp
|
||||
|
dst[0] = Encoding[(ulid.data[0] & 224) >> 5]; |
||||
|
dst[1] = Encoding[ulid.data[0] & 31]; |
||||
|
dst[2] = Encoding[(ulid.data[1] & 248) >> 3]; |
||||
|
dst[3] = Encoding[((ulid.data[1] & 7) << 2) | ((ulid.data[2] & 192) >> 6)]; |
||||
|
dst[4] = Encoding[(ulid.data[2] & 62) >> 1]; |
||||
|
dst[5] = Encoding[((ulid.data[2] & 1) << 4) | ((ulid.data[3] & 240) >> 4)]; |
||||
|
dst[6] = Encoding[((ulid.data[3] & 15) << 1) | ((ulid.data[4] & 128) >> 7)]; |
||||
|
dst[7] = Encoding[(ulid.data[4] & 124) >> 2]; |
||||
|
dst[8] = Encoding[((ulid.data[4] & 3) << 3) | ((ulid.data[5] & 224) >> 5)]; |
||||
|
dst[9] = Encoding[ulid.data[5] & 31]; |
||||
|
|
||||
|
// 16 bytes of entropy
|
||||
|
dst[10] = Encoding[(ulid.data[6] & 248) >> 3]; |
||||
|
dst[11] = Encoding[((ulid.data[6] & 7) << 2) | ((ulid.data[7] & 192) >> 6)]; |
||||
|
dst[12] = Encoding[(ulid.data[7] & 62) >> 1]; |
||||
|
dst[13] = Encoding[((ulid.data[7] & 1) << 4) | ((ulid.data[8] & 240) >> 4)]; |
||||
|
dst[14] = Encoding[((ulid.data[8] & 15) << 1) | ((ulid.data[9] & 128) >> 7)]; |
||||
|
dst[15] = Encoding[(ulid.data[9] & 124) >> 2]; |
||||
|
dst[16] = Encoding[((ulid.data[9] & 3) << 3) | ((ulid.data[10] & 224) >> 5)]; |
||||
|
dst[17] = Encoding[ulid.data[10] & 31]; |
||||
|
dst[18] = Encoding[(ulid.data[11] & 248) >> 3]; |
||||
|
dst[19] = Encoding[((ulid.data[11] & 7) << 2) | ((ulid.data[12] & 192) >> 6)]; |
||||
|
dst[20] = Encoding[(ulid.data[12] & 62) >> 1]; |
||||
|
dst[21] = Encoding[((ulid.data[12] & 1) << 4) | ((ulid.data[13] & 240) >> 4)]; |
||||
|
dst[22] = Encoding[((ulid.data[13] & 15) << 1) | ((ulid.data[14] & 128) >> 7)]; |
||||
|
dst[23] = Encoding[(ulid.data[14] & 124) >> 2]; |
||||
|
dst[24] = Encoding[((ulid.data[14] & 3) << 3) | ((ulid.data[15] & 224) >> 5)]; |
||||
|
dst[25] = Encoding[ulid.data[15] & 31]; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Marshal will marshal a ULID to a std::string. |
||||
|
* */ |
||||
|
std::string Marshal(const ULID& ulid) { |
||||
|
char data[27]; |
||||
|
data[26] = '\0'; |
||||
|
MarshalTo(ulid, data); |
||||
|
return std::string(data); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* MarshalBinaryTo will Marshal a ULID to the passed byte array |
||||
|
* */ |
||||
|
void MarshalBinaryTo(const ULID& ulid, uint8_t dst[16]) { |
||||
|
// timestamp
|
||||
|
dst[0] = ulid.data[0]; |
||||
|
dst[1] = ulid.data[1]; |
||||
|
dst[2] = ulid.data[2]; |
||||
|
dst[3] = ulid.data[3]; |
||||
|
dst[4] = ulid.data[4]; |
||||
|
dst[5] = ulid.data[5]; |
||||
|
|
||||
|
// entropy
|
||||
|
dst[6] = ulid.data[6]; |
||||
|
dst[7] = ulid.data[7]; |
||||
|
dst[8] = ulid.data[8]; |
||||
|
dst[9] = ulid.data[9]; |
||||
|
dst[10] = ulid.data[10]; |
||||
|
dst[11] = ulid.data[11]; |
||||
|
dst[12] = ulid.data[12]; |
||||
|
dst[13] = ulid.data[13]; |
||||
|
dst[14] = ulid.data[14]; |
||||
|
dst[15] = ulid.data[15]; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* MarshalBinary will Marshal a ULID to a byte vector. |
||||
|
* */ |
||||
|
std::vector<uint8_t> MarshalBinary(const ULID& ulid) { |
||||
|
std::vector<uint8_t> dst(16); |
||||
|
MarshalBinaryTo(ulid, dst.data()); |
||||
|
return dst; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* dec storesdecimal encodings for characters. |
||||
|
* 0xFF indicates invalid character. |
||||
|
* 48-57 are digits. |
||||
|
* 65-90 are capital alphabets. |
||||
|
* */ |
||||
|
const uint8_t dec[256] = { |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
/* 0 1 2 3 4 5 6 7 */ |
||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
||||
|
/* 8 9 */ |
||||
|
0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
/* 10(A) 11(B) 12(C) 13(D) 14(E) 15(F) 16(G) */ |
||||
|
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, |
||||
|
/*17(H) 18(J) 19(K) 20(M) 21(N) */ |
||||
|
0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, |
||||
|
/*22(P)23(Q)24(R) 25(S) 26(T) 27(V) 28(W) */ |
||||
|
0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, |
||||
|
/*29(X)30(Y)31(Z) */ |
||||
|
0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF |
||||
|
}; |
||||
|
|
||||
|
/**
|
||||
|
* UnmarshalFrom will unmarshal a ULID from the passed character array. |
||||
|
* */ |
||||
|
void UnmarshalFrom(const char str[26], ULID& ulid) { |
||||
|
// timestamp
|
||||
|
ulid.data[0] = (dec[int(str[0])] << 5) | dec[int(str[1])]; |
||||
|
ulid.data[1] = (dec[int(str[2])] << 3) | (dec[int(str[3])] >> 2); |
||||
|
ulid.data[2] = (dec[int(str[3])] << 6) | (dec[int(str[4])] << 1) | (dec[int(str[5])] >> 4); |
||||
|
ulid.data[3] = (dec[int(str[5])] << 4) | (dec[int(str[6])] >> 1); |
||||
|
ulid.data[4] = (dec[int(str[6])] << 7) | (dec[int(str[7])] << 2) | (dec[int(str[8])] >> 3); |
||||
|
ulid.data[5] = (dec[int(str[8])] << 5) | dec[int(str[9])]; |
||||
|
|
||||
|
// entropy
|
||||
|
ulid.data[6] = (dec[int(str[10])] << 3) | (dec[int(str[11])] >> 2); |
||||
|
ulid.data[7] = (dec[int(str[11])] << 6) | (dec[int(str[12])] << 1) | (dec[int(str[13])] >> 4); |
||||
|
ulid.data[8] = (dec[int(str[13])] << 4) | (dec[int(str[14])] >> 1); |
||||
|
ulid.data[9] = (dec[int(str[14])] << 7) | (dec[int(str[15])] << 2) | (dec[int(str[16])] >> 3); |
||||
|
ulid.data[10] = (dec[int(str[16])] << 5) | dec[int(str[17])]; |
||||
|
ulid.data[11] = (dec[int(str[18])] << 3) | (dec[int(str[19])] >> 2); |
||||
|
ulid.data[12] = (dec[int(str[19])] << 6) | (dec[int(str[20])] << 1) | (dec[int(str[21])] >> 4); |
||||
|
ulid.data[13] = (dec[int(str[21])] << 4) | (dec[int(str[22])] >> 1); |
||||
|
ulid.data[14] = (dec[int(str[22])] << 7) | (dec[int(str[23])] << 2) | (dec[int(str[24])] >> 3); |
||||
|
ulid.data[15] = (dec[int(str[24])] << 5) | dec[int(str[25])]; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Unmarshal will create a new ULID by unmarshaling the passed string. |
||||
|
* */ |
||||
|
ULID Unmarshal(const std::string& str) { |
||||
|
ULID ulid; |
||||
|
UnmarshalFrom(str.c_str(), ulid); |
||||
|
return ulid; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* UnmarshalBinaryFrom will unmarshal a ULID from the passed byte array. |
||||
|
* */ |
||||
|
void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) { |
||||
|
// timestamp
|
||||
|
ulid.data[0] = b[0]; |
||||
|
ulid.data[1] = b[1]; |
||||
|
ulid.data[2] = b[2]; |
||||
|
ulid.data[3] = b[3]; |
||||
|
ulid.data[4] = b[4]; |
||||
|
ulid.data[5] = b[5]; |
||||
|
|
||||
|
// entropy
|
||||
|
ulid.data[6] = b[6]; |
||||
|
ulid.data[7] = b[7]; |
||||
|
ulid.data[8] = b[8]; |
||||
|
ulid.data[9] = b[9]; |
||||
|
ulid.data[10] = b[10]; |
||||
|
ulid.data[11] = b[11]; |
||||
|
ulid.data[12] = b[12]; |
||||
|
ulid.data[13] = b[13]; |
||||
|
ulid.data[14] = b[14]; |
||||
|
ulid.data[15] = b[15]; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Unmarshal will create a new ULID by unmarshaling the passed byte vector. |
||||
|
* */ |
||||
|
ULID UnmarshalBinary(const std::vector<uint8_t>& b) { |
||||
|
ULID ulid; |
||||
|
UnmarshalBinaryFrom(b.data(), ulid); |
||||
|
return ulid; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* CompareULIDs will compare two ULIDs. |
||||
|
* returns: |
||||
|
* -1 if ulid1 is Lexicographically before ulid2 |
||||
|
* 1 if ulid1 is Lexicographically after ulid2 |
||||
|
* 0 if ulid1 is same as ulid2 |
||||
|
* */ |
||||
|
int CompareULIDs(const ULID& ulid1, const ULID& ulid2) { |
||||
|
// for (int i = 0 ; i < 16 ; i++) {
|
||||
|
// if (ulid1.data[i] != ulid2.data[i]) {
|
||||
|
// return (ulid1.data[i] < ulid2.data[i]) * -2 + 1;
|
||||
|
// }
|
||||
|
// }
|
||||
|
|
||||
|
// unrolled loop
|
||||
|
|
||||
|
if (ulid1.data[0] != ulid2.data[0]) { |
||||
|
return (ulid1.data[0] < ulid2.data[0]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[1] != ulid2.data[1]) { |
||||
|
return (ulid1.data[1] < ulid2.data[1]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[2] != ulid2.data[2]) { |
||||
|
return (ulid1.data[2] < ulid2.data[2]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[3] != ulid2.data[3]) { |
||||
|
return (ulid1.data[3] < ulid2.data[3]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[4] != ulid2.data[4]) { |
||||
|
return (ulid1.data[4] < ulid2.data[4]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[5] != ulid2.data[5]) { |
||||
|
return (ulid1.data[5] < ulid2.data[5]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[6] != ulid2.data[6]) { |
||||
|
return (ulid1.data[6] < ulid2.data[6]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[7] != ulid2.data[7]) { |
||||
|
return (ulid1.data[7] < ulid2.data[7]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[8] != ulid2.data[8]) { |
||||
|
return (ulid1.data[8] < ulid2.data[8]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[9] != ulid2.data[9]) { |
||||
|
return (ulid1.data[9] < ulid2.data[9]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[10] != ulid2.data[10]) { |
||||
|
return (ulid1.data[10] < ulid2.data[10]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[11] != ulid2.data[11]) { |
||||
|
return (ulid1.data[11] < ulid2.data[11]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[12] != ulid2.data[12]) { |
||||
|
return (ulid1.data[12] < ulid2.data[12]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[13] != ulid2.data[13]) { |
||||
|
return (ulid1.data[13] < ulid2.data[13]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[14] != ulid2.data[14]) { |
||||
|
return (ulid1.data[14] < ulid2.data[14]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
if (ulid1.data[15] != ulid2.data[15]) { |
||||
|
return (ulid1.data[15] < ulid2.data[15]) * -2 + 1; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Time will extract the timestamp used to generate a ULID |
||||
|
* */ |
||||
|
time_t Time(const ULID& ulid) { |
||||
|
time_t ans = 0; |
||||
|
|
||||
|
ans |= ulid.data[0]; |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= ulid.data[1]; |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= ulid.data[2]; |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= ulid.data[3]; |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= ulid.data[4]; |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= ulid.data[5]; |
||||
|
|
||||
|
return ans; |
||||
|
} |
||||
|
|
||||
|
}; // namespace ulid
|
@ -0,0 +1,539 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <chrono> |
||||
|
#include <cstdlib> |
||||
|
#include <ctime> |
||||
|
#include <functional> |
||||
|
#include <random> |
||||
|
#include <vector> |
||||
|
|
||||
|
namespace ulid { |
||||
|
|
||||
|
/**
|
||||
|
* ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier |
||||
|
* */ |
||||
|
typedef __uint128_t ULID; |
||||
|
|
||||
|
/**
|
||||
|
* EncodeTime will encode the first 6 bytes of a uint8_t array to the passed |
||||
|
* timestamp |
||||
|
* */ |
||||
|
void EncodeTime(time_t timestamp, ULID& ulid) { |
||||
|
ULID t = static_cast<uint8_t>(timestamp >> 40); |
||||
|
|
||||
|
t <<= 8; |
||||
|
t |= static_cast<uint8_t>(timestamp >> 32); |
||||
|
|
||||
|
t <<= 8; |
||||
|
t |= static_cast<uint8_t>(timestamp >> 24); |
||||
|
|
||||
|
t <<= 8; |
||||
|
t |= static_cast<uint8_t>(timestamp >> 16); |
||||
|
|
||||
|
t <<= 8; |
||||
|
t |= static_cast<uint8_t>(timestamp >> 8); |
||||
|
|
||||
|
t <<= 8; |
||||
|
t |= static_cast<uint8_t>(timestamp); |
||||
|
|
||||
|
t <<= 80; |
||||
|
|
||||
|
ULID mask = 1; |
||||
|
mask <<= 80; |
||||
|
mask--; |
||||
|
|
||||
|
ulid = t | (ulid & mask); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeTimeNow will encode a ULID using the time obtained using std::time(nullptr) |
||||
|
* */ |
||||
|
void EncodeTimeNow(ULID& ulid) { |
||||
|
EncodeTime(std::time(nullptr), ulid); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeTimeSystemClockNow will encode a ULID using the time obtained using |
||||
|
* std::chrono::system_clock::now() by taking the timestamp in milliseconds. |
||||
|
* */ |
||||
|
void EncodeTimeSystemClockNow(ULID& ulid) { |
||||
|
auto now = std::chrono::system_clock::now(); |
||||
|
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()); |
||||
|
EncodeTime(ms.count(), ulid); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeEntropy will encode the last 10 bytes of the passed uint8_t array with |
||||
|
* the values generated using the passed random number generator. |
||||
|
* */ |
||||
|
void EncodeEntropy(const std::function<uint8_t()>& rng, ULID& ulid) { |
||||
|
ulid = (ulid >> 80) << 80; |
||||
|
|
||||
|
ULID e = rng(); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= rng(); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= rng(); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= rng(); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= rng(); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= rng(); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= rng(); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= rng(); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= rng(); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= rng(); |
||||
|
|
||||
|
ulid |= e; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeEntropyRand will encode a ulid using std::rand |
||||
|
* |
||||
|
* std::rand returns values in [0, RAND_MAX] |
||||
|
* */ |
||||
|
void EncodeEntropyRand(ULID& ulid) { |
||||
|
ulid = (ulid >> 80) << 80; |
||||
|
|
||||
|
ULID e = (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= (std::rand() * 255ull) / RAND_MAX; |
||||
|
|
||||
|
ulid |= e; |
||||
|
} |
||||
|
|
||||
|
std::uniform_int_distribution<uint8_t> Distribution_0_255(0, 255); |
||||
|
|
||||
|
/**
|
||||
|
* EncodeEntropyMt19937 will encode a ulid using std::mt19937 |
||||
|
* |
||||
|
* It also creates a std::uniform_int_distribution to generate values in [0, 255] |
||||
|
* */ |
||||
|
void EncodeEntropyMt19937(std::mt19937& generator, ULID& ulid) { |
||||
|
ulid = (ulid >> 80) << 80; |
||||
|
|
||||
|
ULID e = Distribution_0_255(generator); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= Distribution_0_255(generator); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= Distribution_0_255(generator); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= Distribution_0_255(generator); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= Distribution_0_255(generator); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= Distribution_0_255(generator); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= Distribution_0_255(generator); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= Distribution_0_255(generator); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= Distribution_0_255(generator); |
||||
|
|
||||
|
e <<= 8; |
||||
|
e |= Distribution_0_255(generator); |
||||
|
|
||||
|
ulid |= e; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Encode will create an encoded ULID with a timestamp and a generator. |
||||
|
* */ |
||||
|
void Encode(time_t timestamp, const std::function<uint8_t()>& rng, ULID& ulid) { |
||||
|
EncodeTime(timestamp, ulid); |
||||
|
EncodeEntropy(rng, ulid); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* EncodeNowRand = EncodeTimeNow + EncodeEntropyRand. |
||||
|
* */ |
||||
|
void EncodeNowRand(ULID& ulid) { |
||||
|
EncodeTimeNow(ulid); |
||||
|
EncodeEntropyRand(ulid); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Create will create a ULID with a timestamp and a generator. |
||||
|
* */ |
||||
|
ULID Create(time_t timestamp, const std::function<uint8_t()>& rng) { |
||||
|
ULID ulid = 0; |
||||
|
Encode(timestamp, rng, ulid); |
||||
|
return ulid; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* CreateNowRand:EncodeNowRand = Create:Encode. |
||||
|
* */ |
||||
|
ULID CreateNowRand() { |
||||
|
ULID ulid = 0; |
||||
|
EncodeNowRand(ulid); |
||||
|
return ulid; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Crockford's Base32 |
||||
|
* */ |
||||
|
const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"; |
||||
|
|
||||
|
/**
|
||||
|
* MarshalTo will marshal a ULID to the passed character array. |
||||
|
* |
||||
|
* Implementation taken directly from oklog/ulid |
||||
|
* (https://sourcegraph.com/github.com/oklog/ulid@0774f81f6e44af5ce5e91c8d7d76cf710e889ebb/-/blob/ulid.go#L162-190)
|
||||
|
* |
||||
|
* timestamp: |
||||
|
* dst[0]: first 3 bits of data[0] |
||||
|
* dst[1]: last 5 bits of data[0] |
||||
|
* dst[2]: first 5 bits of data[1] |
||||
|
* dst[3]: last 3 bits of data[1] + first 2 bits of data[2] |
||||
|
* dst[4]: bits 3-7 of data[2] |
||||
|
* dst[5]: last bit of data[2] + first 4 bits of data[3] |
||||
|
* dst[6]: last 4 bits of data[3] + first bit of data[4] |
||||
|
* dst[7]: bits 2-6 of data[4] |
||||
|
* dst[8]: last 2 bits of data[4] + first 3 bits of data[5] |
||||
|
* dst[9]: last 5 bits of data[5] |
||||
|
* |
||||
|
* entropy: |
||||
|
* follows similarly, except now all components are set to 5 bits. |
||||
|
* */ |
||||
|
void MarshalTo(const ULID& ulid, char dst[26]) { |
||||
|
// 10 byte timestamp
|
||||
|
dst[0] = Encoding[(static_cast<uint8_t>(ulid >> 120) & 224) >> 5]; |
||||
|
dst[1] = Encoding[static_cast<uint8_t>(ulid >> 120) & 31]; |
||||
|
dst[2] = Encoding[(static_cast<uint8_t>(ulid >> 112) & 248) >> 3]; |
||||
|
dst[3] = Encoding[((static_cast<uint8_t>(ulid >> 112) & 7) << 2) | ((static_cast<uint8_t>(ulid >> 104) & 192) >> 6)]; |
||||
|
dst[4] = Encoding[(static_cast<uint8_t>(ulid >> 104) & 62) >> 1]; |
||||
|
dst[5] = Encoding[((static_cast<uint8_t>(ulid >> 104) & 1) << 4) | ((static_cast<uint8_t>(ulid >> 96) & 240) >> 4)]; |
||||
|
dst[6] = Encoding[((static_cast<uint8_t>(ulid >> 96) & 15) << 1) | ((static_cast<uint8_t>(ulid >> 88) & 128) >> 7)]; |
||||
|
dst[7] = Encoding[(static_cast<uint8_t>(ulid >> 88) & 124) >> 2]; |
||||
|
dst[8] = Encoding[((static_cast<uint8_t>(ulid >> 88) & 3) << 3) | ((static_cast<uint8_t>(ulid >> 80) & 224) >> 5)]; |
||||
|
dst[9] = Encoding[static_cast<uint8_t>(ulid >> 80) & 31]; |
||||
|
|
||||
|
// 16 bytes of entropy
|
||||
|
dst[10] = Encoding[(static_cast<uint8_t>(ulid >> 72) & 248) >> 3]; |
||||
|
dst[11] = Encoding[((static_cast<uint8_t>(ulid >> 72) & 7) << 2) | ((static_cast<uint8_t>(ulid >> 64) & 192) >> 6)]; |
||||
|
dst[12] = Encoding[(static_cast<uint8_t>(ulid >> 64) & 62) >> 1]; |
||||
|
dst[13] = Encoding[((static_cast<uint8_t>(ulid >> 64) & 1) << 4) | ((static_cast<uint8_t>(ulid >> 56) & 240) >> 4)]; |
||||
|
dst[14] = Encoding[((static_cast<uint8_t>(ulid >> 56) & 15) << 1) | ((static_cast<uint8_t>(ulid >> 48) & 128) >> 7)]; |
||||
|
dst[15] = Encoding[(static_cast<uint8_t>(ulid >> 48) & 124) >> 2]; |
||||
|
dst[16] = Encoding[((static_cast<uint8_t>(ulid >> 48) & 3) << 3) | ((static_cast<uint8_t>(ulid >> 40) & 224) >> 5)]; |
||||
|
dst[17] = Encoding[static_cast<uint8_t>(ulid >> 40) & 31]; |
||||
|
dst[18] = Encoding[(static_cast<uint8_t>(ulid >> 32) & 248) >> 3]; |
||||
|
dst[19] = Encoding[((static_cast<uint8_t>(ulid >> 32) & 7) << 2) | ((static_cast<uint8_t>(ulid >> 24) & 192) >> 6)]; |
||||
|
dst[20] = Encoding[(static_cast<uint8_t>(ulid >> 24) & 62) >> 1]; |
||||
|
dst[21] = Encoding[((static_cast<uint8_t>(ulid >> 24) & 1) << 4) | ((static_cast<uint8_t>(ulid >> 16) & 240) >> 4)]; |
||||
|
dst[22] = Encoding[((static_cast<uint8_t>(ulid >> 16) & 15) << 1) | ((static_cast<uint8_t>(ulid >> 8) & 128) >> 7)]; |
||||
|
dst[23] = Encoding[(static_cast<uint8_t>(ulid >> 8) & 124) >> 2]; |
||||
|
dst[24] = Encoding[((static_cast<uint8_t>(ulid >> 8) & 3) << 3) | (((static_cast<uint8_t>(ulid)) & 224) >> 5)]; |
||||
|
dst[25] = Encoding[(static_cast<uint8_t>(ulid)) & 31]; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Marshal will marshal a ULID to a std::string. |
||||
|
* */ |
||||
|
std::string Marshal(const ULID& ulid) { |
||||
|
char data[27]; |
||||
|
data[26] = '\0'; |
||||
|
MarshalTo(ulid, data); |
||||
|
return std::string(data); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* MarshalBinaryTo will Marshal a ULID to the passed byte array |
||||
|
* */ |
||||
|
void MarshalBinaryTo(const ULID& ulid, uint8_t dst[16]) { |
||||
|
// timestamp
|
||||
|
dst[0] = static_cast<uint8_t>(ulid >> 120); |
||||
|
dst[1] = static_cast<uint8_t>(ulid >> 112); |
||||
|
dst[2] = static_cast<uint8_t>(ulid >> 104); |
||||
|
dst[3] = static_cast<uint8_t>(ulid >> 96); |
||||
|
dst[4] = static_cast<uint8_t>(ulid >> 88); |
||||
|
dst[5] = static_cast<uint8_t>(ulid >> 80); |
||||
|
|
||||
|
// entropy
|
||||
|
dst[6] = static_cast<uint8_t>(ulid >> 72); |
||||
|
dst[7] = static_cast<uint8_t>(ulid >> 64); |
||||
|
dst[8] = static_cast<uint8_t>(ulid >> 56); |
||||
|
dst[9] = static_cast<uint8_t>(ulid >> 48); |
||||
|
dst[10] = static_cast<uint8_t>(ulid >> 40); |
||||
|
dst[11] = static_cast<uint8_t>(ulid >> 32); |
||||
|
dst[12] = static_cast<uint8_t>(ulid >> 24); |
||||
|
dst[13] = static_cast<uint8_t>(ulid >> 16); |
||||
|
dst[14] = static_cast<uint8_t>(ulid >> 8); |
||||
|
dst[15] = static_cast<uint8_t>(ulid); |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* MarshalBinary will Marshal a ULID to a byte vector. |
||||
|
* */ |
||||
|
std::vector<uint8_t> MarshalBinary(const ULID& ulid) { |
||||
|
std::vector<uint8_t> dst(16); |
||||
|
MarshalBinaryTo(ulid, dst.data()); |
||||
|
return dst; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* dec storesdecimal encodings for characters. |
||||
|
* 0xFF indicates invalid character. |
||||
|
* 48-57 are digits. |
||||
|
* 65-90 are capital alphabets. |
||||
|
* */ |
||||
|
const uint8_t dec[256] = { |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
/* 0 1 2 3 4 5 6 7 */ |
||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
||||
|
/* 8 9 */ |
||||
|
0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
/* 10(A) 11(B) 12(C) 13(D) 14(E) 15(F) 16(G) */ |
||||
|
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, |
||||
|
/*17(H) 18(J) 19(K) 20(M) 21(N) */ |
||||
|
0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, |
||||
|
/*22(P)23(Q)24(R) 25(S) 26(T) 27(V) 28(W) */ |
||||
|
0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, |
||||
|
/*29(X)30(Y)31(Z) */ |
||||
|
0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
|
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF |
||||
|
}; |
||||
|
|
||||
|
/**
|
||||
|
* UnmarshalFrom will unmarshal a ULID from the passed character array. |
||||
|
* */ |
||||
|
void UnmarshalFrom(const char str[26], ULID& ulid) { |
||||
|
// timestamp
|
||||
|
ulid = (dec[int(str[0])] << 5) | dec[int(str[1])]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[2])] << 3) | (dec[int(str[3])] >> 2); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[3])] << 6) | (dec[int(str[4])] << 1) | (dec[int(str[5])] >> 4); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[5])] << 4) | (dec[int(str[6])] >> 1); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[6])] << 7) | (dec[int(str[7])] << 2) | (dec[int(str[8])] >> 3); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[8])] << 5) | dec[int(str[9])]; |
||||
|
|
||||
|
// entropy
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[10])] << 3) | (dec[int(str[11])] >> 2); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[11])] << 6) | (dec[int(str[12])] << 1) | (dec[int(str[13])] >> 4); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[13])] << 4) | (dec[int(str[14])] >> 1); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[14])] << 7) | (dec[int(str[15])] << 2) | (dec[int(str[16])] >> 3); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[16])] << 5) | dec[int(str[17])]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[18])] << 3) | (dec[int(str[19])] >> 2); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[19])] << 6) | (dec[int(str[20])] << 1) | (dec[int(str[21])] >> 4); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[21])] << 4) | (dec[int(str[22])] >> 1); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[22])] << 7) | (dec[int(str[23])] << 2) | (dec[int(str[24])] >> 3); |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= (dec[int(str[24])] << 5) | dec[int(str[25])]; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Unmarshal will create a new ULID by unmarshaling the passed string. |
||||
|
* */ |
||||
|
ULID Unmarshal(const std::string& str) { |
||||
|
ULID ulid; |
||||
|
UnmarshalFrom(str.c_str(), ulid); |
||||
|
return ulid; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* UnmarshalBinaryFrom will unmarshal a ULID from the passed byte array. |
||||
|
* */ |
||||
|
void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) { |
||||
|
// timestamp
|
||||
|
ulid = b[0]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[1]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[2]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[3]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[4]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[5]; |
||||
|
|
||||
|
// entropy
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[6]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[7]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[8]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[9]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[10]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[11]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[12]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[13]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[14]; |
||||
|
|
||||
|
ulid <<= 8; |
||||
|
ulid |= b[15]; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Unmarshal will create a new ULID by unmarshaling the passed byte vector. |
||||
|
* */ |
||||
|
ULID UnmarshalBinary(const std::vector<uint8_t>& b) { |
||||
|
ULID ulid; |
||||
|
UnmarshalBinaryFrom(b.data(), ulid); |
||||
|
return ulid; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* CompareULIDs will compare two ULIDs. |
||||
|
* returns: |
||||
|
* -1 if ulid1 is Lexicographically before ulid2 |
||||
|
* 1 if ulid1 is Lexicographically after ulid2 |
||||
|
* 0 if ulid1 is same as ulid2 |
||||
|
* */ |
||||
|
int CompareULIDs(const ULID& ulid1, const ULID& ulid2) { |
||||
|
return -2 * (ulid1 < ulid2) - 1 * (ulid1 == ulid2) + 1; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Time will extract the timestamp used to generate a ULID |
||||
|
* */ |
||||
|
time_t Time(const ULID& ulid) { |
||||
|
time_t ans = 0; |
||||
|
|
||||
|
ans |= static_cast<uint8_t>(ulid >> 120); |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= static_cast<uint8_t>(ulid >> 112); |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= static_cast<uint8_t>(ulid >> 104); |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= static_cast<uint8_t>(ulid >> 96); |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= static_cast<uint8_t>(ulid >> 88); |
||||
|
|
||||
|
ans <<= 8; |
||||
|
ans |= static_cast<uint8_t>(ulid >> 80); |
||||
|
|
||||
|
return ans; |
||||
|
} |
||||
|
|
||||
|
}; // namespace ulid
|
Loading…
Reference in new issue