Compare commits
No commits in common. "54f5d55f2755cfc25812a749a4a7629ce37d6330" and "9fd2afadb73598849363a36b3e03b4159302a9cd" have entirely different histories.
54f5d55f27
...
9fd2afadb7
26
README.md
26
README.md
|
@ -1,27 +1,27 @@
|
||||||
# go-xim
|
# go-iid
|
||||||
|
|
||||||
Package **xim** provides quazi‐ monotonically‐increasing unique‐identifiers.
|
Package **iid** provides quazi‐ monotonically‐increasing unique‐identifiers.
|
||||||
|
|
||||||
The serialized form of the **xim-id** is safe to use as a _file_ or _directory_ name.
|
The serialized form of the **IID** is safe to use as a _file_ or _directory_ name.
|
||||||
|
|
||||||
|
|
||||||
## Documention
|
## Documention
|
||||||
|
|
||||||
Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-xim
|
Online documentation, which includes examples, can be found at: http://godoc.org/github.com/reiver/go-iid
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/reiver/go-xim?status.svg)](https://godoc.org/github.com/reiver/go-xim)
|
[![GoDoc](https://godoc.org/github.com/reiver/go-iid?status.svg)](https://godoc.org/github.com/reiver/go-iid)
|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Here is an example of using `package xim`:
|
Here is an example of using `package iid`:
|
||||||
```go
|
```go
|
||||||
var id xim.ID = xim.Generate()
|
var id iid.IID = iid.Generate()
|
||||||
```
|
```
|
||||||
|
|
||||||
## Representation
|
## Representation
|
||||||
|
|
||||||
Internally, the **xim-id** is compactly stored in an `uint64`. The anatomy of this is as follows:
|
Internally, the IID is compactly stored in an `uint64`. The anatomy of this is as follows:
|
||||||
```
|
```
|
||||||
unix timestamp (39-bits)
|
unix timestamp (39-bits)
|
||||||
▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
|
▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼
|
||||||
|
@ -30,11 +30,11 @@ Internally, the **xim-id** is compactly stored in an `uint64`. The anatomy of th
|
||||||
always zero (1-bit) chaos (20-bits)
|
always zero (1-bit) chaos (20-bits)
|
||||||
```
|
```
|
||||||
|
|
||||||
The `xim.ID.UnixTime()` method will give you that 39-bit _unix timestamp_.
|
The `iid.IID.UnixTime()` method will give you that 39-bit _unix timestamp_.
|
||||||
|
|
||||||
And the `xim.ID.Chaos()` method will give you that 24-bit _chaos_.
|
And the `iid.IID.Chaos()` method will give you that 24-bit _chaos_.
|
||||||
|
|
||||||
(The _chaos_ is just a randomness that helps make these **xim-ids** unique, when multiple **xim-ids** are being produced simultaneously.)
|
(The _chaos_ is just a randomness that helps make these IIDs unique, when multiple IIDs are being produced simultaneously.)
|
||||||
|
|
||||||
## Temporal Ordering of Representation
|
## Temporal Ordering of Representation
|
||||||
|
|
||||||
|
@ -203,7 +203,3 @@ The advantage of this is that lexical-ordering (in Unicode & ASCII) is also symb
|
||||||
One design goal is that lexical ordering of the **xim** strings is (almost always) also temporal ordering of the **xim** strings.
|
One design goal is that lexical ordering of the **xim** strings is (almost always) also temporal ordering of the **xim** strings.
|
||||||
|
|
||||||
Another design goal of it is that these **xim** strings should be able to be used as a file-name, or a directory-name.
|
Another design goal of it is that these **xim** strings should be able to be used as a file-name, or a directory-name.
|
||||||
|
|
||||||
## See Also:
|
|
||||||
|
|
||||||
* https://github.com/reiver/xim-id
|
|
||||||
|
|
10
compile.go
10
compile.go
|
@ -5,8 +5,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maskunixtime = 0b0111111111111111111111111111111111111111000000000000000000000000
|
maskfirst = 0b0111111111111111111111111111111111111111000000000000000000000000
|
||||||
maskchaos = 0b0000000000000000000000000000000000000000111111111111111111111111
|
masksecond = 0b0000000000000000000000000000000000000000111111111111111111111111
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -16,7 +16,7 @@ const (
|
||||||
|
|
||||||
func compile(first uint64, second uint64) uint64 {
|
func compile(first uint64, second uint64) uint64 {
|
||||||
|
|
||||||
var compiled uint64 = ((first << widthsecond) & maskunixtime) | (second & maskchaos)
|
var compiled uint64 = ((first << widthsecond) & maskfirst) | (second & masksecond)
|
||||||
|
|
||||||
return compiled
|
return compiled
|
||||||
|
|
||||||
|
@ -24,8 +24,8 @@ func compile(first uint64, second uint64) uint64 {
|
||||||
|
|
||||||
func decompile(value uint64) (uint64, uint64) {
|
func decompile(value uint64) (uint64, uint64) {
|
||||||
|
|
||||||
var first uint64 = (value & maskunixtime) >> widthsecond
|
var first uint64 = (value & maskfirst) >> widthsecond
|
||||||
var second uint64 = value & maskchaos
|
var second uint64 = value & masksecond
|
||||||
|
|
||||||
return first, second
|
return first, second
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,10 @@ var (
|
||||||
|
|
||||||
func generate() uint64 {
|
func generate() uint64 {
|
||||||
|
|
||||||
var now int64 = time.Now().Unix()
|
var now int64 = time.Now().Unix()
|
||||||
return generateforunixtime(now)
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateforunixtime(unixtimestamp int64) uint64 {
|
|
||||||
var chaos uint64 = randomness.Uint64()
|
var chaos uint64 = randomness.Uint64()
|
||||||
|
|
||||||
var value uint64 = compile(uint64(unixtimestamp), chaos)
|
var value uint64 = compile(uint64(now), chaos)
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
210
id.go
210
id.go
|
@ -1,210 +0,0 @@
|
||||||
package xim
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ID represents a xim-id.
|
|
||||||
//
|
|
||||||
// It is a scheme used to generate quazi‐monotonically‐increasing‐unique identifier.
|
|
||||||
//
|
|
||||||
// This can be used for anything that needs a unique-identifier. And its serialization should be safe to use as a file-name, or a directory-name.
|
|
||||||
//
|
|
||||||
// Example usage:
|
|
||||||
//
|
|
||||||
// var id xim.ID = xim.Generate()
|
|
||||||
//
|
|
||||||
// fmt.Println("xim-id =", id)
|
|
||||||
type ID struct {
|
|
||||||
value uint64
|
|
||||||
loaded bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID is an option-type. Nothing returns the nothing value for the option-type.
|
|
||||||
//
|
|
||||||
// An example usage might be:
|
|
||||||
//
|
|
||||||
// if xim.Nothing() == id {
|
|
||||||
// //@TODO
|
|
||||||
// }
|
|
||||||
func Nothing() ID {
|
|
||||||
return ID{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID is an option-type. something returns a something value for the option-type, that contains the value of the input variable ‘value’ inside of it.
|
|
||||||
func something(value uint64) ID {
|
|
||||||
return ID{
|
|
||||||
value:value,
|
|
||||||
loaded:true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse parses the string contained in the input-variable ‘ximnotation’, and if it contains a valid xim-id (in xim-notation), then it returns it.
|
|
||||||
//
|
|
||||||
// If the value of the input-variable ‘ximnotation’ is not a valid (xim-notation) xim-id, then it returns the option-type value nothing.
|
|
||||||
//
|
|
||||||
// An example usage might be:
|
|
||||||
//
|
|
||||||
// var ximid string = "xi-558YtmR06wm"
|
|
||||||
//
|
|
||||||
// // ...
|
|
||||||
//
|
|
||||||
// var id xim.ID = xim.Parse(ximid)
|
|
||||||
// if xim.Nothing() == id {
|
|
||||||
// fmt.Fprintf(os.Stderr, "error: invalid xim-id — %q \n", ximid)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fmt.Printf("SUCCESS! the xim-id is \n", id)
|
|
||||||
func Parse(ximnotation string) ID {
|
|
||||||
|
|
||||||
value, successful := unserialize(ximnotation)
|
|
||||||
if !successful {
|
|
||||||
return Nothing()
|
|
||||||
}
|
|
||||||
|
|
||||||
return something(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate returns a new xim-id.
|
|
||||||
//
|
|
||||||
// Example usage:
|
|
||||||
//
|
|
||||||
// var id xim.ID = xim.Generate()
|
|
||||||
//
|
|
||||||
// fmt.Println("xim-id =", id)
|
|
||||||
func Generate() ID {
|
|
||||||
|
|
||||||
var value uint64 = generate()
|
|
||||||
|
|
||||||
return something(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateForTime returns a new xim-id that has embedded in it the time given by the value in the input-variable ‘when’.
|
|
||||||
//
|
|
||||||
// Example usage:
|
|
||||||
//
|
|
||||||
// var moment time.Time = ???
|
|
||||||
//
|
|
||||||
// // ...
|
|
||||||
//
|
|
||||||
// var id xim.ID = xim.GenerateForTime(moment)
|
|
||||||
//
|
|
||||||
// fmt.Println("xim-id =", id)
|
|
||||||
//
|
|
||||||
// The chaos part of this xim-id will be chosen randomly (just like with xim.Generate()).
|
|
||||||
func GenerateForTime(when time.Time) ID {
|
|
||||||
|
|
||||||
var unixtimestamp int64 = when.Unix()
|
|
||||||
|
|
||||||
var value uint64 = generateforunixtime(unixtimestamp)
|
|
||||||
|
|
||||||
return something(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateForUnixTime returns a new xim-id that has embedded in it the time given by the unix-timestamp in the input-variable ‘unixtimestamp’.
|
|
||||||
//
|
|
||||||
// Example usage:
|
|
||||||
//
|
|
||||||
// var unixTimeStamp int32 = 1636403305 // I.e., Monday, November 8, 2021 12:28:25 PM GMT-08:00
|
|
||||||
//
|
|
||||||
// var id xim.ID = xim.GenerateForUnixTime(unixTimeStamp)
|
|
||||||
//
|
|
||||||
// fmt.Println("xim-id =", id)
|
|
||||||
//
|
|
||||||
// The chaos part of this xim-id will be chosen randomly (just like with xim.Generate()).
|
|
||||||
func GenerateForUnixTime(unixtimestamp int32) ID {
|
|
||||||
|
|
||||||
var when int64 = int64(unixtimestamp)
|
|
||||||
|
|
||||||
var value uint64 = generateforunixtime(when)
|
|
||||||
|
|
||||||
return something(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Chaos returns the randomness that is embeddd in the xim-id.
|
|
||||||
//
|
|
||||||
// Example usage:
|
|
||||||
//
|
|
||||||
// var id xim.ID = xim.Generate()
|
|
||||||
//
|
|
||||||
// chaos, successful := id.Chaos()
|
|
||||||
// if !successful {
|
|
||||||
// return errors.New("xim-id was not initialized")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fmt.Printf("chaos = %#020b \n", chaos)
|
|
||||||
func (receiver ID) Chaos() (uint64, bool) {
|
|
||||||
if Nothing() == receiver {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
_, value := decompile(receiver.value)
|
|
||||||
|
|
||||||
return value, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalText makes xim.ID fit the encoding.TextMarshaler interface.
|
|
||||||
func (receiver ID) MarshalText() (text []byte, err error) {
|
|
||||||
if Nothing() == receiver {
|
|
||||||
return nil, errNothing
|
|
||||||
}
|
|
||||||
|
|
||||||
var serialized string = serialize(receiver.value)
|
|
||||||
|
|
||||||
return []byte(serialized), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String makes xim.ID fit the fmt.Stringer interface.
|
|
||||||
//
|
|
||||||
// String also returns the serialized for of a xim-id, in xim-notation.
|
|
||||||
func (receiver ID) String() string {
|
|
||||||
if Nothing() == receiver {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
var serialized string = serialize(receiver.value)
|
|
||||||
|
|
||||||
return serialized
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnixTime returns the unix‐time that is embeddd in the xim-id.
|
|
||||||
//
|
|
||||||
// Example usage:
|
|
||||||
//
|
|
||||||
// var id xim.ID = xim.Generate()
|
|
||||||
//
|
|
||||||
// unixtime, successful := id.UnixTime()
|
|
||||||
// if !successful {
|
|
||||||
// return errors.New("xim-id was not initialized")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var t time.Time = time.Unix(unixtime, 0)
|
|
||||||
//
|
|
||||||
// fmt.Println("xim-id was created on:", t)
|
|
||||||
func (receiver ID) UnixTime() (int64, bool) {
|
|
||||||
if Nothing() == receiver {
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
value, _ := decompile(receiver.value)
|
|
||||||
|
|
||||||
return int64(value), true
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText makes xim.ID fit the encoding.TextUnmarshaler interface.
|
|
||||||
func (receiver *ID) UnmarshalText(p []byte) error {
|
|
||||||
if nil == receiver {
|
|
||||||
return errNilReceiver
|
|
||||||
}
|
|
||||||
|
|
||||||
var serialized string = string(p)
|
|
||||||
|
|
||||||
value, successful := unserialize(serialized)
|
|
||||||
if !successful {
|
|
||||||
return errBadRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
*receiver = something(value)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
package xim
|
||||||
|
|
||||||
|
// IID represents an interaction‐identifier — i.e., an interaction‐ID — i.e., an IID.
|
||||||
|
//
|
||||||
|
// It is a scheme used to generate quazi‐monotonically‐increasing‐unique.
|
||||||
|
type IID struct {
|
||||||
|
value uint64
|
||||||
|
loaded bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func Nothing() IID {
|
||||||
|
return IID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func something(value uint64) IID {
|
||||||
|
return IID{
|
||||||
|
value:value,
|
||||||
|
loaded:true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate creates a new interaction‐identifier — i.e., an interaction‐ID — i.e., an IID.
|
||||||
|
func Generate() IID {
|
||||||
|
|
||||||
|
var value uint64 = generate()
|
||||||
|
|
||||||
|
return something(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chaos returns the randomness that is embeddd in the interaction‐identifier — i.e., an interaction‐ID — i.e., an IID.
|
||||||
|
func (receiver IID) Chaos() (uint64, bool) {
|
||||||
|
if Nothing() == receiver {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
_, value := decompile(receiver.value)
|
||||||
|
|
||||||
|
return value, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (receiver IID) String() string {
|
||||||
|
if Nothing() == receiver {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var serialized string = serialize(receiver.value)
|
||||||
|
|
||||||
|
return serialized
|
||||||
|
}
|
||||||
|
|
||||||
|
func (receiver IID) MarshalText() (text []byte, err error) {
|
||||||
|
if Nothing() == receiver {
|
||||||
|
return nil, errNothing
|
||||||
|
}
|
||||||
|
|
||||||
|
var serialized string = serialize(receiver.value)
|
||||||
|
|
||||||
|
return []byte(serialized), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnixTime returns the unix‐time that is embeddd in the interaction‐identifier — i.e., an interaction‐ID — i.e., an IID.
|
||||||
|
func (receiver IID) UnixTime() (int64, bool) {
|
||||||
|
if Nothing() == receiver {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
value, _ := decompile(receiver.value)
|
||||||
|
|
||||||
|
return int64(value), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (receiver *IID) UnmarshalText(p []byte) error {
|
||||||
|
if nil == receiver {
|
||||||
|
return errNilReceiver
|
||||||
|
}
|
||||||
|
|
||||||
|
var serialized string = string(p)
|
||||||
|
|
||||||
|
value, successful := unserialize(serialized)
|
||||||
|
if !successful {
|
||||||
|
return errBadRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
*receiver = something(value)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -4,17 +4,17 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestID_MarshalText(t *testing.T) {
|
func TestIID_MarshalText(t *testing.T) {
|
||||||
|
|
||||||
for testNumber, test := range stdtests {
|
for testNumber, test := range stdtests {
|
||||||
|
|
||||||
var id ID = something(test.Value)
|
var intid IID = something(test.Value)
|
||||||
|
|
||||||
var marshaled []byte
|
var marshaled []byte
|
||||||
{
|
{
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
marshaled, err = id.MarshalText()
|
marshaled, err = intid.MarshalText()
|
||||||
if nil != err {
|
if nil != err {
|
||||||
t.Errorf("For test #%d, did not expect an error when mashaling, but actually got one.", testNumber)
|
t.Errorf("For test #%d, did not expect an error when mashaling, but actually got one.", testNumber)
|
||||||
t.Logf("VALUE: %064b", test.Value)
|
t.Logf("VALUE: %064b", test.Value)
|
||||||
|
@ -30,9 +30,9 @@ func TestID_MarshalText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var newid ID
|
var newintid IID
|
||||||
|
|
||||||
err := newid.UnmarshalText(marshaled)
|
err := newintid.UnmarshalText(marshaled)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
t.Errorf("For test #%d, did not expect an error when unmashaling, but actually got one.", testNumber)
|
t.Errorf("For test #%d, did not expect an error when unmashaling, but actually got one.", testNumber)
|
||||||
t.Logf("VALUE: %064b", test.Value)
|
t.Logf("VALUE: %064b", test.Value)
|
||||||
|
@ -42,8 +42,8 @@ func TestID_MarshalText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var expected ID = id
|
var expected IID = intid
|
||||||
var actual ID = newid
|
var actual IID = newintid
|
||||||
|
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
t.Errorf("For test #%d, the actual unmarshaled marshaled value is not what was expected.", testNumber)
|
t.Errorf("For test #%d, the actual unmarshaled marshaled value is not what was expected.", testNumber)
|
|
@ -1,58 +0,0 @@
|
||||||
package xim
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
|
||||||
|
|
||||||
for testNumber, test := range stdtests {
|
|
||||||
|
|
||||||
var id ID = something(test.Value)
|
|
||||||
|
|
||||||
var ximid string = id.String()
|
|
||||||
|
|
||||||
var actual ID = Parse(ximid)
|
|
||||||
if Nothing() == actual {
|
|
||||||
t.Errorf("For test #%d, the actual result of parsing is not what was expected — it is option-type value nothing" , testNumber)
|
|
||||||
t.Logf("VALUE: %#064b", test.Value)
|
|
||||||
t.Logf("ID: %s", id)
|
|
||||||
{
|
|
||||||
chaos, successful := id.Chaos()
|
|
||||||
t.Logf("ID-CHAOS-SUCCESSFUL: %t", successful)
|
|
||||||
t.Logf("ID-CHAOS: %#064b", chaos)
|
|
||||||
}
|
|
||||||
t.Logf("XIM-ID: %s", ximid)
|
|
||||||
t.Logf("ACTUAL: %s", actual)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
actualchaos, successful := id.Chaos()
|
|
||||||
if !successful {
|
|
||||||
t.Errorf("For test #%d, the actual result of parsing is not what was expected — it is option-type value nothing" , testNumber)
|
|
||||||
t.Logf("VALUE: %#064b", test.Value)
|
|
||||||
t.Logf("ID: %s", id)
|
|
||||||
{
|
|
||||||
chaos, successful := id.Chaos()
|
|
||||||
t.Logf("ID-CHAOS-SUCCESSFUL: %t", successful)
|
|
||||||
t.Logf("ID-CHAOS: %#064b", chaos)
|
|
||||||
}
|
|
||||||
t.Logf("XIM-ID: %s", ximid)
|
|
||||||
t.Logf("ACTUAL: %s", actual)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if expectedchaos := test.Value & maskchaos; expectedchaos != actualchaos {
|
|
||||||
t.Errorf("For test #%d, the chaos from the actual result of parsing is not what was expected" , testNumber)
|
|
||||||
t.Logf("VALUE: %#064b", test.Value)
|
|
||||||
t.Logf("ID: %s", id)
|
|
||||||
t.Logf("XIM-ID: %s", ximid)
|
|
||||||
t.Logf("ACTUAL: %s", actual)
|
|
||||||
t.Logf("ACTUAL-CHAOS: %#064b", actualchaos)
|
|
||||||
t.Logf("EXPECTED-CHAOS: %#064b", expectedchaos)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue