Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page > Hide TOC

Writing a Callback to Swap Data Bytes

You can provide a byte-swapping callback routine, also referred to as a flipper, to the system for custom resource data, custom pasteboard data, and custom Apple event data. When you install a byte-swapping callback, you specify which domain that the data type belongs to. There are two data domains—Apple event and resource. The resource data domain specifies custom pasteboard data or custom resource data. If the callback can be applied to either domain (Apple event and resource), you can specify that as well.

The Core Endian API defines a callback that you provide to swap bytes for custom resource and Apple event data. You must provide one callback for each type of data you want to swap bytes. The prototype for the CoreEndianFlipProc callback is:

typedef CALLBACK_API (OSStatus, CoreEndianFlipProc)
    (OSType dataDomain,
    OSType dataType,
    short id,
    void *dataPtr,
    UInt32 dataSize,
    Boolean currentlyNative,
    void *refcon
);

The callback takes the following parameters:

The callback returns a result code that indicates whether bytes are swapped successfully. Your callback should return noErr if the data is byte swapped without error and the appropriate result code to indicate an error condition—errCoreEndianDataTooShortForFormat, errCoreEndianDataTooLongForFormat, or errCoreEndianDataDoesNotMatchFormat. The result code you return is propagated through the appropriate manager (Resource Manager (ResError) or Apple Event Manager) to the caller.

You do not need to swap bytes for quantities that are not numerical (such as strings, byte streams, and so forth). You need to provide a callback only to swap bytes data types for which the order of bytes in a word or long word are important. (For the preferred way to handle Unicode strings, see “Unicode Text Files.”)

Your callback should traverse the data structure that contains the data and swap bytes for:

The Core Endian API provides these functions for working with your callback:

As an example, look at a callback for the custom resource type ('PREF') defined in Listing 3-7. The MyPreferences structure is used to store preferences data on disk. The structure contains a number of values and includes two instances of the RGBColor data type and an array of RGBColor values.

Listing 3-7  A declaration for a custom resource

#define kMyPreferencesType      'PREF'
 
struct MyPreferences {
                SInt32          fPrefsVersion;
 
                Boolean         fHighlightLinks;
                Boolean         fUnderlineLinks;
 
                RGBColor        fHighlightColor;
                RGBColor        fUnderlineColor;
                SInt16          fZoomValue;
 
                char            fCString[32];
 
                SInt16          fCount;
                RGBColor        fPalette[];
};

You can handle the RGBColor data type by writing a function that swaps bytes in an RGBColor data structure, such as the function MyRGBSwap, shown in Listing 3-8. This function calls the Core Endian macro EndianS16_Swap to swap bytes for each of the values in the RGBColor data structure. The function doesn’t need to check for the currently executing system because the function is never called unless the values in the RGBColor data type need to have their bytes swapped. The MyRGBSwap function is called by the byte-swapping callback routine (shown in Listing 3-9) that’s provided to handle the custom 'PREF' resource (that is defined in Listing 3-7).

Listing 3-8  A flipper function for RGBColor data

static void MyRGBSwap (RGBColor *p)
{
    p->red = Endian16_Swap(p->red);
    p->blue = Endian16_Swap(p->blue);
    p->green = Endian16_Swap(p->green);
}

Listing 3-9 shows a byte-swapping callback for the custom 'PREF' resource. An explanation for each numbered line of code appears following the listing. Note that the flipper checks for data that is malformed or is of an unexpected length. If the data passed into the flipper routine is a shorter length than the flipped type is normally, or (for example) contains garbage data instead of an array count, the flipper must be careful not to read or write data beyond the end of the passed-in data. Instead, the routine returns an error.

Listing 3-9  A flipper for the custom 'PREF' resource

#define kCurrentVersion    0x00010400
 
static OSStatus MyFlipPreferences (OSType dataDomain,  // 1
                    OSType dataType,   // 2
                    short id,   // 3
                    void * dataPtr,   // 4
                    UInt32 dataSize,   // 5
                    Boolean currentlyNative,  // 6
                    void* refcon)  // 7
{
    UInt32  versionNumber;
 
    OSStatus status = noErr;
    MyPreferences* toFlip = (MyPreferences*) dataPtr;       // 8
    int count, i;
 
    if (dataSize < sizeof(MyPreferences))
        return errCoreEndianDataTooShortForFormat;  // 9
    if (currentlyNative)   // 10
    {
        count = toFlip->fCount;
        versionNumber = toFlip->fPrefsVersion;
        toFlip->fPrefsVersion = Endian32_Swap (toFlip->fPrefsVersion);
        toFlip->fCount = Endian16_Swap (toFlip->fCount);
        toFlip->fZoomValue = Endian16_Swap (toFlip->fZoomValue);
    }
    else   // 11
    {
        toFlip->fPrefsVersion = Endian32_Swap (toFlip->fPrefsVersion);
        versionNumber = toFlip->fPrefsVersion;
        toFlip->fCount = Endian16_Swap (toFlip->fCount);
        toFlip->fZoomValue = Endian16_Swap (toFlip->fZoomValue);
        count = toFlip->fCount;
    }
    if (versionNumber != kCurrentVersion)  // 12
                return errCoreEndianDataDoesNotMatchFormat;
 
    MyRGBSwap (&toFlip->fHighlightColor);  // 13
    MyRGBSwap (&toFlip->fUnderlineColor);  // 14
 
    if (dataSize < sizeof(MyPreferences) + count * sizeof(RGBColor))
        return errCoreEndianDataTooShortForFormat;   // 15
 
    for(i = 0; i < count; i++)
    {
        MyRGBSwap (&toFlip->fPalette[i]);  // 16
    }
 
    return status;     // 17
}

Here’s what the code does:

  1. The system passes to your callback the domain to which the callback applies. You define the domain when you register the callback using the function CoreEndianInstallFlipper.

  2. The system passes to your callback the resource type you defined for the data. In this example, the resource type is 'PREF'.

  3. The system passes to your callback the resource ID of the data type. If the data is not a resource, this value is 0.

  4. The system passes to your callback a pointer to the resource data that needs to have its bytes swapped. In this case, the pointer refers to a MyPreferences data structure.

  5. The system passes to your callback the size of the data pointed to by the pointer described in the previous step.

  6. The system passes to your callback true if the data in the buffer passed to the callback is in the byte ordering of the currently executing code. On a PowerPC Macintosh, when currentlyNative is true, the data is in big-endian order. On a Macintosh that uses an Intel microprocessor, when currentlyNative is true, the data is in little-endian order. Your callback needs to know this value, because if your callback uses a value in the data buffer to decide how to process other data in the buffer (for example, the count variable shown in the code), you must know whether that value needs to be flipped before the value can be used by the callback.

  7. The system passes to your callback a pointer that refers to application-specific data. In this example, the callback doesn’t require any application-specific data.

  8. Defines a variable for the MyPreferences data type and assigns the contents of the data pointer to the newly-defined toFlip variable.

  9. Checks the static-length portion of the structure. If the size is less than it should be, the routine returns the error errCoreEndianDataTooLongForFormat.

  10. If currentlyNative is true, saves the count value to a local variable and then swaps the bytes for the other values in the MyPreferences data structure. You must save the count value before you swap because you need it for an iteration later in the function. The fact that currentlyNative is true indicates that the value does not need to be byte swapped if it is used in the currently executing code. However, the value does need to be swapped to be stored to disk.

    The values are swapped using the appropriate Core Endian macros.

  11. If currentlyNative is false, flips the values in the MyPreferences data structure before it saves the count value to a local variable. The fact that currentlyNative is false indicates that the count value needs to have its bytes swapped before it can be used in the callback.

  12. Checks to make sure the version of the data structure is supported by the application. If the version is not supported, then your callback would not swap bytes for the data and would return the result errCoreEndianDataDoesNotMatchFormat.

  13. Calls the MyRGBSwap function (shown in Listing 3-8) to swap the bytes of the fHighlightColor field of the data structure.

  14. Calls the MyRGBSwap function to swap the bytes of the fUnderlineColor field of the data structure.

  15. Checks the data size to make sure that it is less than it should be. If not, the routine returns the error errCoreEndianDataTooLongForFormat.

  16. Iterates through the elements in the fPalette array, calling the MyRGBSwap function to swap the bytes of the data in the array.

  17. Returns noErr to indicate that the data is flipped without error.

Although the sample performs some error checking, it does not include all the error-handling code that it could. When you write a flipper you may want to include such code.

Note: The callback does not flip any of the Boolean values in the MyPreferences data structure because these are single character values. The callback also ignores the C string.

You register a byte-swapping callback routine by calling the function CoreEndianInstallFlipper. You should register the callback when your application calls its initialization routine or when you open your resources. For example, you would register the flipper callback shown in Listing 3-9 using the following code:

OSStatus status = noErr;
status = CoreEndianInstallFlipper (kCoreEndianResourceManagerDomain,
                        kMyPreferencesType,
                        MyFlipPreferences,
                        NULL);

The system invokes the callback for the specified resource type and data domain when currentlyNative is false at the time a resource is loaded and true at the time the resource is set to be written. For example, the sample byte-swapping callback gets invoked any time the following line of code is executed in your application:

MyPreferences** hPrefs = (MyPreferences**) GetResource ('PREF',  128);

After swapping the bytes of the data, you can modify it as much as you’d like.

When the Resource Manager reads a resource from disk, it looks up the resource type (for example, 'PREF') in a table of byte-swapping routines. If a callback is installed for that resource type, the Resource Manager invokes the callback if it is appropriate to do so. Similar actions are taken when the Resource Manager writes a resource to disk. It finds the appropriate routine and invokes the callback to swap the bytes of the resource if it is appropriate to do so.

When you copy or drag custom data from an application that has a callback installed for pasteboard data, the system invokes your callback at the appropriate time. If you copy or drag custom data to a native application, the data callback is not invoked. If you copy or drag custom data to a nonnative application, the system invokes your callback to swap the bytes of the custom data. If you paste or drop custom data into your application from a nonnative application, and a callback exists for that custom data, the system invokes the callback at the time of the paste or drop. If the custom data is copied or dragged from another native application, the callback is not invoked.

Note that different pasteboard APIs use different type specifiers. The Scrap Manager and Drag Manager use OSType data types. The Pasteboard Manager uses Uniform Type Identifiers (UTI), and the NSPasteboard class uses its own type mechanism. In each case, the type is converted by the system to an OSType data type to discover if there is a byte-swapping callback for that type.

Apple event data types are typically swapped to network byte order when sent over a network. The callback you install is called only if a custom data type that you define is sent to another machine, or if another machine sends Apple event data to your application. The byte ordering of Apple events on the network is big-endian.

For cases in which the system would not normally invoke your byte-swapping callback, you can call the function CoreEndianFlipData to invoke the callback function installed for the specified data type and domain.



< Previous PageNext Page > Hide TOC


Last updated: 2007-02-26




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice