Project

General

Profile

Bug #1329 ยป htsmsg.py

Nick Tuckett, 2012-10-14 17:40

 
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
# ############################################################################
    (1-1/1)