github.com/philpearl/plenc@v0.0.15/plenccodec/string_test.go (about)

     1  package plenccodec_test
     2  
     3  import (
     4  	"reflect"
     5  	"sync"
     6  	"testing"
     7  	"unsafe"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	"github.com/philpearl/plenc"
    11  	"github.com/philpearl/plenc/plenccodec"
    12  )
    13  
    14  func TestString(t *testing.T) {
    15  	tests := []string{
    16  		"",
    17  		"a",
    18  		"¬πø∆˙©¥å∫˜",
    19  		"this is a string",
    20  		"ɚ&d珿ȨDT葚µ噘",
    21  		"\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00",
    22  		`¶娋搱ĚoÞB@\贞敠ơƸŜ`,
    23  		"\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00",
    24  	}
    25  
    26  	for _, test := range tests {
    27  		t.Run(test, func(t *testing.T) {
    28  			c, err := plenc.CodecForType(reflect.TypeOf(test))
    29  			if err != nil {
    30  				t.Fatal(err)
    31  			}
    32  
    33  			l := c.Size(unsafe.Pointer(&test), nil)
    34  
    35  			data := c.Append(nil, unsafe.Pointer(&test), nil)
    36  
    37  			if len(data) != l {
    38  				t.Errorf("data not expected length. %d %d", l, len(data))
    39  			}
    40  
    41  			var out string
    42  			_, err = c.Read(data, unsafe.Pointer(&out), c.WireType())
    43  			if err != nil {
    44  				t.Fatal(err)
    45  			}
    46  
    47  			if diff := cmp.Diff(test, out); diff != "" {
    48  				t.Fatal(diff)
    49  			}
    50  		})
    51  	}
    52  }
    53  
    54  func TestStringMarshal(t *testing.T) {
    55  	tests := []string{
    56  		"",
    57  		"a",
    58  		"¬πø∆˙©¥å∫˜",
    59  		"this is a string",
    60  		"ɚ&d珿ȨDT葚µ噘",
    61  		"\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00",
    62  		`¶娋搱ĚoÞB@\贞敠ơƸŜ`,
    63  		"\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00",
    64  	}
    65  
    66  	for _, test := range tests {
    67  		t.Run(test, func(t *testing.T) {
    68  			data, err := plenc.Marshal(nil, test)
    69  			if err != nil {
    70  				t.Fatal(err)
    71  			}
    72  
    73  			var out string
    74  			if err := plenc.Unmarshal(data, &out); err != nil {
    75  				t.Fatal(err)
    76  			}
    77  
    78  			if diff := cmp.Diff(test, out); diff != "" {
    79  				t.Fatal(diff)
    80  			}
    81  		})
    82  	}
    83  }
    84  
    85  func TestInternedString(t *testing.T) {
    86  	var c plenccodec.InternedStringCodec
    87  
    88  	values := []string{
    89  		"hat", "cheese", "elephant", "hat", "hat", "cheese", "elephant",
    90  	}
    91  
    92  	var data []byte
    93  	allocs := testing.AllocsPerRun(1000, func() {
    94  		for _, test := range values {
    95  			l := c.Size(unsafe.Pointer(&test), nil)
    96  			data = c.Append(data[:0], unsafe.Pointer(&test), nil)
    97  
    98  			if len(data) != l {
    99  				t.Errorf("data not expected length. %d %d", l, len(data))
   100  			}
   101  
   102  			var out string
   103  			_, err := c.Read(data, unsafe.Pointer(&out), c.WireType())
   104  			if err != nil {
   105  				t.Fatal(err)
   106  			}
   107  
   108  			if out != test {
   109  				t.Fatalf("mismatch %q, %q", test, out)
   110  			}
   111  
   112  		}
   113  	})
   114  
   115  	if allocs > 0.1 {
   116  		t.Fatal(allocs)
   117  	}
   118  }
   119  
   120  func TestInternedStringParallel(t *testing.T) {
   121  	type my struct {
   122  		V string `plenc:"1,intern"`
   123  	}
   124  
   125  	values := []string{
   126  		"hat", "cheese", "elephant", "hat", "hat", "cheese", "elephant",
   127  	}
   128  	var wg sync.WaitGroup
   129  	wg.Add(100)
   130  	for i := 0; i < 100; i++ {
   131  		go func() {
   132  			defer wg.Done()
   133  			var data []byte
   134  			var val, out my
   135  			for i := 0; i < 1000; i++ {
   136  				for _, test := range values {
   137  					val.V = test
   138  					data, err := plenc.Marshal(data[:0], &val)
   139  					if err != nil {
   140  						t.Error(err)
   141  						return
   142  					}
   143  
   144  					if err := plenc.Unmarshal(data, &out); err != nil {
   145  						t.Error(err)
   146  						return
   147  					}
   148  
   149  					if out.V != test {
   150  						t.Errorf("mismatch %q, %q", test, out)
   151  						return
   152  					}
   153  				}
   154  			}
   155  		}()
   156  	}
   157  	wg.Wait()
   158  }
   159  
   160  func BenchmarkInternedString(b *testing.B) {
   161  	type myb struct {
   162  		V string `plenc:"1,intern"`
   163  	}
   164  
   165  	values := []string{
   166  		"hat", "cheese", "elephant", "hat", "hat", "cheese", "elephant",
   167  	}
   168  	var data []byte
   169  	var val, out myb
   170  	for _, test := range values {
   171  		val.V = test
   172  		var err error
   173  		data, err = plenc.Marshal(data[:0], &val)
   174  		if err != nil {
   175  			b.Fatal(err)
   176  		}
   177  
   178  		if err := plenc.Unmarshal(data, &out); err != nil {
   179  			b.Fatal(err)
   180  		}
   181  
   182  		if out.V != test {
   183  			b.Fatalf("mismatch %q, %q", test, out)
   184  		}
   185  	}
   186  
   187  	b.ResetTimer()
   188  	b.ReportAllocs()
   189  	for i := 0; i < b.N; i++ {
   190  		out = myb{}
   191  		if err := plenc.Unmarshal(data, &out); err != nil {
   192  			b.Fatal(err)
   193  		}
   194  
   195  		if out.V != "elephant" {
   196  			b.Fatalf("mismatch %q, %q", "elephant", out)
   197  		}
   198  	}
   199  }
   200  
   201  func TestStringSlice(t *testing.T) {
   202  	v := []string{
   203  		"M珣X觻%ƾƽ9J9S腸H滩Ýk",
   204  		"Eŕ漠",
   205  		"织睱Ħ7õ咖Ê騄",
   206  		"t沋晛岊ıƭ宋Yȯ¿q&",
   207  		"Ʊãʙ#訃睩愴émė6Ɍ邔5汚鞗Ƈ",
   208  		"桏&",
   209  		"?Ȗ曽Ȯɕ稌!r囮ǯWQ猒÷飹嫗MJ",
   210  		"",
   211  	}
   212  
   213  	c, err := plenc.CodecForType(reflect.TypeOf(v))
   214  	if err != nil {
   215  		t.Fatal(err)
   216  	}
   217  
   218  	data := c.Append(nil, unsafe.Pointer(&v), nil)
   219  
   220  	var out []string
   221  	_, err = c.Read(data, unsafe.Pointer(&out), c.WireType())
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  
   226  	if diff := cmp.Diff(v, out); diff != "" {
   227  		t.Fatal(diff)
   228  	}
   229  }
   230  
   231  func TestStringSliceCompat(t *testing.T) {
   232  	type my struct {
   233  		V []string `plenc:"1"`
   234  	}
   235  	v := my{V: []string{
   236  		"M珣X觻%ƾƽ9J9S腸H滩Ýk",
   237  		"Eŕ漠",
   238  		"织睱Ħ7õ咖Ê騄",
   239  		"t沋晛岊ıƭ宋Yȯ¿q&",
   240  		"Ʊãʙ#訃睩愴émė6Ɍ邔5汚鞗Ƈ",
   241  		"桏&",
   242  		"?Ȗ曽Ȯɕ稌!r囮ǯWQ猒÷飹嫗MJ",
   243  		"",
   244  		"cheese it",
   245  		"hats",
   246  	}}
   247  
   248  	var pl plenc.Plenc
   249  	pl.ProtoCompatibleArrays = true
   250  	pl.RegisterDefaultCodecs()
   251  
   252  	data, err := pl.Marshal(nil, v)
   253  	if err != nil {
   254  		t.Fatal(err)
   255  	}
   256  
   257  	var out my
   258  
   259  	if err := pl.Unmarshal(data, &out); err != nil {
   260  		t.Fatal(err)
   261  	}
   262  
   263  	if diff := cmp.Diff(v, out); diff != "" {
   264  		t.Fatal(diff)
   265  	}
   266  }