You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by yu...@apache.org on 2021/02/04 05:08:53 UTC

[thrift] branch master updated: THRIFT-5337 Go set fields write improvement

This is an automated email from the ASF dual-hosted git repository.

yuxuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/thrift.git


The following commit(s) were added to refs/heads/master by this push:
     new 4aaef75  THRIFT-5337 Go set fields write improvement
4aaef75 is described below

commit 4aaef75819aea3f79ec812393dbe13bb62bd6c45
Author: wangtieju <wa...@bytedance.com>
AuthorDate: Thu Feb 4 11:26:44 2021 +0800

    THRIFT-5337 Go set fields write improvement
    
    Client: go
    
    There is a duplicate elements check for set in writeFields* function,
    and it compares elements using reflect.DeepEqual which is expensive.
    
    It's much faster that generates a *Equals* function for set elements and
    call it in duplicate elements check, especially for nested struct
    element.
    
    Closes #2307.
---
 compiler/cpp/src/thrift/generate/t_go_generator.cc | 199 ++++++++++++++++-
 lib/go/test/EqualsTest.thrift                      | 109 +++++++++
 lib/go/test/Makefile.am                            |   7 +-
 lib/go/test/tests/equals_test.go                   | 243 +++++++++++++++++++++
 4 files changed, 549 insertions(+), 9 deletions(-)

diff --git a/compiler/cpp/src/thrift/generate/t_go_generator.cc b/compiler/cpp/src/thrift/generate/t_go_generator.cc
index 33cd12d..d9f9d51 100644
--- a/compiler/cpp/src/thrift/generate/t_go_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_go_generator.cc
@@ -152,6 +152,7 @@ public:
                                  const string& tstruct_name,
                                  bool is_result = false,
                                  bool uses_countsetfields = false);
+  void generate_go_struct_equals(std::ostream& out, t_struct* tstruct, const string& tstruct_name);
   void generate_go_function_helpers(t_function* tfunction);
   void get_publicized_name_and_def_value(t_field* tfield,
                                          string* OUT_pub_name,
@@ -229,6 +230,12 @@ public:
 
   void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter);
 
+  void generate_go_equals(std::ostream& out, t_type* ttype, string tgt, string src);
+
+  void generate_go_equals_struct(std::ostream& out, t_type* ttype, string tgt, string src);
+
+  void generate_go_equals_container(std::ostream& out, t_type* ttype, string tgt, string src);
+
   void generate_go_docstring(std::ostream& out, t_struct* tstruct);
 
   void generate_go_docstring(std::ostream& out, t_function* tfunction);
@@ -307,6 +314,7 @@ private:
   std::set<std::string> package_identifiers_set_;
   std::string read_method_name_;
   std::string write_method_name_;
+  std::string equals_method_name_;
 
   std::set<std::string> commonInitialisms;
 
@@ -724,6 +732,7 @@ void t_go_generator::init_generator() {
     read_method_name_ = "Read";
     write_method_name_ = "Write";
   }
+  equals_method_name_ = "Equals";
 
   while (true) {
     // TODO: Do better error checking here.
@@ -912,7 +921,6 @@ string t_go_generator::go_imports_begin(bool consts) {
   std::vector<string> system_packages;
   system_packages.push_back("bytes");
   system_packages.push_back("context");
-  system_packages.push_back("reflect");
   // If not writing constants, and there are enums, need extra imports.
   if (!consts && get_program()->get_enums().size() > 0) {
     system_packages.push_back("database/sql/driver");
@@ -937,7 +945,6 @@ string t_go_generator::go_imports_end() {
       "var _ = thrift.ZERO\n"
       "var _ = fmt.Printf\n"
       "var _ = context.Background\n"
-      "var _ = reflect.DeepEqual\n"
       "var _ = time.Now\n"
       "var _ = bytes.Equal\n\n");
 }
@@ -1482,6 +1489,9 @@ void t_go_generator::generate_go_struct_definition(ostream& out,
   generate_isset_helpers(out, tstruct, tstruct_name, is_result);
   generate_go_struct_reader(out, tstruct, tstruct_name, is_result);
   generate_go_struct_writer(out, tstruct, tstruct_name, is_result, num_setable > 0);
+  if (!is_result && !is_args) {
+    generate_go_struct_equals(out, tstruct, tstruct_name);
+  }
 
   out << indent() << "func (p *" << tstruct_name << ") String() string {" << endl;
   out << indent() << "  if p == nil {" << endl;
@@ -1851,6 +1861,61 @@ void t_go_generator::generate_go_struct_writer(ostream& out,
   }
 }
 
+void t_go_generator::generate_go_struct_equals(ostream& out,
+                                               t_struct* tstruct,
+                                               const string& tstruct_name) {
+  string name(tstruct->get_name());
+  const vector<t_field*>& fields = tstruct->get_sorted_members();
+  vector<t_field*>::const_iterator f_iter;
+  indent(out) << "func (p *" << tstruct_name << ") " << equals_method_name_ << "(other *"
+              << tstruct_name << ") bool {" << endl;
+  indent_up();
+
+  string field_name;
+  string publicize_field_name;
+  out << indent() << "if p == other {" << endl;
+  indent_up();
+  out << indent() << "return true" << endl;
+  indent_down();
+  out << indent() << "} else if p == nil || other == nil {" << endl;
+  indent_up();
+  out << indent() << "return false" << endl;
+  indent_down();
+  out << indent() << "}" << endl;
+
+  for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
+    field_name = (*f_iter)->get_name();
+    t_type* field_type = (*f_iter)->get_type();
+    publicize_field_name = publicize(field_name);
+    string goType = type_to_go_type_with_opt(field_type, is_pointer_field(*f_iter));
+
+    string tgt = "p." + publicize_field_name;
+    string src = "other." + publicize_field_name;
+    t_type* ttype = field_type->get_true_type();
+    // Compare field contents
+    if (is_pointer_field(*f_iter)
+        && (ttype->is_base_type() || ttype->is_enum() || ttype->is_container())) {
+      string tgtv = "(*" + tgt + ")";
+      string srcv = "(*" + src + ")";
+      out << indent() << "if " << tgt << " != " << src << " {" << endl;
+      indent_up();
+      out << indent() << "if " << tgt << " == nil || " << src << " == nil {" << endl;
+      indent_up();
+      out << indent() << "return false" << endl;
+      indent_down();
+      out << indent() << "}" << endl;
+      generate_go_equals(out, field_type, tgtv, srcv);
+      indent_down();
+      out << indent() << "}" << endl;
+    } else {
+      generate_go_equals(out, field_type, tgt, src);
+    }
+  }
+  out << indent() << "return true" << endl;
+  indent_down();
+  out << indent() << "}" << endl << endl;
+}
+
 /**
  * Generates a thrift service.
  *
@@ -3389,15 +3454,30 @@ void t_go_generator::generate_serialize_container(ostream& out,
   } else if (ttype->is_set()) {
     t_set* tset = (t_set*)ttype;
     out << indent() << "for i := 0; i<len(" << prefix << "); i++ {" << endl;
-    out << indent() << "  for j := i+1; j<len(" << prefix << "); j++ {" << endl;
+    indent_up();
+    out << indent() << "for j := i+1; j<len(" << prefix << "); j++ {" << endl;
+    indent_up();
     string wrapped_prefix = prefix;
     if (pointer_field) {
       wrapped_prefix = "(" + prefix + ")";
     }
-    out << indent() << "    if reflect.DeepEqual(" << wrapped_prefix << "[i]," << wrapped_prefix << "[j]) { " << endl;
-    out << indent() << "      return thrift.PrependError(\"\", fmt.Errorf(\"%T error writing set field: slice is not unique\", " << wrapped_prefix << "[i]))" << endl;
-    out << indent() << "    }" << endl;
-    out << indent() << "  }" << endl;
+    string goType = type_to_go_type(tset->get_elem_type());
+    out << indent() << "if func(tgt, src " << goType << ") bool {" << endl;
+    indent_up();
+    generate_go_equals(out, tset->get_elem_type(), "tgt", "src");
+    out << indent() << "return true" << endl;
+    indent_down();
+    out << indent() << "}(" << wrapped_prefix << "[i], " << wrapped_prefix << "[j]) {" << endl;
+    indent_up();
+    out << indent()
+        << "return thrift.PrependError(\"\", fmt.Errorf(\"%T error writing set field: slice is not "
+           "unique\", "
+        << wrapped_prefix << "))" << endl;
+    indent_down();
+    out << indent() << "}" << endl;
+    indent_down();
+    out << indent() << "}" << endl;
+    indent_down();
     out << indent() << "}" << endl;
     out << indent() << "for _, v := range " << prefix << " {" << endl;
     indent_up();
@@ -3464,6 +3544,111 @@ void t_go_generator::generate_serialize_list_element(ostream& out, t_list* tlist
 }
 
 /**
+ * Compares any type
+ */
+void t_go_generator::generate_go_equals(ostream& out, t_type* ori_type, string tgt, string src) {
+
+  t_type* ttype = get_true_type(ori_type);
+  // Do nothing for void types
+  if (ttype->is_void()) {
+    throw "compiler error: cannot generate equals for void type: " + tgt;
+  }
+
+  if (ttype->is_struct() || ttype->is_xception()) {
+    generate_go_equals_struct(out, ttype, tgt, src);
+  } else if (ttype->is_container()) {
+    generate_go_equals_container(out, ttype, tgt, src);
+  } else if (ttype->is_base_type() || ttype->is_enum()) {
+    out << indent() << "if ";
+    if (ttype->is_base_type()) {
+      t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
+      switch (tbase) {
+      case t_base_type::TYPE_VOID:
+        throw "compiler error: cannot equals void: " + tgt;
+        break;
+
+      case t_base_type::TYPE_STRING:
+        if (ttype->is_binary()) {
+          out << "bytes.Compare(" << tgt << ", " << src << ") != 0";
+        } else {
+          out << tgt << " != " << src;
+        }
+        break;
+
+      case t_base_type::TYPE_BOOL:
+      case t_base_type::TYPE_I8:
+      case t_base_type::TYPE_I16:
+      case t_base_type::TYPE_I32:
+      case t_base_type::TYPE_I64:
+      case t_base_type::TYPE_DOUBLE:
+        out << tgt << " != " << src;
+        break;
+
+      default:
+        throw "compiler error: no Go name for base type " + t_base_type::t_base_name(tbase);
+      }
+    } else if (ttype->is_enum()) {
+      out << tgt << " != " << src;
+    }
+
+    out << " { return false }" << endl;
+  } else {
+    throw "compiler error: Invalid type in generate_go_equals '" + ttype->get_name() + "' for '"
+        + tgt + "'";
+  }
+}
+
+/**
+ * Compares the members of a struct
+ */
+void t_go_generator::generate_go_equals_struct(ostream& out,
+                                               t_type* ttype,
+                                               string tgt,
+                                               string src) {
+  (void)ttype;
+  out << indent() << "if !" << tgt << "." << equals_method_name_ << "(" << src
+      << ") { return false }" << endl;
+}
+
+/**
+ * Compares any container type
+ */
+void t_go_generator::generate_go_equals_container(ostream& out,
+                                                  t_type* ttype,
+                                                  string tgt,
+                                                  string src) {
+  out << indent() << "if len(" << tgt << ") != len(" << src << ") { return false }" << endl;
+  if (ttype->is_map()) {
+    t_map* tmap = (t_map*)ttype;
+    out << indent() << "for k, _tgt := range " << tgt << " {" << endl;
+    indent_up();
+    string element_source = tmp("_src");
+    out << indent() << element_source << " := " << src << "[k]" << endl;
+    generate_go_equals(out, tmap->get_val_type(), "_tgt", element_source);
+    indent_down();
+    indent(out) << "}" << endl;
+  } else if (ttype->is_list() || ttype->is_set()) {
+    t_type* elem;
+    if (ttype->is_list()) {
+      t_list* temp = (t_list*)ttype;
+      elem = temp->get_elem_type();
+    } else {
+      t_set* temp = (t_set*)ttype;
+      elem = temp->get_elem_type();
+    }
+    out << indent() << "for i, _tgt := range " << tgt << " {" << endl;
+    indent_up();
+    string element_source = tmp("_src");
+    out << indent() << element_source << " := " << src << "[i]" << endl;
+    generate_go_equals(out, elem, "_tgt", element_source);
+    indent_down();
+    indent(out) << "}" << endl;
+  } else {
+    throw "INVALID TYPE IN generate_go_equals_container '" + ttype->get_name();
+  }
+}
+
+/**
  * Generates the docstring for a given struct.
  */
 void t_go_generator::generate_go_docstring(ostream& out, t_struct* tstruct) {
diff --git a/lib/go/test/EqualsTest.thrift b/lib/go/test/EqualsTest.thrift
new file mode 100644
index 0000000..c699232
--- /dev/null
+++ b/lib/go/test/EqualsTest.thrift
@@ -0,0 +1,109 @@
+typedef i8 mybyte
+typedef string mystr
+typedef binary mybin
+
+enum EnumFoo {
+  e1
+  e2
+}
+
+struct BasicEqualsFoo {
+  1: bool BoolFoo,
+  2: optional bool OptBoolFoo,
+  3: i8 I8Foo,
+  4: optional i8 OptI8Foo,
+  5: i16 I16Foo,
+  6: optional i16 OptI16Foo,
+  7: i32 I32Foo,
+  8: optional i32 OptI32Foo,
+  9: i64 I64Foo,
+  10: optional i64 OptI64Foo,
+  11: double DoubleFoo,
+  12: optional double OptDoubleFoo,
+  13: string StrFoo,
+  14: optional string OptStrFoo,
+  15: binary BinFoo,
+  16: optional binary OptBinFoo,
+  17: EnumFoo EnumFoo,
+  18: optional EnumFoo OptEnumFoo,
+  19: mybyte MyByteFoo,
+  20: optional mybyte OptMyByteFoo,
+  21: mystr MyStrFoo,
+  22: optional mystr OptMyStrFoo,
+  23: mybin MyBinFoo,
+  24: optional mybin OptMyBinFoo,
+}
+
+struct StructEqualsFoo {
+  1: BasicEqualsFoo StructFoo,
+  2: optional BasicEqualsFoo OptStructFoo,
+}
+
+struct ListEqualsFoo {
+    1: list<i64> I64ListFoo,
+    2: optional list<i64> OptI64ListFoo,
+    3: list<string> StrListFoo,
+    4: optional list<string> OptStrListFoo,
+    5: list<binary> BinListFoo,
+    6: optional list<binary> OptBinListFoo,
+    7: list<BasicEqualsFoo> StructListFoo,
+    8: optional list<BasicEqualsFoo> OptStructListFoo,
+    9: list<list<i64>> I64ListListFoo,
+    10: optional list<list<i64>> OptI64ListListFoo,
+    11: list<set<i64>> I64SetListFoo,
+    12: optional list<set<i64>> OptI64SetListFoo,
+    13: list<map<i64, string>> I64StringMapListFoo,
+    14: optional list<map<i64, string>> OptI64StringMapListFoo,
+    15: list<mybyte> MyByteListFoo,
+    16: optional list<mybyte> OptMyByteListFoo,
+    17: list<mystr> MyStrListFoo,
+    18: optional list<mystr> OptMyStrListFoo,
+    19: list<mybin> MyBinListFoo,
+    20: optional list<mybin> OptMyBinListFoo,
+}
+
+struct SetEqualsFoo {
+    1: set<i64> I64SetFoo,
+    2: optional set<i64> OptI64SetFoo,
+    3: set<string> StrSetFoo,
+    4: optional set<string> OptStrSetFoo,
+    5: set<binary> BinSetFoo,
+    6: optional set<binary> OptBinSetFoo,
+    7: set<BasicEqualsFoo> StructSetFoo,
+    8: optional set<BasicEqualsFoo> OptStructSetFoo,
+    9: set<list<i64>> I64ListSetFoo,
+    10: optional set<list<i64>> OptI64ListSetFoo,
+    11: set<set<i64>> I64SetSetFoo,
+    12: optional set<set<i64>> OptI64SetSetFoo,
+    13: set<map<i64, string>> I64StringMapSetFoo,
+    14: optional set<map<i64, string>> OptI64StringMapSetFoo,
+    15: set<mybyte> MyByteSetFoo,
+    16: optional set<mybyte> OptMyByteSetFoo,
+    17: set<mystr> MyStrSetFoo,
+    18: optional set<mystr> OptMyStrSetFoo,
+    19: set<mybin> MyBinSetFoo,
+    20: optional set<mybin> OptMyBinSetFoo,
+}
+
+struct MapEqualsFoo {
+    1: map<i64, string> I64StrMapFoo,
+    2: optional map<i64, string> OptI64StrMapFoo,
+    3: map<string, i64> StrI64MapFoo,
+    4: optional map<string, i64>  OptStrI64MapFoo,
+    5: map<BasicEqualsFoo, binary> StructBinMapFoo,
+    6: optional map<BasicEqualsFoo, binary> OptStructBinMapFoo,
+    7: map<binary, BasicEqualsFoo> BinStructMapFoo,
+    8: optional map<binary, BasicEqualsFoo> OptBinStructMapFoo,
+    9: map<i64, list<i64>> I64I64ListMapFoo,
+    10: optional map<i64, list<i64>> OptI64I64ListMapFoo,
+    11: map<i64, set<i64>> I64I64SetMapFoo,
+    12: optional map<i64, set<i64>> OptI64I64SetMapFoo,
+    13: map<i64, map<i64, string>> I64I64StringMapMapFoo,
+    14: optional map<i64, map<i64, string>> OptI64I64StringMapMapFoo,
+    15: map<mystr, mybin> MyStrMyBinMapFoo,
+    16: optional map<mystr, mybin> OptMyStrMyBinMapFoo,
+    17: map<i64, mybyte> Int64MyByteMapFoo,
+    18: optional map<i64, mybyte> OptInt64MyByteMapFoo,
+    19: map<mybyte, i64> MyByteInt64MapFoo,
+    20: optional map<mybyte, i64> OptMyByteInt64MapFoo,
+}
\ No newline at end of file
diff --git a/lib/go/test/Makefile.am b/lib/go/test/Makefile.am
index f5de26e..4419577 100644
--- a/lib/go/test/Makefile.am
+++ b/lib/go/test/Makefile.am
@@ -73,6 +73,7 @@ gopath: $(THRIFT) $(THRIFTTEST) \
 	$(THRIFT) $(THRIFTARGS) ConflictNamespaceTestSuperThing.thrift
 	$(THRIFT) $(THRIFTARGS) ConflictNamespaceServiceTest.thrift
 	$(THRIFT) $(THRIFTARGS) -r DuplicateImportsTest.thrift
+	$(THRIFT) $(THRIFTARGS) EqualsTest.thrift
 	GOPATH=`pwd`/gopath $(GO) get github.com/golang/mock/gomock || true
 	sed -i 's/\"context\"/\"golang.org\/x\/net\/context\"/g' gopath/src/github.com/golang/mock/gomock/controller.go || true
 	GOPATH=`pwd`/gopath $(GO) get github.com/golang/mock/gomock
@@ -97,7 +98,8 @@ check: gopath
 				conflictnamespacetestsuperthing \
 				conflict/context/conflict_service-remote \
 				servicestest/container_test-remote \
-				duplicateimportstest
+				duplicateimportstest \
+				equalstest
 	GOPATH=`pwd`/gopath $(GO) test thrift tests dontexportrwtest
 
 clean-local:
@@ -132,4 +134,5 @@ EXTRA_DIST = \
 	ConflictNamespaceTestC.thrift \
 	ConflictNamespaceTestD.thrift \
 	ConflictNamespaceTestSuperThing.thrift \
-	ConflictNamespaceServiceTest.thrift
+	ConflictNamespaceServiceTest.thrift \
+	EqualsTest.thrift
diff --git a/lib/go/test/tests/equals_test.go b/lib/go/test/tests/equals_test.go
new file mode 100644
index 0000000..3489efa
--- /dev/null
+++ b/lib/go/test/tests/equals_test.go
@@ -0,0 +1,243 @@
+package tests
+
+import (
+	"equalstest"
+	"strconv"
+	"testing"
+)
+
+func TestEquals(t *testing.T) {
+	// test basic field
+	basicTgt, basicSrc := genBasicFoo(), genBasicFoo()
+	if !basicTgt.Equals(basicSrc) {
+		t.Error("BasicEqualsFoo.Equals() test failed")
+	}
+	basicSrc.EnumFoo = equalstest.EnumFoo_e2
+	if basicTgt.Equals(basicSrc) {
+		t.Error("BasicEqualsFoo.Equals() test failed")
+	}
+	basicSrc = genBasicFoo()
+	basicSrc.OptBoolFoo = nil
+	if basicTgt.Equals(basicSrc) || basicSrc.Equals(basicTgt) {
+		t.Error("BasicEqualsFoo.Equals() test failed")
+	}
+	if !(&equalstest.BasicEqualsFoo{}).Equals(&equalstest.BasicEqualsFoo{}) {
+		t.Error("BasicEqualsFoo.Equals() test failed")
+	}
+	// test struct field
+	structTgt, structSrc := genStructFoo(), genStructFoo()
+	if !structTgt.Equals(structSrc) {
+		t.Error("StructEqualsFoo.Equals() test failed")
+	}
+	structSrc.OptStructFoo.EnumFoo = equalstest.EnumFoo_e2
+	if structTgt.Equals(structSrc) {
+		t.Error("StructEqualsFoo.Equals() test failed")
+	}
+	structSrc = genStructFoo()
+	structSrc.OptStructFoo = nil
+	if structTgt.Equals(structSrc) || structSrc.Equals(structTgt) {
+		t.Error("StructEqualsFoo.Equals() test failed")
+	}
+	if !(&equalstest.StructEqualsFoo{}).Equals(&equalstest.StructEqualsFoo{}) {
+		t.Error("StructEqualsFoo.Equals() test failed")
+	}
+	// test list field
+	listTgt, listSrc := genListFoo(), genListFoo()
+	if !listTgt.Equals(listSrc) {
+		t.Error("ListEqualsFoo.Equals() test failed")
+	}
+	listSrc.OptI64StringMapListFoo[0][1] = "0"
+	if listTgt.Equals(listSrc) {
+		t.Error("ListEqualsFoo.Equals() test failed")
+	}
+	listSrc = genListFoo()
+	listSrc.OptI64StringMapListFoo = nil
+	if listTgt.Equals(listSrc) || listSrc.Equals(listTgt) {
+		t.Error("ListEqualsFoo.Equals() test failed")
+	}
+	if !(&equalstest.ListEqualsFoo{}).Equals(&equalstest.ListEqualsFoo{}) {
+		t.Error("ListEqualsFoo.Equals() test failed")
+	}
+	// test set field
+	setTgt, setSrc := genSetFoo(), genSetFoo()
+	if !setTgt.Equals(setSrc) {
+		t.Error("SetEqualsFoo.Equals() test failed")
+	}
+	setSrc.OptI64StringMapSetFoo[0][1] = "0"
+	if setTgt.Equals(setSrc) {
+		t.Error("SetEqualsFoo.Equals() test failed")
+	}
+	setSrc = genSetFoo()
+	setSrc.OptI64StringMapSetFoo = nil
+	if setTgt.Equals(setSrc) || setSrc.Equals(setTgt) {
+		t.Error("SetEqualsFoo.Equals() test failed")
+	}
+	if !(&equalstest.SetEqualsFoo{}).Equals(&equalstest.SetEqualsFoo{}) {
+		t.Error("SetEqualsFoo.Equals() test failed")
+	}
+	// test map field
+	mapTgt, mapSrc := genMapFoo(), genMapFoo()
+	if !mapTgt.Equals(mapSrc) {
+		t.Error("MapEqualsFoo.Equals() test failed")
+	}
+	mapSrc.OptI64I64StringMapMapFoo[1][1] = "0"
+	if mapTgt.Equals(mapSrc) {
+		t.Error("MapEqualsFoo.Equals() test failed")
+	}
+	mapSrc = genMapFoo()
+	mapSrc.OptI64I64StringMapMapFoo = nil
+	if mapTgt.Equals(mapSrc) || mapSrc.Equals(mapTgt) {
+		t.Error("MapEqualsFoo.Equals() test failed")
+	}
+	if !(&equalstest.MapEqualsFoo{}).Equals(&equalstest.MapEqualsFoo{}) {
+		t.Error("MapEqualsFoo.Equals() test failed")
+	}
+}
+
+func genBasicFoo() *equalstest.BasicEqualsFoo {
+	return &equalstest.BasicEqualsFoo{
+		BoolFoo:      true,
+		OptBoolFoo:   &(&struct{ x bool }{true}).x,
+		I8Foo:        1,
+		OptI8Foo:     &(&struct{ x int8 }{1}).x,
+		I16Foo:       2,
+		OptI16Foo:    &(&struct{ x int16 }{2}).x,
+		I32Foo:       3,
+		OptI32Foo:    &(&struct{ x int32 }{3}).x,
+		I64Foo:       4,
+		OptI64Foo:    &(&struct{ x int64 }{4}).x,
+		DoubleFoo:    5,
+		OptDoubleFoo: &(&struct{ x float64 }{5}).x,
+		StrFoo:       "6",
+		OptStrFoo:    &(&struct{ x string }{"6"}).x,
+		BinFoo:       []byte("7"),
+		OptBinFoo:    []byte("7"),
+		EnumFoo:      equalstest.EnumFoo_e1,
+		OptEnumFoo:   equalstest.EnumFooPtr(equalstest.EnumFoo_e1),
+		MyByteFoo:    equalstest.Mybyte(8),
+		OptMyByteFoo: equalstest.MybytePtr(8),
+		MyStrFoo:     equalstest.Mystr("9"),
+		OptMyStrFoo:  equalstest.MystrPtr(equalstest.Mystr("9")),
+		MyBinFoo:     equalstest.Mybin("10"),
+		OptMyBinFoo:  equalstest.Mybin("10"),
+	}
+}
+
+func genStructFoo() *equalstest.StructEqualsFoo {
+	return &equalstest.StructEqualsFoo{
+		StructFoo:    genBasicFoo(),
+		OptStructFoo: genBasicFoo(),
+	}
+}
+
+func genListFoo() *equalstest.ListEqualsFoo {
+	return &equalstest.ListEqualsFoo{
+		I64ListFoo:             genInt64Slice(6),
+		OptI64ListFoo:          genInt64Slice(6),
+		StrListFoo:             genStringSlice(6),
+		OptStrListFoo:          genStringSlice(6),
+		BinListFoo:             genBytesSlice(6),
+		OptBinListFoo:          genBytesSlice(6),
+		StructListFoo:          []*equalstest.BasicEqualsFoo{genBasicFoo(), {}},
+		OptStructListFoo:       []*equalstest.BasicEqualsFoo{genBasicFoo(), {}},
+		I64ListListFoo:         [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)},
+		OptI64ListListFoo:      [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)},
+		I64SetListFoo:          [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)},
+		OptI64SetListFoo:       [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)},
+		I64StringMapListFoo:    []map[int64]string{{6: "6"}, {5: "5"}, {4: "4"}, {3: "3"}, {2: "2"}, {1: "1"}},
+		OptI64StringMapListFoo: []map[int64]string{{6: "6"}, {5: "5"}, {4: "4"}, {3: "3"}, {2: "2"}, {1: "1"}},
+		MyByteListFoo:          []equalstest.Mybyte{6, 5, 4, 3, 2, 1},
+		OptMyByteListFoo:       []equalstest.Mybyte{6, 5, 4, 3, 2, 1},
+		MyStrListFoo:           []equalstest.Mystr{equalstest.Mystr("6"), equalstest.Mystr("5"), equalstest.Mystr("4"), equalstest.Mystr("3"), equalstest.Mystr("2"), equalstest.Mystr("1")},
+		OptMyStrListFoo:        []equalstest.Mystr{equalstest.Mystr("6"), equalstest.Mystr("5"), equalstest.Mystr("4"), equalstest.Mystr("3"), equalstest.Mystr("2"), equalstest.Mystr("1")},
+		MyBinListFoo:           []equalstest.Mybin{equalstest.Mybin("6"), equalstest.Mybin("5"), equalstest.Mybin("4"), equalstest.Mybin("3"), equalstest.Mybin("2"), equalstest.Mybin("1")},
+		OptMyBinListFoo:        []equalstest.Mybin{equalstest.Mybin("6"), equalstest.Mybin("5"), equalstest.Mybin("4"), equalstest.Mybin("3"), equalstest.Mybin("2"), equalstest.Mybin("1")},
+	}
+}
+
+func genSetFoo() *equalstest.SetEqualsFoo {
+	return &equalstest.SetEqualsFoo{
+		I64SetFoo:             genInt64Slice(6),
+		OptI64SetFoo:          genInt64Slice(6),
+		StrSetFoo:             genStringSlice(6),
+		OptStrSetFoo:          genStringSlice(6),
+		BinSetFoo:             genBytesSlice(6),
+		OptBinSetFoo:          genBytesSlice(6),
+		StructSetFoo:          []*equalstest.BasicEqualsFoo{genBasicFoo(), {}},
+		OptStructSetFoo:       []*equalstest.BasicEqualsFoo{genBasicFoo(), {}},
+		I64ListSetFoo:         [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)},
+		OptI64ListSetFoo:      [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)},
+		I64SetSetFoo:          [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)},
+		OptI64SetSetFoo:       [][]int64{genInt64Slice(6), genInt64Slice(5), genInt64Slice(4), genInt64Slice(3), genInt64Slice(2), genInt64Slice(1)},
+		I64StringMapSetFoo:    []map[int64]string{{6: "6"}, {5: "5"}, {4: "4"}, {3: "3"}, {2: "2"}, {1: "1"}},
+		OptI64StringMapSetFoo: []map[int64]string{{6: "6"}, {5: "5"}, {4: "4"}, {3: "3"}, {2: "2"}, {1: "1"}},
+		MyByteSetFoo:          []equalstest.Mybyte{6, 5, 4, 3, 2, 1},
+		OptMyByteSetFoo:       []equalstest.Mybyte{6, 5, 4, 3, 2, 1},
+		MyStrSetFoo:           []equalstest.Mystr{equalstest.Mystr("6"), equalstest.Mystr("5"), equalstest.Mystr("4"), equalstest.Mystr("3"), equalstest.Mystr("2"), equalstest.Mystr("1")},
+		OptMyStrSetFoo:        []equalstest.Mystr{equalstest.Mystr("6"), equalstest.Mystr("5"), equalstest.Mystr("4"), equalstest.Mystr("3"), equalstest.Mystr("2"), equalstest.Mystr("1")},
+		MyBinSetFoo:           []equalstest.Mybin{equalstest.Mybin("6"), equalstest.Mybin("5"), equalstest.Mybin("4"), equalstest.Mybin("3"), equalstest.Mybin("2"), equalstest.Mybin("1")},
+		OptMyBinSetFoo:        []equalstest.Mybin{equalstest.Mybin("6"), equalstest.Mybin("5"), equalstest.Mybin("4"), equalstest.Mybin("3"), equalstest.Mybin("2"), equalstest.Mybin("1")},
+	}
+}
+
+var (
+	structMapKey0 = genBasicFoo()
+	structMapKey1 = &equalstest.BasicEqualsFoo{}
+)
+
+func genMapFoo() *equalstest.MapEqualsFoo {
+	return &equalstest.MapEqualsFoo{
+		I64StrMapFoo:             genInt64StringMap(6),
+		OptI64StrMapFoo:          genInt64StringMap(6),
+		StrI64MapFoo:             map[string]int64{"6": 6, "5": 5, "4": 4, "3": 3, "2": 2, "1": 1},
+		OptStrI64MapFoo:          map[string]int64{"6": 6, "5": 5, "4": 4, "3": 3, "2": 2, "1": 1},
+		StructBinMapFoo:          map[*equalstest.BasicEqualsFoo][]byte{structMapKey0: []byte("0"), structMapKey1: []byte("1")},
+		OptStructBinMapFoo:       map[*equalstest.BasicEqualsFoo][]byte{structMapKey0: []byte("0"), structMapKey1: []byte("1")},
+		BinStructMapFoo:          map[string]*equalstest.BasicEqualsFoo{"1": genBasicFoo(), "0": {}},
+		OptBinStructMapFoo:       map[string]*equalstest.BasicEqualsFoo{"1": genBasicFoo(), "0": {}},
+		I64I64ListMapFoo:         map[int64][]int64{6: genInt64Slice(6), 5: genInt64Slice(5), 4: genInt64Slice(4), 3: genInt64Slice(3), 2: genInt64Slice(2), 1: genInt64Slice(1)},
+		OptI64I64ListMapFoo:      map[int64][]int64{6: genInt64Slice(6), 5: genInt64Slice(5), 4: genInt64Slice(4), 3: genInt64Slice(3), 2: genInt64Slice(2), 1: genInt64Slice(1)},
+		I64I64SetMapFoo:          map[int64][]int64{6: genInt64Slice(6), 5: genInt64Slice(5), 4: genInt64Slice(4), 3: genInt64Slice(3), 2: genInt64Slice(2), 1: genInt64Slice(1)},
+		OptI64I64SetMapFoo:       map[int64][]int64{6: genInt64Slice(6), 5: genInt64Slice(5), 4: genInt64Slice(4), 3: genInt64Slice(3), 2: genInt64Slice(2), 1: genInt64Slice(1)},
+		I64I64StringMapMapFoo:    map[int64]map[int64]string{6: genInt64StringMap(6), 5: genInt64StringMap(5), 4: genInt64StringMap(4), 3: genInt64StringMap(3), 2: genInt64StringMap(2), 1: genInt64StringMap(1)},
+		OptI64I64StringMapMapFoo: map[int64]map[int64]string{6: genInt64StringMap(6), 5: genInt64StringMap(5), 4: genInt64StringMap(4), 3: genInt64StringMap(3), 2: genInt64StringMap(2), 1: genInt64StringMap(1)},
+		MyStrMyBinMapFoo:         map[equalstest.Mystr]equalstest.Mybin{equalstest.Mystr("1"): equalstest.Mybin("1"), equalstest.Mystr("0"): equalstest.Mybin("0")},
+		OptMyStrMyBinMapFoo:      map[equalstest.Mystr]equalstest.Mybin{equalstest.Mystr("1"): equalstest.Mybin("1"), equalstest.Mystr("0"): equalstest.Mybin("0")},
+		Int64MyByteMapFoo:        map[int64]equalstest.Mybyte{6: equalstest.Mybyte(6), 5: equalstest.Mybyte(5), 4: equalstest.Mybyte(4), 3: equalstest.Mybyte(3), 2: equalstest.Mybyte(2), 1: equalstest.Mybyte(1)},
+		OptInt64MyByteMapFoo:     map[int64]equalstest.Mybyte{6: equalstest.Mybyte(6), 5: equalstest.Mybyte(5), 4: equalstest.Mybyte(4), 3: equalstest.Mybyte(3), 2: equalstest.Mybyte(2), 1: equalstest.Mybyte(1)},
+		MyByteInt64MapFoo:        map[equalstest.Mybyte]int64{equalstest.Mybyte(6): 6, equalstest.Mybyte(5): 5, equalstest.Mybyte(4): 4, equalstest.Mybyte(3): 3, equalstest.Mybyte(2): 2, equalstest.Mybyte(1): 1},
+		OptMyByteInt64MapFoo:     map[equalstest.Mybyte]int64{equalstest.Mybyte(6): 6, equalstest.Mybyte(5): 5, equalstest.Mybyte(4): 4, equalstest.Mybyte(3): 3, equalstest.Mybyte(2): 2, equalstest.Mybyte(1): 1},
+	}
+}
+
+func genInt64Slice(length int) []int64 {
+	ret := make([]int64, length)
+	for i := 0; i < length; i++ {
+		ret[i] = int64(length - i)
+	}
+	return ret
+}
+
+func genStringSlice(length int) []string {
+	ret := make([]string, length)
+	for i := 0; i < length; i++ {
+		ret[i] = strconv.Itoa(length - i)
+	}
+	return ret
+}
+
+func genBytesSlice(length int) [][]byte {
+	ret := make([][]byte, length)
+	for i := 0; i < length; i++ {
+		ret[i] = []byte(strconv.Itoa(length - i))
+	}
+	return ret
+}
+
+func genInt64StringMap(length int) map[int64]string {
+	ret := make(map[int64]string, length)
+	for i := 0; i < length; i++ {
+		ret[int64(i)] = strconv.Itoa(i)
+	}
+	return ret
+}