Packed struct support (#290)
authorDag Sverre Seljebotn <dagss@student.matnat.uio.no>
Thu, 23 Apr 2009 18:03:36 +0000 (20:03 +0200)
committerDag Sverre Seljebotn <dagss@student.matnat.uio.no>
Thu, 23 Apr 2009 18:03:36 +0000 (20:03 +0200)
Cython/Compiler/ModuleNode.py
Cython/Compiler/Nodes.py
Cython/Compiler/Parsing.py
Cython/Compiler/PyrexTypes.py
Cython/Compiler/Scanning.py
Cython/Compiler/Symtab.py
tests/errors/e_packedstruct_T290.pyx [new file with mode: 0644]
tests/run/packedstruct_T290.pyx [new file with mode: 0644]

index 018fb19dbaeb54c9aa394c932b2ea43d89d9b710..d5f79953a3f394fdcf4e6bafc2a04dc9a10976d7 100644 (file)
@@ -655,9 +655,18 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
         type = entry.type
         scope = type.scope
         if scope:
+            kind = type.kind
+            packed = type.is_struct and type.packed
+            if packed:
+                kind = "%s %s" % (type.kind, "__Pyx_PACKED")
+                code.globalstate.use_utility_code(packed_struct_utility_code)
             header, footer = \
-                self.sue_header_footer(type, type.kind, type.cname)
+                self.sue_header_footer(type, kind, type.cname)
             code.putln("")
+            if packed:
+                code.putln("#if !defined(__GNUC__)")
+                code.putln("#pragma pack(push, 1)")
+                code.putln("#endif")
             code.putln(header)
             var_entries = scope.var_entries
             if not var_entries:
@@ -669,6 +678,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
                     "%s;" %
                         attr.type.declaration_code(attr.cname))
             code.putln(footer)
+            if packed:
+                code.putln("#if !defined(__GNUC__)")
+                code.putln("#pragma pack(pop)")
+                code.putln("#endif")
 
     def generate_enum_definition(self, entry, code):
         code.mark_pos(entry.pos)
@@ -2479,3 +2492,11 @@ int main(int argc, char** argv) {
     return r;
 }
 """)
+
+packed_struct_utility_code = UtilityCode(proto="""
+#if defined(__GNUC__)
+#define __Pyx_PACKED __attribute__((__packed__))
+#else
+#define __Pyx_PACKED
+#endif
+""", impl="")
index 2b5cef25f28e30267302bcf05d49cfd5c72cba79..1638189d1425930eb14e86423dbe08cc1b8dc2f0 100644 (file)
@@ -840,16 +840,19 @@ class CStructOrUnionDefNode(StatNode):
     #  in_pxd        boolean
     #  attributes    [CVarDefNode] or None
     #  entry         Entry
+    #  packed        boolean
     
     child_attrs = ["attributes"]
 
     def analyse_declarations(self, env):
         scope = None
+        if self.visibility == 'extern' and self.packed:
+            error(self.pos, "Cannot declare extern struct as 'packed'")
         if self.attributes is not None:
             scope = StructOrUnionScope(self.name)
         self.entry = env.declare_struct_or_union(
             self.name, self.kind, scope, self.typedef_flag, self.pos,
-            self.cname, visibility = self.visibility)
+            self.cname, visibility = self.visibility, packed = self.packed)
         if self.attributes is not None:
             if self.in_pxd and not env.in_cinclude:
                 self.entry.defined_in_pxd = 1
index 62c041aa09136ea3ecb4d183da5fa4d5b91aed2e..85919514a482d57dc0ffe08ceffc0684050856eb 100644 (file)
@@ -2078,7 +2078,7 @@ def p_cdef_statement(s, ctx):
         #if ctx.api:
         #    error(pos, "'api' not allowed with extension class")
         return p_c_class_definition(s, pos, ctx)
-    elif s.sy == 'IDENT' and s.systring in struct_union_or_enum:
+    elif s.sy == 'IDENT' and s.systring in ("struct", "union", "enum", "packed"):
         if ctx.level not in ('module', 'module_pxd'):
             error(pos, "C struct/union/enum definition not allowed here")
         #if ctx.visibility == 'public':
@@ -2114,10 +2114,6 @@ def p_cdef_extern_block(s, pos, ctx):
         include_file = include_file,
         body = body)
 
-struct_union_or_enum = (
-    "struct", "union", "enum"
-)
-
 def p_c_enum_definition(s, pos, ctx):
     # s.sy == ident 'enum'
     s.next()
@@ -2168,6 +2164,12 @@ def p_c_enum_item(s, items):
         name = name, cname = cname, value = value))
 
 def p_c_struct_or_union_definition(s, pos, ctx):
+    packed = False
+    if s.systring == 'packed':
+        packed = True
+        s.next()
+        if s.sy != 'IDENT' and s.systring != 'struct':
+            s.expected('struct')
     # s.sy == ident 'struct' or 'union'
     kind = s.systring
     s.next()
@@ -2193,7 +2195,7 @@ def p_c_struct_or_union_definition(s, pos, ctx):
     return Nodes.CStructOrUnionDefNode(pos, 
         name = name, cname = cname, kind = kind, attributes = attributes,
         typedef_flag = ctx.typedef_flag, visibility = ctx.visibility,
-        in_pxd = ctx.level == 'module_pxd')
+        in_pxd = ctx.level == 'module_pxd', packed = packed)
 
 def p_visibility(s, prev_visibility):
     pos = s.position()
@@ -2265,7 +2267,7 @@ def p_ctypedef_statement(s, ctx):
         ctx.api = 1
     if s.sy == 'class':
         return p_c_class_definition(s, pos, ctx)
-    elif s.sy == 'IDENT' and s.systring in ('struct', 'union', 'enum'):
+    elif s.sy == 'IDENT' and s.systring in ('packed', 'struct', 'union', 'enum'):
         if s.systring == 'enum':
             return p_c_enum_definition(s, pos, ctx)
         else:
index 1660dec54fbad4b61063702337bb3c2a19766bfd..7bba47076dfe22c41cd8ce8e86150794f0dc7e68 100644 (file)
@@ -1023,11 +1023,12 @@ class CStructOrUnionType(CType):
     #  kind          string              "struct" or "union"
     #  scope         StructOrUnionScope, or None if incomplete
     #  typedef_flag  boolean
+    #  packed        boolean
     
     is_struct_or_union = 1
     has_attributes = 1
     
-    def __init__(self, name, kind, scope, typedef_flag, cname):
+    def __init__(self, name, kind, scope, typedef_flag, cname, packed=False):
         self.name = name
         self.cname = cname
         self.kind = kind
@@ -1038,6 +1039,7 @@ class CStructOrUnionType(CType):
             self.to_py_function = "%s_to_py_%s" % (Naming.convert_func_prefix, self.cname)
         self.exception_check = True
         self._convert_code = None
+        self.packed = packed
         
     def create_convert_utility_code(self, env):
         if env.outer_scope is None:
index d5eae3b6e405d15b7616885b81ac612c4cc8341d..fd8d42c9c7ee4f20ff14ececa55af18a2aae0bdc 100644 (file)
@@ -466,7 +466,7 @@ class PyrexScanner(Scanner):
         else:
             self.expected(what, message)
     
-    def expected(self, what, message):
+    def expected(self, what, message = None):
         if message:
             self.error(message)
         else:
index ff6db3b1f7dcee9a74db60c8f426bb763d6de84d..d972309f0a46bfec2935808b3c39ff1760b72dc4 100644 (file)
@@ -353,7 +353,8 @@ class Scope(object):
         return entry
         
     def declare_struct_or_union(self, name, kind, scope, 
-            typedef_flag, pos, cname = None, visibility = 'private'):
+            typedef_flag, pos, cname = None, visibility = 'private',
+            packed = False):
         # Add an entry for a struct or union definition.
         if not cname:
             if self.in_cinclude or visibility == 'public':
@@ -363,7 +364,7 @@ class Scope(object):
         entry = self.lookup_here(name)
         if not entry:
             type = PyrexTypes.CStructOrUnionType(
-                name, kind, scope, typedef_flag, cname)
+                name, kind, scope, typedef_flag, cname, packed)
             entry = self.declare_type(name, type, pos, cname,
                 visibility = visibility, defining = scope is not None)
             self.sue_entries.append(entry)
diff --git a/tests/errors/e_packedstruct_T290.pyx b/tests/errors/e_packedstruct_T290.pyx
new file mode 100644 (file)
index 0000000..408246c
--- /dev/null
@@ -0,0 +1,7 @@
+cdef extern:
+    cdef packed struct MyStruct:
+        char a
+
+_ERRORS = u"""
+2:9: Cannot declare extern struct as 'packed'
+"""
diff --git a/tests/run/packedstruct_T290.pyx b/tests/run/packedstruct_T290.pyx
new file mode 100644 (file)
index 0000000..a66915f
--- /dev/null
@@ -0,0 +1,16 @@
+"""
+>>> f()
+(9, 9)
+"""
+
+cdef packed struct MyCdefStruct:
+    char a
+    double b
+
+ctypedef packed struct MyCTypeDefStruct:
+    char a
+    double b
+
+def f():
+    return (sizeof(MyCdefStruct), sizeof(MyCTypeDefStruct))
+