Warning: This HTML rendition of the RFC is experimental. It is programmatically generated, and small parts may be missing, damaged, or badly formatted. However, it is much more convenient to read via web browsers, however. Refer to the PostScript or text renditions for the ultimate authority.

OSF DCE SIG J. Harrow (DEC)
Request For Comments: 2.1 July 1992

PROPOSED ENHANCEMENTS FOR DCE 1.1 IDL

INTRODUCTION

This document proposes enhancements to the DCE 1.0 IDL language and compiler, to be supported in the DCE 1.1 release.

COMPLETE IDL ARRAY SUPPORT

IDL support for arrays in DCE V1.0 was limited to:

  1. Arrays with a lower bound of zero.
  2. Arrays with conformance or varying dimensions only in the first (major) dimension.

DCE 1.1 will remove these restrictions by supporting fully general arrays as described in the IDL functional specification. The following example includes declarations that were not supported in DCE 1.0 that will be allowed by DCE V1.1:

  1. long c1[][4];
  2. long c2[][0..3]; /* Same array shape as c1 */
  3. long c3[0..*][4]; /* Same array shape as c1 */
  4. long c4[0..*][0..3]; /* Same array shape as c1 */
  5. float d1[1..10]; /* Equivalent to FORTRAN REAL D1(10) */
  6. float d2[*..10]; /* Lower bound is determined at run time */
  7. float d3[*..*]; /* Both bounds determined at run time */

The Min_is Attribute

The <attr_var>s are in one-to-one correspondence with the dimensions of the array, starting at the first. If there are fewer <attr_var>s than the array has dimensions then the missing <attr_var>s are assumed to be null. An <attr_var> will be non-null if and only if the lower bound of the corresponding dimension is determined at runtime. Not all <attr_var>s in a min_is clause may be null.

Below are examples of the syntax. Assume values of variables are as follows: long a = -10; long b = -20; long c = -30; long d = 15; long e = 25.

  1. [min_is(a)] long g1[*..10]; /* g1[-10..10] */
  2. [min_is(a)] long g2[*..10][4]; /* g2[-10..10][0..3] */
  3. [min_is(a,b)] long g3[*..10][*..20]; /* g3[-10..10][-20..20] */
  4. [min_is(,b)] long g4[2][*..20]; /* g4[0..1][-20..20] */
  5. [min_is(a,,c)] long g5[*..7][2..9][*..8]; /* g5[-10..7][2..9][-30..8] */
  6. [min_is(a,b,)] long g6[*..10][*..20][3..8]; /* g6[-10..10][-20..20][3..8] */
  7. [max_is(,,e),min_is(a)] long g7[*..1][2..9][3..*]; /* g7[-10..1][2..9][3..25] */
  8. [min_is(a,,c),max_is(,d,e)] long g8[*..1][2..*][*..*]; /* g8[-10..1][2..15][-30..25] */

Implementation Note: As C does not directly support conformant arrays or arrays with non-zero lower bounds, the C representation of such an array is a pointer to the base type of the array.

Analysis of Implementation Tasks

  1. Remove semantic checks that impose the DCE 1.0 restrictions.
  2. Enhance header file generation for the fully general arrays.
  3. Enhance stub generation to support the fully general arrays.

REFLECTION OF NODE DELETIONS FROM SERVER TO CLIENT

The DCE 1.0 support for [ptr] pointers is not transparent in that if a manager routine releases storage via rpc_ss_free() for an input parameter, the corresponding free call does not occur in the client code. This additional transparency will be supported by DCE 1.1. It is enabled on a per-operation basis by specifying the [reflect_deletions] operation attribute. In the following example, if rpc_ss_free() is called with the value of parameter A in the manager code, the appropriate free() routine will be invoked in the client stub with the value of parameter A in the client address space.

    [reflect_deletions] my_oper( [in,ptr] long int *A );

Distinction of Node Deletion and Orphaning

If, during an RPC call, application code uses the IDL-provided rpc_ss_free routine to free a node pointed to by a [ptr] pointer, the effect of that free will be duplicated on return to the caller by having the caller stub call the appropriate free routine. If the called application routine orphans a node by simply breaking the link to it instead of freeing it as described above, then the corresponding node will be orphaned at the caller. Note that this discussion applies only to nodes that are a part of the parameter either at the time of the call or at the time of the return. Nodes that are created by the callee and either deleted or orphaned before the return will not be reflected back to the caller in any way. Also, changes to orphaned nodes are not reflected back to the caller.

Analysis of Implementation Tasks

  1. Add [reflect_deletions] attribute to IDL grammar.
  2. Enhance server implementation of rpc_ss_free() routine to save list of node numbers that have been freed.
  3. Modify server stub generation to transmit the conformant array of node numbers specifying the list of node numbers to be freed.
  4. Modify client stub generation to receive the conformant array of node numbers, lookup the associated addresses and invoke the proper free routine.

NON-ENCAPSULATED UNIONS

A union that is created without the use of the <union_switch> production is a non-encapsulated union. The discriminant of a non-encapsulated union is another parameter, if the union is a parameter, or another structure field if the union is a structure field.

When the non-encapsulated union is being declared as a type, the <union_type_switch_attr> production must be used. When a type that is a non-encapsulated type is used to declare a structure field or a parameter, the <union_instance_switch_attr> production must be used. When a non-encapsulated union is being declared directly as a structure field or a parameter, the <union_instance_switch_attr> production must be used.

Some examples of non-encapsulated unions:

  1. typedef [switch_type(long)] union { [case (1,3)] float a_float; [case (2)] short b_short; [default]; /* An empty arm; nothing is shipped. */ } n_e_union_t;
  2. typedef struct { long a; /* The discriminant for the union later in this struct. */ [switch_is(a)] n_e_union_t b; } a_struct; /* Note switch can follow union in operation */
  3. void op1 ( [in] handle_t h, [in, switch_is (s)] n_e_union_t u, [in] long s );

The n_e_union_t construct, above, gets turned into the following C union declaration in the generated header file.

    typedef union { float a_float; short b_short; } n_e_union_t;

Complete Union Syntax

The syntax for a union definition is:

  1. <tagged_union_declarator> ::= union <tag> | <tagged_union>
  2. <union_type> ::= union <union_switch> { <union_body> } | union { <union_body_n_e> }
  3. <union_switch> ::= switch ( <switch_type_spec> <Identifier> ) [ <union_name> ]
  4. <switch_type_spec> ::= <primitive_integer_type> | <char_type> | <boolean_type> | <enumeration_type>
  5. <tagged_union> ::= union <tag> <union_switch> { <union_body> } | union <tag> { <union_body_n_e> }
  6. <union_name> ::= <Identifier>
  7. <union_body> ::= <union_case> [ <union_case> ] ...
  8. <union_body_n_e> ::= <union_case_n_e> [ <union_case_n_e> ] ...
  9. <union_case> ::= <union_case_label> [ <union_case_label> ] ... <union_arm> | <default_case>
  10. <union_case_n_e> ::= <union_case_label_n_e> <union_arm> | <default_case_n_e>
  11. <union_case_label> ::= case <const_exp> :
  12. <union_case_label_n_e> ::= <[> case ( <const_exp> [ , <const_exp> ] ... ) <]>
  13. <default_case> ::= default : <union_arm>
  14. <default_case_n_e> ::= <[> default <]> <union_arm>
  15. <union_arm> ::= [ <field_declarator> ] ;
  16. <union_type_switch_attr> ::= switch_type ( <switch_type_spec> )
  17. <union_instance_switch_attr> ::= switch_is ( <attr_var> )

Analysis of Implementation Tasks

  1. IDL grammar
  2. Semantic checks
  3. Header file generation
  4. Stub generation

UNIQUE ATTRIBUTE FOR POINTERS

The [unique] attribute is the third and final pointer class to be supported by IDL. It provides a level of capability which can be efficiently implemented, and yet provides enough flexibility for the needs of many applications. Just as the current attributes [ref] and [ptr], it may be applied at the declaration site of a pointer in order to describe the required capabilities of the pointer.

Description of Unique Pointers

A unique pointer is more flexible than a reference pointer, although it shares several important characteristics. The distinguishing characteristics of a unique pointer are:

  1. A unique pointer may have the value NULL.
  2. A unique pointer may change from NULL to non-NULL during a call. This results in allocating memory on return from the call, into which the result is stored.
  3. A unique pointer may change from non-NULL to NULL during a call. This results in orphaning the pointee on return from the call.
  4. If a unique pointer changes from one non-NULL value to another non-NULL value, the change is ignored. Unique pointers do not identify particular extents of memory. They simply identify an extent of memory suitable for storing the data. If it is important to know that a different memory location is storing the data for some reason, then one should use full pointers.

A unique pointer has the following characteristics in common with reference pointers.

  1. No storage pointed to by a unique pointer can be reached from any other name in the operation; i.e., the pointer does not cause any aliasing of data within the operation.
  2. Data returned from the callee is written into the existing storage specified by the unique pointer if the pointer did not have the value NULL.

Analysis of Remaining Implementation Tasks

  1. Modify stub generatation to handle [unique] pointers.
  2. Regenerate stub support library routines for pointed-at types to include the newly add [unique] support.

USER-DEFINED EXCEPTIONS

This extension to the IDL compiler will allow specification of a set of user-defined exceptions that may be generated by the server implementation of the interface. If an exception occurs during the execution of the server, it terminates the operation and the exception is propagated from server to client. Exceptions are declared via the exceptions interface attribute which has the following syntax:

    <exceptions_attribute> :== exceptions ( <Identifier> [, <Identifier>] ... )

Example of the exceptions attribute:

    [uuid(06255501-08af-11cb-8c4f-08002b13d56d), version(1.1), exceptions( exc_e_exquota, binop_e_aborted, binop_e_too_busy, binop_e_shutdown) ] interface binop { long binop_add( [in] long a, [in] long b ); }

Since exceptions are associated with operation implementation, they are not imported into other interfaces via the import statement.

ACF Support for User-Defined Exceptions

In order to make exceptions as transparent as possible, the default behavior is for IDL to define and initialize the exceptions under a once block in the generated stubs. If, however, the user wants to share exception names across multiple interfaces, or closely control their definition and initialization, they may use an ACF attribute to disable this automatic exception definition mechanism.

  1. <extern_exceptions> :== extern_exceptions [ ( <Identifier> [, <Identifier>] ... ) ]
  2. [extern_exceptions(exc_e_exquota)] interface binop {}

If [extern_exceptions] is specified without a list of exceptions, no exceptions specified in the interface are defined or initialized. If a list of exceptions are present they represent the set of exceptions that are defined external to the interface and thus are not defined or initialized. All other exceptions listed in the [exceptions] attribute of the interface are defined and initialized. This attribute indicates that the specified exceptions are defined and initialized in some other external manner prior to making a call on the interface. They may be predefined exceptions (such as exc_e_exquota), provided by some other interface, or defined and initialized explicitly by the application itself.

NDR Representation of User-defined Exceptions

The set of exceptions listed in the <exceptions_attribute> of an interface will be associated in order with unsigned integer values starting with one. These integer values provide the network representation of the exceptionsfor this interface. The NDR of a user-defined exception is a fault packet containing two idl_ulong_int values. The first being the fault nca_s_fault_user_defined from ncastat.idl followed by the integer value associated with the particular exception that occurred.

Design Tradeoffs

The <Identifiers> specified in the <exceptions_attribute> are the names of exceptions manipulated via the dce/exc_handling.h exception package. This is necessary to provide a consistent treatment of exceptions throughout DCE and allow for a portable implementation.

The design outlined above allows specification of exceptions on a per-interface basis. If desired, exception specification could be allowed on a per-operation basis. This appears, however, to provide primarily a form of forced documentation, while providing little additional capability.

Advantages of per-operation exceptions:

  1. Provides explicit documentation of which operation may raise which exceptions.
  2. If an operation raises an unexpected exception, the client will see rpc_x_unknown_remote_fault instead of the actual exception thus the application may handle all error cases without the use of a CATCH_ALL.

Disadvantages of per-operation exceptions:

  1. Requires repeated specification of shared exceptions on each operation.
  2. Requires un/marshaling code to be generated per-operation.
  3. If an operation raises an unexpected exception, the client will see rpc_x_unknown_remote_fault instead of the actual exception thus reducing the level of information available.

Considering the concerns regarding stub size, and relatively small advantages provided by per-operation exceptions, this design chose declaration of exceptions on a per-interface basis.

Analysis of Implementation Tasks

  1. Add [exceptions(...)] interface attribute to IDL grammar.
  2. Add [extern_exceptions(...)] interface attribute to the ACF grammar.
  3. Generate two mapping functions into the cstub and sstub. One for idl_ulong_int-to-exception and one from exception-to-idl_ulong_int.
  4. Add a nca_s_fault_user_defined status to ncastat.idl that identifies a fault packet containing an exception integer value.
  5. Generate a routine into the cstub and sstub to perform the EXC_INIT on each of the exceptions defined in the interface subject to the [extern_exceptions] attribute as described above.
  6. Call the internal exception initialization routine for the interface within the stub once block.
  7. Upon detecting an exception in the server stub, the exception-to-idl_ulong_int function is called and a fault packet containing nca_s_fault_user_defined followed by the idl_ulong_int representing the exception number.
  8. The client stub should be extended to handle the nca_s_fault_user_defined specially by unmarshalling the exception number, calling the idl_ulong_int-to-exception function and then raising the indicated exception.

IDL ENCODING SERVICES (SUPPORT FOR PICKLING/PERSISTENT DATA STORAGE)

This extension to the IDL stub compiler will enable instances of one or more data types to be encoded into and decoded from a byte stream format suitable for persistent storage.

IDL Syntax

The ACF grammar will be extended by the following two attributes:

  1. [encode] -- Indicates that the generated stub should support encoding the arguments into a byte stream rather than invoking a remote procedure.
  2. [decode] -- Indicates that the generated stub should support decoding the arguments from a byte stream rather than invoking a remote procedure.

The [encode] and [decode] attributes may be applied to the interface or to operations within the interface.

IDL Semantics

Operations with the [encode] and/or [decode] attribute result in a stub generated into the client stub (cstub) file. An operation with the [encode] attribute will result in a client stub which supports the creation of the byte stream. An operation with the [decode] attribute will result in a stub that supports the retrieving the arguments from a byte stream. Applying the attribute to the interface has the same effect as applying the attribute on each operation in the interface. Although [encode] and [decode] are commonly used together, either may be omitted to reduce the size of the generated stub code.

Only parameters specified as [in] are represented in an encoding. In order to allow decoding, all parameters specified as [in] should also have the [out] attribute. Function results, [out]-only parameters, and [in]-only parameters will result in compiler diagnostics being displayed.

An operation with either the [encode] or [decode] attribute (either explicitly or implicitly by application on the interface) restricts the form of the operation. Example restrictions include:

  1. The operation may not contain any pipe parameters.
  2. The operation may not utilize [auto_handle] for binding.
  3. The operation may not utilize customized binding (via the [handle] attribute on a parameter).
  4. The operation may utilize implicit binding only if the type of the implicit handle is an encoding services handle (idl_es_handle_t).

Example ACF files

  1. example1.acf:

    /* ** All operations support encode/decode. */ [encode,decode] interface user_records {}

  2. example2.acf:

    /* ** The file_ACL operation is used to encode/decode data ** possibly utilized by other operations in the interface. */ [explicit_handle] interface files { [encode,decode,comm_status,fault_status] file_ACL(); file_open( [comm_status,fault_status] est ); file_close( [comm_status,fault_status] est ); }

  3. example3.acf:

    /* ** For clients that only create file_ACLs, we use only the ** [encode] attribute to save space in the generated stub. */ interface files { [encode] file_ACL(); }

User API

For an operation definition which represents an RPC operation, the IDL compiler generates RPC stubs which provide transparent access to an RPC server. In a similar fashion, a stub is generated for an encode/decode operation. These encoding stubs, however, hide the encoding/decoding of a set of data values into a byte stream.

When generating RPC stubs, the handle_t data type is generated as an rpc_binding_handle_t which describes the location of the RPC server. When generating an encoding stub, the handle_t data type is mapped into a idl_es_handle_t (IDL encoding services handle) which describes which controls the encoding process.

Data types and function prototypes

/*
** Data types for read/write/alloc routines that manage incremental
** processing of the encoding.
*/
typedef void (*)(
    idl_void_p_t  state,     /* [in,out] user state */
    idl_char      *buf,      /* [out] Address of buffer */
    idl_ulong_int *size      /* [in,out] Requested Size/Size of
                                buffer */
    ) idl_es_allocate_rtn_t; /* Routine to allocate a buffer */

typedef void (*)(
    idl_void_p_t  state,  /* [in,out] user state */
    idl_char      *buf,   /* [in] Encoded data */
    idl_ulong_int size    /* [in] Size of encoded data */
    ) idl_es_write_rtn_t; /* Routine to provide encoded data
                             to application */

typedef void (*)(
    idl_void_p_t  state, /* [in,out] user state */
    idl_char      *buf,  /* [out] Data to be decoded */
    idl_ulong_int *size  /* [out] Size of buf */
    ) idl_es_read_rtn_t; /* Routine to obtain encoded data
                            from application */

/*
** Provides an idl_es_handle_t which allows an encoding operation to
** be performed in an incremental fashion similar to pipe processing.
** Buffers areas are requested by the stubs via the allocate routine
** as necessary and when filled, are provided to the application via
** a call to the write routine.  This routine is suitable for
** encodings that can be incrementally processed by the applications
** such as when they are written to a file.  The application may
** invoke multiple operations utilizing the same handle.
*/
void idl_es_encode_incremental(
    idl_void_p_t      state, /* [in] user state */
    idl_es_allocate_t alloc, /* [in] alloc routine */
    idl_es_write_t    write, /* [in] write routine */
    idl_es_handle_t   *h,    /* [out] encoding handle */
    error_status_t    *st    /* [out] status */
    );

/*
** Provides an idl_es_handle_t which allows an encoding operation to
** be performed into a buffer provided by the application.  If the
** buffer is not of sufficient size to receive the encoding the error
** rpc_s_no_memory is reported.  This mechanism provides a simple,
** but high performance encoding mechanism provided that the upper
** limit of the encoding size is known.  The application may invoke
** multiple operation utilizing the same handle.
*/
void idl_es_encode_fixed_buffer(
    idl_byte        *ep,    /* [out] pointer to buffer to recieve the
                               encoding (must be 8-byte aligned) */
    idl_ulong_int   bsize,  /* [in] size of buffer provided */
    idl_ulong_int   *esize, /* [out] size of the resulting encoding
                               (set after execution of the stub) */
    idl_es_handle_t *h,     /* [out] encoding handle */
    error_status_t  *st     /* [out] status */
    );

/*
** Provides and idl_es_handle_t which provides an encoding in a
** stub-allocated buffer.  Although this mechanism provides the
** simplest application interface, large encodings may incur a
** performance penalty for this convenience.  The return buffer is
** allocated via the client memory allocation mechanism currently in
** effect at the time of the encoding call.
*/
void idl_es_encode_dyn_buffer(
    idl_byte        **ep,   /* [out] pointer to recieve the
                               dynamically allocated buffer which
                               contains the encoding */
    idl_ulong_int   *esize, /* [out] size of the resulting encoding
                               (set after execution of the stub) */
    idl_es_handle_t *h,     /* [out] decoding handle */
    error_status_t  *st     /* [out] status */
    );

/*
** Provides an idl_es_handle_t which allows an decoding operation to
** be performed in an incremental fashion similar to pipe processing.
** Buffers containing portions of the encoding are provided by the
** application via calls to the read routine.  This routine is
** suitable for encodings that can be incremental decoded by the
** stubs such as when they are read from a file.
*/
void idl_es_decode_incremental(
    idl_void_p_t    state, /* [in] user state */
    idl_es_read_t   write, /* [in] routine to supply buffers */
    idl_es_handle_t *h,    /* [out] decoding handle */
    error_status_t  *st    /* [out] status */
    );

/*
** Provides an idl_es_handle_t which allows an decoding operation to
** be performed from a buffer containing the encoded data.  There may
** be a performance penalty if the buffer provided is not 8-byte
** aligned.
*/
void idl_es_decode_buffer(
    idl_byte        *ep,  /* [in] pointer to buffer containing the
                             encoding */
    idl_ulong_int   size, /* [in] size of buffer provided */
    idl_es_handle_t *h,   /* [out] decoding handle */
    error_status_t  *st   /* [out] status */
    );

/*
** Returns the rpc_if_id_t and operation number contained within the
** encoding associated with an idl_es_handle_t.  This information is
** available during a decode operation, and after the operation has
** been invoked for an encode operations.  If this information is not
** available, the status rpc_s_unknown_if is returned.
*/
void idl_es_inq_encoding_id(
    idl_es_handle_t h,   /* [in] decoding handle */
    rpc_if_id_t     *if, /* [out] RPC interface identifier
                            (including version information) */
    idl_ulong_int   *op, /* [out] operation number */
    error_status_t  *st  /* [out] status */
    );

/*
** Frees a idl_es_handle_t and its associated resources.
*/
void idl_es_handle_free(
    idl_es_handle_t *h, /* [in,out] handle to free */
    error_status_t  *st /* [out] status */
    );

The idl_es_allocate_rtn_t routine

The user-provided allocate function is required to return the address of a buffer that is 8-byte aligned and a multiple of 8-bytes in length. This allows for optimized data manipulation in the generated stubs. The size argument provides the size requested by the stubs, the allocate routine may choose to return a buffer of a different size within the above constraints by indicating the size of the resulting buffer by modifying the value of the size argument.

The idl_es_write_rtn_t routine

The user-provided write function is required to process the provided buffer that now contains the encoded byte stream. It will be called when the active buffer has been filled, or when the end of the encoding has been reached.

The idl_es_read_rtn_t routine

The user-provided read function is required to return the address of a buffer which contains a portion of the encoded data that is 8-byte aligned and a multiple of 8-bytes in length. This allows for optimized data manipulation in the generated stubs.

Processing Description

In order to support a wide variety of applications, the API of an encoding interface can be used in several ways. In the most general form, processing of the byte stream is done via routine. This allows for large, dynamically sized data, to be encoded/decoded incremental.

Incremental encode operation

In order to perform an encode operation, the application creates an idl_es_handle_t with the idl_es_encode_incremental routine.

  1. The application state parameter may be used to specify application-specific state information which is passed, unmodified, to the allocate and write routines.
  2. The allocate routine is invoked by the encoding stub to obtain buffers into which the byte stream is written.
  3. The write routine is invoked by the encoding stub to communicate completed buffers back to the application for processing.

Upon invocation of encode operation stub, the allocate function supplied will be called to obtain a buffer into which the byte stream will be written. If the size of all operation arguments can be determined at IDL compile time, then the requested size will be an approximation of the size of the resulting byte stream. In either case, the allocate routine may choose to provide either a larger or smaller buffer. The actual size of the buffer returned is indicated via the size output argument of the allocate routine. The stub processes each argument, encoding it into the buffer provided. After filling the buffer, or reaching the end of the byte stream, the stub calls the write routine.

Since control returns to the application after the encoding operation, it is the application's responsibility to release any resources related to the application state, and free the idl_es_handle_t.

Incremental decode operation

In order to perform a decode operation, the application creates an idl_es_handle_t via the idl_es_decode_incremental routine.

  1. The application state parameter may be used to specify application-specific state information which is passed, unmodified, to the allocate and write routines.
  2. The read routine is invoked by the decoding stub to request a portion of the byte stream be delivered to the stub for processing.

Upon invocation of the decoding stub routine, the read function supplied will be called to obtain a buffer containing a portion of the byte stream. The read function will be called repeatedly until the entire byte stream has been obtained and the arguments decoded back into the operation arguments.

Example incremental decode

  1. scalars.idl:

    [uuid(689d24d8-45f0-11cb-825f-08002b107359)] interface scalars { void data_set1( [in] handle_t h, [in,out] long *a, [in,out] long *b, [in,out] double *c); }

  2. scalars.acf:

    [encode,decode] interface scalars { data_set1([comm_status] st); }

  3. scalars.c:

    #include <stdio.h> #include "scalars.h extern my_read(); main() { idl_ulong_int a,b; idl_long_float c; FILE *x = fopen("my_scalars.dat"); idl_es_handle_t h; error_status_t st; /* Decode file x, into values */ idl_es_decode_incremental((idl_void_p_t)x, my_read, &h, &st); if (st != error_status_ok) abort(); data_set1(h, &a, &b, &c, &st); if (st != error_status_ok) abort(); idl_es_handle_free(&h, &st); if (st != error_status_ok) abort(); fclose(x); printf("My data: %d, %d, %f\en", a, b, c); }

Buffer-based encode/decode operations

For applications that manipulate smaller units of encoded data, and/or require less control over the allocation and manipulation of the byte stream, IDL encoding services also provides a simpler interface. Under the simplified interface the byte stream created by the encode operation is returned via single buffer. This buffer may be provided by the application (idl_es_encode_fixed_buffer) or dynamically allocated by the encoding stub (idl_es_encode_dyn_buffer). For dynamically allocated buffers, the client memory management routines in effect at the time of the call to the stubs are used to allocate the resulting buffer. This is normally malloc/free (rpc_ss_allocate/rpc_ss_free from within a manager call) unless otherwise specified via rpc_ss_set_client_alloc_free() or rpc_ss_swap_client_alloc_free().

For a simplified decode operation, the application provides the starting address of the buffer containing the byte stream which is then decoded into the operation parameters specified.

Example of buffer-based encode

  1. scalars.idl:

    [uuid(689d24d8-45f0-11cb-825f-08002b107359)] interface scalars { void data_set1( [in] handle_t h, [in,out] long *a, [in,out] long *b, [in,out] double *c); }

  2. scalars.c:

    #include "scalars.h main() { idl_ulong_int a = 8, b = 22; idl_long_float c = 72.3; idl_es_handle_t h; idl_byte my_encoding[1000]; error_status_t st; idl_unlong_int my_encoding_size; /* Decode file x, into values */ idl_es_encode_fixed_buffer(my_encoding, 1000, &my_encoding_size, &h, &st); if (st != error_status_ok) abort(); /* Create byte stream encoding of my data */ data_set1(h, &a, &b, &c); idl_es_handle_free(&h, &st); if (st != error_status_ok) abort(); /* Display then free the encoded byte stream */ fwrite(my_encoding, my_encoding_size, 1, stdout); }

Interactions with Other IDL Features

Because an operation with the [decode] and/or [encode] attribute, is a client-only function, the manager entry point vector entry for that operation will be NULL. Servers requiring encoding services should incorporate the client stub for the desired interface.

Implementation Issues

The encoding format of the byte stream will be an extended version of that used by DCE security. This format contains a generic header portion which identifies the contents of the encoding (interface, version, operation number, and the transfer syntax used to encode the data portion), and the data itself. Any transfer-syntax dependent data (such as the drep for NDR) is encoded at the beginning of the data portion. The idl_pkl_header_t utilized by DCE SEC will be modified in the following ways:

  1. Add the interface version number.
  2. Add the interface operation number in the encoding.
  3. The size field will be removed.
  4. An integer drep field will be added to make the header endian-independant.

The resulting internal header format used by an IDL encoding is provided below. This format is distinguished from a DCE SEC pickle via the version number field. By examination of the version number field DEC SEC can case between the existing pickling code, and the IDL encoding services encoding stubs.

/*
 * i d l _ e s _ p v t _ h e a d e r _ t
 *
 * An IDL encoding is an opaque byte stream from the application
 * perspective.  The representation in a byte stream of a set of
 * values whose type is specified in IDL.  An encoding is divided
 * into two parts: a "header" part followed by a "data" part.
 *
 * The header part contains four fields encoded according to the spec
 * given below: an 8-bit version number, an 8-bit integer drep value
 * used to encode the header portion, an rpc_syntax_id_t specifying
 * the encoding used in the encoding, an rpc_if_id_t specifying the
 * interface uuid and version, of the value encoded in the pickle.
 * The data part of a pickle is a byte stream that contains the
 * encoding of the value per the encoding syntax specified in the
 * header; this may include some info that is needed by the selected
 * syntax and properly part of the pickled value (e.g., the NDR
 * format label).
 *
 * Assuming that the info in a pickle header could be modeled by the
 * following type:
 *
 *     struct idl_es_pvt_header_t {
 *         unsigned8       version;  /* pickle header version (now
 *                                      version=1)
 *         unsigned8       int_drep; /* drep of pickle header
 *         unsigned8       fill2;
 *         unsigned8       fill3;
 *         rpc_syntax_id_t syntax;   /* encoding syntax used
 *         rpc_if_id_t     if_id;    /* uuid/version of interface
 *         unsigned32      op_num;   /* operation number of encoding
 *     } idl_es_pvt_header_t;
 *
 * Here is the layout of the pickle header (all integer values in the
 * header are encoded in the endian format indicated by the
 * integer_drep field):
 *
 * name                                     type        offset(s)
 * ----                                     ----        ---------
 * version                                  unsigned8   0
 * integer_drep                             unsigned8   1
 * fill                                     unsigned8   2,3
 * syntax.id.time_low                       unsigned32  4,5,6,7
 * syntax.id.time_mid                       unsigned16  8,9
 * syntax.id.time_hi_and_version            unsigned16  10,11
 * syntax.id.clock_seq_hi_and_reserved      unsigned8   12
 * syntax.id.clock_seq_low                  unsigned8   13
 * syntax.id.node[0]                        byte        14
 * syntax.id.node[1]                        byte        15
 * syntax.id.node[2]                        byte        16
 * syntax.id.node[3]                        byte        17
 * syntax.id.node[4]                        byte        18
 * syntax.id.node[5]                        byte        19
 * syntax.version                           unsigned32  20,21,22,23
 * if_id.uuid.id.time_low                   unsigned32  24,25,26,27
 * if_id.uuid.id.time_mid                   unsigned16  28,29
 * if_id.uuid.id.time_hi_and_version        unsigned16  30,31
 * if_id.uuid.id.clock_seq_hi_and_reserved  unsigned8   32
 * if_id.uuid.id.clock_seq_low              unsigned8   33
 * if_id.uuid.id.node[0]                    byte        34
 * if_id.uuid.id.node[1]                    byte        35
 * if_id.uuid.id.node[2]                    byte        36
 * if_id.uuid.id.node[3]                    byte        37
 * if_id.uuid.id.node[4]                    byte        38
 * if_id.uuid.id.node[5]                    byte        39
 * if_id.vers_major                         unsigned16  40,41
 * if_id.vers_minor                         unsigned16  44,43
 * op_num                                   unsigned32  44,45,46,47
 */

ENABLE REBINDING UNDER CUSTOMIZED/IMPLICIT BINDING

This feature is an enhancement to the API for customized and implicit binding to allow attempts on multiple servers. Currently, the use of implicit or customized binding handles provide users with a way to build RPC an interface that appear to the caller as a local interface. They currently do not, however, support attempts on multiple servers due to their limited API. This feature is enabled by applying the [rebind] ACF attribute to either the customized handle or an customized implicit handle (i.e., an implicit handle whose type is not handle_t).

Upon the failure of the RPC call utilizing either the customized or customized implicit binding method with the [rebind] attribute, a user-supplied rebind routine will be invoked to provide another handle on which to attempt the call. The rules under which a rebind are attempted due to an error are the same as those for the [auto_handle] binding method. The rebind routine for a handle with the [rebind] attribute has the following signature:

    handle_t <TYPENAME>_bind( /* [in] */ <TYPENAME> instance, /* Customized handle */ /* [in,out] */ void *state /* Application state */ ); handle_t <TYPENAME>_rebind( /* [in] */ <TYPENAME> instance, /* Customized handle */ /* [in,out] */ void *state, /* Application state */ /* [in] */ handle_t h, /* Previous handle (from failing call) */ /* [in] */ error_status_t status, /* Status from failing call */ ); void <TYPENAME>_unbind( /* [in] */ <TYPENAME> instance, /* Customized handle */ /* [in,out] */ void *state, /* Application state */ /* [in] */ handle_t h /* Previous handle (from failing call) */ );

The application may indicate that rebinding is not possible by returning the value of NULL as the function result of the rebind routine. Note that the rebind routine has the responsibility for freeing the h argument, if necessary. The unbind routine is only called once per operation with the possibly NULL handle_t returned from the final call to the rebind routine.

Analysis of Implementation Tasks

  1. Add [rebind] attribute to ACF grammar.
  2. Generate rebind routine prototype into header file.
  3. Adapt retry loop used to implement auto_handle, for retry of customized bindings.
  4. Invoke the rebind routine, and retry call if a new handle is provided.

AUTHOR'S ADDRESS

Jerry Harrow Internet email: harrow@r2me2.enet.dec.com
ZK02-1/Q18 Telephone: +1-603-881-2193
Digital Equipment Corporation
110 Spitbrook Rd.
Nashua, NH 03062
USA