diff --git a/.busted b/.busted
index ca66496..313c185 100644
--- a/.busted
+++ b/.busted
@@ -1,7 +1,7 @@
return {
default = {
verbose = true,
- coverage = false,
+ coverage = true,
output = "gtest",
},
}
diff --git a/.editorconfig b/.editorconfig
index c039bab..a1b3a15 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -12,13 +12,12 @@ indent_size = 2
[*.rockspec]
indent_style = space
-indent_size = 3
+indent_size = 2
-[*.c]
-indent_style = tab
+[*.md]
+indent_style = space
+indent_size = 2
[Makefile]
indent_style = tab
-
-[*.md]
-indent_style = space
+indent_size = 4
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..5de37de
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,38 @@
+name: Lint
+
+concurrency:
+ # for PR's cancel the running task, if another commit is pushed
+ group: ${{ github.workflow }} ${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+on:
+ # build on PR and push-to-main. This works for short-lived branches, and saves
+ # CPU cycles on duplicated tests.
+ # For long-lived branches that diverge, you'll want to run on all pushes, not
+ # just on push-to-main.
+ pull_request: {}
+ push:
+ branches:
+ - main
+
+
+jobs:
+ lint:
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - uses: leafo/gh-actions-lua@v10
+ with:
+ luaVersion: "5.4"
+
+ - uses: leafo/gh-actions-luarocks@v4
+
+ - name: dependencies
+ run: |
+ make dev
+
+ - name: lint
+ run: |
+ make lint
diff --git a/.github/workflows/unix_build.yml b/.github/workflows/unix_build.yml
new file mode 100644
index 0000000..c60df76
--- /dev/null
+++ b/.github/workflows/unix_build.yml
@@ -0,0 +1,52 @@
+name: "Unix build"
+
+concurrency:
+ # for PR's cancel the running task, if another commit is pushed
+ group: ${{ github.workflow }} ${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
+on:
+ # build on PR and push-to-main. This works for short-lived branches, and saves
+ # CPU cycles on duplicated tests.
+ # For long-lived branches that diverge, you'll want to run on all pushes, not
+ # just on push-to-main.
+ pull_request: {}
+ push:
+ branches:
+ - main
+
+
+jobs:
+ test:
+ runs-on: ubuntu-22.04
+
+ strategy:
+ fail-fast: false
+ matrix:
+ luaVersion: ["5.1", "5.2", "5.3", "5.4", "luajit-2.1.0-beta3", "luajit-openresty"]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - uses: leafo/gh-actions-lua@v10
+ with:
+ luaVersion: ${{ matrix.luaVersion }}
+
+ - uses: leafo/gh-actions-luarocks@v4
+
+ - name: dependencies
+ run: |
+ make dev
+ luarocks install luacov-coveralls
+
+ - name: test
+ run: |
+ make testinst BUSTED='--coverage --Xoutput "--color"'
+
+ - name: Report test coverage
+ if: success()
+ continue-on-error: true
+ run: luacov-coveralls
+ env:
+ COVERALLS_REPO_TOKEN: ${{ github.token }}
diff --git a/.gitignore b/.gitignore
index 43c6239..eae1ed2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,45 @@
+# Compiled Lua sources
+luac.out
+
+# LuaCov files
+*.report.out
+*.stats.out
+
+# luarocks build files
*.rock
+*.zip
+*.tar.gz
+
+# Object files
+*.o
+*.os
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+*.def
+*.exp
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
diff --git a/.luacheckrc b/.luacheckrc
index cf442da..4dc4ec4 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -1,30 +1,32 @@
-std = "ngx_lua"
unused_args = false
redefined = false
max_line_length = false
-
globals = {
+-- "ngx",
}
-
not_globals = {
+ -- deprecated Lua 5.0 functions
"string.len",
"table.getn",
}
-
-ignore = {
- --"6.", -- ignore whitespace warnings
+include_files = {
+ "**/*.lua",
+ "**/*.rockspec",
+ ".busted",
+ ".luacheckrc",
}
+files["spec/**/*.lua"] = {
+ std = "+busted",
+}
exclude_files = {
- --"spec/fixtures/invalid-module.lua",
- --"spec-old-api/fixtures/invalid-module.lua",
+ -- The Github Actions Lua Environment
+ ".lua",
+ ".luarocks",
+ ".install",
}
-
-files["spec/**/*.lua"] = {
- std = "ngx_lua+busted",
-}
diff --git a/.luacov b/.luacov
new file mode 100644
index 0000000..3267fca
--- /dev/null
+++ b/.luacov
@@ -0,0 +1,5 @@
+modules = {
+ ["uuid.*"] = "src"
+}
+
+runreport = true
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index cdfb872..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-language: python
-sudo: false
-
-env:
- - LUA="lua 5.1"
- - LUA="lua 5.2"
- - LUA="lua 5.3"
- - LUA="luajit 2.0"
- - LUA="luajit 2.0 --compat 5.2"
- - LUA="luajit 2.1"
- - LUA="luajit 2.1 --compat 5.2"
-
-before_install:
- - pip install hererocks
- - hererocks here -r^ --$LUA
- - source here/bin/activate
- - luarocks install luacheck
- - luarocks install busted
- - luarocks install luacov-coveralls
-
-install:
- - luarocks install luasocket
- - luarocks make
-
-script:
- - luacheck .
- - busted
-
-#after_success:
- #- luacov-coveralls
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..4aede0b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,50 @@
+# CHANGELOG
+
+## Versioning
+
+This library is versioned based on Semantic Versioning ([SemVer](https://semver.org/)).
+
+#### Version scoping
+
+The scope of what is covered by the version number excludes:
+
+- error messages; the text of the messages can change, unless specifically documented.
+
+#### Releasing new versions
+
+- create a release branch
+- update the changelog below
+- update version and copyright-years in `./LICENSE.md` (bottom) and `./src/uuid.lua` (in
+ doc-comments header)
+- create a new rockspec and update the version inside the new rockspec:
+ `cp uuid-dev-1.rockspec ./rockspecs/uuid-X.Y.Z-1.rockspec`
+- test: run `make test` and `make lint`
+- clean and render the docs: run `make clean` and `make docs`
+- commit the changes as `release X.Y.Z`
+- push the commit, and create a release PR
+- after merging tag the release commit with `X.Y.Z`
+- upload to LuaRocks:
+ `luarocks upload ./rockspecs/uuid-X.Y.Z-1.rockspec --api-key=ABCDEFGH`
+- test the newly created rock:
+ `luarocks install uuid`
+
+## Version history
+
+### Version X.Y.Z, unreleased
+
+- bla bla
+
+### Version 0.3, released 11-Jul-2021
+
+- Fix: set proper type for UUIDv4 type
+- Feat: improve seeding for OpenResty
+- Doc: fix link in readme
+
+### Version 0.2, released 09-May-2013
+
+- Bugfix; 0-hex was displayed as "" instead of "00", making some uuids too short
+- Bugfix; math.randomseed() overflow caused bad seeding
+
+### Version 0.1, released 28-Apr-2013
+
+ - initial version
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..c13b4c6
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,204 @@
+# Apache 2.0 License
+```
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2012 Rackspace, 2013-2021 Thijs Schreijer
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+```
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..a05fc71
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,115 @@
+# additional Busted options to pass
+BUSTED:=
+
+# SCM rockspec label; scm/cvs/dev
+SCM_LABEL:=$(shell cat *.rockspec | grep "local package_version" | sed "s/ //g" | sed "s/localpackage_version=//g" | sed "s/\"//g")
+ROCK_REV:=$(shell cat *.rockspec | grep "local rockspec_revision" | sed "s/ //g" | sed "s/localrockspec_revision=//g" | sed "s/\"//g")
+ROCK_NAME:=$(shell cat *.rockspec | grep "local package_name" | sed "s/ //g" | sed "s/localpackage_name=//g" | sed "s/\"//g")
+ROCKSPEC:=${ROCK_NAME}-${SCM_LABEL}-${ROCK_REV}.rockspec
+TAB=$(shell printf "\t")
+
+# dev/test dependencies; versions can be pinned. Example: "ldoc 1.4.6"
+DEV_ROCKS = "busted" "luacheck" "ldoc" "luacov"
+
+
+target_not_specified: help
+ @exit 1
+
+
+help:
+ @echo "Available make targets for ${ROCK_NAME}:"
+ @echo ""
+ @echo "install: uses LuaRocks to install ${ROCK_NAME}"
+ @echo "uninstall: uninstalls ALL versions of ${ROCK_NAME} (using LuaRocks with"
+ @echo " the '--force' flag)"
+ @echo "clean: removes LuaCov output, packed rocks, and restores docs to the"
+ @echo " last commited version"
+ @echo "test: runs the test suite using Busted"
+ @echo "testinst: installs ${ROCK_NAME} and runs tests using the installed version"
+ @echo " (this modifies the local installation, but also tests the"
+ @echo " .rockspec file). This is best used when testing in CI."
+ @echo "lint: will validate all 'rockspec' files using LuaRocks, and the"
+ @echo " '.lua' files with LuaCheck"
+ @echo "doc/docs: regenerates the documentation using LDoc"
+ @echo "deps: installs the module dependencies"
+ @echo "dev: installs the development dependencies (Busted, LuaCheck, etc.)"
+ @echo "help: displays this list of make targets"
+ @echo ""
+
+
+install: luarocks
+ luarocks make
+
+
+uninstall: luarocks
+ if (luarocks list --porcelain ${ROCK_NAME} | grep "^${ROCK_NAME}${TAB}" | grep -q "installed") ; then \
+ luarocks remove ${ROCK_NAME} --force; \
+ fi;
+
+
+# note: restore the docs to the last committed version
+clean: clean_luacov clean_luarocks clean_doc
+ git checkout docs
+
+
+.PHONY: test
+test: clean_luacov dev
+ busted ${BUSTED}
+
+
+# test while having the code installed; also tests the rockspec, but
+# this will modify the local luarocks installation/tree!!
+.PHONY: testinst
+testinst: clean_luacov dev uninstall install
+ busted --lpath="" --cpath="" ${BUSTED}
+
+
+.PHONY: lint
+lint: dev
+ @echo "luarocks lint ..."
+ @for spec in $(shell find . -type f -name "*.rockspec") ; do \
+ (luarocks lint $$spec && echo "$$spec [OK]") || (echo "$$spec [NOK]"; exit 1); \
+ done
+ luacheck .
+
+
+.PHONY: doc
+doc: clean_doc dev
+ mkdir -p ./docs
+ ldoc . --date=""
+
+
+.PHONY: docs
+docs: doc
+
+
+.PHONY: deps
+deps: luarocks
+ luarocks install $(ROCKSPEC) --deps-only
+
+
+.PHONY: dev
+dev: luarocks deps
+ @for rock in $(DEV_ROCKS) ; do \
+ (luarocks list --porcelain $$rock | grep -q "installed") || (luarocks install $$rock || exit 1); \
+ done;
+
+
+.PHONY: clean_doc
+clean_doc:
+ $(RM) -r docs
+
+
+.PHONY: clean_luarocks
+clean_luarocks:
+ $(RM) *.rock
+
+
+.PHONY: clean_luacov
+clean_luacov:
+ $(RM) luacov.report.out luacov.stats.out
+
+
+.PHONY: luarocks
+luarocks:
+ @which luarocks > /dev/null || (echo "LuaRocks was not found. Please install and/or make available in the path." && exit 1)
diff --git a/README.md b/README.md
index c4df1cb..6f8d1f0 100644
--- a/README.md
+++ b/README.md
@@ -1,47 +1,21 @@
-[](https://travis-ci.com/Tieske/uuid)
+[](https://github.com/Tieske/uuid/actions/workflows/unix_build.yml)
+[](https://coveralls.io/github/Tieske/uuid)
+[](https://github.com/Tieske/uuid/actions/workflows/lint.yml)
+[](CHANGELOG.md)
-uuid
-====
+# uuid
-Modified module from [Rackspace](https://github.com/kans/zirgo/blob/807250b1af6725bad4776c931c89a784c1e34db2/util/uuid.lua) original. Generates uuids in pure Lua.
+Generates uuids in pure Lua, but requires a
+good random generator/seed or a unique string. Please check the documentation.
-Notes
-=====
-Please read [documentation](https://tieske.github.io/uuid/) carefully regarding random seeds or unique strings to be provided to get a decent randomized uuid value.
+## License and copyright
-Home
-====
-[Source code](https://github.com/Tieske/uuid) is on github
+See [LICENSE.md](LICENSE.md)
-License & copyright
-===================
-Rackspace (original) and Thijs Schreijer (modifications), Apache 2.0, see `uuid.lua`
+## Documentation
-Install
-=======
-Use LuaRocks. To fetch and install from a LuaRocks server do `luarocks install uuid`.
-For a development installation from local source, do `luarocks make` from the main directory.
+See [online documentation](https://Tieske.github.io/uuid/)
-Test
-====
-Tests are available and can be executed using [busted](http://olivinelabs.com/busted/),
-and LuaCheck for linting.
-
-Changes
-=======
-
-0.3 11-Jul-2021
-
- - Fix: set proper type for UUIDv4 type
- - Feat: improve seeding for OpenResty
- - Doc: fix link in readme
-
-0.2 09-May-2013
-
- - Bugfix; 0-hex was displayed as "" instead of "00", making some uuids too short
- - Bugfix; math.randomseed() overflow caused bad seeding
-
-0.1 28-Apr-2013
-
- - initial version
+## Changelog & Versioning
+See [CHANGELOG.md](CHANGELOG.md)
diff --git a/config.ld b/config.ld
index a3e59d8..95e989e 100644
--- a/config.ld
+++ b/config.ld
@@ -1,8 +1,15 @@
project='uuid'
title='uuid generator'
description='Module to generate uuids in pure Lua'
-format='discount'
-file='./src/'
+
+format='markdown'
+use_markdown_titles = true
+style="./doc_topics/"
+
+file={'./src/'}
+topics={'./doc_topics/', './LICENSE.md', './CHANGELOG.md'}
+
dir='docs'
-readme='readme.md'
-style='./docs/'
+sort=true
+sort_modules=true
+all=false
diff --git a/doc_topics/01-introduction.md b/doc_topics/01-introduction.md
new file mode 100644
index 0000000..329ce30
--- /dev/null
+++ b/doc_topics/01-introduction.md
@@ -0,0 +1,16 @@
+# 1. Introduction
+
+High quality UUIDs v4 can only be generated if the source of the random data is good enough.
+If it is predictable then it might become a security risk.
+
+Read up on the use of the `randomseed` and `seed` functions if you need a pure-Lua
+implementation. Preferably the Lua-System module is used or an even stronger random
+number generator.
+
+**Important:** the random seed is a global piece of data. Hence setting it is
+an application level responsibility, libraries should never set it!
+
+See this issue; [https://github.com/Kong/kong/issues/478](https://github.com/Kong/kong/issues/478)
+It demonstrates the problem of using time as a random seed. Specifically when used from multiple processes.
+So make sure to seed only once, application wide. And to not have multiple processes do that
+simultaneously.
diff --git a/doc_topics/ldoc.css b/doc_topics/ldoc.css
new file mode 100644
index 0000000..5b9fbbf
--- /dev/null
+++ b/doc_topics/ldoc.css
@@ -0,0 +1,291 @@
+body {
+ color: #47555c;
+ font-size: 16px;
+ font-family: "Open Sans", sans-serif;
+ margin: 0;
+ background: #eff4ff;
+}
+
+a:link { color: #008fee; }
+a:visited { color: #008fee; }
+a:hover { color: #22a7ff; }
+
+h1 { font-size:26px; font-weight: normal; }
+h2 { font-size:22px; font-weight: normal; }
+h3 { font-size:18px; font-weight: normal; }
+h4 { font-size:16px; font-weight: bold; }
+
+hr {
+ height: 1px;
+ background: #c1cce4;
+ border: 0px;
+ margin: 15px 0;
+}
+
+code, tt {
+ font-family: monospace;
+}
+span.parameter {
+ font-family: monospace;
+ font-weight: bold;
+ color: rgb(99, 115, 131);
+}
+span.parameter:after {
+ content:":";
+}
+span.types:before {
+ content:"(";
+}
+span.types:after {
+ content:")";
+}
+.type {
+ font-weight: bold; font-style:italic
+}
+
+p.name {
+ font-family: "Andale Mono", monospace;
+}
+
+#navigation {
+ float: left;
+ background-color: white;
+ border-right: 1px solid #d3dbec;
+ border-bottom: 1px solid #d3dbec;
+
+ width: 14em;
+ vertical-align: top;
+ overflow: visible;
+}
+
+#navigation br {
+ display: none;
+}
+
+#navigation h1 {
+ background-color: white;
+ border-bottom: 1px solid #d3dbec;
+ padding: 15px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+#navigation h2 {
+ font-size: 18px;
+ background-color: white;
+ border-bottom: 1px solid #d3dbec;
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ margin-top: 30px;
+ margin-bottom: 0px;
+}
+
+#content h1 {
+ background-color: #2c3e67;
+ color: white;
+ padding: 15px;
+ margin: 0px;
+}
+
+#content h2 {
+ background-color: #6c7ea7;
+ color: white;
+ padding: 15px;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ margin-top: 0px;
+}
+
+#content h2 a {
+ background-color: #6c7ea7;
+ color: white;
+ text-decoration: none;
+}
+
+#content h2 a:hover {
+ text-decoration: underline;
+}
+
+#content h3 {
+ font-style: italic;
+ padding-top: 15px;
+ padding-bottom: 4px;
+ margin-right: 15px;
+ margin-left: 15px;
+ margin-bottom: 5px;
+ border-bottom: solid 1px #bcd;
+}
+
+#content h4 {
+ margin-right: 15px;
+ margin-left: 15px;
+ border-bottom: solid 1px #bcd;
+}
+
+#content pre {
+ margin: 15px;
+}
+
+pre {
+ background-color: rgb(50, 55, 68);
+ color: white;
+ border-radius: 3px;
+ /* border: 1px solid #C0C0C0; /* silver */
+ padding: 15px;
+ overflow: auto;
+ font-family: "Andale Mono", monospace;
+}
+
+#content ul pre.example {
+ margin-left: 0px;
+}
+
+table.index {
+/* border: 1px #00007f; */
+}
+table.index td { text-align: left; vertical-align: top; }
+
+#navigation ul
+{
+ font-size:1em;
+ list-style-type: none;
+ margin: 1px 1px 10px 1px;
+ padding-left: 20px;
+}
+
+#navigation li {
+ text-indent: -1em;
+ display: block;
+ margin: 3px 0px 0px 22px;
+}
+
+#navigation li li a {
+ margin: 0px 3px 0px -1em;
+}
+
+#content {
+ margin-left: 14em;
+}
+
+#content p {
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+#content table {
+ padding-left: 15px;
+ padding-right: 15px;
+ background-color: white;
+}
+
+#content p, #content table, #content ol, #content ul, #content dl {
+ max-width: 900px;
+}
+
+#about {
+ padding: 15px;
+ padding-left: 16em;
+ background-color: white;
+ border-top: 1px solid #d3dbec;
+ border-bottom: 1px solid #d3dbec;
+}
+
+table.module_list, table.function_list {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #cccccc;
+ border-collapse: collapse;
+ margin: 15px;
+}
+table.module_list td, table.function_list td {
+ border-width: 1px;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ border: solid 1px rgb(193, 204, 228);
+}
+table.module_list td.name, table.function_list td.name {
+ background-color: white; min-width: 200px; border-right-width: 0px;
+}
+table.module_list td.summary, table.function_list td.summary {
+ background-color: white; width: 100%; border-left-width: 0px;
+}
+
+dl.function {
+ margin-right: 15px;
+ margin-left: 15px;
+ border-bottom: solid 1px rgb(193, 204, 228);
+ border-left: solid 1px rgb(193, 204, 228);
+ border-right: solid 1px rgb(193, 204, 228);
+ background-color: white;
+}
+
+dl.function dt {
+ color: rgb(99, 123, 188);
+ font-family: monospace;
+ border-top: solid 1px rgb(193, 204, 228);
+ padding: 15px;
+}
+
+dl.function dd {
+ margin-left: 15px;
+ margin-right: 15px;
+ margin-top: 5px;
+ margin-bottom: 15px;
+}
+
+#content dl.function dd h3 {
+ margin-top: 0px;
+ margin-left: 0px;
+ padding-left: 0px;
+ font-size: 16px;
+ color: rgb(128, 128, 128);
+ border-bottom: solid 1px #def;
+}
+
+#content dl.function dd ul, #content dl.function dd ol {
+ padding: 0px;
+ padding-left: 15px;
+ list-style-type: none;
+}
+
+ul.nowrap {
+ overflow:auto;
+ white-space:nowrap;
+}
+
+.section-description {
+ padding-left: 15px;
+ padding-right: 15px;
+}
+
+/* stop sublists from having initial vertical space */
+ul ul { margin-top: 0px; }
+ol ul { margin-top: 0px; }
+ol ol { margin-top: 0px; }
+ul ol { margin-top: 0px; }
+
+/* make the target distinct; helps when we're navigating to a function */
+a:target + * {
+ background-color: #FF9;
+}
+
+
+/* styles for prettification of source */
+pre .comment { color: #bbccaa; }
+pre .constant { color: #a8660d; }
+pre .escape { color: #844631; }
+pre .keyword { color: #ffc090; font-weight: bold; }
+pre .library { color: #0e7c6b; }
+pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; }
+pre .string { color: #8080ff; }
+pre .number { color: #f8660d; }
+pre .operator { color: #2239a8; font-weight: bold; }
+pre .preprocessor, pre .prepro { color: #a33243; }
+pre .global { color: #c040c0; }
+pre .user-keyword { color: #800080; }
+pre .prompt { color: #558817; }
+pre .url { color: #272fc2; text-decoration: underline; }
diff --git a/spec/uuid_spec.lua b/spec/uuid_spec.lua
index 5323afa..043e569 100644
--- a/spec/uuid_spec.lua
+++ b/spec/uuid_spec.lua
@@ -1,14 +1,25 @@
-local uuid = require("uuid")
-- start tests
describe("Testing uuid library", function()
-
+ local uuid
+ local old_get_random_bytes
before_each(function()
+ uuid = require("uuid")
+
+ old_get_random_bytes = uuid.get_random_bytes
+ uuid.get_random_bytes = function(n)
+ return string.char(0):rep(n)
+ end
+ end)
+
+ after_each(function()
+ uuid.get_random_bytes = old_get_random_bytes
end)
it("tests generating a uuid", function()
- assert.is_string(uuid.new())
- assert.is_string(uuid())
+ local id = uuid.new()
+ assert.are.same('00000000-0000-4000-8000-000000000000', id)
+ assert.are.same(id, uuid())
end)
it("tests the format of the generated uuid", function()
@@ -27,9 +38,12 @@ describe("Testing uuid library", function()
assert.has_error(function() uuid("123a4::xxyy;;590") end) -- too short after clean
assert.has_error(function() uuid(true) end) -- not a string
assert.has_error(function() uuid(123) end) -- not a string
- assert.not_has_error(function() uuid("abcdefabcdef") end) -- hex only
- assert.not_has_error(function() uuid("123456789012") end) -- right size
- assert.not_has_error(function() uuid("1234567890123") end) -- oversize
+ assert.has_no.error(function() uuid("abcdefabcdef") end) -- hex only
+ assert.same('00000000-0000-4000-8000-abcdefabcdef', uuid("abcdefabcdef"))
+ assert.has_no.error(function() uuid("123456789012") end) -- right size
+ assert.same('00000000-0000-4000-8000-123456789012', uuid("123456789012"))
+ assert.has_no.error(function() uuid("1234567890123") end) -- oversize
+ assert.same('00000000-0000-4000-8000-123456789012', uuid("1234567890123"))
end)
it("tests uuid.seed() using luasocket gettime() if available, os.time() if unavailable", function()
diff --git a/src/uuid.lua b/src/uuid.lua
index f205a48..b4add0a 100644
--- a/src/uuid.lua
+++ b/src/uuid.lua
@@ -1,53 +1,28 @@
---------------------------------------------------------------------------------------
-- Copyright 2012 Rackspace (original), 2013-2021 Thijs Schreijer (modifications)
--
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
+-- see [http://www.ietf.org/rfc/rfc4122.txt](http://www.ietf.org/rfc/rfc4122.txt)
--
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS-IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- see http://www.ietf.org/rfc/rfc4122.txt
---
--- Note that this is not a true version 4 (random) UUID. Since `os.time()` precision is only 1 second, it would be hard
--- to guarantee spacial uniqueness when two hosts generate a uuid after being seeded during the same second. This
--- is solved by using the node field from a version 1 UUID. It represents the mac address.
---
--- 28-apr-2013 modified by Thijs Schreijer from the original [Rackspace code](https://github.com/kans/zirgo/blob/807250b1af6725bad4776c931c89a784c1e34db2/util/uuid.lua) as a generic Lua module.
--- Regarding the above mention on `os.time()`; the modifications use the `socket.gettime()` function from LuaSocket
--- if available and hence reduce that problem (provided LuaSocket has been loaded before uuid).
---
--- **Important:** the random seed is a global piece of data. Hence setting it is
--- an application level responsibility, libraries should never set it!
---
--- See this issue; [https://github.com/Kong/kong/issues/478](https://github.com/Kong/kong/issues/478)
--- It demonstrates the problem of using time as a random seed. Specifically when used from multiple processes.
--- So make sure to seed only once, application wide. And to not have multiple processes do that
--- simultaneously.
+-- @license MIT, see `LICENSE.md`.
local M = {}
local math = require('math')
local os = require('os')
local string = require('string')
+local format = string.format
+local char = string.char
+local byte = string.byte
local bitsize = 32 -- bitsize assumed for Lua VM. See randomseed function below.
local lua_version = tonumber(_VERSION:match("%d%.*%d*")) -- grab Lua version used
local MATRIX_AND = {{0,0},{0,1} }
local MATRIX_OR = {{0,1},{1,1}}
-local HEXES = '0123456789abcdef'
local math_floor = math.floor
local math_random = math.random
local math_abs = math.abs
-local string_sub = string.sub
local to_number = tonumber
local assert = assert
local type = type
@@ -65,18 +40,76 @@ local function BITWISE(x, y, matrix)
return z
end
-local function INT2HEX(x)
- local s,base = '',16
- local d
- while x > 0 do
- d = x % base + 1
- x = math_floor(x/base)
- s = string_sub(HEXES, d, d)..s
+
+-- converts a HW identifier (mac address) to a string of byte values
+local hwaddr_to_bytes do
+ -- typically there is 1 address, so cache that one
+ local hwaddr_stored, bytes_stored
+
+ local function convert(hwaddr)
+ assert(type(hwaddr)=="string", "Expected hex string, got "..type(hwaddr))
+ -- Cleanup provided string, assume mac address, so start from back and cleanup until we've got 12 characters
+ local hwaddr_clean = hwaddr:gsub("[^%x]",""):sub(1,12):lower()
+ assert(#hwaddr_clean == 12, "Provided string did not contain at least 12 hex characters, got '"..hwaddr.."'")
+
+ return char(
+ to_number(hwaddr_clean:sub(1, 2), 16),
+ to_number(hwaddr_clean:sub(3, 4), 16),
+ to_number(hwaddr_clean:sub(5, 6), 16),
+ to_number(hwaddr_clean:sub(7, 8), 16),
+ to_number(hwaddr_clean:sub(9, 10), 16),
+ to_number(hwaddr_clean:sub(11, 12), 16)
+ )
+ end
+
+ function hwaddr_to_bytes(hwaddr)
+ if hwaddr_stored ~= hwaddr then
+ hwaddr_stored = hwaddr
+ bytes_stored = convert(hwaddr)
+ end
+ return bytes_stored
end
- while #s < 2 do s = "0" .. s end
- return s
end
+
+----------------------------------------------------------------------------
+-- [REPLACE] Should return a set of random bytes.
+-- This function MUST be replaced by a proper implementation. This is done
+-- purposely to force the user to think about the randomness of the bytes
+-- generated.
+-- @tparam integer n number of bytes to generate
+-- @treturn string of random bytes
+-- @usage
+-- local ok, system = pcall(require, "system")
+-- if ok then
+-- -- set the Lua-System random generator as the one to use
+-- uuid.get_random_bytes = system.get_random_bytes
+-- else
+-- -- use the weak one as a fallback
+-- uuid.get_random_bytes = uuid.weak_random_bytes
+-- end
+function M.get_random_bytes(n)
+ assert(n, "Expected number of bytes to generate")
+ error("Not implemented, please set a function to generate random bytes")
+end
+
+----------------------------------------------------------------------------
+-- Returns a set of random bytes. This implementation uses the default Lua
+-- `math.random()` function, which is not very random. It is recommended to
+-- replace this function with a better implementation.
+-- @tparam integer n number of bytes to generate
+-- @treturn string of random bytes
+-- @usage
+function M.weak_random_bytes(n)
+ assert(n, "Expected number of bytes to generate")
+ local bytes = {}
+ for i = 1, n do
+ bytes[i] = char(math_random(0, 255))
+ end
+ return table.concat(bytes)
+end
+
+
----------------------------------------------------------------------------
-- Creates a new uuid. Either provide a unique hex string, or make sure the
-- random seed is properly set. The module table itself is a shortcut to this
@@ -92,66 +125,41 @@ end
-- eg. `my_uuid = uuid(my_networkcard_macaddress)`
--
-- @return a properly formatted uuid string
--- @param hwaddr (optional) string containing a unique hex value (e.g.: `00:0c:29:69:41:c6`), to be used to compensate for the lesser `math_random()` function. Use a mac address for solid results. If omitted, a fully randomized uuid will be generated, but then you must ensure that the random seed is set properly!
+-- @param hwaddr (optional) string containing a unique hex value (e.g.: `00:0c:29:69:41:c6`),
+-- to be used to compensate for the lesser `math_random()` function. Use a mac address for solid
+-- results. If omitted, a fully randomized uuid will be generated, but then you must ensure that
+-- the random seed is set properly!
-- @usage
-- local uuid = require("uuid")
-- print("here's a new uuid: ",uuid())
function M.new(hwaddr)
- -- bytes are treated as 8bit unsigned bytes.
- local bytes = {
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255),
- math_random(0, 255)
- }
-
+ local bytes
if hwaddr then
assert(type(hwaddr)=="string", "Expected hex string, got "..type(hwaddr))
-- Cleanup provided string, assume mac address, so start from back and cleanup until we've got 12 characters
- local i,str = #hwaddr, hwaddr
- hwaddr = ""
- while i>0 and #hwaddr<12 do
- local c = str:sub(i,i):lower()
- if HEXES:find(c, 1, true) then
- -- valid HEX character, so append it
- hwaddr = c..hwaddr
- end
- i = i - 1
- end
- assert(#hwaddr == 12, "Provided string did not contain at least 12 hex characters, retrieved '"..hwaddr.."' from '"..str.."'")
-
- -- no split() in lua. :(
- bytes[11] = to_number(hwaddr:sub(1, 2), 16)
- bytes[12] = to_number(hwaddr:sub(3, 4), 16)
- bytes[13] = to_number(hwaddr:sub(5, 6), 16)
- bytes[14] = to_number(hwaddr:sub(7, 8), 16)
- bytes[15] = to_number(hwaddr:sub(9, 10), 16)
- bytes[16] = to_number(hwaddr:sub(11, 12), 16)
+ local hwaddr_clean = hwaddr:gsub("[^%x]",""):sub(1,12):lower()
+ assert(#hwaddr_clean == 12, "Provided string did not contain at least 12 hex characters, got '"..hwaddr.."'")
+
+ bytes = hwaddr_to_bytes(hwaddr_clean)
+ else
+ bytes = M.get_random_bytes(6)
end
+ local byte_7, byte_9
-- set the version
- bytes[7] = BITWISE(bytes[7], 0x0f, MATRIX_AND)
- bytes[7] = BITWISE(bytes[7], 0x40, MATRIX_OR)
+ byte_7 = byte(M.get_random_bytes(1))
+ byte_7 = BITWISE(byte_7, 0x0f, MATRIX_AND)
+ byte_7 = BITWISE(byte_7, 0x40, MATRIX_OR)
+ byte_7 = char(byte_7)
-- set the variant
- bytes[9] = BITWISE(bytes[9], 0x3f, MATRIX_AND)
- bytes[9] = BITWISE(bytes[9], 0x80, MATRIX_OR)
- return INT2HEX(bytes[1])..INT2HEX(bytes[2])..INT2HEX(bytes[3])..INT2HEX(bytes[4]).."-"..
- INT2HEX(bytes[5])..INT2HEX(bytes[6]).."-"..
- INT2HEX(bytes[7])..INT2HEX(bytes[8]).."-"..
- INT2HEX(bytes[9])..INT2HEX(bytes[10]).."-"..
- INT2HEX(bytes[11])..INT2HEX(bytes[12])..INT2HEX(bytes[13])..INT2HEX(bytes[14])..INT2HEX(bytes[15])..INT2HEX(bytes[16])
+ byte_9 = byte(M.get_random_bytes(1))
+ byte_9 = BITWISE(byte_9, 0x3f, MATRIX_AND)
+ byte_9 = BITWISE(byte_9, 0x80, MATRIX_OR)
+ byte_9 = char(byte_9)
+
+ bytes = M.get_random_bytes(6) .. byte_7 .. M.get_random_bytes(1) .. byte_9 .. M.get_random_bytes(1) .. bytes
+
+ return format("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", byte(bytes, 1, 16))
end
----------------------------------------------------------------------------
diff --git a/uuid-dev-1.rockspec b/uuid-dev-1.rockspec
index 6ca270f..069a156 100644
--- a/uuid-dev-1.rockspec
+++ b/uuid-dev-1.rockspec
@@ -8,7 +8,7 @@ local github_repo_name = package_name
package = package_name
version = package_version.."-"..rockspec_revision
source = {
- url = "git://github.com/"..github_account_name.."/"..github_repo_name..".git",
+ url = "git+https://github.com/"..github_account_name.."/"..github_repo_name..".git",
branch = (package_version == "dev") and "master" or nil,
tag = (package_version ~= "dev") and package_version or nil,
}
@@ -24,7 +24,7 @@ description = {
}
dependencies = {
- "lua >= 5.1",
+ "lua >= 5.1, < 5.5",
}
build = {
@@ -32,4 +32,7 @@ build = {
modules = {
["uuid"] = "src/uuid.lua",
},
+ copy_directories = {
+ "docs",
+ },
}