From 057158c9f3a31a5c87902f9705c690c4f14a74bb Mon Sep 17 00:00:00 2001 From: Michal Moskal Date: Sat, 24 Oct 2015 19:28:36 -0700 Subject: [PATCH 01/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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); } /**