kirr has uploaded this change for review.

View Change

trx_toolkit/data_msg: Optimize RxMsg/TxMsg parsing

Thread all calls related in parsing to be done via C-level and with
explicit types. Leverage bytearray ability to cut prefix and suffix
efficiently. Replace struct.unpack with custom C-level functions to
deserialize big-endian integers. Use raw C pointers to access bytearray
data to avoid related py-index overhead.

Change-Id: I661a414bf5091fa6b872831ba911465a8d073397
---
M src/target/trx_toolkit/data_if.pyx
M src/target/trx_toolkit/data_msg.pxd
M src/target/trx_toolkit/data_msg.pyx
3 files changed, 69 insertions(+), 32 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/78/40078/1
diff --git a/src/target/trx_toolkit/data_if.pyx b/src/target/trx_toolkit/data_if.pyx
index c355666..71a1878 100644
--- a/src/target/trx_toolkit/data_if.pyx
+++ b/src/target/trx_toolkit/data_if.pyx
@@ -65,7 +65,7 @@
# Attempt to parse a TRXD Tx message
try:
msg = TxMsg()
- msg.parse_msg(data)
+ msg._parse_msg(data)
except:
log.error("Failed to parse a TRXD Tx message "
"from R:%s" % self.desc_remote())
@@ -85,7 +85,7 @@
# Attempt to parse a TRXD Rx message
try:
msg = RxMsg()
- msg.parse_msg(data)
+ msg._parse_msg(data)
except:
log.error("Failed to parse a TRXD Rx message "
"from R:%s" % self.desc_remote())
diff --git a/src/target/trx_toolkit/data_msg.pxd b/src/target/trx_toolkit/data_msg.pxd
index 2823b82..6823397 100644
--- a/src/target/trx_toolkit/data_msg.pxd
+++ b/src/target/trx_toolkit/data_msg.pxd
@@ -32,8 +32,9 @@

cdef append_hdr_to(self, bytearray buf)
cdef append_burst_to(self, bytearray buf)
- cdef parse_hdr(self, msg)
- cdef parse_burst(self, burst)
+ cdef parse_hdr(self, bytearray msg)
+ cdef parse_burst(self, bytearray burst)
+ cdef int _parse_msg(self, bytearray msg) except -1
cdef _validate(self)

@staticmethod
@@ -84,5 +85,5 @@
cpdef validate(self)
cdef int gen_mts(self) except -1

- cdef _parse_burst_v0(self, burst)
+ cdef _parse_burst_v0(self, bytearray burst)
cdef int parse_mts(self, uint8_t mts) except -1
diff --git a/src/target/trx_toolkit/data_msg.pyx b/src/target/trx_toolkit/data_msg.pyx
index 5929b47..ac3dc49 100644
--- a/src/target/trx_toolkit/data_msg.pyx
+++ b/src/target/trx_toolkit/data_msg.pyx
@@ -20,6 +20,7 @@
# GNU General Public License for more details.

from cython cimport final
+from libc.stdint cimport int16_t, uint16_t, uint32_t

from cpython cimport array
cdef array.array _Bempty = array.array('B', [])
@@ -101,7 +102,7 @@
''' Generate message specific header by appending it to buf. '''
raise NotImplementedError

- cdef parse_hdr(self, msg):
+ cdef parse_hdr(self, bytearray msg):
''' Parse message specific header. '''
raise NotImplementedError

@@ -109,8 +110,11 @@
''' Generate message specific burst by appending it to buf. '''
raise NotImplementedError

- cdef parse_burst(self, burst):
- ''' Parse message specific burst to burst. '''
+ cdef parse_burst(self, bytearray burst):
+ ''' Parse message specific burst to burst.
+
+ burst bytearray can be trimmed in-place to leave only burst data.
+ '''
raise NotImplementedError

@abc.abstractmethod
@@ -267,35 +271,43 @@
return buf

def parse_msg(self, msg):
+ if type(msg) is not bytearray:
+ msg = bytearray(msg)
+ self._parse_msg(msg)
+ cdef int _parse_msg(self, bytearray msg) except -1:
''' Parse a TRX DATA message. '''
+ cdef uint8_t *msgb = <uint8_t*>PyByteArray_AS_STRING(msg)
+ cdef Py_ssize_t msgl = len(msg)

# Make sure we have at least common header
- if len(msg) < Msg.CHDR_LEN():
+ if msgl < Msg.CHDR_LEN():
raise ValueError("Message is to short: missing common header")

# Parse the header version first
- self.ver = (msg[0] >> 4)
+ self.ver = (msgb[0] >> 4)
if not (0 <= self.ver <= KNOWN_VERSION_MAX):
raise ValueError("Unknown TRXD header version %d" % self.ver)

# Parse TDMA TN and FN
- self.tn = (msg[0] & 0x07)
- self.fn = struct.unpack(">L", msg[1:5])[0]
+ self.tn = (msgb[0] & 0x07)
+ self.fn = _unpack_u32(&msgb[1])

# Make sure we have the whole header,
# including the version specific fields
- if len(msg) < self.HDR_LEN():
+ hlen = self.HDR_LEN()
+ if msgl < hlen:
raise ValueError("Message is to short: missing version specific header")

# Specific message part
self.parse_hdr(msg)

- # Copy burst, skipping header
- if len(msg) == self.HDR_LEN():
+ # Extract burst, skipping header
+ # reuse original msg bytearray for burst data
+ if msgl == hlen:
self.burst = None
- return
- msg_burst = memoryview(msg)[self.HDR_LEN():]
- self.parse_burst(msg_burst)
+ return 0
+ del msg[:hlen] # NOTE bytearray handles prefix delete efficiently
+ self.parse_burst(msg)


# Constants
@@ -379,8 +391,10 @@
# Put power
buf.append(self.pwr)

- cdef parse_hdr(self, hdr):
+ cdef parse_hdr(self, bytearray msg):
''' Parse message specific header part. '''
+ cdef uint8_t *hdr = <uint8_t*>PyByteArray_AS_STRING(msg)
+ # NOTE header lenght is already pre-checked by _parse_msg

# Parse power level
self.pwr = hdr[5]
@@ -391,20 +405,22 @@
# Copy burst 'as is'
return buf.extend(self.burst)

- cdef parse_burst(self, burst):
+ cdef parse_burst(self, bytearray burst):
''' Parse message specific burst. '''

+ # trim msg down with removing everything besides burst data
+ # bytearray handles prefix and suffix removal efficiently
length = len(burst)

# Distinguish between GSM and EDGE
if length >= EDGE_BURST_LEN:
if length > EDGE_BURST_LEN:
- burst = memoryview(burst)[:EDGE_BURST_LEN]
+ del burst[EDGE_BURST_LEN:]
else:
if length > GMSK_BURST_LEN:
- burst = memoryview(burst)[:GMSK_BURST_LEN]
+ del burst[GMSK_BURST_LEN:]

- self.burst = bytearray(burst)
+ self.burst = burst

def rand_burst(self, length = GMSK_BURST_LEN):
''' Generate a random message specific burst. '''
@@ -688,21 +704,23 @@
# C/I: Carrier-to-Interference ratio (in centiBels)
buf += struct.pack(">h", self.ci)

- cdef parse_hdr(self, hdr):
+ cdef parse_hdr(self, bytearray msg):
''' Parse message specific header part. '''
+ cdef uint8_t *hdr = <uint8_t*>PyByteArray_AS_STRING(msg)
+ # NOTE header lenght is already pre-checked by _parse_msg

# Parse RSSI
self.rssi = -(hdr[5])

# Parse ToA (Time of Arrival)
- self.toa256 = struct.unpack(">h", hdr[6:8])[0]
+ self.toa256 = _unpack_s16(&hdr[6])

if self.ver >= 0x01:
# Modulation and Training Sequence info
self.parse_mts(hdr[8])

# C/I: Carrier-to-Interference ratio (in centiBels)
- self.ci = struct.unpack(">h", hdr[9:11])[0]
+ self.ci = _unpack_s16(&hdr[9])

cdef append_burst_to(self, bytearray buf):
''' Generate message specific burst appending it to buf. '''
@@ -713,7 +731,8 @@
Msg.__sbit2usbit(<int8_t*>self.burst.data.as_chars, len(self.burst),
<uint8_t*>&PyByteArray_AS_STRING(buf)[l])

- cdef _parse_burst_v0(self, burst):
+
+ cdef _parse_burst_v0(self, bytearray burst):
''' Parse message specific burst for header version 0. '''

bl = len(burst)
@@ -727,15 +746,13 @@
if self.mod_type is None:
raise ValueError("Odd burst length %u" % bl)

- return burst[:self.mod_type.bl]
+ del burst[self.mod_type.bl:]

- cdef parse_burst(self, burst):
+ cdef parse_burst(self, bytearray burst):
''' Parse message specific burst. '''

- burst = array.array('B', burst)
-
if self.ver == 0x00:
- burst = self._parse_burst_v0(burst)
+ self._parse_burst_v0(burst)

# Convert unsigned soft-bits to soft-bits
self.burst = Msg._usbit2sbit(burst)
@@ -762,3 +779,22 @@
<uint8_t*>PyByteArray_AS_STRING(msg.burst))

return msg
+
+
+# ---- misc ----
+
+# _unpack_(u|s)(16|32) deserialize big-endian representation of corresponding integer type from memory pointed by b.
+
+cdef int16_t _unpack_s16(const uint8_t *b):
+ cdef uint16_t r = 0
+ r |= (<uint16_t>b[0]) << 8
+ r |= (<uint16_t>b[1]) << 0
+ return <int16_t>r
+
+cdef uint32_t _unpack_u32(const uint8_t *b):
+ cdef uint32_t r = 0
+ r |= (<uint32_t>b[0]) << 24
+ r |= (<uint32_t>b[1]) << 16
+ r |= (<uint32_t>b[2]) << 8
+ r |= (<uint32_t>b[3]) << 0
+ return r

To view, visit change 40078. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Change-Id: I661a414bf5091fa6b872831ba911465a8d073397
Gerrit-Change-Number: 40078
Gerrit-PatchSet: 1
Gerrit-Owner: kirr <kirr@nexedi.com>
Gerrit-CC: fixeria <vyanitskiy@sysmocom.de>
Gerrit-CC: osmith <osmith@sysmocom.de>
Gerrit-CC: pespin <pespin@sysmocom.de>