First stab at more memory efficient strings

This commit is contained in:
Michal Moskal 2015-10-24 19:34:04 -07:00
parent 057158c9f3
commit 5a10bda561
2 changed files with 120 additions and 102 deletions

View File

@ -3,6 +3,29 @@
#include "mbed.h" #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. * 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 // Internally we record the string as a char *, but control access to this to proide immutability
// and reference counting. // and reference counting.
char *data; StringData *ptr;
int16_t *ref;
int16_t len;
public: 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. * Constructor.
* Create a managed string from a pointer to an 8-bit character buffer. * 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). * character buffer may be decalred on the stack for instance).
* *
* @param str The character array on which to base the new ManagedString. * @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. * @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. * Determines the length of this ManagedString in characters.
@ -269,7 +308,10 @@ class ManagedString
* print(s.length()) // prints "4" * print(s.length()) // prints "4"
* @endcode * @endcode
*/ */
int16_t length(); int16_t length() const
{
return ptr->size;
}
/** /**
* Empty String constant * Empty String constant

View File

@ -3,6 +3,9 @@
#include "mbed.h" #include "mbed.h"
#include "MicroBit.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. * Internal constructor helper.
@ -10,11 +13,7 @@
*/ */
void ManagedString::initEmpty() void ManagedString::initEmpty()
{ {
data = ManagedString::EmptyString.data; ptr = (StringData*)(void*)empty;
ref = ManagedString::EmptyString.ref;
len = ManagedString::EmptyString.len;
(*ref)++;
} }
/** /**
@ -25,11 +24,11 @@ void ManagedString::initString(const char *str)
{ {
// Initialise this ManagedString as a new string, using the data provided. // Initialise this ManagedString as a new string, using the data provided.
// We assume the string is sane, and null terminated. // We assume the string is sane, and null terminated.
len = strlen(str); int len = strlen(str);
data = (char *) malloc(len+1); ptr = (StringData *) malloc(4+len+1);
memcpy(data, str, len+1); ptr->size = len;
ref = (int16_t *) malloc(sizeof(int16_t)); ptr->refcnt = 1;
*ref = 1; memcpy(ptr->data, str, len+1);
} }
@ -65,7 +64,6 @@ ManagedString::ManagedString(const int value)
*/ */
ManagedString::ManagedString(const char value) ManagedString::ManagedString(const char value)
{ {
char str[2] = {value, 0}; char str[2] = {value, 0};
initString(str); initString(str);
} }
@ -82,7 +80,7 @@ ManagedString::ManagedString(const char value)
ManagedString::ManagedString(const char *str) ManagedString::ManagedString(const char *str)
{ {
// Sanity check. Return EmptyString for anything distasteful // Sanity check. Return EmptyString for anything distasteful
if ((str == NULL || *str == 0) && this != &ManagedString::EmptyString) if (str == NULL || *str == 0)
{ {
initEmpty(); initEmpty();
return; return;
@ -94,19 +92,17 @@ ManagedString::ManagedString(const char *str)
ManagedString::ManagedString(const ManagedString &s1, const ManagedString &s2) ManagedString::ManagedString(const ManagedString &s1, const ManagedString &s2)
{ {
// Calculate length of new string. // 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. // 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. // Enter the data, and terminate the string.
memcpy(data, s1.data, s1.len); memcpy(ptr->data, s1.toCharArray(), s1.length());
memcpy(data + s1.len, s2.data, s2.len); memcpy(ptr->data + s1.length(), s2.toCharArray(), s2.length());
data[len] = 0; ptr->data[len] = 0;
// Initialise the ref count and we're done.
ref = (int16_t *) malloc(sizeof(int16_t));
*ref = 1;
} }
@ -133,17 +129,14 @@ ManagedString::ManagedString(const char *str, const int16_t length)
return; return;
} }
// Store the length of the new string
len = length;
// Allocate a new buffer, and create a NULL terminated string. // Allocate a new buffer, and create a NULL terminated string.
data = (char *) malloc(len+1); ptr = (StringData*) malloc(4+length+1);
memcpy(data, str, len); // Store the length of the new string
data[len] = 0; ptr->size = length;
ptr->refcnt = 1;
// Initialize a refcount and we're done. memcpy(ptr->data, str, length);
ref = (int16_t *) malloc(sizeof(int16_t)); ptr->data[length] = 0;
*ref = 1;
} }
/** /**
@ -161,11 +154,8 @@ ManagedString::ManagedString(const char *str, const int16_t length)
*/ */
ManagedString::ManagedString(const ManagedString &s) ManagedString::ManagedString(const ManagedString &s)
{ {
data = s.data; ptr = s.ptr;
ref = s.ref; ptr->incr();
len = s.len;
(*ref)++;
} }
@ -193,11 +183,7 @@ ManagedString::ManagedString()
*/ */
ManagedString::~ManagedString() ManagedString::~ManagedString()
{ {
if(--(*ref) == 0) ptr->decr();
{
free(data);
free(ref);
}
} }
/** /**
@ -220,19 +206,12 @@ ManagedString::~ManagedString()
*/ */
ManagedString& ManagedString::operator = (const ManagedString& s) ManagedString& ManagedString::operator = (const ManagedString& s)
{ {
if(this == &s) if (this->ptr == s.ptr)
return *this; return *this;
if(--(*ref) == 0) ptr->decr();
{ ptr = s.ptr;
free(data); ptr->incr();
free(ref);
}
data = s.data;
ref = s.ref;
len = s.len;
(*ref)++;
return *this; return *this;
} }
@ -258,7 +237,7 @@ ManagedString& ManagedString::operator = (const ManagedString& s)
*/ */
bool 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) 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) 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) ManagedString ManagedString::substring(int16_t start, int16_t length)
{ {
// If the parameters are illegal, just return a reference to the empty string. // If the parameters are illegal, just return a reference to the empty string.
if (start >= len) if (start >= this->length())
return ManagedString(ManagedString::EmptyString); return ManagedString(ManagedString::EmptyString);
// Compute a safe copy length; // Compute a safe copy length;
length = min(len-start, length); length = min(this->length()-start, length);
// Build a ManagedString from this. // 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) 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 the other string is empty, nothing to do!
if(s.len == 0) if(s.length() == 0)
return *this; return *this;
if (len == 0) if (length() == 0)
return s;
if(s == ManagedString::EmptyString)
return *this;
if(*this == ManagedString::EmptyString)
return s; 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) 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()
{
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;
} }
/** /**
* Empty string constant literal * 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()
{
}