Merge into master from pull request #284:
Convert list accessors to templates and optimize (https://github.com/floodlight/loxigen/pull/284)
diff --git a/c_gen/build_of_g.py b/c_gen/build_of_g.py
index 4d37b36..9b19af6 100755
--- a/c_gen/build_of_g.py
+++ b/c_gen/build_of_g.py
@@ -242,6 +242,7 @@
         of_g.base_length[(cls, wire_version)] = fixed_offset
         if (offset != -1):
             of_g.is_fixed_length.add((cls, wire_version))
+
     for list_type in lists:
         classes[list_type] = []
         of_g.ordered_classes[wire_version].append(list_type)
diff --git a/c_gen/c_code_gen.py b/c_gen/c_code_gen.py
index ee87d0d..191fcd3 100644
--- a/c_gen/c_code_gen.py
+++ b/c_gen/c_code_gen.py
@@ -1081,172 +1081,6 @@
 
 ################################################################
 #
-# List accessor code generation
-#
-# Currently these all implement copy on read semantics
-#
-################################################################
-
-def init_call(e_type, obj, ver, length, cw):
-    """
-    Generate the init call given the strings for params
-    """
-    hdr = "" # If inheritance type, coerce to hdr object
-    obj_name = obj
-    if e_type in type_maps.inheritance_map:
-        hdr = "_header"
-        obj_name = "(%s_header_t *)" % e_type + obj
-
-    return """\
-%(e_type)s%(hdr)s_init(%(obj_name)s,
-            %(ver)s, %(length)s, %(cw)s)\
-""" % dict(e_type=e_type, hdr=hdr, obj_name=obj_name, ver=ver,
-           length=length, cw=cw)
-
-def gen_list_first(out, cls, e_type):
-    """
-    Generate the body of a list_first operation
-    @param cls The class name for which code is being generated
-    @param e_type The element type of the list
-    @param out The file to which to write
-    """
-    i_call = init_call(e_type, "obj", "list->version", "0", "1")
-    if e_type in type_maps.inheritance_map:
-        len_str = "obj->header.length"
-    else:
-        len_str = "obj->length"
-
-    out.write("""
-/**
- * Associate an iterator with a list
- * @param list The list to iterate over
- * @param obj The list entry iteration pointer
- * @return OF_ERROR_RANGE if the list is empty (end of list)
- *
- * The obj instance is completely initialized.  The caller is responsible
- * for cleaning up any wire buffers associated with obj before this call
- */
-
-int
-%(cls)s_first(%(cls)s_t *list,
-    %(e_type)s_t *obj)
-{
-    int rv;
-
-    %(i_call)s;
-    if ((rv = of_list_first((of_object_t *)list, (of_object_t *)obj)) < 0) {
-        return rv;
-    }
-""" % dict(cls=cls, e_type=e_type, i_call=i_call))
-
-    # Special case flow_stats_entry lists
-
-    out.write("""
-    of_object_wire_init((of_object_t *) obj, %(u_type)s,
-                        list->length);
-    if (%(len_str)s == 0) {
-        return OF_ERROR_PARSE;
-    }
-
-    return rv;
-}
-""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
-
-def gen_list_next(out, cls, e_type):
-    """
-    Generate the body of a list_next operation
-    @param cls The class name for which code is being generated
-    @param e_type The element type of the list
-    @param out The file to which to write
-    """
-
-    if e_type in type_maps.inheritance_map:
-        len_str = "obj->header.length"
-    else:
-        len_str = "obj->length"
-
-    out.write("""
-/**
- * Advance an iterator to the next element in a list
- * @param list The list being iterated
- * @param obj The list entry iteration pointer
- * @return OF_ERROR_RANGE if already at the last entry on the list
- *
- */
-
-int
-%(cls)s_next(%(cls)s_t *list,
-    %(e_type)s_t *obj)
-{
-    int rv;
-
-    if ((rv = of_list_next((of_object_t *)list, (of_object_t *)obj)) < 0) {
-        return rv;
-    }
-
-    rv = of_object_wire_init((of_object_t *) obj, %(u_type)s,
-        list->length);
-
-    if ((rv == OF_ERROR_NONE) && (%(len_str)s == 0)) {
-        return OF_ERROR_PARSE;
-    }
-
-    return rv;
-}
-""" % dict(cls=cls, e_type=e_type, u_type=enum_name(e_type), len_str=len_str))
-
-def gen_list_append(out, cls, e_type):
-    """
-    Generate the body of a list append functions
-    @param cls The class name for which code is being generated
-    @param e_type The element type of the list
-    @param out The file to which to write
-    """
-
-    out.write("""
-/**
- * Set up to append an object of type %(e_type)s to an %(cls)s.
- * @param list The list that is prepared for append
- * @param obj Pointer to object to hold data to append
- *
- * The obj instance is completely initialized.  The caller is responsible
- * for cleaning up any wire buffers associated with obj before this call.
- *
- * See the generic documentation for of_list_append_bind.
- */
-
-int
-%(cls)s_append_bind(%(cls)s_t *list,
-    %(e_type)s_t *obj)
-{
-    return of_list_append_bind((of_object_t *)list, (of_object_t *)obj);
-}
-
-/**
- * Append an item to a %(cls)s list.
- *
- * This copies data from item and leaves item untouched.
- *
- * See the generic documentation for of_list_append.
- */
-
-int
-%(cls)s_append(%(cls)s_t *list,
-    %(e_type)s_t *item)
-{
-    return of_list_append((of_object_t *)list, (of_object_t *)item);
-}
-
-""" % dict(cls=cls, e_type=e_type))
-
-def gen_list_accessors(out, cls):
-    e_type = loxi_utils.list_to_entry_type(cls)
-    gen_list_first(out, cls, e_type)
-    gen_list_next(out, cls, e_type)
-    gen_list_append(out, cls, e_type)
-
-################################################################
-#
 # Accessor Functions
 #
 ################################################################
diff --git a/c_gen/codegen.py b/c_gen/codegen.py
index 7cb46db..b8a6757 100644
--- a/c_gen/codegen.py
+++ b/c_gen/codegen.py
@@ -43,6 +43,8 @@
 import c_gen.of_g_legacy as of_g
 import c_gen.type_maps as type_maps
 import c_gen.c_type_maps as c_type_maps
+import loxi_utils.loxi_utils as loxi_utils
+import c_gen.loxi_utils_legacy as loxi_utils_legacy
 
 CLASS_CHUNK_SIZE = 32
 
@@ -145,14 +147,24 @@
             legacy_code=tmp.getvalue())
 
 def generate_lists(install_dir):
-    for cls in of_g.ordered_list_objects:
+    # Collect all the lists in use
+    list_oftypes = set()
+    for uclass in loxi_globals.unified.classes:
+        for ofclass in uclass.version_classes.values():
+            for m in ofclass.members:
+                if isinstance(m, ir.OFDataMember) and \
+                        loxi_utils.oftype_is_list(m.oftype):
+                    list_oftypes.add(m.oftype)
+
+    for oftype in sorted(list(list_oftypes)):
+        cls, e_cls = loxi_utils_legacy.list_name_extract(oftype)
+        e_cls = e_cls[:-2]
+        e_uclass = loxi_globals.unified.class_by_name(e_cls)
         with template_utils.open_output(install_dir, "loci/src/%s.c" % cls) as out:
-            util.render_template(out, "class.c",
-                push_wire_types_data=None,
-                parse_wire_types_data=None)
+            util.render_template(out, "list.c", cls=cls, e_cls=e_cls, e_uclass=e_uclass,
+                                 wire_length_get=class_metadata_dict[e_cls].wire_length_get)
             # Append legacy generated code
             c_code_gen.gen_new_function_definitions(out, cls)
-            c_code_gen.gen_list_accessors(out, cls)
 
 def generate_strings(install_dir):
     object_id_strs = []
@@ -182,159 +194,165 @@
 ClassMetadata = namedtuple('ClassMetadata',
     ['name', 'wire_length_get', 'wire_length_set', 'wire_type_get', 'wire_type_set'])
 
+class_metadata = []
+class_metadata_dict = {}
+
+def build_class_metadata():
+    for uclass in loxi_globals.unified.classes:
+        wire_length_get = 'NULL'
+        wire_length_set = 'NULL'
+        wire_type_get = 'NULL'
+        wire_type_set = 'NULL'
+
+        if uclass and not uclass.virtual and uclass.has_type_members:
+            wire_type_set = '%s_push_wire_types' % uclass.name
+
+        if uclass.is_message:
+            wire_length_get = 'of_object_message_wire_length_get'
+            wire_length_set = 'of_object_message_wire_length_set'
+        elif uclass.is_action:
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_action_wire_object_id_get'
+        elif uclass.is_instanceof('of_bsn_vport'):
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_bsn_vport_wire_object_id_get'
+        elif uclass.is_action_id:
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_action_id_wire_object_id_get'
+        elif uclass.is_instruction:
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_instruction_wire_object_id_get'
+        elif uclass.is_instanceof('of_instruction_id'):
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_instruction_id_wire_object_id_get'
+        elif uclass.is_instanceof('of_queue_prop'):
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_queue_prop_wire_object_id_get'
+        elif uclass.is_instanceof('of_table_feature_prop'):
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_table_feature_prop_wire_object_id_get'
+        elif uclass.is_instanceof('of_meter_band'):
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_meter_band_wire_object_id_get'
+        elif uclass.is_instanceof('of_hello_elem'):
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_hello_elem_wire_object_id_get'
+        elif uclass.is_instanceof('of_bsn_tlv'):
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_get = 'of_bsn_tlv_wire_object_id_get'
+        elif uclass.is_oxm:
+            wire_length_get = 'of_oxm_wire_length_get'
+            wire_type_get = 'of_oxm_wire_object_id_get'
+        elif uclass.name == "of_packet_queue":
+            wire_length_get = 'of_packet_queue_wire_length_get'
+            wire_length_set = 'of_packet_queue_wire_length_set'
+        elif uclass.name == "of_meter_stats":
+            wire_length_get = 'of_meter_stats_wire_length_get'
+            wire_length_set = 'of_meter_stats_wire_length_set'
+        elif uclass.name in ["of_group_desc_stats_entry", "of_group_stats_entry",
+                "of_flow_stats_entry", "of_bucket", "of_table_features",
+                "of_bsn_port_counter_stats_entry", "of_bsn_vlan_counter_stats_entry",
+                "of_bsn_gentable_entry_desc_stats_entry", "of_bsn_gentable_entry_stats_entry",
+                "of_bsn_gentable_desc_stats_entry"]:
+            wire_length_get = "of_u16_len_wire_length_get"
+            wire_length_set = "of_u16_len_wire_length_set"
+        elif uclass.name == 'of_match_v3':
+            wire_length_set = 'of_tlv16_wire_length_set'
+            wire_length_get = 'of_tlv16_wire_length_get'
+            wire_type_set = 'of_match_v3_push_wire_types'
+
+        class_metadata.append(ClassMetadata(
+            name=uclass.name,
+            wire_length_get=wire_length_get,
+            wire_length_set=wire_length_set,
+            wire_type_get=wire_type_get,
+            wire_type_set=wire_type_set))
+
+    class_metadata.extend([
+        ClassMetadata(
+            name="of_action_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_action_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_action_id_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_action_id_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_bsn_vport_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_bsn_vport_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_instruction_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_instruction_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_instruction_id_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_instruction_id_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_queue_prop_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_queue_prop_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_table_feature_prop_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_table_feature_prop_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_meter_band_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_meter_band_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_hello_elem_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_hello_elem_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_bsn_tlv_header",
+            wire_length_set='of_tlv16_wire_length_set',
+            wire_length_get='of_tlv16_wire_length_get',
+            wire_type_get='of_bsn_tlv_wire_object_id_get',
+            wire_type_set='NULL'),
+        ClassMetadata(
+            name="of_oxm_header",
+            wire_length_set='NULL',
+            wire_length_get='of_oxm_wire_length_get',
+            wire_type_get='of_oxm_wire_object_id_get',
+            wire_type_set='NULL'),
+    ])
+
+    for metadata in class_metadata:
+        class_metadata_dict[metadata.name] = metadata
+
 def generate_class_metadata(install_dir):
     with template_utils.open_output(install_dir, "loci/inc/loci/loci_class_metadata.h") as out:
         util.render_template(out, "loci_class_metadata.h")
 
     with template_utils.open_output(install_dir, "loci/src/loci_class_metadata.c") as out:
-        class_metadata = []
-        for uclass in loxi_globals.unified.classes:
-            wire_length_get = 'NULL'
-            wire_length_set = 'NULL'
-            wire_type_get = 'NULL'
-            wire_type_set = 'NULL'
-
-            if uclass and not uclass.virtual and uclass.has_type_members:
-                wire_type_set = '%s_push_wire_types' % uclass.name
-
-            if uclass.is_message and uclass.name != "of_header":
-                wire_length_get = 'of_object_message_wire_length_get'
-                wire_length_set = 'of_object_message_wire_length_set'
-            elif uclass.is_action:
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_action_wire_object_id_get'
-            elif uclass.is_instanceof('of_bsn_vport'):
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_bsn_vport_wire_object_id_get'
-            elif uclass.is_action_id:
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_action_id_wire_object_id_get'
-            elif uclass.is_instruction:
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_instruction_wire_object_id_get'
-            elif uclass.is_instanceof('of_instruction_id'):
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_instruction_id_wire_object_id_get'
-            elif uclass.is_instanceof('of_queue_prop'):
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_queue_prop_wire_object_id_get'
-            elif uclass.is_instanceof('of_table_feature_prop'):
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_table_feature_prop_wire_object_id_get'
-            elif uclass.is_instanceof('of_meter_band'):
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_meter_band_wire_object_id_get'
-            elif uclass.is_instanceof('of_hello_elem'):
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_hello_elem_wire_object_id_get'
-            elif uclass.is_instanceof('of_bsn_tlv'):
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_get = 'of_bsn_tlv_wire_object_id_get'
-            elif uclass.is_oxm:
-                wire_length_get = 'of_oxm_wire_length_get'
-                wire_type_get = 'of_oxm_wire_object_id_get'
-            elif uclass.name == "of_packet_queue":
-                wire_length_get = 'of_packet_queue_wire_length_get'
-                wire_length_set = 'of_packet_queue_wire_length_set'
-            elif uclass.name == "of_meter_stats":
-                wire_length_get = 'of_meter_stats_wire_length_get'
-                wire_length_set = 'of_meter_stats_wire_length_set'
-            elif uclass.name in ["of_group_desc_stats_entry", "of_group_stats_entry",
-                   "of_flow_stats_entry", "of_bucket", "of_table_features",
-                   "of_bsn_port_counter_stats_entry", "of_bsn_vlan_counter_stats_entry",
-                   "of_bsn_gentable_entry_desc_stats_entry", "of_bsn_gentable_entry_stats_entry",
-                   "of_bsn_gentable_desc_stats_entry"]:
-                wire_length_get = "of_u16_len_wire_length_get"
-                wire_length_set = "of_u16_len_wire_length_set"
-            elif uclass.name == 'of_match_v3':
-                wire_length_set = 'of_tlv16_wire_length_set'
-                wire_length_get = 'of_tlv16_wire_length_get'
-                wire_type_set = 'of_match_v3_push_wire_types'
-
-            class_metadata.append(ClassMetadata(
-                name=uclass.name,
-                wire_length_get=wire_length_get,
-                wire_length_set=wire_length_set,
-                wire_type_get=wire_type_get,
-                wire_type_set=wire_type_set))
-
-        class_metadata.extend([
-            ClassMetadata(
-                name="of_action_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_action_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_action_id_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_action_id_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_bsn_vport_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_bsn_vport_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_instruction_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_instruction_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_instruction_id_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_instruction_id_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_queue_prop_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_queue_prop_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_table_feature_prop_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_table_feature_prop_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_meter_band_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_meter_band_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_hello_elem_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_hello_elem_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_bsn_tlv_header",
-                wire_length_set='of_tlv16_wire_length_set',
-                wire_length_get='of_tlv16_wire_length_get',
-                wire_type_get='of_bsn_tlv_wire_object_id_get',
-                wire_type_set='NULL'),
-            ClassMetadata(
-                name="of_oxm_header",
-                wire_length_set='NULL',
-                wire_length_get='of_oxm_wire_length_get',
-                wire_type_get='of_oxm_wire_object_id_get',
-                wire_type_set='NULL'),
-        ])
-
         util.render_template(out, "loci_class_metadata.c", class_metadata=class_metadata)
diff --git a/c_gen/templates/list.c b/c_gen/templates/list.c
new file mode 100644
index 0000000..9ff6e42
--- /dev/null
+++ b/c_gen/templates/list.c
@@ -0,0 +1,125 @@
+:: # Copyright 2014, Big Switch Networks, Inc.
+:: #
+:: # LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
+:: # the following special exception:
+:: #
+:: # LOXI Exception
+:: #
+:: # As a special exception to the terms of the EPL, you may distribute libraries
+:: # generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
+:: # that copyright and licensing notices generated by LoxiGen are not altered or removed
+:: # from the LoxiGen Libraries and the notice provided below is (i) included in
+:: # the LoxiGen Libraries, if distributed in source code form and (ii) included in any
+:: # documentation for the LoxiGen Libraries, if distributed in binary form.
+:: #
+:: # Notice: "Copyright 2014, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
+:: #
+:: # You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
+:: # a copy of the EPL at:
+:: #
+:: # http://www.eclipse.org/legal/epl-v10.html
+:: #
+:: # Unless required by applicable law or agreed to in writing, software
+:: # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+:: # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+:: # EPL for the specific language governing permissions and limitations
+:: # under the EPL.
+::
+:: include('_copyright.c')
+:: include('_pragmas.c')
+
+#include "loci_log.h"
+#include "loci_int.h"
+
+/**
+ * Associate an iterator with a list
+ * @param list The list to iterate over
+ * @param obj The list entry iteration pointer
+ * @return OF_ERROR_RANGE if the list is empty (end of list)
+ *
+ * The obj instance is completely initialized.  The caller is responsible
+ * for cleaning up any wire buffers associated with obj before this call
+ */
+
+int
+${cls}_first(${cls}_t *list, ${e_cls}_t *_obj)
+{
+    int rv;
+    of_object_t *obj = (of_object_t *)_obj;
+
+    ${e_cls}_init(_obj, list->version, -1, 1);
+
+    if ((rv = of_list_first(list, obj)) < 0) {
+        return rv;
+    }
+
+:: if e_uclass.virtual:
+    ${e_cls}_wire_object_id_get(obj, &obj->object_id);
+:: #endif
+
+:: if wire_length_get != 'NULL':
+    ${wire_length_get}(obj, &obj->length);
+:: #endif
+
+    return rv;
+}
+
+/**
+ * Advance an iterator to the next element in a list
+ * @param list The list being iterated
+ * @param obj The list entry iteration pointer
+ * @return OF_ERROR_RANGE if already at the last entry on the list
+ *
+ */
+
+int
+${cls}_next(${cls}_t *list, ${e_cls}_t *_obj)
+{
+    int rv;
+    of_object_t *obj = (of_object_t *)_obj;
+
+    if ((rv = of_list_next(list, obj)) < 0) {
+        return rv;
+    }
+
+:: if e_uclass.virtual:
+    ${e_cls}_wire_object_id_get(obj, &obj->object_id);
+:: #endif
+
+:: if wire_length_get != 'NULL':
+    ${wire_length_get}(obj, &obj->length);
+:: #endif
+
+    return rv;
+}
+
+/**
+ * Set up to append an object of type ${e_cls} to an ${cls}.
+ * @param list The list that is prepared for append
+ * @param obj Pointer to object to hold data to append
+ *
+ * The obj instance is completely initialized.  The caller is responsible
+ * for cleaning up any wire buffers associated with obj before this call.
+ *
+ * See the generic documentation for of_list_append_bind.
+ */
+
+int
+${cls}_append_bind(${cls}_t *list, ${e_cls}_t *obj)
+{
+    return of_list_append_bind(list, (of_object_t *)obj);
+}
+
+/**
+ * Append an object to a ${cls} list.
+ *
+ * This copies data from obj and leaves item untouched.
+ *
+ * See the generic documentation for of_list_append.
+ */
+
+int
+${cls}_append(${cls}_t *list, ${e_cls}_t *obj)
+{
+    return of_list_append(list, (of_object_t *)obj);
+}
diff --git a/c_gen/templates/of_object.c b/c_gen/templates/of_object.c
index b0d3e14..5647d81 100644
--- a/c_gen/templates/of_object.c
+++ b/c_gen/templates/of_object.c
@@ -455,7 +455,7 @@
         return OF_ERROR_RANGE;
     }
 
-    object_child_attach(parent, child, 0, 0);
+    of_object_attach(parent, child, 0, child->length);
 
     return OF_ERROR_NONE;
 }
@@ -508,7 +508,7 @@
     /* Offset is relative to parent start */
     offset = (child->obj_offset - parent->obj_offset) +
         child->length;
-    object_child_attach(parent, child, offset, 0);
+    of_object_attach(parent, child, offset, child->length);
 
     return OF_ERROR_NONE;
 }
diff --git a/c_gen/templates/of_type_maps.c b/c_gen/templates/of_type_maps.c
index 13b879a..9fa59e0 100644
--- a/c_gen/templates/of_type_maps.c
+++ b/c_gen/templates/of_type_maps.c
@@ -37,9 +37,6 @@
 #include <loci/loci.h>
 #include <loci/of_message.h>
 
-#define OF_INSTRUCTION_EXPERIMENTER_ID_OFFSET 4
-#define OF_INSTRUCTION_EXPERIMENTER_SUBTYPE_OFFSET 8
-
 ${legacy_code}
 
 /****************************************************************
@@ -308,53 +305,3 @@
     of_wire_buffer_u16_set(wbuf, 
         OF_OBJECT_ABSOLUTE_OFFSET(obj, OF_METER_STATS_LENGTH_OFFSET), bytes);
 }
-
-int
-of_experimenter_stats_request_to_object_id(uint32_t experimenter, uint32_t subtype, int ver)
-{
-    switch (experimenter) {
-    case OF_EXPERIMENTER_ID_BSN:
-        switch (subtype) {
-        case 1: return OF_BSN_LACP_STATS_REQUEST;
-        case 2: return OF_BSN_GENTABLE_ENTRY_DESC_STATS_REQUEST;
-        case 3: return OF_BSN_GENTABLE_ENTRY_STATS_REQUEST;
-        case 4: return OF_BSN_GENTABLE_DESC_STATS_REQUEST;
-        case 5: return OF_BSN_GENTABLE_BUCKET_STATS_REQUEST;
-        case 6: return OF_BSN_SWITCH_PIPELINE_STATS_REQUEST;
-        case 7: return OF_BSN_GENTABLE_STATS_REQUEST;
-        case 8: return OF_BSN_PORT_COUNTER_STATS_REQUEST;
-        case 9: return OF_BSN_VLAN_COUNTER_STATS_REQUEST;
-        case 10: return OF_BSN_FLOW_CHECKSUM_BUCKET_STATS_REQUEST;
-        case 11: return OF_BSN_TABLE_CHECKSUM_STATS_REQUEST;
-        case 12: return OF_BSN_DEBUG_COUNTER_STATS_REQUEST;
-        case 13: return OF_BSN_DEBUG_COUNTER_DESC_STATS_REQUEST;
-        case 14: return OF_BSN_IMAGE_DESC_STATS_REQUEST;
-        }
-    }
-    return OF_OBJECT_INVALID;
-}
-
-int
-of_experimenter_stats_reply_to_object_id(uint32_t experimenter, uint32_t subtype, int ver)
-{
-    switch (experimenter) {
-    case OF_EXPERIMENTER_ID_BSN:
-        switch (subtype) {
-        case 1: return OF_BSN_LACP_STATS_REPLY;
-        case 2: return OF_BSN_GENTABLE_ENTRY_DESC_STATS_REPLY;
-        case 3: return OF_BSN_GENTABLE_ENTRY_STATS_REPLY;
-        case 4: return OF_BSN_GENTABLE_DESC_STATS_REPLY;
-        case 5: return OF_BSN_GENTABLE_BUCKET_STATS_REPLY;
-        case 6: return OF_BSN_SWITCH_PIPELINE_STATS_REPLY;
-        case 7: return OF_BSN_GENTABLE_STATS_REPLY;
-        case 8: return OF_BSN_PORT_COUNTER_STATS_REPLY;
-        case 9: return OF_BSN_VLAN_COUNTER_STATS_REPLY;
-        case 10: return OF_BSN_FLOW_CHECKSUM_BUCKET_STATS_REPLY;
-        case 11: return OF_BSN_TABLE_CHECKSUM_STATS_REPLY;
-        case 12: return OF_BSN_DEBUG_COUNTER_STATS_REPLY;
-        case 13: return OF_BSN_DEBUG_COUNTER_DESC_STATS_REPLY;
-        case 14: return OF_BSN_IMAGE_DESC_STATS_REPLY;
-        }
-    }
-    return OF_OBJECT_INVALID;
-}
diff --git a/lang_c.py b/lang_c.py
index 8e153fe..c7b68f7 100644
--- a/lang_c.py
+++ b/lang_c.py
@@ -123,6 +123,7 @@
     for (name, fn) in targets.items():
         with template_utils.open_output(install_dir, name) as outfile:
             fn(outfile, os.path.basename(name))
+    c_gen.codegen.build_class_metadata()
     c_gen.codegen.generate_classes(install_dir)
     c_gen.codegen.generate_header_classes(install_dir)
     c_gen.codegen.generate_classes_header(install_dir)
diff --git a/loxi_utils/loxi_utils.py b/loxi_utils/loxi_utils.py
index 865891f..c86c503 100644
--- a/loxi_utils/loxi_utils.py
+++ b/loxi_utils/loxi_utils.py
@@ -177,3 +177,11 @@
         return enum.params['wire_type']
     else:
         return oftype
+
+def oftype_is_list(oftype):
+    return (oftype.find("list(") == 0)
+
+# Converts "list(of_flow_stats_entry_t)" to "of_flow_stats_entry"
+def oftype_list_elem(oftype):
+    assert oftype.find("list(") == 0
+    return oftype[5:-3]