Completed
Pull Request — master (#192)
by Marek
01:39
created

ige.IMarshal.IPacket.__repr__()   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
#
2
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
3
#
4
#  This file is part of Outer Space.
5
#
6
#  Outer Space is free software; you can redistribute it and/or modify
7
#  it under the terms of the GNU General Public License as published by
8
#  the Free Software Foundation; either version 2 of the License, or
9
#  (at your option) any later version.
10
#
11
#  Outer Space is distributed in the hope that it will be useful,
12
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
#  GNU General Public License for more details.
15
#
16
#  You should have received a copy of the GNU General Public License
17
#  along with Outer Space; if not, write to the Free Software
18
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
#
20
import types
21
22
from IDataHolder import IDataHolder
23
import zlib, string
24
25
__all__ = ('EncodeException', 'DecodeException', 'IPacket', 'IMarshal')
26
27
class EncodeException(Exception):
28
    pass
29
30
class DecodeException(Exception):
31
    pass
32
33
class IPacket:
34
35
    def __init__(self):
36
        self.sid = None
37
        self.method = None
38
        self.params = None
39
        self.result = None
40
        self.messages = None
41
        self.exception = None
42
        self.clientAddr = None
43
44
    def __repr__(self):
45
        result = '<%s.%s %d ' % (self.__class__.__module__, self.__class__.__name__, id(self))
46
        for key, value in self.__dict__.items():
47
            result += '%s=%s, ' % (key, repr(value))
48
        result += '>'
49
        return result
50
51
class IMarshal:
52
53
    def __init__(self):
54
        pass
55
56
    def encode(self, data, version = "V20"):
57
        if version == "V11":
58
            return u'V11%s' % string.join(self.__encodeV11(data), '')
59
            # Profiling code TODO remove profiling code
60
            data = u'V11%s' % self.__encodeV11(data)
61
            stats.encBytes += len(data)
62
            # TODO remove zlib profiling
63
            stats.zipBytes += len(zlib.compress(data))
64
            # End of profiling code
65
            return data
66
        elif version == "V20":
67
            return "V20%s" % zlib.compress(pickle.dumps(data, 1))
68
        else:
69
            raise EncodeException("Cannot handle version %s." % version)
70
71
    def __encodeV11(self, data, isKey = 0):
72
        t = type(data)
73
        if t == types.NoneType:
74
            return [u'|N']
75
        elif t == types.IntType:
76
            return [u'|I%d' % (data,)]
77
        elif t == types.LongType:
78
            return [u'|G%d' % (data,)]
79
        elif t == types.FloatType:
80
            return [u'|F%.2f' % (data,)]
81
        elif t == types.StringType:
82
            # Profiling code TODO comment this code
83
            #if isKey:
84
            #    global stats
85
            #    stats.data[data] = stats.data.get(data, 0) + 1
86
            #    stats.total += 1
87
            #    stats.totalBytes += len(data)
88
            #    if stats.total % 100 == 0:
89
            #        saveStats()
90
            #    if compress.has_key(data):
91
            #        stats.hits += 1
92
            #        stats.savedBytes += len(data) - len(compress[data])
93
            # End of profiling code
94
            if compress.has_key(data):
95
                return [u'|C%s' % (compress[data])]
96
            return [u'|S%s' % (data.replace('\\','\\\\').replace('|', '\\/').replace('\n', '\\n').replace('\r', '\\r'),)]
97
        elif t == types.UnicodeType:
98
            return [u'|U%s' % (data.replace('\\','\\\\').replace('|', '\\/').replace('\n', '\\n').replace('\r', '\\r'),)]
99
        elif t == types.ListType:
100
            result = [u'|L']
101
            for item in data:
102
                result.extend(self.__encodeV11(item))
103
            result.append(u'|E')
104
            return result
105
        elif t == types.TupleType:
106
            result = [u'|T']
107
            for item in data:
108
                result.extend(self.__encodeV11(item))
109
            result.append(u'|E')
110
            return result
111
        elif t == types.DictType:
112
            result = [u'|D']
113
            for item in data.keys():
114
                result.extend(self.__encodeV11(item, 1))
115
                result.extend(self.__encodeV11(data[item]))
116
            result.append(u'|E')
117
            return result
118
        elif t == types.InstanceType:
119
            result = [u'|O']
120
            result.extend(self.__encodeV11('%s.%s' % (data.__class__.__module__, data.__class__.__name__), 1))
121
            for item in data.__dict__.keys():
122
                result.extend(self.__encodeV11(item, 1))
123
                result.extend(self.__encodeV11(data.__dict__[item]))
124
            result.append(u'|E')
125
            return result
126
        else:
127
            raise EncodeException('Cannot handle ' + repr(t))
128
129
    def decode(self, str):
130
        prefix = str[:3]
131
        if prefix == u'V11':
132
            self.index = 0
133
            list = str.split('|')
134
            self.list = list[1:]
135
            data = self.__decodeV11()
136
        elif prefix == u'V20':
137
            data = pickle.loads(zlib.decompress(str[3:]))
138
        else:
139
            raise DecodeException('Cannot handle version %s [message: %s]' % (prefix, str))
140
        return data
141
142
    def __decodeV11(self):
143
        item = self.list[self.index]
144
        self.index += 1
145
        #@print self.index,
146
        t = item[0]
147
        if t == u'N':
148
            #@print 'None'
149
            return None
150
        if t == u'I':
151
            return int(item[1:])
152
        if t == u'G':
153
            return long(item[1:])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable long does not seem to be defined.
Loading history...
154
        if t == u'F':
155
            return float(item[1:])
156
        elif t == u'S':
157
            #@print 'String', item[1:]
158
            str = item[1:]
159
            i = str.find('\\')
160
            index = 0
161
            result = ''
162 View Code Duplication
            while i > -1:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
163
                #@print str, i,
164
                result += str[index:i]
165
                index = i + 2
166
                c = str[i + 1]
167
                if c == 'n': result += '\n'
168
                elif c == 'r': result += '\r'
169
                elif c == '/': result += '|'
170
                else: result += c
171
                #@print repr(result)
172
                i = str.find('\\', i + 2)
173
            result += str[index:]
174
            return result.encode('ascii')
175
        elif t == u'U':
176
            #@print 'String', item[1:]
177
            str = item[1:]
178
            i = str.find('\\')
179
            index = 0
180
            result = ''
181 View Code Duplication
            while i > -1:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
182
                #@print str, i,
183
                result += str[index:i]
184
                index = i + 2
185
                c = str[i + 1]
186
                if c == 'n': result += '\n'
187
                elif c == 'r': result += '\r'
188
                elif c == '/': result += '|'
189
                else: result += c
190
                #@print repr(result)
191
                i = str.find('\\', i + 2)
192
            result += str[index:]
193
            return result
194
        elif t == u'C':
195
            return decompress[item[1:]]
196
        elif t == u'T':
197
            #@print 'Tuple'
198
            result = []
199
            while self.list[self.index][0] != 'E':
200
                result.append(self.__decodeV11())
201
            self.index += 1
202
            #@print ':=', result
203
            return tuple(result)
204
        elif t == u'L':
205
            #@print 'Tuple'
206
            result = []
207
            while self.list[self.index][0] != 'E':
208
                result.append(self.__decodeV11())
209
            self.index += 1
210
            #@print ':=', result
211
            return result
212
        elif t == u'D':
213
            #@print 'Dict'
214
            result = {}
215
            while self.list[self.index][0] != 'E':
216
                key = self.__decodeV11()
217
                value = self.__decodeV11()
218
                result[key] = value
219
            self.index += 1
220
            #@print ':=', result
221
            return result
222
        elif t == u'O':
223
            #@print 'Dict'
224
            str = self.__decodeV11()
225
            if str == 'ige.IMarshal.IPacket':
226
                result = IPacket()
227
            elif str == 'ige.IDataHolder.IDataHolder':
228
                result = IDataHolder()
229
            else:
230
                DecodeException('Unsupported class %s' % str)
231
            while self.list[self.index][0] != 'E':
232
                key = self.__decodeV11()
233
                value = self.__decodeV11()
234
                setattr(result, key ,value)
0 ignored issues
show
introduced by
The variable result does not seem to be defined for all execution paths.
Loading history...
235
            self.index += 1
236
            #@print ':=', result
237
            return result
238
        else:
239
            raise DecodeException('Cannot handle type <%s>' % t)
240
241
# (de)compress dictionary
242
#try:
243
#    from ICompressScheme import compress
244
#except ImportError:
245
compress = {}
246
247
decompress = {}
248
249
for key, value in compress.items():
250
    decompress[str(value)] = key
251
252
# statistics
253
import cPickle as pickle
254
255
class Stats:
256
    def __init__(self):
257
        self.data = {}
258
        self.total = 0
259
        self.hits = 0
260
        self.totalBytes = 0
261
        self.savedBytes = 0
262
        self.encBytes = 0
263
        self.zipBytes = 0
264
265
# load stats TODO remove profiling code
266
# TODO change dir according to config file
267
try:
268
    fh = open('var/marshal.stats.data', 'rb')
269
    stats = pickle.load(fh)
270
    fh.close()
271
except IOError, e:
272
    stats = Stats()
273
except EOFError, e:
274
    stats = Stats()
275
276
def saveStats(directory):
277
    print 'Saving IMarshal statistics'
278
    # stats
279
    fh = open(os.path.join(directory, 'marshal.stats.data'), 'wb')
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable os does not seem to be defined.
Loading history...
280
    pickle.dump(stats, fh, 1)
281
    fh.close()
282
    # various data
283
    keys = []
284
    for key in stats.data.keys():
285
        keys.append((len(key) * stats.data[key],key))
286
    keys.sort()
287
    keys.reverse()
288
    fstats = open(os.path.join(directory, 'marshal.stats'), 'w')
289
    fscheme = open(os.path.join(directory, 'marshal.cscheme'), 'w')
290
    fpysrc = open(os.path.join(directory, 'marshal.cscheme.py'), 'w')
291
    print >> fpysrc, 'compress = {'
292
    print >> fstats, '# Summary'
293
    print >> fstats, '# Total strings:', stats.total
294
    print >> fstats, '# Compressed strings:', stats.hits
295
    print >> fstats, '# Uncompressed strings:', stats.total - stats.hits
296
    print >> fstats, '# Ratio:', stats.hits / stats.total * 100L, '%'
297
    print >> fstats, '# Uncompressed size:', stats.totalBytes
298
    print >> fstats, '# Compressed size:', stats.totalBytes - stats.savedBytes
299
    print >> fstats, '# Saved bytes:', stats.savedBytes
300
    print >> fstats, '# Ratio:', stats.savedBytes / stats.totalBytes * 100L, '%'
301
    print >> fstats, '# Encoded pckt bytes total:', stats.encBytes
302
    print >> fstats, '# Encoded pckt bytes total (no compression, est.):', stats.encBytes + stats.savedBytes
303
    print >> fstats, '# Ratio:', stats.encBytes / (stats.encBytes + stats.savedBytes) * 100L, '%'
304
    print >> fstats, '# Encoded pckt bytes total (zipped):', stats.zipBytes
305
    print >> fstats, '# Ratio (to compressed):', stats.zipBytes / stats.encBytes * 100L, '%'
306
    print >> fstats, '# Ratio (to uncompressed):', stats.zipBytes / (stats.encBytes + stats.savedBytes)* 100L , '%'
307
    print >> fstats, '# total bytes,number of items,string'
308
    index = 0
309
    for key in keys:
310
        count, name = key
311
        print >> fstats, '%d,%d,%s' % (count, stats.data[name], name)
312
        code = makeCode(index)
313
        # include in scheme when there is save in bytes
314
        if len(code) < len(name):
315
            print >> fscheme, code, name
316
            print >> fpysrc, "    '%s' : '%s'," % (name, code)
317
        index += 1
318
    print >>fpysrc, '}'
319
    fstats.close()
320
    fscheme.close()
321
    fpysrc.close()
322
323
codeChars = '0123456789abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
324
325
def makeCode(index):
326
    if index < len(codeChars):
327
        return codeChars[index]
328
    elif index / len(codeChars) - 1 < len(codeChars):
329
        return '%s%s' % (
330
            codeChars[index / len(codeChars) - 1],
331
            codeChars[index % len(codeChars)]
332
        )
333
    else:
334
        return None
335
336
# testing only
337
if __name__ == '__main__':
338
339
    packet = IPacket()
340
    packet.sid = '0123456789'
341
    packet.method = 'test'
342
    packet.params = { 'name':u'Corvus', 'componentOf':1001, 'rules':[1,2], 'isA': (1,2) }
343
    marshal = IMarshal()
344
345
    str = marshal.encode(packet)
346
347
    print repr(str)
348
    print len(str)
349
    packet = marshal.decode(str)
350
    print packet.params
351
352
    import pprint
353
    pprint.pprint(packet.params)
354
355