From 057158c9f3a31a5c87902f9705c690c4f14a74bb Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sat, 24 Oct 2015 19:28:36 -0700 Subject: [PATCH 01/31] Cache getSerial(); otherwise BLE information device initalization may crash --- inc/MicroBit.h | 1 + source/MicroBit.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/inc/MicroBit.h b/inc/MicroBit.h index b1651de..1e97ad0 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -77,6 +77,7 @@ class MicroBit void seedRandom(); uint32_t randomValue; + ManagedString serialCache; public: diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index 07634d0..bc170ee 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -221,6 +221,9 @@ ManagedString MicroBit::getName() */ ManagedString MicroBit::getSerial() { + if (serialCache.length() > 0) + return serialCache; + // We take to 16 bit numbers here, as we want the full range of ID bits, but don't want negative numbers... int n1 = NRF_FICR->DEVICEID[1] & 0xffff; int n2 = (NRF_FICR->DEVICEID[1] >> 16) & 0xffff; @@ -229,7 +232,9 @@ ManagedString MicroBit::getSerial() ManagedString s1 = ManagedString(n1); ManagedString s2 = ManagedString(n2); - return s1+s2; + serialCache = s1 + s2; + + return serialCache; } /** From 5a10bda5618a940a39460a119fa4a424d5741ae5 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sat, 24 Oct 2015 19:34:04 -0700 Subject: [PATCH 02/31] First stab at more memory efficient strings --- inc/ManagedString.h | 56 +++++++++++-- source/ManagedString.cpp | 166 +++++++++++++++++---------------------- 2 files changed, 120 insertions(+), 102 deletions(-) diff --git a/inc/ManagedString.h b/inc/ManagedString.h index 81c06bf..d2432ea 100644 --- a/inc/ManagedString.h +++ b/inc/ManagedString.h @@ -3,6 +3,29 @@ #include "mbed.h" +struct RefCounted +{ +public: + uint16_t refcnt; + uint16_t size; + + void incr(); + void decr(); + +}; + +class VirtualRefCounted : RefCounted +{ +public: + virtual ~VirtualRefCounted(); +}; + +struct StringData : RefCounted +{ + char data[0]; +}; + + /** * Class definition for a ManagedString. * @@ -19,16 +42,28 @@ class ManagedString { // Internally we record the string as a char *, but control access to this to proide immutability // and reference counting. - char *data; - int16_t *ref; - int16_t len; + StringData *ptr; public: + /** + * Constructor. + * Create a managed string from a specially prepared string literal. + * + * @param ptr The literal - first two bytes should be 0xff, then the length in little endian, then the literal. The literal has to be 4-byte aligned. + * + * Example: + * @code + * static const char hello[] __attribute__ ((aligned (4))) = "\xff\xff\x05\x00" "Hello"; + * ManagedString s((StringData*)(void*)hello); + * @endcode + */ + ManagedString(StringData *p) : ptr(p) {} + /** * Constructor. * Create a managed string from a pointer to an 8-bit character buffer. - * The buffer is copied to ensure sage memory management (the supplied + * The buffer is copied to ensure safe memory management (the supplied * character buffer may be decalred on the stack for instance). * * @param str The character array on which to base the new ManagedString. @@ -251,11 +286,15 @@ class ManagedString /** - * Provides an immutable 8 bit wide haracter buffer representing this string. + * Provides an immutable 8 bit wide character buffer representing this string. * * @return a pointer to the character buffer. */ - const char *toCharArray(); + const char *toCharArray() const + { + if (ptr->refcnt == 0) panic(243); + return ptr->data; + } /** * Determines the length of this ManagedString in characters. @@ -269,7 +308,10 @@ class ManagedString * print(s.length()) // prints "4" * @endcode */ - int16_t length(); + int16_t length() const + { + return ptr->size; + } /** * Empty String constant diff --git a/source/ManagedString.cpp b/source/ManagedString.cpp index aa5d315..23e8bf3 100644 --- a/source/ManagedString.cpp +++ b/source/ManagedString.cpp @@ -3,6 +3,9 @@ #include "mbed.h" #include "MicroBit.h" +#define printf(...) uBit.serial.printf(__VA_ARGS__) + +static const char empty[] __attribute__ ((aligned (4))) = "\xff\xff\0\0\0"; /** * Internal constructor helper. @@ -10,11 +13,7 @@ */ void ManagedString::initEmpty() { - data = ManagedString::EmptyString.data; - ref = ManagedString::EmptyString.ref; - len = ManagedString::EmptyString.len; - - (*ref)++; + ptr = (StringData*)(void*)empty; } /** @@ -25,11 +24,11 @@ void ManagedString::initString(const char *str) { // Initialise this ManagedString as a new string, using the data provided. // We assume the string is sane, and null terminated. - len = strlen(str); - data = (char *) malloc(len+1); - memcpy(data, str, len+1); - ref = (int16_t *) malloc(sizeof(int16_t)); - *ref = 1; + int len = strlen(str); + ptr = (StringData *) malloc(4+len+1); + ptr->size = len; + ptr->refcnt = 1; + memcpy(ptr->data, str, len+1); } @@ -65,7 +64,6 @@ ManagedString::ManagedString(const int value) */ ManagedString::ManagedString(const char value) { - char str[2] = {value, 0}; initString(str); } @@ -82,7 +80,7 @@ ManagedString::ManagedString(const char value) ManagedString::ManagedString(const char *str) { // Sanity check. Return EmptyString for anything distasteful - if ((str == NULL || *str == 0) && this != &ManagedString::EmptyString) + if (str == NULL || *str == 0) { initEmpty(); return; @@ -94,19 +92,17 @@ ManagedString::ManagedString(const char *str) ManagedString::ManagedString(const ManagedString &s1, const ManagedString &s2) { // Calculate length of new string. - len = s1.len + s2.len; + int len = s1.length() + s2.length(); // Create a new buffer for holding the new string data. - data = (char *) malloc(len+1); + ptr = (StringData*) malloc(4+len+1); + ptr->size = len; + ptr->refcnt = 1; // Enter the data, and terminate the string. - memcpy(data, s1.data, s1.len); - memcpy(data + s1.len, s2.data, s2.len); - data[len] = 0; - - // Initialise the ref count and we're done. - ref = (int16_t *) malloc(sizeof(int16_t)); - *ref = 1; + memcpy(ptr->data, s1.toCharArray(), s1.length()); + memcpy(ptr->data + s1.length(), s2.toCharArray(), s2.length()); + ptr->data[len] = 0; } @@ -133,17 +129,14 @@ ManagedString::ManagedString(const char *str, const int16_t length) return; } - // Store the length of the new string - len = length; // Allocate a new buffer, and create a NULL terminated string. - data = (char *) malloc(len+1); - memcpy(data, str, len); - data[len] = 0; - - // Initialize a refcount and we're done. - ref = (int16_t *) malloc(sizeof(int16_t)); - *ref = 1; + ptr = (StringData*) malloc(4+length+1); + // Store the length of the new string + ptr->size = length; + ptr->refcnt = 1; + memcpy(ptr->data, str, length); + ptr->data[length] = 0; } /** @@ -161,11 +154,8 @@ ManagedString::ManagedString(const char *str, const int16_t length) */ ManagedString::ManagedString(const ManagedString &s) { - data = s.data; - ref = s.ref; - len = s.len; - - (*ref)++; + ptr = s.ptr; + ptr->incr(); } @@ -193,11 +183,7 @@ ManagedString::ManagedString() */ ManagedString::~ManagedString() { - if(--(*ref) == 0) - { - free(data); - free(ref); - } + ptr->decr(); } /** @@ -220,19 +206,12 @@ ManagedString::~ManagedString() */ ManagedString& ManagedString::operator = (const ManagedString& s) { - if(this == &s) + if (this->ptr == s.ptr) return *this; - if(--(*ref) == 0) - { - free(data); - free(ref); - } - - data = s.data; - ref = s.ref; - len = s.len; - (*ref)++; + ptr->decr(); + ptr = s.ptr; + ptr->incr(); return *this; } @@ -258,7 +237,7 @@ ManagedString& ManagedString::operator = (const ManagedString& s) */ bool ManagedString::operator== (const ManagedString& s) { - return ((len == s.len) && (memcmp(data,s.data,len)==0)); + return ((length() == s.length()) && (memcmp(toCharArray(),s.toCharArray(),s.length())==0)); } /** @@ -282,7 +261,7 @@ bool ManagedString::operator== (const ManagedString& s) */ bool ManagedString::operator< (const ManagedString& s) { - return (memcmp(data, s.data,min(len,s.len))<0); + return (memcmp(toCharArray(), s.toCharArray(), min(length(),s.length()))<0); } /** @@ -306,7 +285,7 @@ bool ManagedString::operator< (const ManagedString& s) */ bool ManagedString::operator> (const ManagedString& s) { - return (memcmp(data, s.data,min(len,s.len))>0); + return (memcmp(toCharArray(), s.toCharArray(), min(length(),s.length()))>0); } /** @@ -326,14 +305,14 @@ bool ManagedString::operator> (const ManagedString& s) ManagedString ManagedString::substring(int16_t start, int16_t length) { // If the parameters are illegal, just return a reference to the empty string. - if (start >= len) + if (start >= this->length()) return ManagedString(ManagedString::EmptyString); // Compute a safe copy length; - length = min(len-start, length); + length = min(this->length()-start, length); // Build a ManagedString from this. - return ManagedString(data+start, length); + return ManagedString(toCharArray()+start, length); } /** @@ -352,20 +331,16 @@ ManagedString ManagedString::substring(int16_t start, int16_t length) */ ManagedString ManagedString::operator+ (ManagedString& s) { + //printf("add(%d,%d)\n", this->length(), s.length()); uBit.sleep(100); + // If the other string is empty, nothing to do! - if(s.len == 0) + if(s.length() == 0) return *this; - if (len == 0) - return s; - - if(s == ManagedString::EmptyString) - return *this; - - if(*this == ManagedString::EmptyString) + if (length() == 0) return s; - return ManagedString(data, s.data); + return ManagedString(*this, s); } @@ -384,39 +359,40 @@ ManagedString ManagedString::operator+ (ManagedString& s) */ char ManagedString::charAt(int16_t index) { - return (index >=0 && index < len) ? data[index] : 0; -} - -/** - * Provides an immutable 8 bit wide haracter buffer representing this string. - * - * @return a pointer to the charcter buffer. - */ -const char *ManagedString::toCharArray() -{ - return data; -} - -/** - * Determines the length of this ManagedString in characters. - * - * @return the length of the string in characters. - * - * Example: - * @code - * ManagedString s("abcd"); - * - * print(s.length()) // prints "4" - * @endcode - */ -int16_t ManagedString::length() -{ - return len; + return (index >=0 && index < length()) ? ptr->data[index] : 0; } /** * Empty string constant literal */ -ManagedString ManagedString::EmptyString("\0"); +ManagedString ManagedString::EmptyString((StringData*)(void*)empty); +void RefCounted::incr() +{ + if (refcnt == 0xffff) + return; + if (refcnt == 0) + uBit.panic(30); + refcnt++; +} + +void RefCounted::decr() +{ + if (refcnt == 0xffff) + return; + if (refcnt == 0) + uBit.panic(31); + refcnt--; + if (refcnt == 0) { + // size of 0xffff indicates a class with a virtual destructor + if (size == 0xffff) + delete (VirtualRefCounted*)this; + else + free(this); + } +} + +VirtualRefCounted::~VirtualRefCounted() +{ +} From 9330b401d521be67876977e73f0e83d9970aca57 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sat, 24 Oct 2015 20:30:15 -0700 Subject: [PATCH 03/31] Move RefCounted class to a separate file --- .gitignore | 6 +++++ inc/ManagedString.h | 19 +-------------- inc/RefCounted.h | 51 ++++++++++++++++++++++++++++++++++++++++ source/CMakeLists.txt | 1 + source/ManagedString.cpp | 30 ----------------------- source/RefCounted.cpp | 31 ++++++++++++++++++++++++ 6 files changed, 90 insertions(+), 48 deletions(-) create mode 100644 .gitignore create mode 100644 inc/RefCounted.h create mode 100644 source/RefCounted.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..efc51aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.yotta.json +build +*.swp +yotta_modules +yotta_targets +Makefile diff --git a/inc/ManagedString.h b/inc/ManagedString.h index d2432ea..5cf9c07 100644 --- a/inc/ManagedString.h +++ b/inc/ManagedString.h @@ -1,24 +1,7 @@ #ifndef MANAGED_STRING_H #define MANAGED_STRING_H -#include "mbed.h" - -struct RefCounted -{ -public: - uint16_t refcnt; - uint16_t size; - - void incr(); - void decr(); - -}; - -class VirtualRefCounted : RefCounted -{ -public: - virtual ~VirtualRefCounted(); -}; +#include "RefCounted.h" struct StringData : RefCounted { diff --git a/inc/RefCounted.h b/inc/RefCounted.h new file mode 100644 index 0000000..0136888 --- /dev/null +++ b/inc/RefCounted.h @@ -0,0 +1,51 @@ +#ifndef REF_COUNTED_H +#define REF_COUNTED_H + +#include "mbed.h" + +/** + * Base class for payload for ref-counted objects. Used by ManagedString and MicroBitImage. + * There is no constructor, as this struct is typically malloc()ed. + */ +struct RefCounted +{ +public: + /** + * Number of outstanding references. Should never be zero (object should be deleted then). + * When it's set to 0xffff, it means the object sits in flash and should not be counted. + */ + uint16_t refcnt; + + /** + * For strings this is length. For images this is both width and length (8 bit each). + * A value of 0xffff indicates that this is in fact an instance of VirtualRefCounted + * and therefore when the reference count reaches zero, the virtual destructor + * should be called. + */ + union { + uint16_t size; + struct { + uint8_t width; + uint8_t height; + }; + }; + + /** Increment reference count. */ + void incr(); + + /** Decrement reference count. */ + void decr(); + +}; + +/** + * Base class for ref-counted objects. Used in native compiler in TD for Collections, + * user-defined records, closures etc. + */ +class VirtualRefCounted : RefCounted +{ +public: + virtual ~VirtualRefCounted(); +}; + +#endif diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 2d9806c..83b74a8 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -28,6 +28,7 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "MicroBitSerial.cpp" "MicroBitHeapAllocator.cpp" "MicroBitListener.cpp" + "RefCounted.cpp" "MemberFunctionCallback.cpp" "ble-services/MicroBitDFUService.cpp" "ble-services/MicroBitEventService.cpp" diff --git a/source/ManagedString.cpp b/source/ManagedString.cpp index 23e8bf3..5d79f95 100644 --- a/source/ManagedString.cpp +++ b/source/ManagedString.cpp @@ -366,33 +366,3 @@ char ManagedString::charAt(int16_t index) * Empty string constant literal */ ManagedString ManagedString::EmptyString((StringData*)(void*)empty); - - -void RefCounted::incr() -{ - if (refcnt == 0xffff) - return; - if (refcnt == 0) - uBit.panic(30); - refcnt++; -} - -void RefCounted::decr() -{ - if (refcnt == 0xffff) - return; - if (refcnt == 0) - uBit.panic(31); - refcnt--; - if (refcnt == 0) { - // size of 0xffff indicates a class with a virtual destructor - if (size == 0xffff) - delete (VirtualRefCounted*)this; - else - free(this); - } -} - -VirtualRefCounted::~VirtualRefCounted() -{ -} diff --git a/source/RefCounted.cpp b/source/RefCounted.cpp new file mode 100644 index 0000000..31763d5 --- /dev/null +++ b/source/RefCounted.cpp @@ -0,0 +1,31 @@ +#include "mbed.h" +#include "MicroBit.h" + +void RefCounted::incr() +{ + if (refcnt == 0xffff) + return; + if (refcnt == 0) + uBit.panic(30); + refcnt++; +} + +void RefCounted::decr() +{ + if (refcnt == 0xffff) + return; + if (refcnt == 0) + uBit.panic(31); + refcnt--; + if (refcnt == 0) { + // size of 0xffff indicates a class with a virtual destructor + if (size == 0xffff) + delete (VirtualRefCounted*)this; + else + free(this); + } +} + +VirtualRefCounted::~VirtualRefCounted() +{ +} From 4f82a6c37c19584d9e537b8f16ba94adc6b4a683 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sat, 24 Oct 2015 20:58:03 -0700 Subject: [PATCH 04/31] Use RefCounted also for MicroBitImage. --- inc/MicroBit.h | 5 + inc/MicroBitImage.h | 61 ++++++++++- source/MicroBitDisplay.cpp | 6 +- source/MicroBitImage.cpp | 218 +++++++++++++------------------------ 4 files changed, 141 insertions(+), 149 deletions(-) diff --git a/inc/MicroBit.h b/inc/MicroBit.h index 1e97ad0..e3ea4a2 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -1,6 +1,11 @@ #ifndef MICROBIT_H #define MICROBIT_H +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wconversion-null" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wparentheses" + #include "mbed.h" #include "MicroBitConfig.h" diff --git a/inc/MicroBitImage.h b/inc/MicroBitImage.h index f0093f0..5fdbf3c 100644 --- a/inc/MicroBitImage.h +++ b/inc/MicroBitImage.h @@ -2,6 +2,12 @@ #define MICROBIT_IMAGE_H #include "mbed.h" +#include "RefCounted.h" + +struct ImageData : RefCounted +{ + uint8_t data[0]; +}; /** * Class definition for a MicroBitImage. @@ -11,9 +17,8 @@ */ class MicroBitImage { - int16_t width; // Width of the bitmap, in pixels. - int16_t height; // Height of the bitmap, in pixels. - int16_t *ref; // Reference count. + ImageData *ptr; // Pointer to payload data + // Width/height (in pixels) are in high/low byte of ptr->size /** @@ -32,7 +37,28 @@ class MicroBitImage public: static MicroBitImage EmptyImage; // Shared representation of a null image. - uint8_t *bitmap; // 2D array representing the bitmap image. + + /** + * Return a 2D array representing the bitmap image. + */ + uint8_t *getBitmap() + { + return ptr->data; + } + + /** + * Constructor. + * Create an image from a specially prepared constant array, with no copying. + * + * @param p The literal - first two bytes should be 0xff, then width, height, and the bitmap. The literal has to be 4-byte aligned. + * + * Example: + * @code + * static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 5, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart + * ManagedString s((ImageData*)(void*)heart); + * @endcode + */ + MicroBitImage(ImageData *p) : ptr(p) {} /** * Default Constructor. @@ -315,7 +341,10 @@ class MicroBitImage * i.getWidth(); //equals 10... * @endcode */ - int getWidth(); + int getWidth() const + { + return ptr->width; + } /** * Gets the height of this image. @@ -329,8 +358,28 @@ class MicroBitImage * i.getHeight(); //equals 5... * @endcode */ - int getHeight(); + int getHeight() const + { + return ptr->height; + } + /** + * Gets number of bytes in the bitmap, ie., width * height. + * + * @return The size of the bitmap. + * + * Example: + * @code + * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart + * MicroBitImage i(10,5,heart); + * i.getSize(); //equals 50... + * @endcode + */ + int getSize() const + { + return ptr->width * ptr->height; + } + /** * Converts the bitmap to a csv string. * diff --git a/source/MicroBitDisplay.cpp b/source/MicroBitDisplay.cpp index 4a27b26..cd9aea7 100644 --- a/source/MicroBitDisplay.cpp +++ b/source/MicroBitDisplay.cpp @@ -125,7 +125,7 @@ void MicroBitDisplay::render() y = height - 1 - t; } - if(image.bitmap[y*(width*2)+x]) + if(image.getBitmap()[y*(width*2)+x]) coldata |= (1 << i); } @@ -174,7 +174,7 @@ void MicroBitDisplay::renderGreyscale() y = height - 1 - t; } - if(min(image.bitmap[y * (width * 2) + x],brightness) & greyscaleBitMsk) + if(min(image.getBitmap()[y * (width * 2) + x],brightness) & greyscaleBitMsk) coldata |= (1 << i); } //write the new bit pattern @@ -810,7 +810,7 @@ void MicroBitDisplay::error(int statusCode) int outerCount = 0; //display the current character - while( outerCount < 100000) + while( outerCount < 50000) { int coldata = 0; diff --git a/source/MicroBitImage.cpp b/source/MicroBitImage.cpp index 13c3a90..ed4d97a 100644 --- a/source/MicroBitImage.cpp +++ b/source/MicroBitImage.cpp @@ -65,12 +65,8 @@ MicroBitImage::MicroBitImage(const int16_t x, const int16_t y) */ MicroBitImage::MicroBitImage(const MicroBitImage &image) { - bitmap = image.bitmap; - width = image.width; - height = image.height; - ref = image.ref; - - (*ref)++; + ptr = image.ptr; + ptr->incr(); } /** @@ -138,17 +134,12 @@ MicroBitImage::MicroBitImage(const char *s) parseReadPtr++; } - // Store the geomtery. - this->width = width; - this->height = height; - this->bitmap = (uint8_t *) malloc(width * height); - this->ref = (int16_t *) malloc(sizeof(int16_t)); - *ref = 1; + this->init(width, height, NULL); // Second pass: collect the data. parseReadPtr = s; parseWritePtr = parseBuf; - bitmapPtr = this->bitmap; + bitmapPtr = this->getBitmap(); while (*parseReadPtr) { @@ -197,11 +188,7 @@ MicroBitImage::MicroBitImage(const int16_t x, const int16_t y, const uint8_t *bi */ MicroBitImage::~MicroBitImage() { - if(--(*ref) == 0) - { - free(bitmap); - free(ref); - } + ptr->decr(); } /** @@ -209,12 +196,7 @@ MicroBitImage::~MicroBitImage() */ void MicroBitImage::init_empty() { - bitmap = MicroBitImage::EmptyImage.bitmap; - width = MicroBitImage::EmptyImage.width; - height = MicroBitImage::EmptyImage.height; - ref = MicroBitImage::EmptyImage.ref; - - (*ref)++; + ptr = MicroBitImage::EmptyImage.ptr; } /** @@ -227,28 +209,26 @@ void MicroBitImage::init_empty() void MicroBitImage::init(const int16_t x, const int16_t y, const uint8_t *bitmap) { //sanity check size of image - you cannot have a negative sizes - if(x < 0 || y < 0) + if(x < 0 || y < 0 || x >= 0xff || y >= 0xff) { init_empty(); return; } + // Create a copy of the array - this->width = x; - this->height = y; + ptr = (ImageData*)malloc(4 + x * y); + ptr->width = x; + ptr->height = y; + ptr->refcnt = 1; // create a linear buffer to represent the image. We could use a jagged/2D array here, but experimentation // showed this had a negative effect on memory management (heap fragmentation etc). - this->bitmap = (uint8_t *) malloc(width*height); - if (bitmap) this->printImage(x,y,bitmap); else this->clear(); - - ref = (int16_t *) malloc(sizeof(int16_t)); - *ref = 1; } /** @@ -271,21 +251,12 @@ void MicroBitImage::init(const int16_t x, const int16_t y, const uint8_t *bitmap */ MicroBitImage& MicroBitImage::operator = (const MicroBitImage& i) { - if(this == &i) + if(ptr == i.ptr) return *this; - if(--(*ref) == 0) - { - free(bitmap); - free(ref); - } - - bitmap = i.bitmap; - width = i.width; - height = i.height; - ref = i.ref; - - (*ref)++; + ptr->decr(); + ptr = i.ptr; + ptr->incr(); return *this; } @@ -309,10 +280,10 @@ MicroBitImage& MicroBitImage::operator = (const MicroBitImage& i) */ bool MicroBitImage::operator== (const MicroBitImage& i) { - if (bitmap == i.bitmap) + if (ptr == i.ptr) return true; else - return ((width == i.width) && (height == i.height) && (memcmp(bitmap, i.bitmap,width*height)==0)); + return (ptr->size == i.ptr->size && (memcmp(getBitmap(), i.ptr->data, getSize())==0)); } @@ -327,7 +298,7 @@ bool MicroBitImage::operator== (const MicroBitImage& i) */ void MicroBitImage::clear() { - memclr(this->bitmap, width*height); + memclr(getBitmap(), getSize()); } /** @@ -345,10 +316,10 @@ void MicroBitImage::clear() void MicroBitImage::setPixelValue(int16_t x , int16_t y, uint8_t value) { //sanity check - if(x >= width || y >= height || x < 0 || y < 0) + if(x >= getWidth() || y >= getHeight() || x < 0 || y < 0) return; - this->bitmap[y*width+x] = value; + this->getBitmap()[y*getWidth()+x] = value; } /** @@ -364,10 +335,10 @@ void MicroBitImage::setPixelValue(int16_t x , int16_t y, uint8_t value) int MicroBitImage::getPixelValue(int16_t x , int16_t y) { //sanity check - if(x >= width || y >= height || x < 0 || y < 0) + if(x >= getWidth() || y >= getHeight() || x < 0 || y < 0) return MICROBIT_INVALID_VALUE; - return this->bitmap[y*width+x]; + return this->getBitmap()[y*getWidth()+x]; } /** @@ -397,18 +368,18 @@ void MicroBitImage::printImage(int16_t width, int16_t height, const uint8_t *bit return; // Calcualte sane start pointer. - pixelsToCopyX = min(width,this->width); - pixelsToCopyY = min(height,this->height); + pixelsToCopyX = min(width,this->getWidth()); + pixelsToCopyY = min(height,this->getHeight()); pIn = bitmap; - pOut = this->bitmap; + pOut = this->getBitmap(); // Copy the image, stride by stride. for (int i=0; iwidth; + pOut += this->getWidth(); } } @@ -437,21 +408,21 @@ int MicroBitImage::paste(const MicroBitImage &image, int16_t x, int16_t y, uint8 // Sanity check. // We permit writes that overlap us, but ones that are clearly out of scope we can filter early. - if (x >= width || y >= height || x+image.width <= 0 || y+image.height <= 0) + if (x >= getWidth() || y >= getHeight() || x+image.getWidth() <= 0 || y+image.getHeight() <= 0) return 0; //Calculate the number of byte we need to copy in each dimension. - cx = x < 0 ? min(image.width + x, width) : min(image.width, width - x); - cy = y < 0 ? min(image.height + y, height) : min(image.height, height - y); + cx = x < 0 ? min(image.getWidth() + x, getWidth()) : min(image.getWidth(), getWidth() - x); + cy = y < 0 ? min(image.getHeight() + y, getHeight()) : min(image.getHeight(), getHeight() - y); // Calcualte sane start pointer. - pIn = image.bitmap; + pIn = image.ptr->data; pIn += (x < 0) ? -x : 0; - pIn += (y < 0) ? -image.width*y : 0; + pIn += (y < 0) ? -image.getWidth()*y : 0; - pOut = bitmap; + pOut = getBitmap(); pOut += (x > 0) ? x : 0; - pOut += (y > 0) ? width*y : 0; + pOut += (y > 0) ? getWidth()*y : 0; // Copy the image, stride by stride // If we want primitive transparecy, we do this byte by byte. @@ -470,8 +441,8 @@ int MicroBitImage::paste(const MicroBitImage &image, int16_t x, int16_t y, uint8 } } - pIn += image.width; - pOut += width; + pIn += image.getWidth(); + pOut += getWidth(); } } else @@ -481,8 +452,8 @@ int MicroBitImage::paste(const MicroBitImage &image, int16_t x, int16_t y, uint8 memcpy(pOut, pIn, cx); pxWritten += cx; - pIn += image.width; - pOut += width; + pIn += image.getWidth(); + pOut += getWidth(); } } @@ -510,7 +481,7 @@ void MicroBitImage::print(char c, int16_t x, int16_t y) MicroBitFont font = uBit.display.getFont(); // Sanity check. Silently ignore anything out of bounds. - if (x >= width || y >= height || c < MICROBIT_FONT_ASCII_START || c > font.asciiEnd) + if (x >= getWidth() || y >= getHeight() || c < MICROBIT_FONT_ASCII_START || c > font.asciiEnd) return; // Paste. @@ -530,8 +501,8 @@ void MicroBitImage::print(char c, int16_t x, int16_t y) // Update our X co-ord write position x1 = x+col; - if (x1 < width && y1 < height) - this->bitmap[y1*width+x1] = (v & (0x10 >> col)) ? 255 : 0; + if (x1 < getWidth() && y1 < getHeight()) + this->getBitmap()[y1*getWidth()+x1] = (v & (0x10 >> col)) ? 255 : 0; } } } @@ -551,24 +522,24 @@ void MicroBitImage::print(char c, int16_t x, int16_t y) */ void MicroBitImage::shiftLeft(int16_t n) { - uint8_t *p = bitmap; - int pixels = width-n; + uint8_t *p = getBitmap(); + int pixels = getWidth()-n; if (n <= 0 ) return; - if(n >= width) + if(n >= getWidth()) { clear(); return; } - for (int y = 0; y < height; y++) + for (int y = 0; y < getHeight(); y++) { // Copy, and blank fill the rightmost column. memcpy(p, p+n, pixels); memclr(p+pixels, n); - p += width; + p += getWidth(); } } @@ -587,24 +558,24 @@ void MicroBitImage::shiftLeft(int16_t n) */ void MicroBitImage::shiftRight(int16_t n) { - uint8_t *p = bitmap; - int pixels = width-n; + uint8_t *p = getBitmap(); + int pixels = getWidth()-n; if (n <= 0) return; - if(n >= width) + if(n >= getWidth()) { clear(); return; } - for (int y = 0; y < height; y++) + for (int y = 0; y < getHeight(); y++) { // Copy, and blank fill the leftmost column. memmove(p+n, p, pixels); memclr(p, n); - p += width; + p += getWidth(); } } @@ -628,25 +599,25 @@ void MicroBitImage::shiftUp(int16_t n) if (n <= 0 ) return; - if(n >= height) + if(n >= getHeight()) { clear(); return; } - pOut = bitmap; - pIn = bitmap+width*n; + pOut = getBitmap(); + pIn = getBitmap()+getWidth()*n; - for (int y = 0; y < height; y++) + for (int y = 0; y < getHeight(); y++) { // Copy, and blank fill the leftmost column. - if (y < height-n) - memcpy(pOut, pIn, width); + if (y < getHeight()-n) + memcpy(pOut, pIn, getWidth()); else - memclr(pOut, width); + memclr(pOut, getWidth()); - pIn += width; - pOut += width; + pIn += getWidth(); + pOut += getWidth(); } } @@ -670,61 +641,28 @@ void MicroBitImage::shiftDown(int16_t n) if (n <= 0 ) return; - if(n >= height) + if(n >= getHeight()) { clear(); return; } - pOut = bitmap + width*(height-1); - pIn = pOut - width*n; + pOut = getBitmap() + getWidth()*(getHeight()-1); + pIn = pOut - getWidth()*n; - for (int y = 0; y < height; y++) + for (int y = 0; y < getHeight(); y++) { // Copy, and blank fill the leftmost column. - if (y < height-n) - memcpy(pOut, pIn, width); + if (y < getHeight()-n) + memcpy(pOut, pIn, getWidth()); else - memclr(pOut, width); + memclr(pOut, getWidth()); - pIn -= width; - pOut -= width; + pIn -= getWidth(); + pOut -= getWidth(); } } -/** - * Gets the width of this image. - * - * @return The width of this image. - * - * Example: - * @code - * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart - * MicroBitImage i(10,5,heart); - * i.getWidth(); //equals 10... - * @endcode - */ -int MicroBitImage::getWidth() -{ - return width; -} - -/** - * Gets the height of this image. - * - * @return The height of this image. - * - * Example: - * @code - * const uint8_t heart[] = { 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart - * MicroBitImage i(10,5,heart); - * i.getHeight(); //equals 5... - * @endcode - */ -int MicroBitImage::getHeight() -{ - return height; -} /** * Converts the bitmap to a csv string. @@ -739,14 +677,14 @@ int MicroBitImage::getHeight() ManagedString MicroBitImage::toString() { //width including commans and \n * height - int stringSize = ((width * 2) * height); + int stringSize = getSize() * 2; //plus one for string terminator char parseBuffer[stringSize + 1]; parseBuffer[stringSize] = '\0'; - uint8_t *bitmapPtr = bitmap; + uint8_t *bitmapPtr = getBitmap(); int parseIndex = 0; int widthCount = 0; @@ -760,7 +698,7 @@ ManagedString MicroBitImage::toString() parseIndex++; - if(widthCount == width-1) + if(widthCount == getWidth()-1) { parseBuffer[parseIndex] = '\n'; widthCount = 0; @@ -798,17 +736,17 @@ MicroBitImage MicroBitImage::crop(int startx, int starty, int cropWidth, int cro int newWidth = startx + cropWidth; int newHeight = starty + cropHeight; - if (newWidth >= width || newWidth <=0) - newWidth = width; + if (newWidth >= getWidth() || newWidth <=0) + newWidth = getWidth(); - if (newHeight >= height || newHeight <= 0) - newHeight = height; + if (newHeight >= getHeight() || newHeight <= 0) + newHeight = getHeight(); //allocate our storage. uint8_t cropped[newWidth * newHeight]; //calculate the pointer to where we want to begin cropping - uint8_t *copyPointer = bitmap + (width * starty) + startx; + uint8_t *copyPointer = getBitmap() + (getWidth() * starty) + startx; //get a reference to our storage uint8_t *pastePointer = cropped; @@ -818,7 +756,7 @@ MicroBitImage MicroBitImage::crop(int startx, int starty, int cropWidth, int cro { memcpy(pastePointer, copyPointer, newWidth); - copyPointer += width; + copyPointer += getWidth(); pastePointer += newHeight; } From 5df59c2f7edec62d6a3feb745d5972ad90824c67 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sat, 24 Oct 2015 21:52:51 -0700 Subject: [PATCH 05/31] Remove VirtualRefCounted class; non-virtual <-> virtual casts are not no-ops. --- inc/RefCounted.h | 10 ---------- source/RefCounted.cpp | 6 +----- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/inc/RefCounted.h b/inc/RefCounted.h index 0136888..91eea8b 100644 --- a/inc/RefCounted.h +++ b/inc/RefCounted.h @@ -38,14 +38,4 @@ public: }; -/** - * Base class for ref-counted objects. Used in native compiler in TD for Collections, - * user-defined records, closures etc. - */ -class VirtualRefCounted : RefCounted -{ -public: - virtual ~VirtualRefCounted(); -}; - #endif diff --git a/source/RefCounted.cpp b/source/RefCounted.cpp index 31763d5..123f73a 100644 --- a/source/RefCounted.cpp +++ b/source/RefCounted.cpp @@ -20,12 +20,8 @@ void RefCounted::decr() if (refcnt == 0) { // size of 0xffff indicates a class with a virtual destructor if (size == 0xffff) - delete (VirtualRefCounted*)this; + uBit.panic(32); else free(this); } } - -VirtualRefCounted::~VirtualRefCounted() -{ -} From 11c99d0b84094250b7fe39a08fe9028baaa975d1 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sun, 25 Oct 2015 08:30:05 -0700 Subject: [PATCH 06/31] Make the reference count always odd to make it possible to distinguish it from a vtable --- inc/ManagedString.h | 5 ++-- inc/MicroBitImage.h | 5 ++-- inc/RefCounted.h | 25 +++++++------------- source/ManagedString.cpp | 14 +++++------ source/MicroBitImage.cpp | 4 ++-- source/RefCounted.cpp | 51 ++++++++++++++++++++++++++++------------ 6 files changed, 59 insertions(+), 45 deletions(-) diff --git a/inc/ManagedString.h b/inc/ManagedString.h index 5cf9c07..7ff9060 100644 --- a/inc/ManagedString.h +++ b/inc/ManagedString.h @@ -5,6 +5,7 @@ struct StringData : RefCounted { + uint16_t len; char data[0]; }; @@ -275,7 +276,7 @@ class ManagedString */ const char *toCharArray() const { - if (ptr->refcnt == 0) panic(243); + ptr->isReadOnly(); // this performs sanity checks on refCount return ptr->data; } @@ -293,7 +294,7 @@ class ManagedString */ int16_t length() const { - return ptr->size; + return ptr->len; } /** diff --git a/inc/MicroBitImage.h b/inc/MicroBitImage.h index 5fdbf3c..bb0e028 100644 --- a/inc/MicroBitImage.h +++ b/inc/MicroBitImage.h @@ -6,7 +6,9 @@ struct ImageData : RefCounted { - uint8_t data[0]; + uint8_t width; // Width in pixels + uint8_t height; // Height in pixels + uint8_t data[0]; // 2D array representing the bitmap image }; /** @@ -18,7 +20,6 @@ struct ImageData : RefCounted class MicroBitImage { ImageData *ptr; // Pointer to payload data - // Width/height (in pixels) are in high/low byte of ptr->size /** diff --git a/inc/RefCounted.h b/inc/RefCounted.h index 91eea8b..9d332ad 100644 --- a/inc/RefCounted.h +++ b/inc/RefCounted.h @@ -11,24 +11,12 @@ struct RefCounted { public: /** - * Number of outstanding references. Should never be zero (object should be deleted then). + * The high 15 bits hold the number of outstanding references. The lowest bit is always 1 + * to make sure it doesn't look like vtable. + * Should never be even or one (object should be deleted then). * When it's set to 0xffff, it means the object sits in flash and should not be counted. */ - uint16_t refcnt; - - /** - * For strings this is length. For images this is both width and length (8 bit each). - * A value of 0xffff indicates that this is in fact an instance of VirtualRefCounted - * and therefore when the reference count reaches zero, the virtual destructor - * should be called. - */ - union { - uint16_t size; - struct { - uint8_t width; - uint8_t height; - }; - }; + uint16_t refCount; /** Increment reference count. */ void incr(); @@ -36,6 +24,11 @@ public: /** Decrement reference count. */ void decr(); + /** Initializes for one outstanding reference. */ + void init(); + + /** Checks if the object sits in flash memory. */ + bool isReadOnly(); }; #endif diff --git a/source/ManagedString.cpp b/source/ManagedString.cpp index 5d79f95..c730967 100644 --- a/source/ManagedString.cpp +++ b/source/ManagedString.cpp @@ -26,8 +26,8 @@ void ManagedString::initString(const char *str) // We assume the string is sane, and null terminated. int len = strlen(str); ptr = (StringData *) malloc(4+len+1); - ptr->size = len; - ptr->refcnt = 1; + ptr->init(); + ptr->len = len; memcpy(ptr->data, str, len+1); } @@ -96,8 +96,8 @@ ManagedString::ManagedString(const ManagedString &s1, const ManagedString &s2) // Create a new buffer for holding the new string data. ptr = (StringData*) malloc(4+len+1); - ptr->size = len; - ptr->refcnt = 1; + ptr->init(); + ptr->len = len; // Enter the data, and terminate the string. memcpy(ptr->data, s1.toCharArray(), s1.length()); @@ -132,9 +132,9 @@ ManagedString::ManagedString(const char *str, const int16_t length) // Allocate a new buffer, and create a NULL terminated string. ptr = (StringData*) malloc(4+length+1); + ptr->init(); // Store the length of the new string - ptr->size = length; - ptr->refcnt = 1; + ptr->len = length; memcpy(ptr->data, str, length); ptr->data[length] = 0; } @@ -331,8 +331,6 @@ ManagedString ManagedString::substring(int16_t start, int16_t length) */ ManagedString ManagedString::operator+ (ManagedString& s) { - //printf("add(%d,%d)\n", this->length(), s.length()); uBit.sleep(100); - // If the other string is empty, nothing to do! if(s.length() == 0) return *this; diff --git a/source/MicroBitImage.cpp b/source/MicroBitImage.cpp index ed4d97a..a5c7781 100644 --- a/source/MicroBitImage.cpp +++ b/source/MicroBitImage.cpp @@ -218,9 +218,9 @@ void MicroBitImage::init(const int16_t x, const int16_t y, const uint8_t *bitmap // Create a copy of the array ptr = (ImageData*)malloc(4 + x * y); + ptr->init(); ptr->width = x; ptr->height = y; - ptr->refcnt = 1; // create a linear buffer to represent the image. We could use a jagged/2D array here, but experimentation // showed this had a negative effect on memory management (heap fragmentation etc). @@ -283,7 +283,7 @@ bool MicroBitImage::operator== (const MicroBitImage& i) if (ptr == i.ptr) return true; else - return (ptr->size == i.ptr->size && (memcmp(getBitmap(), i.ptr->data, getSize())==0)); + return (ptr->width == i.ptr->width && ptr->height == i.ptr->height && (memcmp(getBitmap(), i.ptr->data, getSize())==0)); } diff --git a/source/RefCounted.cpp b/source/RefCounted.cpp index 123f73a..b8d646e 100644 --- a/source/RefCounted.cpp +++ b/source/RefCounted.cpp @@ -1,27 +1,48 @@ #include "mbed.h" #include "MicroBit.h" +void RefCounted::init() +{ + // Initialize to one reference (lowest bit set to 1) + refCount = 3; +} + +static inline bool isReadOnlyInline(RefCounted *t) +{ + uint32_t refCount = t->refCount; + + if (refCount == 0xffff) + return true; // object in flash + + // Do some sanity checking while we're here + if (refCount == 1) + uBit.panic(30); // object should have been deleted + + if ((refCount & 1) == 0) + uBit.panic(31); // refCount doesn't look right + + // Not read only + return false; +} + +bool RefCounted::isReadOnly() +{ + return isReadOnlyInline(this); +} + void RefCounted::incr() { - if (refcnt == 0xffff) - return; - if (refcnt == 0) - uBit.panic(30); - refcnt++; + if (!isReadOnlyInline(this)) + refCount += 2; } void RefCounted::decr() { - if (refcnt == 0xffff) + if (isReadOnlyInline(this)) return; - if (refcnt == 0) - uBit.panic(31); - refcnt--; - if (refcnt == 0) { - // size of 0xffff indicates a class with a virtual destructor - if (size == 0xffff) - uBit.panic(32); - else - free(this); + + refCount -= 2; + if (refCount == 2) { + free(this); } } From d0b75c96d178d85ef8599904cb6e89eff75d0423 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sun, 25 Oct 2015 10:03:14 -0700 Subject: [PATCH 07/31] Add MicroBitImage::isReadOnly,clone. Add MicroBitImage,ManagedString::leakData(). Work more on incr()/decr() protocol. --- inc/ManagedString.h | 10 ++++++-- inc/MicroBit.h | 1 + inc/MicroBitImage.h | 27 +++++++++++++++++---- source/ManagedString.cpp | 28 ++++++++++++++++++++++ source/MicroBitImage.cpp | 52 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 110 insertions(+), 8 deletions(-) diff --git a/inc/ManagedString.h b/inc/ManagedString.h index 7ff9060..653e96c 100644 --- a/inc/ManagedString.h +++ b/inc/ManagedString.h @@ -32,7 +32,7 @@ class ManagedString /** * Constructor. - * Create a managed string from a specially prepared string literal. + * Create a managed string from a specially prepared string literal. It will ptr->incr(). * * @param ptr The literal - first two bytes should be 0xff, then the length in little endian, then the literal. The literal has to be 4-byte aligned. * @@ -42,7 +42,13 @@ class ManagedString * ManagedString s((StringData*)(void*)hello); * @endcode */ - ManagedString(StringData *p) : ptr(p) {} + ManagedString(StringData *ptr); + + /** + * Get current ptr, do not decr() it, and set the current instance to empty string. + * This is to be used by specialized runtimes which pass StringData around. + */ + StringData *leakData(); /** * Constructor. diff --git a/inc/MicroBit.h b/inc/MicroBit.h index e3ea4a2..e8f18af 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -5,6 +5,7 @@ #pragma GCC diagnostic ignored "-Wconversion-null" #pragma GCC diagnostic ignored "-Wsign-compare" #pragma GCC diagnostic ignored "-Wparentheses" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" #include "mbed.h" diff --git a/inc/MicroBitImage.h b/inc/MicroBitImage.h index bb0e028..71c3310 100644 --- a/inc/MicroBitImage.h +++ b/inc/MicroBitImage.h @@ -39,6 +39,12 @@ class MicroBitImage public: static MicroBitImage EmptyImage; // Shared representation of a null image. + /** + * Get current ptr, do not decr() it, and set the current instance to empty image. + * This is to be used by specialized runtimes which pass ImageData around. + */ + ImageData *leakData(); + /** * Return a 2D array representing the bitmap image. */ @@ -49,17 +55,17 @@ class MicroBitImage /** * Constructor. - * Create an image from a specially prepared constant array, with no copying. + * Create an image from a specially prepared constant array, with no copying. Will call ptr->incr(). * - * @param p The literal - first two bytes should be 0xff, then width, height, and the bitmap. The literal has to be 4-byte aligned. + * @param ptr The literal - first two bytes should be 0xff, then width, height, and the bitmap. The literal has to be 4-byte aligned. * * Example: * @code * static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 5, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart - * ManagedString s((ImageData*)(void*)heart); + * MicroBitImage i((ImageData*)(void*)heart); * @endcode */ - MicroBitImage(ImageData *p) : ptr(p) {} + MicroBitImage(ImageData *ptr); /** * Default Constructor. @@ -392,7 +398,7 @@ class MicroBitImage * @endcode */ ManagedString toString(); - + /** * Crops the image to the given dimensions * @@ -412,6 +418,17 @@ class MicroBitImage */ MicroBitImage crop(int startx, int starty, int finx, int finy); + /** + * Check if image is read-only (i.e., residing in flash). + */ + bool isReadOnly(); + + /** + * Create a copy of the image bitmap. Used particularly, when isReadOnly() is true. + * + * @return an instance of MicroBitImage which can be modified independently of the current instance + */ + MicroBitImage clone(); }; #endif diff --git a/source/ManagedString.cpp b/source/ManagedString.cpp index c730967..66af6fe 100644 --- a/source/ManagedString.cpp +++ b/source/ManagedString.cpp @@ -31,6 +31,34 @@ void ManagedString::initString(const char *str) memcpy(ptr->data, str, len+1); } +/** + * Constructor. + * Create a managed string from a specially prepared string literal. It will ptr->incr(). + * + * @param ptr The literal - first two bytes should be 0xff, then the length in little endian, then the literal. The literal has to be 4-byte aligned. + * + * Example: + * @code + * static const char hello[] __attribute__ ((aligned (4))) = "\xff\xff\x05\x00" "Hello"; + * ManagedString s((StringData*)(void*)hello); + * @endcode + */ +ManagedString::ManagedString(StringData *p) +{ + ptr = p; + ptr->incr(); +} + +/** + * Get current ptr, do not decr() it, and set the current instance to empty string. + * This is to be used by specialized runtimes which pass StringData around. + */ +StringData* ManagedString::leakData() +{ + StringData *res = ptr; + initEmpty(); + return res; +} /** * Constructor. diff --git a/source/MicroBitImage.cpp b/source/MicroBitImage.cpp index a5c7781..059ee7b 100644 --- a/source/MicroBitImage.cpp +++ b/source/MicroBitImage.cpp @@ -7,10 +7,12 @@ #include "MicroBit.h" +static const uint8_t empty[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 1, 1, 0, 0 }; + /* * The null image. We actally create a small one byte buffer here, just to keep NULL pointers out of the equation. */ -MicroBitImage MicroBitImage::EmptyImage(1,1); +MicroBitImage MicroBitImage::EmptyImage((ImageData*)(void*)empty); /** * Default Constructor. @@ -163,6 +165,36 @@ MicroBitImage::MicroBitImage(const char *s) } } +/** + * Constructor. + * Create an image from a specially prepared constant array, with no copying. Will call ptr->incr(). + * + * @param ptr The literal - first two bytes should be 0xff, then width, height, and the bitmap. The literal has to be 4-byte aligned. + * + * Example: + * @code + * static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 5, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart + * MicroBitImage i((ImageData*)(void*)heart); + * @endcode + */ +MicroBitImage::MicroBitImage(ImageData *p) +{ + ptr = p; + ptr->incr(); +} + +/** + * Get current ptr, do not decr() it, and set the current instance to empty image. + * This is to be used by specialized runtimes which pass ImageData around. + */ +ImageData *MicroBitImage::leakData() +{ + ImageData* res = ptr; + init_empty(); + return res; +} + + /** * Constructor. * Create a bitmap representation of a given size, based on a given buffer. @@ -762,3 +794,21 @@ MicroBitImage MicroBitImage::crop(int startx, int starty, int cropWidth, int cro return MicroBitImage(newWidth, newHeight, cropped); } + +/** + * Check if image is read-only (i.e., residing in flash). + */ +bool MicroBitImage::isReadOnly() +{ + return ptr->isReadOnly(); +} + +/** + * Create a copy of the image bitmap. Used particularly, when isReadOnly() is true. + * + * @return an instance of MicroBitImage which can be modified independently of the current instance + */ +MicroBitImage MicroBitImage::clone() +{ + return MicroBitImage(getWidth(), getHeight(), getBitmap()); +} From 85b4e45863d9ff27b62130e2ef1bdd47644de161 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sun, 25 Oct 2015 11:38:00 -0700 Subject: [PATCH 08/31] Order-of-initialization fix. --- source/MicroBitImage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/MicroBitImage.cpp b/source/MicroBitImage.cpp index 059ee7b..ca63a05 100644 --- a/source/MicroBitImage.cpp +++ b/source/MicroBitImage.cpp @@ -228,7 +228,7 @@ MicroBitImage::~MicroBitImage() */ void MicroBitImage::init_empty() { - ptr = MicroBitImage::EmptyImage.ptr; + ptr = (ImageData*)(void*)empty; } /** From 98ffcc1fd1bdd80f8229d9d2a759f63939914ce9 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sun, 25 Oct 2015 12:59:45 -0700 Subject: [PATCH 09/31] Add status panic codes for ref-count errors --- inc/ErrorNo.h | 8 +++++++- source/RefCounted.cpp | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/inc/ErrorNo.h b/inc/ErrorNo.h index 84a37d6..19c6e62 100644 --- a/inc/ErrorNo.h +++ b/inc/ErrorNo.h @@ -22,6 +22,12 @@ enum Error{ MICROBIT_OOM = 20, // Corruption detected in the micro:bit heap space - MICROBIT_HEAP_ERROR = 30 + MICROBIT_HEAP_ERROR = 30, + + // refcounter on an object is invalid (memory corruption) + MICROBIT_REF_COUNT_CORRUPTION = 31, + + // refcounter was incremented/decremented after it already reached zero + MICROBIT_USE_AFTER_FREE = 32, }; #endif diff --git a/source/RefCounted.cpp b/source/RefCounted.cpp index b8d646e..4ed60c1 100644 --- a/source/RefCounted.cpp +++ b/source/RefCounted.cpp @@ -16,10 +16,10 @@ static inline bool isReadOnlyInline(RefCounted *t) // Do some sanity checking while we're here if (refCount == 1) - uBit.panic(30); // object should have been deleted + uBit.panic(MICROBIT_USE_AFTER_FREE); // object should have been deleted if ((refCount & 1) == 0) - uBit.panic(31); // refCount doesn't look right + uBit.panic(MICROBIT_REF_COUNT_CORRUPTION); // refCount doesn't look right // Not read only return false; From 7b82f3e8eb63e73fa8b6d7dc80e7f06b9d5af2a8 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sun, 25 Oct 2015 13:09:24 -0700 Subject: [PATCH 10/31] Improve comments; cleanup --- inc/ManagedString.h | 12 ++++++++---- source/ManagedString.cpp | 2 -- source/MicroBitImage.cpp | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/inc/ManagedString.h b/inc/ManagedString.h index 653e96c..fefc271 100644 --- a/inc/ManagedString.h +++ b/inc/ManagedString.h @@ -21,11 +21,15 @@ struct StringData : RefCounted * 1) std::shared_ptr is not yet availiable on the ARMCC compiler * 2) to reduce memory footprint - we don't need many of the other features in the std library * 3) it makes an interestin case study for anyone interested in seeing how it works! + * 4) we need explicit reference counting to inter-op with low-level application langauge runtimes + * 5) the reference counting needs to also work for read-only, flash-resident strings */ class ManagedString { - // Internally we record the string as a char *, but control access to this to proide immutability - // and reference counting. + // StringData contains the reference count, the length, follwed by char[] data, all in one block. + // When referece count is 0xffff, then it's read only and should not be counted. + // Otherwise the block was malloc()ed. + // We control access to this to proide immutability and reference counting. StringData *ptr; public: @@ -140,7 +144,7 @@ class ManagedString * * Free this ManagedString, and decrement the reference count to the * internal character buffer. If we're holding the last reference, - * also free the character buffer and reference counter. + * also free the character buffer. */ ~ManagedString(); @@ -282,7 +286,7 @@ class ManagedString */ const char *toCharArray() const { - ptr->isReadOnly(); // this performs sanity checks on refCount + // ptr->isReadOnly(); // this performs sanity checks on refCount return ptr->data; } diff --git a/source/ManagedString.cpp b/source/ManagedString.cpp index 66af6fe..d2265ed 100644 --- a/source/ManagedString.cpp +++ b/source/ManagedString.cpp @@ -3,8 +3,6 @@ #include "mbed.h" #include "MicroBit.h" -#define printf(...) uBit.serial.printf(__VA_ARGS__) - static const char empty[] __attribute__ ((aligned (4))) = "\xff\xff\0\0\0"; /** diff --git a/source/MicroBitImage.cpp b/source/MicroBitImage.cpp index ca63a05..d67eacc 100644 --- a/source/MicroBitImage.cpp +++ b/source/MicroBitImage.cpp @@ -7,11 +7,11 @@ #include "MicroBit.h" -static const uint8_t empty[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 1, 1, 0, 0 }; /* * The null image. We actally create a small one byte buffer here, just to keep NULL pointers out of the equation. */ +static const uint8_t empty[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 1, 1, 0, 0 }; MicroBitImage MicroBitImage::EmptyImage((ImageData*)(void*)empty); /** From c1050df0d9ce6bcaff1b0a30ef30ff4eeba94e3f Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 27 Oct 2015 08:02:42 -0700 Subject: [PATCH 11/31] Make the image width/height 16 bit wide --- inc/MicroBitImage.h | 8 ++++---- source/MicroBitImage.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/inc/MicroBitImage.h b/inc/MicroBitImage.h index 71c3310..dbffe6a 100644 --- a/inc/MicroBitImage.h +++ b/inc/MicroBitImage.h @@ -6,8 +6,8 @@ struct ImageData : RefCounted { - uint8_t width; // Width in pixels - uint8_t height; // Height in pixels + uint16_t width; // Width in pixels + uint16_t height; // Height in pixels uint8_t data[0]; // 2D array representing the bitmap image }; @@ -57,11 +57,11 @@ class MicroBitImage * Constructor. * Create an image from a specially prepared constant array, with no copying. Will call ptr->incr(). * - * @param ptr The literal - first two bytes should be 0xff, then width, height, and the bitmap. The literal has to be 4-byte aligned. + * @param ptr The literal - first two bytes should be 0xff, then width, 0, height, 0, and the bitmap. Width and height are 16 bit. The literal has to be 4-byte aligned. * * Example: * @code - * static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 5, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart + * static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 0, 5, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart * MicroBitImage i((ImageData*)(void*)heart); * @endcode */ diff --git a/source/MicroBitImage.cpp b/source/MicroBitImage.cpp index d67eacc..61841dd 100644 --- a/source/MicroBitImage.cpp +++ b/source/MicroBitImage.cpp @@ -11,7 +11,7 @@ /* * The null image. We actally create a small one byte buffer here, just to keep NULL pointers out of the equation. */ -static const uint8_t empty[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 1, 1, 0, 0 }; +static const uint16_t empty[] __attribute__ ((aligned (4))) = { 0xffff, 1, 1, 0, }; MicroBitImage MicroBitImage::EmptyImage((ImageData*)(void*)empty); /** @@ -169,11 +169,11 @@ MicroBitImage::MicroBitImage(const char *s) * Constructor. * Create an image from a specially prepared constant array, with no copying. Will call ptr->incr(). * - * @param ptr The literal - first two bytes should be 0xff, then width, height, and the bitmap. The literal has to be 4-byte aligned. + * @param ptr The literal - first two bytes should be 0xff, then width, 0, height, 0, and the bitmap. Width and height are 16 bit. The literal has to be 4-byte aligned. * * Example: * @code - * static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 5, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart + * static const uint8_t heart[] __attribute__ ((aligned (4))) = { 0xff, 0xff, 10, 0, 5, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, }; // a cute heart * MicroBitImage i((ImageData*)(void*)heart); * @endcode */ @@ -241,7 +241,7 @@ void MicroBitImage::init_empty() void MicroBitImage::init(const int16_t x, const int16_t y, const uint8_t *bitmap) { //sanity check size of image - you cannot have a negative sizes - if(x < 0 || y < 0 || x >= 0xff || y >= 0xff) + if(x < 0 || y < 0) { init_empty(); return; From 6437e714fa21a55da7fcb709e15f229a322d7170 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 27 Oct 2015 08:19:56 -0700 Subject: [PATCH 12/31] Delete when ref-count goes to 1, not 2\! --- source/RefCounted.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/RefCounted.cpp b/source/RefCounted.cpp index 4ed60c1..7ca3449 100644 --- a/source/RefCounted.cpp +++ b/source/RefCounted.cpp @@ -42,7 +42,7 @@ void RefCounted::decr() return; refCount -= 2; - if (refCount == 2) { + if (refCount == 1) { free(this); } } From 2877fd68f2598a1360a0fd18657a8ec0d244f109 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 27 Oct 2015 08:23:14 -0700 Subject: [PATCH 13/31] Remove GCC warning pragmas --- inc/MicroBit.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/inc/MicroBit.h b/inc/MicroBit.h index e8f18af..1e97ad0 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -1,12 +1,6 @@ #ifndef MICROBIT_H #define MICROBIT_H -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Wconversion-null" -#pragma GCC diagnostic ignored "-Wsign-compare" -#pragma GCC diagnostic ignored "-Wparentheses" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" - #include "mbed.h" #include "MicroBitConfig.h" From 59853855b04b77ba29d62eaef970a9eed971b0b8 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 27 Oct 2015 08:26:07 -0700 Subject: [PATCH 14/31] Remove caching of getSerial(); use temporary for BLE init --- inc/MicroBit.h | 1 - source/MicroBit.cpp | 11 ++++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/inc/MicroBit.h b/inc/MicroBit.h index 1e97ad0..b1651de 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -77,7 +77,6 @@ class MicroBit void seedRandom(); uint32_t randomValue; - ManagedString serialCache; public: diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index bc170ee..8cb359f 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -118,7 +118,9 @@ void MicroBit::init() #endif #if CONFIG_ENABLED(MICROBIT_BLE_DEVICE_INFORMATION_SERVICE) - DeviceInformationService ble_device_information_service (*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, getSerial().toCharArray(), MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION); + // Create a temporary, so that compiler doesn't delete the pointer before DeviceInformationService copies it + ManagedString tmp = getSerial(); + DeviceInformationService ble_device_information_service (*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, tmp.toCharArray(), MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION); #endif #if CONFIG_ENABLED(MICROBIT_BLE_EVENT_SERVICE) @@ -221,9 +223,6 @@ ManagedString MicroBit::getName() */ ManagedString MicroBit::getSerial() { - if (serialCache.length() > 0) - return serialCache; - // We take to 16 bit numbers here, as we want the full range of ID bits, but don't want negative numbers... int n1 = NRF_FICR->DEVICEID[1] & 0xffff; int n2 = (NRF_FICR->DEVICEID[1] >> 16) & 0xffff; @@ -232,9 +231,7 @@ ManagedString MicroBit::getSerial() ManagedString s1 = ManagedString(n1); ManagedString s2 = ManagedString(n2); - serialCache = s1 + s2; - - return serialCache; + return s1 + s2; } /** From 39009625563cb1d4e48f26e88b8f00aed652403f Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 27 Oct 2015 08:28:06 -0700 Subject: [PATCH 15/31] Remove specific panic codes for ref-counting. --- inc/ErrorNo.h | 8 +------- source/RefCounted.cpp | 8 +++----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/inc/ErrorNo.h b/inc/ErrorNo.h index 19c6e62..2e350a2 100644 --- a/inc/ErrorNo.h +++ b/inc/ErrorNo.h @@ -22,12 +22,6 @@ enum Error{ MICROBIT_OOM = 20, // Corruption detected in the micro:bit heap space - MICROBIT_HEAP_ERROR = 30, - - // refcounter on an object is invalid (memory corruption) - MICROBIT_REF_COUNT_CORRUPTION = 31, - - // refcounter was incremented/decremented after it already reached zero - MICROBIT_USE_AFTER_FREE = 32, + MICROBIT_HEAP_ERROR = 30 }; #endif diff --git a/source/RefCounted.cpp b/source/RefCounted.cpp index 7ca3449..3d4105e 100644 --- a/source/RefCounted.cpp +++ b/source/RefCounted.cpp @@ -15,11 +15,9 @@ static inline bool isReadOnlyInline(RefCounted *t) return true; // object in flash // Do some sanity checking while we're here - if (refCount == 1) - uBit.panic(MICROBIT_USE_AFTER_FREE); // object should have been deleted - - if ((refCount & 1) == 0) - uBit.panic(MICROBIT_REF_COUNT_CORRUPTION); // refCount doesn't look right + if (refCount == 1 || // object should have been deleted + (refCount & 1) == 0) // refCount doesn't look right + uBit.panic(MICROBIT_HEAP_ERROR); // Not read only return false; From 7c4f87036fe19b297a80581feaec46d42fe301d3 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 27 Oct 2015 08:28:50 -0700 Subject: [PATCH 16/31] Remove left-behind code --- inc/ManagedString.h | 1 - 1 file changed, 1 deletion(-) diff --git a/inc/ManagedString.h b/inc/ManagedString.h index fefc271..bf2017c 100644 --- a/inc/ManagedString.h +++ b/inc/ManagedString.h @@ -286,7 +286,6 @@ class ManagedString */ const char *toCharArray() const { - // ptr->isReadOnly(); // this performs sanity checks on refCount return ptr->data; } From 9b5fa0fcfc40d1c96a83fc4035eaa0eb9231c90d Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Tue, 17 Nov 2015 16:58:42 +0000 Subject: [PATCH 17/31] minor bugfixes in MICROBIT_HEAP_DEBUG --- source/MicroBitHeapAllocator.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/MicroBitHeapAllocator.cpp b/source/MicroBitHeapAllocator.cpp index 36cf1d0..965e893 100644 --- a/source/MicroBitHeapAllocator.cpp +++ b/source/MicroBitHeapAllocator.cpp @@ -77,7 +77,7 @@ void microbit_heap_print(HeapDefinition &heap) while (block < heap.heap_end) { blockSize = *block & ~MICROBIT_HEAP_BLOCK_FREE; - uBit.serialpc.printf("[%C:%d] ", *block & MICROBIT_HEAP_BLOCK_FREE ? 'F' : 'U', blockSize*4); + uBit.serial.printf("[%C:%d] ", *block & MICROBIT_HEAP_BLOCK_FREE ? 'F' : 'U', blockSize*4); if (cols++ == 20) { uBit.serial.printf("\n"); @@ -329,7 +329,7 @@ void *microbit_malloc(size_t size) if (p != NULL) { #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) - pc.uBit.serial("microbit_malloc: ALLOCATED: %d [%p]\n", size, p); + uBit.serial.printf("microbit_malloc: ALLOCATED: %d [%p]\n", size, p); #endif return p; } @@ -345,7 +345,7 @@ void *microbit_malloc(size_t size) #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) // Keep everything trasparent if we've not been initialised yet if (microbit_active_heaps()) - pc.uBit.serial("microbit_malloc: NATIVE ALLOCATED: %d [%p]\n", size, p); + uBit.serial.printf("microbit_malloc: NATIVE ALLOCATED: %d [%p]\n", size, p); #endif return p; } @@ -354,7 +354,7 @@ void *microbit_malloc(size_t size) #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) // Keep everything trasparent if we've not been initialised yet if (microbit_active_heaps()) - pc.uBit.serial("microbit_malloc: OUT OF MEMORY\n"); + uBit.serial.printf("microbit_malloc: OUT OF MEMORY\n"); #endif #if CONFIG_ENABLED(MICROBIT_PANIC_HEAP_FULL) @@ -375,7 +375,7 @@ void microbit_free(void *mem) #if CONFIG_ENABLED(MICROBIT_DBG) && CONFIG_ENABLED(MICROBIT_HEAP_DBG) if (microbit_active_heaps()) - pc.uBit.serial("microbit_free: %p\n", mem); + uBit.serial.printf("microbit_free: %p\n", mem); #endif // Sanity check. if (memory == NULL) From b44095f0c55e4957f7eca9c5340fb00014c6881b Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Tue, 17 Nov 2015 17:20:30 +0000 Subject: [PATCH 18/31] BUGFIX: String comparison operations Corrected response of < and > operations where substrings are compared. --- source/ManagedString.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/ManagedString.cpp b/source/ManagedString.cpp index ee38d13..e200ce0 100644 --- a/source/ManagedString.cpp +++ b/source/ManagedString.cpp @@ -263,7 +263,7 @@ ManagedString& ManagedString::operator = (const ManagedString& s) */ bool ManagedString::operator== (const ManagedString& s) { - return ((length() == s.length()) && (memcmp(toCharArray(),s.toCharArray(),s.length())==0)); + return ((length() == s.length()) && (strcmp(toCharArray(),s.toCharArray())==0)); } /** @@ -287,7 +287,7 @@ bool ManagedString::operator== (const ManagedString& s) */ bool ManagedString::operator< (const ManagedString& s) { - return (memcmp(toCharArray(), s.toCharArray(), min(length(),s.length()))<0); + return (strcmp(toCharArray(), s.toCharArray())<0); } /** @@ -311,7 +311,7 @@ bool ManagedString::operator< (const ManagedString& s) */ bool ManagedString::operator> (const ManagedString& s) { - return (memcmp(toCharArray(), s.toCharArray(), min(length(),s.length()))>0); + return (strcmp(toCharArray(), s.toCharArray())>0); } /** From af0c0a408288399437af4eef26f93d5ce2151794 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Tue, 17 Nov 2015 17:22:41 +0000 Subject: [PATCH 19/31] version v1.3.7 --- module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module.json b/module.json index 097d9a0..18f8d59 100644 --- a/module.json +++ b/module.json @@ -1,6 +1,6 @@ { "name": "microbit-dal", - "version": "1.3.6", + "version": "1.3.7", "license": "Apache2", "description": "The runtime library for the BBC micro:bit, developed by Lancaster University", "keywords": [ From 892689c54a743f44215264572221747eb115a8d9 Mon Sep 17 00:00:00 2001 From: Robert May Date: Tue, 17 Nov 2015 18:19:46 +0000 Subject: [PATCH 20/31] Fix Microbit::random() to be random The LFSR used only provides 1 bit of random data each time it is cycled. This implementation generates the minimum number of bits needed. Further it discards numbers that are bigger than required and re-calculates - this keeps the distribution flat. --- source/MicroBit.cpp | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index ed8d38d..3db4e42 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -301,17 +301,49 @@ int MicroBit::sleep(int milliseconds) */ int MicroBit::random(int max) { + uint32_t m; + uint8_t b, bits = 0; + //return MICROBIT_INVALID_VALUE if max is <= 0... if(max <= 0) return MICROBIT_INVALID_PARAMETER; + + // Calculate the number of bits we need + m = max; + while (m >>= 1) { + bits++; + } - // Cycle the LFSR (Linear Feedback Shift Register). - // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneider here (a true legend in the field!), - // For those interested, it's documented in his paper: - // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors" + m = 0; + do { + for(b=0; b> 31) ^ (randomValue >> 6) ^ (randomValue >> 4) ^ (randomValue >> 2) ^ (randomValue >> 1) ^ randomValue) & 0x0000001) << 31 ) | (randomValue >> 1); - return randomValue % max; + __disable_irq(); + + randomValue = ((((randomValue >> 31) + ^ (randomValue >> 6) + ^ (randomValue >> 4) + ^ (randomValue >> 2) + ^ (randomValue >> 1) + ^ randomValue) + & 0x0000001) + << 31 ) + | (randomValue >> 1); + + __enable_irq(); + + m = ((m << 1) | (randomValue & 0x00000001)); + } + } while (m > (uint32_t)max); + + + return m; } From 901faaeec7e33abb70921a83b19691cc5e136f73 Mon Sep 17 00:00:00 2001 From: Robert May Date: Tue, 17 Nov 2015 18:51:10 +0000 Subject: [PATCH 21/31] reset variable m in the correct place. --- source/MicroBit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index 3db4e42..a10d2eb 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -314,8 +314,8 @@ int MicroBit::random(int max) bits++; } - m = 0; do { + m = 0; for(b=0; b Date: Tue, 17 Nov 2015 15:57:19 -0800 Subject: [PATCH 22/31] Fix allocation size for ImageData (the header is 6, not 4 bytes long) --- source/MicroBitImage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/MicroBitImage.cpp b/source/MicroBitImage.cpp index 686450e..d2e9684 100644 --- a/source/MicroBitImage.cpp +++ b/source/MicroBitImage.cpp @@ -249,7 +249,7 @@ void MicroBitImage::init(const int16_t x, const int16_t y, const uint8_t *bitmap // Create a copy of the array - ptr = (ImageData*)malloc(4 + x * y); + ptr = (ImageData*)malloc(sizeof(ImageData) + x * y); ptr->init(); ptr->width = x; ptr->height = y; From d5dfdbcc5eb1df3277ccb31c355e1355a08d17a0 Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Tue, 17 Nov 2015 15:57:59 -0800 Subject: [PATCH 23/31] version v1.3.8 --- module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module.json b/module.json index 18f8d59..790b9aa 100644 --- a/module.json +++ b/module.json @@ -1,6 +1,6 @@ { "name": "microbit-dal", - "version": "1.3.7", + "version": "1.3.8", "license": "Apache2", "description": "The runtime library for the BBC micro:bit, developed by Lancaster University", "keywords": [ From 1bb4377c8cae2dc058a65d0d21b314c4ba40d421 Mon Sep 17 00:00:00 2001 From: Robert May Date: Wed, 18 Nov 2015 12:08:34 +0000 Subject: [PATCH 24/31] Combine loops and fix max returned value. --- source/MicroBit.cpp | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index a10d2eb..0e74061 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -301,22 +301,19 @@ int MicroBit::sleep(int milliseconds) */ int MicroBit::random(int max) { - uint32_t m; - uint8_t b, bits = 0; + uint32_t m, result; //return MICROBIT_INVALID_VALUE if max is <= 0... if(max <= 0) return MICROBIT_INVALID_PARAMETER; - // Calculate the number of bits we need - m = max; - while (m >>= 1) { - bits++; - } - + // Our maximum return value is actually one less than passed + max--; + do { - m = 0; - for(b=0; b>= 1) { // Cycle the LFSR (Linear Feedback Shift Register). // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneider here (a true legend in the field!), // For those interested, it's documented in his paper: @@ -338,12 +335,12 @@ int MicroBit::random(int max) __enable_irq(); - m = ((m << 1) | (randomValue & 0x00000001)); + result = ((result << 1) | (randomValue & 0x00000001)); } - } while (m > (uint32_t)max); + } while (result > (uint32_t)max); - return m; + return result; } From db5227872affd9cca252a1b39551d239212b9680 Mon Sep 17 00:00:00 2001 From: Robert May Date: Wed, 18 Nov 2015 14:15:17 +0000 Subject: [PATCH 25/31] Cchange random() to use libc srand()/rand() Uisng libc's implementation is likely to be safer than rolling our own. At least the failure modes are well documented. (glibc's implementation of rand() is actually not bad). --- inc/MicroBit.h | 6 ++--- source/MicroBit.cpp | 63 +++++++++++++++------------------------------ 2 files changed, 24 insertions(+), 45 deletions(-) diff --git a/inc/MicroBit.h b/inc/MicroBit.h index 417b300..9056492 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -90,7 +90,6 @@ class MicroBit private: void seedRandom(); - uint32_t randomValue; public: @@ -214,8 +213,9 @@ class MicroBit /** * Generate a random number in the given range. - * We use the NRF51822 in built random number generator here - * TODO: Determine if we want to, given its relatively high power consumption! + * We use libc's rand() which is sufficient for our applications and much + * more lightweight than the hardware random number generator built into + * the processor, which takes a long time and uses a lot of energy. * * @param max the upper range to generate a number for. This number cannot be negative * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_PARAMETER if max is <= 0. diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index 0e74061..cedcefa 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -1,4 +1,5 @@ #include "MicroBit.h" +#include /* rand(),srand() */ char MICROBIT_BLE_DEVICE_NAME[] = "BBC micro:bit [xxxxx]"; @@ -283,13 +284,11 @@ int MicroBit::sleep(int milliseconds) /** * Generate a random number in the given range. - * We use a simple Galois LFSR random number generator here, - * as a Galois LFSR is sufficient for our applications, and much more lightweight - * than the hardware random number generator built int the processor, which takes - * a long time and uses a lot of energy. + * We use libc's rand() which is sufficient for our applications and much + * more lightweight than the hardware random number generator built into + * the processor, which takes a long time and uses a lot of energy. * - * KIDS: You shouldn't use this is the real world to generte cryptographic keys though... - * have a think why not. :-) + * KIDS: You shouldn't use this is the real world to generate cryptographic keys though... * * @param max the upper range to generate a number for. This number cannot be negative * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE (defined in ErrorNo.h) if max is <= 0. @@ -301,60 +300,38 @@ int MicroBit::sleep(int milliseconds) */ int MicroBit::random(int max) { - uint32_t m, result; + unsigned long num_bins, num_rand, bin_size, defect; + int result; //return MICROBIT_INVALID_VALUE if max is <= 0... if(max <= 0) return MICROBIT_INVALID_PARAMETER; - // Our maximum return value is actually one less than passed - max--; + num_bins = (unsigned long) max; + num_rand = (unsigned long) RAND_MAX + 1; + bin_size = num_rand / num_bins; + defect = num_rand % num_bins; do { - m = (uint32_t)max; - result = 0; - while(m >>= 1) { - // Cycle the LFSR (Linear Feedback Shift Register). - // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneider here (a true legend in the field!), - // For those interested, it's documented in his paper: - // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors" - // https://www.schneier.com/paper-pseudorandom-sequence.html - // Avoid interupts (and hence fibre context switch) while we are doing this - - __disable_irq(); - - randomValue = ((((randomValue >> 31) - ^ (randomValue >> 6) - ^ (randomValue >> 4) - ^ (randomValue >> 2) - ^ (randomValue >> 1) - ^ randomValue) - & 0x0000001) - << 31 ) - | (randomValue >> 1); - - __enable_irq(); - - result = ((result << 1) | (randomValue & 0x00000001)); - } - } while (result > (uint32_t)max); + result = rand(); + } while ((num_rand - defect) <= (unsigned long)result); // CARE: avoid overflow - return result; + return result/bin_size; } /** * Seed our a random number generator (RNG). - * We use the NRF51822 in built cryptographic random number generator to seed a Galois LFSR. + * We use the NRF51822 in built cryptographic random number generator to seed rand(). * We do this as the hardware RNG is relatively high power, and use the the BLE stack internally, - * with a less than optimal application interface. A Galois LFSR is sufficient for our + * with a less than optimal application interface. rand() is sufficient for our * applications, and much more lightweight. */ void MicroBit::seedRandom() { - randomValue = 0; - + unsigned int seed = 0; + // Start the Random number generator. No need to leave it running... I hope. :-) NRF_RNG->TASKS_START = 1; @@ -366,11 +343,13 @@ void MicroBit::seedRandom() // Wait for a number ot be generated. while ( NRF_RNG->EVENTS_VALRDY == 0); - randomValue = (randomValue << 8) | ((int) NRF_RNG->VALUE); + seed = (seed << 8) | ((int) NRF_RNG->VALUE); } // Disable the generator to save power. NRF_RNG->TASKS_STOP = 1; + + srand(seed); } From 53337296441f7b962e6a424996f2deb3f95e51c9 Mon Sep 17 00:00:00 2001 From: Robert May Date: Wed, 18 Nov 2015 15:50:44 +0000 Subject: [PATCH 26/31] Revert "Cchange random() to use libc srand()/rand()" This reverts commit db5227872affd9cca252a1b39551d239212b9680. return to using our own random implementation. --- inc/MicroBit.h | 6 ++--- source/MicroBit.cpp | 63 ++++++++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/inc/MicroBit.h b/inc/MicroBit.h index 9056492..417b300 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -90,6 +90,7 @@ class MicroBit private: void seedRandom(); + uint32_t randomValue; public: @@ -213,9 +214,8 @@ class MicroBit /** * Generate a random number in the given range. - * We use libc's rand() which is sufficient for our applications and much - * more lightweight than the hardware random number generator built into - * the processor, which takes a long time and uses a lot of energy. + * We use the NRF51822 in built random number generator here + * TODO: Determine if we want to, given its relatively high power consumption! * * @param max the upper range to generate a number for. This number cannot be negative * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_PARAMETER if max is <= 0. diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index cedcefa..0e74061 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -1,5 +1,4 @@ #include "MicroBit.h" -#include /* rand(),srand() */ char MICROBIT_BLE_DEVICE_NAME[] = "BBC micro:bit [xxxxx]"; @@ -284,11 +283,13 @@ int MicroBit::sleep(int milliseconds) /** * Generate a random number in the given range. - * We use libc's rand() which is sufficient for our applications and much - * more lightweight than the hardware random number generator built into - * the processor, which takes a long time and uses a lot of energy. + * We use a simple Galois LFSR random number generator here, + * as a Galois LFSR is sufficient for our applications, and much more lightweight + * than the hardware random number generator built int the processor, which takes + * a long time and uses a lot of energy. * - * KIDS: You shouldn't use this is the real world to generate cryptographic keys though... + * KIDS: You shouldn't use this is the real world to generte cryptographic keys though... + * have a think why not. :-) * * @param max the upper range to generate a number for. This number cannot be negative * @return A random, natural number between 0 and the max-1. Or MICROBIT_INVALID_VALUE (defined in ErrorNo.h) if max is <= 0. @@ -300,38 +301,60 @@ int MicroBit::sleep(int milliseconds) */ int MicroBit::random(int max) { - unsigned long num_bins, num_rand, bin_size, defect; - int result; + uint32_t m, result; //return MICROBIT_INVALID_VALUE if max is <= 0... if(max <= 0) return MICROBIT_INVALID_PARAMETER; - num_bins = (unsigned long) max; - num_rand = (unsigned long) RAND_MAX + 1; - bin_size = num_rand / num_bins; - defect = num_rand % num_bins; + // Our maximum return value is actually one less than passed + max--; do { - result = rand(); - } while ((num_rand - defect) <= (unsigned long)result); // CARE: avoid overflow + m = (uint32_t)max; + result = 0; + while(m >>= 1) { + // Cycle the LFSR (Linear Feedback Shift Register). + // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneider here (a true legend in the field!), + // For those interested, it's documented in his paper: + // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors" + // https://www.schneier.com/paper-pseudorandom-sequence.html + // Avoid interupts (and hence fibre context switch) while we are doing this + + __disable_irq(); + + randomValue = ((((randomValue >> 31) + ^ (randomValue >> 6) + ^ (randomValue >> 4) + ^ (randomValue >> 2) + ^ (randomValue >> 1) + ^ randomValue) + & 0x0000001) + << 31 ) + | (randomValue >> 1); + + __enable_irq(); + + result = ((result << 1) | (randomValue & 0x00000001)); + } + } while (result > (uint32_t)max); - return result/bin_size; + return result; } /** * Seed our a random number generator (RNG). - * We use the NRF51822 in built cryptographic random number generator to seed rand(). + * We use the NRF51822 in built cryptographic random number generator to seed a Galois LFSR. * We do this as the hardware RNG is relatively high power, and use the the BLE stack internally, - * with a less than optimal application interface. rand() is sufficient for our + * with a less than optimal application interface. A Galois LFSR is sufficient for our * applications, and much more lightweight. */ void MicroBit::seedRandom() { - unsigned int seed = 0; - + randomValue = 0; + // Start the Random number generator. No need to leave it running... I hope. :-) NRF_RNG->TASKS_START = 1; @@ -343,13 +366,11 @@ void MicroBit::seedRandom() // Wait for a number ot be generated. while ( NRF_RNG->EVENTS_VALRDY == 0); - seed = (seed << 8) | ((int) NRF_RNG->VALUE); + randomValue = (randomValue << 8) | ((int) NRF_RNG->VALUE); } // Disable the generator to save power. NRF_RNG->TASKS_STOP = 1; - - srand(seed); } From f5cf8f2dd4c7b25199936aa40eb8ea1ac0d92376 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 30 Nov 2015 00:43:24 +0000 Subject: [PATCH 27/31] microbit: Minor optimisations to patch - LFSR generated values in a subset of the range requested (rounded to the nearest lower power of 2). Corrected by increasing the number of bits usedby one, such that it now generated st least the power of 2 greater. - replaced enable/disable of interrupts with a local snapshot of randomValue. Much of the nordic software is sensitive to interrupt timings, so best avoided where possible. Chance of race condition is low, and effect is minimal (duplicate number returned). --- source/MicroBit.cpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index edcc250..2f5467c 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -315,30 +315,28 @@ int MicroBit::random(int max) do { m = (uint32_t)max; result = 0; - while(m >>= 1) { + do { // Cycle the LFSR (Linear Feedback Shift Register). - // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneider here (a true legend in the field!), + // We use an optimal sequence with a period of 2^32-1, as defined by Bruce Schneier here (a true legend in the field!), // For those interested, it's documented in his paper: // "Pseudo-Random Sequence Generator for 32-Bit CPUs: A fast, machine-independent generator for 32-bit Microprocessors" // https://www.schneier.com/paper-pseudorandom-sequence.html - // Avoid interupts (and hence fibre context switch) while we are doing this - - __disable_irq(); + uint32_t rnd = randomValue; - randomValue = ((((randomValue >> 31) - ^ (randomValue >> 6) - ^ (randomValue >> 4) - ^ (randomValue >> 2) - ^ (randomValue >> 1) - ^ randomValue) + rnd = ((((rnd >> 31) + ^ (rnd >> 6) + ^ (rnd >> 4) + ^ (rnd >> 2) + ^ (rnd >> 1) + ^ rnd) & 0x0000001) << 31 ) - | (randomValue >> 1); + | (rnd >> 1); - __enable_irq(); + randomValue = rnd; - result = ((result << 1) | (randomValue & 0x00000001)); - } + result = ((result << 1) | (rnd & 0x00000001)); + } while(m >>= 1); } while (result > (uint32_t)max); From f0cbfb4c502fb9f76c7a8fe592f4c5cf662653b2 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 30 Nov 2015 00:51:26 +0000 Subject: [PATCH 28/31] microbit: Default display brightness changed to 100% Feedback from trials showed that this improved daylight visibility, especially for the first programs before kids have discovered brightness APIs. --- inc/MicroBitConfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/MicroBitConfig.h b/inc/MicroBitConfig.h index aace015..0e347ea 100644 --- a/inc/MicroBitConfig.h +++ b/inc/MicroBitConfig.h @@ -247,7 +247,7 @@ // Selects the default brightness for the display // in the region of zero (off) to 255 (full brightness) #ifndef MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS -#define MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS ((MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS - MICROBIT_DISPLAY_MINIMUM_BRIGHTNESS) / 2) +#define MICROBIT_DISPLAY_DEFAULT_BRIGHTNESS MICROBIT_DISPLAY_MAXIMUM_BRIGHTNESS #endif // Selects the default scroll speed for the display. From 2327ad44ff56152e9027ca919b60b3436b8d2f0a Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Mon, 30 Nov 2015 00:54:49 +0000 Subject: [PATCH 29/31] version v1.3.9 --- module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module.json b/module.json index 790b9aa..a557ebd 100644 --- a/module.json +++ b/module.json @@ -1,6 +1,6 @@ { "name": "microbit-dal", - "version": "1.3.8", + "version": "1.3.9", "license": "Apache2", "description": "The runtime library for the BBC micro:bit, developed by Lancaster University", "keywords": [ From 14831ac9b9399079e7d4ed9da3c59c1cd6caf2b3 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Fri, 11 Dec 2015 04:57:06 +0000 Subject: [PATCH 30/31] Updates to BLE 2.1.11 / ble-nrf51822 2.2.3 Minor amends: - bleDisconnectionCallback signature change - bleSetAdvertisingInterval now takes milliseconds as a parameter - event based invocation of DFU bootloader --- inc/MicroBit.h | 2 +- source/MicroBit.cpp | 5 ++- source/MicroBitSuperMain.cpp | 2 +- source/ble-services/MicroBitDFUService.cpp | 37 +++++++++++++++++++++- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/inc/MicroBit.h b/inc/MicroBit.h index 417b300..86d2d12 100644 --- a/inc/MicroBit.h +++ b/inc/MicroBit.h @@ -302,7 +302,7 @@ extern MicroBit uBit; // BLE callback when an active GATT session with another device is terminated. // Used to reset state and restart advertising ourselves. // -void bleDisconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason); +void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason); // Entry point for application programs. Called after the super-main function // has initialized the device and runtime environment. diff --git a/source/MicroBit.cpp b/source/MicroBit.cpp index 2f5467c..2550e37 100644 --- a/source/MicroBit.cpp +++ b/source/MicroBit.cpp @@ -31,9 +31,8 @@ microbit_reset() /** * Callback when a BLE GATT disconnect occurs. */ -void bleDisconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) +void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *reason) { - (void) handle; /* -Wunused-param */ (void) reason; /* -Wunused-param */ uBit.ble->startAdvertising(); @@ -167,7 +166,7 @@ void MicroBit::init() ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)MICROBIT_BLE_DEVICE_NAME, sizeof(MICROBIT_BLE_DEVICE_NAME)); ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); - ble->setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(200)); + ble->setAdvertisingInterval(200); ble->startAdvertising(); #endif diff --git a/source/MicroBitSuperMain.cpp b/source/MicroBitSuperMain.cpp index ed1a790..9b62a81 100644 --- a/source/MicroBitSuperMain.cpp +++ b/source/MicroBitSuperMain.cpp @@ -60,7 +60,7 @@ int main() uBit.ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); uBit.ble->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)MICROBIT_BLE_DEVICE_NAME, sizeof(MICROBIT_BLE_DEVICE_NAME)); uBit.ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); - uBit.ble->setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(200)); + uBit.ble->setAdvertisingInterval(200); uBit.ble->startAdvertising(); } diff --git a/source/ble-services/MicroBitDFUService.cpp b/source/ble-services/MicroBitDFUService.cpp index 96277fa..f6915be 100644 --- a/source/ble-services/MicroBitDFUService.cpp +++ b/source/ble-services/MicroBitDFUService.cpp @@ -17,6 +17,31 @@ #include "MicroBit.h" #include "ble/UUID.h" +#if !defined(__arm) +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +/* + * The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ + * If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL) + * The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this + * as a compatability option, but does not support the options used... + */ +extern "C" { +#include "dfu_app_handler.h" +} + +/* + * Return to our predefined compiler settings. + */ +#if !defined(__arm) +#pragma GCC diagnostic pop +#endif + + + /** * Constructor. * Create a representation of a MicroBit device. @@ -121,7 +146,17 @@ void MicroBitDFUService::onDataWritten(const GattWriteCallbackParams *params) #if CONFIG_ENABLED(MICROBIT_DBG) uBit.serial.printf(" ACTIVATING BOOTLOADER.\n"); #endif - bootloader_start(); + + + // Call bootloader_start implicitly trough a event handler call + // it is a work around for bootloader_start not being public in sdk 8.1 + ble_dfu_t p_dfu; + ble_dfu_evt_t p_evt; + + p_dfu.conn_handle = params->connHandle; + p_evt.ble_dfu_evt_type = BLE_DFU_START; + + dfu_app_on_dfu_evt(&p_dfu, &p_evt); } break; From de28387ff3172927d9ddaafefec6e4d55f7e2144 Mon Sep 17 00:00:00 2001 From: Joe Finney Date: Fri, 11 Dec 2015 04:57:31 +0000 Subject: [PATCH 31/31] version v1.3.10 --- module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module.json b/module.json index a557ebd..f8fdbf8 100644 --- a/module.json +++ b/module.json @@ -1,6 +1,6 @@ { "name": "microbit-dal", - "version": "1.3.9", + "version": "1.3.10", "license": "Apache2", "description": "The runtime library for the BBC micro:bit, developed by Lancaster University", "keywords": [