THRIFT-3464 Fix several defects in c_glib code generator
Client: c_glib compiler
Patch: Nobuaki Sukegawa

This closes #724
diff --git a/lib/c_glib/src/thrift/c_glib/thrift.c b/lib/c_glib/src/thrift/c_glib/thrift.c
index 15f409f..8de869f 100644
--- a/lib/c_glib/src/thrift/c_glib/thrift.c
+++ b/lib/c_glib/src/thrift/c_glib/thrift.c
@@ -31,6 +31,67 @@
 
   *list = g_list_append (*list, key);
 }
+void thrift_safe_hash_table_destroy(GHashTable* hash_table)
+{
+  if (hash_table)
+  {
+    g_hash_table_destroy(hash_table);
+  }
+}
+
+guint thrift_boolean_hash(gconstpointer v)
+{
+  const gboolean* p = v;
+  return p && *p ? 1 : 0;
+}
+gboolean thrift_boolean_equal(gconstpointer a, gconstpointer b)
+{
+  if (a == b) {
+    return TRUE;
+  }
+  if (!a || !b) {
+    return FALSE;
+  }
+  const gboolean* pa = a;
+  const gboolean* pb = b;
+  return *pa == *pb;
+}
+
+guint thrift_int8_hash(gconstpointer v)
+{
+  const gint8* p = v;
+  return p ? *p : 0;
+}
+gboolean thrift_int8_equal(gconstpointer a, gconstpointer b)
+{
+  if (a == b) {
+    return TRUE;
+  }
+  if (!a || !b) {
+    return FALSE;
+  }
+  const gint8* pa = a;
+  const gint8* pb = b;
+  return *pa == *pb;
+}
+
+guint thrift_int16_hash(gconstpointer v)
+{
+  const gint16* p = v;
+  return p ? *p : 0;
+}
+gboolean thrift_int16_equal(gconstpointer a, gconstpointer b)
+{
+  if (a == b) {
+    return TRUE;
+  }
+  if (!a || !b) {
+    return FALSE;
+  }
+  const gint16* pa = a;
+  const gint16* pb = b;
+  return *pa == *pb;
+}
 
 void
 thrift_string_free (gpointer str)
diff --git a/lib/c_glib/src/thrift/c_glib/thrift.h b/lib/c_glib/src/thrift/c_glib/thrift.h
index 858ad86..94a6478 100644
--- a/lib/c_glib/src/thrift/c_glib/thrift.h
+++ b/lib/c_glib/src/thrift/c_glib/thrift.h
@@ -33,6 +33,17 @@
 
 void thrift_hash_table_get_keys (gpointer key, gpointer value,
                                  gpointer user_data);
+void thrift_safe_hash_table_destroy(GHashTable* hash_table);
+
+guint thrift_boolean_hash(gconstpointer v);
+gboolean thrift_boolean_equal(gconstpointer a, gconstpointer b);
+
+guint thrift_int8_hash(gconstpointer v);
+gboolean thrift_int8_equal(gconstpointer a, gconstpointer b);
+
+guint thrift_int16_hash(gconstpointer v);
+gboolean thrift_int16_equal(gconstpointer a, gconstpointer b);
+
 void thrift_string_free (gpointer str);
 
 #endif /* #ifndef _THRIFT_THRIFT_H */
diff --git a/lib/c_glib/test/CMakeLists.txt b/lib/c_glib/test/CMakeLists.txt
index 61dc490..48a30d0 100644
--- a/lib/c_glib/test/CMakeLists.txt
+++ b/lib/c_glib/test/CMakeLists.txt
@@ -28,6 +28,8 @@
 # Create the thrift C test library
 set(testgenc_SOURCES
     gen-c_glib/t_test_debug_proto_test_types.c
+    gen-c_glib/t_test_enum_test_types.c
+    gen-c_glib/t_test_enum_test_service.c
     gen-c_glib/t_test_empty_service.c
     gen-c_glib/t_test_inherited.c
     gen-c_glib/t_test_optional_required_test_types.c
@@ -38,6 +40,8 @@
     gen-c_glib/t_test_thrift_test.c
     gen-c_glib/t_test_thrift_test_types.c
     gen-c_glib/t_test_debug_proto_test_types.h
+    gen-c_glib/t_test_enum_test_types.h
+    gen-c_glib/t_test_enum_test_service.h
     gen-c_glib/t_test_empty_service.h
     gen-c_glib/t_test_inherited.h
     gen-c_glib/t_test_optional_required_test_types.h
@@ -146,6 +150,14 @@
 )
 
 add_custom_command(OUTPUT
+    gen-c_glib/t_test_enum_test_types.c
+    gen-c_glib/t_test_enum_test_types.h
+    gen-c_glib/t_test_enum_test_service.c
+    gen-c_glib/t_test_enum_test_service.h
+    COMMAND ${THRIFT_COMPILER} --gen c_glib ${PROJECT_SOURCE_DIR}/test/EnumTest.thrift
+)
+
+add_custom_command(OUTPUT
     gen-c_glib/t_test_optional_required_test_types.c
     gen-c_glib/t_test_optional_required_test_types.h
     COMMAND ${THRIFT_COMPILER} --gen c_glib ${PROJECT_SOURCE_DIR}/test/OptionalRequiredTest.thrift
diff --git a/lib/c_glib/test/Makefile.am b/lib/c_glib/test/Makefile.am
index 555380c..4d35f2a 100755
--- a/lib/c_glib/test/Makefile.am
+++ b/lib/c_glib/test/Makefile.am
@@ -169,6 +169,8 @@
 nodist_libtestgenc_la_SOURCES = \
         gen-c_glib/t_test_container_test_types.c \
         gen-c_glib/t_test_debug_proto_test_types.c \
+        gen-c_glib/t_test_enum_test_types.c \
+        gen-c_glib/t_test_enum_test_service.c \
         gen-c_glib/t_test_empty_service.c \
         gen-c_glib/t_test_inherited.c \
         gen-c_glib/t_test_optional_required_test_types.c \
@@ -181,6 +183,8 @@
         gen-c_glib/t_test_thrift_test_types.c \
         gen-c_glib/t_test_container_test_types.h \
         gen-c_glib/t_test_debug_proto_test_types.h \
+        gen-c_glib/t_test_enum_test_types.h \
+        gen-c_glib/t_test_enum_test_service.h \
         gen-c_glib/t_test_empty_service.h \
         gen-c_glib/t_test_inherited.h \
         gen-c_glib/t_test_optional_required_test_types.h \
@@ -211,6 +215,9 @@
 gen-c_glib/t_test_debug_proto_test_types.c gen-c_glib/t_test_debug_proto_test_types.h gen-c_glib/t_test_empty_service.c gen-c_glib/t_test_empty_service.h gen-c_glib/t_test_inherited.c gen-c_glib/t_test_inherited.h gen-c_glib/t_test_reverse_order_service.c gen-c_glib/t_test_reverse_order_service.h gen-c_glib/t_test_service_for_exception_with_a_map.c gen-c_glib/t_test_service_for_exception_with_a_map.h gen-c_glib/t_test_srv.c gen-c_glib/t_test_srv.h: ../../../test/DebugProtoTest.thrift
 	$(THRIFT) --gen c_glib $<
 
+gen-c_glib/t_test_enum_test_types.c gen-c_glib/t_test_enum_test_types.h gen-c_glib/t_test_enum_test_service.c gen-c_glib/t_test_enum_test_service.h : ../../../test/EnumTest.thrift
+	$(THRIFT) --gen c_glib $<
+
 gen-c_glib/t_test_optional_required_test_types.c gen-c_glib/t_test_optional_required_test_types.h: ../../../test/OptionalRequiredTest.thrift
 	$(THRIFT) --gen c_glib $<
 
diff --git a/lib/c_glib/test/testoptionalrequired.c b/lib/c_glib/test/testoptionalrequired.c
index ae0c3d2..cfc96a2 100755
--- a/lib/c_glib/test/testoptionalrequired.c
+++ b/lib/c_glib/test/testoptionalrequired.c
@@ -187,6 +187,26 @@
   g_object_unref (t3);
 }
 
+static void
+test_non_set_binary (void)
+{
+  TTestBinaries *b1 = NULL;
+  TTestBinaries *b2 = NULL;
+  GError *error = NULL;
+
+  b1 = g_object_new (T_TEST_TYPE_BINARIES, NULL);
+  b2 = g_object_new (T_TEST_TYPE_BINARIES, NULL);
+
+  write_to_read (THRIFT_STRUCT (b1), THRIFT_STRUCT (b2), NULL, &error);
+  g_assert(!error);
+  write_to_read (THRIFT_STRUCT (b2), THRIFT_STRUCT (b1), NULL, &error);
+  g_assert(!error);
+  // OK. No segfault
+
+  g_object_unref (b1);
+  g_object_unref (b2);
+}
+
 int
 main(int argc, char *argv[])
 {
@@ -202,6 +222,7 @@
   g_test_add_func ("/testoptionalrequired/Tricky2", test_tricky2);
   g_test_add_func ("/testoptionalrequired/Tricky3", test_tricky3);
   g_test_add_func ("/testoptionalrequired/Tricky4", test_tricky4);
+  g_test_add_func ("/testoptionalrequired/Binary", test_non_set_binary);
 
   return g_test_run ();
 }
diff --git a/lib/c_glib/test/testserialization.c b/lib/c_glib/test/testserialization.c
index 0ece2ad..9fc6357 100644
--- a/lib/c_glib/test/testserialization.c
+++ b/lib/c_glib/test/testserialization.c
@@ -3,6 +3,53 @@
 #include <thrift/c_glib/transport/thrift_memory_buffer.h>
 #include <thrift/c_glib/transport/thrift_transport.h>
 #include "gen-c_glib/t_test_debug_proto_test_types.h"
+#include "gen-c_glib/t_test_enum_test_types.h"
+
+static void enum_constants_read_write() {
+  GError* error = NULL;
+  ThriftTransport* transport
+      = THRIFT_TRANSPORT(g_object_new(THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 1024, NULL));
+  ThriftProtocol* protocol
+      = THRIFT_PROTOCOL(g_object_new(THRIFT_TYPE_BINARY_PROTOCOL, "transport", transport, NULL));
+  TTestEnumTestStruct* src = T_TEST_ENUM_TEST;
+  TTestEnumTestStruct* dst = g_object_new(T_TEST_TYPE_ENUM_TEST_STRUCT, NULL);
+  TTestEnumTestStructClass* cls = T_TEST_ENUM_TEST_STRUCT_GET_CLASS(src);
+
+  int write_len = THRIFT_STRUCT_CLASS(cls)->write(THRIFT_STRUCT(src), protocol, &error);
+  g_assert(!error);
+  g_assert(write_len > 0);
+
+  int read_len = THRIFT_STRUCT_CLASS(cls)->read(THRIFT_STRUCT(dst), protocol, &error);
+  g_assert(!error);
+  g_assert_cmpint(write_len, ==, read_len);
+
+  g_object_unref(dst);
+  g_object_unref(protocol);
+  g_object_unref(transport);
+}
+
+static void struct_constants_read_write() {
+  GError* error = NULL;
+  ThriftTransport* transport
+      = THRIFT_TRANSPORT(g_object_new(THRIFT_TYPE_MEMORY_BUFFER, "buf_size", 4096, NULL));
+  ThriftProtocol* protocol
+      = THRIFT_PROTOCOL(g_object_new(THRIFT_TYPE_BINARY_PROTOCOL, "transport", transport, NULL));
+  TTestCompactProtoTestStruct* src = T_TEST_COMPACT_TEST;
+  TTestCompactProtoTestStruct* dst = g_object_new(T_TEST_TYPE_COMPACT_PROTO_TEST_STRUCT, NULL);
+  TTestCompactProtoTestStructClass* cls = T_TEST_COMPACT_PROTO_TEST_STRUCT_GET_CLASS(src);
+
+  int write_len = THRIFT_STRUCT_CLASS(cls)->write(THRIFT_STRUCT(src), protocol, &error);
+  g_assert(!error);
+  g_assert(write_len > 0);
+
+  int read_len = THRIFT_STRUCT_CLASS(cls)->read(THRIFT_STRUCT(dst), protocol, &error);
+  g_assert(!error);
+  g_assert_cmpint(write_len, ==, read_len);
+
+  g_object_unref(dst);
+  g_object_unref(protocol);
+  g_object_unref(transport);
+}
 
 static void struct_read_write_length_should_equal() {
   GError* error = NULL;
@@ -36,5 +83,7 @@
 
   g_test_add_func("/testserialization/StructReadWriteLengthShouldEqual",
                   struct_read_write_length_should_equal);
+  g_test_add_func("/testserialization/StructConstants", struct_constants_read_write);
+  g_test_add_func("/testserialization/EnumConstants", enum_constants_read_write);
   return g_test_run();
 }