Skip to content

Commit 3e075aa

Browse files
author
Andrea Bonomi
committed
refactoring, alignment
1 parent d6c52da commit 3e075aa

11 files changed

+308
-66
lines changed

Makefile

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
help:
3+
@echo - make coverage
4+
@echo - make tests
5+
6+
coverage:
7+
python3 -m coverage run --source=cstruct --omit 'cstruct/examples/*.py' setup.py test && python3 -m coverage report -m
8+
9+
tests:
10+
python3 setup.py test
11+
python2 setup.py test

cstruct/__init__.py

+8-22
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,25 @@
3030
__date__ = '15 August 2013'
3131

3232
import struct
33-
import hashlib
3433
from .base import (
34+
LITTLE_ENDIAN,
35+
BIG_ENDIAN,
36+
NATIVE_ORDER,
3537
STRUCTS,
3638
DEFINES,
3739
TYPEDEFS,
3840
C_TYPE_TO_FORMAT,
3941
CHAR_ZERO,
4042
EMPTY_BYTES_STRING
4143
)
42-
from .c_parser import Tokens
4344
from .abstract import CStructMeta
4445
from .cstruct import CStruct
4546
from .mem_cstruct import MemCStruct
4647

4748
__all__ = [
4849
'LITTLE_ENDIAN',
4950
'BIG_ENDIAN',
51+
'NATIVE_ORDER',
5052
'CHAR_ZERO',
5153
'EMPTY_BYTES_STRING',
5254
'CStruct',
@@ -55,16 +57,9 @@
5557
'undef',
5658
'typedef',
5759
'sizeof',
58-
'factory'
60+
'parse'
5961
]
6062

61-
# little-endian, std. size & alignment
62-
LITTLE_ENDIAN = '<'
63-
# big-endian, std. size & alignment
64-
BIG_ENDIAN = '>'
65-
# native order, size & alignment
66-
NATIVE_ORDER = '@'
67-
6863
def define(key, value):
6964
"""
7065
Define a constant that can be used in the C struct
@@ -116,9 +111,9 @@ def sizeof(type_):
116111
else:
117112
return struct.calcsize(ttype)
118113

119-
def factory(__struct__, __cls__=None, __name__=None, **kargs):
114+
def parse(__struct__, __cls__=None, **kargs):
120115
"""
121-
Return a new class mapping a C struct definition.
116+
Return a new class mapping a C struct/union definition.
122117
123118
:param __struct__: definition of the struct (or union) in C syntax
124119
:param __cls__: (optional) super class - CStruct(default) or MemCStruct
@@ -127,16 +122,7 @@ def factory(__struct__, __cls__=None, __name__=None, **kargs):
127122
:param __is_union__: (optional) True for union, False for struct (default)
128123
:returns: __cls__ subclass
129124
"""
130-
kargs = dict(kargs)
131-
if not isinstance(__struct__, Tokens):
132-
__struct__ = Tokens(__struct__)
133-
kargs['__struct__'] = __struct__
134-
if __name__ is None: # Anonymous struct
135-
__name__ = 'CStruct%s' % hashlib.sha1(str(__struct__).encode('utf-8')).hexdigest()
136-
kargs['__anonymous__'] = True
137-
kargs['__name__'] = __name__
138125
if __cls__ is None:
139126
__cls__ = CStruct
140-
return type(__name__, (__cls__,), kargs) # TODO
141-
127+
return __cls__.parse(__struct__, **kargs)
142128

cstruct/abstract.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
#
2626

2727
from .base import STRUCTS
28-
from .c_parser import parse_struct
28+
import hashlib
29+
from .c_parser import (parse_struct, Tokens)
2930

3031
__all__ = [
3132
'CStructMeta',
@@ -73,6 +74,27 @@ def __init__(self, string=None, **kargs):
7374
for key, value in kargs.items():
7475
setattr(self, key, value)
7576

77+
@classmethod
78+
def parse(cls, __struct__, __name__=None, **kargs):
79+
"""
80+
Return a new class mapping a C struct/union definition.
81+
82+
:param __struct__: definition of the struct (or union) in C syntax
83+
:param __name__: (optional) name of the new class. If empty, a name based on the __struct__ hash is generated
84+
:param __byte_order__: (optional) byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER
85+
:param __is_union__: (optional) True for union, False for struct (default)
86+
:returns: cls subclass
87+
"""
88+
kargs = dict(kargs)
89+
if not isinstance(__struct__, Tokens):
90+
__struct__ = Tokens(__struct__)
91+
kargs['__struct__'] = __struct__
92+
if __name__ is None: # Anonymous struct
93+
__name__ = cls.__name__ + '_' + hashlib.sha1(str(__struct__).encode('utf-8')).hexdigest()
94+
kargs['__anonymous__'] = True
95+
kargs['__name__'] = __name__
96+
return type(__name__, (cls,), kargs)
97+
7698
def unpack(self, buffer):
7799
"""
78100
Unpack the string containing packed C structure data

cstruct/base.py

+24-13
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
import sys
2828

2929
__all__ = [
30+
'LITTLE_ENDIAN',
31+
'BIG_ENDIAN',
32+
'NATIVE_ORDER',
33+
'CHAR_ZERO',
3034
'STRUCTS',
3135
'DEFINES',
3236
'TYPEDEFS',
@@ -35,51 +39,58 @@
3539
'CHAR_ZERO'
3640
]
3741

42+
# little-endian, std. size & alignment
43+
LITTLE_ENDIAN = '<'
44+
# big-endian, std. size & alignment
45+
BIG_ENDIAN = '>'
46+
# native order, size & alignment
47+
NATIVE_ORDER = '@'
48+
3849
STRUCTS = {
3950
}
4051

4152
DEFINES = {
4253
}
4354

4455
TYPEDEFS = {
56+
'short int': 'short',
57+
'unsigned short int': 'unsigned short',
58+
'ushort': 'unsigned short',
59+
'long int': 'long',
60+
'unsigned long int': 'unsigned long',
61+
'int8_t': 'int8',
62+
'uint8_t': 'uint8',
63+
'int16_t': 'int16',
64+
'uint16_t': 'uint16',
65+
'int32_t': 'int32',
66+
'uint32_t': 'uint32',
67+
'int64_t': 'int64',
68+
'uint64_t': 'uint64',
4569
}
4670

4771
C_TYPE_TO_FORMAT = {
4872
'char': 's',
4973
'signed char': 'b',
5074
'unsigned char': 'B',
5175
'short': 'h',
52-
'short int': 'h',
53-
'ushort': 'H',
5476
'unsigned short': 'H',
55-
'unsigned short int': 'H',
5677
'int': 'i',
5778
'unsigned int': 'I',
5879
'long': 'l',
59-
'long int': 'l',
6080
'unsigned long': 'L',
61-
'unsigned long int': 'L',
6281
'long long': 'q',
6382
'unsigned long long': 'Q',
6483
'float': 'f',
6584
'double': 'd',
6685
'void *': 'P',
6786
'int8': 'b',
68-
'int8_t': 'b',
6987
'uint8': 'B',
70-
'uint8_t': 'B',
7188
'int16': 'h',
72-
'int16_t': 'h',
7389
'uint16': 'H',
74-
'uint16_t': 'H',
7590
'int32': 'i',
76-
'int32_t': 'i',
7791
'uint32': 'I',
78-
'uint32_t': 'I',
7992
'int64': 'q',
80-
'int64_t': 'q',
8193
'uint64': 'Q',
82-
'uint64_t': 'Q',
8394
}
8495

8596
EMPTY_BYTES_STRING = bytes()

cstruct/c_parser.py

+34-11
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import re
2828
import struct
2929
from .base import (
30+
NATIVE_ORDER,
3031
DEFINES,
3132
TYPEDEFS,
3233
STRUCTS,
@@ -39,6 +40,9 @@
3940
'parse_struct'
4041
]
4142

43+
def align(__byte_order__):
44+
return __byte_order__ is None or __byte_order__ == NATIVE_ORDER
45+
4246
class FieldType(object):
4347

4448
def __init__(self, vtype, vlen, vsize, fmt, offset, flexible_array):
@@ -121,17 +125,16 @@ def __str__(self):
121125
return str(self.tokens)
122126

123127

124-
def parse_type(tokens, __cls__):
125-
from . import factory
128+
def parse_type(tokens, __cls__, __byte_order__):
126129
if len(tokens) < 2:
127130
raise Exception("Parsing error")
128131
vtype = tokens.pop()
129132
# signed/unsigned/struct
130-
if vtype == 'unsigned' or vtype == 'signed' or vtype == 'struct' and len(tokens) > 1:
133+
if vtype in ['signed', 'unsigned', 'struct', 'union'] and len(tokens) > 1:
131134
vtype = vtype + " " + tokens.pop()
132135
next_token = tokens.pop()
133136
# short int, long int, or long long
134-
if next_token == 'int' or next_token == 'long':
137+
if next_token in ['int', 'long']:
135138
vtype = vtype + " " + next_token
136139
next_token = tokens.pop()
137140
# void *
@@ -170,22 +173,28 @@ def parse_type(tokens, __cls__):
170173
kind, vtype = vtype.split(' ', 1)
171174
if tokens.get() == '{': # Named nested struct
172175
tokens.pop()
173-
vtype = factory(tokens, __name__=vtype, __cls__=__cls__, __is_union__=__is_union__)
176+
vtype = __cls__.parse(tokens, __name__=vtype, __is_union__=__is_union__, __byte_order__=__byte_order__)
174177
elif vtype == '{': # Unnamed nested struct
175-
vtype = factory(tokens, __is_union__=__is_union__)
178+
vtype = __cls__.parse(tokens, __is_union__=__is_union__, __byte_order__=__byte_order__)
176179
else:
177180
try:
178181
vtype = STRUCTS[vtype]
179182
except KeyError:
180183
raise Exception("Unknow %s \"%s\"" % (kind, vtype))
181184
ttype = "c"
182185
fmt = str(vlen * vtype.size) + ttype
186+
# alignment/
187+
alignment = vtype.__alignment__
183188
else: # other types
184189
ttype = C_TYPE_TO_FORMAT.get(vtype, None)
185190
if ttype is None:
186191
raise Exception("Unknow type \"" + vtype + "\"")
187192
fmt = (str(vlen) if vlen > 1 or flexible_array else '') + ttype
188-
return vtype, vlen, fmt, flexible_array
193+
# alignment
194+
alignment = struct.calcsize((__byte_order__ + ttype) if __byte_order__ is not None else ttype)
195+
fmt = (__byte_order__ + fmt) if __byte_order__ is not None else fmt
196+
vsize = struct.calcsize(fmt)
197+
return vtype, vlen, vsize, fmt, flexible_array, alignment
189198

190199

191200
def parse_struct(__struct__, __cls__=None, __fields__=None, __is_union__=False, __byte_order__=None, **kargs):
@@ -195,6 +204,7 @@ def parse_struct(__struct__, __cls__=None, __fields__=None, __is_union__=False,
195204
fields_types = {}
196205
flexible_array = False
197206
offset = 0
207+
max_alignment = 0
198208
if isinstance(__struct__, Tokens):
199209
tokens = __struct__
200210
else:
@@ -206,11 +216,17 @@ def parse_struct(__struct__, __cls__=None, __fields__=None, __is_union__=False,
206216
# flexible array member must be the last member of such a struct
207217
if flexible_array:
208218
raise Exception("Flexible array member must be the last member of such a struct")
209-
vtype, vlen, fmt, flexible_array = parse_type(tokens, __cls__)
219+
vtype, vlen, vsize, fmt, flexible_array, alignment = parse_type(tokens, __cls__, __byte_order__)
210220
vname = tokens.pop()
211221
fields.append(vname)
212-
fmt = (__byte_order__ + fmt) if __byte_order__ is not None else fmt
213-
vsize = struct.calcsize(fmt)
222+
# calculate the max field size (for the alignment)
223+
max_alignment = max(max_alignment, alignment)
224+
# align stuct if byte order is native
225+
if not __is_union__ and align(__byte_order__) and vtype != 'char':
226+
modulo = offset % alignment
227+
if modulo: # not aligned to the field size
228+
delta = alignment - modulo
229+
offset = offset + delta
214230
fields_types[vname] = FieldType(vtype, vlen, vsize, fmt, offset, flexible_array)
215231
if not __is_union__: # C struct
216232
offset = offset + vsize
@@ -224,6 +240,12 @@ def parse_struct(__struct__, __cls__=None, __fields__=None, __is_union__=False,
224240
fmt = '%ds' % size
225241
else: # C struct
226242
fmt = "".join(fmt)
243+
# add padding to stuct if byte order is native
244+
if not __is_union__ and align(__byte_order__):
245+
modulo = offset % max_alignment
246+
if modulo: # not aligned to the max field size
247+
delta = max_alignment - modulo
248+
offset = offset + delta
227249
size = offset # (offset is calculated as size sum)
228250

229251
# Add the byte order as prefix
@@ -236,7 +258,8 @@ def parse_struct(__struct__, __cls__=None, __fields__=None, __is_union__=False,
236258
'__fields_types__': fields_types,
237259
'__size__': size,
238260
'__is_union__': __is_union__,
239-
'__byte_order__': __byte_order__
261+
'__byte_order__': __byte_order__,
262+
'__alignment__': max_alignment
240263
}
241264

242265
# Add the missing fields to the class
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)