1
|
#!/usr/bin/env python
|
2
|
#
|
3
|
# Copyright (C) 2012 Adam Sutton <[email protected]>
|
4
|
#
|
5
|
# This program is free software: you can redistribute it and/or modify
|
6
|
# it under the terms of the GNU General Public License as published by
|
7
|
# the Free Software Foundation, version 3 of the License.
|
8
|
#
|
9
|
# This program is distributed in the hope that it will be useful,
|
10
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
# GNU General Public License for more details.
|
13
|
#
|
14
|
# You should have received a copy of the GNU General Public License
|
15
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
#
|
17
|
"""
|
18
|
Support for processing HTSMSG binary format
|
19
|
"""
|
20
|
|
21
|
# ###########################################################################
|
22
|
# Utilities
|
23
|
# ###########################################################################
|
24
|
|
25
|
def int2bin ( i ):
|
26
|
return chr(i >> 24 & 0xFF) + chr(i >> 16 & 0xFF)\
|
27
|
+ chr(i >> 16 & 0xFF) + chr(i & 0xFF)
|
28
|
|
29
|
def bin2int ( d ):
|
30
|
return (ord(d[0]) << 24) + (ord(d[1]) << 16)\
|
31
|
+ (ord(d[2]) << 8) + ord(d[3])
|
32
|
|
33
|
# ###########################################################################
|
34
|
# HTSMSG Binary handler
|
35
|
#
|
36
|
# Note: this will work with the python native types:
|
37
|
# dict => HMF_MAP
|
38
|
# list => HMF_LIST
|
39
|
# str => HMF_STR
|
40
|
# int => HMF_S64
|
41
|
# hmf_bin => HMF_BIN
|
42
|
#
|
43
|
# Note: BIN/STR are both equated to str in python
|
44
|
# ###########################################################################
|
45
|
|
46
|
# HTSMSG types
|
47
|
HMF_MAP = 1
|
48
|
HMF_S64 = 2
|
49
|
HMF_STR = 3
|
50
|
HMF_BIN = 4
|
51
|
HMF_LIST = 5
|
52
|
|
53
|
# Light wrapper for binary type
|
54
|
class hmf_bin(str):
|
55
|
pass
|
56
|
|
57
|
# Convert python to HTSMSG type
|
58
|
def hmf_type ( f ):
|
59
|
if type(f) == list:
|
60
|
return HMF_MAP
|
61
|
elif type(f) == dict:
|
62
|
return HMF_LIST
|
63
|
elif type(f) == str:
|
64
|
return HMF_STR
|
65
|
elif type(f) == int:
|
66
|
return HMF_S64
|
67
|
elif type(f) == hmf_bin:
|
68
|
return HMF_BIN
|
69
|
else:
|
70
|
raise Exception('invalid type')
|
71
|
|
72
|
# Size for field
|
73
|
def _binary_count ( f ):
|
74
|
ret = 0
|
75
|
if type(f) in [ str, hmf_bin ]:
|
76
|
ret = ret + len(f)
|
77
|
elif type(f) == int:
|
78
|
while (f):
|
79
|
ret = ret + 1
|
80
|
f = f >> 8
|
81
|
elif type(f) in [ list, map ]:
|
82
|
ret = ret + binary_count(f)
|
83
|
else:
|
84
|
raise Exception('invalid data type')
|
85
|
return ret
|
86
|
|
87
|
# Recursively determine size of message
|
88
|
def binary_count ( msg ):
|
89
|
ret = 0
|
90
|
lst = type(msg) == list
|
91
|
for f in msg:
|
92
|
ret = ret + 6
|
93
|
if not lst:
|
94
|
ret = ret + len(f)
|
95
|
f = msg[f]
|
96
|
ret = ret + _binary_count(f)
|
97
|
return ret
|
98
|
|
99
|
# Write out field in binary form
|
100
|
def binary_write ( msg ):
|
101
|
ret = ''
|
102
|
lst = type(msg) == list
|
103
|
for f in msg:
|
104
|
na = ''
|
105
|
if not lst:
|
106
|
na = f
|
107
|
f = msg[f]
|
108
|
ret = ret + chr(hmf_type(f))
|
109
|
ret = ret + chr(len(na) & 0xFF)
|
110
|
l = _binary_count(f)
|
111
|
ret = ret + int2bin(l)
|
112
|
ret = ret + na
|
113
|
|
114
|
if type(f) in [ list, dict ]:
|
115
|
ret = ret + binary_write(f)
|
116
|
elif type(f) in [ str, hmf_bin ]:
|
117
|
ret = ret + f
|
118
|
elif type(f) == int:
|
119
|
while f:
|
120
|
ret = ret + chr(f & 0xFF)
|
121
|
f = f >> 8
|
122
|
else:
|
123
|
raise Exception('invalid type')
|
124
|
return ret
|
125
|
|
126
|
# Serialize a htsmsg
|
127
|
def serialize ( msg ):
|
128
|
cnt = binary_count(msg)
|
129
|
return int2bin(cnt) + binary_write(msg)
|
130
|
|
131
|
def new_map():
|
132
|
return {}
|
133
|
|
134
|
def new_list():
|
135
|
return []
|
136
|
|
137
|
def store_map_item(name, item, store):
|
138
|
store[name] = item
|
139
|
|
140
|
def store_list_item(name, item, store):
|
141
|
store.append(item)
|
142
|
|
143
|
# Deserialize an htsmsg
|
144
|
def deserialize0 ( data, create_storage, store_item ):
|
145
|
msg = create_storage()
|
146
|
while len(data) > 5:
|
147
|
typ = ord(data[0])
|
148
|
nlen = ord(data[1])
|
149
|
dlen = bin2int(data[2:6])
|
150
|
data = data[6:]
|
151
|
|
152
|
if len < nlen + dlen: raise Exception('not enough data')
|
153
|
|
154
|
name = data[:nlen]
|
155
|
data = data[nlen:]
|
156
|
if typ == HMF_STR:
|
157
|
item = data[:dlen]
|
158
|
elif typ == HMF_BIN:
|
159
|
item = hmf_bin(data[:dlen])
|
160
|
elif typ == HMF_S64:
|
161
|
item = 0
|
162
|
i = dlen - 1
|
163
|
while i >= 0:
|
164
|
item = (item << 8) | ord(data[i])
|
165
|
i = i - 1
|
166
|
elif typ == HMF_MAP:
|
167
|
item = deserialize0(data[:dlen], new_map, store_map_item)
|
168
|
elif typ == HMF_LIST:
|
169
|
item = deserialize0(data[:dlen], new_list, store_list_item)
|
170
|
else:
|
171
|
raise Exception('invalid data type %d' % typ)
|
172
|
store_item(name, item, msg)
|
173
|
data = data[dlen:]
|
174
|
return msg
|
175
|
|
176
|
# Deserialize a series of message
|
177
|
def deserialize ( fp, rec = False ):
|
178
|
class _deserialize:
|
179
|
def __init__ ( self, fp, rec = False ):
|
180
|
self._fp = fp
|
181
|
self._rec = rec
|
182
|
def __iter__ ( self ):
|
183
|
print '__iter__()'
|
184
|
return self
|
185
|
def _read ( self, num ):
|
186
|
r = None
|
187
|
if hasattr(self._fp, 'read'):
|
188
|
r = self._fp.read(num)
|
189
|
elif hasattr(self._fp, 'recv'):
|
190
|
r = self._fp.recv(num)
|
191
|
elif type(self._fp) is list:
|
192
|
r = self._fp[:num]
|
193
|
self._fp = self._fp[num:]
|
194
|
else:
|
195
|
raise Exception('invalid data type')
|
196
|
return r
|
197
|
def next ( self ):
|
198
|
if not self._fp: raise StopIteration()
|
199
|
tmp = self._read(4)
|
200
|
if len(tmp) != 4:
|
201
|
self._fp = None
|
202
|
raise StopIteration()
|
203
|
num = bin2int(tmp)
|
204
|
data = ''
|
205
|
while len(data) < num:
|
206
|
tmp = self._read(num - len(data))
|
207
|
if not tmp:
|
208
|
raise Exception('failed to read from fp')
|
209
|
data = data + tmp
|
210
|
if not self._rec: self._fp = None
|
211
|
return deserialize0(data, new_map, store_map_item)
|
212
|
ret = _deserialize(fp, rec)
|
213
|
if not rec:
|
214
|
ret = ret.next()
|
215
|
return ret
|
216
|
|
217
|
# ############################################################################
|
218
|
# Editor Configuration
|
219
|
#
|
220
|
# vim:sts=2:ts=2:sw=2:et
|
221
|
# ############################################################################
|