Join Now

Hi, I'm Paul, but you can also call me Todd and I won't get upset.

FileBrowser

Paul.Todd | 29 June, 2006 23:56

I've been busy working on a new FileBrowser for the S60 3rd Edition that should help developers find out what is going on under the hood.

I'm looking for a couple of people to beta test it and provide feedback what they like or dislike or what they would want to see.

You must have a valid ACSID.

Please email me by clicking the "Feedback to Author" link on the side if you are interested.

If you have anything particular you would like please add a comment and I will see what I can do.

Update: 04/07/06 - The people requesting a version should have recieved it today. Sorry for the delay.
If you have still not recieved it please email me (paul@toddsoftware.com)

SyExpat Port to OS 9.x

Paul.Todd | 21 June, 2006 21:12

I was going to post this on Sunday but with the uncharacteristically good British Summer and England playing Sweden
in football, this slipped over to Wednesday.

Originally I thought that porting SyExpat to OS 9.x would involve a large number of changes.
In the end this turned out to be not the case.

The simplest fix was to xmlparse.c. It seems that the expat guys mixed up the return code enum and the error
code enum, so changing it is all that is needed for the main expat source code.

A slightly tricker problem was the with GCCE. It seems that it likes to define XMLCALL to something. GCCE seems to think it supports Microsoft extensions but it only does it in some cases.I think it was _MSC_EXTENSIONS, so in order to fix this we need to make sure that expat_config.h is defined before expat.h is included in the source files. This will then define XMLCALL and so stop expat_external.h defining it to the default.

The only change then resolves to just make the SyExpat.cpp file include the expat_config file before expat.h
as this sets up a  number of defines that specify how the functions are to be exported and so this sorts out the export declarations not being handles correctly.

Hence we just define XML call as blank and this fixes the compiler issues. See expat_config.h for more
information.

This is now sufficient to be able to build SyExpat for OS 9.x and still retain backward compatibility.

Unfortunately I have run into rather a nasty issue with Carbide.vs and the standard C library.
The whole sorry tale is here - http://discussion.forum.nokia.com/forum/showthread.php?t=75575 so for the
moment it seems there are some serious OS9 issues to fix first, even in the maintentance release.

Thanks to Harri Salminen for his fix for name space parsing which has now bee incorporated as well.

If anyone has any specific problem areas in Symbian they would like covered please email me and I will try to knock something up to show it is supposed to work.

Next week I will look at how to use MobInfo (and CTelephony) as a number of people seem to have a problem understanding how to use active objects with these libraries.

Addendum:
I have just found out I cannot attach files to blog entries so I will put the actual revised code up on my website.

RArray and Resource Mangement

Paul.Todd | 12 June, 2006 00:37

 

If you are not using RArray, you should be!
It offers a substantial performance improvement over the CArray.. implementations for almost all the cases I can think of.

  • It is fast - RArray is coded in ARM assembly in release versions of the code.
  • It does not use asserts internally and also does not leave, it just returns error codes, so the use of trap statements in runtime significant areas of the code can be minimized or reduced.
  • It is lightweight
  • It is a wrapper over a more complex implementation of a generic array.
  • There are templated version of TInt and TUint built into EUSER so it does not bloat your executable.

Most of RArray is fairly simple to understand and works the way any generic array implementation works.

The biggest problem I do have with RArray is that the search and insert functions do not allow a variable to be passed to them so you are required to use a global or some other hack to get a state variable over into the comparison function.

The areas I am going look at are how RArray manages resources and how RArray can be applied effectively used to improve performance.

Before we can continue there are some questions that need to be answered:

How does RArray allocates memory?
If you play around with RArray you will soon find out that it allocates memory via the User::Alloc functions and these allocations are always a multiple of 4 bytes.

What RArray Constructors are there?
There is a default constructor, a constructor that specifies by how much the array will be expanded when it runs out of spare slots, and most importantly a constructor that maps onto a memory locations- but more of that later.

What is not present is a copy constructor, so what will happen then is that the C++ compiler will create a default constructor that is a bitwise copy of the item it is being copied from.

By default RArray does not have any items, however if one item is added, it preallocates by default 8 items, well okay KGranularity items so it can avoid memory fragmentation when adding items by avoiding a reallocate and copy each time an item is added.

Resource Management:
For the purposes of the example we will create a structure called TVector which has two integers in it.
THIS STRUCTURE IS A MUTIPLE OF 4 BYTES WHICH IS MANDATORY FOR RARRAY ELEMENTS!

struct TVector
{
  TInt iSpeed;
  TInt iDirection;
};
typedef RArray<TVector> RVectorArray; // Note that we keep the R prefix on our typedef

(1) static const TVector data[] = { {1,1}, {2,2}, {3,3} };
(2) const RVectorArray array(sizeof(TVector), CONST_CAST(TVector*,&data[0]), sizeof(data) / sizeof(data[0]));
(3) RVectorArray array1;
(4) array1 = array;
(5) TInt count = array.Count();

Each of the lines is numbered and documented as follows:
(1) we create a static const memory block. This could also be a block of memory from somewhere else such as a scanline or bitmap row.
(2) We now create an RArray that maps that memory into the RArray, note that the array is const so we can't expand it by accident.

It is important to read the documentation on this as it is very clear that this constructor does not allocate any memory nor take ownership of the memory, it just provides a mapping for developers to use the RArray with blocks of memory that needs to be manipulated.

(3) We now create a default RArray which has not memory allocated and it does not have any elements.

(4) Finally we create a clone of the array. It is vital to note that the compiler has underneath generated a default copy constructor and created a bitwise clone of data into array.

(5) Finally get the number of items in array after we have cloned it. What is the value of count?
Answer: See below

IMPORTANT NOTES:
(2) This just creates a mapping of the memory structure into the RArray is does not force the RArray to allocate any memory, hence there is nothing that needs to be put onto the cleanup stack. This is a very important point that a lot of people get wrong.
(4) This creates a clone of the array but ownership of the items in the data array is not taken
(5) Since array is a clone of data, count is 3
(6) When the function goes out of scope the arrays are destroyed, but we do not call reset. This is very important, because the arrays do not own any memory they MUST NOT be freed, so calling Reset is a big no-no! Likewise putting it on the cleanup stack is also a big mistake and will cause crashes.

Resource Management Part 2:
As I mentioned earlier, the underyling memory allocator for RArray is User::Alloc, so providing we allocate memory via this mechanism, we can move memory blobs from our structure into an RArray and then use the RArray.

(1) TVector* data2 = REINTERPRET_CAST(TVector*, User::AllocL(sizeof(data))); // actually sizeof(data) / (sizeof(data) / sizeof(data[0]))
(2) Mem::Copy(data2, data, sizeof(data));
(3) RVectorArray array2(sizeof(TVector), data2, sizeof(data) / (sizeof(data) / sizeof(data[0])));
(4) CleanupClosePushL(array2);
(5) const TVector element = {4,4};
(6) array2.Append(element);
(7) count = array2.Count();
(8) CleanupStack::PopAndDestroy(&array2);

Each of the lines is nummered and documented as follows:
(1) We allocate a block of memory using User::Alloc that is size of the 'data' structure (3 * sizeof(TVector))
This data is not put onto the cleanup stack, though of course we could put it onto the cleanup stack here and then remove it from the cleanup stack on line (4) and add the RArray back to the cleanup stack.
(2) We now copy the data from the const memory structure to our newly allocated memory blob
(3) We now create an RArray that maps the memory we just allocated to the internals of an RArray.
(4) Because we allocated the memory somewhere else, I decided that the RArray should now take ownership of the array, so that if any of the following lines should leave the memory will get correctly cleaned up by the RArray.
(5) we declare a new element
(6) This element is now added to our array that owns the memory. Where the array is full, the array will be expanded to cater for the new element, which may result in a memory reallocation, hence BE VERY CAREFUL where ownership of memory blocks is made.
(7) What should count now report? Answer: 4
(8) As we transfered ownship of the memory block to the array2 RArray, the cleanup stack may now delete it. If we did not put it onto the cleanup stack, we can just call Reset which has the same effect.

IMPORTANT NOTES:

(4) Is very important as you want to move ownership of the memory block to the RArray as soon as possible, especially if you want to add or remove items from the RArray.
(6) We should really check the return value from the append as it could have returned a negative value which would indicate that the item failed to expand. I have seen a number of cases where the error code is never checked and so unexpected things start happening.
(8) We do not neccessarily need to put the array2 as a parmeter to the PopAndDestroy but it provides an additional check that the item at the top of the stack is the item we expected it to be.

Some other things about RArray and RPointerArray

  • This is an advanced example of how to use resource management and RArray. Its not for beginners.
  • In real life I have used to read a block of integers (5000) from disk as a TPtrC8 and then cast it and move it to the RArray where the RArray takes ownership of the data. Which knocked about 10 seconds off the load time.
  • The arrays only take items that are 4 bytes or a multiple of 4 bytes, so if your object is say 9 bytes then the RArray will internally allocate 12 bytes for each item, hence take care when creating structures. Likewise, if your structure has different size items in it, check at compile time that the size is what you expect it to be. I have encountered situations where the sizeof has reported a different to what I had expected becuase of alignment issues in structure fields.
  • To get the address of the block of memory allocated to the RArray, take the address of element zero.
    i.e. TVector* ptr = &(array[0]);
  • RPointerArray does not have an implementation that destroys all the pointer objects. Normally you would do this via ResetAndDestroy. For some reason Symbian decided to put the implementation of this in mmfcontrollerpluginresolver.h. The function is called CleanupResetAndDestroyPushL
  • One source of problems with RArray is when appending a large number of items to the array in one go.
    For example lets say:
    (1) RArray<TInt> array;
    (2) for (TInt i=0; i < 1000; i++)
    (3)    array.Append(i);

    Each time (3) is called when the array hits capacity, it will need to be expanded by its default granularity (8) which means that there will be a large number (124 reallocations) during this example.
    In OS 9.x Symbian has added a Reserve function which preallocates a specified number of items in the RArray so that when adding items, the array does not need to be expanded which reduces memory fragmentation.
    To simulate this in OS7 and OS8, we can reuse the tech above to allocate and transfer ownership.

    For example:
    (1) RArray<TVector> temp(sizeof(TVector), REINTERPRET_CAST(TVector*, User::AllocL(1000 * sizeof(TVector))), 1000);
    (2) for (TInt i= 1000-1; i >= 0; i--)
    (3)    temp.Remove(i);
    (4) RArray<TVector> array3 = temp;

    Important Notes
    (1) We allocate 1000 items and put it into the RArray.
    (2,3) We then remove the items starting from the back which prevents horrific memory copying.
    (4) Finally assign the new array to the existing array. Note that if the existing array (array3) already has items then you will need to call Reset on it before assigning the items, otherwise the memory in the array will be orphaned and you will get a memory leak.

In Conclusion:
I hope this has provided an example of how to use resources in RArray where speed is critical. Needless to say, make sure that if you use this technique you keep it localized to one function so that the maintainer can know exactly what you are doing as these techniques need to be used to extreme care and only where there is time critical code.

Next week I want to discuss how I ported SyExpat to OS 9 which I have been promising to deliver for ages now. Its coded, I just hav'nt had a chance to document what I changed and why.

I only just posted a comment about multidimensional RArrays so I might publish some additional notes about these this week as well.

New API's

Paul.Todd | 04 June, 2006 21:04

All the topics in this blog are going to be on Symbian and C++, sorry, but is all I program in these days.

So welcome, have all downloaded the WebKit and source code for the OSS browser?

The best starting off point seems to be the trunk where you will find all the sub project including the C++ source code and

The nicest part of this is the subfolder labelled S60Internals. It seems the nice guys at Nokia have provided some more API's that developers can look at.

There are a number of new API's and header files for S60 that might be useful for developers.

Next week I will be looking at RArray and specifically how to copy instances of it efficiently.

In the meantime you can normally find me answering the complex stuff on the newsgroups.
 
 
Powered by LifeType
     
     RDF Facets:
     
     
     qfnZtypeQUqfnTypeZBlogContentQ
     qfnZtypeQUqfnTypeZCommunityContentQ
     qfnZtypeQUqfnTypeZWebpageQ
     qmarsZlanguageQUxhttpE3aE2fE2fswE2enokiaE2ecomE2flanguageE2d1E2fenX