IDL supports primitive data types as well as composite data types. The set of supported composite types includes unions, structures, arrays, and sequences.
Primitive types
IDL supports the following primitive types:
SInt8, SInt16, SInt32, SInt64 – signed integer.UInt8, UInt16, UInt32, UInt64 – unsigned integer.UIntSize – unsigned integer for representing the object size (in bytes).The range of values depends on the hardware platform (equivalent to the size_t type in C).
UIntPtr – unsigned integer for representing the object address.The range of values depends on the hardware platform (equivalent to the uintptr_t type in C).
Handle – value whose binary representation consists of multiple fields, including a handle field and a handle permissions mask field.A handle can identify any resource.
handle<<interface name>> – value whose binary representation consists of multiple fields, including a callable handle field and a callable handle permissions mask field.A callable handle identifies both the IPC channel to the server and the endpoint of this server with the specified interface. The nk-psl-gen-c compiler verifies that the specified interface is defined in one of the IDL files whose paths are specified when calling the compiler. If there is no definition, the compiler will terminate with an error.
bytes<<maximum size in bytes>> – byte buffer containing no more than the defined number of bytes.string<<maximum size in bytes>> – string buffer consisting of a byte buffer whose actual maximum size is a unit larger than the specified size due to the presence of an additional byte for the terminating zero.Characters in the string buffer must have UTF-8 encoding. A string buffer can contain a zero-length string without a terminating zero, which indicates the absence of data or an empty string. A non-zero-sized string must be terminated with a terminating zero. (If an encoding other than UTF-8 is used, or if there is no terminating zero in a non-zero-sized string, the Kaspersky Security Module will prevent the transfer of the string buffer.)
Integer literals can be specified in decimal format, hexadecimal format (for example, 0x2f, 0X2f, 0x2F, 0X2F) or octal format (for example, 0O123, 0o123).
You can use the reserved word const to define the named integer constants by assigning their values using integer literals or integer expressions.
Example definitions of named integer constants:
const UInt32 DeviceNameMax = 0o100;
const UInt32 HandleTypeUserLast = 0x0001FFFF;
const UInt32 MaxLogMessageSize = (2 << 3) ** 2;
const UInt32 MaxLogMessageCount = 100;
const UIntSize MaxLen = (MaxLogMessageSize + 4) * MaxLogMessageCount;
Named integer constants can be used to avoid problems associated with so-called "magic numbers". For example, if an IDL description defines named integer constants for return codes of an interface method, you can interpret these codes without additional information when describing a policy. Named integer constants and integer expressions can also be applied in definitions of byte buffers, string buffers, and composite types to define the size of data or the number of data elements.
The bytes<<size in bytes>>, string<<size in bytes>>, and handle<<interface name>> constructs are used in definitions of composite types, signatures of interface methods, and when creating type aliases because they define anonymous types (types without a name).
Unions
A union stores different types of data in one memory area. In an IPC message, a union is provided with an additional tag field that defines which specific member of the union is used.
The following construct is used to define a union:
union <type name> {
<member type> <member name>;
...
}
Example of a union definition:
union ExitInfo {
UInt32 code;
ExceptionInfo exc;
}
Structures
The following construct is used to define a structure:
struct <type name> {
<field type> <field name>;
...
}
Example of a structure definition:
struct SessionEvqParams {
UInt32 count;
UInt32 align;
UInt32 size;
}
Arrays
The following construct is used to define an array:
array<<type of elements>, <number of elements>>
This construct is used in definitions of other composite types, signatures of interface methods, and when creating type aliases because it defines an anonymous type.
The Handle and handle<<interface name>> types can be used as the type of array elements if this array is not included in another composite data type. However, the total number of handles in an IPC message cannot exceed 255.
Sequences
A sequence is an array containing no more than the specified number of elements of the defined type. The following construct is used to define a sequence:
sequence<<type of elements>, <maximum number of elements>>
This construct is used in definitions of other composite types, signatures of interface methods, and when creating type aliases because it defines an anonymous type.
The Handle and handle<<interface name>> types cannot be used as types of sequence elements.
Variable-size and fixed-size types
The bytes, string and sequence types are variable-size types. In other words, the maximum number of elements is assigned when defining these types, but less elements (or none) may actually be used. Data of the bytes, string and sequence types are stored in the IPC message arena. All other types are fixed-size types. Data of fixed-size types are stored in the constant part of IPC messages.
Types based on composite types
Composite types can be used to define other composite types. The definition of an array or sequence can also be included in the definition of another type.
Example definition of a structure with embedded definitions of an array and sequence:
const UInt32 MessageSize = 64;
struct BazInfo {
array<UInt8, 100> a;
sequence<sequence<UInt32, MessageSize>, ((2 << 2) + 2 ** 2) * MessageSize> b;
string<100> c;
bytes<4096> d;
UIntSize e;
handle<embedder.DeviceManager> f;
}
The definition of a union or structure cannot be included in the definition of another type. However, a type definition may include already defined unions and structures. This is done by indicating the names of the included types in the type definition.
Example definition of a structure that includes a union and structure:
union foo {
UInt32 value1;
UInt8 value2;
}
struct bar {
UIntPtr a;
UInt8 b;
}
struct BazInfo {
foo x;
bar y;
}
Creating aliases of types
Type aliases make it more convenient to work with types. For example, type aliases can be used to assign mnemonic names to types that have abstract names. Assigned aliases for anonymous types also let you receive named types.
The following construct is used to create a type alias:
typedef <type> <type alias>
Example of creating mnemonic aliases:
typedef UInt64 ApplicationId;
typedef Handle PortHandle;
Example of creating an alias for an array definition:
typedef array<UInt8, 4> IP4;
Example of creating an alias for a sequence definition:
const UInt32 MaxDevices = 8;
struct Device {
string<32> DeviceName;
UInt8 DeviceID;
}
typedef sequence<Device, MaxDevices> Devices;
Example of creating an alias for a union definition:
union foo {
UInt32 value1;
UInt8 value2;
}
typedef foo bar;
Defining anonymous types in signatures of interface methods
Anonymous types can be defined in signatures of interface methods.
Example of defining a sequence in an interface method signature:
const UInt8 DeviceCount = 8;
interface {
Poll(in UInt32 timeout,
out sequence<UInt32, DeviceCount / 2> report,
out UInt32 count,
out UInt32 rc);
}
Page top