Skip to content

Commit

Permalink
py/objstringio: If created from immutable object, follow copy on writ…
Browse files Browse the repository at this point in the history
…e policy.

Don't create copy of immutable object's contents until .write() is called
on BytesIO.
  • Loading branch information
pfalcon committed Jun 9, 2017
1 parent b24ccfc commit 07241cd
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 3 deletions.
30 changes: 27 additions & 3 deletions py/objstringio.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,23 @@ STATIC mp_uint_t stringio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *er
return size;
}

STATIC void stringio_copy_on_write(mp_obj_stringio_t *o) {
const void *buf = o->vstr->buf;
o->vstr->buf = m_new(char, o->vstr->len);
memcpy(o->vstr->buf, buf, o->vstr->len);
o->vstr->fixed_buf = false;
o->ref_obj = MP_OBJ_NULL;
}

STATIC mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
(void)errcode;
mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
check_stringio_is_open(o);

if (o->vstr->fixed_buf) {
stringio_copy_on_write(o);
}

mp_uint_t new_pos = o->pos + size;
if (new_pos < size) {
// Writing <size> bytes will overflow o->pos beyond limit of mp_uint_t.
Expand Down Expand Up @@ -155,11 +168,11 @@ STATIC mp_obj_t stringio___exit__(size_t n_args, const mp_obj_t *args) {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stringio___exit___obj, 4, 4, stringio___exit__);

STATIC mp_obj_stringio_t *stringio_new(const mp_obj_type_t *type, mp_uint_t alloc) {
STATIC mp_obj_stringio_t *stringio_new(const mp_obj_type_t *type) {
mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t);
o->base.type = type;
o->vstr = vstr_new(alloc);
o->pos = 0;
o->ref_obj = MP_OBJ_NULL;
return o;
}

Expand All @@ -170,17 +183,28 @@ STATIC mp_obj_t stringio_make_new(const mp_obj_type_t *type_in, size_t n_args, s
bool initdata = false;
mp_buffer_info_t bufinfo;

mp_obj_stringio_t *o = stringio_new(type_in);

if (n_args > 0) {
if (MP_OBJ_IS_INT(args[0])) {
sz = mp_obj_get_int(args[0]);
} else {
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);

if (MP_OBJ_IS_STR_OR_BYTES(args[0])) {
o->vstr = m_new_obj(vstr_t);
vstr_init_fixed_buf(o->vstr, bufinfo.len, bufinfo.buf);
o->vstr->len = bufinfo.len;
o->ref_obj = args[0];
return MP_OBJ_FROM_PTR(o);
}

sz = bufinfo.len;
initdata = true;
}
}

mp_obj_stringio_t *o = stringio_new(type_in, sz);
o->vstr = vstr_new(sz);

if (initdata) {
stringio_write(MP_OBJ_FROM_PTR(o), bufinfo.buf, bufinfo.len, NULL);
Expand Down
2 changes: 2 additions & 0 deletions py/objstringio.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ typedef struct _mp_obj_stringio_t {
vstr_t *vstr;
// StringIO has single pointer used for both reading and writing
mp_uint_t pos;
// Underlying object buffered by this StringIO
mp_obj_t ref_obj;
} mp_obj_stringio_t;

#endif // MICROPY_INCLUDED_PY_OBJSTRINGIO_H
20 changes: 20 additions & 0 deletions tests/io/bytesio_cow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Make sure that write operations on io.BytesIO don't
# change original object it was constructed from.
try:
import uio as io
except ImportError:
import io

b = b"foobar"

a = io.BytesIO(b)
a.write(b"1")
print(b)
print(a.getvalue())

b = bytearray(b"foobar")

a = io.BytesIO(b)
a.write(b"1")
print(b)
print(a.getvalue())
20 changes: 20 additions & 0 deletions tests/micropython/heapalloc_bytesio2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Creating BytesIO from immutable object should not immediately
# copy its content.
try:
import uio
import micropython
micropython.mem_total
except (ImportError, AttributeError):
print("SKIP")
raise SystemExit


data = b"1234" * 256

before = micropython.mem_total()

buf = uio.BytesIO(data)

after = micropython.mem_total()

print(after - before < len(data))
1 change: 1 addition & 0 deletions tests/micropython/heapalloc_bytesio2.py.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
True

0 comments on commit 07241cd

Please sign in to comment.