Skip to content

Commit 666c27e

Browse files
committed
pybricks.iodevices.I2CDevice: Use bytes obj as buffer.
This way we don't need to add the `max_read_size` parameter and only allocate as much as we need.
1 parent ef904f5 commit 666c27e

File tree

1 file changed

+26
-26
lines changed

1 file changed

+26
-26
lines changed

pybricks/iodevices/pb_type_iodevices_i2cdevice.c

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#if PYBRICKS_PY_IODEVICES && PYBRICKS_PY_IODEVICES_I2CDEVICE
77

88
#include "py/mphal.h"
9+
#include "py/objstr.h"
10+
911

1012
#include <pbdrv/i2c.h>
1113
#include <pbio/port_interface.h>
@@ -31,18 +33,19 @@ typedef struct {
3133
pbio_os_state_t state;
3234
uint8_t address;
3335
bool nxt_quirk;
34-
size_t read_len;
3536
size_t write_len;
36-
size_t read_buf_len;
37-
uint8_t read_buf[];
37+
/**
38+
* The read buffer is allocated as a bytes object when the operation
39+
* begins. It is returned to the user on completion.
40+
*/
41+
mp_obj_str_t *read_result;
3842
} device_obj_t;
3943

4044
// pybricks.iodevices.I2CDevice.__init__
4145
static mp_obj_t make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
4246
PB_PARSE_ARGS_CLASS(n_args, n_kw, args,
4347
PB_ARG_REQUIRED(port),
4448
PB_ARG_REQUIRED(address),
45-
PB_ARG_DEFAULT_INT(max_read_size, 32),
4649
PB_ARG_DEFAULT_FALSE(custom),
4750
PB_ARG_DEFAULT_FALSE(powered),
4851
PB_ARG_DEFAULT_FALSE(nxt_quirk)
@@ -69,11 +72,9 @@ static mp_obj_t make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
6972
pbdrv_i2c_dev_t *i2c_dev;
7073
pb_assert(pbio_port_get_i2c_dev(port, &i2c_dev));
7174

72-
size_t read_buf_len = mp_obj_get_int(max_read_size_in);
73-
device_obj_t *self = mp_obj_malloc_var(device_obj_t, read_buf, uint8_t, read_buf_len, type);
75+
device_obj_t *self = mp_obj_malloc(device_obj_t, type);
7476
self->i2c_dev = i2c_dev;
7577
self->address = mp_obj_get_int(address_in);
76-
self->read_buf_len = read_buf_len;
7778
self->nxt_quirk = mp_obj_is_true(nxt_quirk_in);
7879
if (mp_obj_is_true(powered_in)) {
7980
pbio_port_p1p2_set_power(port, PBIO_PORT_POWER_REQUIREMENTS_BATTERY_VOLTAGE_P1_POS);
@@ -105,14 +106,17 @@ static pbio_error_t operation_iterate_once(device_obj_t *device) {
105106
device->address,
106107
NULL, // Already memcpy'd on initial iteration. No need to provide here.
107108
device->write_len,
108-
device->read_buf,
109-
device->read_len,
109+
(uint8_t *)device->read_result->data,
110+
device->read_result->len,
110111
device->nxt_quirk
111112
);
112113
}
113114

114-
static mp_obj_t operation_get_return_obj(device_obj_t *device) {
115-
return mp_obj_new_bytes(device->read_buf, device->read_len);
115+
static mp_obj_t operation_get_result_obj(device_obj_t *device) {
116+
mp_obj_t result = MP_OBJ_FROM_PTR(device->read_result);
117+
// The device should not hold up garbage collection of the result.
118+
device->read_result = MP_OBJ_NULL;
119+
return result;
116120
}
117121

118122
static mp_obj_t operation_iternext(mp_obj_t self_in) {
@@ -129,15 +133,8 @@ static mp_obj_t operation_iternext(mp_obj_t self_in) {
129133
// Raises on Timeout and other I/O errors. Proceeds on success.
130134
pb_assert(err);
131135

132-
mp_obj_t result = operation_get_return_obj(device);
133-
134-
// None is treated as a special case.
135-
if (result == mp_const_none) {
136-
return MP_OBJ_STOP_ITERATION;
137-
}
138-
139-
// Otherwise, set return value via stop iteration.
140-
return mp_make_stop_iteration(result);
136+
// Set return value via stop iteration.
137+
return mp_make_stop_iteration(operation_get_result_obj(device));
141138
}
142139

143140
static const mp_rom_map_elem_t operation_locals_dict_table[] = {
@@ -162,17 +159,19 @@ static mp_obj_t write_then_read(size_t n_args, const mp_obj_t *pos_args, mp_map_
162159
uint8_t *write_data = (uint8_t *)mp_obj_str_get_data(write_data_in, &self->write_len);
163160

164161
self->state = 0;
165-
self->read_len = mp_obj_get_int(read_size_in);
166-
if (self->read_len > self->read_buf_len) {
167-
self->read_len = self->read_buf_len;
168-
}
162+
163+
// Pre-allocate the return value so we have something to write the result to.
164+
self->read_result = mp_obj_malloc(mp_obj_str_t, &mp_type_bytes);
165+
self->read_result->len = mp_obj_get_int(read_size_in);
166+
self->read_result->hash = 0;
167+
self->read_result->data = m_new(byte, self->read_result->len);
169168

170169
// Kick off the operation. This will immediately raise if a transaction is
171170
// in progress.
172171
pbio_error_t err = pbdrv_i2c_write_then_read(
173172
&self->state, self->i2c_dev, self->address,
174173
write_data, self->write_len,
175-
self->read_buf, self->read_len, self->nxt_quirk);
174+
(uint8_t *)self->read_result->data, self->read_result->len, self->nxt_quirk);
176175

177176
// Expect yield after the initial call.
178177
if (err == PBIO_SUCCESS) {
@@ -193,7 +192,8 @@ static mp_obj_t write_then_read(size_t n_args, const mp_obj_t *pos_args, mp_map_
193192
MICROPY_EVENT_POLL_HOOK;
194193
}
195194
pb_assert(err);
196-
return operation_get_return_obj(self);
195+
196+
return operation_get_result_obj(self);
197197
}
198198
// See also experimental_globals_table below. This function object is added there to make it importable.
199199
static MP_DEFINE_CONST_FUN_OBJ_KW(write_then_read_obj, 0, write_then_read);

0 commit comments

Comments
 (0)