microbit-dal/source/ManagedString.cpp

367 lines
9 KiB
C++
Raw Normal View History

#include <string.h>
#include <stdlib.h>
microbit: Memory Optimisation Mega Update This release contains a widespread set of updates and optimisations to the micro:bit runtime, with a view to reducing the SRAM footprint of the whole system. This is to provide as much usable HEAP storage for application programs as possible. Specific updates and optimisations include: - Additional compilation flags to allow the core micro:bit runtime to be configured. These are defined in MicroBitConfig.h - A custom heap allocator. This is now included for two reasons: 1) To provide a simple mechanism to to utilise both the mbed heap space and other memory regions (such as unused memory in the SoftDevice region) as a single virtual heap. 2) To address some issues that have been noted that are attributable to heap fragmentation. The micro:bit heap allocator has a simple algorithm, but one that is chosen to respond well to the relativelt high 'heap churn' found in the micro:bit environment. All micro:bit components and user programs now use this heap allocator trasparently. - Updates to BLE services to remove persistent references to their GATT services. This consumes vast amounts SRAM, rather unecessarily. Instead only handles to the relevant GATT characteristics are now stored. This specifically includes: + MicroBitDFUService + MicroBitEventService + DeviceInformationService - Updates to the Fiber scheduler to save SRAM. More specifically: + Removed the need to hold an empty processor context to intialise fibers. + The IDLE fiber now runs without a stack + fiber stacks are now only created when a fiber is descheduled for the first time, thereby reducing heap churn. + the 'main' fiber is now recycled into the fiber_pool if it leaves app_main() + fibers created through invoke() now only maintains the necessary part of teh parent stack that is needed, thereby reducing the stack size of spawned fibers. - Updates to the Message Bus to reduce the overall memory footprint of processing events. More specifically: + Event handlers are now always called using invoke(), such that non-blocking event handlers no longer need a dedicated fiber to execute - thereby saving SRAM and processor time. + Processing of events from the event queue is now rate paced. Events only continue to be processed as long as there are no fibers on the run queue. i.e. event processing is no longer greedy, thereby reducing the number of fibers created on the runqueue. - Updates to BLUEZOENE code to bring up core BLE services even if they are not enabled by default. This allows programs that do not require BLE to operate to benefit from the full range of SRAM, whilst still allowing the device to be programmed over BLE. - Updates to the Soft Device initialisation configuration, reducing the size of the GATT table held in the top 1.8K of its 8K memory region to around 800 bytes. This is sufficient to run the default set of BLE services on the micro:bit so the additional memory is configured as HEAP storage by MicroBitHeapAllocator. - Minor changes to a range of components to integrate with the above changes. + rename of free() to release() in DynamicPWM to avoid namespace collision with MicroBitHeap free() + rename of fork_on_block to invoke() to enhance readbility. - Many code cleanups and updates to out of date comments.
2015-08-31 22:25:10 +00:00
#include "mbed.h"
microbit: Added configurable concurrency modes for MicroBitMessageBus handlers. MessageBus handlers can now have one of four concurrency modes for the eventuality of an event being raised whilst a previous event is still being processed. An additional (optional) parameter is provided to the listen() functions to allow this to be selected on a per event handler basis. The permissable options are: MESSAGE_BUS_LISTENER_REENTRANT: The event handler is fired with the new event, regardless of whether or not a previous event is still be processed by that handler. MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY: The new event is queued until such a time as the previous event has completed execution. The new event is then processed. This option does not preclude the processing of the new event by other event handlers. MESSAGE_BUS_LISTENER_DROP_IF_BUSY: The new event is dropped, and will never be processed the the event handler. This option does not preclude the processing of the new event by other event handlers. MESSAGE_BUS_LISTENER_NONBLOCKING: The event handler is self-declaring that it never blocks. This flag is used purely for optimisation, as it permits direct execution of the event hadnelr without inducing any overhead from the scheduler. In addition, the following minor revisions were made in this release: * Cleanup of the #include dependencies contained in the microbit-dal .h files * Bugfix to the scheduler block on event matching code. * Introduced a MICROBIT_ID_ALERT MessageBus channel, for general purpose eventing using nonces.
2015-09-11 15:39:38 +00:00
#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.
* Configures this ManagedString to refer to the static EmptyString
*/
void ManagedString::initEmpty()
{
ptr = (StringData*)(void*)empty;
}
/**
* Internal constructor helper.
* creates this ManagedString based on a given null terminated char array.
*/
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.
int len = strlen(str);
ptr = (StringData *) malloc(4+len+1);
ptr->init();
ptr->len = len;
memcpy(ptr->data, str, len+1);
}
/**
* Constructor.
* Create a managed string from a given integer.
*
* @param value The integer from which to create the ManagedString
*
* Example:
* @code
* ManagedString s(20);
* @endcode
*/
ManagedString::ManagedString(const int value)
{
char str[12];
itoa(value, str);
initString(str);
}
/**
* Constructor.
* Create a managed string from a given char.
*
* @param value The char from which to create the ManagedString
*
* Example:
* @code
* ManagedString s('a');
* @endcode
*/
ManagedString::ManagedString(const char value)
{
char str[2] = {value, 0};
initString(str);
}
/**
* Constructor.
* Create a managed string from a pointer to an 8-bit character buffer.
* The buffer is copied to ensure sane 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.
*/
ManagedString::ManagedString(const char *str)
{
// Sanity check. Return EmptyString for anything distasteful
if (str == NULL || *str == 0)
{
initEmpty();
return;
}
initString(str);
}
ManagedString::ManagedString(const ManagedString &s1, const ManagedString &s2)
{
// Calculate length of new string.
int len = s1.length() + s2.length();
// Create a new buffer for holding the new string data.
ptr = (StringData*) malloc(4+len+1);
ptr->init();
ptr->len = len;
// Enter the data, and terminate the string.
memcpy(ptr->data, s1.toCharArray(), s1.length());
memcpy(ptr->data + s1.length(), s2.toCharArray(), s2.length());
ptr->data[len] = 0;
}
/**
* Constructor.
* Create a managed string from a pointer to an 8-bit character buffer of a given length.
* The buffer is copied to ensure sane memory management (the supplied
* character buffer may be declared on the stack for instance).
*
* @param str The character array on which to base the new ManagedString.
* @param length The length of the character array
*
* Example:
* @code
* ManagedString s("abcdefg",7); // this is generally used for substring... why not use a normal char * constructor?
* @endcode
*/
ManagedString::ManagedString(const char *str, const int16_t length)
{
// Sanity check. Return EmptyString for anything distasteful
if (str == NULL || *str == 0 || length > strlen(str))
{
initEmpty();
return;
}
// 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->len = length;
memcpy(ptr->data, str, length);
ptr->data[length] = 0;
}
/**
* Copy constructor.
* Makes a new ManagedString identical to the one supplied.
* Shares the character buffer and reference count with the supplied ManagedString.
*
* @param s The ManagedString to copy.
*
* Example:
* @code
* ManagedString s("abcdefg");
* ManagedString p(s);
* @endcode
*/
ManagedString::ManagedString(const ManagedString &s)
{
ptr = s.ptr;
ptr->incr();
}
/**
* Default constructor.
*
* Create an empty ManagedString.
*
* Example:
* @code
* ManagedString s();
* @endcode
*/
ManagedString::ManagedString()
{
initEmpty();
}
/**
* Destructor.
*
* 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.
*/
ManagedString::~ManagedString()
{
ptr->decr();
}
/**
* Copy assign operation.
*
* Called when one ManagedString is assigned the value of another.
* If the ManagedString being assigned is already refering to a character buffer,
* decrement the reference count and free up the buffer as necessary.
* Then, update our character buffer to refer to that of the supplied ManagedString,
* and increase its reference count.
*
* @param s The ManagedString to copy.
*
* Example:
* @code
* ManagedString s("abcd");
* ManagedString p("efgh");
* p = s // p now points to s, s' ref is incremented
* @endcode
*/
ManagedString& ManagedString::operator = (const ManagedString& s)
{
if (this->ptr == s.ptr)
return *this;
ptr->decr();
ptr = s.ptr;
ptr->incr();
return *this;
}
/**
* Equality operation.
*
* Called when one ManagedString is tested to be equal to another using the '==' operator.
*
* @param s The ManagedString to test ourselves against.
* @return true if this ManagedString is identical to the one supplied, false otherwise.
*
* Example:
* @code
* ManagedString s("abcd");
* ManagedString p("efgh");
*
* if(p==s)
* print("We are the same!");
* else
* print("We are different!"); //p is not equal to s - this will be called
* @endcode
*/
bool ManagedString::operator== (const ManagedString& s)
{
return ((length() == s.length()) && (memcmp(toCharArray(),s.toCharArray(),s.length())==0));
}
/**
* Inequality operation.
*
* Called when one ManagedString is tested to be less than another using the '<' operator.
*
* @param s The ManagedString to test ourselves against.
* @return true if this ManagedString is alphabetically less than to the one supplied, false otherwise.
*
* Example:
* @code
* ManagedString s("a");
* ManagedString p("b");
*
* if(s<p)
* print("a is before b!"); //a is before b
* else
* print("b is before a!");
* @endcode
*/
bool ManagedString::operator< (const ManagedString& s)
{
return (memcmp(toCharArray(), s.toCharArray(), min(length(),s.length()))<0);
}
/**
* Inequality operation.
*
* Called when one ManagedString is tested to be greater than another using the '>' operator.
*
* @param s The ManagedString to test ourselves against.
* @return true if this ManagedString is alphabetically greater than to the one supplied, false otherwise.
*
* Example:
* @code
* ManagedString s("a");
* ManagedString p("b");
*
* if(p>a)
* print("b is after a!"); //b is after a
* else
* print("a is after b!");
* @endcode
*/
bool ManagedString::operator> (const ManagedString& s)
{
return (memcmp(toCharArray(), s.toCharArray(), min(length(),s.length()))>0);
}
/**
* Extracts a ManagedString from this string, at the position provided.
*
* @param start The index of the first character to extract, indexed from zero.
* @param length The number of characters to extract from the start position
* @return a ManagedString representing the requested substring.
*
* Example:
* @code
* ManagedString s("abcdefg");
*
* print(s.substring(0,2)) // prints "ab"
* @endcode
*/
ManagedString ManagedString::substring(int16_t start, int16_t length)
{
// If the parameters are illegal, just return a reference to the empty string.
if (start >= this->length())
return ManagedString(ManagedString::EmptyString);
// Compute a safe copy length;
length = min(this->length()-start, length);
// Build a ManagedString from this.
return ManagedString(toCharArray()+start, length);
}
/**
* Concatenates this string with the one provided.
*
* @param s The ManagedString to concatenate.
* @return a new ManagedString representing the joined strings.
*
* Example:
* @code
* ManagedString s("abcd");
* ManagedString p("efgh")
*
* print(s + p) // prints "abcdefgh"
* @endcode
*/
ManagedString ManagedString::operator+ (ManagedString& s)
{
// If the other string is empty, nothing to do!
if(s.length() == 0)
return *this;
if (length() == 0)
return s;
return ManagedString(*this, s);
}
/**
* Provides a character value at a given position in the string, indexed from zero.
*
* @param index The position of the character to return.
* @return the character at posisiton index, zero if index is invalid.
*
* Example:
* @code
* ManagedString s("abcd");
*
* print(s.charAt(1)) // prints "b"
* @endcode
*/
char ManagedString::charAt(int16_t index)
{
return (index >=0 && index < length()) ? ptr->data[index] : 0;
}
/**
* Empty string constant literal
*/
ManagedString ManagedString::EmptyString((StringData*)(void*)empty);