Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

The Palm Memory Manager API

0.00/5 (No votes)
5 Nov 2002 3  
How to use Palm's Memory Manager API for dynamic memory allocation in palm handheld applications.

Introduction

Memory management on Palm handhelds is quite different from standard C/C++ and windows memory management that you may be used to. This article will try to shed some light on the crazy world of Palm memory.

Memory is Limited

Although newer Palms are getting more and more RAM most still have under 16MB and none of them have hard drives. All applications and databases are stored in the RAM so Palms have to do things a little differently than PCs. We wouldn't want our applications overwriting permanent storage now would we.

The RAM is divided into two sections: the storage area and the dynamic area (or dynamic heap). As you can guess, the storage area is where permanent data resides, like applications and databases, and is handled by the Database Manager. The dynamic heap is used for Palm OS globals, Palm OS dynamic allocations, your application's global variables, your application's stack space, and any dynamic allocations your application makes. The dynamic heap is handled by the Memory Manager and its size depends on the amount of RAM your Palm has, the OS version and the applications you have installed.

Because of the limited amount of dynamic memory, the Palm OS needs to be able to move chunks of memory around to keep its free space contiguous so there is enough room for new allocation. Thus there are two types of memory chunks, pointers and handles. Pointers are nonmovable chunks of memory, whereas handles are movable by the Palm OS. It is recommended that you use handles as much as possible.

The Memory Manager API

In order to manipulate memory on the Palm, you must use the Memory Manager API.

Using Handles

Lets first look at how we can allocate a new memory handle using the API.

  • The MemHandle MemHandleNew (UInt32 size) function returns a newly allocated MemHandle of the size in bytes specified in the size parameter. FYI: You cannot allocate a single memory chunk larger than 64KB.

Since the OS can move this handle around at will, you need to "lock" it in order to read or write to it.

  • Use MemPtr MemHandleLock (MemHandle h) to lock a handle and obtain a MemPtr (pointer) to the handle's chunk of memory.

After using a locked handle, you must "unlock" it quickly so the OS can move it around again.

  • To unlock a previously locked handle, use Err MemHandleUnlock (MemHandle h).

When you are finished with a dynamically created handle, you need to call Err MemHandleFree (MemHandle h) to dispose of it.

Here is an example of using the Memory Manager functions for handles:

/* example of handle allocation and use */
MemHandle myHandle = MemHandleNew(13);          // create a 13-byte handle
Char* myStr = (Char*)MemHandleLock(myHandle);   // lock the handle before use
StrCopy(myStr, "Hello World!");                 // use it
MemHandleUnlock(myHandle);                      // unlock the handle
MemHandleFree(myHandle);                        // free the handle

When a handle is locked, the OS increments a lock count for that handle. You can lock a handle up to 14 times before you receive a "chunk overlocked" error. Each lock you perform on a handle, you must also unlock. If you unlock a handle more times than the lock count, you will receive a "chunk underlocked" error.

An alternative to the MemHandleUnlock function is Err MemPtrUnlock (MemPtr p). This is handy if you just want to pass your locked pointer to another function and your handle is not available. It decreases the lock count on the locked handle just like MemHandleUnlock.

Using Pointers

To allocate a nonmovable chunk of memory (a pointer), use MemPtr MemPtrNew (UInt32 size). FYI: Remember, you cannot allocate a single memory chunk larger than 64KB.

When you are finished with the pointer, call Err MemPtrFree (MemPtr p) to free the chunk.

Here is an example of using the pointer functions:

/* example of handle allocation and use */
Char* myStr = (Char*) MemPtrNew(13);  // allocate a 13-byte pointer
StrCopy(myStr, "Hello World!");       // use it
MemPtrFree(myStr);                    // free the pointer

You should use pointers instead of handles if you need the memory throughout your entire application, or if your allocation will be short lived. Keep in mind that frequent locking and unlocking of handles will also incur a performance cost over pointer allocation.

Other Memory Functions

Err MemSet (void* dstP, Int32 numBytes, UInt8 value) will set numBytes of the dstP pointer to value (just like C++'s memset).

Err MemMove (void* dstP, const void* sP, Int32 numBytes) will move numBytes of the sP source pointer to the dstP destination pointer, handling overlapping ranges automatically.

UInt32 MemHandleSize (MemHandle h) reports the size in bytes of a handle.

UInt32 MemPtrSize (MemPtr p) reports the size in bytes of a pointer.

To resize an unlocked handle, call Err MemHandleResize (MemHandle h, UInt32 newSize). If you are making the handle larger and there is not enough free space after the handle, the OS will move the chunk to a new location.

If you need to resize a pointer, call Err MemPtrResize (MemPtr p, UInt32 newSize). A pointer will only be resized if you are making it smaller or if there is enough free space directly after the pointer. This function may also be used on a locked handle given its pointer.

The Demo

I modified the "Hello World!" application from my first palm article, An Introduction to Palm Handheld Development, to show how you would use the various Memory Manager functions. As before, the demo was compiled with Metrowerk's CodeWarrior IDE (version 6).

A static/global MemHandle, called myHandle, was added which will hold the text to display on the screen. This handle is created in the MainFormInit function then locked in the SayHello or SayGoodbye functions. The pointer returned from the lock is then passed to the DrawText function for display to the screen. After returning from the drawing function, the handle or pointer is unlocked. The handle is freed in the MainFormClose function.

As you will notice from the code, myHandle is being resized using MemHandleResize and there are also calls to MemSet and MemMove in there just so you can see an example of how they are used. MemPtrResize was not used in the demo since it may not always work. It is essentially the same as resizing a handle.

Here are the main code highlights:

// need a global handle for demo purposes
static MemHandle myHandle = NULL;
static short nCount = 0;

// This routine draws text on the center of the form
static void DrawText(Char* pText)
{
	short nCharWidth = 0;
	short width = 0, height = 0;
	
	// get the width of the string		
	nCharWidth = FntCharsWidth(pText, StrLen(pText));  
	
	// get the width and height of the string
	WinGetWindowExtent(&width, &height);
	
	// draw the text in the center
	WinDrawChars(pText, StrLen(pText), (width/2) - (nCharWidth/2), height/2);
}


// This routine draws "Hello World!" on the form
static void SayHello()
{
	// resize the handle only if you need to
	if (MemHandleSize(myHandle) == 13 || 
		MemHandleResize(myHandle, 13) == 0)
	{
		// lock the handle before use 
		Char* pText = (Char*) MemHandleLock(myHandle);  
		
		// make sure none of the previous string is left in
		MemSet(pText, 13, 0);	
		
		// copy Hello World! into the char ptr
		StrCopy(pText, "Hello World!");

		// pass the pointer and draw it on the screen
		DrawText(pText);
	
		// unlock the handle
		MemHandleUnlock(myHandle);
	}
}


// This routine draws "Goodbye World!" on the form
static void SayGoodbye()
{
	nCount++;
	
	// resize the handle only if you need to
	if (MemHandleSize(myHandle) == 15 || 
		MemHandleResize(myHandle, 15) == 0)
	{
		// lock the handle before use  
		Char* pText = (Char*) MemHandleLock(myHandle);  
		
		// make sure none of the previous string is left in	
		MemSet(pText, 15, 0);	

		// copy Goodbye World! into the char ptr
		StrCopy(pText, "Goodbye World!");
		
		// Lets use MemMove
		if (nCount%2 == 0)
		{
			Char* pTemp = (Char*) MemPtrNew(15);
			
			StrCopy(pTemp, "Bye bye World!");
			
			MemMove(pText, pTemp, 7);
			
			MemPtrFree(pTemp);
			pTemp = NULL;
		}
				
		// pass the pointer and draw it on the screen
		DrawText(pText);
		
		// unlock the pointer in this function
		MemPtrUnlock(pText);
	}
}


// This routine initializes the MainForm form.
static void MainFormInit(FormType* frmP)
{
	// we're going to lock this handle in our SayWhat functions
	// then we'll pass the pointer to the DrawText function
	myHandle = MemHandleNew(20);  // allocate 20 bytes
}


// This routine closes the MainForm form.
static void MainFormClose()
{
	// free the handle
	if (myHandle != NULL) 
	{
		MemHandleFree(myHandle);
		myHandle = NULL;
	}
}

Conclusion

From my own experience, Palm memory handling can be quite harrowing. Writing this article has helped me understand the whole system a little better and I hope it will help you as well

Look for more palm articles in the near future from myself and Christian Graus. We plan to cover palm databases, custom drawn lists, tables, palm conduits and other relevant topics specific to the PalmOS.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here