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; + return (index >=0 && index < length()) ? ptr->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() + * Empty string constant literal + */ +ManagedString ManagedString::EmptyString((StringData*)(void*)empty); + + +void RefCounted::incr() { - return data; + if (refcnt == 0xffff) + return; + if (refcnt == 0) + uBit.panic(30); + refcnt++; } -/** - * 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() +void RefCounted::decr() { - return len; + 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); + } } -/** - * Empty string constant literal - */ -ManagedString ManagedString::EmptyString("\0"); - - +VirtualRefCounted::~VirtualRefCounted() +{ +}