├── go.mod ├── README.md ├── .gitignore ├── goid_test.go ├── LICENSE └── goid.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/fn-code/goid 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # goid 2 | goid is generate 20 byte random id inspired from firebase realtime id 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /goid_test.go: -------------------------------------------------------------------------------- 1 | package goid 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var lastId GID 8 | 9 | func TestNew(t *testing.T) { 10 | for i := 0; i < 100000; i++ { 11 | new := New() 12 | if new.String() == lastId.String() { 13 | t.Errorf("error generate unique new: %v - last: %v", new, lastId) 14 | } 15 | lastId = new 16 | } 17 | } 18 | 19 | func BenchmarkNew(b *testing.B) { 20 | for i := 0; i < b.N; i++ { 21 | New() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Fun Code 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /goid.go: -------------------------------------------------------------------------------- 1 | package goid 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | const ( 9 | // Base64 web-safe chars, but ordered by ASCII. 10 | charID = "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" 11 | ) 12 | 13 | // GID is convert byte to string 14 | type GID string 15 | 16 | var lastTime int64 17 | 18 | // New is used for generate 20 random id base on base64 web save chars 19 | func New() GID { 20 | id := [20]byte{} 21 | timeMs := time.Now().UTC().UnixNano() / 1e6 22 | lastID := generateLastID(timeMs, lastTime) 23 | 24 | lastTime = timeMs 25 | // generate last 12 byte of id 26 | for i := 0; i < 12; i++ { 27 | id[(len(id)-1)-i] = charID[lastID[i]] 28 | } 29 | 30 | // Genererate first 8 byte of id 31 | for i := 7; i >= 0; i-- { 32 | n := int(timeMs % 64) 33 | id[i] = charID[n] 34 | timeMs = timeMs / 64 35 | } 36 | return GID(id[:]) 37 | } 38 | 39 | func (s GID) String() string { 40 | return string(s) 41 | } 42 | 43 | var lastIDS [12]int 44 | 45 | func generateLastID(timeMs, lastTime int64) [12]int { 46 | var randNumber *rand.Rand 47 | randNumber = rand.New(rand.NewSource(time.Now().UTC().UnixNano())) 48 | if timeMs == lastTime { 49 | // incremen last rand id 50 | lastIDS = inLastRandID(lastIDS) 51 | } else { 52 | for i := 0; i < len(lastIDS); i++ { 53 | lastIDS[i] = randNumber.Intn(64) 54 | } 55 | } 56 | return lastIDS 57 | } 58 | 59 | func inLastRandID(r [12]int) [12]int { 60 | for i := 0; i < 12; i++ { 61 | r[i]++ 62 | if r[i] < 64 { 63 | break 64 | } 65 | r[i] = 0 66 | } 67 | return r 68 | } 69 | --------------------------------------------------------------------------------