pax_global_header00006660000000000000000000000064130374745330014523gustar00rootroot0000000000000052 comment=66bb6560562feca7045b23db1ae85b01260f87c5 golang-github-oklog-ulid-0.3.0+git20170117.66bb656/000077500000000000000000000000001303747453300207175ustar00rootroot00000000000000golang-github-oklog-ulid-0.3.0+git20170117.66bb656/.gitignore000066400000000000000000000005111303747453300227040ustar00rootroot00000000000000#### joe made this: http://goel.io/joe #####=== Go ===##### # Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof golang-github-oklog-ulid-0.3.0+git20170117.66bb656/.travis.yml000066400000000000000000000007161303747453300230340ustar00rootroot00000000000000language: go sudo: false go: - 1.7.4 - tip install: - go get -v github.com/golang/lint/golint - go get golang.org/x/tools/cmd/cover - go get github.com/mattn/goveralls - go get -d -t -v ./... - go build -v ./... script: - go vet ./... - $HOME/gopath/bin/golint . - go test -v -race ./... - go test -v -covermode=count -coverprofile=cov.out - $HOME/gopath/bin/goveralls -coverprofile=cov.out -service=travis-ci -repotoken $COVERALLS_TOKEN golang-github-oklog-ulid-0.3.0+git20170117.66bb656/AUTHORS.md000066400000000000000000000000731303747453300223660ustar00rootroot00000000000000- Peter Bourgon (@peterbourgon) - Tomás Senart (@tsenart) golang-github-oklog-ulid-0.3.0+git20170117.66bb656/CHANGELOG.md000066400000000000000000000003271303747453300225320ustar00rootroot00000000000000## 0.3.0 / 2017-01-03 * Implement ULID.Compare method ## 0.2.0 / 2016-12-13 * Remove year 2262 Timestamp bug. (#1) * Gracefully handle invalid encodings when parsing. ## 0.1.0 / 2016-12-06 * First ULID release golang-github-oklog-ulid-0.3.0+git20170117.66bb656/CONTRIBUTING.md000066400000000000000000000014021303747453300231450ustar00rootroot00000000000000# Contributing We use GitHub to manage reviews of pull requests. * If you have a trivial fix or improvement, go ahead and create a pull request, addressing (with `@...`) one or more of the maintainers (see [AUTHORS.md](AUTHORS.md)) in the description of the pull request. * If you plan to do something more involved, first propose your ideas in a Github issue. This will avoid unnecessary work and surely give you and us a good deal of inspiration. * Relevant coding style guidelines are the [Go Code Review Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments) and the _Formatting and style_ section of Peter Bourgon's [Go: Best Practices for Production Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style). golang-github-oklog-ulid-0.3.0+git20170117.66bb656/LICENSE000066400000000000000000000261351303747453300217330ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. golang-github-oklog-ulid-0.3.0+git20170117.66bb656/README.md000066400000000000000000000147031303747453300222030ustar00rootroot00000000000000# Universally Unique Lexicographically Sortable Identifier ![Project status](https://img.shields.io/badge/version-0.3.0-yellow.svg) [![Build Status](https://secure.travis-ci.org/oklog/ulid.png)](http://travis-ci.org/oklog/ulid) [![Go Report Card](https://goreportcard.com/badge/oklog/ulid?cache=0)](https://goreportcard.com/report/oklog/ulid) [![Coverage Status](https://coveralls.io/repos/github/oklog/ulid/badge.svg?branch=master&cache=0)](https://coveralls.io/github/oklog/ulid?branch=master) [![GoDoc](https://godoc.org/github.com/oklog/ulid?status.svg)](https://godoc.org/github.com/oklog/ulid) [![Apache 2 licensed](https://img.shields.io/badge/license-Apache2-blue.svg)](https://raw.githubusercontent.com/oklog/ulid/master/LICENSE) A Go port of [alizain/ulid](https://github.com/alizain/ulid) with binary format implemented. ## Background A GUID/UUID can be suboptimal for many use-cases because: - It isn't the most character efficient way of encoding 128 bits - UUID v1/v2 is impractical in many environments, as it requires access to a unique, stable MAC address - UUID v3/v5 requires a unique seed and produces randomly distributed IDs, which can cause fragmentation in many data structures - UUID v4 provides no other information than randomness which can cause fragmentation in many data structures A ULID however: - Is compatible with UUID/GUID's - 1.21e+24 unique ULIDs per millisecond (1,208,925,819,614,629,174,706,176 to be exact) - Lexicographically sortable - Canonically encoded as a 26 character string, as opposed to the 36 character UUID - Uses Crockford's base32 for better efficiency and readability (5 bits per character) - Case insensitive - No special characters (URL safe) ## Install ```shell go get github.com/oklog/ulid ``` ## Usage An ULID is constructed with a `time.Time` and an `io.Reader` entropy source. This design allows for greater flexibility in choosing your trade-offs. Please note that `rand.Rand` from the `math` package is *not* safe for concurrent use. Instantiate one per long living go-routine or use a `sync.Pool` if you want to avoid the potential contention of a locked `rand.Source` as its been frequently observed in the package level functions. ```go func ExampleULID() { t := time.Unix(1000000, 0) entropy := rand.New(rand.NewSource(t.UnixNano())) fmt.Println(ulid.MustNew(ulid.Timestamp(t), entropy)) // Output: 0000XSNJG0MQJHBF4QX1EFD6Y3 } ``` ## Specification Below is the current specification of ULID as implemented in this repository. ### Components **Timestamp** - 48 bits - UNIX-time in milliseconds - Won't run out of space till the year 10895 AD **Entropy** - 80 bits - User defined entropy source. ### Encoding [Crockford's Base32](http://www.crockford.com/wrmg/base32.html) is used as shown. This alphabet excludes the letters I, L, O, and U to avoid confusion and abuse. ``` 0123456789ABCDEFGHJKMNPQRSTVWXYZ ``` ### Binary Layout and Byte Order The components are encoded as 16 octets. Each component is encoded with the Most Significant Byte first (network byte order). ``` 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 32_bit_uint_time_high | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 16_bit_uint_time_low | 16_bit_uint_random | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 32_bit_uint_random | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 32_bit_uint_random | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ``` ### String Representation ``` 01AN4Z07BY 79KA1307SR9X4MV3 |----------| |----------------| Timestamp Entropy 10 chars 16 chars 48bits 80bits base32 base32 ``` ## Test ```shell go test ./... ``` ## Benchmarks On a Intel Core i7 Ivy Bridge 2.7 GHz, MacOS 10.12.1 and Go 1.8.0beta1 ``` BenchmarkNew/WithCryptoEntropy-8 2000000 771 ns/op 20.73 MB/s 16 B/op 1 allocs/op BenchmarkNew/WithEntropy-8 20000000 65.8 ns/op 243.01 MB/s 16 B/op 1 allocs/op BenchmarkNew/WithoutEntropy-8 50000000 30.0 ns/op 534.06 MB/s 16 B/op 1 allocs/op BenchmarkMustNew/WithCryptoEntropy-8 2000000 781 ns/op 20.48 MB/s 16 B/op 1 allocs/op BenchmarkMustNew/WithEntropy-8 20000000 70.0 ns/op 228.51 MB/s 16 B/op 1 allocs/op BenchmarkMustNew/WithoutEntropy-8 50000000 34.6 ns/op 462.98 MB/s 16 B/op 1 allocs/op BenchmarkParse-8 50000000 30.0 ns/op 866.16 MB/s 0 B/op 0 allocs/op BenchmarkMustParse-8 50000000 35.2 ns/op 738.94 MB/s 0 B/op 0 allocs/op BenchmarkString-8 20000000 64.9 ns/op 246.40 MB/s 32 B/op 1 allocs/op BenchmarkMarshal/Text-8 20000000 55.8 ns/op 286.84 MB/s 32 B/op 1 allocs/op BenchmarkMarshal/TextTo-8 100000000 22.4 ns/op 714.91 MB/s 0 B/op 0 allocs/op BenchmarkMarshal/Binary-8 300000000 4.02 ns/op 3981.77 MB/s 0 B/op 0 allocs/op BenchmarkMarshal/BinaryTo-8 2000000000 1.18 ns/op 13551.75 MB/s 0 B/op 0 allocs/op BenchmarkUnmarshal/Text-8 100000000 20.5 ns/op 1265.27 MB/s 0 B/op 0 allocs/op BenchmarkUnmarshal/Binary-8 300000000 4.94 ns/op 3240.01 MB/s 0 B/op 0 allocs/op BenchmarkNow-8 100000000 15.1 ns/op 528.09 MB/s 0 B/op 0 allocs/op BenchmarkTimestamp-8 2000000000 0.29 ns/op 27271.59 MB/s 0 B/op 0 allocs/op BenchmarkTime-8 2000000000 0.58 ns/op 13717.80 MB/s 0 B/op 0 allocs/op BenchmarkSetTime-8 2000000000 0.89 ns/op 9023.95 MB/s 0 B/op 0 allocs/op BenchmarkEntropy-8 200000000 7.62 ns/op 1311.66 MB/s 0 B/op 0 allocs/op BenchmarkSetEntropy-8 2000000000 0.88 ns/op 11376.54 MB/s 0 B/op 0 allocs/op BenchmarkCompare-8 200000000 7.34 ns/op 4359.23 MB/s 0 B/op 0 allocs/op ``` ## Prior Art - [alizain/ulid](https://github.com/alizain/ulid) - [RobThree/NUlid](https://github.com/RobThree/NUlid) - [imdario/go-ulid](https://github.com/imdario/go-ulid) golang-github-oklog-ulid-0.3.0+git20170117.66bb656/ulid.go000066400000000000000000000263361303747453300222150ustar00rootroot00000000000000// Copyright 2016 The Oklog Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ulid import ( "bytes" "errors" "io" "time" ) /* An ULID is a 16 byte Universally Unique Lexicographically Sortable Identifier The components are encoded as 16 octets. Each component is encoded with the MSB first (network byte order). 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 32_bit_uint_time_high | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 16_bit_uint_time_low | 16_bit_uint_random | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 32_bit_uint_random | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 32_bit_uint_random | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ type ULID [16]byte var ( // ErrDataSize is returned when parsing or unmarshaling ULIDs with the wrong // data size. ErrDataSize = errors.New("ulid: bad data size when unmarshaling") // ErrBufferSize is returned when marshalling ULIDs to a buffer of insufficient // size. ErrBufferSize = errors.New("ulid: bad buffer size when marshaling") // ErrBigTime is returned when constructing an ULID with a time that is larger // than MaxTime. ErrBigTime = errors.New("ulid: time too big") ) // New returns an ULID with the given Unix milliseconds timestamp and an // optional entropy source. Use the Timestamp function to convert // a time.Time to Unix milliseconds. // // ErrBigTime is returned when passing a timestamp bigger than MaxTime. // Reading from the entropy source may also return an error. func New(ms uint64, entropy io.Reader) (id ULID, err error) { if err = id.SetTime(ms); err != nil { return id, err } if entropy != nil { _, err = entropy.Read(id[6:]) } return id, err } // MustNew is a convenience function equivalent to New that panics on failure // instead of returning an error. func MustNew(ms uint64, entropy io.Reader) ULID { id, err := New(ms, entropy) if err != nil { panic(err) } return id } // Parse parses an encoded ULID, returning an error in case of failure. // // ErrDataSize is returned if the len(ulid) is different from an encoded // ULID's length. Invalid encodings produce undefined ULIDs. func Parse(ulid string) (id ULID, err error) { return id, id.UnmarshalText([]byte(ulid)) } // MustParse is a convenience function equivalent to Parse that panics on failure // instead of returning an error. func MustParse(ulid string) ULID { id, err := Parse(ulid) if err != nil { panic(err) } return id } // String returns a lexicographically sortable string encoded ULID // (26 characters, non-standard base 32) e.g. 01AN4Z07BY79KA1307SR9X4MV3 // Format: tttttttttteeeeeeeeeeeeeeee where t is time and e is entropy func (id ULID) String() string { ulid := make([]byte, EncodedSize) _ = id.MarshalTextTo(ulid) return string(ulid) } // MarshalBinary implements the encoding.BinaryMarshaler interface by // returning the ULID as a byte slice. func (id ULID) MarshalBinary() ([]byte, error) { ulid := make([]byte, len(id)) return ulid, id.MarshalBinaryTo(ulid) } // MarshalBinaryTo writes the binary encoding of the ULID to the given buffer. // ErrBufferSize is returned when the len(dst) != 16. func (id ULID) MarshalBinaryTo(dst []byte) error { if len(dst) != len(id) { return ErrBufferSize } copy(dst, id[:]) return nil } // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface by // copying the passed data and converting it to an ULID. ErrDataSize is // returned if the data length is different from ULID length. func (id *ULID) UnmarshalBinary(data []byte) error { if len(data) != len(*id) { return ErrDataSize } copy((*id)[:], data) return nil } // Encoding is the base 32 encoding alphabet used in ULID strings. const Encoding = "0123456789ABCDEFGHJKMNPQRSTVWXYZ" // MarshalText implements the encoding.TextMarshaler interface by // returning the string encoded ULID. func (id ULID) MarshalText() ([]byte, error) { ulid := make([]byte, EncodedSize) return ulid, id.MarshalTextTo(ulid) } // MarshalTextTo writes the ULID as a string to the given buffer. // ErrBufferSize is returned when the len(dst) != 26. func (id ULID) MarshalTextTo(dst []byte) error { // Optimized unrolled loop ahead. // From https://github.com/RobThree/NUlid if len(dst) != EncodedSize { return ErrBufferSize } // 10 byte timestamp dst[0] = Encoding[(id[0]&224)>>5] dst[1] = Encoding[id[0]&31] dst[2] = Encoding[(id[1]&248)>>3] dst[3] = Encoding[((id[1]&7)<<2)|((id[2]&192)>>6)] dst[4] = Encoding[(id[2]&62)>>1] dst[5] = Encoding[((id[2]&1)<<4)|((id[3]&240)>>4)] dst[6] = Encoding[((id[3]&15)<<1)|((id[4]&128)>>7)] dst[7] = Encoding[(id[4]&124)>>2] dst[8] = Encoding[((id[4]&3)<<3)|((id[5]&224)>>5)] dst[9] = Encoding[id[5]&31] // 16 bytes of entropy dst[10] = Encoding[(id[6]&248)>>3] dst[11] = Encoding[((id[6]&7)<<2)|((id[7]&192)>>6)] dst[12] = Encoding[(id[7]&62)>>1] dst[13] = Encoding[((id[7]&1)<<4)|((id[8]&240)>>4)] dst[14] = Encoding[((id[8]&15)<<1)|((id[9]&128)>>7)] dst[15] = Encoding[(id[9]&124)>>2] dst[16] = Encoding[((id[9]&3)<<3)|((id[10]&224)>>5)] dst[17] = Encoding[id[10]&31] dst[18] = Encoding[(id[11]&248)>>3] dst[19] = Encoding[((id[11]&7)<<2)|((id[12]&192)>>6)] dst[20] = Encoding[(id[12]&62)>>1] dst[21] = Encoding[((id[12]&1)<<4)|((id[13]&240)>>4)] dst[22] = Encoding[((id[13]&15)<<1)|((id[14]&128)>>7)] dst[23] = Encoding[(id[14]&124)>>2] dst[24] = Encoding[((id[14]&3)<<3)|((id[15]&224)>>5)] dst[25] = Encoding[id[15]&31] return nil } // Byte to index table for O(1) lookups when unmarshaling. // We use 0xFF as sentinel value for invalid indexes. var dec = [...]byte{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, } // EncodedSize is the length of a text encoded ULID. const EncodedSize = 26 // UnmarshalText implements the encoding.TextUnmarshaler interface by // parsing the data as string encoded ULID. // // ErrDataSize is returned if the len(v) is different from an encoded // ULID's length. Invalid encodings produce undefined ULIDs. func (id *ULID) UnmarshalText(v []byte) error { // Optimized unrolled loop ahead. // From https://github.com/RobThree/NUlid if len(v) != EncodedSize { return ErrDataSize } // 6 bytes timestamp (48 bits) (*id)[0] = ((dec[v[0]] << 5) | dec[v[1]]) (*id)[1] = ((dec[v[2]] << 3) | (dec[v[3]] >> 2)) (*id)[2] = ((dec[v[3]] << 6) | (dec[v[4]] << 1) | (dec[v[5]] >> 4)) (*id)[3] = ((dec[v[5]] << 4) | (dec[v[6]] >> 1)) (*id)[4] = ((dec[v[6]] << 7) | (dec[v[7]] << 2) | (dec[v[8]] >> 3)) (*id)[5] = ((dec[v[8]] << 5) | dec[v[9]]) // 10 bytes of entropy (80 bits) (*id)[6] = ((dec[v[10]] << 3) | (dec[v[11]] >> 2)) (*id)[7] = ((dec[v[11]] << 6) | (dec[v[12]] << 1) | (dec[v[13]] >> 4)) (*id)[8] = ((dec[v[13]] << 4) | (dec[v[14]] >> 1)) (*id)[9] = ((dec[v[14]] << 7) | (dec[v[15]] << 2) | (dec[v[16]] >> 3)) (*id)[10] = ((dec[v[16]] << 5) | dec[v[17]]) (*id)[11] = ((dec[v[18]] << 3) | dec[v[19]]>>2) (*id)[12] = ((dec[v[19]] << 6) | (dec[v[20]] << 1) | (dec[v[21]] >> 4)) (*id)[13] = ((dec[v[21]] << 4) | (dec[v[22]] >> 1)) (*id)[14] = ((dec[v[22]] << 7) | (dec[v[23]] << 2) | (dec[v[24]] >> 3)) (*id)[15] = ((dec[v[24]] << 5) | dec[v[25]]) return nil } // Time returns the Unix time in milliseconds encoded in the ULID. func (id ULID) Time() uint64 { return uint64(id[5]) | uint64(id[4])<<8 | uint64(id[3])<<16 | uint64(id[2])<<24 | uint64(id[1])<<32 | uint64(id[0])<<40 } // maxTime is the maximum Unix time in milliseconds that can be // represented in an ULID. var maxTime = ULID{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}.Time() // MaxTime returns the maximum Unix time in milliseconds that // can be encoded in an ULID. func MaxTime() uint64 { return maxTime } // Now is a convenience function that returns the current // UTC time in Unix milliseconds. Equivalent to: // Timestamp(time.Now().UTC()) func Now() uint64 { return Timestamp(time.Now().UTC()) } // Timestamp converts a time.Time to Unix milliseconds. // // Because of the way ULID stores time, times from the year // 10889 produces undefined results. func Timestamp(t time.Time) uint64 { return uint64(t.Unix())*1000 + uint64(t.Nanosecond()/int(time.Millisecond)) } // SetTime sets the time component of the ULID to the given Unix time // in milliseconds. func (id *ULID) SetTime(ms uint64) error { if ms > maxTime { return ErrBigTime } (*id)[0] = byte(ms >> 40) (*id)[1] = byte(ms >> 32) (*id)[2] = byte(ms >> 24) (*id)[3] = byte(ms >> 16) (*id)[4] = byte(ms >> 8) (*id)[5] = byte(ms) return nil } // Entropy returns the entropy from the ULID. func (id ULID) Entropy() []byte { e := make([]byte, 10) copy(e, id[6:]) return e } // SetEntropy sets the ULID entropy to the passed byte slice. // ErrDataSize is returned if len(e) != 10. func (id *ULID) SetEntropy(e []byte) error { if len(e) != 10 { return ErrDataSize } copy((*id)[6:], e) return nil } // Compare returns an integer comparing id and other lexicographically. // The result will be 0 if id==other, -1 if id < other, and +1 if id > other. func (id ULID) Compare(other ULID) int { return bytes.Compare(id[:], other[:]) } golang-github-oklog-ulid-0.3.0+git20170117.66bb656/ulid_test.go000066400000000000000000000277171303747453300232600ustar00rootroot00000000000000// Copyright 2016 The Oklog Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ulid_test import ( "bytes" crand "crypto/rand" "fmt" "io" "math/rand" "strings" "testing" "testing/quick" "time" "github.com/oklog/ulid" ) func ExampleULID() { t := time.Unix(1000000, 0) entropy := rand.New(rand.NewSource(t.UnixNano())) fmt.Println(ulid.MustNew(ulid.Timestamp(t), entropy)) // Output: 0000XSNJG0MQJHBF4QX1EFD6Y3 } func TestNew(t *testing.T) { t.Parallel() t.Run("ULID", testULID(func(ms uint64, e io.Reader) ulid.ULID { id, err := ulid.New(ms, e) if err != nil { t.Fatal(err) } return id })) t.Run("Error", func(t *testing.T) { _, err := ulid.New(ulid.MaxTime()+1, nil) if got, want := err, ulid.ErrBigTime; got != want { t.Errorf("got err %v, want %v", got, want) } _, err = ulid.New(0, strings.NewReader("")) if got, want := err, io.EOF; got != want { t.Errorf("got err %v, want %v", got, want) } }) } func TestMustNew(t *testing.T) { t.Parallel() t.Run("ULID", testULID(ulid.MustNew)) t.Run("Panic", func(t *testing.T) { defer func() { if got, want := recover(), io.EOF; got != want { t.Errorf("panic with err %v, want %v", got, want) } }() _ = ulid.MustNew(0, strings.NewReader("")) }) } func TestMustParse(t *testing.T) { t.Parallel() defer func() { if got, want := recover(), ulid.ErrDataSize; got != want { t.Errorf("got panic %v, want %v", got, want) } }() _ = ulid.MustParse("") } func testULID(mk func(uint64, io.Reader) ulid.ULID) func(*testing.T) { return func(t *testing.T) { want := ulid.ULID{0x0, 0x0, 0x0, 0x1, 0x86, 0xa0} if got := mk(1e5, nil); got != want { // optional entropy t.Errorf("\ngot %#v\nwant %#v", got, want) } entropy := bytes.Repeat([]byte{0xFF}, 16) copy(want[6:], entropy) if got := mk(1e5, bytes.NewReader(entropy)); got != want { t.Errorf("\ngot %#v\nwant %#v", got, want) } } } func TestRoundTrips(t *testing.T) { t.Parallel() prop := func(id ulid.ULID) bool { bin, err := id.MarshalBinary() if err != nil { t.Fatal(err) } txt, err := id.MarshalText() if err != nil { t.Fatal(err) } var a ulid.ULID if err = a.UnmarshalBinary(bin); err != nil { t.Fatal(err) } var b ulid.ULID if err = b.UnmarshalText(txt); err != nil { t.Fatal(err) } return id == a && b == id && id == ulid.MustParse(id.String()) } err := quick.Check(prop, &quick.Config{MaxCount: 1E5}) if err != nil { t.Fatal(err) } } func TestMarshalingErrors(t *testing.T) { t.Parallel() var id ulid.ULID for _, tc := range []struct { name string fn func([]byte) error err error }{ {"UnmarshalBinary", id.UnmarshalBinary, ulid.ErrDataSize}, {"UnmarshalText", id.UnmarshalText, ulid.ErrDataSize}, {"MarshalBinaryTo", id.MarshalBinaryTo, ulid.ErrBufferSize}, {"MarshalTextTo", id.MarshalTextTo, ulid.ErrBufferSize}, } { t.Run(tc.name, func(t *testing.T) { if got, want := tc.fn([]byte{}), tc.err; got != want { t.Errorf("got err %v, want %v", got, want) } }) } } func TestAlizainCompatibility(t *testing.T) { t.Parallel() ts := uint64(1469918176385) got := ulid.MustNew(ts, bytes.NewReader(make([]byte, 16))) want := ulid.MustParse("01ARYZ6S410000000000000000") if got != want { t.Fatalf("with time=%d, got %q, want %q", ts, got, want) } } func TestEncoding(t *testing.T) { t.Parallel() enc := make(map[rune]bool, len(ulid.Encoding)) for _, r := range ulid.Encoding { enc[r] = true } prop := func(id ulid.ULID) bool { for _, r := range id.String() { if !enc[r] { return false } } return true } if err := quick.Check(prop, &quick.Config{MaxCount: 1E5}); err != nil { t.Fatal(err) } } func TestLexicographicalOrder(t *testing.T) { t.Parallel() prop := func(a, b ulid.ULID) bool { t1, t2 := a.Time(), b.Time() s1, s2 := a.String(), b.String() ord := bytes.Compare(a[:], b[:]) return t1 == t2 || (t1 > t2 && s1 > s2 && ord == +1) || (t1 < t2 && s1 < s2 && ord == -1) } top := ulid.MustNew(ulid.MaxTime(), nil) for i := 0; i < 10; i++ { // test upper boundary state space next := ulid.MustNew(top.Time()-1, nil) if !prop(top, next) { t.Fatalf("bad lexicographical order: (%v, %q) > (%v, %q) == false", top.Time(), top, next.Time(), next, ) } top = next } if err := quick.Check(prop, &quick.Config{MaxCount: 1E6}); err != nil { t.Fatal(err) } } func TestCaseInsensitivity(t *testing.T) { t.Parallel() upper := func(id ulid.ULID) (out ulid.ULID) { return ulid.MustParse(strings.ToUpper(id.String())) } lower := func(id ulid.ULID) (out ulid.ULID) { return ulid.MustParse(strings.ToLower(id.String())) } err := quick.CheckEqual(upper, lower, nil) if err != nil { t.Fatal(err) } } func TestParseRobustness(t *testing.T) { t.Parallel() cases := [][]byte{ {0x1, 0xc0, 0x73, 0x62, 0x4a, 0xaf, 0x39, 0x78, 0x51, 0x4e, 0xf8, 0x44, 0x3b, 0xb2, 0xa8, 0x59, 0xc7, 0x5f, 0xc3, 0xcc, 0x6a, 0xf2, 0x6d, 0x5a, 0xaa, 0x20}, } for _, tc := range cases { if _, err := ulid.Parse(string(tc)); err != nil { t.Error(err) } } prop := func(s [26]byte) (ok bool) { defer func() { if err := recover(); err != nil { t.Error(err) ok = false } }() var err error if _, err = ulid.Parse(string(s[:])); err != nil { t.Error(err) } return err == nil } err := quick.Check(prop, &quick.Config{MaxCount: 1E4}) if err != nil { t.Fatal(err) } } func TestNow(t *testing.T) { t.Parallel() before := ulid.Now() after := ulid.Timestamp(time.Now().UTC().Add(time.Millisecond)) if before >= after { t.Fatalf("clock went mad: before %v, after %v", before, after) } } func TestTimestamp(t *testing.T) { t.Parallel() tm := time.Unix(1, 1000) // will be truncated if got, want := ulid.Timestamp(tm), uint64(1000); got != want { t.Errorf("for %v, got %v, want %v", tm, got, want) } mt := ulid.MaxTime() dt := time.Unix(int64(mt/1000), int64((mt%1000)*1000000)).Truncate(time.Millisecond) ts := ulid.Timestamp(dt) if got, want := ts, mt; got != want { t.Errorf("got timestamp %d, want %d", got, want) } } func TestTime(t *testing.T) { t.Parallel() maxTime := ulid.MaxTime() var id ulid.ULID if got, want := id.SetTime(maxTime+1), ulid.ErrBigTime; got != want { t.Errorf("got err %v, want %v", got, want) } rng := rand.New(rand.NewSource(time.Now().UnixNano())) for i := 0; i < 1e6; i++ { ms := uint64(rng.Int63n(int64(maxTime))) var id ulid.ULID if err := id.SetTime(ms); err != nil { t.Fatal(err) } if got, want := id.Time(), ms; got != want { t.Fatalf("\nfor %v:\ngot %v\nwant %v", id, got, want) } } } func TestEntropy(t *testing.T) { t.Parallel() var id ulid.ULID if got, want := id.SetEntropy([]byte{}), ulid.ErrDataSize; got != want { t.Errorf("got err %v, want %v", got, want) } prop := func(e [10]byte) bool { var id ulid.ULID if err := id.SetEntropy(e[:]); err != nil { t.Fatalf("got err %v", err) } got, want := id.Entropy(), e[:] eq := bytes.Equal(got, want) if !eq { t.Errorf("\n(!= %v\n %v)", got, want) } return eq } if err := quick.Check(prop, nil); err != nil { t.Fatal(err) } } func TestCompare(t *testing.T) { t.Parallel() a := func(a, b ulid.ULID) int { return strings.Compare(a.String(), b.String()) } b := func(a, b ulid.ULID) int { return a.Compare(b) } err := quick.CheckEqual(a, b, &quick.Config{MaxCount: 1E5}) if err != nil { t.Error(err) } } func BenchmarkNew(b *testing.B) { b.Run("WithCryptoEntropy", func(b *testing.B) { b.SetBytes(int64(len(ulid.ULID{}))) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = ulid.New(123, crand.Reader) } }) b.Run("WithEntropy", func(b *testing.B) { now := time.Now().UTC() entropy := rand.New(rand.NewSource(now.UnixNano())) b.SetBytes(int64(len(ulid.ULID{}))) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = ulid.New(123, entropy) } }) b.Run("WithoutEntropy", func(b *testing.B) { b.SetBytes(int64(len(ulid.ULID{}))) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = ulid.New(123, nil) } }) } func BenchmarkMustNew(b *testing.B) { b.Run("WithCryptoEntropy", func(b *testing.B) { b.SetBytes(int64(len(ulid.ULID{}))) b.ResetTimer() for i := 0; i < b.N; i++ { _ = ulid.MustNew(123, crand.Reader) } }) b.Run("WithEntropy", func(b *testing.B) { now := time.Now().UTC() entropy := rand.New(rand.NewSource(now.UnixNano())) b.SetBytes(int64(len(ulid.ULID{}))) b.ResetTimer() for i := 0; i < b.N; i++ { _ = ulid.MustNew(123, entropy) } }) b.Run("WithoutEntropy", func(b *testing.B) { b.SetBytes(int64(len(ulid.ULID{}))) b.ResetTimer() for i := 0; i < b.N; i++ { _ = ulid.MustNew(123, nil) } }) } func BenchmarkParse(b *testing.B) { const s = "0000XSNJG0MQJHBF4QX1EFD6Y3" b.SetBytes(int64(len(s))) for i := 0; i < b.N; i++ { _, _ = ulid.Parse(s) } } func BenchmarkMustParse(b *testing.B) { const s = "0000XSNJG0MQJHBF4QX1EFD6Y3" b.SetBytes(int64(len(s))) for i := 0; i < b.N; i++ { _ = ulid.MustParse(s) } } func BenchmarkString(b *testing.B) { entropy := rand.New(rand.NewSource(time.Now().UnixNano())) id := ulid.MustNew(123456, entropy) b.SetBytes(int64(len(id))) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.String() } } func BenchmarkMarshal(b *testing.B) { entropy := rand.New(rand.NewSource(time.Now().UnixNano())) buf := make([]byte, ulid.EncodedSize) id := ulid.MustNew(123456, entropy) b.Run("Text", func(b *testing.B) { b.SetBytes(int64(len(id))) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = id.MarshalText() } }) b.Run("TextTo", func(b *testing.B) { b.SetBytes(int64(len(id))) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.MarshalTextTo(buf) } }) b.Run("Binary", func(b *testing.B) { b.SetBytes(int64(len(id))) b.ResetTimer() for i := 0; i < b.N; i++ { _, _ = id.MarshalBinary() } }) b.Run("BinaryTo", func(b *testing.B) { b.SetBytes(int64(len(id))) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.MarshalBinaryTo(buf) } }) } func BenchmarkUnmarshal(b *testing.B) { var id ulid.ULID s := "0000XSNJG0MQJHBF4QX1EFD6Y3" txt := []byte(s) bin, _ := ulid.MustParse(s).MarshalBinary() b.Run("Text", func(b *testing.B) { b.SetBytes(int64(len(txt))) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.UnmarshalText(txt) } }) b.Run("Binary", func(b *testing.B) { b.SetBytes(int64(len(bin))) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.UnmarshalBinary(bin) } }) } func BenchmarkNow(b *testing.B) { b.SetBytes(8) b.ResetTimer() for i := 0; i < b.N; i++ { _ = ulid.Now() } } func BenchmarkTimestamp(b *testing.B) { now := time.Now() b.SetBytes(8) b.ResetTimer() for i := 0; i < b.N; i++ { _ = ulid.Timestamp(now) } } func BenchmarkTime(b *testing.B) { id := ulid.MustNew(123456789, nil) b.SetBytes(8) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.Time() } } func BenchmarkSetTime(b *testing.B) { var id ulid.ULID b.SetBytes(8) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.SetTime(123456789) } } func BenchmarkEntropy(b *testing.B) { id := ulid.MustNew(0, strings.NewReader("ABCDEFGHIJKLMNOP")) b.SetBytes(10) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.Entropy() } } func BenchmarkSetEntropy(b *testing.B) { var id ulid.ULID e := []byte("ABCDEFGHIJKLMNOP") b.SetBytes(10) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.SetEntropy(e) } } func BenchmarkCompare(b *testing.B) { id, other := ulid.MustNew(12345, nil), ulid.MustNew(54321, nil) b.SetBytes(int64(len(id) * 2)) b.ResetTimer() for i := 0; i < b.N; i++ { _ = id.Compare(other) } }