Article # 127, added by Geoworks, historical record
| first | previous | index | next | last |

Performance profiling your code in swat

Here's the Notepad data file (notepad.pdb) format, provided by HP.
==========================================================================
Notepad data structures
-----------------------

Notepad data is kept in a database file 

Each note has 5 different data items. They are 

name     - an ascii text string representing the name of the note
category - a category string in database defined format
text     - ascii text of the note
text format - formatting data for the text
ink      - ink data

Record Format 
-------------

Each note in the notepad database is represented by a TYPE_DATA
record with the following format (low memory first) :

DataRecordHeader 
optional zero terminated string 
optional zero terminated string

where DataRecordHeader (in C notation) is :

typedef struct {
  word  DRH_nameOffset;
  word  DRH_categoryOffset;
  sword DRH_text;
  sword DRH_textFormat;
  sword DRH_ink;
  char  DRH_zeroTerminator;
} DataRecordHeader;

That is, there are 5 fields in a notepad database record.

DRH_nameOffset is the byte offset from the start of record to a zero
terminated string representing the name of the note. If there is no
name, DRH_nameOffset points to DRH_zeroTerminator. 

DRH_categoryOffset is similar to DRH_nameOffset except that it points
to the category string of the note. e.g.  "friends;church".

DRH_text has a integer value (>= 0) representing the record number 
of TYPE_NOTE in the database file which holds the null terminated
ascii text of the note. If no ascii text exists, this value is -1.

DRH_textFormat is similar to DRH_text except that it contains the
record number of a (TYPE_USER+5) recordtype. The data it contains is
formatting information for the text of the note. This will be described
later.

DRH_ink also contains a record number. The recordtype is (TYPE_USER+6).
This contains the compressed ink data. This will also be described
later.

DRH_zeroTerminator is a byte value with a constant of zero. This 
provides a convenient null terminator for the name and category strings
when there is no string.

notepad will continue to load the ascii text pointed to by DRH_text 
even if DRH_textFormat is -1 (i.e., no text format).


Fielddefs
---------

The 5 fields have the following fielddefs :

name        - STRING_FIELD with field name "Title"
category    - CATEGORY_FIELD with field name "Category"
text        - NOTE_FIELD with field name "Note Text"
text format - (USER_FIELD+5) with field name "Note Text Format"
ink data    - (USER_FIELD+6) with field name "Note Ink"

The above field names exist in the US/UK version. These strings are
localized for the various language versions because the Connectivity
Pack picks up these names for display.

The field def records are defined as in the order above so an alternate
way of accessing the fielddefs (and looking at what the fields are) is
to access via record number of TYPE_FIELDDEF records.


Text Format Data structure
--------------------------

It looks like this (low memory first) :

 NoteMergedTextFormatHeader
 char run array 
 char element array 
 para run array 
 para element array 

In C notation NoteMergedTextFormatHeader looks like this 

typedef struct {
    word NMTFH_charRunArray;     /* offset to char run array */
    word NMTFH_charElementArray; /* offset to char elem array */ 
    word NMTFH_paraRunArray;     /* offset to para run array */
    word NMTFH_paraElementArray; /* offset to para elem array */
} NoteMergedTextFormatHeader;

Each item in NoteMergedTextFormatHeader is a byte offset to an 
array of structures.

The char and paragraph run arrays describe ranges of text which get
some particular text format. They are described in the Geoworks
Objects Book chapter on text objects.

Briefly they both look like this :

TextRunArrayElement Number 0
TextRunArrayElement Number 1
....

where TextRunArrayElement is 

typedef struct {
    WordAndAHalf    TRAE_position;  /* text position */
    word            TRAE_token;     /* points to an element in the
                                      corresponding element array */
} TextRunArrayElement;


The element arrays are also described in GEOWORKS documentation.
The storage of the char element array and the para element array
differ (because the para element array has variably sized elements)

The char element array looks like this :

VisTextCharAttr element number 0
VisTextCharAttr element number 1
....

where each VisTextCharAttr represents a text format.

The paragraph element array (as stored in the database) is a complete
copy of the paragraph element array inclusive of header structure
as per GEOWORKS documentation.

Briefly it looks like this :

TextElementArrayHeader
word offset to element 0
word offset to element 1
....
word offset to element N
VisTextParaAttr element 0
Tabs array
VisTextParaAttr element 1
Tabs array
....
VisTextParaAttr element N
Tabs array


Ink Format 
----------
The format of the ink is that as described in the Geoworks documentation
for the storage of ink using their DB routines. Basically it is a
run length compression of the ink points.



========================================================================
Here's some information about the Phonebook file format.
========================================================================
 
 A phone book record is a char buffer with the header formatted as follows:
 
 Bytes:  
 00-01     offset to Name field text
 02-03     offset to Home field text
 04-05     offset to Office field text 
 06-07     offset to Fax field text
 08-09     offset to Other field text 
 0A-0B     offset to Company field text 
 0C-0D     offset to Title field text 
 0E-0F     offset to Address1 field text 
 10-11     offset to Address2 field text 
 12-13     offset to Category field text 
 14-15     record index for Note
 16        A null (zero) byte
 
 If a field contains an empty string, then its offset is set to the
 offset of the null byte (16h).  
 
 The first non-empty string will have offset 17h, and its
 null-terminated string will begin at byte 17h (the next byte after
 the null byte). 
 
 The next non-empty string will have an offset that points to the next
 byte after the null-terminator for the previous non-empty string.  I
 have included some output for a sample record and some code that I
 hacked-up to see if it would work.  I'm glad to say that it did.
 
 ----------------------------------------------------------------------- 

         // read an existing record
         DB_ReadRecord(blockHandle, TYPE_DATA, theRecordNum,recordBuffer,
                 RECORD_BUFFER_SIZE,&recSize);
 
         // Modify existing record and write back to file 
         replacePhoneBookTextField(recordBuffer,&recSize,RECORD_BUFFER_SIZE,
                 PHONEBOOK_HOME_FIELD_INDEX,"HOME PHONE");
 
         DB_WriteRecord(blockHandle, TYPE_DATA,theRecordNum,recordBuffer, 
                 recSize,&viewPtIndex);
 
 
 
         // create a new record and add to file 
         // see following routines below...
         initPhoneBookRecord(recordBuffer,&recSize); 
         replacePhoneBookTextField(recordBuffer, &recSize, RECORD_BUFFER_SIZE, 
                 PHONEBOOK_NAME_FIELD_INDEX, "Bogus Record");
         replacePhoneBookTextField(recordBuffer,&recSize,        
                 RECORD_BUFFER_SIZE, PHONEBOOK_OFFICE_FIELD_INDEX, "555-1212");
         DB_AddRecord(blockHandle, TYPE_DATA, recordBuffer, 
                 recSize, &viewPtIndex);
 
 
 
 static void initPhoneBookRecord(char *recordBuffer,unsigned int *recSize)
         // initialize the record, return record size in recSize
 {
         PhoneBookHeaderPtr headerPtr;
 
         memset(recordBuffer,0,RECORD_BUFFER_SIZE); 
         *recSize = EMPTY_PHONE_BOOK_RECORD_SIZE;
 
         // indicate no Note by setting a note index of 0xFFFF 
         headerPtr = (PhoneBookHeaderPtr)recordBuffer; 
         headerPtr->noteRecordIndex = 0xFFFF;
 }
 
 static Boolean replacePhoneBookTextField(char *recordBuffer,unsigned int 
 *recSize,word bufSize,word fieldIndex,char *newText)
         /*** replace an entry in the record
                 recordBuffer = buffer filled with record
                 recSize = size of record, is set to new size after replacing          bufSize = buffer size
                 fieldIndex = which field to replace
                 newText = new text to put into field 
                 returns TRUE if successful
         ***/
 {
         word i,nbytes,destOffset,nullOffset,len; 
         unsigned int newRecSize;
         PhoneBookHeaderPtr newHeader,oldHeader; 
         char *tempBuffer,*srcPtr,*destPtr;
 
         ASSERT_IF(recordBuffer==NULL,"replacePhoneBookTextField - null 
 recordBuffer");
         ASSERT_IF(recSize==NULL,"replacePhoneBookTextField - null recSize"); 
         ASSERT_IF(bufSize==0,"replacePhoneBookTextField - bufSize=0"); 
         ASSERT_IF(fieldIndex>=10,"replacePhoneBookTextField - invalid 
 fieldIndex");
         ASSERT_IF(newText==NULL,"replacePhoneBookTextField - null newText");
 
         // ASSUME max field length is 64 chars 
         ASSERT_IF(strlen(newText)>64,"replacePhoneBookTextField - strlen>64");
 
         /*** 
                 Right now, this function makes a temporary copy of the buffer, 
                 but it can be rewritten to replace the text in-place to
                 save heap space
         ***/
 
         // create temporary buffer to hold current buffer contents 
         nbytes = *recSize;
         if (!allocateNewPtr(nbytes,(void **)&tempBuffer))
                 return(0);
 
         // copy old record contents into temp buffer 
         memcpy(tempBuffer,recordBuffer,nbytes);
 
         // first bytes of buffer is the header
         oldHeader = (PhoneBookHeaderPtr) &tempBuffer[0]; 
         newHeader = (PhoneBookHeaderPtr) &recordBuffer[0];
 
         // set all record bytes to NULL
         memset(recordBuffer,0,bufSize);
         // reset noteRecordIndex
         newHeader->noteRecordIndex = oldHeader->noteRecordIndex;
 
         // build new contents of record
         nullOffset = sizeof(PhoneBookHeaderRecord);  // offset to NULL byte 
         newRecSize = (unsigned int)(nullOffset + 1);
         for (i=0;i<10;i++) {
                 if (i==fieldIndex)
                         srcPtr = newText;
                 else
                         srcPtr = &tempBuffer[oldHeader->offsetList[i]];
 
                 len = strlen(srcPtr);
                 if (len==0) {
                         // offset to null byte
                         destOffset = nullOffset;
                 } else {
                         // offset to new string
                         destOffset = newRecSize;
                         // append string onto record
                         destPtr = &recordBuffer[destOffset]; 
                         strcpy(destPtr,srcPtr);
                         newRecSize += (len+1);
                 }
                 newHeader->offsetList[i] = destOffset;
         }
 
         // dispose of temp buffer
         disposePtr(tempBuffer);
 
         *recSize = newRecSize;
     return(TRUE);
 }
 
 
 ----------------------------------------------------------------------- 
 RECORD HEADER>>
 000f19: 0b 02 74 00 04 00                                   ..t...
 
   Type: 11 (TYPE_DATA)   Status: 02    Length: 116 (0074)   Record #: 4
   Status = Modified
 
 RECORD>>
           00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
 
 0000: 17 00 16 00 22 00 2f 00 3c 00 16 00 16 00 16 00     ...."./.<.......
 
            .  .  i  .  .  .  .  J  o  n     H  a  r  l  a
 0010: 16 00 69 00 04 00 00 4a 6f 6e 20 48 61 72 6c 61     ..i....Jon Harla
 
            n  .  5  1  0  -  5  5  9  -  7  8  7  2  .  5
 0020: 6e 00 35 31 30 2d 35 35 39 2d 37 38 37 32 00 35     n.510-559-7872.5
 
            1  0  -  5  5  9  -  7  8  7  6  .  2  W  A  Y
 0030: 31 30 2d 35 35 39 2d 37 38 37 36 00 32 57 41 59     10-559-7876.2WAY
 
            =  1  7  4  0  7  9  0     E  M  A  I  L  =  j
 0040: 3d 31 37 34 30 37 39 30 20 45 4d 41 49 4c 3d 6a     =1740790 EMAIL=j
 
            o  n  b  o  @  a  o  l  .  c  o  m     P  G  R
 0050: 6f 6e 62 6f 40 61 6f 6c 2e 63 6f 6d 20 50 47 52     onbo@aol.com PGR
 
        =  4  5  8  6  1  5  9  .  N  o  n  e  .  
 0060: 3d 34 35 38 36 31 35 39 00 4e 6f 6e 65 00           =4586159.None.
 
 
 
 /*** PHONEBOOK FIELDS
         0 - Name
         1 - Home
         2 - Office
         3 - fax
         4 - other
         5 - company
         6 - title
         7 - address1
         8 - address2
         9 - category
 ***/
 


========================================================================
Here's some information about the Jotter file format.
========================================================================
 
API for JOTTER ENGINE
========================

library: jotter
include: Objects/jCntlC.h for the jotter control object
include: hp.h             for gcn lists, notification types
author: cthomas

This is the design document Jin Meng Tan and I have composed.  There's
lots of internal details that a developer will probably never touch,
which I'll mark off.  In fact, most of this is stuff a developer will
never use, except for the section on creating an object that accepts a
jotter page.

The JotterControlClass objects work closely with the jotter app to
coordinate the transfer of data between the jotter app and other
objects.  An application's JotterControlClass object is sort of a
go-between for the jotter app and specific objects inside the app.

Jotter Engine Design Specifications dated 29th Sep
==================================================

Class: JotterControlClass
       No external messages.

1.  JOTTER STUCK PAGE FORMAT

1.1.  Stuck Page Block

The VM block containing the currently stuck page has the VM User ID
JOTTER_STUCK_PAGE_USER_ID, and will reside in the system clipboard
file.  If there is no currently stuck, page, then no such VM block
will exist.  

***************** Internal details ******************************
1.2.  Stuck page format

The structure of the stuck page will be a root VMChainTree block with
2 immediate children.  This block will have a UserID of 
JOTTER_STUCK_PAGE_USER_ID.

The first child will be the text transfer item created with
MSG_VIS_TEXT_CREATE_TRANSFER_FORMAT.  The second child will be the ink data
DB item created with MSG_INK_SAVE_TO_DB_ITEM.

Structure of the VMChainTree in the header block will be 
  typedef struct {
     VMChainTree        NTIH_vmChainTree;
     /* offsets from start of block to the name/category strings */
     /* set to zero if name/category does not exist */
     word               NTIH_nameOffset; 
     word               NTIH_categoryOffset;
     /* cached information about the data */
     Boolean            NTIH_textIsEmpty;
     Boolean            NTIH_inkIsEmpty;
 } NoteTransferItemHeader;
 
 The structure of the entire (generic) header block will be 
 (lowest address first) :

                NoteTransferItemHeader 
                Null Terminated Name Ascii String
                Null Terminated Category Ascii String
                VMChain pointer to text transfer item
                DBGroupAndItem pointer to Ink DB Item 

 In the case of the stuck page, both name and category will not be
 supported currently. Thus the structure will look like :

                NoteTransferItemHeader
                VMChain pointer to text transfer item
                DBGroupAndItem pointer to Ink DB Item 

 and 
   NTIH_nameOffset = NTIH_categoryOffset = 0;

The text transfer item itself is a VMChain subtree with leaf blocks
containing various information but the user should not need to know
this. The text transfer item can be retrieved into a text object
with a call to MSG_VIS_TEXT_REPLACE_WITH_TEXT_FORMAT

There will always be 2 children of the root, even if there is no
data to store.  For example, even if the stuck page has only ink,
there will still be 2 blocks with the text DB item having no text
data.

1.2.1  Operations on stuck page

A stuck page can be copied with VMCopyVMChain()

A stuck page can be destroyed with VMFreeVMChain().

When using quick transfer, this structure is preserved (the Jotter 
controller does the VMCopyChain for the application) and its head
is passed as the transfer item.

NoteClass will have a method called MSG_NOTE_LOAD_FROM_TRANSFER_FORMAT 
which will load the text and ink data when passed the VMChain handle.
It will also have a corresponding MSG_NOTE_SAVE_TO_TRANSFER_FORMAT 
but applications are not expected to use this as the main storage 
method for NoteFields is the Jedi specific database engine.

************************ END of INTERNAL stuff ***********************


2.  JOTTER NOTIFICATIONS

2.1.  Jotter GCN lists

The Jotter Application will communicate with JotterControl objects
through the jotter GCN list.  This is a system GCN list, and has the 
following notification ID:

  manufacturer id = MANUFACTURER_ID_HP (insert specific value here)
  GCN list type   = JGCNSLT_JOTTER

In addition, each application's Jotter Control object will reside
on an application GCN list, called the "application jotter GCN list".
This list will be used by objects within an application to
communicate with the application's JotterControl object, and is
identified by the following constants:

  manufacturer id = MANUFACTURER_ID_HP
  GCN list type   = JGAGCNLT_JOTTER

Every jotter control will automatically place themselves on both of
these lists.

2.2.  Jotter GCN List Messages

The only message used to communicate with objects on the jotter GCN
list is MSG_META_NOTIFY, with one of the following possible
notification types passed as a parameter: (of type JediNotificationType)

  JNT_STUCK_PAGE_CHANGED
  JNT_JOTTER_CONTROL_SHRINK
  JNT_JOTTER_CONTROL_DONT_SHRINK (internal to JotterControlClass)
  JNT_JOTTER_CAPABLE_OBJECT_TARGETABLE
  JNT_JOTTER_CAPABLE_OBJECT_NOT_TARGETABLE

2.2.1. JNT_STUCK_PAGE_CHANGED notification type

*** Only sent by the jotter control object or the jotter app ***

This notification type is sent when the status of the stuck page has
changed (i.e., when a page has been "stuck" by the jotter application,
or the stuck page has been pasted, thereby removing the stuck page).

Upon receiving a MSG_META_NOTIFY with JNT_STUCK_PAGE_CHANGED as the
notification type parameter, an object may wish to determine whether
or not there is a stuck page by using:

  VMFind(, 0, JOTTER_STUCK_PAGE_USER_ID)
    (returns the null handle if the block doesn't exist)

2.2.2. JNT_JOTTER_CONTROL_SHRINK

*** Only sent by the jotter app ***

The Jotter Application will send this notification type when the
user presses "Stick".

A JotterControl object which receives this notification will set its
own state such that the next time it becomes visible (or immediately
if it is already visible), it will produce the animated shrink lines
specified in the ERS. (call this the shrink state)

2.2.3. JNT_JOTTER_CONTROL_DONT_SHRINK

*** Sent by jotter control object to other jotter control objects ****

This notification type is sent by the JotterControl which has animated
the shrink lines to other JotterControls on the jotter GCN list.

When a JotterControl receives this message while it is the shrink state,
it will remove itself from the shrink state, effectively preventing
itself from showing the shrink lines.

Without this interaction between JotterControls, Jotter Controls that
become visible later would perform the shrink function, even though
it had already been performed.

2.2.4. JNT_JOTTER_CAPABLE_OBJECT_{NOT_}TARGETABLE

*** Objects which can receive a jotter page via dragging the jotter
***  icon into them, must send these notifications.

When receiving this notification type, the jotter control will:

 - Enable itself when receiving JNT_JOTTER_CAPABLE_OBJECT_TARGETABLE
 - Disable itself when receiving JNT_JOTTER_CAPABLE_OBJECT_NOT_TARGETABLE

These notifications are sent by objects of an application to the
application GCN list.

3.  APPLICATION SUPPORT OF JOTTER

3.1. Application programmer support of the Jotter

Exactly one JotterControlClass object must be in the generic tree of
every application, as a child of the application's GenPrimary object.

3.2. Object Support of the Jotter.

3.2.1. Drag and Drop Support

The dropping of the Jotter Icon into applications will make use of
GEOS' quick-transfer mechansim, with certain changes to allow for the fact
that a pen-based system has no equivalent of a right mouse button
with which to control a quick transfer.

The JotterControl object will initiate the quick-transfer when a stuck
jotter page is available, and the user has held down the jotter icon
in the application's title bar.  It will do this by registering the
block with the ID JOTTER_STUCK_PAGE_USER_ID as the current quick
transfer item.

As destinations of quick-transfer, objects which support pasting of
the stuck jotter page have the following responsibilities:

*** This is very much like the description of quick transfer
*** in the concepts book.

 1) When the potential destination receives MSG_META_PTR, it should
    use ClipboardGetQuickTransferStatus() to determine if a quick
    transfer is in effect.  If it is, it must use ClipboardQueryItem()
    to determine if the item being transfered is a jotter page
    (ClipboardItemFormat CIF_NOTE, Manufacturer id MANUFACTURER_ID_HP)

 2) The object should grab the mouse, and the gadget exclusive (via
    MSG_VIS_TAKE_GADGET_EXCL ) directing further MSG_META_PTR
    messages to the potential destination.  It will call
    ClipboardSetQuickTransferFeedback(CQNF_MOVE).

 3) In this, and subsequent invocations of the MSG_META_PTR method,
    the destination should provide any desired user feedback
    (for note fields, a line drawn around the perimeter of the note.
     for tables, a box drawn around the row under the pen).

 4) If the object receives MSG_META_PTR with the UIFA_IN flag clear,
    or receives the MSG_VIS_LOST_GADGET_EXCLUSIVE, then the object
    must relinquish the mouse grab, and re-transmit the pointer event
    (by returning MRF_REPLAY from the MSG_META_PTR handler).

  5) When the destination receives MSG_META_END_SELECT, it should
    determine if a jotter paste is in effect (as in 1).  It should
    grab the jotter page from the quick-transfer clipboard with
    ClipboardRequestItemFormat(), paste the jotter page data,
    and call ClipboardEndQuickTransfer(), passing CQNF_MOVE.

3.2.1.1.  Table Support of Jotter

Additional details (referring to points 1-5 from 3.2.)

 1) The table goes into "external" drag and drop mode. (which provides
    the row-outlining behavior)

 4) The table goes back into "internal" drag and drop mode.

 5) The table has either been subclassed to provide a handler for
    pasting jotter pages, or calls some other object to perform the
    paste. (like a NoteClass object).

3.2.1.2.  Note Field Support of Jotter

Additional details (referring to points 1-5 from 3.2.)

  3) The note field draws an outline around its perimeter

3.2.2.  Jotter Control Enabling Support

In order that an application's Jotter Control object can know when an
object capable of supporting jotter paste is visible, some
communication from such objects to the application's Jotter Control is
needed.

When an object that supports jotter becomes available to the user
(i.e., comes on-screen), it will send MSG_META_NOTIFY with the
JotterNotificatonType JNT_JOTTER_CAPABLE_OBJECT_TARGETABLE to the
application jotter GCN list.  When the object becomes unavailable to
the user, it will send JNT_JOTTER_CAPABLE_OBJECT_NOT_TARGETABLE.  The
receiving JotterControl will then know when is supported by some piece
of user interface on the screen of the application, and can draw
itself appropriately when a stuck page is available.

*********** This text taken from Objects/jCntlC.goh ******************

** If you happen to look in jCntlC.goh, ignore item 1

 2) In order that the jotter control can know when it should enable and
    disable itself:

    All objects into which the jotter icon may be dropped must send:

       MSG_META_NOTIFY(manufacturer id = MANUFACTURER_ID_HP,
		    notification type = JNT_JOTTER_CAPABLE_OBJECT_TARGETABLE)

    to the application GCN list:

	manufacturer id = MANUFACTURER_ID_HP
	GCN list type   = JGAGCNLT_JOTTER

    when the object becomes interactable to the user.

    This can be done when the object recieves a MSG_VIS_OPEN, or
    by using the Gen visibility mechanism (Objects 2.6.1.5).

  3) All objects into which the jotter icon may be dropped must send:

       MSG_META_NOTIFY(manufacturer id = MANUFACTURER_ID_HP,
		notification type = JNT_JOTTER_CAPABLE_OBJECT_NOT_TARGETABLE)

    to the application GCN list:

	manufacturer id = MANUFACTURER_ID_HP
	GCN list type   = JGAGCNLT_JOTTER

    when the user can no interact with it.

    This can be done when the object recieves a MSG_VIS_CLOSE, or
    by using the Gen visibility mechanism (Objects 2.6.1.5).

  4) If a full screen object (as in 1) containing no jotter capable objects
     is going to obscure another screen that does contain such objects,
     then the screen to be obscured must be dismissed.

     If the obscured screen isn't closed, but simply obscured, then
     the objects on the obscured screen will never know that they are
     not visible to the user, and will not send the message described
     in 3.  The jotter icon in the new screen will be enabled, even though
     there are no visible objects that will take the icon.


======================================================================


The following swat commands can help you in finding areas in
your code that need optimizing.

timebrk
    Manipulates breakpoints that calculate the amount of time
    executing between their starting point and a specified ending point. 
    The breakpoints also record the number of times their start is hit, 
    so you can figure the average amount of time per hit.

tbrk
    Manipulates breakpoints that tally the number of times they are
    hit without stopping execution of the machine -- the breakpoint is
    noted and the machine is immediately continued. Such a breakpoint 
    allows for real-time performance analysis.

cycles
    Counts the number of machine cycles a given set of instructions
    takes to execute on a particular processor.