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
|
||||
// generate ids
|
||||
func (c *Config) WithRuneset(r string) *Config {
|
||||
func (c *Config) WithRuneset(r []rune) *Config {
|
||||
c.runes = ensureUniqueRunes(r)
|
||||
c.runesize = len(c.runes)
|
||||
c.mod = byte(c.runesize - 1)
|
||||
return c
|
||||
}
|
||||
|
||||
func ensureUniqueRunes(r string) []rune {
|
||||
func ensureUniqueRunes(r []rune) []rune {
|
||||
unq := make(map[rune]int)
|
||||
var index int
|
||||
// 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 _, ok := unq[v]; !ok {
|
||||
unq[v] = index
|
||||
@ -97,17 +97,21 @@ func (c *Config) Zero() TinD {
|
||||
|
||||
// Gen generates a TinD with the given configuration
|
||||
func (c *Config) Gen() TinD {
|
||||
t := TinD{
|
||||
bytes: make([]byte, c.size),
|
||||
config: c,
|
||||
}
|
||||
_, err := rand.Read(t.bytes)
|
||||
bytes := make([]byte, c.size)
|
||||
runes := make([]rune, c.size)
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
// Make sure each byte fits the rune so it can be encoded and decoded
|
||||
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
|
||||
}
|
||||
@ -119,12 +123,15 @@ func (c *Config) FromString(in string) (TinD, error) {
|
||||
if len(in) != c.size {
|
||||
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)
|
||||
for i := 0; i < l; i++ {
|
||||
for i := 0; i < c.size; i++ {
|
||||
for j := 0; j < c.runesize; j++ {
|
||||
if r[i] == alphabet[j] {
|
||||
t[i] = byte(j)
|
||||
if r[i] == c.runes[j] {
|
||||
t.bytes[i] = byte(j)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
29
tind.go
29
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
|
||||
// whatever unicode characters you want, as the alphabet it uses is just a slice
|
||||
// of runes
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"log"
|
||||
)
|
||||
|
||||
// defaults are just a call to NewConfig()
|
||||
var defaults = NewTinDConfig()
|
||||
@ -24,9 +19,9 @@ var defaults = NewTinDConfig()
|
||||
// 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
|
||||
// used, the more randomness can be done.
|
||||
type TinD struct{
|
||||
bytes []byte
|
||||
str string
|
||||
type TinD struct {
|
||||
bytes []byte
|
||||
runes []rune
|
||||
config *Config
|
||||
}
|
||||
|
||||
@ -37,11 +32,12 @@ func Gen() TinD {
|
||||
|
||||
// String returns the Human Readable form of the TinD
|
||||
func (t TinD) String() string {
|
||||
if len(t.str) == t.config.size {
|
||||
return t.str
|
||||
}
|
||||
t.str = makeString(t.bytes, t.config.size, t.config.runset)
|
||||
return t.str
|
||||
return string(t.runes)
|
||||
}
|
||||
|
||||
// Runes returns the Rune array of the TinD
|
||||
func (t TinD) Runes() []rune {
|
||||
return t.runes
|
||||
}
|
||||
|
||||
// Bytes returns the raw bytes of the TinD
|
||||
@ -49,10 +45,3 @@ func (t TinD) Bytes() []byte {
|
||||
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