add unlink enpoint

This commit is contained in:
sentriz
2019-04-19 11:56:29 +01:00
parent 1094f2da21
commit a1db249c0b
26 changed files with 54 additions and 2095 deletions

View File

@@ -89,7 +89,8 @@ func setAdminRoutes(mux *http.ServeMux) {
mux.HandleFunc("/admin/home", withUserWare(cont.ServeHome)) mux.HandleFunc("/admin/home", withUserWare(cont.ServeHome))
mux.HandleFunc("/admin/change_own_password", withUserWare(cont.ServeChangeOwnPassword)) mux.HandleFunc("/admin/change_own_password", withUserWare(cont.ServeChangeOwnPassword))
mux.HandleFunc("/admin/change_own_password_do", withUserWare(cont.ServeChangeOwnPasswordDo)) mux.HandleFunc("/admin/change_own_password_do", withUserWare(cont.ServeChangeOwnPasswordDo))
mux.HandleFunc("/admin/link_lastfm_callback", withUserWare(cont.ServeLinkLastFMCallback)) mux.HandleFunc("/admin/link_lastfm_do", withUserWare(cont.ServeLinkLastFMDo))
mux.HandleFunc("/admin/unlink_lastfm_do", withUserWare(cont.ServeUnlinkLastFMDo))
mux.HandleFunc("/admin/change_password", withAdminWare(cont.ServeChangePassword)) mux.HandleFunc("/admin/change_password", withAdminWare(cont.ServeChangePassword))
mux.HandleFunc("/admin/change_password_do", withAdminWare(cont.ServeChangePasswordDo)) mux.HandleFunc("/admin/change_password_do", withAdminWare(cont.ServeChangePasswordDo))
mux.HandleFunc("/admin/create_user", withAdminWare(cont.ServeCreateUser)) mux.HandleFunc("/admin/create_user", withAdminWare(cont.ServeCreateUser))

3
go.mod
View File

@@ -2,7 +2,6 @@ module github.com/sentriz/gonic
require ( require (
cloud.google.com/go v0.37.1 // indirect cloud.google.com/go v0.37.1 // indirect
github.com/davecgh/go-spew v1.1.1
github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect
github.com/dhowden/tag v0.0.0-20181104225729-a9f04c2798ca github.com/dhowden/tag v0.0.0-20181104225729-a9f04c2798ca
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
@@ -17,7 +16,7 @@ require (
github.com/mattn/go-sqlite3 v1.10.0 // indirect github.com/mattn/go-sqlite3 v1.10.0 // indirect
github.com/mozillazg/go-unidecode v0.1.1 github.com/mozillazg/go-unidecode v0.1.1
github.com/myesui/uuid v1.0.0 // indirect github.com/myesui/uuid v1.0.0 // indirect
github.com/twinj/uuid v1.0.0 github.com/twinj/uuid v1.0.0 // indirect
github.com/wader/gormstore v0.0.0-20190302154359-acb787ba3755 github.com/wader/gormstore v0.0.0-20190302154359-acb787ba3755
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a // indirect golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a // indirect
google.golang.org/appengine v1.5.0 // indirect google.golang.org/appengine v1.5.0 // indirect

View File

@@ -22,14 +22,14 @@ func (c *Controller) ServeLoginDo(w http.ResponseWriter, r *http.Request) {
if username == "" || password == "" { if username == "" || password == "" {
session.AddFlash("please provide both a username and password") session.AddFlash("please provide both a username and password")
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, r.Header.Get("Referer"), 302) http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
return return
} }
user := c.GetUserFromName(username) user := c.GetUserFromName(username)
if !(username == user.Name && password == user.Password) { if !(username == user.Name && password == user.Password) {
session.AddFlash("invalid username / password") session.AddFlash("invalid username / password")
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, r.Header.Get("Referer"), 302) http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
return return
} }
// put the user name into the session. future endpoints after this one // put the user name into the session. future endpoints after this one
@@ -37,14 +37,14 @@ func (c *Controller) ServeLoginDo(w http.ResponseWriter, r *http.Request) {
// session and put the row into the request context. // session and put the row into the request context.
session.Values["user"] = user.Name session.Values["user"] = user.Name
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, "/admin/home", 303) http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
} }
func (c *Controller) ServeLogout(w http.ResponseWriter, r *http.Request) { func (c *Controller) ServeLogout(w http.ResponseWriter, r *http.Request) {
session := r.Context().Value("session").(*sessions.Session) session := r.Context().Value("session").(*sessions.Session)
delete(session.Values, "user") delete(session.Values, "user")
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, "/admin/login", 303) http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
} }
func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) { func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) {
@@ -53,9 +53,19 @@ func (c *Controller) ServeHome(w http.ResponseWriter, r *http.Request) {
c.DB.Table("albums").Count(&data.AlbumCount) c.DB.Table("albums").Count(&data.AlbumCount)
c.DB.Table("tracks").Count(&data.TrackCount) c.DB.Table("tracks").Count(&data.TrackCount)
c.DB.Find(&data.AllUsers) c.DB.Find(&data.AllUsers)
var apiKey db.Setting data.CurrentLastFMAPIKey = c.GetSetting("lastfm_api_key")
c.DB.Where("key = ?", "lastfm_api_key").First(&apiKey) scheme := utilities.FirstExisting(
data.CurrentLastFMAPIKey = apiKey.Value "http", // fallback
r.Header.Get("X-Forwarded-Proto"),
r.Header.Get("X-Forwarded-Scheme"),
r.URL.Scheme,
)
host := utilities.FirstExisting(
"localhost:7373", // fallback
r.Header.Get("X-Forwarded-Host"),
r.Host,
)
data.RequestRoot = fmt.Sprintf("%s://%s", scheme, host)
renderTemplate(w, r, "home", &data) renderTemplate(w, r, "home", &data)
} }
@@ -71,41 +81,44 @@ func (c *Controller) ServeChangeOwnPasswordDo(w http.ResponseWriter, r *http.Req
if err != nil { if err != nil {
session.AddFlash(err.Error()) session.AddFlash(err.Error())
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, r.Header.Get("Referer"), 302) http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
return return
} }
user := r.Context().Value("user").(*db.User) user := r.Context().Value("user").(*db.User)
user.Password = passwordOne user.Password = passwordOne
c.DB.Save(user) c.DB.Save(user)
http.Redirect(w, r, "/admin/home", 303) http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
} }
func (c *Controller) ServeLinkLastFMCallback(w http.ResponseWriter, r *http.Request) { func (c *Controller) ServeLinkLastFMDo(w http.ResponseWriter, r *http.Request) {
token := r.URL.Query().Get("token") token := r.URL.Query().Get("token")
if token == "" { if token == "" {
http.Error(w, "please provide a token", 400) http.Error(w, "please provide a token", 400)
return return
} }
var apiKey db.Setting
c.DB.Where("key = ?", "lastfm_api_key").First(&apiKey)
var secret db.Setting
c.DB.Where("key = ?", "lastfm_secret").First(&secret)
sessionKey, err := lastfm.GetSession( sessionKey, err := lastfm.GetSession(
apiKey.Value, c.GetSetting("lastfm_api_key"),
secret.Value, c.GetSetting("lastfm_secret"),
token, token,
) )
session := r.Context().Value("session").(*sessions.Session) session := r.Context().Value("session").(*sessions.Session)
if err != nil { if err != nil {
session.AddFlash(err.Error()) session.AddFlash(err.Error())
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, "/admin/home", 302) http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
return return
} }
user := r.Context().Value("user").(*db.User) user := r.Context().Value("user").(*db.User)
user.LastFMSession = sessionKey user.LastFMSession = sessionKey
c.DB.Save(&user) c.DB.Save(&user)
http.Redirect(w, r, "/admin/home", 302) http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
}
func (c *Controller) ServeUnlinkLastFMDo(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value("user").(*db.User)
user.LastFMSession = ""
c.DB.Save(&user)
http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
} }
func (c *Controller) ServeChangePassword(w http.ResponseWriter, r *http.Request) { func (c *Controller) ServeChangePassword(w http.ResponseWriter, r *http.Request) {
@@ -136,12 +149,12 @@ func (c *Controller) ServeChangePasswordDo(w http.ResponseWriter, r *http.Reques
if err != nil { if err != nil {
session.AddFlash(err.Error()) session.AddFlash(err.Error())
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, r.Header.Get("Referer"), 302) http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
return return
} }
user.Password = passwordOne user.Password = passwordOne
c.DB.Save(&user) c.DB.Save(&user)
http.Redirect(w, r, "/admin/home", 303) http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
} }
func (c *Controller) ServeCreateUser(w http.ResponseWriter, r *http.Request) { func (c *Controller) ServeCreateUser(w http.ResponseWriter, r *http.Request) {
@@ -155,7 +168,7 @@ func (c *Controller) ServeCreateUserDo(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
session.AddFlash(err.Error()) session.AddFlash(err.Error())
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, r.Header.Get("Referer"), 302) http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
return return
} }
passwordOne := r.FormValue("password_one") passwordOne := r.FormValue("password_one")
@@ -164,7 +177,7 @@ func (c *Controller) ServeCreateUserDo(w http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
session.AddFlash(err.Error()) session.AddFlash(err.Error())
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, r.Header.Get("Referer"), 302) http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
return return
} }
user := db.User{ user := db.User{
@@ -177,10 +190,10 @@ func (c *Controller) ServeCreateUserDo(w http.ResponseWriter, r *http.Request) {
"could not create user `%s`: %v", username, err, "could not create user `%s`: %v", username, err,
)) ))
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, r.Header.Get("Referer"), 302) http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
return return
} }
http.Redirect(w, r, "/admin/home", 303) http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
} }
func (c *Controller) ServeUpdateLastFMAPIKey(w http.ResponseWriter, r *http.Request) { func (c *Controller) ServeUpdateLastFMAPIKey(w http.ResponseWriter, r *http.Request) {
@@ -198,10 +211,10 @@ func (c *Controller) ServeUpdateLastFMAPIKeyDo(w http.ResponseWriter, r *http.Re
if err != nil { if err != nil {
session.AddFlash(err.Error()) session.AddFlash(err.Error())
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, r.Header.Get("Referer"), 302) http.Redirect(w, r, r.Header.Get("Referer"), http.StatusSeeOther)
return return
} }
c.SetSetting("lastfm_api_key", apiKey) c.SetSetting("lastfm_api_key", apiKey)
c.SetSetting("lastfm_secret", secret) c.SetSetting("lastfm_secret", secret)
http.Redirect(w, r, "/admin/home", 303) http.Redirect(w, r, "/admin/home", http.StatusSeeOther)
} }

View File

@@ -12,7 +12,6 @@ import (
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"github.com/sentriz/gonic/db" "github.com/sentriz/gonic/db"
"github.com/sentriz/gonic/handler/utilities"
"github.com/sentriz/gonic/subsonic" "github.com/sentriz/gonic/subsonic"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
@@ -182,18 +181,6 @@ func renderTemplate(w http.ResponseWriter, r *http.Request,
if ok { if ok {
data.User = user data.User = user
} }
scheme := utilities.FirstExisting(
"http", // fallback
r.Header.Get("X-Forwarded-Proto"),
r.Header.Get("X-Forwarded-Scheme"),
r.URL.Scheme,
)
host := utilities.FirstExisting(
"localhost:7373", // fallback
r.Header.Get("X-Forwarded-Host"),
r.Host,
)
data.RequestRoot = fmt.Sprintf("%s://%s", scheme, host)
err := templates[name].ExecuteTemplate(w, "layout", data) err := templates[name].ExecuteTemplate(w, "layout", data)
if err != nil { if err != nil {
http.Error(w, fmt.Sprintf("500 when executing: %v", err), 500) http.Error(w, fmt.Sprintf("500 when executing: %v", err), 500)

View File

@@ -117,7 +117,7 @@ func (c *Controller) WithUserSession(next http.HandlerFunc) http.HandlerFunc {
if !ok { if !ok {
session.AddFlash("you are not authenticated") session.AddFlash("you are not authenticated")
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, "/admin/login", 303) http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
return return
} }
// take username from sesion and add the user row // take username from sesion and add the user row
@@ -135,7 +135,7 @@ func (c *Controller) WithAdminSession(next http.HandlerFunc) http.HandlerFunc {
if !user.IsAdmin { if !user.IsAdmin {
session.AddFlash("you are not an admin") session.AddFlash("you are not an admin")
session.Save(r, w) session.Save(r, w)
http.Redirect(w, r, "/admin/login", 303) http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
return return
} }
next.ServeHTTP(w, r) next.ServeHTTP(w, r)

View File

@@ -24,7 +24,7 @@ var (
func getParamSignature(params url.Values, secret string) string { func getParamSignature(params url.Values, secret string) string {
// the parameters must be in order before hashing // the parameters must be in order before hashing
paramKeys := make([]string, 0) paramKeys := make([]string, 0)
for k, _ := range params { for k := range params {
paramKeys = append(paramKeys, k) paramKeys = append(paramKeys, k)
} }
sort.Strings(paramKeys) sort.Strings(paramKeys)

View File

@@ -88,3 +88,10 @@ form input[type=submit] {
background-color: #fd1b1b1c; background-color: #fd1b1b1c;
} }
.angry {
background-color: #f4433669;
}
.happy {
background-color: #4caf5069;
}

View File

@@ -1,19 +0,0 @@
package tags
type probeData struct {
Format *probeFormat `json:"format"`
}
type probeFormat struct {
Filename string `json:"filename"`
NBStreams int `json:"nb_streams"`
NBPrograms int `json:"nb_programs"`
FormatName string `json:"format_name"`
FormatLongName string `json:"format_long_name"`
StartTime float64 `json:"start_time,string"`
Duration float64 `json:"duration,string"`
Size string `json:"size"`
Bitrate int `json:"bit_rate,string"`
ProbeScore int `json:"probe_score"`
Tags map[string]string `json:"tags"`
}

Binary file not shown.

Binary file not shown.

View File

@@ -1,203 +0,0 @@
package tags
import (
"encoding/json"
"fmt"
"os/exec"
"strconv"
)
var (
titleFields = []string{
"TITLE",
"title",
}
albumFields = []string{
"ALBUM",
"album",
}
artistFields = []string{
"ARTIST",
"artist",
}
albumArtistFields = []string{
"ALBUM ARTIST",
"ALBUMARTIST",
"ALBUM_ARTIST",
"album artist",
"album_artist",
"albumartist",
}
composerFields = []string{
"COMPOSER",
"composer",
}
genreFields = []string{
"GENRE",
"genre",
}
yearFields = []string{
"YEAR",
"year",
}
trackFields = []string{
"TRACK",
"track",
}
totaltrackFields = []string{
"TOTALTRACKS",
"TRACKC",
"TRACKTOTAL",
"totaltracks",
"trackc",
"tracktotal",
}
discFields = []string{
"DISC",
"disc",
}
totaldiscFields = []string{
"DISCC",
"DISCTOTAL",
"TOTALDISCS",
"discc",
"disctotal",
"totaldiscs",
}
lyricsFields = []string{
"LYRICS",
"lyrics",
}
commentFields = []string{
"COMMENT",
"COMMENTS",
"comment",
"comments",
}
)
func firstExisting(keys *[]string, map_ *map[string]string) string {
for _, field := range *keys {
v, ok := (*map_)[field]
if !ok {
continue
}
return v
}
return ""
}
type Metadata interface {
Title() string
Album() string
Artist() string
AlbumArtist() string
Composer() string
Genre() string
Year() int
Track() int
TotalTracks() int
Disc() int
TotalDiscs() int
Lyrics() string
Comment() string
Length() float64
Format() string
Bitrate() int
}
type Track struct {
format *probeFormat
}
func (t *Track) Title() string {
return firstExisting(&titleFields, &t.format.Tags)
}
func (t *Track) Album() string {
return firstExisting(&albumFields, &t.format.Tags)
}
func (t *Track) Artist() string {
return firstExisting(&artistFields, &t.format.Tags)
}
func (t *Track) AlbumArtist() string {
return firstExisting(&albumArtistFields, &t.format.Tags)
}
func (t *Track) Composer() string {
return firstExisting(&composerFields, &t.format.Tags)
}
func (t *Track) Genre() string {
return firstExisting(&genreFields, &t.format.Tags)
}
func (t *Track) Year() int {
i, _ := strconv.Atoi(firstExisting(&yearFields, &t.format.Tags))
return i
}
func (t *Track) Track() int {
i, _ := strconv.Atoi(firstExisting(&trackFields, &t.format.Tags))
return i
}
func (t *Track) TotalTracks() int {
i, _ := strconv.Atoi(firstExisting(&totaltrackFields, &t.format.Tags))
return i
}
func (t *Track) Disc() int {
i, _ := strconv.Atoi(firstExisting(&discFields, &t.format.Tags))
return i
}
func (t *Track) TotalDiscs() int {
i, _ := strconv.Atoi(firstExisting(&totaldiscFields, &t.format.Tags))
return i
}
func (t *Track) Lyrics() string {
return firstExisting(&lyricsFields, &t.format.Tags)
}
func (t *Track) Comment() string {
return firstExisting(&commentFields, &t.format.Tags)
}
func (t *Track) Format() string {
return t.format.FormatName
}
func (t *Track) Length() float64 {
return t.format.Duration
}
func (t *Track) Bitrate() int {
return t.format.Bitrate
}
func Read(filename string) (Metadata, error) {
command := exec.Command(
"ffprobe",
"-print_format", "json",
"-show_format",
filename,
)
probe, err := command.Output()
if err != nil {
return nil, fmt.Errorf("when running ffprobe with `%s`: %v",
filename, err)
}
var data probeData
err = json.Unmarshal(probe, &data)
if err != nil {
return nil, fmt.Errorf("when unmarshalling: %v", err)
}
track := Track{
format: data.Format,
}
return &track, nil
}

View File

@@ -1,37 +0,0 @@
package tags
import (
"fmt"
"os"
"testing"
"time"
)
func TestRead(t *testing.T) {
start := time.Now()
cwd, _ := os.Getwd()
data, err := Read(
fmt.Sprintf("%s/static/test_flac", cwd),
)
if err != nil {
t.Errorf("when reading tags: %v\n", err)
return
}
length := data.Length()
if length != 160.4 {
t.Errorf("expected length to be `160.4`, got %f", length)
}
bitrate := data.Bitrate()
if bitrate != 815694 {
t.Errorf("expected bitrate to be `815694`, got %d", bitrate)
}
format := data.Format()
if format != "flac" {
t.Errorf("expected format to be `flac`, got %s", format)
}
fmt.Println(data.Title())
fmt.Println(data.Album())
fmt.Println(data.AlbumArtist())
fmt.Println(data.Year())
fmt.Printf("it's been %s\n", time.Since(start))
}

View File

@@ -21,9 +21,9 @@
{{ end }} {{ end }}
{{ if .CurrentLastFMAPIKey }} {{ if .CurrentLastFMAPIKey }}
{{ if .User.LastFMSession }} {{ if .User.LastFMSession }}
<span class="light">current status</span> linked <span class="light">current status</span> <span class="happy">linked</span> <a href="/admin/unlink_lastfm_do">unlink</a><br/>
{{ else }} {{ else }}
<span class="light">current status</span> unlinked <a href="https://www.last.fm/api/auth/?api_key={{ .CurrentLastFMAPIKey }}&cb={{ .RequestRoot }}/admin/link_lastfm_callback">link</a><br/> <span class="light">current status</span> <span class="angry">unlinked</span> <a href="https://www.last.fm/api/auth/?api_key={{ .CurrentLastFMAPIKey }}&cb={{ .RequestRoot }}/admin/link_lastfm_do">link</a><br/>
{{ end }} {{ end }}
{{ else if not .User.IsAdmin }} {{ else if not .User.IsAdmin }}
<span class="light">api key not set. please ask your admin to set it</span> <span class="light">api key not set. please ask your admin to set it</span>

View File

@@ -1,6 +0,0 @@
*.iml
/savers/*generator-*
/savers/generator-*
/vendor/
*.out
cover*.html

View File

@@ -1,34 +0,0 @@
language: go
sudo: false
go_import_path: github.com/myesui/uuid
go:
- 1.8
- 1.7
- 1.6
os:
- linux
- osx
env:
global:
- PATH=$HOME/gopath/bin:$PATH
before_script:
- gofmt -s -l -e .
- go vet ./...
before_install:
- go get -t -v ./...
- go get github.com/mattn/goveralls
script:
- go test -v -race github.com/myesui/uuid
- go test -v -covermode=count -coverprofile=cover.out
- goveralls -coverprofile=cover.out -service=travis-ci
notifications:
email: true

20
vendor/github.com/twinj/uuid/LICENSE generated vendored
View File

@@ -1,20 +0,0 @@
Copyright (C) 2011 by Krzysztof Kowalik <chris@nu7hat.ch>
Copyright (C) 2016 by Daniel Kemp <twinj@github.com> Derivative work
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,311 +0,0 @@
Go UUID implementation
========================
[![license](http://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/myesui/uuid/master/LICENSE)
[![GoDoc](http://godoc.org/github.com/myesui/uuid?status.png)](http://godoc.org/github.com/myesui/uuid)
[![Build Status](https://ci.appveyor.com/api/projects/status/github/myesui/uuid?branch=master&svg=true)](https://ci.appveyor.com/project/myesui/uuid)
[![Build Status](https://travis-ci.org/myesui/uuid.png?branch=master)](https://travis-ci.org/myesui/uuid)
[![Coverage Status](https://coveralls.io/repos/github/myesui/uuid/badge.svg?branch=master)](https://coveralls.io/github/myesui/uuid?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/myesui/uuid)](https://goreportcard.com/report/github.com/myesui/uuid)
This package provides RFC 4122 and DCE 1.1 compliant UUIDs.
It will generate the following:
* Version 1: based on a Timestamp and MAC address as Node id
* Version 2: based on DCE Security - Experimental
* Version 3: based on MD5 hash
* Version 4: based on cryptographically secure random numbers
* Version 5: based on SHA-1 hash
Functions NewV1, NewV2, NewV3, NewV4, NewV5, New, NewHex and Parse() for
generating version 1, 2, 3, 4 and 5 Uuid's
# Requirements
Will generally support last 3 versions of Go.
- 1.8
- 1.7
- 1.6
## Installation
Use the `go` tool:
$ go get gopkg.in/myesui/uuid.v1
See [gopkg.in](http://labix.org/gopkg.in)
# Typical Usage
See [documentation and examples](http://godoc.org/github.com/myesui/uuid)
for more information.
## All UUIDs
import "gopkg.in/myesui/uuid.v1"
id, _ := uuid.Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
if uuid.Equal(id, uuid.NameSpaceDNS) {
fmt.Println("Alas these are equal")
}
if uuid.Compare(id, uuid.NameSpaceDNS) == 0 {
fmt.Println("They are also equal")
}
if uuid.Compare(id, uuid.NameSpaceX500) == -1 {
fmt.Println("id < uuid.NameSpaceX500")
}
if uuid.Compare(uuid.NameSpaceX50, id) == 1 {
fmt.Println("uuid.NameSpaceX500 > id")
}
// Default Format is FormatCanonical
fmt.Println(uuid.Formatter(id, uuid.FormatCanonicalCurly))
uuid.SwitchFormat(uuid.FormatCanonicalBracket)
## Formatting UUIDs
The default format is uuid.FormatCanonical xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx
Any call to uuid.String() will produce this output.
The format is twice as fast as the others at producing a string from the bytes.
To change to another format permanently use:
uuid.SwitchFormat(uuid.Format*)
uuid.SwitchFormatToUpper(uuid.Format*)
Once this has been called in an init function all UUID.String() calls will use the new format.
Available formats:
FormatHex = xxxxxxxxxxxxxxxxxxxxxxxxxx
FormatHexCurly = {xxxxxxxxxxxxxxxxxxxxxxxxxx}
FormatHexBracket = (xxxxxxxxxxxxxxxxxxxxxxxxxx)
// This is the default format.
FormatCanonical Format = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx
FormatCanonicalCurly = {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx}
FormatCanonicalBracket = (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx)
FormatUrn = urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxx
The uuid.Formatter function also exists and is only ever meant to be used for one time prints where a different format from the default or switched format is required. You can supply your own format as long as it fits the pattern.
which contains %x for the five groups in an UUID. Eg: FormatCanonical = %x-%x-%x-%x-%x
You can also stwict to a custom format
Note: AT this time cutsom formats are not supported for TextMarshalling. If a custom format is deteced it will use the canonical format. Use a call to String() and save as a string instead.
## Version 1 and 2 UUIDs
import "gopkg.in/myesui/uuid.v1"
id := uuid.NewV1()
fmt.Println(id)
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
id = uuid.NewV2(uuid.DomainUser)
fmt.Println(id)
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
ids := uuid.BulkV1(500)
for _, v := range ids {
fmt.Println(v)
}
ids = make([]UUID, 100)
ReadV1(ids)
for _, v := range ids {
fmt.Println(v)
}
// If you wish to register a saving mechanism to keep track of your UUIDs over restarts
// It is recommeneded to add a Saver so as to reduce risk in UUID collisions
saver := savers.FileSystemSaver.Init()
// Must be called before any V1 or V2 UUIDs. Do not call other uuid.Register* if
// registering a Saver
uuid.RegisterSaver(saver)
## Version 3 and 5 UUIDs
import "gopkg.in/myesui/uuid.v1"
id := uuid.NewV3(uuid.NameSpaceURL, uuid.Name("www.example.com"))
fmt.Println(id)
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
id := uuid.NewV5(uuid.NameSpaceURL, uuid.Name("www.example.com"))
fmt.Println(id)
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
id = uuid.NewV5(uuid.NameSpaceURL, id)
fmt.Println(id)
fmt.Printf("version %s variant %x: %s\n", u1.Version(), u1.Variant(), id)
## Version 4 UUIDs
import "gopkg.in/myesui/uuid.v1"
// A V4 UUID will panic by default if the systems CPRNG fails - this can
// be changed by registering your own generator
u4 := uuid.NewV4()
fmt.Println(id)
fmt.Printf("version %d variant %x: %s\n", u4.Version(), u4.Variant(), u4)
ids := uuid.BulkV4(500)
for _, v := range ids {
fmt.Println(v)
}
ids := make([]UUID, 100)
ReadV4(ids)
for _, v := range ids {
fmt.Println(v)
}
## Custom Generators
import "gopkg.in/myesui/uuid.v1"
// Improve resolution for V1 and 2 UUIDs
// The resolution correlates to how many ids can be created before waiting
// for the next unique timestamp. The default is a low 1024, this equates
// to Ids that can be created in 100 nanoseconds. It is low to encourage
// you to set it.
uuid.RegisterGenerator(&GeneratorConfig{Resolution: 18465})
// Provide your own node Id or MAC address
uuid.RegisterGenerator(&GeneratorConfig{
Id: func() uuid.Node{
// My Node Id
// If this returns nil a random one will be generated
},
})
// Replace the default Timestamp spinner with your own.
uuid.RegisterGenerator(&GeneratorConfig{
Next: func()(uuid.Timestamp){
// My own Timestamp function...
// Resolution will become reduendant if you set this.
// The package will increment the clock sequence if you produce equal Timestamps
},
})
// Replace the default crypto/rand.Read CPRNG with your own.
uuid.RegisterGenerator(&GeneratorConfig{
Random: func([]byte)(int, error){
// My CPRNG function...
},
})
// type HandleRandomError func([]byte, int, error) error
// Replace the default random number error handler for V4 UUIDs. This function is called
// when there is an error in the crypto/rand CPRNG. The default error handler function reads
// from math.Rand as a fallback.
//
// You can change that behaviour and handle the error by providing your own function.
//
// Errors could be due to a lack of system entropy or some other serious issue. These issues are rare,
// however, having the tools to handle such issues is important.
// This approach was taken as each user of this package will want to handle this differently.
//
// For example one user of the package might want to just panic instead.
// Returning an error will cause a panic.
uuid.RegisterGenerator(&GeneratorConfig{
HandleError: func(id []byte, n int, err error)bool{
return err
},
})
// You can also just generate your own completely.
myGenerator := NewGenerator(nil)
id := myGenerator.NewV4()
// You can replace the logger
uuid.RegisterGenerator(&GeneratorConfig{
Logger: log.New(someWriter, "my-prefix", myFlags),
})
## Coverage
* go test -coverprofile cover.out github.com/myesui/uuid
* go test -coverprofile cover.out github.com/myesui/uuid/savers
* go tool cover -html=cover.out -o cover.html
## Contribution
1. fork from the *master* branch to create your own fork
2. clone from *master* into $GOPATH/src/github.com/myesui/uuid
3. git remote add `username` https://github.com/username/uuid.git
4. push changes on your fork and track your remote
5. Remember to create a branch
To ensure you get the correct packages and subpackages install in a gopath which matches *go/src/github.com/myesui/uuid*
## Links
* [RFC 4122](http://www.ietf.org/rfc/rfc4122.txt)
* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9629399/apdxa.htm)
# Design considerations
* UUID is an interface which correlates to
* V1 UUIDs are sequential. This can cause the Generator to work
more slowly compared to other implementations. It can however be manually tuned
to have performance that is on par. This is achieved by setting the Timestamp
Resolution. Benchmark tests have been provided to help determine the best
setting for your server
Proper test coverage has determined thant the UUID timestamp spinner works
correctly, across multiple clock resolutions. The generator produces
timestamps that roll out sequentially and will only modify the clock
sequence on very rare circumstances.
It is highly recommended that you register a uuid.Saver if you use V1 or V2
UUIDs as it will ensure a higher probability of uniqueness.
Example V1 output:
5fb1a280-30f0-11e6-9614-005056c00001
5fb1a281-30f0-11e6-9614-005056c00001
5fb1a282-30f0-11e6-9614-005056c00001
5fb1a283-30f0-11e6-9614-005056c00001
5fb1a284-30f0-11e6-9614-005056c00001
5fb1a285-30f0-11e6-9614-005056c00001
5fb1a286-30f0-11e6-9614-005056c00001
5fb1a287-30f0-11e6-9614-005056c00001
5fb1a288-30f0-11e6-9614-005056c00001
5fb1a289-30f0-11e6-9614-005056c00001
5fb1a28a-30f0-11e6-9614-005056c00001
5fb1a28b-30f0-11e6-9614-005056c00001
5fb1a28c-30f0-11e6-9614-005056c00001
5fb1a28d-30f0-11e6-9614-005056c00001
5fb1a28e-30f0-11e6-9614-005056c00001
5fb1a28f-30f0-11e6-9614-005056c00001
5fb1a290-30f0-11e6-9614-005056c00001
* The V1 UUID generator should be file system and server agnostic
To achieve this there are:
** No Os locking threads or file system dependant storage
** Provided the uuid.Saver interface so a package can implement its own solution if required
* The V4 UUID should allow a package to handle any error that can occur in the CPRNG. The default is to read from math.Rand``````.
* The package should be able to handle multiple instances of Generators so a package can produce UUIDs from multiple sources.
## Copyright
Copyright (C) 2017 myesui@github.com
See [LICENSE](https://github.com/myesui/uuid/tree/master/LICENSE) file for details.

View File

@@ -1,30 +0,0 @@
# version format
version: "{build}"
# Operating system (build VM template)
os: Windows Server 2012 R2
clone_folder: c:\gopath\src\github.com\myesui\uuid
# environment variables
environment:
GOPATH: c:\gopath
# scripts that run after cloning repository
install:
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
- go version
- go env
- go get -t -v ./...
# to run your custom scripts instead of automatic MSBuild
build_script:
- go vet -x ./...
- gofmt -s -l -e .
- go test ./... -v -short -race
# to disable automatic tests
test: off
# to disable deployment
deploy: off

View File

@@ -1,161 +0,0 @@
package uuid
import (
"strings"
)
// Format represents different styles a UUID can be printed in constants
// represent a pattern used by the package with which to print a UUID.
type Format string
// The following are the default Formats supplied by the uuid package.
const (
FormatHex Format = "%x%x%x%x%x"
FormatHexCurly Format = "{%x%x%x%x%x}"
FormatHexBracket Format = "(%x%x%x%x%x)"
// FormatCanonical is the default format.
FormatCanonical Format = "%x-%x-%x-%x-%x"
FormatCanonicalCurly Format = "{%x-%x-%x-%x-%x}"
FormatCanonicalBracket Format = "(%x-%x-%x-%x-%x)"
FormatUrn Format = "urn:uuid:" + FormatCanonical
)
var (
printFormat = FormatCanonical
defaultFormats map[Format]bool = make(map[Format]bool)
)
func init() {
defaultFormats[FormatHex] = true
defaultFormats[FormatHexCurly] = true
defaultFormats[FormatHexBracket] = true
defaultFormats[FormatCanonical] = true
defaultFormats[FormatCanonicalCurly] = true
defaultFormats[FormatCanonicalBracket] = true
defaultFormats[FormatUrn] = true
}
// SwitchFormat switches the default printing format for ALL UUIDs.
//
// The default is canonical uuid.Format.FormatCanonical which has been
// optimised for use with this package. It is twice as fast compared to other
// formats. However, non package formats are still very quick.
//
// A valid format will have 5 groups of [%x|%X] or follow the pattern,
// *%[xX]*%[xX]*%[xX]*%[xX]*%[xX]*. If the supplied format does not meet this
// standard the function will panic. Note any extra uses of [%] outside of the
// [%x|%X] will also cause a panic.
// Constant uuid.Formats have been provided for most likely formats.
func SwitchFormat(form Format) {
checkFormat(form)
printFormat = form
}
// SwitchFormatToUpper is a convenience function to set the uuid.Format to uppercase
// versions.
func SwitchFormatToUpper(form Format) {
SwitchFormat(Format(strings.ToUpper(string(form))))
}
// Formatter will return a string representation of the given UUID.
//
// Use this for one time formatting when setting the Format using
// uuid.SwitchFormat would be overkill.
//
// A valid format will have 5 groups of [%x|%X] or follow the pattern,
// *%[xX]*%[xX]*%[xX]*%[xX]*%[xX]*. If the supplied format does not meet this
// standard the function will panic. Note any extra uses of [%] outside of the
// [%x|%X] will also cause a panic.
func Formatter(id Implementation, form Format) string {
checkFormat(form)
return formatUuid(id.Bytes(), form)
}
func checkFormat(form Format) {
if defaultFormats[form] {
return
}
s := strings.ToLower(string(form))
if strings.Count(s, "%x") != 5 {
panic("uuid: invalid format")
}
s = strings.Replace(s, "%x", "", -1)
if strings.Count(s, "%") > 0 {
panic("uuid: invalid format")
}
}
const (
hexTable = "0123456789abcdef"
hexUpperTable = "0123456789ABCDEF"
canonicalLength = length*2 + 4
formatArgCount = 10
uuidStringBufferSize = length*2 - formatArgCount
)
var groups = [...]int{4, 2, 2, 2, 6}
func formatUuid(src []byte, form Format) string {
if form == FormatCanonical {
return string(formatCanonical(src))
}
return string(format(src, string(form)))
}
func format(src []byte, form string) []byte {
end := len(form)
buf := make([]byte, end+uuidStringBufferSize)
var s, ls, b, e, p int
var u bool
for _, v := range groups {
ls = s
for ; s < end && form[s] != '%'; s++ {
}
copy(buf[p:], form[ls:s])
p += s - ls
s++
u = form[s] == 'X'
s++
e = b + v
for i, t := range src[b:e] {
j := p + i + i
table := hexTable
if u {
table = hexUpperTable
}
buf[j] = table[t>>4]
buf[j+1] = table[t&0x0f]
}
b = e
p += v + v
}
ls = s
for ; s < end && form[s] != '%'; s++ {
}
copy(buf[p:], form[ls:s])
return buf
}
func formatCanonical(src []byte) []byte {
buf := make([]byte, canonicalLength)
var b, p, e int
for h, v := range groups {
e = b + v
for i, t := range src[b:e] {
j := p + i + i
buf[j] = hexTable[t>>4]
buf[j+1] = hexTable[t&0x0f]
}
b = e
p += v + v
if h < 4 {
buf[p] = '-'
p += 1
}
}
return buf
}

View File

@@ -1,527 +0,0 @@
package uuid
import (
"bytes"
"crypto/md5"
"crypto/rand"
"crypto/sha1"
"encoding/binary"
"fmt"
"hash"
"log"
mrand "math/rand"
"net"
"os"
"sync"
"time"
)
var (
once = new(sync.Once)
generator = newGenerator(nil)
)
func init() {
seed := time.Now().UTC().UnixNano()
b := [8]byte{}
_, err := rand.Read(b[:])
if err == nil {
seed += int64(binary.BigEndian.Uint64(b[:]))
}
mrand.Seed(seed)
}
// Random provides a random number generator which reads into the given []byte, the package
// uses crypto/rand.Read by default. You can supply your own implementation or downgrade it
// to the match/rand package.
//
// The function is used by V4 UUIDs and for setting up V1 and V2 UUIDs in the
// Generator Init or Register* functions.
type Random func([]byte) (int, error)
// Next provides the next Timestamp value to be used by the next V1 or V2 UUID.
// The default uses the uuid.spinner which spins at a resolution of
// 100ns ticks and provides a spin resolution redundancy of 1024
// cycles. This ensures that the system is not too quick when
// generating V1 or V2 UUIDs. Each system requires a tuned Resolution to
// enhance performance.
type Next func() Timestamp
// Identifier provides the Node to be used during the life of a
// uuid.Generator. If it cannot be determined nil should be returned, the
// package will then provide a node identifier provided by the Random function.
// The default generator gets a MAC address from the first interface that is 'up' checking
// net.FlagUp.
type Identifier func() Node
// HandleRandomError provides the ability to manage a serious error that may be
// caused by accessing the standard crypto/rand library or the supplied uuid/Random
// function. Due to the rarity of this occurrence the error is swallowed by the
// uuid/NewV4 function which relies heavily on random numbers, the package will
// panic instead if an error occurs.
//
// You can change this behaviour by passing in your own uuid/HandleError
// function to a custom Generator. This function can attempt to fix the random
// number generator. If your uuid/HandleError returns true the generator will
// attempt to generate another V4 uuid. If another error occurs the function
// will return a fallback v4 uuid generated from the less random math/rand standard library.
//
// Waiting for system entropy may be all that is required in the initial error.
// If something more serious has occurred, handle appropriately using this function.
type HandleRandomError func([]byte, int, error) error
// Generator is used to create and monitor the running of V1 and V2, and V4
// UUIDs. It can be setup to take custom implementations for Timestamp, Node
// and Random number retrieval by providing those functions as required.
// You can also supply a uuid/Saver implementation for saving the state of the generator
// and you can also provide an error policy for V4 UUIDs and possible errors in the random
// number generator.
type Generator struct {
// Access to the store needs to be maintained
sync.Mutex
// Once ensures that the generator is only setup and initialised once.
// This will occur either when you explicitly call the
// uuid.Generator.Init function or when a V1 or V2 id is generated.
sync.Once
// Store contains the current values being used by the Generator.
*Store
// Identifier as per the type Identifier func() Node
Identifier
// HandleRandomError as per the type HandleError func(error) bool
HandleRandomError
// Next as per the type Next func() Timestamp
Next
// Random as per the type Random func([]byte) (int, error)
Random
// Saver provides a non-volatile store to save the state of the
// generator, the default is nil which will cause the timestamp
// clock sequence to populate with random data. You can register your
// own saver by using the uuid.RegisterSaver function or by creating
// your own uuid.Generator instance.
// UUIDs.
Saver
*log.Logger
}
// GeneratorConfig allows you to setup a new uuid.Generator using uuid.NewGenerator or RegisterGenerator. You can supply your own
// implementations for the random number generator Random, Identifier and Timestamp retrieval. You can also
// adjust the resolution of the default Timestamp spinner and supply your own
// error handler for crypto/rand failures.
type GeneratorConfig struct {
Saver
Next
Resolution uint
Identifier
Random
HandleRandomError
*log.Logger
}
// NewGenerator will create a new uuid.Generator with the given functions.
func NewGenerator(config *GeneratorConfig) (*Generator, error) {
return onceDo(newGenerator(config))
}
func onceDo(gen *Generator) (*Generator, error) {
var err error
gen.Do(func() {
err = gen.init()
if err != nil {
gen = nil
}
})
return gen, err
}
func newGenerator(config *GeneratorConfig) (gen *Generator) {
if config == nil {
config = new(GeneratorConfig)
}
gen = new(Generator)
if config.Next == nil {
if config.Resolution == 0 {
config.Resolution = defaultSpinResolution
}
gen.Next = (&spinner{
Resolution: config.Resolution,
Count: 0,
Timestamp: Now(),
now: Now,
}).next
} else {
gen.Next = config.Next
}
if config.Identifier == nil {
gen.Identifier = findFirstHardwareAddress
} else {
gen.Identifier = config.Identifier
}
if config.Random == nil {
gen.Random = rand.Read
} else {
gen.Random = config.Random
}
if config.HandleRandomError == nil {
gen.HandleRandomError = gen.runHandleError
} else {
gen.HandleRandomError = config.HandleRandomError
}
if config.Logger == nil {
gen.Logger = log.New(os.Stderr, "uuid: ", log.LstdFlags)
} else {
gen.Logger = config.Logger
}
gen.Saver = config.Saver
gen.Store = new(Store)
return
}
// RegisterGenerator will set the package generator with the given configuration
// Like uuid.Init this can only be called once. Any subsequent calls will have no
// effect. If you call this you do not need to call uuid.Init.
func RegisterGenerator(config *GeneratorConfig) (err error) {
notOnce := true
once.Do(func() {
generator, err = NewGenerator(config)
notOnce = false
return
})
if notOnce {
panic("uuid: Register* methods cannot be called more than once.")
}
return
}
func (o *Generator) read() {
// Save the state (current timestamp, clock sequence, and node ID)
// back to the stable store
if o.Saver != nil {
defer o.save()
}
// Obtain a lock
o.Lock()
defer o.Unlock()
// Get the current time as a 60-bit count of 100-nanosecond intervals
// since 00:00:00.00, 15 October 1582.
now := o.Next()
// If the last timestamp is later than
// the current timestamp, increment the clock sequence value.
if now <= o.Timestamp {
o.Sequence++
}
// Update the timestamp
o.Timestamp = now
}
func (o *Generator) init() error {
// From a system-wide shared stable store (e.g., a file), read the
// UUID generator state: the values of the timestamp, clock sequence,
// and node ID used to generate the last UUID.
var (
storage Store
err error
)
o.Lock()
defer o.Unlock()
if o.Saver != nil {
storage, err = o.Read()
if err != nil {
o.Saver = nil
}
}
// Get the current time as a 60-bit count of 100-nanosecond intervals
// since 00:00:00.00, 15 October 1582.
now := o.Next()
// Get the current node id
node := o.Identifier()
if node == nil {
o.Println("address error generating random node id")
node = make([]byte, 6)
n, err := o.Random(node)
if err != nil {
o.Printf("could not read random bytes into node - read [%d] %s", n, err)
return err
}
// Mark as randomly generated
node[0] |= 0x01
}
// If the state was unavailable (e.g., non-existent or corrupted), or
// the saved node ID is different than the current node ID, generate
// a random clock sequence value.
if o.Saver == nil || !bytes.Equal(storage.Node, node) {
// 4.1.5. Clock Sequence https://www.ietf.org/rfc/rfc4122.txt
//
// For UUID version 1, the clock sequence is used to help avoid
// duplicates that could arise when the clock is set backwards in time
// or if the node ID changes.
//
// If the clock is set backwards, or might have been set backwards
// (e.g., while the system was powered off), and the UUID generator can
// not be sure that no UUIDs were generated with timestamps larger than
// the value to which the clock was set, then the clock sequence has to
// be changed. If the previous value of the clock sequence is known, it
// can just be incremented; otherwise it should be set to a random or
// high-quality pseudo-random value.
// The clock sequence MUST be originally (i.e., once in the lifetime of
// a system) initialized to a random number to minimize the correlation
// across systems. This provides maximum protection against node
// identifiers that may move or switch from system to system rapidly.
// The initial value MUST NOT be correlated to the node identifier.
b := make([]byte, 2)
n, err := o.Random(b)
if err == nil {
storage.Sequence = Sequence(binary.BigEndian.Uint16(b))
o.Printf("initialised random sequence [%d]", storage.Sequence)
} else {
o.Printf("could not read random bytes into sequence - read [%d] %s", n, err)
return err
}
} else if now < storage.Timestamp {
// If the state was available, but the saved timestamp is later than
// the current timestamp, increment the clock sequence value.
storage.Sequence++
}
storage.Timestamp = now
storage.Node = node
o.Store = &storage
return nil
}
func (o *Generator) save() {
func(state *Generator) {
if state.Saver != nil {
state.Lock()
defer state.Unlock()
state.Save(*state.Store)
}
}(o)
}
// NewV1 generates a new RFC4122 version 1 UUID based on a 60 bit timestamp and
// node id.
func (o *Generator) NewV1() UUID {
o.read()
id := UUID{}
makeUuid(&id,
uint32(o.Timestamp),
uint16(o.Timestamp>>32),
uint16(o.Timestamp>>48),
uint16(o.Sequence),
o.Node)
id.setRFC4122Version(VersionOne)
return id
}
// ReadV1 will read a slice of UUIDs. Be careful with the set amount.
func (o *Generator) ReadV1(ids []UUID) {
for i := range ids {
ids[i] = o.NewV1()
}
}
// BulkV1 will return a slice of V1 UUIDs. Be careful with the set amount.
func (o *Generator) BulkV1(amount int) []UUID {
ids := make([]UUID, amount)
o.ReadV1(ids)
return ids
}
// NewV2 generates a new DCE version 2 UUID based on a 60 bit timestamp, node id
// and the id of the given Id type.
func (o *Generator) NewV2(idType SystemId) UUID {
o.read()
id := UUID{}
var osId uint32
switch idType {
case SystemIdUser:
osId = uint32(os.Getuid())
case SystemIdGroup:
osId = uint32(os.Getgid())
case SystemIdEffectiveUser:
osId = uint32(os.Geteuid())
case SystemIdEffectiveGroup:
osId = uint32(os.Getegid())
case SystemIdCallerProcess:
osId = uint32(os.Getpid())
case SystemIdCallerProcessParent:
osId = uint32(os.Getppid())
}
makeUuid(&id,
osId,
uint16(o.Timestamp>>32),
uint16(o.Timestamp>>48),
uint16(o.Sequence),
o.Node)
id[9] = byte(idType)
id.setRFC4122Version(VersionTwo)
return id
}
// NewV3 generates a new RFC4122 version 3 UUID based on the MD5 hash of a
// namespace UUID namespace Implementation UUID and one or more unique names.
func (o *Generator) NewV3(namespace Implementation, names ...interface{}) UUID {
id := UUID{}
id.unmarshal(digest(md5.New(), namespace.Bytes(), names...))
id.setRFC4122Version(VersionThree)
return id
}
func digest(hash hash.Hash, name []byte, names ...interface{}) []byte {
for _, v := range names {
switch t := v.(type) {
case string:
name = append(name, t...)
continue
case []byte:
name = append(name, t...)
continue
case *string:
name = append(name, (*t)...)
continue
}
if s, ok := v.(fmt.Stringer); ok {
name = append(name, s.String()...)
continue
}
panic(fmt.Sprintf("uuid: does not support type [%T] as a name for hashed UUIDs.", v))
}
hash.Write(name)
return hash.Sum(nil)
}
// NewV4 generates a cryptographically secure random RFC4122 version 4 UUID. If there is an error with the random
// number generator this will
func (o *Generator) NewV4() (id UUID) {
o.v4(&id)
return
}
// ReadV4 will read into a slice of UUIDs. Be careful with the set amount.
// Note: V4 UUIDs require sufficient entropy from the generator.
// If n == len(ids) err will be nil.
func (o *Generator) ReadV4(ids []UUID) {
for i := range ids {
id := UUID{}
o.v4(&id)
ids[i] = id
continue
}
return
}
// BulkV4 will return a slice of V4 UUIDs. Be careful with the set amount.
// Note: V4 UUIDs require sufficient entropy from the generator.
// If n == len(ids) err will be nil.
func (o *Generator) BulkV4(amount int) []UUID {
ids := make([]UUID, amount)
o.ReadV4(ids)
return ids
}
func (o *Generator) v4(id *UUID) {
n, err := o.Random(id[:])
if err != nil {
o.Printf("there was an error getting random bytes [%s]", err)
if err = o.HandleRandomError(id[:], n, err); err != nil {
panic(fmt.Sprintf("random number error - %s", err))
}
}
id.setRFC4122Version(VersionFour)
}
// NewV5 generates an RFC4122 version 5 UUID based on the SHA-1 hash of a
// namespace Implementation UUID and one or more unique names.
func (o *Generator) NewV5(namespace Implementation, names ...interface{}) UUID {
id := UUID{}
id.unmarshal(digest(sha1.New(), namespace.Bytes(), names...))
id.setRFC4122Version(VersionFive)
return id
}
// NewHash generate a UUID based on the given hash implementation. The hash will
// be of the given names. The version will be set to 0 for Unknown and the
// variant will be set to VariantFuture.
func (o *Generator) NewHash(hash hash.Hash, names ...interface{}) UUID {
id := UUID{}
id.unmarshal(digest(hash, []byte{}, names...))
id[versionIndex] &= 0x0f
id[versionIndex] |= uint8(0 << 4)
id[variantIndex] &= variantSet
id[variantIndex] |= VariantFuture
return id
}
func makeUuid(id *UUID, low uint32, mid, hiAndV, seq uint16, node Node) {
id[0] = byte(low >> 24)
id[1] = byte(low >> 16)
id[2] = byte(low >> 8)
id[3] = byte(low)
id[4] = byte(mid >> 8)
id[5] = byte(mid)
id[6] = byte(hiAndV >> 8)
id[7] = byte(hiAndV)
id[8] = byte(seq >> 8)
id[9] = byte(seq)
copy(id[10:], node)
}
func findFirstHardwareAddress() (node Node) {
interfaces, err := net.Interfaces()
if err == nil {
for _, i := range interfaces {
if i.Flags&net.FlagUp != 0 && bytes.Compare(i.HardwareAddr, nil) != 0 {
// Don't use random as we have a real address
node = Node(i.HardwareAddr)
break
}
}
}
return
}
func (o *Generator) runHandleError(id []byte, n int, err error) error {
o.Lock()
mrand.Read(id)
o.Unlock()
return nil
}

View File

@@ -1,65 +0,0 @@
package uuid
import (
"fmt"
)
// Sequence represents an iterated value to help ensure unique UUID generations
// values across the same domain, server restarts and clock issues.
type Sequence uint16
// Node represents the last node id setup used by the generator.
type Node []byte
// Store is used for storage of UUID generation history to ensure continuous
// running of the UUID generator between restarts and to monitor synchronicity
// while generating new V1 or V2 UUIDs.
type Store struct {
Timestamp
Sequence
Node
}
// String returns a string representation of the Store.
func (o Store) String() string {
return fmt.Sprintf("Timestamp[%s]-Sequence[%d]-Node[%x]", o.Timestamp, o.Sequence, o.Node)
}
// Saver is an interface to setup a non volatile store within your system
// if you wish to use V1 and V2 UUIDs based on your node id and a constant time
// it is highly recommended to implement this.
// A default implementation has been provided. FileSystemStorage, the default
// behaviour of the package is to generate random sequences where a Saver is not
// specified.
type Saver interface {
// Read is run once, use this to setup your UUID state machine
// Read should also return the UUID state from the non volatile store
Read() (Store, error)
// Save saves the state to the non volatile store and is called only if
Save(Store)
// Init allows default setup of a new Saver
Init() Saver
}
// RegisterSaver register's a uuid.Saver implementation to the default package
// uuid.Generator. If you wish to save the generator state, this function must
// be run before any calls to V1 or V2 UUIDs. uuid.RegisterSaver cannot be run
// in conjunction with uuid.Init. You may implement the uuid.Saver interface
// or use the provided uuid.Saver's from the uuid/savers package.
func RegisterSaver(saver Saver) (err error) {
notOnce := true
once.Do(func() {
generator.Lock()
generator.Saver = saver
generator.Unlock()
err = generator.init()
notOnce = false
return
})
if notOnce {
panic("uuid: Register* methods cannot be called more than once.")
}
return
}

View File

@@ -1,120 +0,0 @@
package uuid
import (
"time"
)
const (
gregorianToUNIXOffset = 122192928e9
// set the following to the number of 100ns ticks of the actual
// resolution of your system's clock
defaultSpinResolution = 1024
)
// Timestamp as per 4.1.4. Timestamp https://www.ietf.org/rfc/rfc4122.txt
//
// The timestamp is a 60-bit value. For UUID version 1, this is
//
// represented by Coordinated Universal Time (UTC) as a count of 100-
// nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of
// Gregorian reform to the Christian calendar).
//
// For systems that do not have UTC available, but do have the local
// time, they may use that instead of UTC, as long as they do so
// consistently throughout the system. However, this is not recommended
// since generating the UTC from local time only needs a time zone
// offset.
//
// For UUID version 3 or 5, the timestamp is a 60-bit value constructed
// from a name as described in Section 4.3.
//
// For UUID version 4, the timestamp is a randomly or pseudo-randomly
// generated 60-bit value, as described in Section 4.4.
type Timestamp uint64
// Now converts Unix formatted time to RFC4122 UUID formatted times
// UUID UTC base time is October 15, 1582.
// Unix base time is January 1, 1970.
// Converts time to 100 nanosecond ticks since epoch. Uses time.Now
func Now() Timestamp {
return Timestamp(time.Now().UnixNano()/100 + gregorianToUNIXOffset)
}
// Time converts UUID Timestamp to UTC time.Time
// Note some higher clock resolutions will lose accuracy if above 100 ns ticks
func (o Timestamp) Time() time.Time {
return time.Unix(0, int64((o-gregorianToUNIXOffset)*100)).UTC()
}
// Add returns the timestamp as modified by the duration
func (o Timestamp) Add(duration time.Duration) Timestamp {
return o + Timestamp(duration/100)
}
// Sub returns the timestamp as modified by the duration
func (o Timestamp) Sub(duration time.Duration) Timestamp {
return o - Timestamp(duration/100)
}
// String Converts UUID Timestamp to time.Time and then calls the Stringer
func (o Timestamp) String() string {
return o.Time().String()
}
// 4.2.1.2. System Clock Resolution https://www.ietf.org/rfc/rfc4122.txt
//
// The timestamp is generated from the system time, whose resolution may
// be less than the resolution of the UUID timestamp.
//
// If UUIDs do not need to be frequently generated, the timestamp can
// simply be the system time multiplied by the number of 100-nanosecond
// intervals per system time interval.
//
// If a system overruns the generator by requesting too many UUIDs
// within a single system time interval, the UUID service MUST either
// return an error, or stall the UUID generator until the system clock
// catches up.
//
// A high resolution timestamp can be simulated by keeping a count of
// the number of UUIDs that have been generated with the same value of
// the system time, and using it to construct the low order bits of the
// timestamp. The count will range between zero and the number of
// 100-nanosecond intervals per system time interval.
//
// Note: If the processors overrun the UUID generation frequently,
// additional node identifiers can be allocated to the system, which
// will permit higher speed allocation by making multiple UUIDs
// potentially available for each time stamp value.
type spinner struct {
// the amount of ids based on the Timestamp
Count, Resolution uint
// the tracked spin stamp
Timestamp
now func() Timestamp
}
func (o *spinner) next() Timestamp {
for {
now := o.now()
// if clock reading changed since last UUID generated
if o.Timestamp == now {
o.Count++
if o.Count == o.Resolution {
for o.now() < o.Timestamp+Timestamp(o.Resolution) {
}
continue
}
break
}
// reset count of UUIDs with this timestamp
o.Count = 0
o.Timestamp = now
break
}
return o.Timestamp + Timestamp(o.Count)
}

175
vendor/github.com/twinj/uuid/types.go generated vendored
View File

@@ -1,175 +0,0 @@
package uuid
import (
"database/sql/driver"
"fmt"
"errors"
)
const (
length = 16
variantIndex = 8
versionIndex = 6
)
// **************************************************** Create UUIDs
func (o *UUID) unmarshal(data []byte) {
copy(o[:], data)
}
// Set the three most significant bits (bits 0, 1 and 2) of the
// sequenceHiAndVariant equivalent in the array to ReservedRFC4122.
func (o *UUID) setRFC4122Version(version Version) {
o[versionIndex] &= 0x0f
o[versionIndex] |= uint8(version << 4)
o[variantIndex] &= variantSet
o[variantIndex] |= VariantRFC4122
}
// **************************************************** Default implementation
var _ Implementation = &UUID{}
// UUID is the default RFC implementation. All uuid functions will return this
// type.
type UUID [length]byte
// Size returns the octet length of the Uuid
func (o UUID) Size() int {
return length
}
// Version returns the uuid.Version of the Uuid
func (o UUID) Version() Version {
return resolveVersion(o[versionIndex] >> 4)
}
// Variant returns the implementation variant of the Uuid
func (o UUID) Variant() uint8 {
return variant(o[variantIndex])
}
// Bytes return the underlying data representation of the Uuid.
func (o UUID) Bytes() []byte {
return o[:]
}
// String returns the canonical string representation of the UUID or the
// uuid.Format the package is set to via uuid.SwitchFormat
func (o UUID) String() string {
return formatUuid(o[:], printFormat)
}
// **************************************************** Implementations
// MarshalBinary implements the encoding.BinaryMarshaler interface
func (o UUID) MarshalBinary() ([]byte, error) {
return o.Bytes(), nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface
func (o *UUID) UnmarshalBinary(bytes []byte) error {
if len(bytes) != o.Size() {
return errors.New("uuid: invalid length")
}
o.unmarshal(bytes)
return nil
}
// MarshalText implements the encoding.TextMarshaler interface. It will marshal
// text into one of the known formats, if you have changed to a custom Format
// the text be output in canonical format.
func (o UUID) MarshalText() ([]byte, error) {
f := FormatCanonical
if defaultFormats[printFormat] {
f = printFormat
}
return []byte(formatUuid(o.Bytes(), f)), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface. It will
// support any text that MarshalText can produce.
func (o *UUID) UnmarshalText(uuid []byte) error {
id, err := parse(string(uuid))
if err == nil {
o.UnmarshalBinary(id)
}
return err
}
// Value implements the driver.Valuer interface
func (o UUID) Value() (value driver.Value, err error) {
if IsNil(o) {
value, err = nil, nil
return
}
value, err = o.MarshalText()
return
}
// Scan implements the sql.Scanner interface
func (o *UUID) Scan(src interface{}) error {
if src == nil {
return nil
}
if src == "" {
return nil
}
switch src := src.(type) {
case string:
return o.UnmarshalText([]byte(src))
case []byte:
if len(src) == length {
return o.UnmarshalBinary(src)
} else {
return o.UnmarshalText(src)
}
default:
return fmt.Errorf("uuid: cannot scan type [%T] into UUID", src)
}
}
// **************************************************** Immutable UUID
var _ Implementation = new(Immutable)
// Immutable is an easy to use UUID which can be used as a key or for constants
type Immutable string
// Size returns the octet length of the Uuid
func (o Immutable) Size() int {
return length
}
// Version returns the uuid.Version of the Uuid
func (o Immutable) Version() Version {
return resolveVersion(o[versionIndex] >> 4)
}
// Variant returns the implementation variant of the Uuid
func (o Immutable) Variant() uint8 {
return variant(o[variantIndex])
}
// Bytes return the underlying data representation of the Uuid in network byte
// order
func (o Immutable) Bytes() []byte {
return []byte(o)
}
// String returns the canonical string representation of the UUID or the
// uuid.Format the package is set to via uuid.SwitchFormat
func (o Immutable) String() string {
return formatUuid([]byte(o), printFormat)
}
// UUID converts this implementation to the default type uuid.UUID
func (o Immutable) UUID() UUID {
id := UUID{}
id.unmarshal(o.Bytes())
return id
}

267
vendor/github.com/twinj/uuid/uuid.go generated vendored
View File

@@ -1,267 +0,0 @@
// Package uuid provides RFC4122 and DCE 1.1 UUIDs.
//
// Use NewV1, NewV2, NewV3, NewV4, NewV5, for generating new UUIDs.
//
// Use New([]byte), NewHex(string), and Parse(string) for
// creating UUIDs from existing data.
//
// The original version was from Krzysztof Kowalik <chris@nu7hat.ch>
// Unfortunately, that version was non compliant with RFC4122.
//
// The package has since been redesigned.
//
// The example code in the specification was also used as reference for design.
//
// Copyright (C) 2016 myesui@github.com 2016 MIT licence
package uuid
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"hash"
"regexp"
)
// Nil represents an empty UUID.
const Nil Immutable = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
// The following Immutable UUIDs are for use with V3 or V5 UUIDs.
const (
NameSpaceDNS Immutable = "k\xa7\xb8\x10\x9d\xad\x11р\xb4\x00\xc0O\xd40\xc8"
NameSpaceURL Immutable = "k\xa7\xb8\x11\x9d\xad\x11р\xb4\x00\xc0O\xd40\xc8"
NameSpaceOID Immutable = "k\xa7\xb8\x12\x9d\xad\x11р\xb4\x00\xc0O\xd40\xc8"
NameSpaceX500 Immutable = "k\xa7\xb8\x14\x9d\xad\x11р\xb4\x00\xc0O\xd40\xc8"
)
// SystemId denotes the type of id to retrieve from the operating system.
// That id is then used to create an identifier UUID.
type SystemId uint8
// The following SystemId's are for use with V2 UUIDs.
const (
SystemIdUser SystemId = iota + 1
SystemIdEffectiveUser
SystemIdGroup
SystemIdEffectiveGroup
SystemIdCallerProcess
SystemIdCallerProcessParent
)
// Implementation is the common interface implemented by all UUIDs.
type Implementation interface {
// Bytes retrieves the bytes from the underlying UUID
Bytes() []byte
// Size is the length of the underlying UUID implementation
Size() int
// String should return the canonical UUID representation, or the a
// given uuid.Format
String() string
// Variant returns the UUID implementation variant
Variant() uint8
// Version returns the version number of the algorithm used to generate
// the UUID.
Version() Version
}
// New creates a UUID from a slice of bytes.
func New(data []byte) UUID {
o := UUID{}
o.unmarshal(data)
return o
}
// NewHex creates a UUID from a hex string.
// Will panic if hex string is invalid use Parse otherwise.
func NewHex(uuid string) UUID {
o := UUID{}
o.unmarshal(fromHex(uuid))
return o
}
const (
// Pattern used to parse string representation of the UUID.
// Current one allows to parse string where only one opening
// or closing bracket or any of the hyphens are optional.
// It is only used to extract the main bytes to create a UUID,
// so these imperfections are of no consequence.
hexPattern = `^(urn\:uuid\:)?[\{\(\[]?([[:xdigit:]]{8})-?([[:xdigit:]]{4})-?([1-5][[:xdigit:]]{3})-?([[:xdigit:]]{4})-?([[:xdigit:]]{12})[\]\}\)]?$`
)
var (
parseUUIDRegex = regexp.MustCompile(hexPattern)
)
// Parse creates a UUID from a valid string representation.
// Accepts UUID string in following formats:
// 6ba7b8149dad11d180b400c04fd430c8
// 6ba7b814-9dad-11d1-80b4-00c04fd430c8
// {6ba7b814-9dad-11d1-80b4-00c04fd430c8}
// urn:uuid:6ba7b814-9dad-11d1-80b4-00c04fd430c8
// [6ba7b814-9dad-11d1-80b4-00c04fd430c8]
// (6ba7b814-9dad-11d1-80b4-00c04fd430c8)
//
func Parse(uuid string) (*UUID, error) {
id, err := parse(uuid)
if err != nil {
return nil, err
}
a := UUID{}
a.unmarshal(id)
return &a, nil
}
func parse(uuid string) ([]byte, error) {
md := parseUUIDRegex.FindStringSubmatch(uuid)
if md == nil {
return nil, errors.New("uuid: invalid string format this is probably not a UUID")
}
return fromHex(md[2] + md[3] + md[4] + md[5] + md[6]), nil
}
func fromHex(uuid string) []byte {
bytes, err := hex.DecodeString(uuid)
if err != nil {
panic(err)
}
return bytes
}
// NewV1 generates a new RFC4122 version 1 UUID based on a 60 bit timestamp and
// node ID.
func NewV1() UUID {
return generator.NewV1()
}
// BulkV1 will return a slice of V1 UUIDs. Be careful with the set amount.
func BulkV1(amount int) []UUID {
return generator.BulkV1(amount)
}
// ReadV1 will read a slice of UUIDs. Be careful with the set amount.
func ReadV1(ids []UUID) {
generator.ReadV1(ids)
}
// NewV2 generates a new DCE Security version UUID based on a 60 bit timestamp,
// node id and POSIX UID.
func NewV2(pDomain SystemId) UUID {
return generator.NewV2(pDomain)
}
// NewV3 generates a new RFC4122 version 3 UUID based on the MD5 hash of a
// namespace UUID namespace Implementation UUID and one or more unique names.
func NewV3(namespace Implementation, names ...interface{}) UUID {
return generator.NewV3(namespace, names...)
}
// NewV4 generates a new RFC4122 version 4 UUID a cryptographically secure
// random UUID.
func NewV4() UUID {
return generator.NewV4()
}
// ReadV4 will read into a slice of UUIDs. Be careful with the set amount.
// Note: V4 UUIDs require sufficient entropy from the generator.
// If n == len(ids) err will be nil.
func ReadV4(ids []UUID) {
generator.ReadV4(ids)
}
// BulkV4 will return a slice of V4 UUIDs. Be careful with the set amount.
// Note: V4 UUIDs require sufficient entropy from the generator.
// If n == len(ids) err will be nil.
func BulkV4(amount int) []UUID {
return generator.BulkV4(amount)
}
// NewV5 generates an RFC4122 version 5 UUID based on the SHA-1 hash of a
// namespace Implementation UUID and one or more unique names.
func NewV5(namespace Implementation, names ...interface{}) UUID {
return generator.NewV5(namespace, names...)
}
// NewHash generate a UUID based on the given hash implementation. The hash will
// be of the given names. The version will be set to 0 for Unknown and the
// variant will be set to VariantFuture.
func NewHash(hash hash.Hash, names ...interface{}) UUID {
return generator.NewHash(hash, names...)
}
// Compare returns an integer comparing two Implementation UUIDs
// lexicographically.
// The result will be 0 if pId==pId2, -1 if pId < pId2, and +1 if pId > pId2.
// A nil argument is equivalent to the Nil Immutable UUID.
func Compare(pId, pId2 Implementation) int {
var b1, b2 = []byte(Nil), []byte(Nil)
if pId != nil {
b1 = pId.Bytes()
}
if pId2 != nil {
b2 = pId2.Bytes()
}
// Compare the time low bytes
tl1 := binary.BigEndian.Uint32(b1[:4])
tl2 := binary.BigEndian.Uint32(b2[:4])
if tl1 != tl2 {
if tl1 < tl2 {
return -1
}
return 1
}
// Compare the time hi and ver bytes
m1 := binary.BigEndian.Uint16(b1[4:6])
m2 := binary.BigEndian.Uint16(b2[4:6])
if m1 != m2 {
if m1 < m2 {
return -1
}
return 1
}
// Compare the sequence and version
m1 = binary.BigEndian.Uint16(b1[6:8])
m2 = binary.BigEndian.Uint16(b2[6:8])
if m1 != m2 {
if m1 < m2 {
return -1
}
return 1
}
// Compare the node id
return bytes.Compare(b1[8:], b2[8:])
}
// Equal compares whether each Implementation UUID is the same
func Equal(p1, p2 Implementation) bool {
return bytes.Equal(p1.Bytes(), p2.Bytes())
}
// IsNil returns true if Implementation UUID is all zeros?
func IsNil(uuid Implementation) bool {
if uuid == nil {
return true
}
for _, v := range uuid.Bytes() {
if v != 0 {
return false
}
}
return true
}

View File

@@ -1,71 +0,0 @@
package uuid
// Version represents the type of UUID.
type Version int
// The following are the supported Versions.
const (
VersionUnknown Version = iota // Unknown
VersionOne // Time based
VersionTwo // DCE security via POSIX UIDs
VersionThree // Namespace hash uses MD5
VersionFour // Crypto random
VersionFive // Namespace hash uses SHA-1
)
// The following are the supported Variants.
const (
VariantNCS uint8 = 0x00
VariantRFC4122 uint8 = 0x80 // or and A0 if masked with 1F
VariantMicrosoft uint8 = 0xC0
VariantFuture uint8 = 0xE0
)
const (
// 3f used by RFC4122 although 1f works for all
variantSet = 0x3f
// rather than using 0xc0 we use 0xe0 to retrieve the variant
// The result is the same for all other variants
// 0x80 and 0xa0 are used to identify RFC4122 compliance
variantGet = 0xe0
)
// String returns English description of version.
func (o Version) String() string {
switch o {
case VersionOne:
return "Version 1: Based on a 60 Bit Timestamp."
case VersionTwo:
return "Version 2: Based on DCE security domain and 60 bit timestamp."
case VersionThree:
return "Version 3: Namespace UUID and unique names hashed by MD5."
case VersionFour:
return "Version 4: Crypto-random generated."
case VersionFive:
return "Version 5: Namespace UUID and unique names hashed by SHA-1."
default:
return "Unknown: Not supported"
}
}
func resolveVersion(version uint8) Version {
switch Version(version) {
case VersionOne, VersionTwo, VersionThree, VersionFour, VersionFive:
return Version(version)
default:
return VersionUnknown
}
}
func variant(variant uint8) uint8 {
switch variant & variantGet {
case VariantRFC4122, 0xA0:
return VariantRFC4122
case VariantMicrosoft:
return VariantMicrosoft
case VariantFuture:
return VariantFuture
}
return VariantNCS
}

2
vendor/modules.txt vendored
View File

@@ -18,7 +18,5 @@ github.com/mattn/go-sqlite3
# github.com/mozillazg/go-unidecode v0.1.1 # github.com/mozillazg/go-unidecode v0.1.1
github.com/mozillazg/go-unidecode github.com/mozillazg/go-unidecode
github.com/mozillazg/go-unidecode/table github.com/mozillazg/go-unidecode/table
# github.com/twinj/uuid v1.0.0
github.com/twinj/uuid
# github.com/wader/gormstore v0.0.0-20190302154359-acb787ba3755 # github.com/wader/gormstore v0.0.0-20190302154359-acb787ba3755
github.com/wader/gormstore github.com/wader/gormstore