diff options
author | Guido van Rossum <guido@python.org> | 1992-12-14 23:25:04 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 1992-12-14 23:25:04 (GMT) |
commit | e3cafbe7b85c350d4dbeb2af53fd8fc1ae66689b (patch) | |
tree | 3a6030b09829c527c8bb0fdcd6b36f6e9e332fe1 /Demo/rpc | |
parent | 5f59d6018e1b49c3bc581a0f69aa2bb1c88ae52b (diff) | |
download | cpython-e3cafbe7b85c350d4dbeb2af53fd8fc1ae66689b.zip cpython-e3cafbe7b85c350d4dbeb2af53fd8fc1ae66689b.tar.gz cpython-e3cafbe7b85c350d4dbeb2af53fd8fc1ae66689b.tar.bz2 |
Initial revision
Diffstat (limited to 'Demo/rpc')
-rw-r--r-- | Demo/rpc/T.py | 19 | ||||
-rw-r--r-- | Demo/rpc/mountclient.py | 166 | ||||
-rw-r--r-- | Demo/rpc/nfsclient.py | 207 | ||||
-rw-r--r-- | Demo/rpc/rpc.py | 376 | ||||
-rw-r--r-- | Demo/rpc/xdr.py | 141 |
5 files changed, 909 insertions, 0 deletions
diff --git a/Demo/rpc/T.py b/Demo/rpc/T.py new file mode 100644 index 0000000..24d5e8a --- /dev/null +++ b/Demo/rpc/T.py @@ -0,0 +1,19 @@ +import sys, os, time + +def TSTART(): + global t0, t1 + u, s, cu, cs = os.times() + t0 = u+cu, s+cs, time.millitimer() + +def TSTOP(*label): + global t0, t1 + u, s, cu, cs = os.times() + t1 = u+cu, s+cs, time.millitimer() + tt = [] + for i in range(3): + tt.append(t1[i] - t0[i]) + [u, s, r] = tt + msg = '' + for x in label: msg = msg + (x + ' ') + msg = msg + `u` + ' user, ' + `s` + ' sys, ' + `r*0.001` + ' real\n' + sys.stderr.write(msg) diff --git a/Demo/rpc/mountclient.py b/Demo/rpc/mountclient.py new file mode 100644 index 0000000..021de8c --- /dev/null +++ b/Demo/rpc/mountclient.py @@ -0,0 +1,166 @@ +# Mount RPC client -- RFC 1094 (NFS), Appendix A + +# This module demonstrates how to write your own RPC client in Python. +# Since there is no RPC compiler for Python (yet), you must first +# create classes derived from Packer and Unpacker to handle the data +# types for the server you want to interface to. You then write the +# client class. If you want to support both the TCP and the UDP +# version of a protocol, use multiple inheritance as shown below. + + +from rpc import Packer, Unpacker, TCPClient, UDPClient + +MOUNTPROG = 100005 +MOUNTVERS = 1 + +FHSIZE = 32 + + +# Packer derived class for Mount protocol clients. +# The only thing we need to pack beyond basic types is an 'fhandle' + +class MountPacker(Packer): + + def pack_fhandle(self, fhandle): + self.pack_fopaque(FHSIZE, fhandle) + + +# Unpacker derived class for Mount protocol clients. +# The important types we need to unpack are fhandle, fhstatus, +# mountlist and exportlist; mountstruct, exportstruct and groups are +# used to unpack components of mountlist and exportlist and the +# corresponding functions are passed as function argument to the +# generic unpack_list function. + +class MountUnpacker(Unpacker): + + def unpack_fhandle(self): + return self.unpack_fopaque(FHSIZE) + + def unpack_fhstatus(self): + status = self.unpack_uint() + if status == 0: + fh = self.unpack_fhandle() + else: + fh = None + return status, fh + + def unpack_mountlist(self): + return self.unpack_list(self.unpack_mountstruct) + + def unpack_mountstruct(self): + hostname = self.unpack_string() + directory = self.unpack_string() + return (hostname, directory) + + def unpack_exportlist(self): + return self.unpack_list(self.unpack_exportstruct) + + def unpack_exportstruct(self): + filesys = self.unpack_string() + groups = self.unpack_groups() + return (filesys, groups) + + def unpack_groups(self): + return self.unpack_list(self.unpack_string) + + +# These are the procedures specific to the Mount client class. +# Think of this as a derived class of either TCPClient or UDPClient. + +class PartialMountClient: + + # This method is called by Client.init to initialize + # self.packer and self.unpacker + def addpackers(self): + self.packer = MountPacker().init() + self.unpacker = MountUnpacker().init('') + + # The methods Mnt, Dump etc. each implement one Remote + # Procedure Call. Their general structure is + # self.start_call(<procedure-number>) + # <pack arguments using self.packer> + # self.do_call() # This does the actual message exchange + # <unpack reply using self.unpacker> + # self.end_call() + # return <reply> + # If the call fails, an exception is raised by do_call(). + # If the reply does not match what you unpack, an exception is + # raised either during unpacking (if you overrun the buffer) + # or by end_call() (if you leave values in the buffer). + # Calling packer methods with invalid arguments (e.g. if + # invalid arguments were passed from outside) will also result + # in exceptions during packing. + + def Mnt(self, directory): + self.start_call(1) + self.packer.pack_string(directory) + self.do_call() + stat = self.unpacker.unpack_fhstatus() + self.end_call() + return stat + + def Dump(self): + self.start_call(2) + self.do_call() + list = self.unpacker.unpack_mountlist() + self.end_call() + return list + + def Umnt(self, directory): + self.start_call(3) + self.packer.pack_string(directory) + self.do_call() + self.end_call() + + def Umntall(self): + self.start_call(4) + self.do_call() + self.end_call() + + def Export(self): + self.start_call(5) + self.do_call() + list = self.unpacker.unpack_exportlist() + self.end_call() + return list + + +# We turn the partial Mount client into a full one for either protocol +# by use of multiple inheritance. (In general, when class C has base +# classes B1...Bn, if x is an instance of class C, methods of x are +# searched first in C, then in B1, then in B2, ..., finally in Bn.) + +class TCPMountClient(PartialMountClient, TCPClient): + + def init(self, host): + return TCPClient.init(self, host, MOUNTPROG, MOUNTVERS) + + +class UDPMountClient(PartialMountClient, UDPClient): + + def init(self, host): + return UDPClient.init(self, host, MOUNTPROG, MOUNTVERS) + + +# A little test program for the Mount client. This takes a host as +# command line argument (default the local machine), prints its export +# list, and attempt to mount and unmount each exported files system. + +def test(): + import sys + if sys.argv[1:]: host = sys.argv[1] + else: host = '' + mcl = UDPMountClient().init(host) + list = mcl.Export() + for item in list: + print item + try: + mcl.Mnt(item[0]) + except: + print 'Sorry' + continue + mcl.Umnt(item[0]) + return + +#test() diff --git a/Demo/rpc/nfsclient.py b/Demo/rpc/nfsclient.py new file mode 100644 index 0000000..2260139 --- /dev/null +++ b/Demo/rpc/nfsclient.py @@ -0,0 +1,207 @@ +# NFS RPC client -- RFC 1094 + +# (See mountclient.py for some hints on how to write RPC clients in +# Python in general) + +from rpc import UDPClient, TCPClient +from mountclient import FHSIZE, MountPacker, MountUnpacker + +NFS_PROGRAM = 100003 +NFS_VERSION = 2 + +# enum stat +NFS_OK = 0 +# (...many error values...) + +# enum ftype +NFNON = 0 +NFREG = 1 +NFDIR = 2 +NFBLK = 3 +NFCHR = 4 +NFLNK = 5 + + +class NFSPacker(MountPacker): + + def pack_sattrargs(self, sa): + file, attributes = sa + self.pack_fhandle(file) + self.pack_sattr(attributes) + + def pack_sattr(self, sa): + mode, uid, gid, size, atime, mtime = sa + self.pack_uint(mode) + self.pack_uint(uid) + self.pack_uint(gid) + self.pack_uint(size) + self.pack_timeval(atime) + self.pack_timeval(mtime) + + def pack_diropargs(self, da): + dir, name = da + self.pack_fhandle(dir) + self.pack_string(name) + + def pack_readdirargs(self, ra): + dir, cookie, count = ra + self.pack_fhandle(dir) + self.pack_uint(cookie) + self.pack_uint(count) + + def pack_timeval(self, tv): + secs, usecs = tv + self.pack_uint(secs) + self.pack_uint(usecs) + + +class NFSUnpacker(MountUnpacker): + + def unpack_readdirres(self): + status = self.unpack_enum() + if status == NFS_OK: + entries = self.unpack_list(self.unpack_entry) + eof = self.unpack_bool() + rest = (entries, eof) + else: + rest = None + return (status, rest) + + def unpack_entry(self): + fileid = self.unpack_uint() + name = self.unpack_string() + cookie = self.unpack_uint() + return (fileid, name, cookie) + + def unpack_diropres(self): + status = self.unpack_enum() + if status == NFS_OK: + fh = self.unpack_fhandle() + fa = self.unpack_fattr() + rest = (fh, fa) + else: + rest = None + return (status, rest) + + def unpack_attrstat(self): + status = self.unpack_enum() + if status == NFS_OK: + attributes = self.unpack_fattr() + else: + attributes = None + return status, attributes + + def unpack_fattr(self): + type = self.unpack_enum() + mode = self.unpack_uint() + nlink = self.unpack_uint() + uid = self.unpack_uint() + gid = self.unpack_uint() + size = self.unpack_uint() + blocksize = self.unpack_uint() + rdev = self.unpack_uint() + blocks = self.unpack_uint() + fsid = self.unpack_uint() + fileid = self.unpack_uint() + atime = self.unpack_timeval() + mtime = self.unpack_timeval() + ctime = self.unpack_timeval() + return (type, mode, nlink, uid, gid, size, blocksize, \ + rdev, blocks, fsid, fileid, atime, mtime, ctime) + + def unpack_timeval(self): + secs = self.unpack_uint() + usecs = self.unpack_uint() + return (secs, usecs) + + +class NFSClient(UDPClient): + + def init(self, host): + return UDPClient.init(self, host, NFS_PROGRAM, NFS_VERSION) + + def addpackers(self): + self.packer = NFSPacker().init() + self.unpacker = NFSUnpacker().init('') + + def Getattr(self, fh): + self.start_call(1) + self.packer.pack_fhandle(fh) + self.do_call() + as = self.unpacker.unpack_attrstat() + self.end_call() + return as + + def Setattr(self, sa): + self.start_call(2) + self.packer.pack_sattrargs(sa) + self.do_call() + as = self.unpacker.unpack_attrstat() + self.end_call() + return as + + # Root() is obsolete + + def Lookup(self, da): + self.start_call(4) + self.packer.pack_diropargs(da) + self.do_call() + dr = self.unpacker.unpack_diropres() + self.end_call() + return dr + + # ... + + def Readdir(self, ra): + self.start_call(16) + self.packer.pack_readdirargs(ra) + self.do_call() + rr = self.unpacker.unpack_readdirres() + self.end_call() + return rr + + # Shorthand to get the entire contents of a directory + def Listdir(self, dir): + list = [] + ra = (dir, 0, 16) + while 1: + (status, rest) = self.Readdir(ra) + if status <> NFS_OK: + break + entries, eof = rest + last_cookie = None + for fileid, name, cookie in entries: + print (fileid, name, cookie) # XXX + list.append(fileid, name) + last_cookie = cookie + if eof or not last_cookie: + break + ra = (ra[0], last_cookie, ra[2]) + return list + + +def test(): + import sys + if sys.argv[1:]: host = sys.argv[1] + else: host = '' + if sys.argv[2:]: filesys = sys.argv[2] + else: filesys = None + from mountclient import UDPMountClient, TCPMountClient + mcl = TCPMountClient().init(host) + if filesys == None: + list = mcl.Export() + for item in list: + print item + return + sf = mcl.Mnt(filesys) + print sf + fh = sf[1] + if fh: + ncl = NFSClient().init(host) + as = ncl.Getattr(fh) + print as + list = ncl.Listdir(fh) + for item in list: print item + mcl.Unmnt(filesys) + +test() diff --git a/Demo/rpc/rpc.py b/Demo/rpc/rpc.py new file mode 100644 index 0000000..ba3bd54 --- /dev/null +++ b/Demo/rpc/rpc.py @@ -0,0 +1,376 @@ +# Implement (a subset of) Sun RPC, version 2 -- RFC1057. + +import xdr +import socket +import os + +RPCVERSION = 2 + +CALL = 0 +REPLY = 1 + +AUTH_NULL = 0 +AUTH_UNIX = 1 +AUTH_SHORT = 2 +AUTH_DES = 3 + +MSG_ACCEPTED = 0 +MSG_DENIED = 1 + +SUCCESS = 0 # RPC executed successfully +PROG_UNAVAIL = 1 # remote hasn't exported program +PROG_MISMATCH = 2 # remote can't support version # +PROC_UNAVAIL = 3 # program can't support procedure +GARBAGE_ARGS = 4 # procedure can't decode params + +RPC_MISMATCH = 0 # RPC version number != 2 +AUTH_ERROR = 1 # remote can't authenticate caller + +AUTH_BADCRED = 1 # bad credentials (seal broken) +AUTH_REJECTEDCRED = 2 # client must begin new session +AUTH_BADVERF = 3 # bad verifier (seal broken) +AUTH_REJECTEDVERF = 4 # verifier expired or replayed +AUTH_TOOWEAK = 5 # rejected for security reasons + + +class Packer(xdr.Packer): + + def pack_auth(self, auth): + flavor, stuff = auth + self.pack_enum(flavor) + self.pack_opaque(stuff) + + def pack_auth_unix(self, stamp, machinename, uid, gid, gids): + self.pack_uint(stamp) + self.pack_string(machinename) + self.pack_uint(uid) + self.pack_uint(gid) + self.pack_uint(len(gids)) + for i in gids: + self.pack_uint(i) + + def pack_callheader(self, xid, prog, vers, proc, cred, verf): + self.pack_uint(xid) + self.pack_enum(CALL) + self.pack_uint(RPCVERSION) + self.pack_uint(prog) + self.pack_uint(vers) + self.pack_uint(proc) + self.pack_auth(cred) + self.pack_auth(verf) + # Caller must add procedure-specific part of call + + def pack_replyheader(self, xid, verf): + self.pack_uint(xid) + self.pack_enum(REPLY) + self.pack_uint(MSG_ACCEPTED) + self.pack_auth(verf) + self.pack_enum(SUCCESS) + # Caller must add procedure-specific part of reply + + +class Unpacker(xdr.Unpacker): + + def unpack_auth(self): + flavor = self.unpack_enum() + stuff = self.unpack_opaque() + return (flavor, stuff) + + def unpack_replyheader(self): + xid = self.unpack_uint() + mtype = self.unpack_enum() + if mtype <> REPLY: + raise RuntimeError, 'no REPLY but ' + str(mtype) + stat = self.unpack_enum() + if stat <> MSG_ACCEPTED: + if stat == MSG_DENIED: + stat = self.unpack_enum() + if stat == RPC_MISMATCH: + low = self.unpack_uint() + high = self.unpack_uint() + raise 'RPC_MISMATCH', (low, high) + if stat == AUTH_ERROR: + stat = self.unpack_uint() + raise 'AUTH_ERROR', str(stat) + raise 'MSG_REJECTED', str(stat) + raise RuntimeError, 'no MSG_ACCEPTED but ' + str(stat) + verf = self.unpack_auth() + stat = self.unpack_enum() + if stat <> SUCCESS: + raise RuntimeError, 'no SUCCESS but ' + str(stat) + return xid, verf + # Caller must get procedure-specific part of reply + + +# Common base class for clients + +class Client: + + def init(self, host, prog, vers, port, type): + self.host = host + self.prog = prog + self.vers = vers + self.port = port + self.type = type + self.sock = socket.socket(socket.AF_INET, type) + self.sock.connect((host, port)) + self.lastxid = 0 + self.addpackers() + self.cred = None + self.verf = None + return self + + def Null(self): # Procedure 0 is always like this + self.start_call(0) + self.do_call(0) + self.end_call() + + def close(self): + self.sock.close() + + # Functions that may be overridden by specific derived classes + + def addpackers(self): + self.packer = Packer().init() + self.unpacker = Unpacker().init('') + + def mkcred(self, proc): + if self.cred == None: + p = Packer().init() + p.pack_auth_unix(0, socket.gethostname(), \ + os.getuid(), os.getgid(), []) + self.cred = p.get_buf() + return (AUTH_UNIX, self.cred) + + def mkverf(self, proc): + return (AUTH_NULL, '') + + +# Record-Marking standard support + +def sendfrag(sock, last, frag): + x = len(frag) + if last: x = x | 0x80000000L + header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \ + chr(int(x>>8 & 0xff)) + chr(int(x & 0xff))) + sock.send(header + frag) + +def sendrecord(sock, record): + sendfrag(sock, 1, record) + +def recvfrag(sock): + header = sock.recv(4) + x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \ + ord(header[2])<<8 | ord(header[3]) + last = ((x & 0x80000000) != 0) + n = int(x & 0x7fffffff) + frag = '' + while n > 0: + buf = sock.recv(n) + if not buf: raise EOFError + n = n - len(buf) + frag = frag + buf + return last, frag + +def recvrecord(sock): + record = '' + last = 0 + while not last: + last, frag = recvfrag(sock) + record = record + frag + return record + + +# Raw TCP-based client + +class RawTCPClient(Client): + + def init(self, host, prog, vers, port): + return Client.init(self, host, prog, vers, port, \ + socket.SOCK_STREAM) + + def start_call(self, proc): + self.lastxid = xid = self.lastxid + 1 + cred = self.mkcred(proc) + verf = self.mkverf(proc) + p = self.packer + p.reset() + p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf) + + def do_call(self, *rest): + # rest is used for UDP buffer size; ignored for TCP + call = self.packer.get_buf() + sendrecord(self.sock, call) + reply = recvrecord(self.sock) + u = self.unpacker + u.reset(reply) + xid, verf = u.unpack_replyheader() + if xid <> self.lastxid: + # Can't really happen since this is TCP... + raise RuntimeError, 'wrong xid in reply ' + `xid` + \ + ' instead of ' + `self.lastxid` + + def end_call(self): + self.unpacker.done() + + +# Raw UDP-based client +# XXX This class does not recover from missed/duplicated packets! + +class RawUDPClient(Client): + + def init(self, host, prog, vers, port): + return Client.init(self, host, prog, vers, port, \ + socket.SOCK_DGRAM) + + def start_call(self, proc): + self.lastxid = xid = self.lastxid + 1 + cred = self.mkcred(proc) + verf = self.mkverf(proc) + p = self.packer + p.reset() + p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf) + + def do_call(self, *rest): + if len(rest) == 0: + bufsize = 8192 + elif len(rest) > 1: + raise TypeError, 'too many args' + else: + bufsize = rest[0] + 512 + call = self.packer.get_buf() + self.sock.send(call) + # XXX What about time-out and retry? + reply = self.sock.recv(bufsize) + u = self.unpacker + u.reset(reply) + xid, verf = u.unpack_replyheader() + if xid <> self.lastxid: + # XXX Should assume it's an old reply + raise RuntimeError, 'wrong xid in reply ' + `xid` + \ + ' instead of ' + `self.lastxid` + + def end_call(self): + self.unpacker.done() + + +# Port mapper interface + +PMAP_PORT = 111 +PMAP_PROG = 100000 +PMAP_VERS = 2 +PMAPPROC_NULL = 0 # (void) -> void +PMAPPROC_SET = 1 # (mapping) -> bool +PMAPPROC_UNSET = 2 # (mapping) -> bool +PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int +PMAPPROC_DUMP = 4 # (void) -> pmaplist +PMAPPROC_CALLIT = 5 # (call_args) -> call_result + +# A mapping is (prog, vers, prot, port) and prot is one of: + +IPPROTO_TCP = 6 +IPPROTO_UDP = 17 + +# A pmaplist is a variable-length list of mappings, as follows: +# either (1, mapping, pmaplist) or (0). + +# A call_args is (prog, vers, proc, args) where args is opaque; +# a call_result is (port, res) where res is opaque. + + +class PortMapperPacker(Packer): + + def pack_mapping(self, mapping): + prog, vers, prot, port = mapping + self.pack_uint(prog) + self.pack_uint(vers) + self.pack_uint(prot) + self.pack_uint(port) + + def pack_pmaplist(self, list): + self.pack_list(list, self.pack_mapping) + + +class PortMapperUnpacker(Unpacker): + + def unpack_mapping(self): + prog = self.unpack_uint() + vers = self.unpack_uint() + prot = self.unpack_uint() + port = self.unpack_uint() + return prog, vers, prot, port + + def unpack_pmaplist(self): + return self.unpack_list(self.unpack_mapping) + + +class PartialPortMapperClient: + + def addpackers(self): + self.packer = PortMapperPacker().init() + self.unpacker = PortMapperUnpacker().init('') + + def Getport(self, mapping): + self.start_call(PMAPPROC_GETPORT) + self.packer.pack_mapping(mapping) + self.do_call(4) + port = self.unpacker.unpack_uint() + self.end_call() + return port + + def Dump(self): + self.start_call(PMAPPROC_DUMP) + self.do_call(8192-512) + list = self.unpacker.unpack_pmaplist() + self.end_call() + return list + + +class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): + + def init(self, host): + return RawTCPClient.init(self, \ + host, PMAP_PROG, PMAP_VERS, PMAP_PORT) + + +class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient): + + def init(self, host): + return RawUDPClient.init(self, \ + host, PMAP_PROG, PMAP_VERS, PMAP_PORT) + + +class TCPClient(RawTCPClient): + + def init(self, host, prog, vers): + pmap = TCPPortMapperClient().init(host) + port = pmap.Getport((prog, vers, IPPROTO_TCP, 0)) + pmap.close() + return RawTCPClient.init(self, host, prog, vers, port) + + +class UDPClient(RawUDPClient): + + def init(self, host, prog, vers): + pmap = UDPPortMapperClient().init(host) + port = pmap.Getport((prog, vers, IPPROTO_UDP, 0)) + pmap.close() + return RawUDPClient.init(self, host, prog, vers, port) + + +def test(): + import T + T.TSTART() + pmap = UDPPortMapperClient().init('') + T.TSTOP() + pmap.Null() + T.TSTOP() + list = pmap.Dump() + T.TSTOP() + list.sort() + for prog, vers, prot, port in list: + print prog, vers, + if prot == IPPROTO_TCP: print 'tcp', + elif prot == IPPROTO_UDP: print 'udp', + else: print prot, + print port diff --git a/Demo/rpc/xdr.py b/Demo/rpc/xdr.py new file mode 100644 index 0000000..b189b45 --- /dev/null +++ b/Demo/rpc/xdr.py @@ -0,0 +1,141 @@ +# Implement (a subset of) Sun XDR -- RFC1014. + + +import struct + + +class Packer: + + def init(self): + self.reset() + return self + + def reset(self): + self.buf = '' + + def get_buf(self): + return self.buf + + def pack_uint(self, x): + self.buf = self.buf + \ + (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \ + chr(int(x>>8 & 0xff)) + chr(int(x & 0xff))) + if struct.pack('i', 1) == '\0\0\0\1': + def pack_uint(self, x): + self.buf = self.buf + struct.pack('i', x) + + pack_int = pack_uint + + pack_enum = pack_int + + def pack_bool(self, x): + if x: self.buf = self.buf + '\0\0\0\1' + else: self.buf = self.buf + '\0\0\0\0' + + def pack_uhyper(self, x): + self.pack_uint(x>>32 & 0xffffffff) + self.pack_uint(x & 0xffffffff) + + pack_hyper = pack_uhyper + + def pack_fstring(self, n, s): + if n < 0: + raise ValueError, 'fstring size must be nonnegative' + n = ((n+3)/4)*4 + data = s[:n] + data = data + (n - len(data)) * '\0' + self.buf = self.buf + data + + pack_fopaque = pack_fstring + + def pack_string(self, s): + n = len(s) + self.pack_uint(n) + self.pack_fstring(n, s) + + pack_opaque = pack_string + + def pack_list(self, list, pack_item): + for item in list: + self.pack_uint(1) + pack_item(list) + self.pack_uint(0) + + +class Unpacker: + + def init(self, data): + self.reset(data) + return self + + def reset(self, data): + self.buf = data + self.pos = 0 + + def done(self): + if self.pos < len(self.buf): + raise RuntimeError, 'unextracted data remains' + + def unpack_uint(self): + i = self.pos + self.pos = j = i+4 + data = self.buf[i:j] + x = long(ord(data[0]))<<24 | ord(data[1])<<16 | \ + ord(data[2])<<8 | ord(data[3]) + # Return a Python long only if the value is not representable + # as a nonnegative Python int + if x < 0x80000000L: x = int(x) + return x + if struct.unpack('i', '\0\0\0\1') == 1: + def unpack_uint(self): + i = self.pos + self.pos = j = i+4 + return struct.unpack('i', self.buf[i:j]) + + def unpack_int(self): + x = self.unpack_uint() + if x >= 0x80000000L: x = x - 0x100000000L + return int(x) + + unpack_enum = unpack_int + + unpack_bool = unpack_int + + def unpack_uhyper(self): + hi = self.unpack_uint() + lo = self.unpack_uint() + return long(hi)<<32 | lo + + def unpack_hyper(self): + x = self.unpack_uhyper() + if x >= 0x8000000000000000L: x = x - 0x10000000000000000L + return x + + def unpack_fstring(self, n): + if n < 0: + raise ValueError, 'fstring size must be nonnegative' + i = self.pos + j = i + (n+3)/4*4 + if j > len(self.buf): + raise RuntimeError, 'buffer overrun' + self.pos = j + return self.buf[i:i+n] + + unpack_fopaque = unpack_fstring + + def unpack_string(self): + n = self.unpack_uint() + return self.unpack_fstring(n) + + unpack_opaque = unpack_string + + def unpack_list(self, unpack_item): + list = [] + while 1: + x = self.unpack_uint() + if not x: break + if x <> 1: + raise RuntimeError, \ + '0 or 1 expected, got ' + `x` + list.append(unpack_item()) + return list |