Browse Source

initial commit

master
boB Rudis 5 years ago
parent
commit
49db042160
No known key found for this signature in database GPG Key ID: 1D7529BE14E2BBA9
  1. 1
      .Rbuildignore
  2. 29
      DESCRIPTION
  3. 2
      LICENSE
  4. 21
      LICENSE.md
  5. 5
      NAMESPACE
  6. 17
      R/RcppExports.R
  7. 30
      R/ulid-package.R
  8. 40
      README.Rmd
  9. 78
      README.md
  10. 18
      man/ULIDgenerate.Rd
  11. 23
      man/ulid.Rd
  12. 3
      src/.gitignore
  13. 2
      src/Makevars
  14. 28
      src/RcppExports.cpp
  15. 24
      src/ulid-main.cpp
  16. 12
      src/ulid.h
  17. 699
      src/ulid_struct.h
  18. 539
      src/ulid_uint128.h

1
.Rbuildignore

@ -1,3 +1,4 @@
^LICENSE\.md$
^.*\.Rproj$
^\.Rproj\.user$
^\.travis\.yml$

29
DESCRIPTION

@ -1,24 +1,41 @@
Package: ulid
Type: Package
Title: ulid title goes here otherwise CRAN checks fail
Title: Generate Universally Unique Lexicographically Sortable Identifier
Version: 0.1.0
Date: 2018-12-28
Authors@R: c(
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>
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
BugReports: https://gitlab.com/hrbrmstr/ulid/issues
SystemRequirements: C++11
NeedsCompilation: yes
Encoding: UTF-8
License: AGPL
License: MIT + file LICENSE
Suggests:
testthat,
covr
Depends:
R (>= 3.2.0)
Imports:
httr,
jsonlite
Rcpp
RoxygenNote: 6.1.1
LinkingTo:
Rcpp

2
LICENSE

@ -0,0 +1,2 @@
YEAR: 2018
COPYRIGHT HOLDER: Bob Rudis

21
LICENSE.md

@ -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.

5
NAMESPACE

@ -1,4 +1,5 @@
# Generated by roxygen2: do not edit by hand
import(httr)
importFrom(jsonlite,fromJSON)
export(ULIDgenerate)
importFrom(Rcpp,sourceCpp)
useDynLib(ulid, .registration = TRUE)

17
R/RcppExports.R

@ -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)
}

30
R/ulid-package.R

@ -1,4 +1,28 @@
#' ...
#' Generate Universally Unique Lexicographically Sortable Identifier
#'
#' (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)
#'
#' - URL: <https://gitlab.com/hrbrmstr/ulid>
#' - BugReports: <https://gitlab.com/hrbrmstr/ulid/issues>
@ -7,6 +31,6 @@
#' @name ulid
#' @docType package
#' @author Bob Rudis (bob@@rud.is)
#' @import httr
#' @importFrom jsonlite fromJSON
#' @useDynLib ulid, .registration = TRUE
#' @importFrom Rcpp sourceCpp
NULL

40
README.Rmd

@ -4,12 +4,40 @@ output: rmarkdown::github_document
# 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 eval=FALSE}
@ -30,3 +58,15 @@ packageVersion("ulid")
```
### One
```{r}
ulid::ULIDgenerate()
```
### Many
```{r}
ulid::ULIDgenerate(20)
```

78
README.md

@ -1,2 +1,80 @@
# 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"

18
man/ULIDgenerate.Rd

@ -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()
}

23
man/ulid.Rd

@ -4,9 +4,30 @@
\name{ulid}
\alias{ulid}
\alias{ulid-package}
\title{...}
\title{Generate Universally Unique Lexicographically Sortable Identifier}
\description{
(grifted from \url{https://github.com/ulid/spec})
}
\details{
UUID can be suboptimal for many uses-cases because:
\itemize{
\item It isn't the most character efficient way of encoding 128 bits of randomness
\item UUID v1/v2 is impractical in many environments, as it requires access to a unique, stable MAC address
\item UUID v3/v5 requires a unique seed and produces randomly distributed IDs, which can cause fragmentation in many data structures
\item UUID v4 provides no other information than randomness which can cause fragmentation in many data structures
}
Instead, herein is proposed ULID:\preformatted{ulid() // 01ARZ3NDEKTSV4RRFFQ69G5FAV
}
\itemize{
\item 128-bit compatibility with UUID
\item 1.21e+24 unique ULIDs per millisecond
\item Lexicographically sortable!
\item Canonically encoded as a 26 character string, as opposed to the 36 character UUID
\item Uses Crockford's base32 for better efficiency and readability (5 bits per character)
\item Case insensitive
\item No special characters (URL safe)
\item Monotonic sort order (correctly detects and handles the same millisecond)
\item URL: \url{https://gitlab.com/hrbrmstr/ulid}
\item BugReports: \url{https://gitlab.com/hrbrmstr/ulid/issues}
}

3
src/.gitignore

@ -0,0 +1,3 @@
*.o
*.so
*.dll

2
src/Makevars

@ -0,0 +1,2 @@
CXX_STD = CXX11
PKG_LIBS = -L. -lz -lpthread -pthread -std=c++11

28
src/RcppExports.cpp

@ -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);
}

24
src/ulid-main.cpp

@ -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);
}

12
src/ulid.h

@ -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

699
src/ulid_struct.h

@ -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

539
src/ulid_uint128.h

@ -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…
Cancel
Save