Skip to content

Commit 0a1c3c2

Browse files
committed
Prevent looping over sequences of images and strings from allocating memory in the display ticker.
1 parent 2155ab3 commit 0a1c3c2

File tree

6 files changed

+136
-18
lines changed

6 files changed

+136
-18
lines changed

inc/genhdr/qstrdefs.generated.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ QDEF(MP_QSTR_ScrollingString, (const byte*)"\xbd\x0f" "ScrollingString")
468468
QDEF(MP_QSTR_on, (const byte*)"\x64\x02" "on")
469469
QDEF(MP_QSTR_off, (const byte*)"\x8a\x03" "off")
470470
QDEF(MP_QSTR_is_on, (const byte*)"\x61\x05" "is_on")
471+
QDEF(MP_QSTR_Facade, (const byte*)"\xc1\x06" "Facade")
471472
QDEF(MP_QSTR_MicroBitButton, (const byte*)"\x16\x0e" "MicroBitButton")
472473
QDEF(MP_QSTR_button_a, (const byte*)"\xed\x08" "button_a")
473474
QDEF(MP_QSTR_button_b, (const byte*)"\xee\x08" "button_b")

inc/microbit/microbitimage.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ typedef union _microbit_image_obj_t {
6262

6363
} microbit_image_obj_t;
6464

65+
/** Return a facade object that presents the string as a sequence of images */
66+
mp_obj_t microbit_string_facade(mp_obj_t string);
67+
68+
void microbit_image_set_from_char(greyscale_t *img, char c);
6569
microbit_image_obj_t *microbit_image_for_char(char c);
6670
mp_obj_t microbit_image_slice(microbit_image_obj_t *img, mp_int_t start, mp_int_t width, mp_int_t stride);
6771
/* ref exists so that we can pull a string out of an object and not have it GC'ed while oterating over it */
@@ -84,7 +88,6 @@ extern const monochrome_5by5_t microbit_const_image_heart_obj;
8488
#define BLANK_IMAGE (microbit_image_obj_t *)(&microbit_blank_image)
8589
#define HEART_IMAGE (microbit_image_obj_t *)(&microbit_const_image_heart_obj)
8690

87-
microbit_image_obj_t *microbit_image_for_char(char c);
8891
microbit_image_obj_t *microbit_image_dim(microbit_image_obj_t *lhs, mp_float_t fval);
8992
microbit_image_obj_t *microbit_image_sum(microbit_image_obj_t *lhs, microbit_image_obj_t *rhs, bool add);
9093

inc/microbit/qstrdefsport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ Q(ScrollingString)
152152
Q(on)
153153
Q(off)
154154
Q(is_on)
155+
Q(Facade)
155156

156157
Q(MicroBitButton)
157158
Q(button_a)

source/lib/iters.c

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* The MIT License (MIT)
55
*
6-
* Copyright (c) 2015 Mark Shannon
6+
* Copyright (c) 2015/6 Mark Shannon
77
*
88
* Permission is hereby granted, free of charge, to any person obtaining a copy
99
* of this software and associated documentation files (the "Software"), to deal
@@ -27,23 +27,20 @@
2727
#include "py/runtime.h"
2828
#include "lib/iters.h"
2929

30+
3031
typedef struct _repeat_iterator_t {
3132
mp_obj_base_t base;
3233
mp_obj_t iterable;
33-
mp_obj_t iterator;
34+
mp_int_t index;
3435
} repeat_iterator_t;
3536

36-
STATIC mp_obj_t microbit_repeat_iter_next(mp_obj_t iter_in) {
37+
static mp_obj_t microbit_repeat_iter_next(mp_obj_t iter_in) {
3738
repeat_iterator_t *iter = (repeat_iterator_t *)iter_in;
38-
mp_obj_t result = mp_iternext(iter->iterator);
39-
if (result == MP_OBJ_STOP_ITERATION) {
40-
iter->iterator = mp_getiter(iter->iterable);
41-
result = mp_iternext(iter->iterator);
42-
if (result == MP_OBJ_STOP_ITERATION) {
43-
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "cannot repeat empty sequence"));
44-
}
39+
iter->index++;
40+
if (iter->index >= mp_obj_get_int(mp_obj_len(iter->iterable))) {
41+
iter->index = 0;
4542
}
46-
return result;
43+
return mp_obj_subscr(iter->iterable, MP_OBJ_NEW_SMALL_INT(iter->index), MP_OBJ_SENTINEL);
4744
}
4845

4946
const mp_obj_type_t microbit_repeat_iterator_type = {
@@ -65,10 +62,9 @@ const mp_obj_type_t microbit_repeat_iterator_type = {
6562
};
6663

6764
mp_obj_t microbit_repeat_iterator(mp_obj_t iterable) {
68-
mp_obj_t iter = mp_getiter(iterable);
6965
repeat_iterator_t *result = m_new_obj(repeat_iterator_t);
7066
result->base.type = &microbit_repeat_iterator_type;
7167
result->iterable = iterable;
72-
result->iterator = iter;
68+
result->index = -1;
7369
return result;
7470
}

source/microbit/microbitdisplay.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ mp_obj_t microbit_display_show_func(mp_uint_t n_args, const mp_obj_t *pos_args,
105105
goto single_image_immediate;
106106
}
107107
}
108+
image = microbit_string_facade(image);
108109
} else if (mp_obj_get_type(image) == &microbit_image_type) {
109110
if (!clear && !loop) {
110111
goto single_image_immediate;
@@ -381,9 +382,10 @@ void microbit_display_animate(microbit_display_obj_t *self, mp_obj_t iterable, m
381382
MP_STATE_PORT(async_data)[0] = self; // so it doesn't get GC'd
382383
MP_STATE_PORT(async_data)[1] = async_iterator;
383384
wakeup_event = false;
384-
async_mode = ASYNC_MODE_ANIMATION;
385385
mp_obj_t obj = mp_iternext_allow_raise(async_iterator);
386386
draw_object(obj);
387+
async_tick = 0;
388+
async_mode = ASYNC_MODE_ANIMATION;
387389
if (wait) {
388390
wait_for_event();
389391
}

source/microbit/microbitimage.cpp

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,14 +564,19 @@ STATIC mp_int_t get_pixel_from_font_data(const unsigned char *data, int x, int y
564564
return ((data[y]>>(4-x))&1);
565565
}
566566

567-
microbit_image_obj_t *microbit_image_for_char(char c) {
567+
void microbit_image_set_from_char(greyscale_t *img, char c) {
568568
const unsigned char *data = get_font_data_from_char(c);
569-
greyscale_t *result = greyscale_new(5,5);
570569
for (int x = 0; x < 5; ++x) {
571570
for (int y = 0; y < 5; ++y) {
572-
result->setPixelValue(x, y, get_pixel_from_font_data(data, x, y)*MAX_BRIGHTNESS);
571+
img->setPixelValue(x, y, get_pixel_from_font_data(data, x, y)*MAX_BRIGHTNESS);
573572
}
574573
}
574+
}
575+
576+
577+
microbit_image_obj_t *microbit_image_for_char(char c) {
578+
greyscale_t *result = greyscale_new(5,5);
579+
microbit_image_set_from_char(result, c);
575580
return (microbit_image_obj_t *)result;
576581
}
577582

@@ -799,4 +804,114 @@ const mp_obj_type_t microbit_scrolling_string_iterator_type = {
799804
.locals_dict = NULL,
800805
};
801806

807+
/** Facade types to present a string as a sequence of images.
808+
* These are necessary to avoid allocation during iteration,
809+
* which may happen in interrupt handlers.
810+
*/
811+
812+
typedef struct _string_image_facade_t {
813+
mp_obj_base_t base;
814+
mp_obj_t string;
815+
greyscale_t *image;
816+
} string_image_facade_t;
817+
818+
static mp_obj_t string_image_facade_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
819+
if (value == MP_OBJ_SENTINEL) {
820+
// Fill in image
821+
string_image_facade_t *self = (string_image_facade_t *)self_in;
822+
mp_uint_t len;
823+
const char *text = mp_obj_str_get_data(self->string, &len);
824+
mp_uint_t index = mp_get_index(self->base.type, len, index_in, false);
825+
microbit_image_set_from_char(self->image, text[index]);
826+
return self->image;
827+
} else {
828+
return MP_OBJ_NULL; // op not supported
829+
}
830+
}
831+
832+
static mp_obj_t facade_unary_op(mp_uint_t op, mp_obj_t self_in) {
833+
string_image_facade_t *self = (string_image_facade_t *)self_in;
834+
switch (op) {
835+
case MP_UNARY_OP_LEN:
836+
return mp_obj_len(self->string);
837+
default: return MP_OBJ_NULL; // op not supported
838+
}
839+
}
840+
841+
static mp_obj_t microbit_facade_iterator(mp_obj_t iterable);
842+
843+
const mp_obj_type_t string_image_facade_type = {
844+
{ &mp_type_type },
845+
.name = MP_QSTR_Facade,
846+
.print = NULL,
847+
.make_new = NULL,
848+
.call = NULL,
849+
.unary_op = facade_unary_op,
850+
.binary_op = NULL,
851+
.attr = NULL,
852+
.subscr = string_image_facade_subscr,
853+
.getiter = microbit_facade_iterator,
854+
.iternext = NULL,
855+
.buffer_p = {NULL},
856+
.stream_p = NULL,
857+
.bases_tuple = NULL,
858+
NULL
859+
};
860+
861+
862+
typedef struct _facade_iterator_t {
863+
mp_obj_base_t base;
864+
mp_obj_t string;
865+
mp_uint_t index;
866+
greyscale_t *image;
867+
} facade_iterator_t;
868+
869+
mp_obj_t microbit_string_facade(mp_obj_t string) {
870+
string_image_facade_t *result = m_new_obj(string_image_facade_t);
871+
result->base.type = &string_image_facade_type;
872+
result->string = string;
873+
result->image = greyscale_new(5,5);
874+
return result;
875+
}
876+
877+
static mp_obj_t microbit_facade_iter_next(mp_obj_t iter_in) {
878+
facade_iterator_t *iter = (facade_iterator_t *)iter_in;
879+
mp_uint_t len;
880+
const char *text = mp_obj_str_get_data(iter->string, &len);
881+
if (iter->index >= len) {
882+
return MP_OBJ_STOP_ITERATION;
883+
}
884+
microbit_image_set_from_char(iter->image, text[iter->index]);
885+
iter->index++;
886+
return iter->image;
887+
}
888+
889+
const mp_obj_type_t microbit_facade_iterator_type = {
890+
{ &mp_type_type },
891+
.name = MP_QSTR_iterator,
892+
.print = NULL,
893+
.make_new = NULL,
894+
.call = NULL,
895+
.unary_op = NULL,
896+
.binary_op = NULL,
897+
.attr = NULL,
898+
.subscr = NULL,
899+
.getiter = mp_identity,
900+
.iternext = microbit_facade_iter_next,
901+
.buffer_p = {NULL},
902+
.stream_p = NULL,
903+
.bases_tuple = NULL,
904+
NULL
905+
};
906+
907+
mp_obj_t microbit_facade_iterator(mp_obj_t iterable_in) {
908+
facade_iterator_t *result = m_new_obj(facade_iterator_t);
909+
string_image_facade_t *iterable = (string_image_facade_t *)iterable_in;
910+
result->base.type = &microbit_facade_iterator_type;
911+
result->string = iterable->string;
912+
result->image = iterable->image;
913+
result->index = 0;
914+
return result;
915+
}
916+
802917
}

0 commit comments

Comments
 (0)