Added basic testing
Started doing all comparisons in Runes string cound emojis as 4 runes instead of just the 1 it is
This commit is contained in:
parent
d00ac81a29
commit
79c235de31
33
config.go
33
config.go
@ -54,18 +54,18 @@ func NewTinDConfig() *Config {
|
|||||||
//
|
//
|
||||||
// This does slow down initialization, but the final product still runs fast to
|
// This does slow down initialization, but the final product still runs fast to
|
||||||
// generate ids
|
// generate ids
|
||||||
func (c *Config) WithRuneset(r string) *Config {
|
func (c *Config) WithRuneset(r []rune) *Config {
|
||||||
c.runes = ensureUniqueRunes(r)
|
c.runes = ensureUniqueRunes(r)
|
||||||
c.runesize = len(c.runes)
|
c.runesize = len(c.runes)
|
||||||
c.mod = byte(c.runesize - 1)
|
c.mod = byte(c.runesize - 1)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureUniqueRunes(r string) []rune {
|
func ensureUniqueRunes(r []rune) []rune {
|
||||||
unq := make(map[rune]int)
|
unq := make(map[rune]int)
|
||||||
var index int
|
var index int
|
||||||
// loop through the runes to find duplicates
|
// loop through the runes to find duplicates
|
||||||
for _, v := range []rune(r) {
|
for _, v := range r {
|
||||||
// if the current rune hasn't been seen yet, assign it
|
// if the current rune hasn't been seen yet, assign it
|
||||||
if _, ok := unq[v]; !ok {
|
if _, ok := unq[v]; !ok {
|
||||||
unq[v] = index
|
unq[v] = index
|
||||||
@ -97,17 +97,21 @@ func (c *Config) Zero() TinD {
|
|||||||
|
|
||||||
// Gen generates a TinD with the given configuration
|
// Gen generates a TinD with the given configuration
|
||||||
func (c *Config) Gen() TinD {
|
func (c *Config) Gen() TinD {
|
||||||
t := TinD{
|
bytes := make([]byte, c.size)
|
||||||
bytes: make([]byte, c.size),
|
runes := make([]rune, c.size)
|
||||||
config: c,
|
_, err := rand.Read(bytes)
|
||||||
}
|
|
||||||
_, err := rand.Read(t.bytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
// Make sure each byte fits the rune so it can be encoded and decoded
|
// Make sure each byte fits the rune so it can be encoded and decoded
|
||||||
for i := 0; i < c.size; i++ {
|
for i := 0; i < c.size; i++ {
|
||||||
t.bytes[i] = t.bytes[i] & c.mod
|
bytes[i] = bytes[i] & c.mod
|
||||||
|
runes[i] = c.runes[bytes[i]]
|
||||||
|
}
|
||||||
|
t := TinD{
|
||||||
|
bytes: bytes,
|
||||||
|
runes: runes,
|
||||||
|
config: c,
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
@ -119,12 +123,15 @@ func (c *Config) FromString(in string) (TinD, error) {
|
|||||||
if len(in) != c.size {
|
if len(in) != c.size {
|
||||||
return c.Zero(), errors.New("given id isn't the same size as the configuration")
|
return c.Zero(), errors.New("given id isn't the same size as the configuration")
|
||||||
}
|
}
|
||||||
t := make([]byte, c.size)
|
t := TinD{
|
||||||
|
bytes: make([]byte, c.size),
|
||||||
|
config: c,
|
||||||
|
}
|
||||||
r := []rune(in)
|
r := []rune(in)
|
||||||
for i := 0; i < l; i++ {
|
for i := 0; i < c.size; i++ {
|
||||||
for j := 0; j < c.runesize; j++ {
|
for j := 0; j < c.runesize; j++ {
|
||||||
if r[i] == alphabet[j] {
|
if r[i] == c.runes[j] {
|
||||||
t[i] = byte(j)
|
t.bytes[i] = byte(j)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
tind.go
27
tind.go
@ -10,11 +10,6 @@ package tind
|
|||||||
// TinD is at it's core a byte slice. It's default alphabet can be replaced with
|
// TinD is at it's core a byte slice. It's default alphabet can be replaced with
|
||||||
// whatever unicode characters you want, as the alphabet it uses is just a slice
|
// whatever unicode characters you want, as the alphabet it uses is just a slice
|
||||||
// of runes
|
// of runes
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// defaults are just a call to NewConfig()
|
// defaults are just a call to NewConfig()
|
||||||
var defaults = NewTinDConfig()
|
var defaults = NewTinDConfig()
|
||||||
@ -24,9 +19,9 @@ var defaults = NewTinDConfig()
|
|||||||
// Each byte can be the value 0 -> len(alphabet) that is used, to ensure
|
// Each byte can be the value 0 -> len(alphabet) that is used, to ensure
|
||||||
// values can be encoded and decoded reliably. The longer the alphabet
|
// values can be encoded and decoded reliably. The longer the alphabet
|
||||||
// used, the more randomness can be done.
|
// used, the more randomness can be done.
|
||||||
type TinD struct{
|
type TinD struct {
|
||||||
bytes []byte
|
bytes []byte
|
||||||
str string
|
runes []rune
|
||||||
config *Config
|
config *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,11 +32,12 @@ func Gen() TinD {
|
|||||||
|
|
||||||
// String returns the Human Readable form of the TinD
|
// String returns the Human Readable form of the TinD
|
||||||
func (t TinD) String() string {
|
func (t TinD) String() string {
|
||||||
if len(t.str) == t.config.size {
|
return string(t.runes)
|
||||||
return t.str
|
}
|
||||||
}
|
|
||||||
t.str = makeString(t.bytes, t.config.size, t.config.runset)
|
// Runes returns the Rune array of the TinD
|
||||||
return t.str
|
func (t TinD) Runes() []rune {
|
||||||
|
return t.runes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes returns the raw bytes of the TinD
|
// Bytes returns the raw bytes of the TinD
|
||||||
@ -49,10 +45,3 @@ func (t TinD) Bytes() []byte {
|
|||||||
return t.bytes
|
return t.bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeString(bytes []byte, size int, runeset []rune) string {
|
|
||||||
r := make([]rune, size)
|
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
r[i] = runeset[bytes[i]]
|
|
||||||
}
|
|
||||||
return string(r)
|
|
||||||
}
|
|
||||||
|
78
tind_test.go
Normal file
78
tind_test.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package tind
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_GenTinD_With_Defaults(t *testing.T) {
|
||||||
|
id := Gen()
|
||||||
|
if len(id.String()) != 4 {
|
||||||
|
t.Errorf("expecting 4 bytes, got %d", len(id.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_GenTinD_With_VaryingSizes(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
size int
|
||||||
|
runes []rune
|
||||||
|
expectedRuneSize int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
size: 4,
|
||||||
|
runes: []rune("abcdefghijklmnopqrstuvwxyz"),
|
||||||
|
expectedRuneSize: 26,
|
||||||
|
}, {
|
||||||
|
size: 8,
|
||||||
|
runes: []rune("HGHHGHGJJHSHGABBJEBJBE"),
|
||||||
|
expectedRuneSize: 7,
|
||||||
|
}, {
|
||||||
|
size: 8,
|
||||||
|
runes: []rune("😀😡🤕🎃🤚🫲🙀😭"),
|
||||||
|
expectedRuneSize: 8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, v := range tests {
|
||||||
|
tt := NewTinDConfig().WithRuneset(v.runes).WithSize(v.size).Gen()
|
||||||
|
if len(tt.Runes()) != v.size {
|
||||||
|
t.Errorf("config set size to %d, but generated an id of size %d. id: %s", v.size, len([]rune(tt.String())), tt.String())
|
||||||
|
}
|
||||||
|
if tt.config.runesize != v.expectedRuneSize {
|
||||||
|
t.Errorf("expected runsize of %d, got runesize of %d", len(v.runes), tt.config.runesize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//func Test_Generate_With_Collisions(t *testing.T) {
|
||||||
|
// log.Println("Test TinD for collisions, base on number of iterations and time")
|
||||||
|
//
|
||||||
|
// maxSize := 16
|
||||||
|
// for size := 4; size < maxSize; size++ {
|
||||||
|
// CheckCollisions(size)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
func CheckCollisions(tindSize int) {
|
||||||
|
ids := make(map[string]struct{})
|
||||||
|
collided := false
|
||||||
|
tc := NewTinDConfig().WithSize(tindSize)
|
||||||
|
var iters uint64
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
log.Printf("Starting check with size %d", tindSize)
|
||||||
|
for !collided {
|
||||||
|
iters++
|
||||||
|
nt := tc.Gen()
|
||||||
|
if _, ok := ids[nt.String()]; ok {
|
||||||
|
collided = true
|
||||||
|
} else {
|
||||||
|
ids[nt.String()] = struct{}{}
|
||||||
|
}
|
||||||
|
// Print a message every 2000000 iterations saying we are still working
|
||||||
|
if iters%2000000 == 0 {
|
||||||
|
log.Printf("Still no collions on size %d after %d iterations", tindSize, iters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("Collision found after %v and %d iterations with size of %d", time.Since(start), iters, tindSize)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user