Browse Source

update library; switch to tinytest; add vignette

master
boB Rudis 5 years ago
parent
commit
03470841f7
No known key found for this signature in database GPG Key ID: 1D7529BE14E2BBA9
  1. 1
      .Rbuildignore
  2. 1
      .gitignore
  3. 3
      .travis.yml
  4. 57
      DESCRIPTION
  5. 9
      NEWS.md
  6. 9
      README.Rmd
  7. 82
      README.md
  8. 52
      appveyor.yml
  9. 27
      inst/tinytest/test_ulid.R
  10. 6
      src/ulid-main.cpp
  11. 5
      src/ulid.h
  12. 81
      src/ulid_struct.h
  13. 101
      src/ulid_uint128.h
  14. 2
      tests/test-all.R
  15. 6
      tests/testthat/test-ulid.R
  16. 5
      tests/tinytest.R
  17. 2
      vignettes/.gitignore
  18. 45
      vignettes/intro-to-ulid.Rmd

1
.Rbuildignore

@ -12,3 +12,4 @@
^tmp$
^notes$
^\.gitlab-ci\.yml$
^appveyor\.yml$

1
.gitignore

@ -6,3 +6,4 @@
src/*.o
src/*.so
src/*.dll
inst/doc

3
.travis.yml

@ -1,6 +1,5 @@
language: R
sudo: false
cache: packages
after_success:
- Rscript -e 'covr::codecov()'
- Rscript -e 'covr::codecov()'

57
DESCRIPTION

@ -1,27 +1,28 @@
Package: ulid
Type: Package
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")),
person("suyash", role = c("aut"),
comment = "ULID C++ Port <https://github.com/suyash/ulid/>")
)
Version: 0.3.0
Date: 2019-07-04
Authors@R: c( person("Bob", "Rudis", email = "bob@rud.is", role =
c("aut", "cre"), 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: 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
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
@ -30,12 +31,12 @@ NeedsCompilation: yes
Encoding: UTF-8
License: MIT + file LICENSE
Suggests:
testthat,
covr
Depends:
R (>= 3.2.0)
Imports:
Rcpp
covr,
tinytest,
knitr,
rmarkdown
Depends: R (>= 3.2.0)
Imports: Rcpp
RoxygenNote: 6.1.1
LinkingTo:
Rcpp
LinkingTo: Rcpp
VignetteBuilder: knitr

9
NEWS.md

@ -1,2 +1,11 @@
0.3.0
* Updated libary
* Uses R C random bits vs `std::rand()`
* Vignette
* Converted to {tinytest}
0.2.0
* Polished version
0.1.0
* Initial release

9
README.Rmd

@ -2,6 +2,11 @@
output: rmarkdown::github_document
---
[![Travis-CI Build Status](https://travis-ci.org/hrbrmstr/ulid.svg?branch=master)](https://travis-ci.org/hrbrmstr/ulid)
[![AppVeyor build status](https://ci.appveyor.com/api/projects/status/github/hrbrmstr/ulid?branch=master&svg=true)](https://ci.appveyor.com/project/hrbrmstr/ulid)
[![Coverage Status](https://codecov.io/gh/hrbrmstr/ulid/branch/master/graph/badge.svg)](https://codecov.io/gh/hrbrmstr/ulid)
[![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/ulid)](https://cran.r-project.org/package=ulid)
# ulid
Universally Unique Lexicographically Sortable Identifier
@ -65,8 +70,8 @@ The following functions are implemented:
## Installation
```{r eval=FALSE}
devtools::install_github("hrbrmstr/ulid")
```{r nstall-ex, results='asis', echo = FALSE}
hrbrpkghelpr::install_block()
```
```{r message=FALSE, warning=FALSE, error=FALSE, include=FALSE}

82
README.md

@ -1,4 +1,12 @@
[![Travis-CI Build
Status](https://travis-ci.org/hrbrmstr/ulid.svg?branch=master)](https://travis-ci.org/hrbrmstr/ulid)
[![AppVeyor build
status](https://ci.appveyor.com/api/projects/status/github/hrbrmstr/ulid?branch=master&svg=true)](https://ci.appveyor.com/project/hrbrmstr/ulid)
[![Coverage
Status](https://codecov.io/gh/hrbrmstr/ulid/branch/master/graph/badge.svg)](https://codecov.io/gh/hrbrmstr/ulid)
[![CRAN\_Status\_Badge](https://www.r-pkg.org/badges/version/ulid)](https://cran.r-project.org/package=ulid)
# ulid
Universally Unique Lexicographically Sortable Identifier
@ -73,6 +81,12 @@ The following functions are implemented:
## Installation
``` r
install.packages("ulid", repos = "https://cinc.rud.is")
# or
devtools::install_git("https://git.sr.ht/~hrbrmstr/ulid")
# or
devtools::install_gitlab("hrbrmstr/ulid")
# or
devtools::install_github("hrbrmstr/ulid")
```
@ -85,7 +99,7 @@ library(ulid)
packageVersion("ulid")
```
## [1] '0.1.0'
## [1] '0.3.0'
### One
@ -93,7 +107,7 @@ packageVersion("ulid")
ulid::ULIDgenerate()
```
## [1] "0001E2ERKHVPKZJ6FA6ZWHH1KS"
## [1] "0001EHX06QWTHZTAT6TE2GVAVT"
### Many
@ -101,11 +115,11 @@ ulid::ULIDgenerate()
(u <- ulid::ULIDgenerate(20))
```
## [1] "0001E2ERKHVX5QF5D59SX2E65T" "0001E2ERKHKD6MHKYB1G8JHN5X" "0001E2ERKHTK0XEHVV2G5877K9" "0001E2ERKHKFGG5NPN24PC1N0W"
## [5] "0001E2ERKH3F48CAKJCVMSCBKS" "0001E2ERKHF3N0B94VK05GTXCW" "0001E2ERKH24GCJ2CT3Z5WM1FD" "0001E2ERKH381RJ232KK7SMWQW"
## [9] "0001E2ERKH7NAZ1T4HR4ZRQRND" "0001E2ERKHSATC17G2QAPYXE0C" "0001E2ERKH76R83NFST3MZNW84" "0001E2ERKHFKS52SD8WJ8FHXMV"
## [13] "0001E2ERKHQM6VBM5JB235JJ1W" "0001E2ERKHXG2KNYWHHFS8X69Z" "0001E2ERKHQW821KPRM4GQFANJ" "0001E2ERKHD5KWTM5S345A3RP4"
## [17] "0001E2ERKH0D901W6KX66B1BHE" "0001E2ERKHKPHZBFSC16FC7FFC" "0001E2ERKHQQH7315GMY8HRYXV" "0001E2ERKH016YBAJAB7K9777T"
## [1] "0001EHX06Q1C0H9NQPZQW7YN65" "0001EHX06QN1D7PYNDZ33B7QF1" "0001EHX06QZHV13RGNDWAJ7VX9" "0001EHX06Q2XC40QWY7AFR8DCZ"
## [5] "0001EHX06QXWZRT9EJ1YM75214" "0001EHX06QYDSTG3KRWSKG01EE" "0001EHX06QNC8YWTX0H2M7HHYQ" "0001EHX06QFPBJWQ1PAQ1KXTJY"
## [9] "0001EHX06Q6HW1WE5GP8Q9J6D6" "0001EHX06Q3QEY4KF2DX0FKTFB" "0001EHX06QS3FDSPSHW2W7AB7X" "0001EHX06QS2VENPTAADWYKAQW"
## [13] "0001EHX06QQJ7XEK9ZDT5542SD" "0001EHX06QHWVRKRF2SC86KB3Z" "0001EHX06QKG0V1129FBA0A5XY" "0001EHX06QY66KTA09CB9KKQP8"
## [17] "0001EHX06QZ9VMBDT8YZMXK1B5" "0001EHX06QJ60ZFVA45DFAGB5R" "0001EHX06QC0CXK917QFWXA71M" "0001EHX06QBW2TTWPDAFG23J7E"
### Unmarshal
@ -114,26 +128,26 @@ unmarshal(u)
```
## ts rnd
## 1 2018-12-29 07:02:57 VX5QF5D59SX2E65T
## 2 2018-12-29 07:02:57 KD6MHKYB1G8JHN5X
## 3 2018-12-29 07:02:57 TK0XEHVV2G5877K9
## 4 2018-12-29 07:02:57 KFGG5NPN24PC1N0W
## 5 2018-12-29 07:02:57 3F48CAKJCVMSCBKS
## 6 2018-12-29 07:02:57 F3N0B94VK05GTXCW
## 7 2018-12-29 07:02:57 24GCJ2CT3Z5WM1FD
## 8 2018-12-29 07:02:57 381RJ232KK7SMWQW
## 9 2018-12-29 07:02:57 7NAZ1T4HR4ZRQRND
## 10 2018-12-29 07:02:57 SATC17G2QAPYXE0C
## 11 2018-12-29 07:02:57 76R83NFST3MZNW84
## 12 2018-12-29 07:02:57 FKS52SD8WJ8FHXMV
## 13 2018-12-29 07:02:57 QM6VBM5JB235JJ1W
## 14 2018-12-29 07:02:57 XG2KNYWHHFS8X69Z
## 15 2018-12-29 07:02:57 QW821KPRM4GQFANJ
## 16 2018-12-29 07:02:57 D5KWTM5S345A3RP4
## 17 2018-12-29 07:02:57 0D901W6KX66B1BHE
## 18 2018-12-29 07:02:57 KPHZBFSC16FC7FFC
## 19 2018-12-29 07:02:57 QQH7315GMY8HRYXV
## 20 2018-12-29 07:02:57 016YBAJAB7K9777T
## 1 2019-07-04 18:42:31 1C0H9NQPZQW7YN65
## 2 2019-07-04 18:42:31 N1D7PYNDZ33B7QF1
## 3 2019-07-04 18:42:31 ZHV13RGNDWAJ7VX9
## 4 2019-07-04 18:42:31 2XC40QWY7AFR8DCZ
## 5 2019-07-04 18:42:31 XWZRT9EJ1YM75214
## 6 2019-07-04 18:42:31 YDSTG3KRWSKG01EE
## 7 2019-07-04 18:42:31 NC8YWTX0H2M7HHYQ
## 8 2019-07-04 18:42:31 FPBJWQ1PAQ1KXTJY
## 9 2019-07-04 18:42:31 6HW1WE5GP8Q9J6D6
## 10 2019-07-04 18:42:31 3QEY4KF2DX0FKTFB
## 11 2019-07-04 18:42:31 S3FDSPSHW2W7AB7X
## 12 2019-07-04 18:42:31 S2VENPTAADWYKAQW
## 13 2019-07-04 18:42:31 QJ7XEK9ZDT5542SD
## 14 2019-07-04 18:42:31 HWVRKRF2SC86KB3Z
## 15 2019-07-04 18:42:31 KG0V1129FBA0A5XY
## 16 2019-07-04 18:42:31 Y66KTA09CB9KKQP8
## 17 2019-07-04 18:42:31 Z9VMBDT8YZMXK1B5
## 18 2019-07-04 18:42:31 J60ZFVA45DFAGB5R
## 19 2019-07-04 18:42:31 C0CXK917QFWXA71M
## 20 2019-07-04 18:42:31 BW2TTWPDAFG23J7E
### Use defined timestamps
@ -141,14 +155,14 @@ unmarshal(u)
(ut <- ts_generate(as.POSIXct("2017-11-01 15:00:00", origin="1970-01-01")))
```
## [1] "0001CZM6DGE66RJEY4N05F5R95"
## [1] "0001CZM6DGVRVF68B8AP8VATS0"
``` r
unmarshal(ut)
```
## ts rnd
## 1 2017-11-01 15:00:00 E66RJEY4N05F5R95
## 1 2017-11-01 15:00:00 VRVF68B8AP8VATS0
## Package Code Metrics
@ -157,8 +171,8 @@ cloc::cloc_pkg_md()
```
| Lang | \# Files | (%) | LoC | (%) | Blank lines | (%) | \# Lines | (%) |
| :----------- | -------: | ---: | --: | ---: | ----------: | ---: | -------: | ---: |
| C/C++ Header | 3 | 0.27 | 755 | 0.87 | 231 | 0.78 | 264 | 0.57 |
| C++ | 2 | 0.18 | 81 | 0.09 | 22 | 0.07 | 37 | 0.08 |
| R | 5 | 0.45 | 17 | 0.02 | 7 | 0.02 | 95 | 0.20 |
| Rmd | 1 | 0.09 | 10 | 0.01 | 37 | 0.12 | 69 | 0.15 |
| :----------- | -------: | --: | --: | ---: | ----------: | ---: | -------: | ---: |
| C/C++ Header | 3 | 0.3 | 763 | 0.87 | 238 | 0.78 | 302 | 0.60 |
| C++ | 2 | 0.2 | 87 | 0.10 | 22 | 0.07 | 37 | 0.07 |
| R | 4 | 0.4 | 15 | 0.02 | 7 | 0.02 | 94 | 0.19 |
| Rmd | 1 | 0.1 | 10 | 0.01 | 38 | 0.12 | 73 | 0.14 |

52
appveyor.yml

@ -0,0 +1,52 @@
# DO NOT CHANGE the "init" and "install" sections below
# Download script file from GitHub
init:
ps: |
$ErrorActionPreference = "Stop"
Invoke-WebRequest http://raw.github.com/krlmlr/r-appveyor/master/scripts/appveyor-tool.ps1 -OutFile "..\appveyor-tool.ps1"
Import-Module '..\appveyor-tool.ps1'
install:
ps: Bootstrap
cache:
- C:\RLibrary
environment:
NOT_CRAN: true
# env vars that may need to be set, at least temporarily, from time to time
# see https://github.com/krlmlr/r-appveyor#readme for details
# USE_RTOOLS: true
# R_REMOTES_STANDALONE: true
# Adapt as necessary starting from here
build_script:
- travis-tool.sh install_deps
test_script:
- travis-tool.sh run_tests
on_failure:
- 7z a failure.zip *.Rcheck\*
- appveyor PushArtifact failure.zip
artifacts:
- path: '*.Rcheck\**\*.log'
name: Logs
- path: '*.Rcheck\**\*.out'
name: Logs
- path: '*.Rcheck\**\*.fail'
name: Logs
- path: '*.Rcheck\**\*.Rout'
name: Logs
- path: '\*_*.tar.gz'
name: Bits
- path: '\*_*.zip'
name: Bits

27
inst/tinytest/test_ulid.R

@ -0,0 +1,27 @@
library(ulid)
x <- ULIDgenerate()
expect_true(is.character(x))
expect_true(nchar(x) == 26)
x <- ULIDgenerate(20)
expect_true(is.character(x))
expect_true(length(x) == 20)
expect_true(sum(nchar(x)) == 520)
expect_true(all(rle(x)[["values"]] == x))
x <- unmarshal(x)
expect_true(is.data.frame(x))
expect_true(nrow(x) == 20)
expect_true(inherits(x[["ts"]], "POSIXct"))
expect_true(is.character(x[["rnd"]]))
expect_true(sum(nchar(x[["rnd"]])) == 320)
expect_true(all(rle(x[["rnd"]])[["values"]] == x[["rnd"]]))
x <- ts_generate(as.POSIXct("2017-11-01 15:00:00", origin="1970-01-01"))
y <- unmarshal(x)
expect_true(is.data.frame(y))
expect_true(y[["ts"]][[1]] == "2017-11-01 15:00:00")
expect_true(is.character(y[["rnd"]][[1]]))
expect_true(nchar(y[["rnd"]][[1]]) == 16)

6
src/ulid-main.cpp

@ -28,6 +28,12 @@ CharacterVector ts_generate(Rcpp::DatetimeVector tsv) {
return(c);
}
inline long intrand() {
GetRNGstate();
long ret = (long)(unif_rand()*RAND_MAX);
PutRNGstate();
return(ret);
}
//' Generate ULID
//'

5
src/ulid.h

@ -1,4 +1,5 @@
#pragma once
#ifndef ULID_HH
#define ULID_HH
// http://stackoverflow.com/a/23981011
#ifdef __SIZEOF_INT128__
@ -10,3 +11,5 @@
#else
#include "ulid_struct.h"
#endif // ULIDUINT128
#endif // ULID_HH

81
src/ulid_struct.h

@ -1,4 +1,5 @@
#pragma once
#ifndef ULID_STRUCT_HH
#define ULID_STRUCT_HH
#include <chrono>
#include <cstdlib>
@ -7,6 +8,8 @@
#include <random>
#include <vector>
extern long intrand();
namespace ulid {
/**
@ -248,7 +251,7 @@ struct ULID {
* EncodeTime will encode the first 6 bytes of a uint8_t array to the passed
* timestamp
* */
void EncodeTime(time_t timestamp, ULID& ulid) {
inline 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);
@ -260,7 +263,7 @@ void EncodeTime(time_t timestamp, ULID& ulid) {
/**
* EncodeTimeNow will encode a ULID using the time obtained using std::time(nullptr)
* */
void EncodeTimeNow(ULID& ulid) {
inline void EncodeTimeNow(ULID& ulid) {
EncodeTime(std::time(nullptr), ulid);
}
@ -268,7 +271,7 @@ void EncodeTimeNow(ULID& 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) {
inline 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);
@ -278,7 +281,7 @@ void EncodeTimeSystemClockNow(ULID& 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) {
inline void EncodeEntropy(const std::function<uint8_t()>& rng, ULID& ulid) {
ulid.data[6] = rng();
ulid.data[7] = rng();
ulid.data[8] = rng();
@ -297,26 +300,36 @@ void EncodeEntropy(const std::function<uint8_t()>& rng, ULID& ulid) {
* 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;
ulid.data[6] = (intrand() * 255ull) / RAND_MAX;
ulid.data[7] = (intrand() * 255ull) / RAND_MAX;
ulid.data[8] = (intrand() * 255ull) / RAND_MAX;
ulid.data[9] = (intrand() * 255ull) / RAND_MAX;
ulid.data[10] = (intrand() * 255ull) / RAND_MAX;
ulid.data[11] = (intrand() * 255ull) / RAND_MAX;
ulid.data[12] = (intrand() * 255ull) / RAND_MAX;
ulid.data[13] = (intrand() * 255ull) / RAND_MAX;
ulid.data[14] = (intrand() * 255ull) / RAND_MAX;
ulid.data[15] = (intrand() * 255ull) / RAND_MAX;
// 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);
static 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) {
inline 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);
@ -332,7 +345,7 @@ void EncodeEntropyMt19937(std::mt19937& generator, ULID& ulid) {
/**
* 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) {
inline void Encode(time_t timestamp, const std::function<uint8_t()>& rng, ULID& ulid) {
EncodeTime(timestamp, ulid);
EncodeEntropy(rng, ulid);
}
@ -340,7 +353,7 @@ void Encode(time_t timestamp, const std::function<uint8_t()>& rng, ULID& ulid) {
/**
* EncodeNowRand = EncodeTimeNow + EncodeEntropyRand.
* */
void EncodeNowRand(ULID& ulid) {
inline void EncodeNowRand(ULID& ulid) {
EncodeTimeNow(ulid);
EncodeEntropyRand(ulid);
}
@ -348,7 +361,7 @@ void EncodeNowRand(ULID& ulid) {
/**
* Create will create a ULID with a timestamp and a generator.
* */
ULID Create(time_t timestamp, const std::function<uint8_t()>& rng) {
inline ULID Create(time_t timestamp, const std::function<uint8_t()>& rng) {
ULID ulid;
Encode(timestamp, rng, ulid);
return ulid;
@ -357,7 +370,7 @@ ULID Create(time_t timestamp, const std::function<uint8_t()>& rng) {
/**
* CreateNowRand:EncodeNowRand = Create:Encode.
* */
ULID CreateNowRand() {
inline ULID CreateNowRand() {
ULID ulid;
EncodeNowRand(ulid);
return ulid;
@ -366,7 +379,7 @@ ULID CreateNowRand() {
/**
* Crockford's Base32
* */
const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
static const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
/**
* MarshalTo will marshal a ULID to the passed character array.
@ -389,7 +402,7 @@ const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
* entropy:
* follows similarly, except now all components are set to 5 bits.
* */
void MarshalTo(const ULID& ulid, char dst[26]) {
inline 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];
@ -424,7 +437,7 @@ void MarshalTo(const ULID& ulid, char dst[26]) {
/**
* Marshal will marshal a ULID to a std::string.
* */
std::string Marshal(const ULID& ulid) {
inline std::string Marshal(const ULID& ulid) {
char data[27];
data[26] = '\0';
MarshalTo(ulid, data);
@ -434,7 +447,7 @@ std::string Marshal(const ULID& ulid) {
/**
* MarshalBinaryTo will Marshal a ULID to the passed byte array
* */
void MarshalBinaryTo(const ULID& ulid, uint8_t dst[16]) {
inline void MarshalBinaryTo(const ULID& ulid, uint8_t dst[16]) {
// timestamp
dst[0] = ulid.data[0];
dst[1] = ulid.data[1];
@ -459,7 +472,7 @@ void MarshalBinaryTo(const ULID& ulid, uint8_t dst[16]) {
/**
* MarshalBinary will Marshal a ULID to a byte vector.
* */
std::vector<uint8_t> MarshalBinary(const ULID& ulid) {
inline std::vector<uint8_t> MarshalBinary(const ULID& ulid) {
std::vector<uint8_t> dst(16);
MarshalBinaryTo(ulid, dst.data());
return dst;
@ -471,7 +484,7 @@ std::vector<uint8_t> MarshalBinary(const ULID& ulid) {
* 48-57 are digits.
* 65-90 are capital alphabets.
* */
const uint8_t dec[256] = {
static 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,
@ -522,7 +535,7 @@ const uint8_t dec[256] = {
/**
* UnmarshalFrom will unmarshal a ULID from the passed character array.
* */
void UnmarshalFrom(const char str[26], ULID& ulid) {
inline 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);
@ -547,7 +560,7 @@ void UnmarshalFrom(const char str[26], ULID& ulid) {
/**
* Unmarshal will create a new ULID by unmarshaling the passed string.
* */
ULID Unmarshal(const std::string& str) {
inline ULID Unmarshal(const std::string& str) {
ULID ulid;
UnmarshalFrom(str.c_str(), ulid);
return ulid;
@ -556,7 +569,7 @@ ULID Unmarshal(const std::string& str) {
/**
* UnmarshalBinaryFrom will unmarshal a ULID from the passed byte array.
* */
void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) {
inline void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) {
// timestamp
ulid.data[0] = b[0];
ulid.data[1] = b[1];
@ -581,7 +594,7 @@ void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) {
/**
* Unmarshal will create a new ULID by unmarshaling the passed byte vector.
* */
ULID UnmarshalBinary(const std::vector<uint8_t>& b) {
inline ULID UnmarshalBinary(const std::vector<uint8_t>& b) {
ULID ulid;
UnmarshalBinaryFrom(b.data(), ulid);
return ulid;
@ -594,7 +607,7 @@ ULID UnmarshalBinary(const std::vector<uint8_t>& b) {
* 1 if ulid1 is Lexicographically after ulid2
* 0 if ulid1 is same as ulid2
* */
int CompareULIDs(const ULID& ulid1, const ULID& ulid2) {
inline 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;
@ -673,7 +686,7 @@ int CompareULIDs(const ULID& ulid1, const ULID& ulid2) {
/**
* Time will extract the timestamp used to generate a ULID
* */
time_t Time(const ULID& ulid) {
inline time_t Time(const ULID& ulid) {
time_t ans = 0;
ans |= ulid.data[0];
@ -697,3 +710,5 @@ time_t Time(const ULID& ulid) {
}
}; // namespace ulid
#endif // ULID_STRUCT_HH

101
src/ulid_uint128.h

@ -1,4 +1,5 @@
#pragma once
#ifndef ULID_UINT128_HH
#define ULID_UINT128_HH
#include <chrono>
#include <cstdlib>
@ -7,6 +8,8 @@
#include <random>
#include <vector>
extern long intrand();
namespace ulid {
/**
@ -18,7 +21,7 @@ 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) {
inline void EncodeTime(time_t timestamp, ULID& ulid) {
ULID t = static_cast<uint8_t>(timestamp >> 40);
t <<= 8;
@ -48,7 +51,7 @@ void EncodeTime(time_t timestamp, ULID& ulid) {
/**
* EncodeTimeNow will encode a ULID using the time obtained using std::time(nullptr)
* */
void EncodeTimeNow(ULID& ulid) {
inline void EncodeTimeNow(ULID& ulid) {
EncodeTime(std::time(nullptr), ulid);
}
@ -56,7 +59,7 @@ void EncodeTimeNow(ULID& 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) {
inline 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);
@ -66,7 +69,7 @@ void EncodeTimeSystemClockNow(ULID& 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) {
inline void EncodeEntropy(const std::function<uint8_t()>& rng, ULID& ulid) {
ulid = (ulid >> 80) << 80;
ULID e = rng();
@ -109,46 +112,76 @@ void EncodeEntropy(const std::function<uint8_t()>& rng, ULID& ulid) {
void EncodeEntropyRand(ULID& ulid) {
ulid = (ulid >> 80) << 80;
ULID e = (std::rand() * 255ull) / RAND_MAX;
ULID e = (intrand() * 255ull) / RAND_MAX;
e <<= 8;
e |= (std::rand() * 255ull) / RAND_MAX;
e |= (intrand() * 255ull) / RAND_MAX;
e <<= 8;
e |= (std::rand() * 255ull) / RAND_MAX;
e |= (intrand() * 255ull) / RAND_MAX;
e <<= 8;
e |= (std::rand() * 255ull) / RAND_MAX;
e |= (intrand() * 255ull) / RAND_MAX;
e <<= 8;
e |= (std::rand() * 255ull) / RAND_MAX;
e |= (intrand() * 255ull) / RAND_MAX;
e <<= 8;
e |= (std::rand() * 255ull) / RAND_MAX;
e |= (intrand() * 255ull) / RAND_MAX;
e <<= 8;
e |= (std::rand() * 255ull) / RAND_MAX;
e |= (intrand() * 255ull) / RAND_MAX;
e <<= 8;
e |= (std::rand() * 255ull) / RAND_MAX;
e |= (intrand() * 255ull) / RAND_MAX;
e <<= 8;
e |= (std::rand() * 255ull) / RAND_MAX;
e |= (intrand() * 255ull) / RAND_MAX;
e <<= 8;
e |= (std::rand() * 255ull) / RAND_MAX;
e |= (intrand() * 255ull) / RAND_MAX;
// 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);
static 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) {
inline void EncodeEntropyMt19937(std::mt19937& generator, ULID& ulid) {
ulid = (ulid >> 80) << 80;
ULID e = Distribution_0_255(generator);
@ -186,7 +219,7 @@ void EncodeEntropyMt19937(std::mt19937& generator, ULID& ulid) {
/**
* 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) {
inline void Encode(time_t timestamp, const std::function<uint8_t()>& rng, ULID& ulid) {
EncodeTime(timestamp, ulid);
EncodeEntropy(rng, ulid);
}
@ -194,7 +227,7 @@ void Encode(time_t timestamp, const std::function<uint8_t()>& rng, ULID& ulid) {
/**
* EncodeNowRand = EncodeTimeNow + EncodeEntropyRand.
* */
void EncodeNowRand(ULID& ulid) {
inline void EncodeNowRand(ULID& ulid) {
EncodeTimeNow(ulid);
EncodeEntropyRand(ulid);
}
@ -202,7 +235,7 @@ void EncodeNowRand(ULID& ulid) {
/**
* Create will create a ULID with a timestamp and a generator.
* */
ULID Create(time_t timestamp, const std::function<uint8_t()>& rng) {
inline ULID Create(time_t timestamp, const std::function<uint8_t()>& rng) {
ULID ulid = 0;
Encode(timestamp, rng, ulid);
return ulid;
@ -211,7 +244,7 @@ ULID Create(time_t timestamp, const std::function<uint8_t()>& rng) {
/**
* CreateNowRand:EncodeNowRand = Create:Encode.
* */
ULID CreateNowRand() {
inline ULID CreateNowRand() {
ULID ulid = 0;
EncodeNowRand(ulid);
return ulid;
@ -220,7 +253,7 @@ ULID CreateNowRand() {
/**
* Crockford's Base32
* */
const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
static const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
/**
* MarshalTo will marshal a ULID to the passed character array.
@ -243,7 +276,7 @@ const char Encoding[33] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
* entropy:
* follows similarly, except now all components are set to 5 bits.
* */
void MarshalTo(const ULID& ulid, char dst[26]) {
inline 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];
@ -278,7 +311,7 @@ void MarshalTo(const ULID& ulid, char dst[26]) {
/**
* Marshal will marshal a ULID to a std::string.
* */
std::string Marshal(const ULID& ulid) {
inline std::string Marshal(const ULID& ulid) {
char data[27];
data[26] = '\0';
MarshalTo(ulid, data);
@ -288,7 +321,7 @@ std::string Marshal(const ULID& ulid) {
/**
* MarshalBinaryTo will Marshal a ULID to the passed byte array
* */
void MarshalBinaryTo(const ULID& ulid, uint8_t dst[16]) {
inline 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);
@ -313,7 +346,7 @@ void MarshalBinaryTo(const ULID& ulid, uint8_t dst[16]) {
/**
* MarshalBinary will Marshal a ULID to a byte vector.
* */
std::vector<uint8_t> MarshalBinary(const ULID& ulid) {
inline std::vector<uint8_t> MarshalBinary(const ULID& ulid) {
std::vector<uint8_t> dst(16);
MarshalBinaryTo(ulid, dst.data());
return dst;
@ -325,7 +358,7 @@ std::vector<uint8_t> MarshalBinary(const ULID& ulid) {
* 48-57 are digits.
* 65-90 are capital alphabets.
* */
const uint8_t dec[256] = {
static 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,
@ -376,7 +409,7 @@ const uint8_t dec[256] = {
/**
* UnmarshalFrom will unmarshal a ULID from the passed character array.
* */
void UnmarshalFrom(const char str[26], ULID& ulid) {
inline void UnmarshalFrom(const char str[26], ULID& ulid) {
// timestamp
ulid = (dec[int(str[0])] << 5) | dec[int(str[1])];
@ -430,7 +463,7 @@ void UnmarshalFrom(const char str[26], ULID& ulid) {
/**
* Unmarshal will create a new ULID by unmarshaling the passed string.
* */
ULID Unmarshal(const std::string& str) {
inline ULID Unmarshal(const std::string& str) {
ULID ulid;
UnmarshalFrom(str.c_str(), ulid);
return ulid;
@ -439,7 +472,7 @@ ULID Unmarshal(const std::string& str) {
/**
* UnmarshalBinaryFrom will unmarshal a ULID from the passed byte array.
* */
void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) {
inline void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) {
// timestamp
ulid = b[0];
@ -493,7 +526,7 @@ void UnmarshalBinaryFrom(const uint8_t b[16], ULID& ulid) {
/**
* Unmarshal will create a new ULID by unmarshaling the passed byte vector.
* */
ULID UnmarshalBinary(const std::vector<uint8_t>& b) {
inline ULID UnmarshalBinary(const std::vector<uint8_t>& b) {
ULID ulid;
UnmarshalBinaryFrom(b.data(), ulid);
return ulid;
@ -506,14 +539,14 @@ ULID UnmarshalBinary(const std::vector<uint8_t>& b) {
* 1 if ulid1 is Lexicographically after ulid2
* 0 if ulid1 is same as ulid2
* */
int CompareULIDs(const ULID& ulid1, const ULID& ulid2) {
inline 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) {
inline time_t Time(const ULID& ulid) {
time_t ans = 0;
ans |= static_cast<uint8_t>(ulid >> 120);
@ -537,3 +570,5 @@ time_t Time(const ULID& ulid) {
}
}; // namespace ulid
#endif // ULID_UINT128_HH

2
tests/test-all.R

@ -1,2 +0,0 @@
library(testthat)
test_check("ulid")

6
tests/testthat/test-ulid.R

@ -1,6 +0,0 @@
context("minimal package functionality")
test_that("we can do something", {
#expect_that(some_function(), is_a("data.frame"))
})

5
tests/tinytest.R

@ -0,0 +1,5 @@
if ( requireNamespace("tinytest", quietly=TRUE) ){
tinytest::test_package("ulid")
}

2
vignettes/.gitignore

@ -0,0 +1,2 @@
*.html
*.R

45
vignettes/intro-to-ulid.Rmd

@ -0,0 +1,45 @@
---
title: "Introduction to {ulid}"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Introduction to {ulid}}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
```{r setup}
library(ulid)
```
### One
```{r}
ulid::ULIDgenerate()
```
### Many
```{r}
(u <- ulid::ULIDgenerate(20))
```
### Unmarshal
```{r}
unmarshal(u)
```
### Use defined timestamps
```{r}
(ut <- ts_generate(as.POSIXct("2017-11-01 15:00:00", origin="1970-01-01")))
unmarshal(ut)
```
Loading…
Cancel
Save