on master help sign in
47 files changed+2336−1177
since splitsnapshots
unifiedsplit
.git-blame-ignore-revs+2 .github/workflows/haskell-ci-bench.yml+241 .github/workflows/haskell-ci.yml+262 .gitignore+13 CHANGES−99 CONTRIBUTORS+6−1 Data/UUID/Internal.hs−431 Data/UUID/V1.hs−138 Data/UUID/V4.hs−29 Policy.md+11 README.md+12 cabal.bench.project+4 cabal.haskell-ci+7 cabal.project+15 gen-ci.sh+5 tests/BenchByteString.hs−82 tests/TestUUID.hs−202 tests/summarize-bench-results.pl−14 LICENSE → uuid-bench/LICENSE uuid-bench/bench/uuid-benchmark.hs+33 tests/BenchUUID.hs → uuid-bench/bench/uuid-types-benchmark.hs+62−55 uuid-bench/src-uuid+1 uuid-bench/src-uuid-types+1 uuid-bench/uuid-bench.cabal+126 uuid-types/ChangeLog.md+31 uuid-types/LICENSE+28 Setup.hs → uuid-types/Setup.hs uuid-types/cabal.project+1 uuid-types/src/Data/UUID/Types.hs+47 uuid-types/src/Data/UUID/Types/Internal.hs+575 Data/UUID/Builder.hs → uuid-types/src/Data/UUID/Types/Internal/Builder.hs+21−5 uuid-types/tests/TestUUID.hs+209 uuid-types/uuid-types.cabal+99 uuid.cabal−100 uuid/CHANGES.md+186 Demo.hs → uuid/Demo.hs−1 uuid/LICENSE+28 uuid/Setup.hs+4 Data/UUID.hs → uuid/src/Data/UUID.hs+16−14 Data/UUID/Named.hs → uuid/src/Data/UUID/Named.hs+1−1 uuid/src/Data/UUID/Util.hs+42 Data/UUID/V3.hs → uuid/src/Data/UUID/V3.hs+2−3 uuid/src/Data/UUID/V4.hs+33 Data/UUID/V5.hs → uuid/src/Data/UUID/V5.hs+1−2 uuid/src/Data/Word/Util.hs+20 uuid/tests/TestUUID.hs+96 uuid/uuid.cabal+95
.git-blame-ignore-revsnew file+2
@@ -0,0 +1,2 @@
1+# Move code into subdirectories
2+bf1ac1bad848bb2c081f0cae1631bf53811ec05a
.github/workflows/haskell-ci-bench.ymlnew file+241
@@ -0,0 +1,241 @@
1+# This GitHub workflow config has been generated by a script via
2+#
3+# haskell-ci 'github' '--project' 'cabal.bench.project' '-o' '.github/workflows/haskell-ci-bench.yml' '--github-action-name' 'Benchmarks'
4+#
5+# To regenerate the script (for example after adjusting tested-with) run
6+#
7+# haskell-ci regenerate
8+#
9+# For more information, see https://github.com/haskell-CI/haskell-ci
10+#
11+# version: 0.19.20240702
12+#
13+# REGENDATA ("0.19.20240702",["github","--project","cabal.bench.project","-o",".github/workflows/haskell-ci-bench.yml","--github-action-name","Benchmarks"])
14+#
15+name: Benchmarks
16+on:
17+ push:
18+ branches:
19+ - master
20+ pull_request:
21+ branches:
22+ - master
23+jobs:
24+ linux:
25+ name: Benchmarks - Linux - ${{ matrix.compiler }}
26+ runs-on: ubuntu-20.04
27+ timeout-minutes:
28+ 60
29+ container:
30+ image: buildpack-deps:jammy
31+ continue-on-error: ${{ matrix.allow-failure }}
32+ strategy:
33+ matrix:
34+ include:
35+ - compiler: ghc-9.10.1
36+ compilerKind: ghc
37+ compilerVersion: 9.10.1
38+ setup-method: ghcup
39+ allow-failure: false
40+ - compiler: ghc-9.8.2
41+ compilerKind: ghc
42+ compilerVersion: 9.8.2
43+ setup-method: ghcup
44+ allow-failure: false
45+ - compiler: ghc-9.6.6
46+ compilerKind: ghc
47+ compilerVersion: 9.6.6
48+ setup-method: ghcup
49+ allow-failure: false
50+ - compiler: ghc-9.4.8
51+ compilerKind: ghc
52+ compilerVersion: 9.4.8
53+ setup-method: ghcup
54+ allow-failure: false
55+ - compiler: ghc-9.2.8
56+ compilerKind: ghc
57+ compilerVersion: 9.2.8
58+ setup-method: ghcup
59+ allow-failure: false
60+ - compiler: ghc-9.0.2
61+ compilerKind: ghc
62+ compilerVersion: 9.0.2
63+ setup-method: ghcup
64+ allow-failure: false
65+ - compiler: ghc-8.10.7
66+ compilerKind: ghc
67+ compilerVersion: 8.10.7
68+ setup-method: ghcup
69+ allow-failure: false
70+ - compiler: ghc-8.8.4
71+ compilerKind: ghc
72+ compilerVersion: 8.8.4
73+ setup-method: ghcup
74+ allow-failure: false
75+ - compiler: ghc-8.6.5
76+ compilerKind: ghc
77+ compilerVersion: 8.6.5
78+ setup-method: ghcup
79+ allow-failure: false
80+ fail-fast: false
81+ steps:
82+ - name: apt
83+ run: |
84+ apt-get update
85+ apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
86+ mkdir -p "$HOME/.ghcup/bin"
87+ curl -sL https://downloads.haskell.org/ghcup/0.1.20.0/x86_64-linux-ghcup-0.1.20.0 > "$HOME/.ghcup/bin/ghcup"
88+ chmod a+x "$HOME/.ghcup/bin/ghcup"
89+ "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
90+ "$HOME/.ghcup/bin/ghcup" install cabal 3.12.1.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
91+ env:
92+ HCKIND: ${{ matrix.compilerKind }}
93+ HCNAME: ${{ matrix.compiler }}
94+ HCVER: ${{ matrix.compilerVersion }}
95+ - name: Set PATH and environment variables
96+ run: |
97+ echo "$HOME/.cabal/bin" >> $GITHUB_PATH
98+ echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
99+ echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
100+ echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
101+ HCDIR=/opt/$HCKIND/$HCVER
102+ HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
103+ HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
104+ HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
105+ echo "HC=$HC" >> "$GITHUB_ENV"
106+ echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
107+ echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
108+ echo "CABAL=$HOME/.ghcup/bin/cabal-3.12.1.0 -vnormal+nowrap" >> "$GITHUB_ENV"
109+ HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
110+ echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
111+ echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
112+ echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
113+ echo "HEADHACKAGE=false" >> "$GITHUB_ENV"
114+ echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
115+ echo "GHCJSARITH=0" >> "$GITHUB_ENV"
116+ env:
117+ HCKIND: ${{ matrix.compilerKind }}
118+ HCNAME: ${{ matrix.compiler }}
119+ HCVER: ${{ matrix.compilerVersion }}
120+ - name: env
121+ run: |
122+ env
123+ - name: write cabal config
124+ run: |
125+ mkdir -p $CABAL_DIR
126+ cat >> $CABAL_CONFIG <<EOF
127+ remote-build-reporting: anonymous
128+ write-ghc-environment-files: never
129+ remote-repo-cache: $CABAL_DIR/packages
130+ logs-dir: $CABAL_DIR/logs
131+ world-file: $CABAL_DIR/world
132+ extra-prog-path: $CABAL_DIR/bin
133+ symlink-bindir: $CABAL_DIR/bin
134+ installdir: $CABAL_DIR/bin
135+ build-summary: $CABAL_DIR/logs/build.log
136+ store-dir: $CABAL_DIR/store
137+ install-dirs user
138+ prefix: $CABAL_DIR
139+ repository hackage.haskell.org
140+ url: http://hackage.haskell.org/
141+ EOF
142+ cat >> $CABAL_CONFIG <<EOF
143+ program-default-options
144+ ghc-options: $GHCJOBS +RTS -M3G -RTS
145+ EOF
146+ cat $CABAL_CONFIG
147+ - name: versions
148+ run: |
149+ $HC --version || true
150+ $HC --print-project-git-commit-id || true
151+ $CABAL --version || true
152+ - name: update cabal index
153+ run: |
154+ $CABAL v2-update -v
155+ - name: install cabal-plan
156+ run: |
157+ mkdir -p $HOME/.cabal/bin
158+ curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.7.3.0/cabal-plan-0.7.3.0-x86_64-linux.xz > cabal-plan.xz
159+ echo 'f62ccb2971567a5f638f2005ad3173dba14693a45154c1508645c52289714cb2 cabal-plan.xz' | sha256sum -c -
160+ xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
161+ rm -f cabal-plan.xz
162+ chmod a+x $HOME/.cabal/bin/cabal-plan
163+ cabal-plan --version
164+ - name: checkout
165+ uses: actions/checkout@v4
166+ with:
167+ path: source
168+ - name: initial cabal.project for sdist
169+ run: |
170+ touch cabal.project
171+ echo "packages: $GITHUB_WORKSPACE/source/uuid-bench" >> cabal.project
172+ cat cabal.project
173+ - name: sdist
174+ run: |
175+ mkdir -p sdist
176+ $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist
177+ - name: unpack
178+ run: |
179+ mkdir -p unpacked
180+ find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \;
181+ - name: generate cabal.project
182+ run: |
183+ PKGDIR_uuid_bench="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/uuid-bench-[0-9.]*')"
184+ echo "PKGDIR_uuid_bench=${PKGDIR_uuid_bench}" >> "$GITHUB_ENV"
185+ rm -f cabal.project cabal.project.local
186+ touch cabal.project
187+ touch cabal.project.local
188+ echo "packages: ${PKGDIR_uuid_bench}" >> cabal.project
189+ echo "package uuid-bench" >> cabal.project
190+ echo " ghc-options: -Werror=missing-methods" >> cabal.project
191+ cat >> cabal.project <<EOF
192+ EOF
193+ $HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: any.$_ installed\n" unless /^(uuid-bench)$/; }' >> cabal.project.local
194+ cat cabal.project
195+ cat cabal.project.local
196+ - name: dump install plan
197+ run: |
198+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
199+ cabal-plan
200+ - name: restore cache
201+ uses: actions/cache/restore@v4
202+ with:
203+ key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
204+ path: ~/.cabal/store
205+ restore-keys: ${{ runner.os }}-${{ matrix.compiler }}-
206+ - name: install dependencies
207+ run: |
208+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all
209+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all
210+ - name: build w/o tests
211+ run: |
212+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
213+ - name: build
214+ run: |
215+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always
216+ - name: cabal check
217+ run: |
218+ cd ${PKGDIR_uuid_bench} || false
219+ ${CABAL} -vnormal check
220+ - name: haddock
221+ run: |
222+ $CABAL v2-haddock --disable-documentation --haddock-all $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
223+ - name: unconstrained build
224+ run: |
225+ rm -f cabal.project.local
226+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
227+ - name: prepare for constraint sets
228+ run: |
229+ rm -f cabal.project.local
230+ - name: constraint set random-1.2
231+ run: |
232+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='random==1.2.*' all --dry-run
233+ cabal-plan topo | sort
234+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='random==1.2.*' --dependencies-only -j2 all
235+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='random==1.2.*' all
236+ - name: save cache
237+ uses: actions/cache/save@v4
238+ if: always()
239+ with:
240+ key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
241+ path: ~/.cabal/store
.github/workflows/haskell-ci.ymlnew file+262
@@ -0,0 +1,262 @@
1+# This GitHub workflow config has been generated by a script via
2+#
3+# haskell-ci 'github' 'cabal.project'
4+#
5+# To regenerate the script (for example after adjusting tested-with) run
6+#
7+# haskell-ci regenerate
8+#
9+# For more information, see https://github.com/haskell-CI/haskell-ci
10+#
11+# version: 0.19.20241223
12+#
13+# REGENDATA ("0.19.20241223",["github","cabal.project"])
14+#
15+name: Haskell-CI
16+on:
17+ push:
18+ branches:
19+ - master
20+ pull_request:
21+ branches:
22+ - master
23+jobs:
24+ linux:
25+ name: Haskell-CI - Linux - ${{ matrix.compiler }}
26+ runs-on: ubuntu-20.04
27+ timeout-minutes:
28+ 60
29+ container:
30+ image: buildpack-deps:jammy
31+ continue-on-error: ${{ matrix.allow-failure }}
32+ strategy:
33+ matrix:
34+ include:
35+ - compiler: ghc-9.12.1
36+ compilerKind: ghc
37+ compilerVersion: 9.12.1
38+ setup-method: ghcup
39+ allow-failure: false
40+ - compiler: ghc-9.10.1
41+ compilerKind: ghc
42+ compilerVersion: 9.10.1
43+ setup-method: ghcup
44+ allow-failure: false
45+ - compiler: ghc-9.8.4
46+ compilerKind: ghc
47+ compilerVersion: 9.8.4
48+ setup-method: ghcup
49+ allow-failure: false
50+ - compiler: ghc-9.6.6
51+ compilerKind: ghc
52+ compilerVersion: 9.6.6
53+ setup-method: ghcup
54+ allow-failure: false
55+ - compiler: ghc-9.4.8
56+ compilerKind: ghc
57+ compilerVersion: 9.4.8
58+ setup-method: ghcup
59+ allow-failure: false
60+ - compiler: ghc-9.2.8
61+ compilerKind: ghc
62+ compilerVersion: 9.2.8
63+ setup-method: ghcup
64+ allow-failure: false
65+ - compiler: ghc-9.0.2
66+ compilerKind: ghc
67+ compilerVersion: 9.0.2
68+ setup-method: ghcup
69+ allow-failure: false
70+ - compiler: ghc-8.10.7
71+ compilerKind: ghc
72+ compilerVersion: 8.10.7
73+ setup-method: ghcup
74+ allow-failure: false
75+ - compiler: ghc-8.8.4
76+ compilerKind: ghc
77+ compilerVersion: 8.8.4
78+ setup-method: ghcup
79+ allow-failure: false
80+ - compiler: ghc-8.6.5
81+ compilerKind: ghc
82+ compilerVersion: 8.6.5
83+ setup-method: ghcup
84+ allow-failure: false
85+ fail-fast: false
86+ steps:
87+ - name: apt-get install
88+ run: |
89+ apt-get update
90+ apt-get install -y --no-install-recommends gnupg ca-certificates dirmngr curl git software-properties-common libtinfo5
91+ - name: Install GHCup
92+ run: |
93+ mkdir -p "$HOME/.ghcup/bin"
94+ curl -sL https://downloads.haskell.org/ghcup/0.1.30.0/x86_64-linux-ghcup-0.1.30.0 > "$HOME/.ghcup/bin/ghcup"
95+ chmod a+x "$HOME/.ghcup/bin/ghcup"
96+ - name: Install cabal-install
97+ run: |
98+ "$HOME/.ghcup/bin/ghcup" install cabal 3.14.1.0 || (cat "$HOME"/.ghcup/logs/*.* && false)
99+ echo "CABAL=$HOME/.ghcup/bin/cabal-3.14.1.0 -vnormal+nowrap" >> "$GITHUB_ENV"
100+ - name: Install GHC (GHCup)
101+ if: matrix.setup-method == 'ghcup'
102+ run: |
103+ "$HOME/.ghcup/bin/ghcup" install ghc "$HCVER" || (cat "$HOME"/.ghcup/logs/*.* && false)
104+ HC=$("$HOME/.ghcup/bin/ghcup" whereis ghc "$HCVER")
105+ HCPKG=$(echo "$HC" | sed 's#ghc$#ghc-pkg#')
106+ HADDOCK=$(echo "$HC" | sed 's#ghc$#haddock#')
107+ echo "HC=$HC" >> "$GITHUB_ENV"
108+ echo "HCPKG=$HCPKG" >> "$GITHUB_ENV"
109+ echo "HADDOCK=$HADDOCK" >> "$GITHUB_ENV"
110+ env:
111+ HCKIND: ${{ matrix.compilerKind }}
112+ HCNAME: ${{ matrix.compiler }}
113+ HCVER: ${{ matrix.compilerVersion }}
114+ - name: Set PATH and environment variables
115+ run: |
116+ echo "$HOME/.cabal/bin" >> $GITHUB_PATH
117+ echo "LANG=C.UTF-8" >> "$GITHUB_ENV"
118+ echo "CABAL_DIR=$HOME/.cabal" >> "$GITHUB_ENV"
119+ echo "CABAL_CONFIG=$HOME/.cabal/config" >> "$GITHUB_ENV"
120+ HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\d+)\.(\d+)\.(\d+)(\.(\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')
121+ echo "HCNUMVER=$HCNUMVER" >> "$GITHUB_ENV"
122+ echo "ARG_TESTS=--enable-tests" >> "$GITHUB_ENV"
123+ echo "ARG_BENCH=--enable-benchmarks" >> "$GITHUB_ENV"
124+ echo "HEADHACKAGE=false" >> "$GITHUB_ENV"
125+ echo "ARG_COMPILER=--$HCKIND --with-compiler=$HC" >> "$GITHUB_ENV"
126+ env:
127+ HCKIND: ${{ matrix.compilerKind }}
128+ HCNAME: ${{ matrix.compiler }}
129+ HCVER: ${{ matrix.compilerVersion }}
130+ - name: env
131+ run: |
132+ env
133+ - name: write cabal config
134+ run: |
135+ mkdir -p $CABAL_DIR
136+ cat >> $CABAL_CONFIG <<EOF
137+ remote-build-reporting: anonymous
138+ write-ghc-environment-files: never
139+ remote-repo-cache: $CABAL_DIR/packages
140+ logs-dir: $CABAL_DIR/logs
141+ world-file: $CABAL_DIR/world
142+ extra-prog-path: $CABAL_DIR/bin
143+ symlink-bindir: $CABAL_DIR/bin
144+ installdir: $CABAL_DIR/bin
145+ build-summary: $CABAL_DIR/logs/build.log
146+ store-dir: $CABAL_DIR/store
147+ install-dirs user
148+ prefix: $CABAL_DIR
149+ repository hackage.haskell.org
150+ url: http://hackage.haskell.org/
151+ EOF
152+ cat >> $CABAL_CONFIG <<EOF
153+ program-default-options
154+ ghc-options: $GHCJOBS +RTS -M3G -RTS
155+ EOF
156+ cat $CABAL_CONFIG
157+ - name: versions
158+ run: |
159+ $HC --version || true
160+ $HC --print-project-git-commit-id || true
161+ $CABAL --version || true
162+ - name: update cabal index
163+ run: |
164+ $CABAL v2-update -v
165+ - name: install cabal-plan
166+ run: |
167+ mkdir -p $HOME/.cabal/bin
168+ curl -sL https://github.com/haskell-hvr/cabal-plan/releases/download/v0.7.3.0/cabal-plan-0.7.3.0-x86_64-linux.xz > cabal-plan.xz
169+ echo 'f62ccb2971567a5f638f2005ad3173dba14693a45154c1508645c52289714cb2 cabal-plan.xz' | sha256sum -c -
170+ xz -d < cabal-plan.xz > $HOME/.cabal/bin/cabal-plan
171+ rm -f cabal-plan.xz
172+ chmod a+x $HOME/.cabal/bin/cabal-plan
173+ cabal-plan --version
174+ - name: checkout
175+ uses: actions/checkout@v4
176+ with:
177+ path: source
178+ - name: initial cabal.project for sdist
179+ run: |
180+ touch cabal.project
181+ echo "packages: $GITHUB_WORKSPACE/source/uuid-types" >> cabal.project
182+ echo "packages: $GITHUB_WORKSPACE/source/uuid" >> cabal.project
183+ cat cabal.project
184+ - name: sdist
185+ run: |
186+ mkdir -p sdist
187+ $CABAL sdist all --output-dir $GITHUB_WORKSPACE/sdist
188+ - name: unpack
189+ run: |
190+ mkdir -p unpacked
191+ find sdist -maxdepth 1 -type f -name '*.tar.gz' -exec tar -C $GITHUB_WORKSPACE/unpacked -xzvf {} \;
192+ - name: generate cabal.project
193+ run: |
194+ PKGDIR_uuid_types="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/uuid-types-[0-9.]*')"
195+ echo "PKGDIR_uuid_types=${PKGDIR_uuid_types}" >> "$GITHUB_ENV"
196+ PKGDIR_uuid="$(find "$GITHUB_WORKSPACE/unpacked" -maxdepth 1 -type d -regex '.*/uuid-[0-9.]*')"
197+ echo "PKGDIR_uuid=${PKGDIR_uuid}" >> "$GITHUB_ENV"
198+ rm -f cabal.project cabal.project.local
199+ touch cabal.project
200+ touch cabal.project.local
201+ echo "packages: ${PKGDIR_uuid_types}" >> cabal.project
202+ echo "packages: ${PKGDIR_uuid}" >> cabal.project
203+ echo "package uuid-types" >> cabal.project
204+ echo " ghc-options: -Werror=missing-methods" >> cabal.project
205+ echo "package uuid" >> cabal.project
206+ echo " ghc-options: -Werror=missing-methods" >> cabal.project
207+ cat >> cabal.project <<EOF
208+ EOF
209+ $HCPKG list --simple-output --names-only | perl -ne 'for (split /\s+/) { print "constraints: any.$_ installed\n" unless /^(uuid|uuid-types)$/; }' >> cabal.project.local
210+ cat cabal.project
211+ cat cabal.project.local
212+ - name: dump install plan
213+ run: |
214+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dry-run all
215+ cabal-plan
216+ - name: restore cache
217+ uses: actions/cache/restore@v4
218+ with:
219+ key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
220+ path: ~/.cabal/store
221+ restore-keys: ${{ runner.os }}-${{ matrix.compiler }}-
222+ - name: install dependencies
223+ run: |
224+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --dependencies-only -j2 all
225+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH --dependencies-only -j2 all
226+ - name: build w/o tests
227+ run: |
228+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
229+ - name: build
230+ run: |
231+ $CABAL v2-build $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --write-ghc-environment-files=always
232+ - name: tests
233+ run: |
234+ $CABAL v2-test $ARG_COMPILER $ARG_TESTS $ARG_BENCH all --test-show-details=direct
235+ - name: cabal check
236+ run: |
237+ cd ${PKGDIR_uuid_types} || false
238+ ${CABAL} -vnormal check
239+ cd ${PKGDIR_uuid} || false
240+ ${CABAL} -vnormal check
241+ - name: haddock
242+ run: |
243+ $CABAL v2-haddock --disable-documentation --haddock-all $ARG_COMPILER --with-haddock $HADDOCK $ARG_TESTS $ARG_BENCH all
244+ - name: unconstrained build
245+ run: |
246+ rm -f cabal.project.local
247+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks all
248+ - name: prepare for constraint sets
249+ run: |
250+ rm -f cabal.project.local
251+ - name: constraint set random-1.2
252+ run: |
253+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='random==1.2.*' all --dry-run
254+ cabal-plan topo | sort
255+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='random==1.2.*' --dependencies-only -j2 all
256+ $CABAL v2-build $ARG_COMPILER --disable-tests --disable-benchmarks --constraint='random==1.2.*' all
257+ - name: save cache
258+ if: always()
259+ uses: actions/cache/save@v4
260+ with:
261+ key: ${{ runner.os }}-${{ matrix.compiler }}-${{ github.sha }}
262+ path: ~/.cabal/store
.gitignorenew file+13
@@ -0,0 +1,13 @@
1+# Sandboxing
2+.cabal-sandbox/
3+cabal.sandbox.config
4+
5+# Cabal stuff
6+dist/
7+cabal.config
8+dist-newstyle/
9+/cabal.project.local
10+.ghc.environment.*
11+
12+# Emacs
13+*~
CHANGESdeleted−99
@@ -1,99 +0,0 @@
1−1.2.13
2−
3−Benchmark only changes:
4−
5−- Allow criterion 0.8.*
6−
7−1.2.12
8−
9−Test only changes:
10−
11−- Allow QuickCheck 2.6.*
12−
13−1.2.11
14−
15−- Allow binary 0.7.*
16−
17−1.2.10
18−
19−- Allow cryptohash version 0.9.*
20−- Cleanup tests
21−
22−1.2.9
23−
24−- Bumped dependency on 'cryptohash'.
25−
26−1.2.8
27−
28−- Bumped various dependencies and cleaned up dependencies in general.
29−
30−1.2.7
31−
32−- Added stricter constraints on 'random' package.
33−
34−1.2.6
35−
36−- Add module 'V4' to direct attention to our Random instance
37−
38−- In module 'V1' seed the generator with a random number
39− if the hardware MAC address could not be discovered.
40−
41−- Fix and cleanup various haddocks.
42−
43−- In module docs, warn about MD5 use in Data.UUID.V3 and
44− encourage the reader to use Data.UUID.V5 instead.
45−
46−1.2.5
47−
48−- Use 'cryptohash' package for MD5 and SHA1 instead of 'Crypto'
49−
50−1.2.4
51−
52−- Unpack Word32 values into UUID constructor.
53−
54−- Update test suite to QuickCheck 2
55−
56−- Bump other dependencies in tests/benchmarks
57−
58−1.2.3
59−
60−- The Read instance now drops leading spaces in the string to be parsed.
61−Thanks to Marc Ziegert for reporting this bug.
62−
63−- The tests have moved over to the new Cabal test running framework.
64−
65−1.2.2
66−
67−- Add functions fromWords/toWords
68−
69−The goal was to have a total function that can be used to construct
70−a UUID, primarily for use by uuid-th.
71−
72−1.2.1
73−
74−- Fix concurrency bug in Data.UUID.V1 (thanks to Neil Mitchell for reporting
75−and a test case)
76−
77−1.2.0
78− (Contributors: Antoine Latter & Mark Lentczner)
79−
80−- added functions toByteString and fromByteString
81−- added 'nil' UUID
82−- added unit tests and benchmarks, built when configured -ftest
83−- major speed up of to/from functions (as well as in general)
84−- added version-3 generation (deterministic based on MD5)
85−- major changes to internal representation
86− - now uses four strict Word32 values
87− - internal ByteSource classes for easy construction (see Builder.hs)
88−- Storable instance now stores in memory as system libraries in C do:
89− 16 bytes derived from the network order of the fields, no matter what
90− the host native endianess is.
91−- fixed bugs in V1 time and clock stepping, and V1 generated values
92−- builds cleanly under GHC's -Wall
93−- added CHANGES file
94−
95−1.1.1
96−
97−- no longer exporting 'null' from the prelude
98−- add 'null' predicate on UUIDs
99−- documentation fix (thanks Mark Lentczner)
CONTRIBUTORS+6−1
@@ -7,4 +7,9 @@ Mark Lentczner
77Neil Mitchell
88Bas van Dijk
99Sergei Trofimovich
10−davean
10+davean
11+Francesco Mazzoli
12+Michael Snoyman
13+Bardur Arantsson
14+Timo von Holtz
15+Oleg Grenrus
Data/UUID/Internal.hsdeleted−431
@@ -1,431 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable, TypeFamilies, CPP #-}
2−
3−-- |
4−-- Module : Data.UUID
5−-- Copyright : (c) 2008-2009, 2012 Antoine Latter
6−-- (c) 2009 Mark Lentczner
7−--
8−-- License : BSD-style
9−--
10−-- Maintainer : aslatter@gmail.com
11−-- Stability : experimental
12−-- Portability : portable
13−
14−module Data.UUID.Internal
15− (UUID(..)
16− ,null
17− ,nil
18− ,fromByteString
19− ,toByteString
20− ,fromString
21− ,toString
22− ,fromWords
23− ,toWords
24− ,toList
25− ,buildFromBytes
26− ,buildFromWords
27− ,fromASCIIBytes
28− ,toASCIIBytes
29− ,fromLazyASCIIBytes
30− ,toLazyASCIIBytes
31− ) where
32−
33−import Prelude hiding (null)
34−
35−import Control.Applicative ((<*>))
36−import Control.DeepSeq (NFData(..))
37−import Control.Monad (liftM4, guard)
38−import Data.Functor ((<$>))
39−import Data.Char
40−import Data.Bits
41−import Data.Hashable
42−import Data.Maybe
43−import Data.List (elemIndices)
44−import Foreign.Ptr (Ptr)
45−
46−#if MIN_VERSION_base(4,0,0)
47−import Data.Data
48−#else
49−import Data.Generics.Basics
50−#endif
51−
52−import Foreign.Storable
53−
54−import Data.Binary
55−import Data.Binary.Put
56−import Data.Binary.Get
57−import qualified Data.ByteString as B
58−import qualified Data.ByteString.Internal as BI
59−import qualified Data.ByteString.Lazy as BL
60−import qualified Data.ByteString.Unsafe as BU
61−
62−import Data.UUID.Builder
63−
64−import System.Random
65−
66−
67−-- |The UUID type. A 'Random' instance is provided which produces
68−-- version 4 UUIDs as specified in RFC 4122. The 'Storable' and
69−-- 'Binary' instances are compatible with RFC 4122, storing the fields
70−-- in network order as 16 bytes.
71−data UUID
72− = UUID
73− {-# UNPACK #-} !Word32
74− {-# UNPACK #-} !Word32
75− {-# UNPACK #-} !Word32
76− {-# UNPACK #-} !Word32
77− deriving (Eq, Ord, Typeable)
78−{-
79− Other representations that we tried are:
80− Mimic V1 structure: !Word32 !Word16 !Word16 !Word16
81− !Word8 !Word8 !Word8 !Word8 !Word8 !Word8
82− Sixteen bytes: !Word8 ... (x 16)
83− Simple list of bytes: [Word8]
84− ByteString (strict) ByteString
85− Immutable array: UArray Int Word8
86− Vector: UArr Word8
87− None was as fast, overall, as the representation used here.
88−-}
89−
90−-- | Covert a 'UUID' into a sequence of 'Word32' values.
91−-- Usefull for when you need to serialize a UUID and
92−-- neither 'Storable' nor 'Binary' are appropriate.
93−-- Introduced in version 1.2.2.
94−toWords :: UUID -> (Word32, Word32, Word32, Word32)
95−toWords (UUID w1 w2 w3 w4) = (w1, w2, w3, w4)
96−
97−-- | Create a 'UUID' from a sequence of 'Word32'. The
98−-- opposite of 'toWords'. Useful when you need a total
99−-- function for constructing 'UUID' values.
100−-- Introduced in version 1.2.2.
101−fromWords :: Word32 -> Word32 -> Word32 -> Word32 -> UUID
102−fromWords = UUID
103−
104−--
105−-- UTILITIES
106−--
107−
108−-- |Build a Word32 from four Word8 values, presented in big-endian order
109−word :: Word8 -> Word8 -> Word8 -> Word8 -> Word32
110−word a b c d = (fromIntegral a `shiftL` 24)
111− .|. (fromIntegral b `shiftL` 16)
112− .|. (fromIntegral c `shiftL` 8)
113− .|. (fromIntegral d )
114−
115−-- |Extract a Word8 from a Word32. Bytes, high to low, are numbered from 3 to 0,
116−byte :: Int -> Word32 -> Word8
117−byte i w = fromIntegral (w `shiftR` (i * 8))
118−
119−
120−-- |Make a UUID from sixteen Word8 values
121−makeFromBytes :: Word8 -> Word8 -> Word8 -> Word8
122− -> Word8 -> Word8 -> Word8 -> Word8
123− -> Word8 -> Word8 -> Word8 -> Word8
124− -> Word8 -> Word8 -> Word8 -> Word8
125− -> UUID
126−makeFromBytes b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
127− = UUID w0 w1 w2 w3
128− where w0 = word b0 b1 b2 b3
129− w1 = word b4 b5 b6 b7
130− w2 = word b8 b9 ba bb
131− w3 = word bc bd be bf
132−
133−-- |Make a UUID from four Word32 values
134−makeFromWords :: Word32 -> Word32 -> Word32 -> Word32 -> UUID
135−makeFromWords = UUID
136−
137−-- |A Builder for constructing a UUID of a given version.
138−buildFromBytes :: Word8
139− -> Word8 -> Word8 -> Word8 -> Word8
140− -> Word8 -> Word8 -> Word8 -> Word8
141− -> Word8 -> Word8 -> Word8 -> Word8
142− -> Word8 -> Word8 -> Word8 -> Word8
143− -> UUID
144−buildFromBytes v b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf =
145− makeFromBytes b0 b1 b2 b3 b4 b5 b6' b7 b8' b9 ba bb bc bd be bf
146− where b6' = b6 .&. 0x0f .|. (v `shiftL` 4)
147− b8' = b8 .&. 0x3f .|. 0x80
148−
149−-- |Build a UUID of a given version from Word32 values.
150−buildFromWords :: Word8 -> Word32 -> Word32 -> Word32 -> Word32 -> UUID
151−buildFromWords v w0 w1 w2 w3 = makeFromWords w0 w1' w2' w3
152− where w1' = w1 .&. 0xffff0fff .|. ((fromIntegral v) `shiftL` 12)
153− w2' = w2 .&. 0x3fffffff .|. 0x80000000
154−
155−
156−-- |Return the bytes that make up the UUID
157−toList :: UUID -> [Word8]
158−toList (UUID w0 w1 w2 w3) =
159− [byte 3 w0, byte 2 w0, byte 1 w0, byte 0 w0,
160− byte 3 w1, byte 2 w1, byte 1 w1, byte 0 w1,
161− byte 3 w2, byte 2 w2, byte 1 w2, byte 0 w2,
162− byte 3 w3, byte 2 w3, byte 1 w3, byte 0 w3]
163−
164−-- |Construct a UUID from a list of Word8. Returns Nothing if the list isn't
165−-- exactly sixteen bytes long
166−fromList :: [Word8] -> Maybe UUID
167−fromList [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf] =
168− Just $ makeFromBytes b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
169−fromList _ = Nothing
170−
171−
172−--
173−-- UUID API
174−--
175−
176−-- |Returns true if the passed-in UUID is the 'nil' UUID.
177−null :: UUID -> Bool
178−null = (== nil)
179− -- Note: This actually faster than:
180− -- null (UUID 0 0 0 0) = True
181− -- null _ = False
182−
183−-- |The nil UUID, as defined in RFC 4122.
184−-- It is a UUID of all zeros. @'null' u@ iff @'u' == 'nil'@.
185−nil :: UUID
186−nil = UUID 0 0 0 0
187−
188−-- |Extract a UUID from a 'ByteString' in network byte order.
189−-- The argument must be 16 bytes long, otherwise 'Nothing' is returned.
190−fromByteString :: BL.ByteString -> Maybe UUID
191−fromByteString = fromList . BL.unpack
192−
193−-- |Encode a UUID into a 'ByteString' in network order.
194−toByteString :: UUID -> BL.ByteString
195−toByteString = BL.pack . toList
196−
197−-- |If the passed in 'String' can be parsed as a 'UUID', it will be.
198−-- The hyphens may not be omitted.
199−-- Example:
200−--
201−-- @
202−-- fromString \"c2cc10e1-57d6-4b6f-9899-38d972112d8c\"
203−-- @
204−--
205−-- Hex digits may be upper or lower-case.
206−fromString :: String -> Maybe UUID
207−fromString xs | validFmt = fromString' xs
208− | otherwise = Nothing
209− where validFmt = elemIndices '-' xs == [8,13,18,23]
210−
211−fromString' :: String -> Maybe UUID
212−fromString' s0 = do
213− (w0, s1) <- hexWord s0
214− (w1, s2) <- hexWord s1
215− (w2, s3) <- hexWord s2
216− (w3, s4) <- hexWord s3
217− if s4 /= "" then Nothing
218− else Just $ UUID w0 w1 w2 w3
219− where hexWord :: String -> Maybe (Word32, String)
220− hexWord s = Just (0, s) >>= hexByte >>= hexByte
221− >>= hexByte >>= hexByte
222−
223− hexByte :: (Word32, String) -> Maybe (Word32, String)
224− hexByte (w, '-':ds) = hexByte (w, ds)
225− hexByte (w, hi:lo:ds)
226− | bothHex = Just ((w `shiftL` 8) .|. octet, ds)
227− | otherwise = Nothing
228− where bothHex = isHexDigit hi && isHexDigit lo
229− octet = fromIntegral (16 * digitToInt hi + digitToInt lo)
230− hexByte _ = Nothing
231−
232−-- | Convert a UUID into a hypenated string using lower-case letters.
233−-- Example:
234−--
235−-- @
236−-- toString $ fromString \"550e8400-e29b-41d4-a716-446655440000\"
237−-- @
238−toString :: UUID -> String
239−toString (UUID w0 w1 w2 w3) = hexw w0 $ hexw' w1 $ hexw' w2 $ hexw w3 ""
240− where hexw :: Word32 -> String -> String
241− hexw w s = hexn w 28 : hexn w 24 : hexn w 20 : hexn w 16
242− : hexn w 12 : hexn w 8 : hexn w 4 : hexn w 0 : s
243−
244− hexw' :: Word32 -> String -> String
245− hexw' w s = '-' : hexn w 28 : hexn w 24 : hexn w 20 : hexn w 16
246− : '-' : hexn w 12 : hexn w 8 : hexn w 4 : hexn w 0 : s
247−
248− hexn :: Word32 -> Int -> Char
249− hexn w r = intToDigit $ fromIntegral ((w `shiftR` r) .&. 0xf)
250−
251−
252−--
253−-- Class Instances
254−--
255−
256−instance Random UUID where
257− random g = (fromGenNext w0 w1 w2 w3 w4, g4)
258− where (w0, g0) = next g
259− (w1, g1) = next g0
260− (w2, g2) = next g1
261− (w3, g3) = next g2
262− (w4, g4) = next g3
263− randomR _ = random -- range is ignored
264−
265−-- |Build a UUID from the results of five calls to next on a StdGen.
266−-- While next on StdGet returns an Int, it doesn't provide 32 bits of
267−-- randomness. This code relies on at last 28 bits of randomness in the
268−-- and optimizes its use so as to make only five random values, not six.
269−fromGenNext :: Int -> Int -> Int -> Int -> Int -> UUID
270−fromGenNext w0 w1 w2 w3 w4 =
271− buildFromBytes 4 /-/ (ThreeByte w0)
272− /-/ (ThreeByte w1)
273− /-/ w2 -- use all 4 bytes because we know the version
274− -- field will "cover" the upper, non-random bits
275− /-/ (ThreeByte w3)
276− /-/ (ThreeByte w4)
277−
278−-- |A ByteSource to extract only three bytes from an Int, since next on StdGet
279−-- only returns 31 bits of randomness.
280−type instance ByteSink ThreeByte g = Takes3Bytes g
281−newtype ThreeByte = ThreeByte Int
282−instance ByteSource ThreeByte where
283− f /-/ (ThreeByte w) = f b1 b2 b3
284− where b1 = fromIntegral (w `shiftR` 16)
285− b2 = fromIntegral (w `shiftR` 8)
286− b3 = fromIntegral w
287−
288−instance Hashable UUID where
289− hash (UUID w0 w1 w2 w3) =
290− hash w0 `hashWithSalt` w1
291− `hashWithSalt` w2
292− `hashWithSalt` w3
293− hashWithSalt s (UUID w0 w1 w2 w3) =
294− s `hashWithSalt` w0
295− `hashWithSalt` w1
296− `hashWithSalt` w2
297− `hashWithSalt` w3
298−
299−instance Show UUID where
300− show = toString
301−
302−instance Read UUID where
303− readsPrec _ str =
304− let noSpaces = dropWhile isSpace str
305− in case fromString (take 36 noSpaces) of
306− Nothing -> []
307− Just u -> [(u,drop 36 noSpaces)]
308−
309−
310−instance Storable UUID where
311− sizeOf _ = 16
312− alignment _ = 1 -- UUIDs are stored as individual octets
313−
314− peekByteOff p off =
315− mapM (peekByteOff p) [off..(off+15)] >>= return . fromJust . fromList
316−
317− pokeByteOff p off u =
318− sequence_ $ zipWith (pokeByteOff p) [off..] (toList u)
319−
320−
321−instance Binary UUID where
322− put (UUID w0 w1 w2 w3) =
323− putWord32be w0 >> putWord32be w1 >> putWord32be w2 >> putWord32be w3
324− get = liftM4 UUID getWord32be getWord32be getWord32be getWord32be
325−
326−
327−-- My goal with this instance was to make it work just enough to do what
328−-- I want when used with the HStringTemplate library.
329−instance Data UUID where
330− toConstr uu = mkConstr uuidType (show uu) [] (error "fixity")
331− gunfold _ _ = error "gunfold"
332− dataTypeOf _ = uuidType
333−
334−uuidType :: DataType
335−uuidType = mkNoRepType "Data.UUID.UUID"
336−
337−#if !(MIN_VERSION_base(4,2,0))
338−mkNoRepType :: String -> DataType
339−mkNoRepType = mkNorepType
340−#endif
341−
342−fromASCIIBytes :: B.ByteString -> Maybe UUID
343−fromASCIIBytes bs = do
344− guard wellFormed
345− fromWords <$> single 0 <*> double 9 14 <*> double 19 24 <*> single 28
346− where
347− -- ord '-' ==> 45
348− dashIx bs' ix = BU.unsafeIndex bs' ix == 45
349−
350− -- Important: check the length first, given the `unsafeIndex` later.
351− wellFormed =
352− B.length bs == 36 && dashIx bs 8 && dashIx bs 13 &&
353− dashIx bs 18 && dashIx bs 23
354−
355− single ix = combine <$> octet ix <*> octet (ix + 2)
356− <*> octet (ix + 4) <*> octet (ix + 6)
357− double ix0 ix1 = combine <$> octet ix0 <*> octet (ix0 + 2)
358− <*> octet ix1 <*> octet (ix1 + 2)
359−
360− combine o0 o1 o2 o3 = shiftL o0 24 .|. shiftL o1 16 .|. shiftL o2 8 .|. o3
361−
362− octet ix = do
363− hi <- fromIntegral <$> toDigit (BU.unsafeIndex bs ix)
364− lo <- fromIntegral <$> toDigit (BU.unsafeIndex bs (ix + 1))
365− return (16 * hi + lo)
366−
367− toDigit :: Word8 -> Maybe Word8
368− toDigit w
369− -- Digit
370− | w >= 48 && w <= 57 = Just (w - 48)
371− -- Uppercase
372− | w >= 65 && w <= 70 = Just (10 + w - 65)
373− -- Lowercase
374− | w >= 97 && w <= 102 = Just (10 + w - 97)
375− | otherwise = Nothing
376−
377−toASCIIBytes :: UUID -> B.ByteString
378−toASCIIBytes uuid = BI.unsafeCreate 36 (pokeASCII uuid)
379−
380−pokeASCII :: UUID -> Ptr Word8 -> IO ()
381−pokeASCII uuid ptr = do
382− pokeDash 8
383− pokeDash 13
384− pokeDash 18
385− pokeDash 23
386− pokeSingle 0 w0
387− pokeDouble 9 w1
388− pokeDouble 19 w2
389− pokeSingle 28 w3
390− where
391− (w0, w1, w2, w3) = toWords uuid
392−
393− -- ord '-' ==> 45
394− pokeDash ix = pokeElemOff ptr ix 45
395−
396− pokeSingle ix w = do
397− pokeWord ix w 28
398− pokeWord (ix + 1) w 24
399− pokeWord (ix + 2) w 20
400− pokeWord (ix + 3) w 16
401− pokeWord (ix + 4) w 12
402− pokeWord (ix + 5) w 8
403− pokeWord (ix + 6) w 4
404− pokeWord (ix + 7) w 0
405−
406− -- We skip the dash in the middle
407− pokeDouble ix w = do
408− pokeWord ix w 28
409− pokeWord (ix + 1) w 24
410− pokeWord (ix + 2) w 20
411− pokeWord (ix + 3) w 16
412− pokeWord (ix + 5) w 12
413− pokeWord (ix + 6) w 8
414− pokeWord (ix + 7) w 4
415− pokeWord (ix + 8) w 0
416−
417− pokeWord ix w r =
418− pokeElemOff ptr ix (fromIntegral (toDigit ((w `shiftR` r) .&. 0xf)))
419−
420− toDigit :: Word32 -> Word32
421− toDigit w = if w < 10 then 48 + w else 97 + w - 10
422−
423−fromLazyASCIIBytes :: BL.ByteString -> Maybe UUID
424−fromLazyASCIIBytes bs =
425− if BL.length bs == 36 then fromASCIIBytes (BL.toStrict bs) else Nothing
426−
427−toLazyASCIIBytes :: UUID -> BL.ByteString
428−toLazyASCIIBytes = BL.fromStrict . toASCIIBytes
429−
430−instance NFData UUID where
431− rnf = rnf . toWords
Data/UUID/V1.hsdeleted−138
@@ -1,138 +0,0 @@
1−{-# OPTIONS_GHC -fno-cse #-}
2−{-# LANGUAGE TypeFamilies #-}
3−
4−{- |
5−Module : Data.UUID.V1
6−Copyright : (c) 2008 Jason Dusek
7− (c) 2009 Mark Lentczner
8− (c) 2009-2010,2012 Antoine Latter
9−
10−License : BSD-style
11−
12−Maintainer : aslatter@gmail.com
13−Stability : experimental
14−Portability : portable
15−
16−RFC 4122 Version 1 UUID state machine.
17−
18−The generated UUID is based on the hardware MAC
19−address and the system clock.
20−
21−If we cannot lookup the MAC address we seed the
22−generator with a psuedo-random number.
23−-}
24−
25−module Data.UUID.V1(nextUUID)
26−where
27−
28−
29−import Data.Bits
30−import Data.Maybe
31−import Data.Time
32−import Data.Word
33−
34−import Control.Applicative ((<$>),(<*>))
35−import Control.Concurrent.MVar
36−import System.IO.Unsafe
37−
38−import qualified System.Random as R
39−
40−import Network.Info
41−
42−import Data.UUID.Builder
43−import Data.UUID.Internal
44−
45−-- | Returns a new UUID derived from the local hardware MAC
46−-- address and the current system time.
47−-- Is generated according to the Version 1 UUID sepcified in
48−-- RFC 4122.
49−--
50−-- Returns 'Nothing' if you request UUIDs too quickly.
51−nextUUID :: IO (Maybe UUID)
52−nextUUID = do
53− res <- stepTime
54− case res of
55− Just (mac', c, t) -> return $ Just $ makeUUID t c mac'
56− _ -> return Nothing
57−
58−
59−makeUUID :: Word64 -> Word16 -> MAC -> UUID
60−makeUUID time clock mac' =
61− buildFromBytes 1 /-/ tLow /-/ tMid /-/ tHigh /-/ clock /-/ (MACSource mac')
62− where tLow = (fromIntegral time) :: Word32
63− tMid = (fromIntegral (time `shiftR` 32)) :: Word16
64− tHigh = (fromIntegral (time `shiftR` 48)) :: Word16
65−
66−newtype MACSource = MACSource MAC
67−instance ByteSource MACSource where
68− z /-/ (MACSource (MAC a b c d e f)) = z a b c d e f
69−type instance ByteSink MACSource g = Takes3Bytes (Takes3Bytes g)
70−
71−
72−-- |Approximates the clock algorithm in RFC 4122, section 4.2
73−-- Isn't system wide or thread safe, nor does it properly randomize
74−-- the clock value on initialization.
75−stepTime :: IO (Maybe (MAC, Word16, Word64))
76−stepTime = do
77− h1 <- fmap hundredsOfNanosSinceGregorianReform getCurrentTime
78− modifyMVar state $ \s@(State mac' c0 h0) ->
79− if h1 > h0
80− then
81− return (State mac' c0 h1, Just (mac', c0, h1))
82− else
83− let
84− c1 = succ c0
85− in if c1 <= 0x3fff -- when clock is initially randomized,
86− -- then this test will need to change
87− then
88− return (State mac' c1 h1, Just (mac', c1, h1))
89− else
90− return (s, Nothing)
91−
92−
93−{-# NOINLINE state #-}
94−state :: MVar State
95−state = unsafePerformIO $ do
96− h0 <- fmap hundredsOfNanosSinceGregorianReform getCurrentTime
97− mac' <- getMac
98− newMVar $ State mac' 0 h0 -- the 0 should be a random number
99−
100−-- SysMAC.mac can fail on some machines.
101−-- In those cases we fake it with a random
102−-- 6 bytes seed.
103−getMac :: IO MAC
104−getMac =
105− getNetworkInterfaces >>=
106− return . listToMaybe . filter (minBound /=) . map mac >>=
107− \macM -> case macM of
108− Just m -> return m
109− Nothing -> randomMac
110−
111−randomMac :: IO MAC
112−randomMac =
113− -- I'm too lazy to thread through
114− -- the random state ...
115− MAC
116− <$> (R.randomIO >>= return . (1 .|.)) -- We must set the multicast bit to True. See section 4.5 of the RFC.
117− <*> R.randomIO
118− <*> R.randomIO
119− <*> R.randomIO
120− <*> R.randomIO
121− <*> R.randomIO
122−
123−data State = State
124− {-# UNPACK #-} !MAC
125− {-# UNPACK #-} !Word16
126− {-# UNPACK #-} !Word64
127− deriving (Show)
128−
129−
130−
131−hundredsOfNanosSinceGregorianReform :: UTCTime -> Word64
132−hundredsOfNanosSinceGregorianReform t = floor $ 10000000 * dt
133− where
134− gregorianReform = UTCTime (fromGregorian 1582 10 15) 0
135− dt = t `diffUTCTime` gregorianReform
136−
137−
138−
Data/UUID/V4.hsdeleted−29
@@ -1,29 +0,0 @@
1−{- |
2− Module : Data.UUID.V4
3− Copyright : (c) 2012 Antoine Latter
4−
5− License : BSD-style
6−
7− Maintainer : aslatter@gmail.com
8− Stability : experimental
9− Portability : portable
10−
11− This module implements Version 4 UUIDs as specified
12− in RFC 4122.
13−
14− These UUIDs are generated from a psuedo-random generator.
15− We use the System.Random 'R.StdGen' as our random source.
16−
17− All of the logic is encapsulated in the 'R.Random' instance
18− for the UUID type, so you are also free to use the random generator
19− of your choice.
20−-}
21−module Data.UUID.V4 (nextRandom) where
22−
23−import Data.UUID
24−import qualified System.Random as R
25−
26−-- | Generate a random UUID. Introduced in version 1.2.6.
27−nextRandom :: IO UUID
28−nextRandom = R.randomIO
29−
Policy.mdnew file+11
@@ -0,0 +1,11 @@
1+# Versioning
2+
3+Because many consumers of the `UUID` type receive the type
4+through the `uuid` package and not the `uuid-types` package
5+we need to be careful about removing instances from the `uuid`
6+type.
7+
8+If the `uuid-types` package has a major-version bump because of
9+the removal of a class instance then we must also bump the major
10+version of the `uuid` package when we update the package
11+description to pull in the new version of `uuid-types`.
README.mdnew file+12
@@ -0,0 +1,12 @@
1+# uuid [![Build Status](https://travis-ci.org/hvr/uuid.svg?branch=master)](https://travis-ci.org/hvr/uuid)
2+
3+This library is useful for creating, comparing, parsing and printing Universally
4+Unique Identifiers. See http://en.wikipedia.org/wiki/UUID for the general idea.
5+
6+We offer two different packages: `uuid` and `uuid-types`
7+
8+The `uuid` package aims to be a fully-featured library for working with UUIDs.
9+
10+However if you're developing a library and only want the `UUID` type to show up
11+in your APIs you may want to use the `uuid-types` library to minimize the number
12+of dependencies needed by users of your library.
cabal.bench.projectnew file+4
@@ -0,0 +1,4 @@
1+-- separate package for benchmarks
2+-- this way we can build criterion
3+packages: uuid-bench
4+benchmarks: True
cabal.haskell-cinew file+7
@@ -0,0 +1,7 @@
1+branches: master
2+-- jobs-selection: any
3+head-hackage: False
4+
5+constraint-set random-1.2
6+ ghc: >=7.10
7+ constraints: random==1.2.*
cabal.projectnew file+15
@@ -0,0 +1,15 @@
1+-- see http://cabal.readthedocs.io/en/latest/nix-local-build.html
2+-- for more details
3+
4+packages: uuid-types/
5+packages: uuid/
6+
7+package uuid-types
8+ tests: True
9+ -- profiling: True
10+ -- profiling-detail: toplevel-functions
11+
12+package uuid
13+ tests: True
14+ -- profiling: True
15+ -- profiling-detail: toplevel-functions
gen-ci.shnew file+5
@@ -0,0 +1,5 @@
1+#!/bin/sh
2+
3+# haskell-ci doesn't know how to regenerate multiple GHA workflows
4+haskell-ci github cabal.project
5+haskell-ci github --project cabal.bench.project -o .github/workflows/haskell-ci-bench.yml --github-action-name Benchmarks
tests/BenchByteString.hsdeleted−82
@@ -1,82 +0,0 @@
1−import Control.Applicative ((<*>))
2−import Data.Functor ((<$>))
3−
4−import Data.ByteString (ByteString)
5−import qualified Data.ByteString.Char8 as BC8
6−import System.Random
7−import Test.QuickCheck
8−
9−import Criterion
10−import Criterion.Main
11−
12−import Data.UUID
13−
14−instance Arbitrary UUID where
15− arbitrary =
16− fromWords <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
17−
18−-- Testing what we do in the codebase
19−fromASCIIBytes_naive :: ByteString -> Maybe UUID
20−fromASCIIBytes_naive = fromString . BC8.unpack
21−
22−toASCIIBytes_naive :: UUID -> ByteString
23−toASCIIBytes_naive = BC8.pack . toString
24−
25−randomUUIDs :: IO [UUID]
26−randomUUIDs = sample' arbitrary
27−
28−randomCorrect :: IO [String]
29−randomCorrect = map toString <$> randomUUIDs
30−
31−randomSlightlyWrong :: IO [String]
32−randomSlightlyWrong = mapM screw =<< randomCorrect
33− where
34− screw s = do
35− ix <- randomRIO (0, length s - 1)
36− return (take ix s ++ "x" ++ drop (ix + 1) s)
37−
38−randomVeryWrong :: IO [String]
39−randomVeryWrong = sample' arbitrary
40−
41−main :: IO ()
42−main = do
43− uuids <- randomUUIDs
44− correct <- randomCorrect
45− let correctBytes = map BC8.pack correct
46− slightlyWrong <- randomSlightlyWrong
47− let slightlyWrongBytes = map BC8.pack slightlyWrong
48− veryWrong <- randomVeryWrong
49− let veryWrongBytes = map BC8.pack veryWrong
50−
51− defaultMain
52− [ bgroup "decoding"
53− [ bcompare
54− [ bgroup "correct"
55− [ bench "fromASCIIBytes" (nf (map fromASCIIBytes) correctBytes)
56− , bench "fromString" (nf (map fromString) correct)
57− , bench "fromASCIIBytes_naive" (nf (map fromASCIIBytes_naive) correctBytes)
58− ]
59− ]
60− , bcompare
61− [ bgroup "slightly wrong"
62− [ bench "fromASCIIBytes" (nf (map fromASCIIBytes) slightlyWrongBytes)
63− , bench "fromString" (nf (map fromString) slightlyWrong)
64− , bench "fromASCIIBytes_naive" (nf (map fromASCIIBytes_naive) slightlyWrongBytes)
65− ]
66− ]
67− , bcompare
68− [ bgroup "very wrong"
69− [ bench "fromASCIIBytes" (nf (map fromASCIIBytes) veryWrongBytes)
70− , bench "fromString" (nf (map fromString) veryWrong)
71− , bench "fromASCIIBytes_naive" (nf (map fromASCIIBytes_naive) veryWrongBytes)
72− ]
73− ]
74− ]
75− , bcompare
76− [ bgroup "encoding"
77− [ bench "toASCIIBytes" (nf (map toASCIIBytes) uuids)
78− , bench "toString" (nf (map toString) uuids)
79− , bench "toASCIIBytes_naive" (nf (map toASCIIBytes_naive) uuids)
80− ]
81− ]
82− ]
tests/TestUUID.hsdeleted−202
@@ -1,202 +0,0 @@
1−{-# LANGUAGE ViewPatterns #-}
2−import Control.Monad (replicateM)
3−import Data.Bits
4−import qualified Data.ByteString.Lazy as BL
5−import qualified Data.ByteString.Char8 as BC8
6−import Data.Char (ord)
7−import Data.Functor ((<$>))
8−import Data.List (nub, (\\))
9−import Data.Maybe
10−import Data.Word
11−import qualified Data.UUID as U
12−import qualified Data.UUID.V1 as U
13−import qualified Data.UUID.V3 as U3
14−import qualified Data.UUID.V5 as U5
15−import qualified Test.HUnit as H
16−import Test.HUnit hiding (Test)
17−import Test.QuickCheck hiding ((.&.))
18−import Test.Framework (defaultMain, Test(..))
19−import Test.Framework.Providers.HUnit (hUnitTestToTests)
20−import Test.Framework.Providers.QuickCheck2 (testProperty)
21−import System.IO
22−
23−
24−isValidVersion :: Int -> U.UUID -> Bool
25−isValidVersion v u = lenOK && variantOK && versionOK
26− where bs = U.toByteString u
27− lenOK = BL.length bs == 16
28− variantOK = (BL.index bs 8) .&. 0xc0 == 0x80
29− versionOK = (BL.index bs 6) .&. 0xf0 == fromIntegral (v `shiftL` 4)
30−
31−
32−instance Arbitrary U.UUID where
33− -- the UUID random instance ignores bounds
34− arbitrary = choose (U.nil, U.nil)
35−
36−
37−test_null :: H.Test
38−test_null = H.TestList [
39− "nil is null" ~: assertBool "" (U.null U.nil),
40− "namespaceDNS is not null" ~: assertBool "" (not $ U.null U3.namespaceDNS)
41− ]
42−
43−test_nil :: H.Test
44−test_nil = H.TestList [
45− "nil string" ~: U.toString U.nil @?= "00000000-0000-0000-0000-000000000000",
46− "nil bytes" ~: U.toByteString U.nil @?= BL.pack (replicate 16 0)
47− ]
48−
49−test_conv :: H.Test
50−test_conv = H.TestList [
51− "conv bytes to string" ~:
52− maybe "" (U.toString) (U.fromByteString b16) @?= s16,
53− "conv string to bytes" ~:
54− maybe BL.empty (U.toByteString) (U.fromString s16) @?= b16
55− ]
56− where b16 = BL.pack [1..16]
57− s16 = "01020304-0506-0708-090a-0b0c0d0e0f10"
58−
59−test_v1 :: [Maybe U.UUID] -> H.Test
60−test_v1 v1s = H.TestList [
61− "V1 unique" ~: nub (v1s \\ nub v1s) @?= [],
62− "V1 not null" ~: H.TestList $ map (testUUID (not . U.null)) v1s,
63− "V1 valid" ~: H.TestList $ map (testUUID (isValidVersion 1)) v1s
64− ]
65− where testUUID :: (U.UUID -> Bool) -> Maybe U.UUID -> H.Test
66− testUUID p u = maybe False p u ~? show u
67−
68−test_v3 :: H.Test
69−test_v3 = H.TestList [
70− "V3 computation" ~:
71− U3.generateNamed U3.namespaceDNS name @?= uV3
72− ]
73− where name = map (fromIntegral . ord) "www.widgets.com" :: [Word8]
74− uV3 = fromJust $ U.fromString "3d813cbb-47fb-32ba-91df-831e1593ac29"
75−
76−test_v5 :: H.Test
77−test_v5 = H.TestList [
78− "V5 computation" ~:
79− U5.generateNamed U5.namespaceDNS name @?= uV5
80− ]
81− where name = map (fromIntegral . ord) "www.widgets.com" :: [Word8]
82− uV5 = fromJust $ U.fromString "21f7f8de-8051-5b89-8680-0195ef798b6a"
83−
84−prop_stringRoundTrip :: Test
85−prop_stringRoundTrip = testProperty "String round trip" stringRoundTrip
86− where stringRoundTrip :: U.UUID -> Bool
87− stringRoundTrip u = maybe False (== u) $ U.fromString (U.toString u)
88−
89−prop_byteStringRoundTrip :: Test
90−prop_byteStringRoundTrip = testProperty "ByteString round trip" byteStringRoundTrip
91− where byteStringRoundTrip :: U.UUID -> Bool
92− byteStringRoundTrip u = maybe False (== u)
93− $ U.fromByteString (U.toByteString u)
94−
95−prop_stringLength :: Test
96−prop_stringLength = testProperty "String length" stringLength
97− where stringLength :: U.UUID -> Bool
98− stringLength u = length (U.toString u) == 36
99−
100−prop_byteStringLength :: Test
101−prop_byteStringLength = testProperty "ByteString length" byteStringLength
102− where byteStringLength :: U.UUID -> Bool
103− byteStringLength u = BL.length (U.toByteString u) == 16
104−
105−prop_randomsDiffer :: Test
106−prop_randomsDiffer = testProperty "Randoms differ" randomsDiffer
107− where randomsDiffer :: (U.UUID, U.UUID) -> Bool
108− randomsDiffer (u1, u2) = u1 /= u2
109−
110−prop_randomNotNull :: Test
111−prop_randomNotNull = testProperty "Random not null" randomNotNull
112− where randomNotNull :: U.UUID -> Bool
113− randomNotNull = not. U.null
114−
115−prop_randomsValid :: Test
116−prop_randomsValid = testProperty "Random valid" randomsValid
117− where randomsValid :: U.UUID -> Bool
118− randomsValid = isValidVersion 4
119−
120−prop_v3NotNull :: Test
121−prop_v3NotNull = testProperty "V3 not null" v3NotNull
122− where v3NotNull :: [Word8] -> Bool
123− v3NotNull = not . U.null . U3.generateNamed U3.namespaceDNS
124−
125−prop_v3Valid :: Test
126−prop_v3Valid = testProperty "V3 valid" v3Valid
127− where v3Valid :: [Word8] -> Bool
128− v3Valid = isValidVersion 3 . U3.generateNamed U3.namespaceDNS
129−
130−prop_v5NotNull :: Test
131−prop_v5NotNull = testProperty "V5 not null" v5NotNull
132− where v5NotNull :: [Word8] -> Bool
133− v5NotNull = not . U.null . U5.generateNamed U5.namespaceDNS
134−
135−prop_v5Valid :: Test
136−prop_v5Valid = testProperty "V5 valid" v5Valid
137− where v5Valid :: [Word8] -> Bool
138− v5Valid = isValidVersion 5 . U5.generateNamed U5.namespaceDNS
139−
140−prop_readShowRoundTrip :: Test
141−prop_readShowRoundTrip = testProperty "Read/Show round-trip" prop
142− where -- we're using 'Maybe UUID' to add a bit of
143− -- real-world complexity.
144− prop :: U.UUID -> Bool
145− prop uuid = read (show (Just uuid)) == Just uuid
146−
147−-- Mostly going to test for wrong UUIDs
148−fromASCIIBytes_fromString1 :: String -> Bool
149−fromASCIIBytes_fromString1 s =
150− if all (\c -> ord c < 256) s
151− then U.fromString s == U.fromASCIIBytes (BC8.pack s)
152− else True
153−
154−fromASCIIBytes_fromString2 :: U.UUID -> Bool
155−fromASCIIBytes_fromString2 (U.toString -> s) =
156− U.fromString s == U.fromASCIIBytes (BC8.pack s)
157−
158−toASCIIBytes_toString :: U.UUID -> Bool
159−toASCIIBytes_toString uuid =
160− U.toString uuid == BC8.unpack (U.toASCIIBytes uuid)
161−
162−fromASCIIBytes_toASCIIBytes :: U.UUID -> Bool
163−fromASCIIBytes_toASCIIBytes (BC8.pack . U.toString -> bs) =
164− Just bs == (U.toASCIIBytes <$> U.fromASCIIBytes bs)
165−
166−toASCIIBytes_fromASCIIBytes :: U.UUID -> Bool
167−toASCIIBytes_fromASCIIBytes uuid =
168− Just uuid == U.fromASCIIBytes (U.toASCIIBytes uuid)
169−
170−main :: IO ()
171−main = do
172− v1s <- replicateM 100 U.nextUUID
173− defaultMain $
174− concat $
175− [ hUnitTestToTests $ H.TestList [
176− test_null,
177− test_nil,
178− test_conv,
179− test_v1 v1s,
180− test_v3,
181− test_v5
182− ]
183− , [ prop_stringRoundTrip,
184− prop_readShowRoundTrip,
185− prop_byteStringRoundTrip,
186− prop_stringLength,
187− prop_byteStringLength,
188− prop_randomsDiffer,
189− prop_randomNotNull,
190− prop_randomsValid,
191− prop_v3NotNull,
192− prop_v3Valid,
193− prop_v5NotNull,
194− prop_v5Valid
195− ]
196− , [ testProperty "fromASCIIBytes_fromString1" fromASCIIBytes_fromString1
197− , testProperty "fromASCIIBytes_fromString2" fromASCIIBytes_fromString2
198− , testProperty "fromASCIIBytes_toString" toASCIIBytes_toString
199− , testProperty "fromASCIIBytes_toASCIIBytes" fromASCIIBytes_toASCIIBytes
200− , testProperty "toASCIIBytes_fromASCIIBytes" toASCIIBytes_fromASCIIBytes
201− ]
202− ]
tests/summarize-bench-results.pldeleted−14
@@ -1,14 +0,0 @@
1−#!/usr/bin/env perl
2−
3−
4−while(<>) {
5− if (/^benchmarking (.*)$/) {
6− $name = $1;
7− }
8− if (/^mean:\s*([\d.]*) (\w*),/) {
9− $mean = 1 * $1;
10− $units = $2;
11−
12− printf "%-30s %10.2f %2s\n", $name, $mean, $units;
13− }
14−}
LICENSE → uuid-bench/LICENSErenamed

The content is the same.

uuid-bench/bench/uuid-benchmark.hsnew file+33
@@ -0,0 +1,33 @@
1+import Criterion.Main
2+import Data.Char (ord)
3+import Data.IORef
4+import Data.Word
5+import qualified Data.UUID as U
6+import qualified Data.UUID.V4 as U
7+import qualified Data.UUID.V3 as U3
8+import qualified Data.UUID.V5 as U5
9+import System.Random
10+import System.Random.Mersenne.Pure64
11+
12+main :: IO ()
13+main = do
14+ let n1 = (map (fromIntegral . ord) "http://www.haskell.org/") :: [Word8]
15+
16+ -- setup for random generation
17+ randomState <- newPureMT >>= newIORef
18+ let randomUUID = do
19+ state <- readIORef randomState
20+ let (uuid, state') = random state
21+ writeIORef randomState state'
22+ return uuid
23+
24+ -- benchmark UUID generation
25+ defaultMain [
26+ bgroup "generation" [
27+ bench "V1" $ nfIO U.nextUUID,
28+ bench "V4-stock" $ nfIO (U.nextRandom :: IO U.UUID),
29+ bench "V4-mersenne" $ nfIO (randomUUID :: IO U.UUID),
30+ bench "V3" $ nf (U3.generateNamed U3.namespaceURL) n1,
31+ bench "V5" $ nf (U5.generateNamed U5.namespaceURL) n1
32+ ]
33+ ]
tests/BenchUUID.hs → uuid-bench/bench/uuid-types-benchmark.hsrenamed+62−55
@@ -1,22 +1,22 @@
11{-# LANGUAGE CPP #-}
22
3−import Control.DeepSeq
4−import Criterion.Main
5−import Data.Char (ord)
6−import Data.IORef
7−import Data.Maybe (fromJust)
8−import Data.Word
9−import qualified Data.Set as Set
10−import qualified Data.ByteString.Lazy as BL
3+#if !(MIN_VERSION_bytestring(0,10,0))
4+{-# OPTIONS_GHC -fno-warn-orphans #-}
5+-- Needed for NFData instance
6+import Control.DeepSeq
117import qualified Data.ByteString.Lazy.Internal as BL
12−import qualified Data.UUID as U
13−import qualified Data.UUID.V1 as U
14−import qualified Data.UUID.V3 as U3
15−import qualified Data.UUID.V5 as U5
16−import System.Random
17−import System.Random.Mersenne.Pure64
8+#endif
9+import Criterion.Main
10+import qualified Data.ByteString.Lazy as BL
11+import qualified Data.HashSet as HashSet
12+import Data.Maybe (fromJust)
13+import qualified Data.Set as Set
14+import qualified Data.UUID.Types as U
15+import Foreign (alloca, peek, poke)
16+import System.Random
1817
1918#if !(MIN_VERSION_bytestring(0,10,0))
19+-- orphan
2020instance NFData BL.ByteString where
2121 rnf BL.Empty = ()
2222 rnf (BL.Chunk _ ts) = rnf ts
@@ -24,52 +24,60 @@ instance NFData BL.ByteString where
2424
2525main :: IO ()
2626main = do
27− u1 <- randomIO
28− let s1 = U.toString u1
29− b1 = U.toByteString u1
30− n1 = (map (fromIntegral . ord) "http://www.haskell.org/") :: [Word8]
31− nil2 = fromJust $
32− U.fromString "00000000-0000-0000-0000-000000000000"
33− u2a = fromJust $ U.fromString "169a5a43-c051-4a16-98f4-08447ddd5dc0"
34− u2b = fromJust $ U.fromByteString $
35− BL.pack [0x16, 0x9a, 0x5a, 0x43, 0xc0, 0x51, 0x4a, 0x16,
36− 0x98, 0xf4, 0x08, 0x44, 0x7d, 0xdd, 0x5d, 0xc0]
37− u3 = fromJust $ U.fromString "dea6f619-1038-438b-b4af-f1cdec1e6e23"
27+ u1 <- randomIO
28+ let s1 = U.toString u1
29+ b1 = U.toByteString u1
30+ a1 = U.toASCIIBytes u1
31+ t1 = U.toText u1
32+ (w1a,w1b,w1c,w1d) = U.toWords u1
33+ nil2 = fromJust $
34+ U.fromString "00000000-0000-0000-0000-000000000000"
35+ u2a = fromJust $ U.fromString "169a5a43-c051-4a16-98f4-08447ddd5dc0"
36+ u2b = fromJust $ U.fromByteString $
37+ BL.pack [0x16, 0x9a, 0x5a, 0x43, 0xc0, 0x51, 0x4a, 0x16,
38+ 0x98, 0xf4, 0x08, 0x44, 0x7d, 0xdd, 0x5d, 0xc0]
39+ u3 = fromJust $ U.fromString "dea6f619-1038-438b-b4af-f1cdec1e6e23"
40+
41+ -- setup for Storable benchmark
42+ alloca $ \ uuidPtr -> do
43+
44+ poke uuidPtr u1
3845
39− -- setup for random generation
40− randomState <- newPureMT >>= newIORef
41− let randomUUID = do
42− state <- readIORef randomState
43− let (uuid, state') = random state
44− writeIORef randomState state'
45− return uuid
46+ defaultMain [
47+ bgroup "testing" [
48+ bench "null non-nil" $ whnf U.null u1,
49+ bench "null nil" $ whnf U.null U.nil,
50+ bench "null nil2" $ whnf U.null nil2,
51+ bench "eq same" $ whnf (==u2a) u2b,
52+ bench "eq differ" $ whnf (==u2a) u3,
53+ bench "compare same" $ whnf (compare u2a) u2b,
54+ bench "compare differ" $ whnf (compare u2a) u3
55+ ],
56+ bgroup "conversion" [
57+ bench "toString" $ nf U.toString u1,
58+ bench "fromString" $ nf U.fromString s1,
59+ bench "toByteString" $ nf U.toByteString u1,
60+ bench "fromByteString" $ nf U.fromByteString b1,
61+ bench "toASCIIBytes" $ nf U.toASCIIBytes u1,
62+ bench "fromASCIIBytes" $ nf U.fromASCIIBytes a1,
63+ bench "toWords" $ nf U.toWords u1,
64+ bench "fromWords" $ nf (U.fromWords w1a w1b w1c) w1d,
65+ bench "toText" $ nf U.toText u1,
66+ bench "fromText" $ nf U.fromText t1
67+ ],
4668
47− defaultMain [
48− bgroup "testing" [
49− bench "null non-nil" $ whnf U.null u1,
50− bench "null nil" $ whnf U.null U.nil,
51− bench "null nil2" $ whnf U.null nil2,
52− bench "eq same" $ whnf (==u2a) u2b,
53− bench "eq differ" $ whnf (==u2a) u3
54− ],
55− bgroup "conversion" [
56− bench "toString" $ nf U.toString u1,
57− bench "fromString" $ nf U.fromString s1,
58− bench "toByteString" $ nf U.toByteString u1,
59− bench "fromByteString" $ nf U.fromByteString b1
60− ],
61− bgroup "generation" [
62− bench "V1" $ nfIO U.nextUUID,
63− bench "V4" $ nfIO (randomUUID :: IO U.UUID),
64− bench "V3" $ nf (U3.generateNamed U3.namespaceURL) n1,
65− bench "V5" $ nf (U5.generateNamed U5.namespaceURL) n1
66− ],
67− bench "set making" $ nf Set.fromList uuids
69+ bench "Set making" $ nf Set.fromList uuids,
70+ bench "HashSet making" $ nf HashSet.fromList uuids,
71+
72+ bgroup "storable" [
73+ bench "peek" $ nfIO (peek uuidPtr),
74+ bench "poke" $ whnfIO $ poke uuidPtr u1
6875 ]
76+ ]
6977
7078-- 50 uuids, so tests can be repeatable
7179uuids :: [U.UUID]
72−uuids
80+uuids
7381 = map (fromJust . U.fromString)
7482 [
7583 "35d42593-1fca-4465-b588-a2e78cb996ba",
@@ -123,4 +131,3 @@ uuids
123131 "8091837c-6456-42c3-a686-f4731a41d4f9",
124132 "2a0e2efb-a11c-4a44-81ee-3efc37379b48"
125133 ]
126−
uuid-bench/src-uuidnew file+1
@@ -0,0 +1 @@
1+../uuid/src
uuid-bench/src-uuid-typesnew file+1
@@ -0,0 +1 @@
1+../uuid-types/src
uuid-bench/uuid-bench.cabalnew file+126
@@ -0,0 +1,126 @@
1+cabal-version: 1.12
2+name: uuid-bench
3+version: 0
4+copyright:
5+ (c) 2017-2018 Herbert Valerio Riedel
6+ (c) 2008-2014 Antoine Latter
7+
8+author: Antoine Latter
9+maintainer: Oleg Grenrus <oleg.grenrus@iki.fi>
10+license: BSD3
11+license-file: LICENSE
12+category: Data
13+build-type: Simple
14+
15+-- Benchmarking with older GHCs is not meaningful
16+tested-with:
17+ GHC ==8.6.5
18+ || ==8.8.4
19+ || ==8.10.7
20+ || ==9.0.2
21+ || ==9.2.8
22+ || ==9.4.8
23+ || ==9.6.6
24+ || ==9.8.2
25+ || ==9.10.1
26+
27+synopsis: UUID benchmarks
28+description:
29+ UUID benchmarks.
30+ This package is combined uuid and uuid-types, renamed so we do not
31+ cause benchmark tools to be build against local package.
32+
33+homepage: https://github.com/haskell-hvr/uuid
34+bug-reports: https://github.com/haskell-hvr/uuid/issues
35+
36+source-repository head
37+ type: git
38+ location: https://github.com/haskell-hvr/uuid.git
39+ subdir: uuid-bench
40+
41+library
42+ build-depends:
43+ base >=4.12.0.0 && <5
44+ , binary >=0.8.6.0 && <0.9
45+ , bytestring >=0.10.8.2 && <0.13
46+ , cryptohash-md5 >=0.11.100 && <0.12
47+ , cryptohash-sha1 >=0.11.100 && <0.12
48+ , deepseq >=1.4.4.0 && <1.6
49+ , entropy >=0.3.7 && <0.5
50+ , hashable >=1.4.4.0 && <1.6
51+ , network-info >=0.2 && <0.3
52+ , random >=1.2.1.2 && <1.3
53+ , template-haskell >=2.14.0.0 && <2.23
54+ , text >=1.2.3.0 && <1.3 || >=2.0 && <2.2
55+ , time >=1.4 && <1.13
56+
57+ -- uuid modules
58+ exposed-modules:
59+ Data.UUID
60+ Data.UUID.Util
61+ Data.UUID.V3
62+ Data.UUID.V4
63+ Data.UUID.V5
64+
65+ other-modules:
66+ Data.UUID.Named
67+ Data.Word.Util
68+
69+ -- uuid-types modules
70+ exposed-modules: Data.UUID.Types
71+
72+ -- Exposed for companion projects; *NOT* part of the official API:
73+ exposed-modules:
74+ Data.UUID.Types.Internal
75+ Data.UUID.Types.Internal.Builder
76+
77+ default-language: Haskell2010
78+ other-extensions:
79+ DeriveDataTypeable
80+ TypeFamilies
81+
82+ ghc-options: -Wall
83+ hs-source-dirs: src-uuid-types src-uuid
84+
85+benchmark uuid-types-benchmark
86+ type: exitcode-stdio-1.0
87+ main-is: uuid-types-benchmark.hs
88+ hs-source-dirs: bench
89+ default-language: Haskell2010
90+ other-extensions: CPP
91+ ghc-options: -Wall
92+
93+ -- inherited constraints
94+ build-depends:
95+ base
96+ , bytestring
97+ , random
98+ , uuid-bench
99+
100+ -- deps w/o inherited constraints
101+ build-depends:
102+ containers >=0.5 && <0.8
103+ , criterion >=1.5 && <1.7
104+ , unordered-containers >=0.2.7 && <0.3
105+
106+benchmark uuid-benchmark
107+ type: exitcode-stdio-1.0
108+ main-is: uuid-benchmark.hs
109+ hs-source-dirs: bench
110+ default-language: Haskell2010
111+ default-extensions:
112+ CPP
113+ DeriveDataTypeable
114+
115+ ghc-options: -Wall -fno-warn-orphans
116+
117+ -- inherited constraints
118+ build-depends:
119+ base
120+ , random
121+ , uuid-bench
122+
123+ -- deps w/o inherited constraints
124+ build-depends:
125+ criterion >=1.5 && <1.7
126+ , mersenne-random-pure64 >=0.2 && <0.3
uuid-types/ChangeLog.mdnew file+31
@@ -0,0 +1,31 @@
1+## 1.0.6 (2023-04-16)
2+
3+- Support GHC-8.6.5..GHC-9.10.1
4+
5+## 1.0.5 (2021-05-03)
6+
7+- Add (Template Haskell) `Lift UUID` instance
8+
9+## 1.0.4.0
10+
11+- Declare `Data.UUID.Types` module "`Trustworthy`" with respect to SafeHaskell.
12+- Use more compact heap object representation which saves 16 bytes on 64bit platforms.
13+- Add `toWords64`/`fromWords64` functions
14+- Drop support for GHC < 7.
15+- Add support for `random-1.2`, i.e. idiomatic `Random` and added `Uniform` instances.
16+
17+## 1.0.3
18+
19+- Bump package dependencies.
20+
21+## 1.0.2
22+
23+- Add `toText`/`fromText` functions.
24+
25+## 1.0.1
26+
27+- Update dependencies in tests and benchmarks.
28+
29+## 1.0.0
30+
31+- Initial split from "`uuid-1.3.8`" package.
uuid-types/LICENSEnew file+28
@@ -0,0 +1,28 @@
1+Copyright (c) 2008, Antoine Latter
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are
7+met:
8+
9+ * Redistributions of source code must retain the above copyright
10+notice, this list of conditions and the following disclaimer.
11+ * Redistributions in binary form must reproduce the above copyright
12+notice, this list of conditions and the following disclaimer in the
13+documentation and/or other materials provided with the distribution.
14+ * The names of the authors may not be used to endorse or promote
15+products derived from this software without specific prior written
16+permission.
17+
18+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Setup.hs → uuid-types/Setup.hsrenamed

The content is the same.

uuid-types/cabal.projectnew file+1
@@ -0,0 +1 @@
1+packages: .
uuid-types/src/Data/UUID/Types.hsnew file+47
@@ -0,0 +1,47 @@
1+{-# LANGUAGE Trustworthy #-}
2+
3+{- |
4+Module : Data.UUID.Types
5+Copyright : (c) 2017-2018 Herbert Valerio Riedel
6+ (c) 2008,2012 Antoine Latter
7+
8+License : BSD-style
9+
10+Maintainer : hvr@gnu.org
11+Portability : portable
12+
13+This library is useful for comparing, parsing and
14+printing <http://en.wikipedia.org/wiki/UUID Universally Unique Identifiers (UUID)>.
15+See <http://tools.ietf.org/html/rfc4122 RFC 4122> for the specification.
16+
17+-}
18+module Data.UUID.Types
19+ ( -- * The 'UUID' Type
20+ UUID
21+ -- * Nil UUID
22+ , nil
23+ , null
24+ -- * Textual Representation
25+ , toString
26+ , fromString
27+ , toText
28+ , fromText
29+ , toASCIIBytes
30+ , fromASCIIBytes
31+ , toLazyASCIIBytes
32+ , fromLazyASCIIBytes
33+ -- * Binary Representation
34+ , toByteString
35+ , fromByteString
36+ -- * Integer Representation
37+ , toWords
38+ , fromWords
39+ , toWords64
40+ , fromWords64
41+ ) where
42+
43+import Data.UUID.Types.Internal
44+import Prelude ()
45+
46+-- Everything is really implemented in Data.UUID.Types.Internal, but I
47+-- don't want to export the constructors out of the package.
uuid-types/src/Data/UUID/Types/Internal.hsnew file+575
@@ -0,0 +1,575 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE TypeFamilies #-}
4+{-# LANGUAGE ViewPatterns #-}
5+{-# LANGUAGE Unsafe #-}
6+{-# LANGUAGE DeriveLift #-}
7+{-# LANGUAGE StandaloneDeriving #-}
8+{-# OPTIONS_HADDOCK hide #-}
9+
10+-- |
11+-- Module : Data.UUID.Types.Internal
12+-- Copyright : (c) 2017-2018 Herbert Valerio Riedel
13+-- (c) 2008-2009, 2012 Antoine Latter
14+-- (c) 2009 Mark Lentczner
15+--
16+-- License : BSD-style
17+--
18+-- Maintainer : hvr@gnu.org
19+-- Portability : portable
20+
21+module Data.UUID.Types.Internal
22+ ( UUID(..)
23+ , null
24+ , nil
25+ , fromByteString
26+ , toByteString
27+ , fromString
28+ , toString
29+ , fromText
30+ , toText
31+ , fromWords
32+ , toWords
33+ , fromWords64
34+ , toWords64
35+ , toList
36+ , buildFromBytes
37+ , buildFromWords
38+ , fromASCIIBytes
39+ , toASCIIBytes
40+ , fromLazyASCIIBytes
41+ , toLazyASCIIBytes
42+ , UnpackedUUID(..)
43+ , pack
44+ , unpack
45+ ) where
46+
47+import Prelude hiding (null)
48+
49+import Control.DeepSeq (NFData (..))
50+import Control.Monad (guard, liftM2)
51+import Data.Bits
52+import Data.Char
53+import Data.Data
54+import Data.Hashable
55+import Data.List (elemIndices)
56+import Foreign.Ptr (Ptr)
57+
58+import Foreign.Storable
59+
60+import Data.Binary
61+import Data.Binary.Get
62+import Data.Binary.Put
63+import qualified Data.ByteString as B
64+import qualified Data.ByteString.Internal as BI
65+import qualified Data.ByteString.Lazy as BL
66+import qualified Data.ByteString.Unsafe as BU
67+import Data.Text (Text)
68+import qualified Data.Text.Encoding as T
69+
70+import Data.UUID.Types.Internal.Builder
71+
72+import System.Random (Random (..), uniform)
73+import System.Random.Stateful (Uniform (..), uniformWord64)
74+
75+import Language.Haskell.TH.Syntax (Lift)
76+
77+-- | Type representing <https://en.wikipedia.org/wiki/UUID Universally Unique Identifiers (UUID)> as specified in
78+-- <http://tools.ietf.org/html/rfc4122 RFC 4122>.
79+data UUID = UUID {-# UNPACK #-} !Word64 {-# UNPACK #-} !Word64
80+ deriving (Eq, Ord)
81+{-
82+ Prior to uuid-types-1.0.4:
83+ !Word32 !Word32 !Word32 !Word32
84+ Other representations that we tried are:
85+ Mimic V1 structure: !Word32 !Word16 !Word16 !Word16
86+ !Word8 !Word8 !Word8 !Word8 !Word8 !Word8
87+ Sixteen bytes: !Word8 ... (x 16)
88+ Simple list of bytes: [Word8]
89+ ByteString (strict) ByteString
90+ Immutable array: UArray Int Word8
91+ Vector: UArr Word8
92+ None was as fast, overall, as the representation used here.
93+-}
94+
95+-- | Convert a 'UUID' into a sequence of 'Word32' values.
96+-- Useful for when you need to serialize a UUID and
97+-- neither 'Storable' nor 'Binary' are appropriate.
98+--
99+-- >>> toWords <$> fromString "550e8400-e29b-41d4-a716-446655440000"
100+-- Just (1427014656,3801825748,2803254374,1430519808)
101+--
102+-- See also 'toWords64'.
103+--
104+-- /Since: @uuid-1.2.2@/
105+--
106+-- @since 1.0.0
107+toWords :: UUID -> (Word32, Word32, Word32, Word32)
108+toWords (UUID w12 w34) = (w1, w2, w3, w4)
109+ where
110+ w1 = fromIntegral (w12 `unsafeShiftR` 32)
111+ w2 = fromIntegral w12
112+ w3 = fromIntegral (w34 `unsafeShiftR` 32)
113+ w4 = fromIntegral w34
114+
115+-- | Create a 'UUID' from a sequence of 'Word32'. The
116+-- inverse of 'toWords'. Useful when you need a total
117+-- function for constructing 'UUID' values.
118+--
119+-- See also 'fromWords64'.
120+--
121+-- /Since: @uuid-1.2.2@/
122+--
123+-- @since 1.0.0
124+fromWords :: Word32 -> Word32 -> Word32 -> Word32 -> UUID
125+fromWords w1 w2 w3 w4 = UUID (w32to64 w1 w2) (w32to64 w3 w4)
126+
127+-- | Convert a 'UUID' into a pair of 'Word64's.
128+--
129+-- >>> toWords64 <$> fromString "550e8400-e29b-41d4-a716-446655440000"
130+-- Just (6128981282234515924,12039885860129472512)
131+--
132+-- See also 'toWords'.
133+--
134+-- @since 1.0.4
135+toWords64 :: UUID -> (Word64, Word64)
136+toWords64 (UUID w12 w34) = (w12,w34)
137+
138+-- | Create a 'UUID' from a pair of 'Word64's.
139+--
140+-- Inverse of 'toWords64'. See also 'fromWords'.
141+--
142+-- @since 1.0.4
143+fromWords64 :: Word64 -> Word64 -> UUID
144+fromWords64 = UUID
145+
146+data UnpackedUUID =
147+ UnpackedUUID {
148+ time_low :: Word32 -- 0-3
149+ , time_mid :: Word16 -- 4-5
150+ , time_hi_and_version :: Word16 -- 6-7
151+ , clock_seq_hi_res :: Word8 -- 8
152+ , clock_seq_low :: Word8 -- 9
153+ , node_0 :: Word8
154+ , node_1 :: Word8
155+ , node_2 :: Word8
156+ , node_3 :: Word8
157+ , node_4 :: Word8
158+ , node_5 :: Word8
159+ }
160+ deriving (Read, Show, Eq, Ord)
161+
162+unpack :: UUID -> UnpackedUUID
163+unpack (UUID w0 w1) = build /-/ w0 /-/ w1
164+ where
165+ build x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF =
166+ UnpackedUUID {
167+ time_low = word x0 x1 x2 x3
168+ , time_mid = w8to16 x4 x5
169+ , time_hi_and_version = w8to16 x6 x7
170+ , clock_seq_hi_res = x8
171+ , clock_seq_low = x9
172+ , node_0 = xA
173+ , node_1 = xB
174+ , node_2 = xC
175+ , node_3 = xD
176+ , node_4 = xE
177+ , node_5 = xF
178+ }
179+
180+pack :: UnpackedUUID -> UUID
181+pack unpacked =
182+ makeFromBytes /-/ (time_low unpacked)
183+ /-/ (time_mid unpacked)
184+ /-/ (time_hi_and_version unpacked)
185+ /-/ (clock_seq_hi_res unpacked)
186+ /-/ (clock_seq_low unpacked)
187+ /-/ (node_0 unpacked) /-/ (node_1 unpacked)
188+ /-/ (node_2 unpacked) /-/ (node_3 unpacked)
189+ /-/ (node_4 unpacked) /-/ (node_5 unpacked)
190+
191+
192+--
193+-- UTILITIES
194+--
195+
196+-- |Build a Word32 from four Word8 values, presented in big-endian order
197+word :: Word8 -> Word8 -> Word8 -> Word8 -> Word32
198+word a b c d = (fromIntegral a `unsafeShiftL` 24)
199+ .|. (fromIntegral b `unsafeShiftL` 16)
200+ .|. (fromIntegral c `unsafeShiftL` 8)
201+ .|. (fromIntegral d )
202+
203+-- |Extract a Word8 from a Word64. Bytes, high to low, are numbered from 7 to 0,
204+byte :: Int -> Word64 -> Word8
205+byte i w = fromIntegral (w `shiftR` (i * 8))
206+
207+-- |Build a Word16 from two Word8 values, presented in big-endian order.
208+w8to16 :: Word8 -> Word8 -> Word16
209+w8to16 w0s w1s =
210+ (w0 `unsafeShiftL` 8) .|. w1
211+ where
212+ w0 = fromIntegral w0s
213+ w1 = fromIntegral w1s
214+
215+-- | Construct 'Word64' from low/high 'Word32's
216+w32to64 :: Word32 -> Word32 -> Word64
217+w32to64 w0 w1 = (fromIntegral w0 `unsafeShiftL` 32) .|. (fromIntegral w1)
218+
219+-- |Make a UUID from sixteen Word8 values
220+makeFromBytes :: Word8 -> Word8 -> Word8 -> Word8
221+ -> Word8 -> Word8 -> Word8 -> Word8
222+ -> Word8 -> Word8 -> Word8 -> Word8
223+ -> Word8 -> Word8 -> Word8 -> Word8
224+ -> UUID
225+makeFromBytes b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
226+ = fromWords w0 w1 w2 w3
227+ where w0 = word b0 b1 b2 b3
228+ w1 = word b4 b5 b6 b7
229+ w2 = word b8 b9 ba bb
230+ w3 = word bc bd be bf
231+
232+-- |A Builder for constructing a UUID of a given version.
233+buildFromBytes :: Word8
234+ -> Word8 -> Word8 -> Word8 -> Word8
235+ -> Word8 -> Word8 -> Word8 -> Word8
236+ -> Word8 -> Word8 -> Word8 -> Word8
237+ -> Word8 -> Word8 -> Word8 -> Word8
238+ -> UUID
239+buildFromBytes v b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf =
240+ makeFromBytes b0 b1 b2 b3 b4 b5 b6' b7 b8' b9 ba bb bc bd be bf
241+ where b6' = b6 .&. 0x0f .|. (v `shiftL` 4)
242+ b8' = b8 .&. 0x3f .|. 0x80
243+
244+-- |Build a UUID of a given version from 'Word32' values.
245+buildFromWords :: Word8 -> Word32 -> Word32 -> Word32 -> Word32 -> UUID
246+buildFromWords v w0 w1 w2 w3 = fromWords w0 w1' w2' w3
247+ where w1' = w1 .&. 0xffff0fff .|. ((fromIntegral v) `shiftL` 12)
248+ w2' = w2 .&. 0x3fffffff .|. 0x80000000
249+
250+
251+-- |Return the bytes that make up the UUID
252+toList :: UUID -> [Word8]
253+toList (UUID w0 w1) =
254+ [byte 7 w0, byte 6 w0, byte 5 w0, byte 4 w0,
255+ byte 3 w0, byte 2 w0, byte 1 w0, byte 0 w0,
256+ byte 7 w1, byte 6 w1, byte 5 w1, byte 4 w1,
257+ byte 3 w1, byte 2 w1, byte 1 w1, byte 0 w1]
258+
259+-- |Construct a UUID from a list of Word8. Returns Nothing if the list isn't
260+-- exactly sixteen bytes long
261+fromList :: [Word8] -> Maybe UUID
262+fromList [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf] =
263+ Just $ makeFromBytes b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
264+fromList _ = Nothing
265+
266+
267+--
268+-- UUID API
269+--
270+
271+-- |Returns true if the passed-in UUID is the 'nil' UUID.
272+null :: UUID -> Bool
273+null = (== nil)
274+ -- Note: This actually faster than:
275+ -- null (UUID 0 0 0 0) = True
276+ -- null _ = False
277+
278+-- |The 'nil' UUID, as defined in <http://tools.ietf.org/html/rfc4122 RFC 4122>.
279+-- It is a UUID of all zeros. @'null' u@ /iff/ @'u' == 'nil'@.
280+nil :: UUID
281+nil = UUID 0 0
282+
283+-- |Extract a UUID from a 'ByteString' in network byte order.
284+-- The argument must be 16 bytes long, otherwise 'Nothing' is returned.
285+fromByteString :: BL.ByteString -> Maybe UUID
286+fromByteString = fromList . BL.unpack
287+
288+-- |Encode a UUID into a 'ByteString' in network order.
289+--
290+-- This uses the same encoding as the 'Binary' instance.
291+toByteString :: UUID -> BL.ByteString
292+toByteString = BL.pack . toList
293+
294+-- |If the passed in 'String' can be parsed as a 'UUID', it will be.
295+-- The hyphens may not be omitted.
296+-- Example:
297+--
298+-- >>> fromString "c2cc10e1-57d6-4b6f-9899-38d972112d8c"
299+-- Just c2cc10e1-57d6-4b6f-9899-38d972112d8c
300+--
301+-- Hex digits may be upper or lower-case.
302+fromString :: String -> Maybe UUID
303+fromString xs | validFmt = fromString' xs
304+ | otherwise = Nothing
305+ where validFmt = elemIndices '-' xs == [8,13,18,23]
306+
307+fromString' :: String -> Maybe UUID
308+fromString' s0 = do
309+ (w0, s1) <- hexWord s0
310+ (w1, s2) <- hexWord s1
311+ (w2, s3) <- hexWord s2
312+ (w3, s4) <- hexWord s3
313+ if s4 /= "" then Nothing
314+ else Just $ fromWords w0 w1 w2 w3
315+ where hexWord :: String -> Maybe (Word32, String)
316+ hexWord s = Just (0, s) >>= hexByte >>= hexByte
317+ >>= hexByte >>= hexByte
318+
319+ hexByte :: (Word32, String) -> Maybe (Word32, String)
320+ hexByte (w, '-':ds) = hexByte (w, ds)
321+ hexByte (w, hi:lo:ds)
322+ | bothHex = Just ((w `shiftL` 8) .|. octet, ds)
323+ | otherwise = Nothing
324+ where bothHex = isHexDigit hi && isHexDigit lo
325+ octet = fromIntegral (16 * digitToInt hi + digitToInt lo)
326+ hexByte _ = Nothing
327+
328+-- | Convert a UUID into a hypenated string using lower-case letters.
329+-- Example:
330+--
331+-- >>> toString <$> fromString "550e8400-e29b-41d4-a716-446655440000"
332+-- Just "550e8400-e29b-41d4-a716-446655440000"
333+--
334+--
335+toString :: UUID -> String
336+toString uuid = hexw0 w0 $ hexw1 w1 ""
337+ where hexw0 :: Word64 -> String -> String
338+ hexw0 w s = hexn w 60 : hexn w 56 : hexn w 52 : hexn w 48
339+ : hexn w 44 : hexn w 40 : hexn w 36 : hexn w 32
340+ : '-' : hexn w 28 : hexn w 24 : hexn w 20 : hexn w 16
341+ : '-' : hexn w 12 : hexn w 8 : hexn w 4 : hexn w 0
342+ : s
343+
344+ hexw1 :: Word64 -> String -> String
345+ hexw1 w s = '-' : hexn w 60 : hexn w 56 : hexn w 52 : hexn w 48
346+ : '-' : hexn w 44 : hexn w 40 : hexn w 36 : hexn w 32
347+ : hexn w 28 : hexn w 24 : hexn w 20 : hexn w 16
348+ : hexn w 12 : hexn w 8 : hexn w 4 : hexn w 0
349+ : s
350+
351+ hexn :: Word64 -> Int -> Char
352+ hexn w r = intToDigit $ fromIntegral ((w `shiftR` r) .&. 0xf)
353+
354+ (w0,w1) = toWords64 uuid
355+
356+-- | If the passed in `Text` can be parsed as an ASCII representation of
357+-- a `UUID`, it will be. The hyphens may not be omitted.
358+fromText :: Text -> Maybe UUID
359+fromText = fromASCIIBytes . T.encodeUtf8
360+
361+-- | Convert a UUID into a hyphentated string using lower-case letters.
362+toText :: UUID -> Text
363+toText = T.decodeLatin1 . toASCIIBytes
364+
365+-- | Convert a UUID into a hyphentated string using lower-case letters, packed
366+-- as ASCII bytes into `B.ByteString`.
367+--
368+-- This should be equivalent to `toString` with `Data.ByteString.Char8.pack`.
369+toASCIIBytes :: UUID -> B.ByteString
370+toASCIIBytes uuid = BI.unsafeCreate 36 (pokeASCII uuid)
371+
372+-- | Helper function for `toASCIIBytes`
373+pokeASCII :: UUID -> Ptr Word8 -> IO ()
374+pokeASCII uuid ptr = do
375+ pokeDash 8
376+ pokeDash 13
377+ pokeDash 18
378+ pokeDash 23
379+ pokeSingle 0 w0
380+ pokeDouble 9 w1
381+ pokeDouble 19 w2
382+ pokeSingle 28 w3
383+ where
384+ (w0, w1, w2, w3) = toWords uuid
385+
386+ -- ord '-' ==> 45
387+ pokeDash ix = pokeElemOff ptr ix 45
388+
389+ pokeSingle ix w = do
390+ pokeWord ix w 28
391+ pokeWord (ix + 1) w 24
392+ pokeWord (ix + 2) w 20
393+ pokeWord (ix + 3) w 16
394+ pokeWord (ix + 4) w 12
395+ pokeWord (ix + 5) w 8
396+ pokeWord (ix + 6) w 4
397+ pokeWord (ix + 7) w 0
398+
399+ -- We skip the dash in the middle
400+ pokeDouble ix w = do
401+ pokeWord ix w 28
402+ pokeWord (ix + 1) w 24
403+ pokeWord (ix + 2) w 20
404+ pokeWord (ix + 3) w 16
405+ pokeWord (ix + 5) w 12
406+ pokeWord (ix + 6) w 8
407+ pokeWord (ix + 7) w 4
408+ pokeWord (ix + 8) w 0
409+
410+ pokeWord ix w r =
411+ pokeElemOff ptr ix (fromIntegral (toDigit ((w `shiftR` r) .&. 0xf)))
412+
413+ toDigit :: Word32 -> Word32
414+ toDigit w = if w < 10 then 48 + w else 97 + w - 10
415+
416+-- | If the passed in `B.ByteString` can be parsed as an ASCII representation of
417+-- a `UUID`, it will be. The hyphens may not be omitted.
418+--
419+-- This should be equivalent to `fromString` with `Data.ByteString.Char8.unpack`.
420+fromASCIIBytes :: B.ByteString -> Maybe UUID
421+fromASCIIBytes bs = do
422+ guard wellFormed
423+ fromWords <$> single 0 <*> double 9 14 <*> double 19 24 <*> single 28
424+ where
425+ -- ord '-' ==> 45
426+ dashIx bs' ix = BU.unsafeIndex bs' ix == 45
427+
428+ -- Important: check the length first, given the `unsafeIndex` later.
429+ wellFormed =
430+ B.length bs == 36 && dashIx bs 8 && dashIx bs 13 &&
431+ dashIx bs 18 && dashIx bs 23
432+
433+ single ix = combine <$> octet ix <*> octet (ix + 2)
434+ <*> octet (ix + 4) <*> octet (ix + 6)
435+ double ix0 ix1 = combine <$> octet ix0 <*> octet (ix0 + 2)
436+ <*> octet ix1 <*> octet (ix1 + 2)
437+
438+ combine o0 o1 o2 o3 = shiftL o0 24 .|. shiftL o1 16 .|. shiftL o2 8 .|. o3
439+
440+ octet ix = do
441+ hi <- fromIntegral <$> toDigit (BU.unsafeIndex bs ix)
442+ lo <- fromIntegral <$> toDigit (BU.unsafeIndex bs (ix + 1))
443+ return (16 * hi + lo)
444+
445+ toDigit :: Word8 -> Maybe Word8
446+ toDigit w
447+ -- Digit
448+ | w >= 48 && w <= 57 = Just (w - 48)
449+ -- Uppercase
450+ | w >= 65 && w <= 70 = Just (10 + w - 65)
451+ -- Lowercase
452+ | w >= 97 && w <= 102 = Just (10 + w - 97)
453+ | otherwise = Nothing
454+
455+-- | Similar to `toASCIIBytes` except we produce a lazy `BL.ByteString`.
456+toLazyASCIIBytes :: UUID -> BL.ByteString
457+toLazyASCIIBytes =
458+ BL.fromStrict
459+ . toASCIIBytes
460+
461+-- | Similar to `fromASCIIBytes` except parses from a lazy `BL.ByteString`.
462+fromLazyASCIIBytes :: BL.ByteString -> Maybe UUID
463+fromLazyASCIIBytes bs =
464+ if BL.length bs == 36 then fromASCIIBytes (BL.toStrict bs) else Nothing
465+
466+--
467+-- Class Instances
468+--
469+
470+-- | This 'Random' instance produces __insecure__ version 4 UUIDs as
471+-- specified in <http://tools.ietf.org/html/rfc4122 RFC 4122>.
472+instance Random UUID where
473+ random = uniform
474+ randomR _ = random -- range is ignored
475+
476+-- @since 1.0.4
477+instance Uniform UUID where
478+ uniformM gen = do
479+ w0 <- uniformWord64 gen
480+ w1 <- uniformWord64 gen
481+ pure $ buildFromBytes 4 /-/ w0 /-/ w1
482+
483+-- |A ByteSource to extract only three bytes from an Int, since next on StdGet
484+-- only returns 31 bits of randomness.
485+type instance ByteSink ThreeByte g = Takes3Bytes g
486+newtype ThreeByte = ThreeByte Int
487+instance ByteSource ThreeByte where
488+ f /-/ (ThreeByte w) = f b1 b2 b3
489+ where b1 = fromIntegral (w `unsafeShiftR` 16)
490+ b2 = fromIntegral (w `unsafeShiftR` 8)
491+ b3 = fromIntegral w
492+
493+instance NFData UUID where
494+ rnf = flip seq ()
495+
496+instance Hashable UUID where
497+ hash (toWords -> (w0,w1,w2,w3)) =
498+ hash w0 `hashWithSalt` w1
499+ `hashWithSalt` w2
500+ `hashWithSalt` w3
501+ hashWithSalt s (toWords -> (w0,w1,w2,w3)) =
502+ s `hashWithSalt` w0
503+ `hashWithSalt` w1
504+ `hashWithSalt` w2
505+ `hashWithSalt` w3
506+
507+-- | Pretty prints a 'UUID' (without quotation marks). See also 'toString'.
508+--
509+-- >>> show nil
510+-- "00000000-0000-0000-0000-000000000000"
511+--
512+instance Show UUID where
513+ show = toString
514+
515+instance Read UUID where
516+ readsPrec _ str =
517+ let noSpaces = dropWhile isSpace str
518+ in case fromString (take 36 noSpaces) of
519+ Nothing -> []
520+ Just u -> [(u,drop 36 noSpaces)]
521+
522+-- | This 'Storable' instance uses the memory layout as described in <http://tools.ietf.org/html/rfc4122 RFC 4122>, but in contrast to the 'Binary' instance, __the fields are stored in host byte order__.
523+instance Storable UUID where
524+ sizeOf _ = 16
525+ alignment _ = 4
526+
527+ peekByteOff p off =
528+ pack <$>
529+ (UnpackedUUID
530+ <$> peekByteOff p off -- Word32
531+ <*> peekByteOff p (off+4) -- Word16
532+ <*> peekByteOff p (off+6) -- Word16
533+ <*> peekByteOff p (off+8) -- Word8
534+ <*> peekByteOff p (off+9) -- Word8
535+ <*> peekByteOff p (off+10) -- Word8
536+ <*> peekByteOff p (off+11) -- Word8
537+ <*> peekByteOff p (off+12) -- Word8
538+ <*> peekByteOff p (off+13) -- Word8
539+ <*> peekByteOff p (off+14) -- Word8
540+ <*> peekByteOff p (off+15) -- Word8
541+ )
542+
543+ pokeByteOff p off u =
544+ case unpack u of
545+ (UnpackedUUID x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 x10) ->
546+ do
547+ pokeByteOff p off x0
548+ pokeByteOff p (off+4) x1
549+ pokeByteOff p (off+6) x2
550+ pokeByteOff p (off+8) x3
551+ pokeByteOff p (off+9) x4
552+ pokeByteOff p (off+10) x5
553+ pokeByteOff p (off+11) x6
554+ pokeByteOff p (off+12) x7
555+ pokeByteOff p (off+13) x8
556+ pokeByteOff p (off+14) x9
557+ pokeByteOff p (off+15) x10
558+
559+-- | This 'Binary' instance is compatible with <http://tools.ietf.org/html/rfc4122 RFC 4122>, storing the fields in network order as 16 bytes.
560+instance Binary UUID where
561+ put (UUID w0 w1) = putWord64be w0 >> putWord64be w1
562+ get = liftM2 UUID getWord64be getWord64be
563+
564+
565+-- My goal with this instance was to make it work just enough to do what
566+-- I want when used with the HStringTemplate library.
567+instance Data UUID where
568+ toConstr uu = mkConstr uuidType (show uu) [] (error "fixity")
569+ gunfold _ _ = error "gunfold"
570+ dataTypeOf _ = uuidType
571+
572+uuidType :: DataType
573+uuidType = mkNoRepType "Data.UUID.Types.UUID"
574+
575+deriving instance Lift UUID
Data/UUID/Builder.hs → uuid-types/src/Data/UUID/Types/Internal/Builder.hsrenamed+21−5
@@ -1,6 +1,9 @@
11{-# LANGUAGE TypeFamilies #-}
2+{-# LANGUAGE Unsafe #-}
23
3−-- Module : Data.UUID.Builder
4+{-# OPTIONS_HADDOCK hide #-}
5+
6+-- | Module : Data.UUID.Types.Internal.Builder
47-- Copyright : (c) 2009 Mark Lentczner
58--
69-- License : BSD-style
@@ -10,14 +13,14 @@
1013-- Portability : portable
1114--
1215-- This module provides a system that can call a function that takes
13−-- a sequence of some number of Word8 arguments.
14−--
16+-- a sequence of some number of Word8 arguments.
17+--
1518-- The twist is that the Word8 arguments can be supplied directly
1619-- from Word8s, or from other sources that may provide more than
1720-- one Word8 apiece. Examples are Word16 and Word32 that supply
1821-- two and four Word8s respectively. Other ByteSource instances
1922-- can be defined.
20−--
23+--
2124-- This module is admittedly overkill. There are only three places
2225-- in the uuid package that need to call buildFromBytes with 16
2326-- Word8 values, but each place uses Words of different lengths:
@@ -28,7 +31,7 @@
2831-- code was ungainly. Using this module makes the code very
2932-- concise, and turns out to optimize to just as fast, or faster!
3033
31−module Data.UUID.Builder
34+module Data.UUID.Types.Internal.Builder
3235 (ByteSource(..)
3336 ,ByteSink
3437 ,Takes1Byte
@@ -46,6 +49,7 @@ type Takes1Byte g = Word8 -> g
4649type Takes2Bytes g = Word8 -> Word8 -> g
4750type Takes3Bytes g = Word8 -> Word8 -> Word8 -> g
4851type Takes4Bytes g = Word8 -> Word8 -> Word8 -> Word8 -> g
52+type Takes8Bytes g = Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> Word8 -> g
4953
5054-- | Type of function that a given ByteSource needs.
5155-- This function must take as many Word8 arguments as the ByteSource provides
@@ -53,6 +57,7 @@ type family ByteSink w g
5357type instance ByteSink Word8 g = Takes1Byte g
5458type instance ByteSink Word16 g = Takes2Bytes g
5559type instance ByteSink Word32 g = Takes4Bytes g
60+type instance ByteSink Word64 g = Takes8Bytes g
5661type instance ByteSink Int g = Takes4Bytes g
5762
5863
@@ -80,6 +85,17 @@ instance ByteSource Word32 where
8085 b3 = fromIntegral (w `shiftR` 8)
8186 b4 = fromIntegral w
8287
88+instance ByteSource Word64 where
89+ f /-/ w = f b1 b2 b3 b4 b5 b6 b7 b8
90+ where b1 = fromIntegral (w `shiftR` 56)
91+ b2 = fromIntegral (w `shiftR` 48)
92+ b3 = fromIntegral (w `shiftR` 40)
93+ b4 = fromIntegral (w `shiftR` 32)
94+ b5 = fromIntegral (w `shiftR` 24)
95+ b6 = fromIntegral (w `shiftR` 16)
96+ b7 = fromIntegral (w `shiftR` 8)
97+ b8 = fromIntegral w
98+
8399instance ByteSource Int where
84100 f /-/ w = f b1 b2 b3 b4
85101 where b1 = fromIntegral (w `shiftR` 24)
uuid-types/tests/TestUUID.hsnew file+209
@@ -0,0 +1,209 @@
1+{-# LANGUAGE ViewPatterns #-}
2+{-# LANGUAGE TemplateHaskell #-}
3+
4+{-# OPTIONS_GHC -fno-warn-orphans #-}
5+
6+import Data.Binary (encode)
7+import qualified Data.ByteString.Char8 as BC8
8+import qualified Data.ByteString.Lazy as BL
9+import qualified Data.ByteString.Lazy.Char8 as BL8
10+import Data.Char (ord)
11+import Data.Functor ((<$>))
12+import qualified Data.UUID.Types as U
13+import Data.Word
14+import Foreign (alloca, castPtr, peek, poke,
15+ sizeOf)
16+import GHC.ByteOrder (ByteOrder(..), targetByteOrder)
17+import System.IO.Unsafe (unsafePerformIO)
18+
19+import Test.QuickCheck (Arbitrary (arbitrary), choose)
20+
21+import Test.Tasty (TestTree, defaultMain, testGroup)
22+import Test.Tasty.HUnit (assertBool, testCase, (@=?), (@?=))
23+import Test.Tasty.QuickCheck (testProperty)
24+
25+
26+-- orphan
27+instance Arbitrary U.UUID where
28+ -- the UUID random instance ignores bounds
29+ arbitrary = choose (U.nil, U.nil)
30+
31+type Test = TestTree
32+
33+test_null :: Test
34+test_null =
35+ testCase "nil is null" $
36+ assertBool "" (U.null U.nil)
37+
38+test_nil :: Test
39+test_nil = testGroup "nil" [
40+ testCase "nil string" $ U.toString U.nil @?= "00000000-0000-0000-0000-000000000000",
41+ testCase "nil bytes" $ U.toByteString U.nil @?= BL.pack (replicate 16 0)
42+ ]
43+
44+test_lift :: Test
45+test_lift = testCase "TH.Lift" $ do
46+ let uuid = U.fromWords64 123456789 987654321
47+ uuid @?= $( [| uuid |] )
48+
49+test_conv :: Test
50+test_conv = testGroup "conversions" [
51+ testCase "conv bytes to string" $
52+ maybe "" (U.toString) (U.fromByteString b16) @?= s16,
53+ testCase "conv string to bytes" $
54+ maybe BL.empty (U.toByteString) (U.fromString s16) @?= b16
55+ ]
56+ where b16 = BL.pack [1..16]
57+ s16 = "01020304-0506-0708-090a-0b0c0d0e0f10"
58+
59+-- | Test fromByteString with a fixed-input.
60+test_fromByteString :: Test
61+test_fromByteString =
62+ testCase "UUID fromByteString" $
63+ Just inputUUID @=?
64+ U.fromByteString (BL8.pack "\xa5\xca\x85\x66\xd9\xc5\x48\x35\x99\xc8\xe1\xf1\x3e\x73\xb5\xe2")
65+
66+-- | Test fromWords with a fixed-input
67+test_fromWords :: Test
68+test_fromWords =
69+ testCase "UUID fromWords" $
70+ inputUUID @=? U.fromWords 0xa5ca8566 0xd9c54835 0x99c8e1f1 0x3e73b5e2
71+
72+
73+test_Storeable :: Test
74+test_Storeable =
75+ testCase "UUID Storeable(poke)" $
76+ case targetByteOrder of
77+ LittleEndian -> BC8.pack "\x66\x85\xca\xa5\xc5\xd9\x35\x48\x99\xc8\xe1\xf1\x3e\x73\xb5\xe2" @=? pokeAsBS inputUUID
78+ BigEndian -> BC8.pack "\xa5\xca\x85\x66\xd9\xc5\x48\x35\x99\xc8\xe1\xf1\x3e\x73\xb5\xe2" @=? pokeAsBS inputUUID
79+ where
80+ pokeAsBS :: U.UUID -> BC8.ByteString
81+ pokeAsBS uuid = unsafePerformIO $ do
82+ alloca $ \ptr -> do
83+ poke ptr uuid
84+ BC8.packCStringLen (castPtr ptr, sizeOf uuid)
85+
86+test_Binary :: Test
87+test_Binary =
88+ testCase "UUID Binary(encode)" $
89+ (BL8.pack "\xa5\xca\x85\x66\xd9\xc5\x48\x35\x99\xc8\xe1\xf1\x3e\x73\xb5\xe2") @=? encode inputUUID
90+
91+inputUUID :: U.UUID
92+inputUUID = read "a5ca8566-d9c5-4835-99c8-e1f13e73b5e2"
93+
94+prop_stringRoundTrip :: Test
95+prop_stringRoundTrip = testProperty "String round trip" stringRoundTrip
96+ where stringRoundTrip :: U.UUID -> Bool
97+ stringRoundTrip u = maybe False (== u) $ U.fromString (U.toString u)
98+
99+prop_byteStringRoundTrip :: Test
100+prop_byteStringRoundTrip = testProperty "ByteString round trip" byteStringRoundTrip
101+ where byteStringRoundTrip :: U.UUID -> Bool
102+ byteStringRoundTrip u = maybe False (== u)
103+ $ U.fromByteString (U.toByteString u)
104+
105+prop_stringLength :: Test
106+prop_stringLength = testProperty "String length" stringLength
107+ where stringLength :: U.UUID -> Bool
108+ stringLength u = length (U.toString u) == 36
109+
110+prop_byteStringLength :: Test
111+prop_byteStringLength = testProperty "ByteString length" byteStringLength
112+ where byteStringLength :: U.UUID -> Bool
113+ byteStringLength u = BL.length (U.toByteString u) == 16
114+
115+prop_randomsDiffer :: Test
116+prop_randomsDiffer = testProperty "Randoms differ" randomsDiffer
117+ where randomsDiffer :: (U.UUID, U.UUID) -> Bool
118+ randomsDiffer (u1, u2) = u1 /= u2
119+
120+prop_randomNotNull :: Test
121+prop_randomNotNull = testProperty "Random not null" randomNotNull
122+ where randomNotNull :: U.UUID -> Bool
123+ randomNotNull = not. U.null
124+
125+prop_readShowRoundTrip :: Test
126+prop_readShowRoundTrip = testProperty "Read/Show round-trip" prop
127+ where -- we're using 'Maybe UUID' to add a bit of
128+ -- real-world complexity.
129+ prop :: U.UUID -> Bool
130+ prop uuid = read (show (Just uuid)) == Just uuid
131+
132+-- Mostly going to test for wrong UUIDs
133+fromASCIIBytes_fromString1 :: String -> Bool
134+fromASCIIBytes_fromString1 s =
135+ if all (\c -> ord c < 256) s
136+ then U.fromString s == U.fromASCIIBytes (BC8.pack s)
137+ else True
138+
139+fromASCIIBytes_fromString2 :: U.UUID -> Bool
140+fromASCIIBytes_fromString2 (U.toString -> s) =
141+ U.fromString s == U.fromASCIIBytes (BC8.pack s)
142+
143+toASCIIBytes_toString :: U.UUID -> Bool
144+toASCIIBytes_toString uuid =
145+ U.toString uuid == BC8.unpack (U.toASCIIBytes uuid)
146+
147+fromASCIIBytes_toASCIIBytes :: U.UUID -> Bool
148+fromASCIIBytes_toASCIIBytes (BC8.pack . U.toString -> bs) =
149+ Just bs == (U.toASCIIBytes <$> U.fromASCIIBytes bs)
150+
151+toASCIIBytes_fromASCIIBytes :: U.UUID -> Bool
152+toASCIIBytes_fromASCIIBytes uuid =
153+ Just uuid == U.fromASCIIBytes (U.toASCIIBytes uuid)
154+
155+toWords_fromWords :: U.UUID -> Bool
156+toWords_fromWords uuid =
157+ uuid == myUncurry4 U.fromWords (U.toWords uuid)
158+
159+fromWords_toWords :: (Word32, Word32, Word32, Word32) -> Bool
160+fromWords_toWords wds =
161+ wds == U.toWords (myUncurry4 U.fromWords wds)
162+
163+myUncurry4 :: (x1 -> x2 -> x3 -> x4 -> y) -> (x1, x2, x3, x4) -> y
164+myUncurry4 f (a,b,c,d) = f a b c d
165+
166+prop_storableRoundTrip :: Test
167+prop_storableRoundTrip =
168+ testProperty "Storeable round-trip" $ unsafePerformIO . prop
169+ where
170+ prop :: U.UUID -> IO Bool
171+ prop uuid =
172+ alloca $ \ptr -> do
173+ poke ptr uuid
174+ uuid2 <- peek ptr
175+ return $ uuid == uuid2
176+
177+main :: IO ()
178+main = do
179+ defaultMain $
180+ testGroup "tests" $
181+ concat $
182+ [ [
183+ test_null,
184+ test_nil,
185+ test_lift,
186+ test_conv,
187+ test_fromByteString,
188+ test_fromWords,
189+ test_Storeable,
190+ test_Binary
191+ ]
192+ , [ prop_stringRoundTrip,
193+ prop_readShowRoundTrip,
194+ prop_byteStringRoundTrip,
195+ prop_storableRoundTrip,
196+ prop_stringLength,
197+ prop_byteStringLength,
198+ prop_randomsDiffer,
199+ prop_randomNotNull
200+ ]
201+ , [ testProperty "fromASCIIBytes_fromString1" fromASCIIBytes_fromString1
202+ , testProperty "fromASCIIBytes_fromString2" fromASCIIBytes_fromString2
203+ , testProperty "fromASCIIBytes_toString" toASCIIBytes_toString
204+ , testProperty "fromASCIIBytes_toASCIIBytes" fromASCIIBytes_toASCIIBytes
205+ , testProperty "toASCIIBytes_fromASCIIBytes" toASCIIBytes_fromASCIIBytes
206+ , testProperty "toWords_fromWords" toWords_fromWords
207+ , testProperty "fromWords_toWords" fromWords_toWords
208+ ]
209+ ]
uuid-types/uuid-types.cabalnew file+99
@@ -0,0 +1,99 @@
1+cabal-version: 1.12
2+name: uuid-types
3+version: 1.0.6
4+x-revision: 2
5+copyright:
6+ (c) 2017-2018 Herbert Valerio Riedel
7+ (c) 2008-2014 Antoine Latter
8+
9+author: Antoine Latter
10+maintainer: Oleg Grenrus <oleg.grenrus@iki.fi>
11+license: BSD3
12+license-file: LICENSE
13+category: Data
14+build-type: Simple
15+tested-with:
16+ GHC ==8.6.5
17+ || ==8.8.4
18+ || ==8.10.7
19+ || ==9.0.2
20+ || ==9.2.8
21+ || ==9.4.8
22+ || ==9.6.6
23+ || ==9.8.4
24+ || ==9.10.1
25+ || ==9.12.1
26+
27+synopsis: Type definitions for Universally Unique Identifiers
28+description:
29+ This library contains type definitions for
30+ <https://en.wikipedia.org/wiki/UUID Universally Unique Identifiers (UUID)>
31+ (as specified in
32+ <http://tools.ietf.org/html/rfc4122 RFC 4122>)
33+ and basic conversion functions.
34+ .
35+ See also the <https://hackage.haskell.org/package/uuid 'uuid' package>
36+ providing a high-level API for managing the different UUID versions.
37+
38+homepage: https://github.com/haskell-hvr/uuid
39+bug-reports: https://github.com/haskell-hvr/uuid/issues
40+extra-source-files: ChangeLog.md
41+
42+source-repository head
43+ type: git
44+ location: https://github.com/haskell-hvr/uuid.git
45+ subdir: uuid-types
46+
47+library
48+ build-depends:
49+ base >=4.12.0.0 && <5
50+ , binary >=0.8.6.0 && <0.9
51+ , bytestring >=0.10.8.2 && <0.13
52+ , deepseq >=1.4.4.0 && <1.6
53+ , hashable >=1.4.4.0 && <1.6
54+ , random >=1.2.1.2 && <1.3
55+ , template-haskell >=2.14.0.0 && <2.24
56+ , text >=1.2.3.0 && <1.3 || >=2.0 && <2.2
57+
58+ exposed-modules: Data.UUID.Types
59+
60+ -- Exposed for companion projects; *NOT* part of the official API:
61+ exposed-modules:
62+ Data.UUID.Types.Internal
63+ Data.UUID.Types.Internal.Builder
64+
65+ default-language: Haskell2010
66+ other-extensions:
67+ DeriveDataTypeable
68+ TypeFamilies
69+
70+ ghc-options: -Wall
71+ hs-source-dirs: src
72+
73+test-suite testuuid
74+ type: exitcode-stdio-1.0
75+ main-is: TestUUID.hs
76+ hs-source-dirs: tests
77+ default-language: Haskell2010
78+ other-extensions: ViewPatterns
79+ ghc-options: -Wall
80+ if impl(ghc >= 8.10)
81+ ghc-options: -Wunused-packages
82+
83+ -- inherited constraints
84+ build-depends:
85+ base
86+ , binary
87+ , bytestring
88+ , template-haskell
89+ , uuid-types
90+
91+ -- deps w/o inherited constraints
92+ build-depends:
93+ QuickCheck >=2.14.2 && <2.16
94+ , tasty >=1.4.0.1 && <1.6
95+ , tasty-hunit >=0.10 && <0.11
96+ , tasty-quickcheck >=0.10 && <0.12
97+
98+ if !impl(ghc >=8.4)
99+ build-depends: ghc-byteorder >=4.11 && <4.12
uuid.cabaldeleted−100
@@ -1,100 +0,0 @@
1−Name: uuid
2−Version: 1.3.0
3−Copyright: (c) 2008-2013 Antoine Latter
4−Author: Antoine Latter
5−Maintainer: aslatter@gmail.com
6−License: BSD3
7−License-file: LICENSE
8−
9−Category: Data
10−Build-Type: Simple
11−Cabal-Version: >= 1.8
12−
13−Description:
14− This library is useful for creating, comparing, parsing and
15− printing Universally Unique Identifiers.
16− See <http://en.wikipedia.org/wiki/UUID> for the general idea.
17−
18−Synopsis: For creating, comparing, parsing and printing Universally Unique Identifiers
19−
20−Homepage: http://projects.haskell.org/uuid/
21−Bug-Reports: mailto:aslatter@gmail.com
22−
23−Extra-Source-Files:
24− CHANGES
25− CONTRIBUTORS
26−
27−
28−Library
29− Build-Depends: base >=3 && < 5,
30− binary >= 0.4 && < 0.8,
31− bytestring >= 0.9 && < 1.1,
32− cryptohash >= 0.7 && < 0.11,
33− hashable == 1.2.*,
34− network-info == 0.2.*,
35− random >= 1.0.1 && < 1.1,
36− time >= 1.1 && < 1.5,
37− deepseq >= 1.3 && < 1.4
38−
39− Exposed-Modules:
40− Data.UUID
41− Data.UUID.V1
42− Data.UUID.V3
43− Data.UUID.V4
44− Data.UUID.V5
45−
46− Other-Modules:
47− Data.UUID.Builder
48− Data.UUID.Internal
49− Data.UUID.Named
50−
51− Extensions: DeriveDataTypeable
52− Ghc-Options: -Wall
53−
54−source-repository head
55− type: darcs
56− location: http://code.haskell.org/uuid
57−
58−-- not strictly a test, but we want it to error if
59−-- it can't build
60−Test-Suite benchmark
61− Type: exitcode-stdio-1.0
62− Main-is: BenchUUID.hs
63− Hs-source-dirs: tests
64− Extensions: DeriveDataTypeable, CPP
65− Build-depends: base == 4.*,
66− uuid,
67− bytestring >= 0.9 && < 1.1,
68− containers >= 0.4 && < 0.6,
69− criterion >= 0.4 && < 0.9,
70− deepseq >= 1.1 && < 1.4,
71− mersenne-random-pure64,
72− random >= 1.0.1 && < 1.1
73−
74−Test-Suite testuuid
75− Type: exitcode-stdio-1.0
76− Main-is: TestUUID.hs
77− Hs-source-dirs: tests
78− Extensions: DeriveDataTypeable
79− Ghc-Options: -Wall -fno-warn-orphans
80− Build-Depends: base >= 3 && < 5,
81− uuid,
82− bytestring >= 0.9 && < 1.1,
83− HUnit >=1.2 && < 1.3,
84− QuickCheck >=2.4 && < 2.7,
85− random >= 1.0.1 && < 1.1,
86− test-framework == 0.8.*,
87− test-framework-hunit == 0.3.*,
88− test-framework-quickcheck2 == 0.3.*
89−
90−benchmark benchbytestring
91− type: exitcode-stdio-1.0
92− hs-source-dirs: tests
93− main-is: BenchByteString.hs
94− build-depends: base >= 4 && < 5
95− , uuid >= 1.2
96− , bytestring >= 0.10
97− , criterion >= 0.8
98− , random >= 1.0
99− , QuickCheck >= 2
100− ghc-options: -threaded -rtsopts -Wall -O2
uuid/CHANGES.mdnew file+186
@@ -0,0 +1,186 @@
1+1.3.16
2+
3+- Support GHC-8.6.5...9.10.1
4+
5+1.3.15
6+
7+- Add (Template Haskell) `Lift UUID` instance
8+
9+1.3.14
10+
11+- Use more compact heap object representation which saves 16 bytes on 64bit platforms.
12+- Add `toWords64`/`fromWords64` functions
13+
14+1.3.13
15+
16+- Optimize `V4.nextRandom` (~3x speed increase)
17+- Optimize UUID V3 & V5 generation (~2x speed increase)
18+- Use `cryptohash-md5`/`cryptohash-sha1`/`entropy` instead
19+ of `memory`/`cryptonite` for better performance and stability,
20+ but GHCJS is now no longer supported.
21+- Update cabal-spec to version 1.10
22+
23+1.3.12
24+
25+- Update package dependencies
26+- Use `cryptonite` for crypto
27+- The function `V4.nextRandom` is now implemented with functions from
28+ `Crypto.Random` in package `cryptonite`. This does slow-down random
29+ UUID generation but provides for far greater randomness. The
30+ `Random` instance for `UUID` can be used to re-gain the old
31+ behavior.
32+
33+1.3.11
34+
35+- Add `toText`/`fromText`
36+
37+1.3.10
38+
39+- Update dependencies in tests and benchmarks.
40+
41+1.3.9
42+
43+- Split definition of UUID data type into separate Cabal package to
44+ enable 3rd party libraries to avoid some of the larger dependencies.
45+
46+1.3.8
47+
48+- Allow building against newer `deepseq`.
49+
50+1.3.7
51+
52+- Allow building against newer `time`.
53+
54+1.3.6
55+
56+- Move to GitHub.
57+
58+1.3.5
59+
60+- Allow building against newer `random`.
61+
62+1.3.4
63+
64+- Allow building against newer `QuickCheck`.
65+
66+1.3.3
67+
68+- More complex version constraints on `hashable`, to avoid building against
69+ versions less than 1.1.1.0 or equal to 1.2.0.*.
70+
71+1.3.2
72+
73+- Fix for building against `bytestring` 0.9.*
74+
75+1.3.1
76+
77+- Allow building against `hashable` 1.1.* in addition to 1.2.*
78+
79+1.3.0
80+
81+- New functions for parsing and printing UUIDs to and from ASCII BytesStrings
82+- New module `Data.UUID.Util`. This module includes the type `UnpackedUUID`,
83+ whose fields correspond to the UUID fields described in RFC 4122.
84+- The `Storable` instance now stores a UUID in host byte-order instead of
85+ big endian.
86+- There is now an instance for `Hashable UUID`.
87+
88+1.2.13
89+
90+Benchmark only changes:
91+
92+- Allow `criterion` 0.8.*
93+
94+1.2.12
95+
96+Test only changes:
97+
98+- Allow `QuickCheck` 2.6.*
99+
100+1.2.11
101+
102+- Allow `binary` 0.7.*
103+
104+1.2.10
105+
106+- Allow `cryptohash` version 0.9.*
107+- Cleanup tests
108+
109+1.2.9
110+
111+- Bumped dependency on `cryptohash`.
112+
113+1.2.8
114+
115+- Bumped various dependencies and cleaned up dependencies in general.
116+
117+1.2.7
118+
119+- Added stricter constraints on `random` package.
120+
121+1.2.6
122+
123+- Add module `V4` to direct attention to our Random instance
124+
125+- In module `V1` seed the generator with a random number
126+ if the hardware MAC address could not be discovered.
127+
128+- Fix and cleanup various haddocks.
129+
130+- In module docs, warn about MD5 use in Data.UUID.V3 and
131+ encourage the reader to use Data.UUID.V5 instead.
132+
133+1.2.5
134+
135+- Use `cryptohash` package for MD5 and SHA1 instead of `Crypto`
136+
137+1.2.4
138+
139+- Unpack Word32 values into UUID constructor.
140+
141+- Update test suite to QuickCheck 2
142+
143+- Bump other dependencies in tests/benchmarks
144+
145+1.2.3
146+
147+- The Read instance now drops leading spaces in the string to be parsed.
148+Thanks to Marc Ziegert for reporting this bug.
149+
150+- The tests have moved over to the new Cabal test running framework.
151+
152+1.2.2
153+
154+- Add functions fromWords/toWords
155+
156+The goal was to have a total function that can be used to construct
157+a UUID, primarily for use by uuid-th.
158+
159+1.2.1
160+
161+- Fix concurrency bug in Data.UUID.V1 (thanks to Neil Mitchell for reporting
162+and a test case)
163+
164+1.2.0
165+ (Contributors: Antoine Latter & Mark Lentczner)
166+
167+- added functions toByteString and fromByteString
168+- added `nil` UUID
169+- added unit tests and benchmarks, built when configured -ftest
170+- major speed up of to/from functions (as well as in general)
171+- added version-3 generation (deterministic based on MD5)
172+- major changes to internal representation
173+ - now uses four strict Word32 values
174+ - internal ByteSource classes for easy construction (see Builder.hs)
175+- Storable instance now stores in memory as system libraries in C do:
176+ 16 bytes derived from the network order of the fields, no matter what
177+ the host native endianess is.
178+- fixed bugs in V1 time and clock stepping, and V1 generated values
179+- builds cleanly under GHC's -Wall
180+- added CHANGES file
181+
182+1.1.1
183+
184+- no longer exporting `null` from the prelude
185+- add `null` predicate on UUIDs
186+- documentation fix (thanks Mark Lentczner)
Demo.hs → uuid/Demo.hsrenamed−1
@@ -13,4 +13,3 @@ main = sequence_ $ replicate count $ do
1313 x <- newUUID
1414 y <- newUUID
1515 seq (x == y) $ return ()
16−
uuid/LICENSEnew file+28
@@ -0,0 +1,28 @@
1+Copyright (c) 2008, Antoine Latter
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are
7+met:
8+
9+ * Redistributions of source code must retain the above copyright
10+notice, this list of conditions and the following disclaimer.
11+ * Redistributions in binary form must reproduce the above copyright
12+notice, this list of conditions and the following disclaimer in the
13+documentation and/or other materials provided with the distribution.
14+ * The names of the authors may not be used to endorse or promote
15+products derived from this software without specific prior written
16+permission.
17+
18+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
24+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
uuid/Setup.hsnew file+4
@@ -0,0 +1,4 @@
1+import Distribution.Simple
2+main :: IO ()
3+main = defaultMain
4+
Data/UUID.hs → uuid/src/Data/UUID.hsrenamed+16−14
@@ -14,34 +14,36 @@ printing Universally Unique Identifiers.
1414See <http://en.wikipedia.org/wiki/UUID> for the general idea.
1515See <http://tools.ietf.org/html/rfc4122> for the specification.
1616
17−* Random UUIDs may be generated using 'Data.UUID.V4.nextRandom' or
18−your favorite instance of 'System.Random.Random'.
19−
20−* We have an implementation of generating a UUID from the hardware
21−MAC address and current system time in "Data.UUID.V1".
17+* Use 'Data.UUID.V4.nextRandom' to generate secure random UUIDs, and your
18+favorite instance of 'System.Random.Random' for faster but insecure
19+generation of UUIDs.
2220
2321* For name-based generation of UUIDs using SHA-1 hashing see
2422"Data.UUID.V5".
2523-}
26−
2724module Data.UUID(UUID
2825 ,toString
2926 ,fromString
27+ ,toText
28+ ,fromText
29+ ,toASCIIBytes
30+ ,fromASCIIBytes
31+ ,toLazyASCIIBytes
32+ ,fromLazyASCIIBytes
3033 ,toByteString
3134 ,fromByteString
3235 ,toWords
3336 ,fromWords
37+ ,toWords64
38+ ,fromWords64
3439 ,null
3540 ,nil
36− ,fromASCIIBytes
37− ,toASCIIBytes
38− ,fromLazyASCIIBytes
39− ,toLazyASCIIBytes
4041 ) where
4142
4243import Prelude () -- we need to hide Prelude.null
43−import Data.UUID.Internal
44+import Data.UUID.Types
4445
45−-- Everything is really implemented in Data.UUID.Internal,
46−-- but I don't want to export the constructors out of the
47−-- package.
46+-- We use explicit re-exports of everything from Data.UUID.Types in
47+-- preference to just re-exporting the whole module. This is to avoid
48+-- unforeseen transitive API breakage if the Data.UUID.Types module
49+-- should change.
Data/UUID/Named.hs → uuid/src/Data/UUID/Named.hsrenamed+1−1
@@ -26,7 +26,7 @@ module Data.UUID.Named
2626 ,namespaceX500
2727 ) where
2828
29−import Data.UUID.Internal
29+import Data.UUID.Types.Internal
3030
3131import Control.Applicative ((<*>),(<$>))
3232import Data.Binary.Get (runGet, getWord32be)
uuid/src/Data/UUID/Util.hsnew file+42
@@ -0,0 +1,42 @@
1+{-# LANGUAGE TypeFamilies #-}
2+module Data.UUID.Util (
3+ UnpackedUUID(..)
4+ , unpack, pack
5+ , version
6+ , extractTime
7+ , setTime
8+ ) where
9+
10+import Prelude hiding (null)
11+import Data.Word
12+import Data.Word.Util
13+import Data.Bits
14+import Data.UUID.Types.Internal
15+import Data.Int (Int64)
16+
17+version :: UUID -> Int
18+version uuid =
19+ fromEnum ((time_hi_and_version unpacked `shiftR` 12) .&. 0xF)
20+ where
21+ unpacked = unpack uuid
22+
23+-- Note UUID time is in 10^-7 seconds.
24+setTime :: (Integral a, Bits a) => UUID -> a -> Maybe UUID
25+setTime uuid t =
26+ if version uuid == 1
27+ then Just $ pack $ (unpack uuid){time_low = new_low_bits, time_mid = new_mid_bits, time_hi_and_version = new_hi_and_version_bits}
28+ else Nothing
29+ where new_low_bits = fromIntegral $ t .&. 0xFFFFFFFF
30+ new_mid_bits = fromIntegral $ (t `shiftR` 32) .&. 0xFFFF
31+ new_hi_and_version_bits = fromIntegral $ 0x1000 .|. ((t `shiftR` 48) .&. 0x0FFF)
32+
33+extractTime :: UUID -> Maybe Int64
34+extractTime uuid =
35+ if version uuid == 1
36+ then Just $ fromIntegral $ w32to64 (w16to32 (timeAndVersionToTime $ time_hi_and_version unpacked) $ time_mid unpacked) (time_low unpacked)
37+ else Nothing
38+ where
39+ unpacked = unpack uuid
40+
41+timeAndVersionToTime :: Word16 -> Word16
42+timeAndVersionToTime tv = tv .&. 0x0FFF
Data/UUID/V3.hs → uuid/src/Data/UUID/V3.hsrenamed+2−3
@@ -8,7 +8,7 @@ Maintainer : aslatter@gmail.com
88Stability : experimental
99Portability : portable
1010
11−NOTE: This module uses MD5 hashing. Unless you know
11+NOTE\: This module uses MD5 hashing. Unless you know
1212you need to use this module, you should probably be
1313using "Data.UUID.V5", which offers the same sort of
1414functionality as this module except implemented with
@@ -33,7 +33,7 @@ module Data.UUID.V3
3333
3434import Data.Word
3535
36−import Data.UUID.Internal
36+import Data.UUID.Types.Internal
3737import qualified Data.UUID.Named as Shared
3838import qualified Crypto.Hash.MD5 as MD5
3939
@@ -47,4 +47,3 @@ generateNamed :: UUID -- ^Namespace
4747 -> [Word8] -- ^Object
4848 -> UUID
4949generateNamed = Shared.generateNamed MD5.hash 3
50−
uuid/src/Data/UUID/V4.hsnew file+33
@@ -0,0 +1,33 @@
1+{- |
2+ Module : Data.UUID.V4
3+ Copyright : (c) 2012-2016 Antoine Latter
4+
5+ License : BSD-style
6+
7+ Maintainer : aslatter@gmail.com
8+ Stability : experimental
9+ Portability : portable
10+
11+ This module implements Version 4 UUIDs as specified
12+ in RFC 4122.
13+
14+ These UUIDs are generated from a pseudo-random generator.
15+ We use the 'getEntropy' method from the <https://hackage.haskell.org/package/entropy entropy> package,
16+ which should provide cryptographically secure random data.
17+-}
18+module Data.UUID.V4 (nextRandom) where
19+
20+import Data.UUID
21+import Data.UUID.Types.Internal ( buildFromBytes )
22+
23+import System.Entropy ( getEntropy )
24+import Data.ByteString ( unpack )
25+
26+-- | Generate a crytographically secure, random UUID.
27+--
28+-- @since 1.2.6
29+nextRandom :: IO UUID
30+nextRandom = do
31+ [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf]
32+ <- unpack `fmap` getEntropy 16
33+ return $ buildFromBytes 4 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf
Data/UUID/V5.hs → uuid/src/Data/UUID/V5.hsrenamed+1−2
@@ -28,7 +28,7 @@ module Data.UUID.V5
2828
2929import Data.Word
3030
31−import Data.UUID.Internal
31+import Data.UUID.Types.Internal
3232import qualified Data.UUID.Named as Shared
3333import qualified Crypto.Hash.SHA1 as SHA1
3434
@@ -42,4 +42,3 @@ generateNamed :: UUID -- ^Namespace
4242 -> [Word8] -- ^Object
4343 -> UUID
4444generateNamed = Shared.generateNamed SHA1.hash 5
45−
uuid/src/Data/Word/Util.hsnew file+20
@@ -0,0 +1,20 @@
1+
2+-- | Internal utilites
3+module Data.Word.Util where
4+
5+import Data.Bits
6+import Data.Word
7+
8+w16to32 :: Word16 -> Word16 -> Word32
9+w16to32 w0s w1s =
10+ (w0 `shiftL` 16) .|. w1
11+ where
12+ w0 = fromIntegral w0s
13+ w1 = fromIntegral w1s
14+
15+w32to64 :: Word32 -> Word32 -> Word64
16+w32to64 w0s w1s =
17+ (w0 `shiftL` 32) .|. w1
18+ where
19+ w0 = fromIntegral w0s
20+ w1 = fromIntegral w1s
uuid/tests/TestUUID.hsnew file+96
@@ -0,0 +1,96 @@
1+{-# LANGUAGE ViewPatterns #-}
2+
3+import Data.Bits
4+import qualified Data.ByteString.Lazy as BL
5+import Data.Char (ord)
6+import Data.Maybe
7+import Data.Word
8+import qualified Data.UUID as U
9+import qualified Data.UUID.V3 as U3
10+import qualified Data.UUID.V5 as U5
11+
12+import Test.QuickCheck ( Arbitrary(arbitrary) )
13+import Test.Tasty ( TestTree, testGroup, defaultMain )
14+import Test.Tasty.HUnit
15+ ( assertBool, (@?=), testCase )
16+import Test.Tasty.QuickCheck ( testProperty )
17+
18+type Test = TestTree
19+
20+isValidVersion :: Int -> U.UUID -> Bool
21+isValidVersion v u = lenOK && variantOK && versionOK
22+ where bs = U.toByteString u
23+ lenOK = BL.length bs == 16
24+ variantOK = BL.index bs 8 .&. 0xc0 == 0x80
25+ versionOK = BL.index bs 6 .&. 0xf0 == fromIntegral (v `shiftL` 4)
26+
27+
28+instance Arbitrary U.UUID where
29+ -- the UUID random instance ignores bounds
30+ arbitrary = pure U.nil
31+
32+
33+test_null :: Test
34+test_null =
35+ testCase "namespaceDNS is not null" $
36+ assertBool "" (not $ U.null U3.namespaceDNS)
37+
38+test_v3 :: Test
39+test_v3 =
40+ testCase "V3 computation" $
41+ U3.generateNamed U3.namespaceDNS name @?= uV3
42+
43+ where name = map (fromIntegral . ord) "www.widgets.com" :: [Word8]
44+ uV3 = fromJust $ U.fromString "3d813cbb-47fb-32ba-91df-831e1593ac29"
45+
46+test_v5 :: Test
47+test_v5 =
48+ testCase "V5 computation" $
49+ U5.generateNamed U5.namespaceDNS name @?= uV5
50+
51+ where name = map (fromIntegral . ord) "www.widgets.com" :: [Word8]
52+ uV5 = fromJust $ U.fromString "21f7f8de-8051-5b89-8680-0195ef798b6a"
53+
54+
55+prop_randomsValid :: Test
56+prop_randomsValid = testProperty "Random valid" randomsValid
57+ where randomsValid :: U.UUID -> Bool
58+ randomsValid = isValidVersion 4
59+
60+prop_v3NotNull :: Test
61+prop_v3NotNull = testProperty "V3 not null" v3NotNull
62+ where v3NotNull :: [Word8] -> Bool
63+ v3NotNull = not . U.null . U3.generateNamed U3.namespaceDNS
64+
65+prop_v3Valid :: Test
66+prop_v3Valid = testProperty "V3 valid" v3Valid
67+ where v3Valid :: [Word8] -> Bool
68+ v3Valid = isValidVersion 3 . U3.generateNamed U3.namespaceDNS
69+
70+prop_v5NotNull :: Test
71+prop_v5NotNull = testProperty "V5 not null" v5NotNull
72+ where v5NotNull :: [Word8] -> Bool
73+ v5NotNull = not . U.null . U5.generateNamed U5.namespaceDNS
74+
75+prop_v5Valid :: Test
76+prop_v5Valid = testProperty "V5 valid" v5Valid
77+ where v5Valid :: [Word8] -> Bool
78+ v5Valid = isValidVersion 5 . U5.generateNamed U5.namespaceDNS
79+
80+main :: IO ()
81+main = do
82+ defaultMain $
83+ testGroup "tests" $
84+ concat $
85+ [ [
86+ test_null,
87+ test_v3,
88+ test_v5
89+ ]
90+ , [ prop_randomsValid,
91+ prop_v3NotNull,
92+ prop_v3Valid,
93+ prop_v5NotNull,
94+ prop_v5Valid
95+ ]
96+ ]
uuid/uuid.cabalnew file+95
@@ -0,0 +1,95 @@
1+cabal-version: 1.12
2+name: uuid
3+version: 1.3.16
4+x-revision: 2
5+copyright: (c) 2008-2014 Antoine Latter
6+author: Antoine Latter
7+maintainer: Oleg Grenrus <oleg.grenrus@iki.fi>
8+license: BSD3
9+license-file: LICENSE
10+category: Data
11+build-type: Simple
12+tested-with:
13+ GHC ==8.6.5
14+ || ==8.8.4
15+ || ==8.10.7
16+ || ==9.0.2
17+ || ==9.2.8
18+ || ==9.4.8
19+ || ==9.6.6
20+ || ==9.8.4
21+ || ==9.10.1
22+ || ==9.12.1
23+
24+synopsis:
25+ For creating, comparing, parsing and printing Universally Unique Identifiers
26+
27+description:
28+ This library is useful for creating, comparing, parsing and
29+ printing Universally Unique Identifiers.
30+ .
31+ See <http://en.wikipedia.org/wiki/UUID> for the general idea.
32+
33+homepage: https://github.com/haskell-hvr/uuid
34+bug-reports: https://github.com/haskell-hvr/uuid/issues
35+extra-source-files: CHANGES.md
36+
37+source-repository head
38+ type: git
39+ location: https://github.com/haskell-hvr/uuid.git
40+ subdir: uuid
41+
42+library
43+ build-depends:
44+ base >=4.12 && <5
45+ , binary >=0.8.6.0 && <0.9
46+ , bytestring >=0.10.8.2 && <0.13
47+ , cryptohash-md5 >=0.11.100 && <0.12
48+ , cryptohash-sha1 >=0.11.100 && <0.12
49+ , entropy >=0.3.7 && <0.5
50+
51+ -- strict dependency on uuid-types,
52+ -- as we re-rexport datatype, thus leak instances etc.
53+ build-depends: uuid-types >=1.0.6 && <1.0.7
54+ exposed-modules:
55+ Data.UUID
56+ Data.UUID.Util
57+ Data.UUID.V3
58+ Data.UUID.V4
59+ Data.UUID.V5
60+
61+ other-modules:
62+ Data.UUID.Named
63+ Data.Word.Util
64+
65+ default-language: Haskell2010
66+ default-extensions: DeriveDataTypeable
67+ other-extensions: TypeFamilies
68+ ghc-options: -Wall
69+ if impl(ghc >= 8.10)
70+ ghc-options: -Wunused-packages
71+ hs-source-dirs: src
72+
73+test-suite testuuid
74+ type: exitcode-stdio-1.0
75+ main-is: TestUUID.hs
76+ hs-source-dirs: tests
77+ default-language: Haskell2010
78+ default-extensions: DeriveDataTypeable
79+ other-extensions: ViewPatterns
80+ ghc-options: -Wall -fno-warn-orphans
81+ if impl(ghc >= 8.10)
82+ ghc-options: -Wunused-packages
83+
84+ -- inherited constraints
85+ build-depends:
86+ base
87+ , bytestring
88+ , uuid
89+
90+ -- deps w/o inherited constraints
91+ build-depends:
92+ QuickCheck >=2.14.2 && <2.16
93+ , tasty >=1.4.0.1 && <1.6
94+ , tasty-hunit >=0.10 && <0.11
95+ , tasty-quickcheck >=0.10 && <0.12