Skip to content

Commit 0df634e

Browse files
committed
Fixes to string handling
Handle null termination properly and ensure we have a proper copy of the buffer.
1 parent 105d2d3 commit 0df634e

File tree

1 file changed

+18
-4
lines changed

1 file changed

+18
-4
lines changed

softioc/device.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def _compare_values(self, value1, value2):
5252

5353

5454
# This method is called during Out record processing to return the
55-
# underlying value in Python format.
55+
# underlying value in EPICS format.
5656
def _read_value(self, record):
5757
return record.read_val()
5858

@@ -240,6 +240,13 @@ def _Device_Out(*args, **kargs):
240240
mbbo = _Device_Out('mbbo', c_uint16, fields.DBF_SHORT, NO_CONVERT)
241241

242242

243+
def _string_at(value, count):
244+
# Need string_at() twice to ensure string is size limited *and* null
245+
# terminated.
246+
value = ctypes.string_at(ctypes.string_at(value, count))
247+
# Convert bytes to unicode string
248+
return value.decode(errors = 'replace')
249+
243250
class EpicsString:
244251
_fields_ = ['UDF', 'VAL']
245252
_epics_rc_ = EPICS_OK
@@ -249,13 +256,20 @@ class EpicsString:
249256
def _value_to_epics(self, value):
250257
# It's a little odd: we can't simply construct a value from the byte
251258
# string, but we can update the array in an existing value.
252-
# Value being written must be a string.
259+
# Value being written must be a string, and will be automatically null
260+
# terminated where possible.
253261
result = self._ctype_()
254262
result.value = value.encode()
255263
return result
256264

257265
def _epics_to_value(self, epics):
258-
return epics.value.decode(errors = 'replace')
266+
return _string_at(epics, 40)
267+
268+
def _read_value(self, record):
269+
# For strings we need to take a copy of the value read
270+
result = self._ctype_()
271+
result.value = record.read_val().value
272+
return result
259273

260274

261275
class stringin(EpicsString, ProcessDeviceSupportIn):
@@ -400,7 +414,7 @@ def _value_to_epics(self, value):
400414
return encode_string(value, getattr(self, '_nelm', None))
401415

402416
def _epics_to_value(self, value):
403-
return value.decode(errors = 'replace')
417+
return _string_at(value.ctypes, len(value))
404418

405419

406420
class long_stringin(LongStringBase, ProcessDeviceSupportIn):

0 commit comments

Comments
 (0)