Gestaltstorage
Volume Number: 9
Issue Number: 3
Column Tag: Pascal/Assembly
A ‘Global’ storage technique, using _Gestalt
By Alain Danteny, France
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
This article is merely a re-writing and translation of a previous article I
submitted to Apple France’s DTS, which was published in the November 91-issue of
‘La Lettre des Développeurs Apple’. It’s based on an idea by Franck Lefebvre (Hi,
Frank): How to use Gestalt to store “global” informations, mainly in non-A5 worlds.
Frank did it in C, while I propose it in MPW Pascal and Asm
1. GestaltWas ist das?
System 6.0.4 introduced a new Trap, _Gestalt, that lets you read specific
hardware or software configurations and settings: system version, QuickDraw version,
etc.
You can use the Gestalt Manager through a single call to:
FUNCTION Gestalt(selector: OSType; VAR response: Longint): OSErr;
Error codes are sometimes returned:
gestaltUnknownErr = -5550;
{ value returned if Gestalt doesn't know the answer }
gestaltUndefSelectorErr = -5551;
{ undefined selector was passed to Gestalt }
gestaltDupSelectorErr = -5552;
{ tried to add an entry that already existed }
gestaltLocationErr = -5553;
{ gestalt function ptr wasn't in sysheap }
They are a lot of predefined read-only selectors available
More interesting, you can define your own private selector through the use of:
FUNCTION NewGestalt(selector: OSType; selectorFunction: ProcPtr):
OSErr;
The heart of this routine is the selectorFunction parameter: you have to supply
your own mechanism to retrieve your information. This routine is called by the Gestalt
Manager using the following interface:
FUNCTION selectorFunction(selector:OSType; VAR
response:Longint):OSErr;
You will probably write it using assembly, although this is not necessary.
For the purpose of GestaltStorage, we write the following routine :
;1
FUNC
EXPORT (__SelectorFunc,__EndSelectorFunc):CODE
;FUNCTION __SelectorFunc(selector:OSType;
; VAR response:Longint):OSErr;
__SelectorFunc
result EQU $10
selector EQU $C
response EQU $8
BRA.S Skip
Address DC.L 0
;place holder for our address of storage structure
;could be anything else, provided a 4-byte length
Skip LINK A6,#0
CLR.W result(A6) ;init no error
MOVEA.L response(A6),A0
MOVE.L Address(PC),D0
BNE.S GFExit
;if Address is $0 (meaning we already used and released
;this selector) then we return the address of the
;selectorFunction itself to re-use it properly
LEA __SelectorFunc,A1
MOVE.L A1,D0
;we return #$0100 as error code to let user know that the
;address of the selectorFunction is returned instead
MOVE.W #$0100,result(A6)
GFExit MOVE.L D0,(A0)
;otherwise, return ‘global’ address
UNLK A6
MOVEA.L (A7)+,A0
ADDQ.W #$8,A7
JMP (A0)
__EndSelectorFunc ;marker to end of routine
ENDFUNC
As you know by now, check out the Gestalt Manager chapter of Inside Mac Volume
VI, or your interface files.
2. GestaltStorage
The idea behind the GestaltStorage project is to provide simple routines to create,
set, get and dispose of ‘private’ Gestalt selectors in your application.
Whenever you need to keep data ‘global’ (for example, between two calls of an
XCMD, a MDEF, a WDEF etc.), just create a new selector, fill it, use it and release it
when the job is done (actually, when quitting).
There’s one thing you have to remember: Gestalt ‘lives’ in the system heap,
therefore everything you create there stays until the next boot.
The ‘public’ routines of GestaltStorage are:
FUNCTION GSFreeStorage(selector:OSType):Boolean;
This function tests whether your private Gestalt selector already exists or not.
FUNCTION GSNew(selector:OSType;storageSize:Size;
globalData:Ptr):OSErr;
This function allocate a new ‘global’ storage structure in the System heap of type
selector, stuffs the ‘raw’ data pointed by globalData in it, for storageSize bytes long,
and returns an error code (either Gestalt or Memory Manager).
There are three possibilities:
• selector doesn’t exist yet: we simply create it.
• selector already exists, but doesn’t point to data any longer: we reuse it and store
new data, being aware of the previous storageSize field of the internal structure.
• selector already exists and points to data: we overwrite those data for storageSize
bytes long (no dynamic re-allocation!)
FUNCTION GSGet(selector:OSType;bucketPtr:Ptr):OSErr;
This function collects the data stored in the selector structure and stuffs them in
bucketPtr. This pointer must be an already existing and valid pointer, or any
structure pointed to with the @ operator (in Pascal).
FUNCTION GSRead(selector:OSType;offset,length:LongInt;
bucketPtr:Ptr):OSErr;
This is merely the same as GSGet, except that you can get ‘partial’ data, provided
you supply an offset from the beginning of your own structure and a length of bytes to
read from.
FUNCTION GSSet(selector:OSType;globalData:Ptr):OSErr;
This routine lets you reset or modify your previously stored ‘global’ data
(through GSNew). globalData points to the new ‘raw’ data. Only storageSize bytes will
be copied
FUNCTION GSWrite(selector:OSType;offset,length:LongInt;
globalData:Ptr):OSErr;
Merely the same as GSSet, except that you can write ‘partial’ new data, providing
an offset and a length to write to.
FUNCTION GSDispose(selector:OSType):OSErr;
When you’re done with the use of your own selector, this routine de-allocates
any memory used in the System heap and set the ‘global’ address to $0 (a longint).
(Notice that the selector is still alive, until next boot: there’s no [official] way to
unregister a Gestalt selector)
3. Optimizing GestaltStorage?
This mechanism is very simple, yet efficient for any data of constant size.
One could consider re-writting GestaltStorage to handle dynamic allocations,
linked structures, etc. or implement such routines:
FUNCTION GSAppend(selector:OSType;moreSize:Size;
moreGlobalData:Ptr):OSErr;
FUNCTION GSInsert(selector:OSType;offset:LongInt;
moreSize:Size;moreGlobalData:Ptr):OSErr;
FUNCTION GSRemove(selector:OSType;offset:LongInt;
lessSize:Size):OSErr;
For the OOP folks, there’s probably a good class to code here!
4. Tidbits
While sleuthing the MPW libraries, I discovered that the Gestalt Manager
routines are embedded in a 500+-byte ‘glue’, for compatibility purpose: if you really
know what your doing, you can use those ‘un-glued’ routines: