TOC PREV NEXT INDEX DOC LIST MASTER INDEX



Package Data_Decomposition

Package Data_Decomposition provides resources that allow data values written with the Portable_Transfer package to be converted back into their original discrete, record, or array
definitions.

This package and Portable_Transfer are part of the Data Decomposition Annex and are an optional ASIS capability.

This capability is available in Rational ASIS.

For more information, click on a topic:


Resources in Package Data_Decomposition

The resources (excluding types and constants) in package Data_Decomposition fall into several functional groups, as shown below.

To see detailed reference information, click on the name of a resource:
Component properties:
Component_Declaration Is_Array Is_Identical Is_Record
Component_Indication Is_Equal Is_Nil
Obtaining components and nested components:
All_Named_Components Discriminant_Components
Array_Components Record_Components
Accessing array elements:
Array_Index Array_Iterator Next
Array_Indexes Done Reset
Runtime representation queries:
Array_Length Last_Bit Size
First_Bit Position
Data-stream construction, extraction, and conversion:
Component_Data_Stream Portable_Constrained_Subtype
Construct_Artificial_Data_Stream


Key Concepts for Package Data_Decomposition

Package Data_Decomposition provides resources that allow data values written with the Portable_Transfer package to be converted back into their original discrete, record, or array definitions. To see more information, click on a topic:

Data Streams and Component Types

A data stream is an array that contains the encoding of the value of a variable. The variable can be of a scalar, array, or record type. Not all type definitions can be correctly encoded, however. See "Type-Definition Model Kinds" for a description of the kinds of type models defined by ASIS and those that can be encoded.

Variables are converted into data streams with the subprograms in package Portable_Data. They are converted back into variables by instantiations of generics from this package.

Note: It is also possible to create an artificial data stream. This is a stream that contains only discriminant values; it is created with the Construct_Artificial_Data_Stream function. See "Constructing Artificial Data Streams" for more information.

When data streams represent composite structures such as records and arrays, they are constructed so as to allow extraction of the individual components. All that is needed to perform an extraction is a handle to a component.

Component Types

The Record_Component and Array_Component types describe components; the first describes a single component of a record, and the second describes an array. Each record or array component contains sufficient information to extract the component's data stream from a data stream of the enclosing component.

Once a data stream is obtained that represents a simple static type, or a simple dynamic type for which the discriminants are known (see "Type-Definition Model Kinds"), the data stream can be converted into a value of the type.

For more information, click on a topic:

Record Components

The Record_Component_List type defines an array of Record-_Component values. Values of this type are returned by functions that identify only the discriminants or the discriminants and components of a record. See "Obtaining Components and Nested Components" for more information.

A data stream representing a record value can be converted into its component data streams by extracting the streams from the record's stream. The conversion is performed by using a Re-cord_Component that describes one of the record's discriminants or components (see "Constructing Artificial Data Streams").

Array Components

The Array_Component_List type defines an array of Array-_Component values. The Array_Component_List type is defined for completeness and is not used by the ASIS interfaces.

A data stream representing an array value can be converted into data streams that represent the individual elements of the array. The conversion is performed by using an Array_Component in conjunction with a dimensional index, a linear index, or an array iterator (see "Constructing Artificial Data Streams" and "Accessing Array Elements").

Type-Definition Model Kinds

The type-definition model kinds used by ASIS are not intrinsic to Ada. They exist to categorize and simplify reference to the kinds of type definitions that packages Data_Decomposition and Portable_Transfer can correctly encode.

The type-definition model kinds range from simple and static to complex and dynamic, based on whether the definition includes discriminants and variant parts and how the values for those discriminants are obtained.

The Type_Model_Kinds type enumerates the three kinds of models. Descriptions of the characteristics of each model kind and examples of the type definitions defined by each model kind appear in the areas listed below.

You can use the Type_Model_Kind function to determine the type model kind for a particular type definition or component.

Click on a topic for more information:

Simple Static Types

Simple static types contain no variants and have a single fixed 'Size attribute value. All components and attributes are thus themselves static and/or fully constrained. The size and position of all components of the type can be determined without regard to constraints. For example:

Note that simple static types can include discriminant associations (as in the Static_Discriminated record). The record cannot contain a variant part, however.

Simple Dynamic Types

Simple dynamic types contain one or more components or attributes whose size, position, or value depends on the value of one or more constraints assigned during execution. This means that the size, position, and number of components can be determined only by reference to the values of the constraints.

These types can be encoded correctly because the discriminant values are stored within an instance of the type, making them available at runtime for placement into the stream. For example:

Arrays with an unconstrained subtype, whose 'Length, 'First, and 'Last attribute values depend on dynamic index constraints, can also be processed because these attribute values can be obtained at runtime and can be placed in the data stream constructed from the array. For example:

Complex Dynamic Types

Complex dynamic types contain components whose size or position depends on the values of externally defined, nonstatic values that are not stored within instances of the type. The attribute values for the components of an instance of these types cannot be determined without knowing these externally defined values.

These types cannot be encoded correctly because the values of the discriminants are not stored within an instance of the type, so they are not obtainable at runtime.

For example:

Component Properties

Uninitialized record and array component variables test as Is_Nil. An uninitialized component variable is both Is_Equal and Is_Identical to either the Nil_Record_Component or Nil_Array-_Component value, as appropriate.

Two component variables test as Is_Equal if they represent the same component, from the same record or array type, from the same compilation unit. The compilation units need not be from the same library.

Two component variables test as Is_Identical if they are Is_Equal and the compilation units have been obtained from the same library.

A component that represents an array subtype tests as Is_Array. A component that represents a record subtype tests as Is_Record. A component that represents a scalar type tests as neither Is_Array nor Is_Record.

Every non-nil component has an associated declaration. You can use the Component_Declaration function to obtain the ASIS element that represents the declaration.

Every component that tests as Is_Array has an associated subtype indication (Ada83 LRM 3.6(2), Ada95 LRM 3.6(2)). You can use the Subtype-
_Indication function to obtain the element that represents the indication.

Obtaining Components and Nested Components

Ada supports a rich set of type-definition capabilities. This includes the ability to nest types, including record and array definitions, to essentially arbitrary depths. For example, a record definition can consist of composite elements, with each com-posite element consisting of its own composites, and so on.

To decompose a data stream of arbitrary complexity, you must be able to decompose the record structure itself. Three functions, described below, assist with decomposition. Each requires at least a type definition, record component, or array component and returns the constituent component or components.
Function Name
Purpose
Discriminant_Components
Returns a list of the discriminants contained in the specified record type or record component.
Record_Components1
Returns a list of the discriminants and components contained in the specified record type or record component.
Array_Components
Returns a single array component that describes the specified array type or array component.
1 If a simple dynamic model type is specified, a data stream containing appropriate discriminant values must be provided so that the correct variant components can be identified.

Each returned component can itself represent a record or array type. You can continue calling these functions to obtain nested components until a component represents a scalar type.

To obtain all the components of a record type definition, use the All_Named_Components function. This function returns an array of entity name definitions (see package Expressions, "Expression Kinds, Types, and Object Names"). One element is returned for each component in the record; the elements returned are not based on discriminant values.

The returned list allows you to quickly determine whether an element of a particular name exists anywhere within a record.

Accessing Array Elements

An array object is a composite object consisting of components that have the same subtype (Ada83 LRM 3.6(1), Ada95 LRM 3.6(1)). An array object is characterized by:

Arrays present special problems in decomposition. Unlike records, an array component does not describe a single value; it describes the entire array. There is one array element (value) for each index value, in each dimension, of an array.

Consider the following declarations:

 type Team is (Home, Visitor);
type Quarter is range 1 .. 4;
type Scoring
    is array (Home, Quarter) of Natural;

This Scoring array:

This array type definition is used to illustrate the three methods of array decomposition.

Click here for more information on array decomposition.

Array Decomposition

Arrays are decomposed in three ways. The only difference between the methods is how an array element is described, so each method has a corresponding data type. The methods and their associated types are described in these sections:

The following functions accept a dimensional index value, a linear index value, or an iterator and return information related to the identified element:

The Component_Data_Stream function provides the data stream corresponding to a particular component. The other functions provide attribute data for the component.

Dimensional Indexes

You can identify a particular element in an array by specifying a value for the index in each dimension of the array; this method is similar to standard Ada subscripting. The Dimension_Indexes type is used to specify the index values.

The Dimension_Indexes type is an array whose elements are Asis.Asis_Positive values. Each element in the array represents a dimension of the array being decomposed, and the value specifies the relative index value in that dimension.

Note: The range of any dimension's index value is 1 .. Array-_Length, not 'First .. 'Last. Index values are thus relative and not identical to the values that appear in your application. ASIS uses relative indexes because the actual index values might not be available to an application.

If you know what element you are interested in, you can initialize a Dimension_Indexes variable to the appropriate values. This variable, with the array component, then can be used to obtain the data stream or attribute information for the element.

The relationship between index values and dimensional index values is illustrated by the following:
Team
Dimensional Index Values
Home
(1, 1)
(1, 2)
(1, 3)
(1, 4)
Visitor
(2, 1)
(2, 2)
(2, 3)
(2, 4)
Quarter
1
2
3
4

A method of iterating over all elements of an array is also provided (see "Iterators"). The dimensional index value of the element currently referenced by an iterator can be obtained with the Array_Indexes function. Iterators step the rightmost dimension's index value fastest.

Linear Indexes

A linear index value is an Asis.Asis_Positive number. The range of linear index values for any particular array is 1 .. N, where the upper bound is returned by the Array_Length function and represents the product of the lengths (the 'Length attribute value) of each dimension.

If you know what element you are interested in, you can initialize a variable to the appropriate linear index value. This variable, with the array component, then can be used to obtain the data stream or attribute information for the element.

The relationship between index values and linear index values is illustrated by the following:
Team
Linear Index Values
Home
(1)
(2)
(3)
(4)
Visitor
(5)
(6)
(7)
(8)
Quarter
1
2
3
4

A method of iterating over all elements of an array is also pro-vided (see "Iterators"). The linear index value of the element currently referenced by an iterator can be obtained with the Array_Index function.

Iterators

The final method of accessing array elements is with an iterator.

Iterators are represented by the Array_Component_Iterator private type. An iterator either references an actual array element or references beyond all the elements of the array. If the reference is beyond the array, the iterator is said to be done.

The following subprograms are provided for the initialization, query, and control of iterators:
Subprogram
Purpose
Array_Index
Returns a linear index value identifying the iterator's current array element.
Array_Indexes
Returns a dimensional index value identifying the iterator's current array element.
Array_Iterator function
Initializes an iterator to access the first array element.
Done function
Returns True if the iterator has been stepped past the end of the array.
Next procedure
Steps the iterator to the next array element.
Reset procedure
Resets the iterator to the first array element.

For information on using iterators, click here.

Using Iterators

To use an iterator:

1. . Initialize the iterator by calling the Array_Iterator function with an array component.

2. . Verify that the iterator has not exceeded the number of elements in the array by calling the Done function. The Done function returns True immediately after the initialization of an iterator if the array is a nil array.

3. . Use the iterator to obtain attribute or data-stream values.

4. . Step the iterator to the next element with the Next procedure.

Each iterator is independent and maintains its own state information. Iterators can be assigned to one another, and each iterator operates independently.

The order of array-element access with an iterator is illustrated by the following:

Runtime Representation Functions

The runtime representation functions return attribute information. The attribute values available and the corresponding function names are described in the following table:
Attribute Name
Function Name
References
'Length
Array_Length
LRM Annex A(24, 25),
13.6.2, and 3.8.2
'First_Bit
First_Bit
LRM Annex A(16) and
13.7.2
'Last_Bit
Last_Bit
LRM Annex A(23) and
13.7.2
'Position
Position
LRM Annex A(34) and
13.7.2
'Size
Size
LRM Annex A(41, 42) and 13.7.2

The above functions are overloaded, with several different parameter profiles, as indicated by the following table:
Function
Name

Type Definition
Array/Record Component
Array Iterator
Array_Length1

Y

First_Bit

Y
Y
Last_Bit

Y
Y
Position

Y
Y
Size
Ya
Y

1 If a simple dynamic model type is specified, a data stream containing appropriate discriminant values must be provided so that the correct variant components can be identified.

Constructing Artificial Data Streams

Variables typically are converted into data streams with the subprograms in package Portable_Transfer. These data streams represent actual data values and are typically used for subsequent component extraction and conversion. These data streams contain values for all appropriate discriminants and components.

If only attribute values are required, the only component values that need to exist in the stream are those that represent the discriminants. The discriminant values are all that is required to identify the appropriate variant parts and thus size and locate the components. The data streams that represent records partially defined in this way are called artificial data streams; they are constructed with the Construct_Artificial_Data_Stream function.

The construction of an artificial data stream is an iterative process and can require one call to Construct_Artificial_Data-_Stream for each discriminant in the record. For each call, a type definition, a data stream, a record component representing a discriminant, and the value for the discriminant must all be specified. The function returns a data stream with the discriminant value placed into the appropriate location. The initial call specifies a nil data stream; this directs the function to initialize the stream before installing the first discriminant value.

You need not specify all discriminant values; only those in the selected variant parts need be specified. It is good practice, however, to specify all values.

Decomposing Data Streams

Data streams can represent simple scalar values or complex records. The definitions of these structures can be decomposed with the functions that return the components (see "Obtaining Components and Nested Components"). With the component definitions, the component streams can be extracted with the Component_Data_Stream function.

Remember that a record or array component contains sufficient information to extract that component from a stream of the appropriate type. The resulting stream can in turn be described in terms of its components and similarly decomposed. This decomposition can continue until a scalar type is reached.

Decomposition is not required to convert a data stream into a value; the conversion can be performed at any point where a fully constrained subtype representing the value can be declared.

Converting Data Streams to Variables

Conversion of a data stream into a variable is accomplished with the Portable_Constrained_Subtype generic function. As is indicated by the name, the function works only if the actual parameter represents a constrained subtype. The actual parameter to the instantiation can represent a scalar type, such as System-.Integer, a simple static type, or a constrained instance of a simple dynamic type. The function converts the specified data stream into an instance of the type.

A common technique is to decompose a data stream to obtain the discriminant values needed to define the constrained sub-type. The constrained subtype is then defined, the generic is instantiated with this type, and the conversion is performed.

Depending on the implementation of the Portable_Constrained-_Subtype generic function and the compiler being used, conversion of a data stream to an inappropriate type can cause the Constraint_Error exception to be raised. Conversion of a data stream to an incorrect type or an incorrectly discriminated type can cause various runtime exceptions.


Rational Software Corporation  http://www.rational.com
support@rational.com
techpubs@rational.com
Copyright © 1993-2001, Rational Software Corporation. All rights reserved.
TOC PREV NEXT INDEX DOC LIST MASTER INDEX TECHNOTES APEX TIPS