[Neo-report] r1852 vincent - /trunk/neo/protocol.py

nobody at svn.erp5.org nobody at svn.erp5.org
Tue Feb 23 18:50:16 CET 2010


Author: vincent
Date: Tue Feb 23 18:50:10 2010
New Revision: 1852

Log:
Factorise struct declarations.

Reduces the amount of duplicated (or deductible) information in packet
generators and parsers.

Modified:
    trunk/neo/protocol.py

Modified: trunk/neo/protocol.py
==============================================================================
--- trunk/neo/protocol.py [iso-8859-1] (original)
+++ trunk/neo/protocol.py [iso-8859-1] Tue Feb 23 18:50:10 2010
@@ -15,7 +15,7 @@
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
-from struct import pack, unpack, error
+from struct import pack, unpack, error, calcsize
 from socket import inet_ntoa, inet_aton
 
 from neo.util import Enum
@@ -200,6 +200,8 @@
         raise PacketMalformedError("can't read string <%s>" % name)
     return (string, buf[offset+size:])
 
+def _writeString(buf):
+    return pack('!L', len(buf)) + buf
 
 class Packet(object):
     """
@@ -208,6 +210,8 @@
     a tuple respectively.
     """
 
+    _header_format = None
+    _header_len = None
     _request = None
     _answer = None
     _body = None
@@ -311,17 +315,20 @@
     Request a node identification. This must be the first packet for any
     connection. Any -> Any.
     """
+    _header_format = '!LLH16s6s'
+
     def _encode(self, node_type, uuid, address, name):
         uuid = _encodeUUID(uuid)
         address = _encodeAddress(address)
-        return pack('!LLH16s6sL', PROTOCOL_VERSION[0], PROTOCOL_VERSION[1],
-                          node_type, uuid, address, len(name)) + name
-
-    def _decode(self, body):
-        r = unpack('!LLH16s6s', body[:32])
+        return pack(self._header_format, PROTOCOL_VERSION[0],
+                          PROTOCOL_VERSION[1], node_type, uuid, address) + \
+                          _writeString(name)
+
+    def _decode(self, body):
+        r = unpack(self._header_format, body[:self._header_len])
         major, minor, node_type, uuid, address = r
         address = _decodeAddress(address)
-        (name, _) = _readString(body, 'name', offset=32)
+        (name, _) = _readString(body, 'name', offset=self._header_len)
         node_type = _decodeNodeType(node_type)
         uuid = _decodeUUID(uuid)
         if (major, minor) != PROTOCOL_VERSION:
@@ -333,15 +340,17 @@
     Accept a node identification. This should be a reply to Request Node
     Identification. Any -> Any.
     """
+    _header_format = '!H16sLL16s'
+
     def _encode(self, node_type, uuid,
              num_partitions, num_replicas, your_uuid):
         uuid = _encodeUUID(uuid)
         your_uuid = _encodeUUID(your_uuid)
-        return pack('!H16sLL16s', node_type, uuid,
+        return pack(self._header_format, node_type, uuid,
                           num_partitions, num_replicas, your_uuid)
 
     def _decode(self, body):
-        r = unpack('!H16sLL16s', body)
+        r = unpack(self._header_format, body)
         node_type, uuid, num_partitions, num_replicas, your_uuid = r
         node_type = _decodeNodeType(node_type)
         uuid = _decodeUUID(uuid)
@@ -360,20 +369,32 @@
     Reply to Ask Primary Master. This message includes a list of known master
     nodes to make sure that a peer has the same information. M -> Any.
     """
+    _header_format = '!16sL'
+    _list_entry_format = '!6s16s'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, primary_uuid, known_master_list):
         primary_uuid = _encodeUUID(primary_uuid)
-        body = [primary_uuid, pack('!L', len(known_master_list))]
+        body = [pack(self._header_format, primary_uuid,
+            len(known_master_list))]
         for address, uuid in known_master_list:
             uuid = _encodeUUID(uuid)
             address = _encodeAddress(address)
-            body.append(pack('!6s16s', address, uuid))
-        return ''.join(body)
-
-    def _decode(self, body):
-        (primary_uuid, n) = unpack('!16sL', body[:20])
+            body.append(pack(self._list_entry_format, address, uuid))
+        return ''.join(body)
+
+    def _decode(self, body):
+        packet_offset = self._header_len
+        (primary_uuid, n) = unpack(self._header_format,
+            body[:packet_offset])
         known_master_list = []
+        list_entry_len = self._list_entry_len
+        list_entry_format = self._list_entry_format
         for i in xrange(n):
-            address, uuid = unpack('!6s16s', body[20+i*22:42+i*22])
+            next_packet_offset = packet_offset + list_entry_len
+            address, uuid = unpack(list_entry_format,
+                body[packet_offset:next_packet_offset])
+            packet_offset = next_packet_offset
             address = _decodeAddress(address)
             uuid = _decodeUUID(uuid)
             known_master_list.append((address, uuid))
@@ -423,17 +444,28 @@
     Ask rows in a partition table that a storage node stores. Used to recover
     information. PM -> S.
     """
+    _header_format = '!L'
+    _list_entry_format = '!L'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, offset_list):
-        body = [pack('!L', len(offset_list))]
+        body = [pack(self._header_format, len(offset_list))]
+        list_entry_format = self._list_entry_format
         for offset in offset_list:
-            body.append(pack('!L', offset))
-        return ''.join(body)
-
-    def _decode(self, body):
-        (n,) = unpack('!L', body[:4])
+            body.append(pack(list_entry_format, offset))
+        return ''.join(body)
+
+    def _decode(self, body):
+        packet_offset = self._header_len
+        (n,) = unpack(self._header_format, body[:packet_offset])
         offset_list = []
+        list_entry_len = self._list_entry_len
+        list_entry_format = self._list_entry_format
         for i in xrange(n):
-            offset = unpack('!L', body[4+i*4:8+i*4])[0]
+            next_packet_offset = packet_offset + list_entry_len
+            offset = unpack(list_entry_format,
+                body[packet_offset:next_packet_offset])[0]
+            packet_offset = next_packet_offset
             offset_list.append(offset)
         return (offset_list,)
 
@@ -441,28 +473,42 @@
     """
     Answer rows in a partition table. S -> PM.
     """
+    _header_format = '!8sL'
+    _row_entry_format = '!LL'
+    _row_entry_len = calcsize(_row_entry_format)
+    _cell_entry_format = '!16sH'
+    _cell_entry_len = calcsize(_cell_entry_format)
+
     def _encode(self, ptid, row_list):
         ptid = _encodePTID(ptid)
-        body = [pack('!8sL', ptid, len(row_list))]
+        body = [pack(self._header_format, ptid, len(row_list))]
+        row_entry_format = self._row_entry_format
+        cell_entry_format = self._cell_entry_format
         for offset, cell_list in row_list:
-            body.append(pack('!LL', offset, len(cell_list)))
+            body.append(pack(row_entry_format, offset, len(cell_list)))
             for uuid, state in cell_list:
                 uuid = _encodeUUID(uuid)
-                body.append(pack('!16sH', uuid, state))
-        return ''.join(body)
-
-    def _decode(self, body):
-        index = 12
-        (ptid, n) = unpack('!8sL', body[:index])
+                body.append(pack(cell_entry_format, uuid, state))
+        return ''.join(body)
+
+    def _decode(self, body):
+        index = self._header_len
+        (ptid, n) = unpack(self._header_format, body[:index])
         ptid = _decodePTID(ptid)
         row_list = []
         cell_list = []
+        row_entry_format = self._row_entry_format
+        row_entry_len = self._row_entry_len
+        cell_entry_format = self._cell_entry_format
+        cell_entry_len = self._cell_entry_len
         for i in xrange(n):
-            offset, m = unpack('!LL', body[index:index+8])
-            index += 8
+            next_index = index + row_entry_len
+            offset, m = unpack(row_entry_format, body[index:next_index])
+            index = next_index
             for j in xrange(m):
-                uuid, state = unpack('!16sH', body[index:index+18])
-                index += 18
+                next_index = index + cell_entry_len
+                uuid, state = unpack(cell_entry_format, body[index:next_index])
+                index = next_index
                 state = CellStates.get(state)
                 uuid = _decodeUUID(uuid)
                 cell_list.append((uuid, state))
@@ -474,28 +520,42 @@
     """
     Send rows in a partition table to update other nodes. PM -> S, C.
     """
+    _header_format = '!8sL'
+    _row_entry_format = '!LL'
+    _row_entry_len = calcsize(_row_entry_format)
+    _cell_entry_format = '!16sH'
+    _cell_entry_len = calcsize(_cell_entry_format)
+
     def _encode(self, ptid, row_list):
         ptid = _encodePTID(ptid)
-        body = [pack('!8sL', ptid, len(row_list))]
+        body = [pack(self._header_format, ptid, len(row_list))]
+        row_entry_format = self._row_entry_format
+        cell_entry_format = self._cell_entry_format
         for offset, cell_list in row_list:
-            body.append(pack('!LL', offset, len(cell_list)))
+            body.append(pack(row_entry_format, offset, len(cell_list)))
             for uuid, state in cell_list:
                 uuid = _encodeUUID(uuid)
-                body.append(pack('!16sH', uuid, state))
-        return ''.join(body)
-
-    def _decode(self, body):
-        index = 12
-        (ptid, n,) = unpack('!8sL', body[:index])
+                body.append(pack(cell_entry_format, uuid, state))
+        return ''.join(body)
+
+    def _decode(self, body):
+        index = self._header_len
+        (ptid, n,) = unpack(self._header_format, body[:index])
         ptid = _decodePTID(ptid)
         row_list = []
         cell_list = []
+        row_entry_format = self._row_entry_format
+        row_entry_len = self._row_entry_len
+        cell_entry_format = self._cell_entry_format
+        cell_entry_len = self._cell_entry_len
         for i in xrange(n):
-            offset, m = unpack('!LL', body[index:index+8])
-            index += 8
+            next_index = index + row_entry_len
+            offset, m = unpack(row_entry_format, body[index:next_index])
+            index = next_index
             for j in xrange(m):
-                uuid, state = unpack('!16sH', body[index:index+18])
-                index += 18
+                next_index = index + cell_entry_len
+                uuid, state = unpack(cell_entry_format, body[index:next_index])
+                index = next_index
                 state = CellStates.get(state)
                 uuid = _decodeUUID(uuid)
                 cell_list.append((uuid, state))
@@ -508,20 +568,31 @@
     Notify a subset of a partition table. This is used to notify changes.
     PM -> S, C.
     """
+    _header_format = '!8sL'
+    _list_entry_format = '!L16sH'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, ptid, cell_list):
         ptid = _encodePTID(ptid)
-        body = [pack('!8sL', ptid, len(cell_list))]
+        body = [pack(self._header_format, ptid, len(cell_list))]
+        list_entry_format = self._list_entry_format
         for offset, uuid, state in cell_list:
             uuid = _encodeUUID(uuid)
-            body.append(pack('!L16sH', offset, uuid, state))
-        return ''.join(body)
-
-    def _decode(self, body):
-        (ptid, n) = unpack('!8sL', body[:12])
+            body.append(pack(list_entry_format, offset, uuid, state))
+        return ''.join(body)
+
+    def _decode(self, body):
+        packet_offset = self._header_len
+        (ptid, n) = unpack(self._header_format, body[:packet_offset])
         ptid = _decodePTID(ptid)
         cell_list = []
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
         for i in xrange(n):
-            (offset, uuid, state) = unpack('!L16sH', body[12+i*22:34+i*22])
+            next_packet_offset = packet_offset + list_entry_len
+            (offset, uuid, state) = unpack(list_entry_format,
+                body[packet_offset:next_packet_offset])
+            packet_offset = next_packet_offset
             state = CellStates.get(state)
             uuid = _decodeUUID(uuid)
             cell_list.append((offset, uuid, state))
@@ -533,12 +604,13 @@
     a storage to another.
     S -> M
     """
+    _header_format = '!L'
 
     def _encode(self, offset):
-        return pack('!L', offset)
-
-    def _decode(self, body):
-        (offset, ) = unpack('!L', body)
+        return pack(self._header_format, offset)
+
+    def _decode(self, body):
+        (offset, ) = unpack(self._header_format, body)
         return (offset, )
 
 class StartOperation(Packet):
@@ -565,16 +637,25 @@
     """
     Answer unfinished transactions  S -> PM.
     """
+    _header_format = '!L'
+    _list_entry_format = '8s'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, tid_list):
-        body = [pack('!L', len(tid_list))]
+        body = [pack(self._header_format, len(tid_list))]
         body.extend(tid_list)
         return ''.join(body)
 
     def _decode(self, body):
-        (n,) = unpack('!L', body[:4])
+        offset = self._header_len
+        (n,) = unpack(self._header_format, body[:offset])
         tid_list = []
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
         for i in xrange(n):
-            tid = unpack('8s', body[4+i*8:12+i*8])[0]
+            next_offset = offset + list_entry_len
+            tid = unpack(list_entry_format, body[offset:next_offset])[0]
+            offset = next_offset
             tid_list.append(tid)
         return (tid_list,)
 
@@ -643,16 +724,25 @@
     """
     Finish a transaction. C -> PM.
     """
+    _header_format = '!8sL'
+    _list_entry_format = '8s'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, oid_list, tid):
-        body = [pack('!8sL', tid, len(oid_list))]
+        body = [pack(self._header_format, tid, len(oid_list))]
         body.extend(oid_list)
         return ''.join(body)
 
     def _decode(self, body):
-        (tid, n) = unpack('!8sL', body[:12])
+        offset = self._header_len
+        (tid, n) = unpack(self._header_format, body[:offset])
         oid_list = []
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
         for i in xrange(n):
-            oid = unpack('8s', body[12+i*8:20+i*8])[0]
+            next_offset = offset + list_entry_len
+            oid = unpack(list_entry_format, body[offset:next_offset])[0]
+            offset = next_offset
             oid_list.append(oid)
         return (oid_list, tid)
 
@@ -693,16 +783,25 @@
     """
     Invalidate objects. PM -> C.
     """
+    _header_format = '!8sL'
+    _list_entry_format = '8s'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, oid_list, tid):
-        body = [pack('!8sL', tid, len(oid_list))]
+        body = [pack(self._header_format, tid, len(oid_list))]
         body.extend(oid_list)
         return ''.join(body)
 
     def _decode(self, body):
-        (tid, n) = unpack('!8sL', body[:12])
+        offset = self._header_len
+        (tid, n) = unpack(self._header_format, body[:offset])
         oid_list = []
-        for i in xrange(12, 12 + n * 8, 8):
-            oid = unpack('8s', body[i:i+8])[0]
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
+        for i in xrange(n):
+            next_offset = offset + list_entry_len
+            oid = unpack(list_entry_format, body[offset:next_offset])[0]
+            offset = next_offset
             oid_list.append(oid)
         return (oid_list, tid)
 
@@ -721,26 +820,37 @@
     """
     Ask new object IDs. C -> PM.
     """
+    _header_format = '!H'
+
     def _encode(self, num_oids):
-        return pack('!H', num_oids)
-
-    def _decode(self, body):
-        return unpack('!H', body) # num oids
+        return pack(self._header_format, num_oids)
+
+    def _decode(self, body):
+        return unpack(self._header_format, body) # num oids
 
 class AnswerNewOIDs(Packet):
     """
     Answer new object IDs. PM -> C.
     """
+    _header_format = '!H'
+    _list_entry_format = '8s'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, oid_list):
-        body = [pack('!H', len(oid_list))]
+        body = [pack(self._header_format, len(oid_list))]
         body.extend(oid_list)
         return ''.join(body)
 
     def _decode(self, body):
-        (n,) = unpack('!H', body[:2])
+        offset = self._header_len
+        (n,) = unpack(self._header_format, body[:offset])
         oid_list = []
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
         for i in xrange(n):
-            oid = unpack('8s', body[2+i*8:10+i*8])[0]
+            next_offset = offset + list_entry_len
+            oid = unpack(list_entry_format, body[offset:next_offset])[0]
+            offset = next_offset
             oid_list.append(oid)
         return (oid_list,)
 
@@ -749,16 +859,19 @@
     Ask to store an object. Send an OID, an original serial, a current
     transaction ID, and data. C -> S.
     """
+    _header_format = '!8s8s8sBL'
+
     def _encode(self, oid, serial, compression, checksum, data, tid):
         if serial is None:
             serial = INVALID_TID
-        return pack('!8s8s8sBLL', oid, serial, tid, compression,
-                          checksum, len(data)) + data
-
-    def _decode(self, body):
-        r = unpack('!8s8s8sBL', body[:29])
+        return pack(self._header_format, oid, serial, tid, compression,
+                          checksum) + _writeString(data)
+
+    def _decode(self, body):
+        header_len = self._header_len
+        r = unpack(self._header_format, body[:header_len])
         oid, serial, tid, compression, checksum = r
-        (data, _) = _readString(body, 'data', offset=29)
+        (data, _) = _readString(body, 'data', offset=header_len)
         return (oid, serial, compression, checksum, data, tid)
 
 class AnswerStoreObject(Packet):
@@ -768,13 +881,15 @@
     if this serial is newer than the current transaction ID, a client
     node must not try to resolve the conflict. S -> C.
     """
+    _header_format = '!B8s8s'
+
     def _encode(self, conflicting, oid, serial):
         if serial is None:
             serial = INVALID_TID
-        return pack('!B8s8s', conflicting, oid, serial)
-
-    def _decode(self, body):
-        (conflicting, oid, serial) = unpack('!B8s8s', body)
+        return pack(self._header_format, conflicting, oid, serial)
+
+    def _decode(self, body):
+        (conflicting, oid, serial) = unpack(self._header_format, body)
         return (conflicting, oid, serial)
 
 class AbortTransaction(Packet):
@@ -789,9 +904,11 @@
     """
     Ask to store a transaction. C -> S.
     """
+    _header_format = '!8sLHHH'
+
     def _encode(self, tid, user, desc, ext, oid_list):
         lengths = (len(oid_list), len(user), len(desc), len(ext))
-        body = [pack('!8sLHHH', tid, *lengths)]
+        body = [pack(self._header_format, tid, *lengths)]
         body.append(user)
         body.append(desc)
         body.append(ext)
@@ -799,9 +916,9 @@
         return ''.join(body)
 
     def _decode(self, body):
-        r = unpack('!8sLHHH', body[:18])
+        r = unpack(self._header_format, body[:self._header_len])
         tid, oid_len, user_len, desc_len, ext_len = r
-        body = body[18:]
+        body = body[self._header_len:]
         user = body[:user_len]
         body = body[user_len:]
         desc = body[:desc_len]
@@ -832,13 +949,15 @@
     is specified, the specified revision of an object will be returned. If
     a TID is specified, an object right before the TID will be returned. S,C -> S.
     """
+    _header_format = '!8s8s8s'
+
     def _encode(self, oid, serial, tid):
         tid = _encodeTID(tid)
         serial = _encodeTID(serial) # serial is the previous TID
-        return pack('!8s8s8s', oid, serial, tid)
-
-    def _decode(self, body):
-        (oid, serial, tid) = unpack('8s8s8s', body)
+        return pack(self._header_format, oid, serial, tid)
+
+    def _decode(self, body):
+        (oid, serial, tid) = unpack(self._header_format, body)
         if serial == INVALID_TID:
             serial = None
         tid = _decodeTID(tid)
@@ -848,21 +967,24 @@
     """
     Answer the requested object. S -> C.
     """
+    _header_format = '!8s8s8sBL'
+
     def _encode(self, oid, serial_start, serial_end, compression,
             checksum, data):
         if serial_start is None:
             serial_start = INVALID_TID
         if serial_end is None:
             serial_end = INVALID_TID
-        return pack('!8s8s8sBLL', oid, serial_start, serial_end,
-                          compression, checksum, len(data)) + data
-
-    def _decode(self, body):
-        r = unpack('!8s8s8sBL', body[:29])
+        return pack(self._header_format, oid, serial_start, serial_end,
+                          compression, checksum) + _writeString(data)
+
+    def _decode(self, body):
+        header_len = self._header_len
+        r = unpack(self._header_format, body[:header_len])
         oid, serial_start, serial_end, compression, checksum = r
         if serial_end == INVALID_TID:
             serial_end = None
-        (data, _) = _readString(body, 'data', offset=29)
+        (data, _) = _readString(body, 'data', offset=header_len)
         return (oid, serial_start, serial_end, compression, checksum, data)
 
 class AskTIDs(Packet):
@@ -870,26 +992,37 @@
     Ask for TIDs between a range of offsets. The order of TIDs is descending,
     and the range is [first, last). C, S -> S.
     """
+    _header_format = '!QQL'
+
     def _encode(self, first, last, partition):
-        return pack('!QQL', first, last, partition)
-
-    def _decode(self, body):
-        return unpack('!QQL', body) # first, last, partition
+        return pack(self._header_format, first, last, partition)
+
+    def _decode(self, body):
+        return unpack(self._header_format, body) # first, last, partition
 
 class AnswerTIDs(Packet):
     """
     Answer the requested TIDs. S -> C, S.
     """
+    _header_format = '!L'
+    _list_entry_format = '8s'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, tid_list):
-        body = [pack('!L', len(tid_list))]
+        body = [pack(self._header_format, len(tid_list))]
         body.extend(tid_list)
         return ''.join(body)
 
     def _decode(self, body):
-        (n, ) = unpack('!L', body[:4])
+        offset = self._header_len
+        (n, ) = unpack(self._header_format, body[:offset])
         tid_list = []
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
         for i in xrange(n):
-            tid = unpack('8s', body[4+i*8:12+i*8])[0]
+            next_offset = offset + list_entry_len
+            tid = unpack(list_entry_format, body[offset:next_offset])[0]
+            offset = next_offset
             tid_list.append(tid)
         return (tid_list,)
 
@@ -905,9 +1038,11 @@
     """
     Answer information (user, description) about a transaction. S -> Any.
     """
+    _header_format = '!8sHHHBL'
+
     def _encode(self, tid, user, desc, ext, packed, oid_list):
         packed = packed and 1 or 0
-        body = [pack('!8sHHHBL', tid, len(user), len(desc), len(ext),
+        body = [pack(self._header_format, tid, len(user), len(desc), len(ext),
             packed, len(oid_list))]
         body.append(user)
         body.append(desc)
@@ -916,10 +1051,10 @@
         return ''.join(body)
 
     def _decode(self, body):
-        r = unpack('!8sHHHBL', body[:19])
+        r = unpack(self._header_format, body[:self._header_len])
         tid, user_len, desc_len, ext_len, packed, oid_len = r
         packed = bool(packed)
-        body = body[19:]
+        body = body[self._header_len:]
         user = body[:user_len]
         body = body[user_len:]
         desc = body[:desc_len]
@@ -938,28 +1073,40 @@
     Ask history information for a given object. The order of serials is
     descending, and the range is [first, last]. C, S -> S.
     """
+    _header_format = '!8sQQ'
+
     def _encode(self, oid, first, last):
-        return pack('!8sQQ', oid, first, last)
-
-    def _decode(self, body):
-        (oid, first, last) = unpack('!8sQQ', body)
+        return pack(self._header_format, oid, first, last)
+
+    def _decode(self, body):
+        (oid, first, last) = unpack(self._header_format, body)
         return (oid, first, last)
 
 class AnswerObjectHistory(Packet):
     """
     Answer history information (serial, size) for an object. S -> C, S.
     """
+    _header_format = '!8sL'
+    _list_entry_format = '!8sL'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, oid, history_list):
-        body = [pack('!8sL', oid, len(history_list))]
+        body = [pack(self._header_format, oid, len(history_list))]
+        list_entry_format = self._list_entry_format
         for serial, size in history_list:
-            body.append(pack('!8sL', serial, size))
-        return ''.join(body)
-
-    def _decode(self, body):
-        (oid, length) = unpack('!8sL', body[:12])
+            body.append(pack(list_entry_format, serial, size))
+        return ''.join(body)
+
+    def _decode(self, body):
+        offset = self._header_len
+        (oid, length) = unpack(self._header_format, body[:offset])
         history_list = []
-        for i in xrange(12, 12 + length * 12, 12):
-            serial, size = unpack('!8sL', body[i:i+12])
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
+        for i in xrange(length):
+            next_offset = offset + list_entry_len
+            serial, size = unpack(list_entry_format, body[offset:next_offset])
+            offset = next_offset
             history_list.append((serial, size))
         return (oid, history_list)
 
@@ -968,26 +1115,37 @@
     Ask for OIDs between a range of offsets. The order of OIDs is descending,
     and the range is [first, last). S -> S.
     """
+    _header_format = '!QQL'
+
     def _encode(self, first, last, partition):
-        return pack('!QQL', first, last, partition)
-
-    def _decode(self, body):
-        return unpack('!QQL', body) # first, last, partition
+        return pack(self._header_format, first, last, partition)
+
+    def _decode(self, body):
+        return unpack(self._header_format, body) # first, last, partition
 
 class AnswerOIDs(Packet):
     """
     Answer the requested OIDs. S -> S.
     """
+    _header_format = '!L'
+    _list_entry_format = '8s'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, oid_list):
-        body = [pack('!L', len(oid_list))]
+        body = [pack(self._header_format, len(oid_list))]
         body.extend(oid_list)
         return ''.join(body)
 
     def _decode(self, body):
-        (n,) = unpack('!L', body[:4])
+        offset = self._header_len
+        (n,) = unpack(self._header_format, body[:offset])
         oid_list = []
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
         for i in xrange(n):
-            oid = unpack('8s', body[4+i*8:12+i*8])[0]
+            next_offset = offset + list_entry_len
+            oid = unpack(list_entry_format, body[offset:next_offset])[0]
+            offset = next_offset
             oid_list.append(oid)
         return (oid_list,)
 
@@ -996,13 +1154,15 @@
     All the following messages are for neoctl to admin node
     Ask information about partition
     """
+    _header_format = '!LL16s'
+
     def _encode(self, min_offset, max_offset, uuid):
         uuid = _encodeUUID(uuid)
-        body = [pack('!LL16s', min_offset, max_offset, uuid)]
-        return ''.join(body)
-
-    def _decode(self, body):
-        (min_offset, max_offset, uuid) =  unpack('!LL16s', body)
+        body = [pack(self._header_format, min_offset, max_offset, uuid)]
+        return ''.join(body)
+
+    def _decode(self, body):
+        (min_offset, max_offset, uuid) =  unpack(self._header_format, body)
         uuid = _decodeUUID(uuid)
         return (min_offset, max_offset, uuid)
 
@@ -1010,28 +1170,42 @@
     """
     Answer information about partition
     """
+    _header_format = '!8sL'
+    _row_entry_format = '!LL'
+    _row_entry_len = calcsize(_row_entry_format)
+    _cell_entry_format = '!16sH'
+    _cell_entry_len = calcsize(_cell_entry_format)
+
     def _encode(self, ptid, row_list):
         ptid = _encodePTID(ptid)
-        body = [pack('!8sL', ptid, len(row_list))]
+        body = [pack(self._header_format, ptid, len(row_list))]
+        row_entry_format = self._row_entry_format
+        cell_entry_format = self._cell_entry_format
         for offset, cell_list in row_list:
-            body.append(pack('!LL', offset, len(cell_list)))
+            body.append(pack(row_entry_format, offset, len(cell_list)))
             for uuid, state in cell_list:
                 uuid = _encodeUUID(uuid)
-                body.append(pack('!16sH', uuid, state))
-        return ''.join(body)
-
-    def _decode(self, body):
-        index = 12
-        (ptid, n) = unpack('!8sL', body[:index])
+                body.append(pack(cell_entry_format, uuid, state))
+        return ''.join(body)
+
+    def _decode(self, body):
+        index = self._header_len
+        (ptid, n) = unpack(self._header_format, body[:index])
         ptid = _decodePTID(ptid)
         row_list = []
         cell_list = []
+        row_entry_format = self._row_entry_format
+        row_entry_len = self._row_entry_len
+        cell_entry_format = self._cell_entry_format
+        cell_entry_len = self._cell_entry_len
         for i in xrange(n):
-            offset, m = unpack('!LL', body[index:index+8])
-            index += 8
+            next_index = index + row_entry_len
+            offset, m = unpack(row_entry_format, body[index:next_index])
+            index = next_index
             for j in xrange(m):
-                uuid, state = unpack('!16sH', body[index:index+18])
-                index += 18
+                next_index = index + cell_entry_len
+                uuid, state = unpack(cell_entry_format, body[index:next_index])
+                index = next_index
                 state = CellStates.get(state)
                 uuid = _decodeUUID(uuid)
                 cell_list.append((uuid, state))
@@ -1043,11 +1217,13 @@
     """
     Ask information about nodes
     """
+    _header_format = '!H'
+
     def _encode(self, node_type):
-        return ''.join([pack('!H', node_type)])
-
-    def _decode(self, body):
-        (node_type, ) = unpack('!H', body)
+        return ''.join([pack(self._header_format, node_type)])
+
+    def _decode(self, body):
+        (node_type, ) = unpack(self._header_format, body)
         node_type = _decodeNodeType(node_type)
         return (node_type,)
 
@@ -1055,19 +1231,30 @@
     """
     Answer information about nodes
     """
+    _header_format = '!L'
+    _list_entry_format = '!H6s16sH'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, node_list):
-        body = [pack('!L', len(node_list))]
+        body = [pack(self._header_format, len(node_list))]
+        list_entry_format = self._list_entry_format
         for node_type, address, uuid, state in node_list:
             uuid = _encodeUUID(uuid)
             address = _encodeAddress(address)
-            body.append(pack('!H6s16sH', node_type, address, uuid, state))
-        return ''.join(body)
-
-    def _decode(self, body):
-        (n,) = unpack('!L', body[:4])
+            body.append(pack(list_entry_format, node_type, address, uuid,
+                state))
+        return ''.join(body)
+
+    def _decode(self, body):
+        offset = self._header_len
+        (n,) = unpack(self._header_format, body[:offset])
         node_list = []
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
         for i in xrange(n):
-            r = unpack('!H6s16sH', body[4+i*26:30+i*26])
+            next_offset = offset + list_entry_len
+            r = unpack(list_entry_format, body[offset:next_offset])
+            offset = next_offset
             node_type, address, uuid, state = r
             address = _decodeAddress(address)
             node_type = _decodeNodeType(node_type)
@@ -1080,12 +1267,15 @@
     """
     Set the node state
     """
+    _header_format = '!16sHB'
+
     def _encode(self, uuid, state, modify_partition_table):
         uuid = _encodeUUID(uuid)
-        return ''.join([pack('!16sHB', uuid, state, modify_partition_table)])
-
-    def _decode(self, body):
-        (uuid, state, modify) = unpack('!16sHB', body)
+        return ''.join([pack(self._header_format, uuid, state,
+            modify_partition_table)])
+
+    def _decode(self, body):
+        (uuid, state, modify) = unpack(self._header_format, body)
         state = _decodeNodeState(state)
         uuid = _decodeUUID(uuid)
         return (uuid, state, modify)
@@ -1094,12 +1284,14 @@
     """
     Answer state of the node
     """
+    _header_format = '!16sH'
+
     def _encode(self, uuid, state):
         uuid = _encodeUUID(uuid)
-        return ''.join([pack('!16sH', uuid, state)])
-
-    def _decode(self, body):
-        (uuid, state) = unpack('!16sH', body)
+        return ''.join([pack(self._header_format, uuid, state)])
+
+    def _decode(self, body):
+        (uuid, state) = unpack(self._header_format, body)
         state = _decodeNodeState(state)
         uuid = _decodeUUID(uuid)
         return (uuid, state)
@@ -1108,14 +1300,25 @@
     """
     Ask the primary to include some pending node in the partition table
     """
+    _header_format = '!H'
+    _list_header_format = '!16s'
+    _list_header_len = calcsize(_list_header_format)
+
     def _encode(self, uuid_list=()):
+        list_header_format = self._list_header_format
         # an empty list means all current pending nodes
-        uuid_list = [pack('!16s', _encodeUUID(uuid)) for uuid in uuid_list]
-        return pack('!H', len(uuid_list)) + ''.join(uuid_list)
-
-    def _decode(self, body):
-        (n, ) = unpack('!H', body[:2])
-        uuid_list = [unpack('!16s', body[2+i*16:18+i*16])[0] for i in xrange(n)]
+        uuid_list = [pack(list_header_format, _encodeUUID(uuid)) \
+            for uuid in uuid_list]
+        return pack(self._header_format, len(uuid_list)) + ''.join(uuid_list)
+
+    def _decode(self, body):
+        header_len = self._header_len
+        (n, ) = unpack(self._header_format, body[:header_len])
+        list_header_format = self._list_header_format
+        list_header_len = self._list_header_len
+        uuid_list = [unpack(list_header_format,
+            body[header_len+i*list_header_len:\
+                 header_len+(i+1)*list_header_len])[0] for i in xrange(n)]
         uuid_list = [_decodeUUID(x) for x in uuid_list]
         return (uuid_list, )
 
@@ -1123,14 +1326,24 @@
     """
     Answer what are the nodes added in the partition table
     """
+    _header_format = '!H'
+    _list_header_format = '!16s'
+    _list_header_len = calcsize(_list_header_format)
+
     def _encode(self, uuid_list):
+        list_header_format = self._list_header_format
         # an empty list means no new nodes
-        uuid_list = [pack('!16s', _encodeUUID(uuid)) for uuid in uuid_list]
-        return pack('!H', len(uuid_list)) + ''.join(uuid_list)
-
-    def _decode(self, body):
-        (n, ) = unpack('!H', body[:2])
-        uuid_list = [unpack('!16s', body[2+i*16:18+i*16])[0] for i in xrange(n)]
+        uuid_list = [pack(list_header_format, _encodeUUID(uuid)) for uuid in uuid_list]
+        return pack(self._header_format, len(uuid_list)) + ''.join(uuid_list)
+
+    def _decode(self, body):
+        header_len = self._header_len
+        (n, ) = unpack(self._header_format, body[:header_len])
+        list_header_format = self._list_header_format
+        list_header_len = self._list_header_len
+        uuid_list = [unpack(list_header_format,
+            body[header_len+i*list_header_len:\
+                 header_len+(i+1)*list_header_len])[0] for i in xrange(n)]
         uuid_list = [_decodeUUID(x) for x in uuid_list]
         return (uuid_list, )
 
@@ -1138,19 +1351,30 @@
     """
     Notify information about one or more nodes. PM -> Any.
     """
+    _header_format = '!L'
+    _list_entry_format = '!H6s16sH'
+    _list_entry_len = calcsize(_list_entry_format)
+
     def _encode(self, node_list):
-        body = [pack('!L', len(node_list))]
+        body = [pack(self._header_format, len(node_list))]
+        list_entry_format = self._list_entry_format
         for node_type, address, uuid, state in node_list:
             uuid = _encodeUUID(uuid)
             address = _encodeAddress(address)
-            body.append(pack('!H6s16sH', node_type, address, uuid, state))
-        return ''.join(body)
-
-    def _decode(self, body):
-        (n,) = unpack('!L', body[:4])
+            body.append(pack(list_entry_format, node_type, address, uuid,
+                state))
+        return ''.join(body)
+
+    def _decode(self, body):
+        offset = self._header_len
+        (n,) = unpack(self._header_format, body[:offset])
         node_list = []
+        list_entry_format = self._list_entry_format
+        list_entry_len = self._list_entry_len
         for i in xrange(n):
-            r = unpack('!H6s16sH', body[4+i*26:30+i*26])
+            next_offset = offset + list_entry_len
+            r = unpack(list_entry_format, body[offset:next_offset])
+            offset = next_offset
             node_type, address, uuid, state = r
             address = _decodeAddress(address)
             node_type = _decodeNodeType(node_type)
@@ -1175,11 +1399,13 @@
     """
     Set the cluster state
     """
+    _header_format = '!H'
+
     def _encode(self, state):
-        return pack('!H', state)
-
-    def _decode(self, body):
-        (state, ) = unpack('!H', body[:2])
+        return pack(self._header_format, state)
+
+    def _decode(self, body):
+        (state, ) = unpack(self._header_format, body[:self._header_len])
         state = _decodeClusterState(state)
         return (state, )
 
@@ -1187,11 +1413,13 @@
     """
     Notify information about the cluster
     """
+    _header_format = '!H'
+
     def _encode(self, state):
-        return pack('!H', state)
-
-    def _decode(self, body):
-        (state, ) = unpack('!H', body)
+        return pack(self._header_format, state)
+
+    def _decode(self, body):
+        (state, ) = unpack(self._header_format, body)
         state = _decodeClusterState(state)
         return (state, )
 
@@ -1205,11 +1433,13 @@
     """
     Answer state of the cluster
     """
+    _header_format = '!H'
+
     def _encode(self, state):
-        return pack('!H', state)
-
-    def _decode(self, body):
-        (state, ) = unpack('!H', body)
+        return pack(self._header_format, state)
+
+    def _decode(self, body):
+        (state, ) = unpack(self._header_format, body)
         state = _decodeClusterState(state)
         return (state, )
 
@@ -1227,15 +1457,22 @@
     any other message, even if such a message does not expect a reply
     usually. Any -> Any.
     """
+    _header_format = '!H'
+
     def _encode(self, code, message):
-        return pack('!HL', code, len(message)) + message
-
-    def _decode(self, body):
-        (code, ) = unpack('!H', body[:2])
+        return pack(self._header_format, code) + _writeString(message)
+
+    def _decode(self, body):
+        offset = self._header_len
+        (code, ) = unpack(self._header_format, body[:offset])
         code = _decodeErrorCode(code)
-        (message, _) = _readString(body, 'message', offset=2)
+        (message, _) = _readString(body, 'message', offset=offset)
         return (code, message)
 
+
+def initMessage(klass):
+    if klass._header_format is not None:
+        klass._header_len = calcsize(klass._header_format)
 
 StaticRegistry = {}
 def register(code, request, answer=None):
@@ -1243,10 +1480,12 @@
     # register the request
     # assert code & RESPONSE_MASK == 0
     assert code not in StaticRegistry, "Duplicate request packet code"
+    initMessage(request)
     request._code = code
     request._answer = answer
     StaticRegistry[code] = request
     if answer not in (None, Error):
+        initMessage(answer)
         # compute the answer code
         code = code | RESPONSE_MASK
         answer._request = request





More information about the Neo-report mailing list