aboutsummaryrefslogtreecommitdiff
blob: 26aefaf5b00fe942b8a87b7c3e118e10cb7ccc3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import sys, os
from struct import pack, unpack, calcsize

MAGIC = -0x3b83728b

CMSG_INIT        = 'i'
CMSG_START_GRAPH = '['
CMSG_ADD_NODE    = 'n'
CMSG_ADD_EDGE    = 'e'
CMSG_ADD_LINK    = 'l'
CMSG_FIXED_FONT  = 'f'
CMSG_STOP_GRAPH  = ']'
CMSG_MISSING_LINK= 'm'
CMSG_SAY         = 's'

MSG_OK           = 'O'
MSG_ERROR        = 'E'
MSG_RELOAD       = 'R'
MSG_FOLLOW_LINK  = 'L'

# ____________________________________________________________

long_min = -2147483648
long_max = 2147483647


def _encodeme(x):
    if type(x) is unicode:
        x = x.encode('utf-8')
    return x

def message(tp, *values):
    #print >> sys.stderr, tp, values
    typecodes = ['']
    values = map(_encodeme, values)
    for v in values:
        if type(v) is str:
            typecodes.append('%ds' % len(v))
        elif 0 <= v < 256:
            typecodes.append('B')
        elif long_min <= v <= long_max:
            typecodes.append('l')
        else:
            typecodes.append('q')
    typecodes = ''.join(typecodes)
    if len(typecodes) < 256:
        return pack(("!B%dsc" % len(typecodes)) + typecodes,
                    len(typecodes), typecodes, tp, *values)
    else:
        # too many values - encapsulate the message in another one
        return message('\x00', typecodes, pack("!c" + typecodes, tp, *values))

def decodemessage(data):
    if data:
        limit = ord(data[0]) + 1
        if len(data) >= limit:
            typecodes = "!c" + data[1:limit]
            end = limit + calcsize(typecodes)
            if len(data) >= end:
                msg = unpack(typecodes, data[limit:end])
                if msg[0] == '\x00':
                    msg = unpack("!c" + msg[1], msg[2])
                return msg, data[end:]
            #elif end > 1000000:
            #    raise OverflowError
    return None, data

# ____________________________________________________________

class RemoteError(Exception):
    pass


class IO(object):
    _buffer = ''

    def sendmsg(self, tp, *values):
        self.sendall(message(tp, *values))

    def recvmsg(self):
        while True:
            msg, self._buffer = decodemessage(self._buffer)
            if msg is not None:
                break
            self._buffer += self.recv()
        if msg[0] != MSG_ERROR:
            return msg
        raise RemoteError(*msg[1:])


class FileIO(IO):
    def __init__(self, f_in, f_out):
        self.f_in = f_in
        self.f_out = f_out

    def sendall(self, data):
        self.f_out.write(data)
        self.f_out.flush()

    def recv(self):
        fd = self.f_in.fileno()
        data = os.read(fd, 16384)
        if not data:
            raise EOFError
        return data

    def close_sending(self):
        self.f_out.close()

    def close(self):
        self.f_out.close()
        self.f_in.close()


class SocketIO(IO):
    def __init__(self, s):
        self.s = s

    def sendall(self, data):
        self.s.sendall(data)

    def recv(self):
        data = self.s.recv(16384)
        if not data:
            raise EOFError
        return data

    def close_sending(self):
        self.s.shutdown(1)    # SHUT_WR

    def close(self):
        self.s.close()