Compare commits

...

10 Commits

7 changed files with 302 additions and 111 deletions

View File

@ -1,27 +1,27 @@
# go-iid # go-xim
Package **iid** provides quazi monotonicallyincreasing uniqueidentifiers. Package **xim** provides quazi monotonicallyincreasing uniqueidentifiers.
The serialized form of the **IID** is safe to use as a _file_ or _directory_ name. The serialized form of the **xim-id** 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-iid Online documentation, which includes examples, can be found at: http://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) [![GoDoc](https://godoc.org/github.com/reiver/go-xim?status.svg)](https://godoc.org/github.com/reiver/go-xim)
## Example ## Example
Here is an example of using `package iid`: Here is an example of using `package xim`:
```go ```go
var id iid.IID = iid.Generate() var id xim.ID = xim.Generate()
``` ```
## Representation ## Representation
Internally, the IID is compactly stored in an `uint64`. The anatomy of this is as follows: Internally, the **xim-id** 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 IID is compactly stored in an `uint64`. The anatomy of this is a
always zero (1-bit) chaos (20-bits) always zero (1-bit) chaos (20-bits)
``` ```
The `iid.IID.UnixTime()` method will give you that 39-bit _unix timestamp_. The `xim.ID.UnixTime()` method will give you that 39-bit _unix timestamp_.
And the `iid.IID.Chaos()` method will give you that 24-bit _chaos_. And the `xim.ID.Chaos()` method will give you that 24-bit _chaos_.
(The _chaos_ is just a randomness that helps make these IIDs unique, when multiple IIDs are being produced simultaneously.) (The _chaos_ is just a randomness that helps make these **xim-ids** unique, when multiple **xim-ids** are being produced simultaneously.)
## Temporal Ordering of Representation ## Temporal Ordering of Representation
@ -203,3 +203,7 @@ 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

View File

@ -5,8 +5,8 @@ const (
) )
const ( const (
maskfirst = 0b0111111111111111111111111111111111111111000000000000000000000000 maskunixtime = 0b0111111111111111111111111111111111111111000000000000000000000000
masksecond = 0b0000000000000000000000000000000000000000111111111111111111111111 maskchaos = 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) & maskfirst) | (second & masksecond) var compiled uint64 = ((first << widthsecond) & maskunixtime) | (second & maskchaos)
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 & maskfirst) >> widthsecond var first uint64 = (value & maskunixtime) >> widthsecond
var second uint64 = value & masksecond var second uint64 = value & maskchaos
return first, second return first, second
} }

View File

@ -12,9 +12,14 @@ 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(now), chaos) var value uint64 = compile(uint64(unixtimestamp), chaos)
return value return value
} }

210
id.go 100644
View File

@ -0,0 +1,210 @@
package xim
import (
"time"
)
// ID represents a xim-id.
//
// It is a scheme used to generate quazimonotonicallyincreasingunique 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 unixtime 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
}

View File

@ -4,17 +4,17 @@ import (
"testing" "testing"
) )
func TestIID_MarshalText(t *testing.T) { func TestID_MarshalText(t *testing.T) {
for testNumber, test := range stdtests { for testNumber, test := range stdtests {
var intid IID = something(test.Value) var id ID = something(test.Value)
var marshaled []byte var marshaled []byte
{ {
var err error var err error
marshaled, err = intid.MarshalText() marshaled, err = id.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 TestIID_MarshalText(t *testing.T) {
} }
{ {
var newintid IID var newid ID
err := newintid.UnmarshalText(marshaled) err := newid.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 TestIID_MarshalText(t *testing.T) {
} }
var expected IID = intid var expected ID = id
var actual IID = newintid var actual ID = newid
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)

86
iid.go
View File

@ -1,86 +0,0 @@
package xim
// IID represents an interactionidentifier — i.e., an interactionID — i.e., an IID.
//
// It is a scheme used to generate quazimonotonicallyincreasingunique.
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 interactionidentifier — i.e., an interactionID — i.e., an IID.
func Generate() IID {
var value uint64 = generate()
return something(value)
}
// Chaos returns the randomness that is embeddd in the interactionidentifier — i.e., an interactionID — 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 unixtime that is embeddd in the interactionidentifier — i.e., an interactionID — 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
}

58
parse_test.go 100644
View File

@ -0,0 +1,58 @@
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
}
}
}
}