github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/uid/ksuid_test.go (about)

     1  package uid
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"encoding/json"
     7  	"flag"
     8  	"fmt"
     9  	"math"
    10  	"sort"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  func TestConstructionTimestamp(t *testing.T) {
    17  	x := New()
    18  	nowTime := time.Now().Round(1 * time.Minute)
    19  	xTime := x.Time().Round(1 * time.Minute)
    20  
    21  	if xTime != nowTime {
    22  		t.Fatal(xTime, "!=", nowTime)
    23  	}
    24  }
    25  
    26  func TestNil(t *testing.T) {
    27  	if !Nil.IsNil() {
    28  		t.Fatal("Nil should be Nil!")
    29  	}
    30  
    31  	x, _ := FromBytes(make([]byte, byteLength))
    32  	if !x.IsNil() {
    33  		t.Fatal("Zero-byte array should be Nil!")
    34  	}
    35  }
    36  
    37  func TestEncoding(t *testing.T) {
    38  	x, _ := FromBytes(make([]byte, byteLength))
    39  	if !x.IsNil() {
    40  		t.Fatal("Zero-byte array should be Nil!")
    41  	}
    42  
    43  	encoded := x.String()
    44  	expected := strings.Repeat("0", stringEncodedLength)
    45  
    46  	if encoded != expected {
    47  		t.Fatal("expected", expected, "encoded", encoded)
    48  	}
    49  }
    50  
    51  func TestPadding(t *testing.T) {
    52  	b := make([]byte, byteLength)
    53  	for i := 0; i < byteLength; i++ {
    54  		b[i] = 255
    55  	}
    56  
    57  	x, _ := FromBytes(b)
    58  	xEncoded := x.String()
    59  	nilEncoded := Nil.String()
    60  
    61  	if len(xEncoded) != len(nilEncoded) {
    62  		t.Fatal("Encoding should produce equal-length strings for zero and max case")
    63  	}
    64  }
    65  
    66  func TestParse(t *testing.T) {
    67  	_, err := Parse("123")
    68  	if err != errStrSize {
    69  		t.Fatal("Expected Parsing a 3-char string to return an error")
    70  	}
    71  
    72  	parsed, err := Parse(strings.Repeat("0", stringEncodedLength))
    73  	if err != nil {
    74  		t.Fatal("Unexpected error", err)
    75  	}
    76  
    77  	if Compare(parsed, Nil) != 0 {
    78  		t.Fatal("Parsing all-zeroes string should equal Nil value",
    79  			"expected:", Nil,
    80  			"actual:", parsed)
    81  	}
    82  
    83  	maxBytes := make([]byte, byteLength)
    84  	for i := 0; i < byteLength; i++ {
    85  		maxBytes[i] = 255
    86  	}
    87  	maxBytesKSUID, err := FromBytes(maxBytes)
    88  	if err != nil {
    89  		t.Fatal("Unexpected error", err)
    90  	}
    91  
    92  	maxParseKSUID, err := Parse(maxStringEncoded)
    93  	if err != nil {
    94  		t.Fatal("Unexpected error", err)
    95  	}
    96  
    97  	if Compare(maxBytesKSUID, maxParseKSUID) != 0 {
    98  		t.Fatal("String decoder broke for max string")
    99  	}
   100  }
   101  
   102  func TestIssue25(t *testing.T) {
   103  	// https://github.com/segmentio/ksuid/issues/25
   104  	for _, s := range []string{
   105  		"aaaaaaaaaaaaaaaaaaaaaaaaaaa",
   106  		"aWgEPTl1tmebfsQzFP4bxwgy80!",
   107  	} {
   108  		_, err := Parse(s)
   109  		if err != errStrValue {
   110  			t.Error("invalid KSUID representations cannot be successfully parsed, got err =", err)
   111  		}
   112  	}
   113  }
   114  
   115  func TestEncodeAndDecode(t *testing.T) {
   116  	x := New()
   117  	builtFromEncodedString, err := Parse(x.String())
   118  	if err != nil {
   119  		t.Fatal("Unexpected error", err)
   120  	}
   121  
   122  	if Compare(x, builtFromEncodedString) != 0 {
   123  		t.Fatal("Parse(X).String() != X")
   124  	}
   125  }
   126  
   127  func TestMarshalText(t *testing.T) {
   128  	id1 := New()
   129  	var id2 KSUID
   130  
   131  	if err := id2.UnmarshalText([]byte(id1.String())); err != nil {
   132  		t.Fatal(err)
   133  	}
   134  
   135  	if id1 != id2 {
   136  		t.Fatal(id1, "!=", id2)
   137  	}
   138  
   139  	if b, err := id2.MarshalText(); err != nil {
   140  		t.Fatal(err)
   141  	} else if s := string(b); s != id1.String() {
   142  		t.Fatal(s)
   143  	}
   144  }
   145  
   146  func TestMarshalBinary(t *testing.T) {
   147  	id1 := New()
   148  	var id2 KSUID
   149  
   150  	if err := id2.UnmarshalBinary(id1.Bytes()); err != nil {
   151  		t.Fatal(err)
   152  	}
   153  
   154  	if id1 != id2 {
   155  		t.Fatal(id1, "!=", id2)
   156  	}
   157  
   158  	if b, err := id2.MarshalBinary(); err != nil {
   159  		t.Fatal(err)
   160  	} else if bytes.Compare(b, id1.Bytes()) != 0 {
   161  		t.Fatal("bad binary form:", id2)
   162  	}
   163  }
   164  
   165  func TestMarshalJSON(t *testing.T) {
   166  	id1 := New()
   167  	var id2 KSUID
   168  
   169  	if b, err := json.Marshal(id1); err != nil {
   170  		t.Fatal(err)
   171  	} else if err := json.Unmarshal(b, &id2); err != nil {
   172  		t.Fatal(err)
   173  	} else if id1 != id2 {
   174  		t.Error(id1, "!=", id2)
   175  	}
   176  }
   177  
   178  func TestFlag(t *testing.T) {
   179  	id1 := New()
   180  	var id2 KSUID
   181  
   182  	fset := flag.NewFlagSet("test", flag.ContinueOnError)
   183  	fset.Var(&id2, "id", "the KSUID")
   184  
   185  	if err := fset.Parse([]string{"-id", id1.String()}); err != nil {
   186  		t.Fatal(err)
   187  	}
   188  
   189  	if id1 != id2 {
   190  		t.Error(id1, "!=", id2)
   191  	}
   192  }
   193  
   194  func TestSqlValuer(t *testing.T) {
   195  	id, _ := Parse(maxStringEncoded)
   196  
   197  	if v, err := id.Value(); err != nil {
   198  		t.Error(err)
   199  	} else if s, ok := v.(string); !ok {
   200  		t.Error("not a string value")
   201  	} else if s != maxStringEncoded {
   202  		t.Error("bad string value::", s)
   203  	}
   204  }
   205  
   206  func TestSqlValuerNilValue(t *testing.T) {
   207  	if v, err := Nil.Value(); err != nil {
   208  		t.Error(err)
   209  	} else if v != nil {
   210  		t.Errorf("bad nil value: %v", v)
   211  	}
   212  }
   213  
   214  func TestSqlScanner(t *testing.T) {
   215  	id1 := New()
   216  	id2 := New()
   217  
   218  	tests := []struct {
   219  		ksuid KSUID
   220  		value interface{}
   221  	}{
   222  		{Nil, nil},
   223  		{id1, id1.String()},
   224  		{id2, id2.Bytes()},
   225  	}
   226  
   227  	for _, test := range tests {
   228  		t.Run(fmt.Sprintf("%T", test.value), func(t *testing.T) {
   229  			var id KSUID
   230  
   231  			if err := id.Scan(test.value); err != nil {
   232  				t.Error(err)
   233  			}
   234  
   235  			if id != test.ksuid {
   236  				t.Error("bad KSUID:")
   237  				t.Logf("expected %v", test.ksuid)
   238  				t.Logf("found    %v", id)
   239  			}
   240  		})
   241  	}
   242  }
   243  
   244  func TestAppend(t *testing.T) {
   245  	for _, repr := range []string{"0pN1Own7255s7jwpwy495bAZeEa", "aWgEPTl1tmebfsQzFP4bxwgy80V"} {
   246  		k, _ := Parse(repr)
   247  		a := make([]byte, 0, stringEncodedLength)
   248  
   249  		a = append(a, "?: "...)
   250  		a = k.Append(a)
   251  
   252  		if s := string(a); s != "?: "+repr {
   253  			t.Error(s)
   254  		}
   255  	}
   256  }
   257  
   258  func TestSort(t *testing.T) {
   259  	ids1 := [11]KSUID{}
   260  	ids2 := [11]KSUID{}
   261  
   262  	for i := range ids1 {
   263  		ids1[i] = New()
   264  	}
   265  
   266  	ids2 = ids1
   267  	sort.Slice(ids2[:], func(i, j int) bool {
   268  		return Compare(ids2[i], ids2[j]) < 0
   269  	})
   270  
   271  	Sort(ids1[:])
   272  
   273  	if !IsSorted(ids1[:]) {
   274  		t.Error("not sorted")
   275  	}
   276  
   277  	if ids1 != ids2 {
   278  		t.Error("bad order:")
   279  		t.Log(ids1)
   280  		t.Log(ids2)
   281  	}
   282  }
   283  
   284  func TestPrevNext(t *testing.T) {
   285  	tests := []struct {
   286  		id   KSUID
   287  		prev KSUID
   288  		next KSUID
   289  	}{
   290  		{
   291  			id:   Nil,
   292  			prev: Max,
   293  			next: KSUID{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
   294  		},
   295  		{
   296  			id:   Max,
   297  			prev: KSUID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe},
   298  			next: Nil,
   299  		},
   300  	}
   301  
   302  	for _, test := range tests {
   303  		t.Run(test.id.String(), func(t *testing.T) {
   304  			testPrevNext(t, test.id, test.prev, test.next)
   305  		})
   306  	}
   307  }
   308  
   309  func TestGetTimestamp(t *testing.T) {
   310  	nowTime := time.Now()
   311  	x, _ := NewRandomWithTime(nowTime)
   312  	xTime := int64(x.Timestamp())
   313  	unix := nowTime.Unix()
   314  	if xTime != unix-epochStamp {
   315  		t.Fatal(xTime, "!=", unix)
   316  	}
   317  }
   318  
   319  func testPrevNext(t *testing.T, id, prev, next KSUID) {
   320  	id1 := id.Prev()
   321  	id2 := id.Next()
   322  
   323  	if id1 != prev {
   324  		t.Error("previous id of the nil KSUID is wrong:", id1, "!=", prev)
   325  	}
   326  
   327  	if id2 != next {
   328  		t.Error("next id of the nil KSUID is wrong:", id2, "!=", next)
   329  	}
   330  }
   331  
   332  func BenchmarkAppend(b *testing.B) {
   333  	a := make([]byte, 0, stringEncodedLength)
   334  	k := New()
   335  
   336  	for i := 0; i != b.N; i++ {
   337  		k.Append(a)
   338  	}
   339  }
   340  
   341  func BenchmarkString(b *testing.B) {
   342  	k := New()
   343  
   344  	for i := 0; i != b.N; i++ {
   345  		_ = k.String()
   346  	}
   347  }
   348  
   349  func BenchmarkParse(b *testing.B) {
   350  	for i := 0; i != b.N; i++ {
   351  		Parse(maxStringEncoded)
   352  	}
   353  }
   354  
   355  func BenchmarkCompare(b *testing.B) {
   356  	k1 := New()
   357  	k2 := New()
   358  
   359  	for i := 0; i != b.N; i++ {
   360  		Compare(k1, k2)
   361  	}
   362  }
   363  
   364  func BenchmarkSort(b *testing.B) {
   365  	ids1 := [101]KSUID{}
   366  	ids2 := [101]KSUID{}
   367  
   368  	for i := range ids1 {
   369  		ids1[i] = New()
   370  	}
   371  
   372  	for i := 0; i != b.N; i++ {
   373  		ids2 = ids1
   374  		Sort(ids2[:])
   375  	}
   376  }
   377  
   378  func BenchmarkNew(b *testing.B) {
   379  	b.Run("with crypto rand", func(b *testing.B) {
   380  		SetRand(nil)
   381  		for i := 0; i != b.N; i++ {
   382  			New()
   383  		}
   384  	})
   385  	b.Run("with math rand", func(b *testing.B) {
   386  		SetRand(FastRander)
   387  		for i := 0; i != b.N; i++ {
   388  			New()
   389  		}
   390  	})
   391  }
   392  
   393  func TestCmp128(t *testing.T) {
   394  	tests := []struct {
   395  		x uint128
   396  		y uint128
   397  		k int
   398  	}{
   399  		{
   400  			x: makeUint128(0, 0),
   401  			y: makeUint128(0, 0),
   402  			k: 0,
   403  		},
   404  		{
   405  			x: makeUint128(0, 1),
   406  			y: makeUint128(0, 0),
   407  			k: +1,
   408  		},
   409  		{
   410  			x: makeUint128(0, 0),
   411  			y: makeUint128(0, 1),
   412  			k: -1,
   413  		},
   414  		{
   415  			x: makeUint128(1, 0),
   416  			y: makeUint128(0, 1),
   417  			k: +1,
   418  		},
   419  		{
   420  			x: makeUint128(0, 1),
   421  			y: makeUint128(1, 0),
   422  			k: -1,
   423  		},
   424  	}
   425  
   426  	for _, test := range tests {
   427  		t.Run(fmt.Sprintf("cmp128(%s,%s)", test.x, test.y), func(t *testing.T) {
   428  			if k := cmp128(test.x, test.y); k != test.k {
   429  				t.Error(k, "!=", test.k)
   430  			}
   431  		})
   432  	}
   433  }
   434  
   435  func TestAdd128(t *testing.T) {
   436  	tests := []struct {
   437  		x uint128
   438  		y uint128
   439  		z uint128
   440  	}{
   441  		{
   442  			x: makeUint128(0, 0),
   443  			y: makeUint128(0, 0),
   444  			z: makeUint128(0, 0),
   445  		},
   446  		{
   447  			x: makeUint128(0, 1),
   448  			y: makeUint128(0, 0),
   449  			z: makeUint128(0, 1),
   450  		},
   451  		{
   452  			x: makeUint128(0, 0),
   453  			y: makeUint128(0, 1),
   454  			z: makeUint128(0, 1),
   455  		},
   456  		{
   457  			x: makeUint128(1, 0),
   458  			y: makeUint128(0, 1),
   459  			z: makeUint128(1, 1),
   460  		},
   461  		{
   462  			x: makeUint128(0, 1),
   463  			y: makeUint128(1, 0),
   464  			z: makeUint128(1, 1),
   465  		},
   466  		{
   467  			x: makeUint128(0, 0xFFFFFFFFFFFFFFFF),
   468  			y: makeUint128(0, 1),
   469  			z: makeUint128(1, 0),
   470  		},
   471  	}
   472  
   473  	for _, test := range tests {
   474  		t.Run(fmt.Sprintf("add128(%s,%s)", test.x, test.y), func(t *testing.T) {
   475  			if z := add128(test.x, test.y); z != test.z {
   476  				t.Error(z, "!=", test.z)
   477  			}
   478  		})
   479  	}
   480  }
   481  
   482  func TestSub128(t *testing.T) {
   483  	tests := []struct {
   484  		x uint128
   485  		y uint128
   486  		z uint128
   487  	}{
   488  		{
   489  			x: makeUint128(0, 0),
   490  			y: makeUint128(0, 0),
   491  			z: makeUint128(0, 0),
   492  		},
   493  		{
   494  			x: makeUint128(0, 1),
   495  			y: makeUint128(0, 0),
   496  			z: makeUint128(0, 1),
   497  		},
   498  		{
   499  			x: makeUint128(0, 0),
   500  			y: makeUint128(0, 1),
   501  			z: makeUint128(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF),
   502  		},
   503  		{
   504  			x: makeUint128(1, 0),
   505  			y: makeUint128(0, 1),
   506  			z: makeUint128(0, 0xFFFFFFFFFFFFFFFF),
   507  		},
   508  		{
   509  			x: makeUint128(0, 1),
   510  			y: makeUint128(1, 0),
   511  			z: makeUint128(0xFFFFFFFFFFFFFFFF, 1),
   512  		},
   513  		{
   514  			x: makeUint128(0, 0xFFFFFFFFFFFFFFFF),
   515  			y: makeUint128(0, 1),
   516  			z: makeUint128(0, 0xFFFFFFFFFFFFFFFE),
   517  		},
   518  	}
   519  
   520  	for _, test := range tests {
   521  		t.Run(fmt.Sprintf("sub128(%s,%s)", test.x, test.y), func(t *testing.T) {
   522  			if z := sub128(test.x, test.y); z != test.z {
   523  				t.Error(z, "!=", test.z)
   524  			}
   525  		})
   526  	}
   527  }
   528  
   529  func BenchmarkCmp128(b *testing.B) {
   530  	x := makeUint128(0, 0)
   531  	y := makeUint128(0, 0)
   532  
   533  	for i := 0; i != b.N; i++ {
   534  		cmp128(x, y)
   535  	}
   536  }
   537  
   538  func BenchmarkAdd128(b *testing.B) {
   539  	x := makeUint128(0, 0)
   540  	y := makeUint128(0, 0)
   541  
   542  	for i := 0; i != b.N; i++ {
   543  		add128(x, y)
   544  	}
   545  }
   546  
   547  func BenchmarkSub128(b *testing.B) {
   548  	x := makeUint128(0, 0)
   549  	y := makeUint128(0, 0)
   550  
   551  	for i := 0; i != b.N; i++ {
   552  		sub128(x, y)
   553  	}
   554  }
   555  
   556  func TestSequence(t *testing.T) {
   557  	seq := Sequence{Seed: New()}
   558  
   559  	if min, max := seq.Bounds(); min == max {
   560  		t.Error("min and max of KSUID range must differ when no ids have been generated")
   561  	}
   562  
   563  	for i := 0; i <= math.MaxUint16; i++ {
   564  		id, err := seq.Next()
   565  		if err != nil {
   566  			t.Fatal(err)
   567  		}
   568  		if j := int(binary.BigEndian.Uint16(id[len(id)-2:])); j != i {
   569  			t.Fatalf("expected %d but got %d in %s", i, j, id)
   570  		}
   571  	}
   572  
   573  	if _, err := seq.Next(); err == nil {
   574  		t.Fatal("no error returned after exhausting the id generator")
   575  	}
   576  
   577  	if min, max := seq.Bounds(); min != max {
   578  		t.Error("after all KSUIDs were generated the min and max must be equal")
   579  	}
   580  }
   581  
   582  func TestBase10ToBase62AndBack(t *testing.T) {
   583  	number := []byte{1, 2, 3, 4}
   584  	encoded := base2base(number, 10, 62)
   585  	decoded := base2base(encoded, 62, 10)
   586  
   587  	if bytes.Compare(number, decoded) != 0 {
   588  		t.Fatal(number, " != ", decoded)
   589  	}
   590  }
   591  
   592  func TestBase256ToBase62AndBack(t *testing.T) {
   593  	number := []byte{255, 254, 253, 251}
   594  	encoded := base2base(number, 256, 62)
   595  	decoded := base2base(encoded, 62, 256)
   596  
   597  	if bytes.Compare(number, decoded) != 0 {
   598  		t.Fatal(number, " != ", decoded)
   599  	}
   600  }
   601  
   602  func TestEncodeAndDecodeBase62(t *testing.T) {
   603  	helloWorld := []byte("hello world")
   604  	encoded := encodeBase62(helloWorld)
   605  	decoded := decodeBase62(encoded)
   606  
   607  	if len(encoded) < len(helloWorld) {
   608  		t.Fatal("length of encoded base62 string", encoded, "should be >= than raw bytes!")
   609  	}
   610  
   611  	if bytes.Compare(helloWorld, decoded) != 0 {
   612  		t.Fatal(decoded, " != ", helloWorld)
   613  	}
   614  }
   615  
   616  func TestLexographicOrdering(t *testing.T) {
   617  	unsortedStrings := make([]string, 256)
   618  	for i := 0; i < 256; i++ {
   619  		s := string(encodeBase62([]byte{0, byte(i)}))
   620  		unsortedStrings[i] = strings.Repeat("0", 2-len(s)) + s
   621  	}
   622  
   623  	if !sort.StringsAreSorted(unsortedStrings) {
   624  		sortedStrings := make([]string, len(unsortedStrings))
   625  		for i, s := range unsortedStrings {
   626  			sortedStrings[i] = s
   627  		}
   628  		sort.Strings(sortedStrings)
   629  
   630  		t.Fatal("base62 encoder does not produce lexographically sorted output.",
   631  			"expected:", sortedStrings,
   632  			"actual:", unsortedStrings)
   633  	}
   634  }
   635  
   636  func TestBase62Value(t *testing.T) {
   637  	s := base62Characters
   638  
   639  	for i := range s {
   640  		v := int(base62Value(s[i]))
   641  
   642  		if v != i {
   643  			t.Error("bad value:")
   644  			t.Log("<<<", i)
   645  			t.Log(">>>", v)
   646  		}
   647  	}
   648  }
   649  
   650  func TestFastAppendEncodeBase62(t *testing.T) {
   651  	for i := 0; i != 1000; i++ {
   652  		id := New()
   653  
   654  		b0 := id[:]
   655  		b1 := appendEncodeBase62(nil, b0)
   656  		b2 := fastAppendEncodeBase62(nil, b0)
   657  
   658  		s1 := string(leftpad(b1, '0', stringEncodedLength))
   659  		s2 := string(b2)
   660  
   661  		if s1 != s2 {
   662  			t.Error("bad base62 representation of", id)
   663  			t.Log("<<<", s1, len(s1))
   664  			t.Log(">>>", s2, len(s2))
   665  		}
   666  	}
   667  }
   668  
   669  func TestFastAppendDecodeBase62(t *testing.T) {
   670  	for i := 0; i != 1000; i++ {
   671  		id := New()
   672  		b0 := leftpad(encodeBase62(id[:]), '0', stringEncodedLength)
   673  
   674  		b1 := appendDecodeBase62(nil, []byte(string(b0))) // because it modifies the input buffer
   675  		b2 := fastAppendDecodeBase62(nil, b0)
   676  
   677  		if !bytes.Equal(leftpad(b1, 0, byteLength), b2) {
   678  			t.Error("bad binary representation of", string(b0))
   679  			t.Log("<<<", b1)
   680  			t.Log(">>>", b2)
   681  		}
   682  	}
   683  }
   684  
   685  func BenchmarkAppendEncodeBase62(b *testing.B) {
   686  	a := [stringEncodedLength]byte{}
   687  	id := New()
   688  
   689  	for i := 0; i != b.N; i++ {
   690  		appendEncodeBase62(a[:0], id[:])
   691  	}
   692  }
   693  
   694  func BenchmarkAppendFastEncodeBase62(b *testing.B) {
   695  	a := [stringEncodedLength]byte{}
   696  	id := New()
   697  
   698  	for i := 0; i != b.N; i++ {
   699  		fastAppendEncodeBase62(a[:0], id[:])
   700  	}
   701  }
   702  
   703  func BenchmarkAppendDecodeBase62(b *testing.B) {
   704  	a := [byteLength]byte{}
   705  	id := []byte(New().String())
   706  
   707  	for i := 0; i != b.N; i++ {
   708  		b := [stringEncodedLength]byte{}
   709  		copy(b[:], id)
   710  		appendDecodeBase62(a[:0], b[:])
   711  	}
   712  }
   713  
   714  func BenchmarkAppendFastDecodeBase62(b *testing.B) {
   715  	a := [byteLength]byte{}
   716  	id := []byte(New().String())
   717  
   718  	for i := 0; i != b.N; i++ {
   719  		fastAppendDecodeBase62(a[:0], id)
   720  	}
   721  }
   722  
   723  // The functions bellow were the initial implementation of the base conversion
   724  // algorithms, they were replaced by optimized versions later on. We keep them
   725  // in the test files as a reference to ensure compatibility between the generic
   726  // and optimized implementations.
   727  func appendBase2Base(dst []byte, src []byte, inBase int, outBase int) []byte {
   728  	off := len(dst)
   729  	bs := src[:]
   730  	bq := [stringEncodedLength]byte{}
   731  
   732  	for len(bs) > 0 {
   733  		length := len(bs)
   734  		quotient := bq[:0]
   735  		remainder := 0
   736  
   737  		for i := 0; i != length; i++ {
   738  			acc := int(bs[i]) + remainder*inBase
   739  			d := acc/outBase | 0
   740  			remainder = acc % outBase
   741  
   742  			if len(quotient) > 0 || d > 0 {
   743  				quotient = append(quotient, byte(d))
   744  			}
   745  		}
   746  
   747  		// Appends in reverse order, the byte slice gets reversed before it's
   748  		// returned by the function.
   749  		dst = append(dst, byte(remainder))
   750  		bs = quotient
   751  	}
   752  
   753  	reverse(dst[off:])
   754  	return dst
   755  }
   756  
   757  func base2base(src []byte, inBase int, outBase int) []byte {
   758  	return appendBase2Base(nil, src, inBase, outBase)
   759  }
   760  
   761  func appendEncodeBase62(dst []byte, src []byte) []byte {
   762  	off := len(dst)
   763  	dst = appendBase2Base(dst, src, 256, 62)
   764  	for i, c := range dst[off:] {
   765  		dst[off+i] = base62Characters[c]
   766  	}
   767  	return dst
   768  }
   769  
   770  func encodeBase62(in []byte) []byte {
   771  	return appendEncodeBase62(nil, in)
   772  }
   773  
   774  func appendDecodeBase62(dst []byte, src []byte) []byte {
   775  	// Kind of intrusive, we modify the input buffer... it's OK here, it saves
   776  	// a memory allocation in Parse.
   777  	for i, b := range src {
   778  		// O(1)... technically. Has better real-world perf than a map
   779  		src[i] = byte(strings.IndexByte(base62Characters, b))
   780  	}
   781  	return appendBase2Base(dst, src, 62, 256)
   782  }
   783  
   784  func decodeBase62(src []byte) []byte {
   785  	return appendDecodeBase62(
   786  		make([]byte, 0, len(src)*2),
   787  		append(make([]byte, 0, len(src)), src...),
   788  	)
   789  }
   790  
   791  func reverse(b []byte) {
   792  	i := 0
   793  	j := len(b) - 1
   794  
   795  	for i < j {
   796  		b[i], b[j] = b[j], b[i]
   797  		i++
   798  		j--
   799  	}
   800  }
   801  
   802  func leftpad(b []byte, c byte, n int) []byte {
   803  	if n -= len(b); n > 0 {
   804  		for i := 0; i != n; i++ {
   805  			b = append(b, c)
   806  		}
   807  
   808  		copy(b[n:], b)
   809  
   810  		for i := 0; i != n; i++ {
   811  			b[i] = c
   812  		}
   813  	}
   814  	return b
   815  }
   816  
   817  func TestCompressedSet(t *testing.T) {
   818  	tests := []struct {
   819  		scenario string
   820  		function func(*testing.T)
   821  	}{
   822  		{
   823  			scenario: "String",
   824  			function: testCompressedSetString,
   825  		},
   826  		{
   827  			scenario: "GoString",
   828  			function: testCompressedSetGoString,
   829  		},
   830  		{
   831  			scenario: "sparse",
   832  			function: testCompressedSetSparse,
   833  		},
   834  		{
   835  			scenario: "packed",
   836  			function: testCompressedSetPacked,
   837  		},
   838  		{
   839  			scenario: "mixed",
   840  			function: testCompressedSetMixed,
   841  		},
   842  		{
   843  			scenario: "iterating over a nil compressed set returns no ids",
   844  			function: testCompressedSetNil,
   845  		},
   846  		{
   847  			scenario: "concatenating multiple compressed sets is supported",
   848  			function: testCompressedSetConcat,
   849  		},
   850  		{
   851  			scenario: "duplicate ids are appear only once in the compressed set",
   852  			function: testCompressedSetDuplicates,
   853  		},
   854  		{
   855  			scenario: "building a compressed set with a single id repeated multiple times produces the id only once",
   856  			function: testCompressedSetSingle,
   857  		},
   858  		{
   859  			scenario: "iterating over a compressed sequence returns the full sequence",
   860  			function: testCompressedSetSequence,
   861  		},
   862  	}
   863  
   864  	for _, test := range tests {
   865  		t.Run(test.scenario, test.function)
   866  	}
   867  }
   868  
   869  func testCompressedSetString(t *testing.T) {
   870  	id1, _ := Parse("0uHjRkQoL2JKAQIULPdqqb5fOkk")
   871  	id2, _ := Parse("0uHjRvkOG5CbtoXW5oCEp3L2xBu")
   872  	id3, _ := Parse("0uHjSJ4Pe5606kT2XWixK6dirlo")
   873  
   874  	set := Compress(id1, id2, id3)
   875  
   876  	if s := set.String(); s != `["0uHjRkQoL2JKAQIULPdqqb5fOkk", "0uHjRvkOG5CbtoXW5oCEp3L2xBu", "0uHjSJ4Pe5606kT2XWixK6dirlo"]` {
   877  		t.Error(s)
   878  	}
   879  }
   880  
   881  func testCompressedSetGoString(t *testing.T) {
   882  	id1, _ := Parse("0uHjRkQoL2JKAQIULPdqqb5fOkk")
   883  	id2, _ := Parse("0uHjRvkOG5CbtoXW5oCEp3L2xBu")
   884  	id3, _ := Parse("0uHjSJ4Pe5606kT2XWixK6dirlo")
   885  
   886  	set := Compress(id1, id2, id3)
   887  
   888  	if s := set.GoString(); s != `ksuid.CompressedSet{"0uHjRkQoL2JKAQIULPdqqb5fOkk", "0uHjRvkOG5CbtoXW5oCEp3L2xBu", "0uHjSJ4Pe5606kT2XWixK6dirlo"}` {
   889  		t.Error(s)
   890  	}
   891  }
   892  
   893  func testCompressedSetSparse(t *testing.T) {
   894  	now := time.Now()
   895  
   896  	times := [100]time.Time{}
   897  	for i := range times {
   898  		times[i] = now.Add(time.Duration(i) * 2 * time.Second)
   899  	}
   900  
   901  	ksuids := [1000]KSUID{}
   902  	for i := range ksuids {
   903  		ksuids[i], _ = NewRandomWithTime(times[i%len(times)])
   904  	}
   905  
   906  	set := Compress(ksuids[:]...)
   907  
   908  	for i, it := 0, set.Iter(); it.Next(); {
   909  		if i >= len(ksuids) {
   910  			t.Error("too many KSUIDs were produced by the set iterator")
   911  			break
   912  		}
   913  		if ksuids[i] != it.KSUID {
   914  			t.Errorf("bad KSUID at index %d: expected %s but found %s", i, ksuids[i], it.KSUID)
   915  		}
   916  		i++
   917  	}
   918  
   919  	reportCompressionRatio(t, ksuids[:], set)
   920  }
   921  
   922  func testCompressedSetPacked(t *testing.T) {
   923  	sequences := [10]Sequence{}
   924  	for i := range sequences {
   925  		sequences[i] = Sequence{Seed: New()}
   926  	}
   927  
   928  	ksuids := [1000]KSUID{}
   929  	for i := range ksuids {
   930  		ksuids[i], _ = sequences[i%len(sequences)].Next()
   931  	}
   932  
   933  	set := Compress(ksuids[:]...)
   934  
   935  	for i, it := 0, set.Iter(); it.Next(); {
   936  		if i >= len(ksuids) {
   937  			t.Error("too many KSUIDs were produced by the set iterator")
   938  			break
   939  		}
   940  		if ksuids[i] != it.KSUID {
   941  			t.Errorf("bad KSUID at index %d: expected %s but found %s", i, ksuids[i], it.KSUID)
   942  		}
   943  		i++
   944  	}
   945  
   946  	reportCompressionRatio(t, ksuids[:], set)
   947  }
   948  
   949  func testCompressedSetMixed(t *testing.T) {
   950  	now := time.Now()
   951  
   952  	times := [20]time.Time{}
   953  	for i := range times {
   954  		times[i] = now.Add(time.Duration(i) * 2 * time.Second)
   955  	}
   956  
   957  	sequences := [200]Sequence{}
   958  	for i := range sequences {
   959  		seed, _ := NewRandomWithTime(times[i%len(times)])
   960  		sequences[i] = Sequence{Seed: seed}
   961  	}
   962  
   963  	ksuids := [1000]KSUID{}
   964  	for i := range ksuids {
   965  		ksuids[i], _ = sequences[i%len(sequences)].Next()
   966  	}
   967  
   968  	set := Compress(ksuids[:]...)
   969  
   970  	for i, it := 0, set.Iter(); it.Next(); {
   971  		if i >= len(ksuids) {
   972  			t.Error("too many KSUIDs were produced by the set iterator")
   973  			break
   974  		}
   975  		if ksuids[i] != it.KSUID {
   976  			t.Errorf("bad KSUID at index %d: expected %s but found %s", i, ksuids[i], it.KSUID)
   977  		}
   978  		i++
   979  	}
   980  
   981  	reportCompressionRatio(t, ksuids[:], set)
   982  }
   983  
   984  func testCompressedSetDuplicates(t *testing.T) {
   985  	sequence := Sequence{Seed: New()}
   986  
   987  	ksuids := [1000]KSUID{}
   988  	for i := range ksuids[:10] {
   989  		ksuids[i], _ = sequence.Next() // exercise dedupe on the id range code path
   990  	}
   991  	for i := range ksuids[10:] {
   992  		ksuids[i+10] = New()
   993  	}
   994  	for i := 1; i < len(ksuids); i += 4 {
   995  		ksuids[i] = ksuids[i-1] // generate many dupes
   996  	}
   997  
   998  	miss := make(map[KSUID]struct{})
   999  	uniq := make(map[KSUID]struct{})
  1000  
  1001  	for _, id := range ksuids {
  1002  		miss[id] = struct{}{}
  1003  	}
  1004  
  1005  	set := Compress(ksuids[:]...)
  1006  
  1007  	for it := set.Iter(); it.Next(); {
  1008  		if _, dupe := uniq[it.KSUID]; dupe {
  1009  			t.Errorf("duplicate id found in compressed set: %s", it.KSUID)
  1010  		}
  1011  		uniq[it.KSUID] = struct{}{}
  1012  		delete(miss, it.KSUID)
  1013  	}
  1014  
  1015  	if len(miss) != 0 {
  1016  		t.Error("some ids were not found in the compressed set:")
  1017  		for id := range miss {
  1018  			t.Log(id)
  1019  		}
  1020  	}
  1021  }
  1022  
  1023  func testCompressedSetSingle(t *testing.T) {
  1024  	id := New()
  1025  
  1026  	set := Compress(
  1027  		id, id, id, id, id, id, id, id, id, id,
  1028  		id, id, id, id, id, id, id, id, id, id,
  1029  		id, id, id, id, id, id, id, id, id, id,
  1030  		id, id, id, id, id, id, id, id, id, id,
  1031  	)
  1032  
  1033  	n := 0
  1034  
  1035  	for it := set.Iter(); it.Next(); {
  1036  		if n != 0 {
  1037  			t.Errorf("too many ids found in the compressed set: %s", it.KSUID)
  1038  		} else if id != it.KSUID {
  1039  			t.Errorf("invalid id found in the compressed set: %s != %s", it.KSUID, id)
  1040  		}
  1041  		n++
  1042  	}
  1043  
  1044  	if n == 0 {
  1045  		t.Error("no ids were produced by the compressed set")
  1046  	}
  1047  }
  1048  
  1049  func testCompressedSetSequence(t *testing.T) {
  1050  	seq := Sequence{Seed: New()}
  1051  
  1052  	ids := make([]KSUID, 5)
  1053  
  1054  	for i := 0; i < 5; i++ {
  1055  		ids[i], _ = seq.Next()
  1056  	}
  1057  
  1058  	iter := Compress(ids...).Iter()
  1059  
  1060  	index := 0
  1061  	for iter.Next() {
  1062  		if iter.KSUID != ids[index] {
  1063  			t.Errorf("mismatched id at index %d: %s != %s", index, iter.KSUID, ids[index])
  1064  		}
  1065  		index++
  1066  	}
  1067  
  1068  	if index != 5 {
  1069  		t.Errorf("Expected 5 ids, got %d", index)
  1070  	}
  1071  }
  1072  
  1073  func testCompressedSetNil(t *testing.T) {
  1074  	set := CompressedSet(nil)
  1075  
  1076  	for it := set.Iter(); it.Next(); {
  1077  		t.Errorf("too many ids returned by the iterator of a nil compressed set: %s", it.KSUID)
  1078  	}
  1079  }
  1080  
  1081  func testCompressedSetConcat(t *testing.T) {
  1082  	ksuids := [100]KSUID{}
  1083  
  1084  	for i := range ksuids {
  1085  		ksuids[i] = New()
  1086  	}
  1087  
  1088  	set := CompressedSet(nil)
  1089  	set = AppendCompressed(set, ksuids[:42]...)
  1090  	set = AppendCompressed(set, ksuids[42:64]...)
  1091  	set = AppendCompressed(set, ksuids[64:]...)
  1092  
  1093  	for i, it := 0, set.Iter(); it.Next(); i++ {
  1094  		if ksuids[i] != it.KSUID {
  1095  			t.Errorf("invalid ID at index %d: %s != %s", i, ksuids[i], it.KSUID)
  1096  		}
  1097  	}
  1098  }
  1099  
  1100  func reportCompressionRatio(t *testing.T, ksuids []KSUID, set CompressedSet) {
  1101  	len1 := byteLength * len(ksuids)
  1102  	len2 := len(set)
  1103  	t.Logf("original %d B, compressed %d B (%.4g%%)", len1, len2, 100*(1-(float64(len2)/float64(len1))))
  1104  }
  1105  
  1106  func BenchmarkCompressedSet(b *testing.B) {
  1107  	ksuids1 := [1000]KSUID{}
  1108  	ksuids2 := [1000]KSUID{}
  1109  
  1110  	for i := range ksuids1 {
  1111  		ksuids1[i] = New()
  1112  	}
  1113  
  1114  	ksuids2 = ksuids1
  1115  	buf := make([]byte, 0, 1024)
  1116  	set := Compress(ksuids2[:]...)
  1117  
  1118  	b.Run("write", func(b *testing.B) {
  1119  		n := 0
  1120  		for i := 0; i != b.N; i++ {
  1121  			ksuids2 = ksuids1
  1122  			buf = AppendCompressed(buf[:0], ksuids2[:]...)
  1123  			n = len(buf)
  1124  		}
  1125  		b.SetBytes(int64(n + len(ksuids2)))
  1126  	})
  1127  
  1128  	b.Run("read", func(b *testing.B) {
  1129  		n := 0
  1130  		for i := 0; i != b.N; i++ {
  1131  			n = 0
  1132  			for it := set.Iter(); true; {
  1133  				if !it.Next() {
  1134  					n++
  1135  					break
  1136  				}
  1137  			}
  1138  		}
  1139  		b.SetBytes(int64((n * byteLength) + len(set)))
  1140  	})
  1141  }