Skip to content

Commit bb4dcd9

Browse files
authored
Merge pull request #750 from effigies/fix/gifti_buffers
FIX: Ensure loaded GIFTI files expose writable data arrays
2 parents f4847af + 0c79ad1 commit bb4dcd9

File tree

2 files changed

+30
-25
lines changed

2 files changed

+30
-25
lines changed

nibabel/gifti/parse_gifti_fast.py

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,43 +33,41 @@ class GiftiParseError(ExpatError):
3333

3434
def read_data_block(encoding, endian, ordering, datatype, shape, data):
3535
""" Tries to unzip, decode, parse the funny string data """
36-
ord = array_index_order_codes.npcode[ordering]
3736
enclabel = gifti_encoding_codes.label[encoding]
37+
dtype = data_type_codes.type[datatype]
3838
if enclabel == 'ASCII':
3939
# GIFTI_ENCODING_ASCII
4040
c = StringIO(data)
41-
da = np.loadtxt(c)
42-
da = da.astype(data_type_codes.type[datatype])
41+
da = np.loadtxt(c, dtype=dtype)
4342
return da # independent of the endianness
4443

45-
elif enclabel == 'B64BIN':
46-
# GIFTI_ENCODING_B64BIN
47-
dec = base64.b64decode(data.encode('ascii'))
48-
dt = data_type_codes.type[datatype]
49-
sh = tuple(shape)
50-
newarr = np.frombuffer(dec, dtype=dt)
51-
if len(newarr.shape) != len(sh):
52-
newarr = newarr.reshape(sh, order=ord)
53-
54-
elif enclabel == 'B64GZ':
55-
# GIFTI_ENCODING_B64GZ
56-
# convert to bytes array for python 3.2
57-
# http://www.diveintopython3.net/strings.html#byte-arrays
58-
dec = base64.b64decode(data.encode('ascii'))
59-
zdec = zlib.decompress(dec)
60-
dt = data_type_codes.type[datatype]
61-
sh = tuple(shape)
62-
newarr = np.frombuffer(zdec, dtype=dt)
63-
if len(newarr.shape) != len(sh):
64-
newarr = newarr.reshape(sh, order=ord)
65-
6644
elif enclabel == 'External':
6745
# GIFTI_ENCODING_EXTBIN
6846
raise NotImplementedError("In what format are the external files?")
6947

70-
else:
48+
elif enclabel not in ('B64BIN', 'B64GZ'):
7149
return 0
7250

51+
# Numpy arrays created from bytes objects are read-only.
52+
# Neither b64decode nor decompress will return bytearrays, and there
53+
# are not equivalents to fobj.readinto to allow us to pass them, so
54+
# there is not a simple way to avoid making copies.
55+
# If this becomes a problem, we should write a decoding interface with
56+
# a tunable chunk size.
57+
dec = base64.b64decode(data.encode('ascii'))
58+
if enclabel == 'B64BIN':
59+
# GIFTI_ENCODING_B64BIN
60+
buff = bytearray(dec)
61+
else:
62+
# GIFTI_ENCODING_B64GZ
63+
buff = bytearray(zlib.decompress(dec))
64+
del dec
65+
66+
sh = tuple(shape)
67+
newarr = np.frombuffer(buff, dtype=dtype)
68+
if len(newarr.shape) != len(sh):
69+
newarr = newarr.reshape(sh, order=array_index_order_codes.npcode[ordering])
70+
7371
# check if we need to byteswap
7472
required_byteorder = gifti_endian_codes.byteorder[endian]
7573
if (required_byteorder in ('big', 'little') and

nibabel/gifti/tests/test_parse_gifti_fast.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,13 @@ def test_readwritedata():
268268
assert_array_almost_equal(img.darrays[0].data,
269269
img2.darrays[0].data)
270270

271+
def test_modify_darray():
272+
for fname in (DATA_FILE1, DATA_FILE2, DATA_FILE5):
273+
img = load(fname)
274+
darray = img.darrays[0]
275+
darray.data[:] = 0
276+
assert_true(np.array_equiv(darray.data, 0))
277+
271278

272279
def test_write_newmetadata():
273280
img = gi.GiftiImage()

0 commit comments

Comments
 (0)