Merge into master from pull request #213:
Adding unpadded Instruction Ids for ofp_table_features_prop_instructions* (https://github.com/floodlight/loxigen/pull/213)
diff --git a/c_gen/c_test_gen.py b/c_gen/c_test_gen.py
index cf03e0e..5b74649 100644
--- a/c_gen/c_test_gen.py
+++ b/c_gen/c_test_gen.py
@@ -68,6 +68,8 @@
 import c_gen.identifiers as identifiers
 import util
 import test_data
+import loxi_globals
+from loxi_ir import *
 
 def var_name_map(m_type):
     """
@@ -124,40 +126,14 @@
     or those that should not be messed with
     or whose types we're not ready to deal with yet.
     """
-    # This will probably need more granularity as more extensions are added
-    if (type_maps.class_is_extension(cls, version) and (
-            m_name == "experimenter" or
-            m_name == "subtype")):
+
+    uclass = loxi_globals.unified.class_by_name(cls)
+    if not uclass:
         return True
 
-    classes = ["of_bsn_lacp_stats_request",
-               "of_bsn_lacp_stats_reply",
-               "of_bsn_switch_pipeline_stats_request",
-               "of_bsn_switch_pipeline_stats_reply",
-               "of_bsn_port_counter_stats_request",
-               "of_bsn_port_counter_stats_reply",
-               "of_bsn_vlan_counter_stats_request",
-               "of_bsn_vlan_counter_stats_reply",
-               "of_bsn_gentable_entry_desc_stats_request",
-               "of_bsn_gentable_entry_desc_stats_reply",
-               "of_bsn_gentable_entry_stats_request",
-               "of_bsn_gentable_entry_stats_reply",
-               "of_bsn_gentable_desc_stats_request",
-               "of_bsn_gentable_desc_stats_reply",
-               "of_bsn_gentable_stats_request",
-               "of_bsn_gentable_stats_reply",
-               "of_bsn_gentable_bucket_stats_request",
-               "of_bsn_gentable_bucket_stats_reply",
-               "of_bsn_flow_checksum_bucket_stats_request",
-               "of_bsn_flow_checksum_bucket_stats_reply",
-               "of_bsn_table_checksum_stats_request",
-               "of_bsn_table_checksum_stats_reply",
-            ]
-
-    if (cls in classes and (
-            m_name == "experimenter" or
-            m_name == "subtype")):
+    if not isinstance(uclass.member_by_name(m_name), OFDataMember):
         return True
+
     return loxi_utils.skip_member_name(m_name) or m_type not in scalar_types
 
 def gen_fill_string(out):
diff --git a/c_gen/codegen.py b/c_gen/codegen.py
index 3249747..121bc42 100644
--- a/c_gen/codegen.py
+++ b/c_gen/codegen.py
@@ -35,6 +35,7 @@
 from itertools import groupby
 from StringIO import StringIO
 import template_utils
+from generic_utils import chunks
 import loxi_globals
 import loxi_ir.ir as ir
 import util
@@ -43,6 +44,8 @@
 import c_gen.type_maps as type_maps
 import c_gen.c_type_maps as c_type_maps
 
+CLASS_CHUNK_SIZE = 32
+
 PushWireTypesData = namedtuple('PushWireTypesData',
     ['class_name', 'versioned_type_members'])
 PushWireTypesMember = namedtuple('PushWireTypesMember',
@@ -75,14 +78,18 @@
         class_name=uclass.name,
         versioned_type_members=versioned_type_members)
 
+# Output multiple LOCI classes into each C file. This reduces the overhead of
+# parsing header files, which takes longer than compiling the actual code
+# for many classes. It also reduces the compiled code size.
 def generate_classes(install_dir):
-    for uclass in loxi_globals.unified.classes:
-        with template_utils.open_output(install_dir, "loci/src/%s.c" % uclass.name) as out:
-            util.render_template(out, "class.c",
-                push_wire_types_data=push_wire_types_data(uclass))
-            # Append legacy generated code
-            c_code_gen.gen_new_function_definitions(out, uclass.name)
-            c_code_gen.gen_accessor_definitions(out, uclass.name)
+    for i, chunk in enumerate(chunks(loxi_globals.unified.classes, CLASS_CHUNK_SIZE)):
+        with template_utils.open_output(install_dir, "loci/src/class%02d.c" % i) as out:
+            for uclass in chunk:
+                util.render_template(out, "class.c",
+                    push_wire_types_data=push_wire_types_data(uclass))
+                # Append legacy generated code
+                c_code_gen.gen_new_function_definitions(out, uclass.name)
+                c_code_gen.gen_accessor_definitions(out, uclass.name)
 
 # TODO remove header classes and use the corresponding class instead
 def generate_header_classes(install_dir):
diff --git a/generic_utils.py b/generic_utils.py
index 1cfba86..5683aa1 100644
--- a/generic_utils.py
+++ b/generic_utils.py
@@ -214,3 +214,11 @@
         if func(i):
             c +=1
     return c
+
+def chunks(l, n):
+    """
+    Yield successive n-sized chunks from l.
+    From http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
+    """
+    for i in xrange(0, len(l), n):
+        yield l[i:i+n]
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/HexString.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/HexString.java
index ddf0f25..bcc46f7 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/HexString.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/util/HexString.java
@@ -17,8 +17,6 @@
 
 package org.projectfloodlight.openflow.util;
 
-import java.math.BigInteger;
-
 import org.projectfloodlight.openflow.types.U8;
 
 public class HexString {
@@ -86,13 +84,17 @@
         return ret;
     }
 
-    public static long toLong(final String values) throws NumberFormatException {
-        // Long.parseLong() can't handle HexStrings with MSB set. Sigh.
-        BigInteger bi = new BigInteger(values.replaceAll(":", ""), 16);
-        if (bi.bitLength() > 64)
-            throw new NumberFormatException("Input string too big to fit in long: "
-                    + values);
-        return bi.longValue();
+    public static long toLong(String value) throws NumberFormatException {
+        String[] octets = value.split(":");
+        if (octets.length > 8)
+            throw new NumberFormatException("Input string is too big to fit in long: " + value);
+        long l = 0;
+        for (String octet: octets) {
+            if (octet.length() > 2)
+                throw new NumberFormatException("Each colon-separated byte component must consist of 1 or 2 hex digits: " + value);
+            short s = Short.parseShort(octet, 16);
+            l = (l << 8) + s;
+        }
+        return l;
     }
-
 }
diff --git a/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/util/HexStringTest.java b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/util/HexStringTest.java
new file mode 100644
index 0000000..360cb5a
--- /dev/null
+++ b/java_gen/pre-written/src/test/java/org/projectfloodlight/openflow/util/HexStringTest.java
@@ -0,0 +1,103 @@
+/**
+*    Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
+*    University
+*
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    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
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.projectfloodlight.openflow.util;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+/**
+ * Does hexstring conversion work?
+ *
+ * @author Rob Sherwood (rob.sherwood@stanford.edu)
+ */
+public class HexStringTest {
+
+    @Test
+    public void testMarshalling() throws Exception {
+        String dpidStr = "00:00:00:23:20:2d:16:71";
+        long dpid = HexString.toLong(dpidStr);
+        String testStr = HexString.toHexString(dpid);
+        assertEquals(dpidStr, testStr);
+    }
+
+    @Test
+    public void testToLong() {
+        String dpidStr = "3e:1f:01:fc:72:8c:63:31";
+        long valid = 0x3e1f01fc728c6331L;
+        long testLong = HexString.toLong(dpidStr);
+        assertEquals(valid, testLong);
+    }
+
+    @Test
+    public void testToLong2() {
+        String dpidStr = "1f:1:fc:72:3:f:31";
+        long valid = 0x1f01fc72030f31L;
+        long testLong = HexString.toLong(dpidStr);
+        assertEquals(valid, testLong);
+    }
+
+    @Test
+    public void testToLongMSB() {
+        String dpidStr = "ca:7c:5e:d1:64:7a:95:9b";
+        long valid = -3856102927509056101L;
+        long testLong = HexString.toLong(dpidStr);
+        assertEquals(valid, testLong);
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorTooManyBytes() {
+        HexString.toLong("09:08:07:06:05:04:03:02:01");
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorByteValueTooLong() {
+        HexString.toLong("234:01");
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorEmptyByte() {
+        HexString.toLong("03::01");
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorInvalidHexDigit() {
+        HexString.toLong("ss:01");
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testToLongErrorEmptyString() {
+        HexString.toLong("");
+    }
+
+
+    @Test
+    public void testToStringBytes() {
+        byte[] dpid = { 0, 0, 0, 0, 0, 0, 0, -1 };
+        String valid = "00:00:00:00:00:00:00:ff";
+        String testString = HexString.toHexString(dpid);
+        assertEquals(valid, testString);
+    }
+
+    @Test(expected=NumberFormatException.class)
+    public void testFromHexStringError() {
+        String invalidStr = "00:00:00:00:00:00:ffff";
+        HexString.fromHexString(invalidStr);
+    }
+}
+