Merge into master from pull request #245:
Rewrite LOCI validator (https://github.com/floodlight/loxigen/pull/245)
diff --git a/c_gen/c_validator_gen.py b/c_gen/c_validator_gen.py
deleted file mode 100644
index 9bb0407..0000000
--- a/c_gen/c_validator_gen.py
+++ /dev/null
@@ -1,322 +0,0 @@
-# Copyright 2013, 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 2013, 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.
-
-"""
-@brief Validator function generation
-
-Generates validator function files.
-
-"""
-
-import sys
-import c_gen.of_g_legacy as of_g
-import c_gen.match as match
-import c_gen.flags as flags
-from generic_utils import *
-import c_gen.type_maps as type_maps
-import loxi_utils.loxi_utils as loxi_utils
-import c_gen.loxi_utils_legacy as loxi_utils
-import c_gen.identifiers as identifiers
-from c_test_gen import var_name_map
-from c_code_gen import v3_match_offset_get
-
-def gen_h(out, name):
-    loxi_utils.gen_c_copy_license(out)
-    out.write("""
-/**
- *
- * AUTOMATICALLY GENERATED FILE.  Edits will be lost on regen.
- *
- * Declarations of message validation functions. These functions check that an
- * OpenFlow message is well formed. Specifically, they check internal length
- * fields.
- */
-
-#if !defined(_LOCI_VALIDATOR_H_)
-#define _LOCI_VALIDATOR_H_
-
-#include <loci/loci.h>
-
-/*
- * Validate an OpenFlow message.
- * @return 0 if message is valid, -1 otherwise.
- */
-extern int of_validate_message(of_message_t msg, int len);
-
-#endif /* _LOCI_VALIDATOR_H_ */
-""")
-
-def gen_c(out, name):
-    loxi_utils.gen_c_copy_license(out)
-    out.write("""
-/**
- *
- * AUTOMATICALLY GENERATED FILE.  Edits will be lost on regen.
- *
- * Source file for OpenFlow message validation.
- *
- */
-
-#include "loci_log.h"
-#include <loci/loci.h>
-#include <loci/loci_validator.h>
-
-#define VALIDATOR_LOG(...) LOCI_LOG_ERROR("Validator Error: " __VA_ARGS__)
-
-""")
-
-    # Declarations
-    for version in of_g.of_version_range:
-        ver_name = loxi_utils.version_to_name(version)
-        for cls in reversed(of_g.standard_class_order):
-            if not loxi_utils.class_in_version(cls, version):
-                continue
-            if cls in type_maps.inheritance_map:
-                continue
-            out.write("""
-static inline int %(cls)s_%(ver_name)s_validate(uint8_t *buf, int len);\
-""" % dict(cls=cls, ver_name=ver_name))
-
-    out.write("\n")
-
-    # Definitions
-    for version in of_g.of_version_range:
-        ver_name = loxi_utils.version_to_name(version)
-        for cls in reversed(of_g.standard_class_order):
-            if not loxi_utils.class_in_version(cls, version):
-                continue
-            if cls in type_maps.inheritance_map:
-                continue
-            if loxi_utils.class_is_list(cls):
-                gen_list_validator(out, cls, version)
-            else:
-                gen_validator(out, cls, version)
-
-        out.write("""
-int
-of_validate_message_%(ver_name)s(of_message_t msg, int len)
-{
-    of_object_id_t object_id = of_message_to_object_id(msg, len);
-    uint8_t *buf = OF_MESSAGE_TO_BUFFER(msg);
-    switch (object_id) {
-""" % dict(ver_name=ver_name))
-        for cls in reversed(of_g.standard_class_order):
-            if not loxi_utils.class_in_version(cls, version):
-                continue
-            if cls in type_maps.inheritance_map:
-                continue
-            if loxi_utils.class_is_message(cls):
-                out.write("""\
-    case %(cls_id)s:
-        return %(cls)s_%(ver_name)s_validate(buf, len);
-""" % dict(ver_name=ver_name, cls=cls, cls_id=cls.upper()))
-        out.write("""\
-    default:
-        VALIDATOR_LOG("%(cls)s: could not map %(cls_id)s");
-        return -1;
-    }
-}
-""" % dict(ver_name=ver_name, cls=cls, cls_id=cls.upper()))
-
-    out.write("""
-int
-of_validate_message(of_message_t msg, int len)
-{
-    of_version_t version;
-    if (len < OF_MESSAGE_MIN_LENGTH ||
-        len != of_message_length_get(msg)) {
-        VALIDATOR_LOG("message length %d != %d", len,
-                      of_message_length_get(msg));
-        return -1;
-    }
-
-    version = of_message_version_get(msg);
-    switch (version) {
-""")
-
-    for version in of_g.of_version_range:
-        ver_name = loxi_utils.version_to_name(version)
-        out.write("""\
-    case %(ver_name)s:
-        return of_validate_message_%(ver_name)s(msg, len);
-""" % dict(ver_name=ver_name))
-
-    out.write("""\
-    default:
-        VALIDATOR_LOG("Bad version %%d", %(ver_name)s);
-        return -1;
-    }
-}
-""" % dict(ver_name=ver_name))
-
-def gen_validator(out, cls, version):
-    fixed_len = of_g.base_length[(cls, version)];
-    ver_name = loxi_utils.version_to_name(version)
-    out.write("""
-static inline int
-%(cls)s_%(ver_name)s_validate(uint8_t *buf, int len)
-{
-    if (len < %(fixed_len)s) {
-        VALIDATOR_LOG("Class %(cls)s.  Len %%d too small, < %%d", len, %(fixed_len)s);
-        return -1;
-    }
-""" % dict(cls=cls, ver_name=ver_name, cls_id=cls.upper(), fixed_len=fixed_len))
-    members, member_types = loxi_utils.all_member_types_get(cls, version)
-    for member in members:
-        m_type = member["m_type"]
-        m_name = member["name"]
-        m_offset = member['offset']
-        m_cls = m_type[:-2] # Trim _t
-        if loxi_utils.skip_member_name(m_name):
-            continue
-        if not loxi_utils.type_is_of_object(m_type):
-            continue
-        if not loxi_utils.class_is_var_len(m_cls, version):
-            continue
-        if cls == "of_packet_out" and m_name == "actions":
-            # See _PACKET_OUT_ACTION_LEN
-            out.write("""
-    {
-        uint16_t %(m_name)s_len;
-        buf_u16_get(buf + %(m_offset)s - 2, &%(m_name)s_len);
-        if (%(m_name)s_len + %(m_offset)s > len) {
-            VALIDATOR_LOG("Class %(cls)s, member %(m_name)s.  "
-                          "Len %%d and offset %%d too big for %%d",
-                          %(m_name)s_len, %(m_offset)s, len);
-            return -1;
-        }
-""" % dict(m_name=m_name, m_offset=m_offset, cls=cls))
-        elif version >= of_g.VERSION_1_2 and loxi_utils.cls_is_flow_mod(cls) and m_name == "instructions":
-            # See _FLOW_MOD_INSTRUCTIONS_OFFSET
-            match_offset = v3_match_offset_get(cls)
-            m_offset = '%s_offset' % m_name
-            out.write("""
-    {
-        uint16_t %(m_name)s_len, %(m_name)s_offset;
-        uint16_t match_len;
-        buf_u16_get(buf + %(match_offset)s + 2, &match_len);
-        %(m_name)s_offset = %(match_offset)s + OF_MATCH_BYTES(match_len);
-        %(m_name)s_len = len - %(m_name)s_offset;
-""" % dict(m_name=m_name, cls=cls, match_offset=match_offset))
-        elif cls == "of_bsn_gentable_entry_add" and m_name == "value":
-            continue;
-        elif cls == "of_bsn_gentable_entry_desc_stats_entry" and m_name == "value":
-            continue;
-        elif cls == "of_bsn_gentable_entry_stats_entry" and m_name == "stats":
-            continue;
-        else:
-            out.write("""
-
-    {    int %(m_name)s_len = len - %(m_offset)s;
-
-"""  % dict(m_name=m_name, m_offset=m_offset))
-        out.write("""
-        if (%(m_cls)s_%(ver_name)s_validate(buf + %(m_offset)s, %(m_name)s_len) < 0) {
-            return -1;
-        }
-    }
-""" % dict(m_name=m_name, m_cls=m_cls, ver_name=ver_name, m_offset=m_offset))
-    out.write("""
-    return 0;
-}
-""")
-
-def gen_list_validator(out, cls, version):
-    ver_name = loxi_utils.version_to_name(version)
-    e_cls = loxi_utils.list_to_entry_type(cls)
-    fixed_len = of_g.base_length[(e_cls, version)];
-    out.write("""
-static inline int
-%(cls)s_%(ver_name)s_validate(uint8_t *buf, int len)
-{
-""" % dict(cls=cls, ver_name=ver_name, cls_id=cls.upper(), e_cls=e_cls))
-
-    # TLV16
-    if loxi_utils.class_is_tlv16(e_cls):
-        subclasses = type_maps.inheritance_map[e_cls]
-        out.write("""\
-    while (len >= %(fixed_len)s) {
-        of_object_id_t e_id;
-        uint16_t e_type, e_len;
-        buf_u16_get(buf, &e_type);
-        buf_u16_get(buf+2, &e_len);
-        e_id = %(e_cls)s_to_object_id(e_type, %(ver_name)s);
-        switch (e_id) {
-""" % dict(fixed_len=fixed_len, ver_name=ver_name, e_cls=e_cls))
-        for subcls in subclasses:
-            subcls = e_cls + '_' + subcls
-            if not loxi_utils.class_in_version(subcls, version):
-                continue
-            out.write("""\
-        case %(subcls_enum)s:
-            if (%(subcls)s_%(ver_name)s_validate(buf, e_len) < 0) {
-                return -1;
-            }
-            break;
-""" % dict(ver_name=ver_name, subcls=subcls, subcls_enum=loxi_utils.enum_name(subcls)))
-        out.write("""\
-        default:
-            return -1;
-        }
-        buf += e_len;
-        len -= e_len;
-    }
-    if (len != 0) {
-        return -1;
-    }
-""" % dict(e_cls=e_cls, ver_name=ver_name))
-
-    # U16 len
-    elif loxi_utils.class_is_u16_len(e_cls) or loxi_utils.class_is_action(e_cls):
-        out.write("""\
-    /* TODO verify U16 len elements */
-""" % dict())
-
-    # OXM
-    elif loxi_utils.class_is_oxm(e_cls):
-        out.write("""\
-    /* TODO verify OXM elements */
-""" % dict())
-
-    # Fixed length
-    elif not loxi_utils.class_is_var_len(e_cls, version):
-        out.write("""\
-    if ((len / %(fixed_len)s) * %(fixed_len)s != len) {
-        return -1;
-    }
-""" % dict(fixed_len=fixed_len))
-
-    # ???
-    else:
-        out.write("""\
-    /* XXX unknown element format */
-""" % dict())
-
-    out.write("""
-    return 0;
-}
-""")
diff --git a/c_gen/templates/loci_validator.c b/c_gen/templates/loci_validator.c
new file mode 100644
index 0000000..4a5a692
--- /dev/null
+++ b/c_gen/templates/loci_validator.c
@@ -0,0 +1,202 @@
+:: # 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')
+:: import loxi_globals
+:: from loxi_ir import *
+
+/**
+ *
+ * AUTOMATICALLY GENERATED FILE.  Edits will be lost on regen.
+ *
+ * Source file for OpenFlow message validation.
+ *
+ */
+
+#include "loci_log.h"
+#include <loci/loci.h>
+#include <loci/loci_validator.h>
+
+#define VALIDATOR_LOG(...) LOCI_LOG_ERROR("Validator Error: " __VA_ARGS__)
+
+:: raw_validator_name = lambda cls, version: "loci_validate_%s_%s" % (cls, version.constant_version(prefix='OF_VERSION_'))
+:: validator_name = lambda ofclass: "loci_validate_%s_%s" % (ofclass.name, ofclass.protocol.version.constant_version(prefix='OF_VERSION_'))
+
+/* Forward declarations */
+:: for version, proto in loxi_globals.ir.items():
+:: for ofclass in proto.classes:
+static int __attribute__((unused)) ${validator_name(ofclass)}(uint8_t *data, int len, int *out_len);
+:: #endfor
+:: #endfor
+
+:: readers = { 1: 'buf_u8_get', 2: 'buf_u16_get', 4: 'buf_u32_get' }
+:: types = { 1: 'uint8_t', 2: 'uint16_t', 4: 'uint32_t' }
+
+:: for version, proto in loxi_globals.ir.items():
+
+:: # Identify classes in lists and generate list validators
+:: seen_lists = set()
+:: for ofclass in proto.classes:
+:: for m in ofclass.members:
+:: if type(m) == OFDataMember and m.oftype.startswith('list'):
+:: element_name = m.oftype[8:-3]
+:: if element_name in seen_lists:
+:: continue
+:: #endif
+:: seen_lists.add(element_name)
+:: list_validator_name = raw_validator_name('of_list_' + element_name, version)
+static int __attribute__((unused))
+${list_validator_name}(uint8_t *data, int len, int *out_len)
+{
+    while (len > 0) {
+        int cur_len = 0xffff;
+        if (${raw_validator_name('of_' + element_name, version)}(data, len, &cur_len) < 0) {
+            return -1;
+        }
+        len -= cur_len;
+        data += cur_len;
+    }
+
+    return 0;
+}
+
+:: #endif
+:: #endfor
+:: #endfor
+
+:: for ofclass in proto.classes:
+static int
+${validator_name(ofclass)}(uint8_t *data, int len, int *out_len)
+{
+    if (len < ${ofclass.base_length}) {
+        return -1;
+    }
+
+:: if ofclass.is_fixed_length:
+    len = ${ofclass.base_length};
+:: #endif
+
+:: # Read and validate length fields
+:: field_length_members = {}
+:: for m in ofclass.members:
+:: if type(m) == OFLengthMember:
+    ${types[m.length]} wire_len;
+    ${readers[m.length]}(data + ${m.offset}, &wire_len);
+    if (wire_len > len || wire_len < ${ofclass.base_length}) {
+        return -1;
+    }
+
+    len = wire_len;
+
+:: elif type(m) == OFFieldLengthMember:
+:: # Read field length members
+:: field_length_members[m.field_name] = m
+    ${types[m.length]} wire_len_${m.field_name};
+    ${readers[m.length]}(data + ${m.offset}, &wire_len_${m.field_name});
+
+:: #endif
+:: #endfor
+
+:: # Dispatch to subclass validators
+:: if ofclass.virtual:
+:: discriminator = ofclass.discriminator
+    ${types[discriminator.length]} wire_type;
+    ${readers[discriminator.length]}(data + ${discriminator.offset}, &wire_type);
+    switch (wire_type) {
+:: for subclass in proto.classes:
+:: if subclass.superclass == ofclass:
+    case ${subclass.member_by_name(discriminator.name).value}:
+        return ${validator_name(subclass)}(data, len, out_len);
+:: #endif
+:: #endfor
+    }
+:: #endif
+
+:: for m in ofclass.members:
+:: # Validate field-length members
+:: if type(m) == OFDataMember and m.name in field_length_members and m.offset is not None:
+    if (${m.offset} + wire_len_${m.name} > len) {
+        return -1;
+    }
+
+:: #endif
+:: if type(m) == OFDataMember and m.oftype.startswith('list'):
+:: # Validate lists
+:: if m.offset is None:
+    // TODO validate non fixed offset member ${m.name}
+:: continue
+:: #endif
+:: if not m.name in field_length_members:
+    int wire_len_${m.name} = len - ${m.offset};
+:: #endif
+:: element_name = m.oftype[8:-3]
+:: list_validator_name = raw_validator_name('of_list_' + element_name, version)
+    if (${list_validator_name}(data + ${m.offset}, wire_len_${m.name}, out_len) < 0) {
+        return -1;
+    }
+
+:: elif type(m) == OFDataMember and m.oftype == "of_match_t":
+    // TODO validate of_match_t
+:: elif type(m) == OFDataMember and m.oftype == "of_bsn_vport_t":
+    // TODO validate of_bsn_vport_t
+:: elif type(m) == OFDataMember and m.oftype == "of_meter_config_t":
+    // TODO validate of_meter_config_t
+:: elif type(m) == OFDataMember and m.oftype == "of_meter_features_t":
+    // TODO validate of_meter_features_t
+:: #endif
+:: #endfor
+
+    *out_len = len;
+    return 0;
+}
+
+:: #endfor
+:: #endfor
+
+int
+of_validate_message(of_message_t msg, int len)
+{
+    of_version_t version;
+    if (len < OF_MESSAGE_MIN_LENGTH ||
+        len != of_message_length_get(msg)) {
+        VALIDATOR_LOG("message length %d != %d", len,
+                      of_message_length_get(msg));
+        return -1;
+    }
+
+    version = of_message_version_get(msg);
+    int out_len;
+    switch (version) {
+:: for version, proto in loxi_globals.ir.items():
+    case ${version.constant_version(prefix='OF_VERSION_')}:
+        return ${validator_name(proto.class_by_name('of_header'))}(msg, len, &out_len);
+:: #endfor
+    default:
+        VALIDATOR_LOG("Bad version %d", OF_VERSION_1_3);
+        return -1;
+    }
+}
diff --git a/c_gen/templates/loci_validator.h b/c_gen/templates/loci_validator.h
new file mode 100644
index 0000000..de8cce8
--- /dev/null
+++ b/c_gen/templates/loci_validator.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior University */
+/* Copyright (c) 2011, 2012 Open Networking Foundation */
+/* Copyright (c) 2012, 2013 Big Switch Networks, Inc. */
+/* See the file LICENSE.loci which should have been included in the source distribution */
+
+/**
+ *
+ * AUTOMATICALLY GENERATED FILE.  Edits will be lost on regen.
+ *
+ * Declarations of message validation functions. These functions check that an
+ * OpenFlow message is well formed. Specifically, they check internal length
+ * fields.
+ */
+
+#if !defined(_LOCI_VALIDATOR_H_)
+#define _LOCI_VALIDATOR_H_
+
+#include <loci/loci.h>
+
+/*
+ * Validate an OpenFlow message.
+ * @return 0 if message is valid, -1 otherwise.
+ */
+extern int of_validate_message(of_message_t msg, int len);
+
+#endif /* _LOCI_VALIDATOR_H_ */
diff --git a/lang_c.py b/lang_c.py
index 1969a83..24ccc06 100644
--- a/lang_c.py
+++ b/lang_c.py
@@ -39,7 +39,6 @@
 import c_gen.c_test_gen as c_test_gen
 import c_gen.c_dump_gen as c_dump_gen
 import c_gen.c_show_gen as c_show_gen
-import c_gen.c_validator_gen as c_validator_gen
 import c_gen.util
 import c_gen.codegen
 import loxi_utils.loxi_utils as loxi_utils
@@ -57,7 +56,7 @@
     'loci/inc/loci/loci_doc.h': c_code_gen.gen_accessor_doc,
     'loci/inc/loci/loci_obj_dump.h': c_dump_gen.gen_obj_dump_h,
     'loci/inc/loci/loci_obj_show.h': c_show_gen.gen_obj_show_h,
-    'loci/inc/loci/loci_validator.h': c_validator_gen.gen_h,
+    'loci/inc/loci/loci_validator.h': static,
 
     # Static LOCI headers
     'loci/inc/loci/bsn_ext.h': static,
@@ -75,7 +74,7 @@
     'loci/src/of_match.c': c_code_gen.match_c_gen,
     'loci/src/loci_obj_dump.c': c_dump_gen.gen_obj_dump_c,
     'loci/src/loci_obj_show.c': c_show_gen.gen_obj_show_c,
-    'loci/src/loci_validator.c': c_validator_gen.gen_c,
+    'loci/src/loci_validator.c': static,
 
     # Static LOCI code
     'loci/src/loci_int.h': static,