2 # Copyright (c) 2008 Martin Decky
3 # Copyright (c) 2011 Martin Sucha
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
10 # - Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # - Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in the
14 # documentation and/or other materials provided with the distribution.
15 # - The name of the author may not be used to endorse or promote products
16 # derived from this software without specific prior written permission.
18 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 Convert descriptive structure definitions to structure object
37 # Handle long integer conversions nicely in both Python 2 and Python 3
38 integer_types
= (int, long) if sys
.version
< '3' else (int,)
40 # Ensure that 's' format for struct receives correct data type depending
41 # on Python version (needed due to different way to encode into bytes)
43 (lambda value
: value
if type(value
) is str else bytes(value
)) \
44 if sys
.version
< '3' else \
45 (lambda value
: bytes(value
, 'ascii') if type(value
) is str else value
)
48 'B': (integer_types
, 0x00, 0xff),
49 'H': (integer_types
, 0x0000, 0xffff),
50 'L': (integer_types
, 0x00000000, 0xffffffff),
51 'Q': (integer_types
, 0x0000000000000000, 0xffffffffffffffff),
52 'b': (integer_types
, -0x80, 0x7f),
53 'h': (integer_types
, -0x8000, 0x7fff),
54 'l': (integer_types
, -0x80000000, 0x7fffffff) ,
55 'q': (integer_types
, -0x8000000000000000, 0x7fffffffffffffff),
58 def check_range(varname
, fmt
, value
):
60 raise ValueError('Variable "%s" not set' % varname
)
63 vartype
, varmin
, varmax
= ranges
[fmt
]
64 if not isinstance(value
, vartype
):
65 raise ValueError('Variable "%s" is %s but should be %s' %
66 (varname
, str(type(value
)), str(vartype
)))
67 if value
< varmin
or value
> varmax
:
68 raise ValueError('Variable "%s" value %s out of range %s..%s' %
69 (varname
, repr(value
), repr(varmin
), repr(varmax
)))
73 return struct
.calcsize(self
._format
_)
77 for variable
, fmt
, length
in self
._args
_:
78 value
= self
.__dict
__[variable
]
79 if isinstance(value
, list):
80 if length
!= None and length
!= len(value
):
81 raise ValueError('Variable "%s" length %u does not match %u' %
82 (variable
, len(value
), length
))
83 for index
, item
in enumerate(value
):
84 check_range(variable
+ '[' + repr(index
) + ']', fmt
, item
)
88 value
= ensure_string(value
)
89 check_range(variable
, fmt
, value
)
91 return struct
.pack(self
._format
_, *args
)
93 def unpack(self
, data
):
94 values
= struct
.unpack(self
._format
_, data
)
96 for variable
, fmt
, length
in self
._args
_:
97 self
.__dict
__[variable
] = values
[i
]
100 def create(definition
):
101 "Create structure object"
103 tokens
= definition
.split(None)
105 # Initial byte order tag
107 "little:": lambda: "<",
109 "network:": lambda: "!"
117 for token
in tokens
[1:]:
127 if (variable
!= None):
128 subtokens
= token
.split("[")
131 if (len(subtokens
) > 1):
132 length
= int(subtokens
[1].split("]")[0])
133 format
+= "%d" % length
137 inst
.__dict
__[subtokens
[0]] = None
138 args
.append((subtokens
[0], variable
, length
))
143 if (token
[0:8] == "padding["):
144 size
= token
[8:].split("]")[0]
145 format
+= "%dx" % int(size
)
150 "uint8_t": lambda: "B",
151 "uint16_t": lambda: "H",
152 "uint32_t": lambda: "L",
153 "uint64_t": lambda: "Q",
155 "int8_t": lambda: "b",
156 "int16_t": lambda: "h",
157 "int32_t": lambda: "l",
158 "int64_t": lambda: "q"
161 inst
.__dict
__['_format_'] = format
162 inst
.__dict
__['_args_'] = args