3 Understanding Flists
Learn about flists (field lists), which pass data between Oracle Communications Billing and Revenue Management (BRM) processes.
Topics in this document:
About Flists
The flist is the primary data structure used in BRM. Flists are containers that hold fields, each consisting of a data field name and value. Flists do not contain actual data but instead provide links to the data's location. The only exceptions are integers and strings, which are stored in flists.
Here is a simple flist example:
0 PIN_FLD_LAST_NAME STR [0] "Smith" 0 PIN_FLD_FIRST_NAME STR [0] "Joe" 0 PIN_FLD_COMPANY STR [0] "XYZ Corporation" 0 PIN_FLD_CURRENCY INT [0] 840
Many BRM processes interpret data in flist format. For example, the storage manager in the Data Manager (DM) translates flists to a format that the database can process and then translates the data from the database into an flist before passing it to the Connection Manager (CM).
BRM uses flists in these ways:
-
Objects are passed as flists between opcodes or programs that manipulate the objects.
-
Opcodes use flists to pass data between BRM applications and the database. For each opcode, an input flist is passed to PCM_OP, and the return flist is passed back from this routine.
In an flist, you can use any data type such as decimals, buffers, arrays, and substructures. Flists can contain any number of fields. You can place flists within other flists. Remember, though, that except for integers, data is not stored in the flist; it just links to where the data is located.
For a description of the BRM data types you can use in an flist, see "Understanding the BRM Data Types".
Note:
[0] after the field type represents the element ID. The numbers 0, 1, 2, and so on at the beginning of each line indicate the field's nesting level, and 0 indicates the top level.
When you include a field in an flist, you must also include an abbreviation of the field's data type. Table 3-1 lists the valid BRM field types and their abbreviations.
Table 3-1 Flist Field Types
Field Type | Abbreviation |
---|---|
PIN_FLDT_ARRAY |
ARRAY |
PIN_FLDT_BINSTR |
BINSTR |
PIN_FLDT_BUF |
BUF |
PIN_FLDT_DECIMAL |
DECIMAL |
PIN_FLDT_ENUM |
ENUM |
PIN_FLDT_ERRBUF |
ERR |
PIN_FLDT_INT |
INT |
PIN_FLDT_POID |
POID |
PIN_FLDT_STR |
STR |
PIN_FLDT_SUBSTRUCT |
SUBSTRUCT |
PIN_FLDT_TSTAMP |
TSTAMP |
Opcode Input and Output Specifications
Each PCM opcode requires specific data to perform its operation. The opcodes take input and output data as field lists (flists), which are lists of field name and value pairs. For more information about flists, see "About Flists".
Each opcode requires its input flist to contain specific fields for operation. For example, to create an object, the PCM_OP_CREATE_OBJECT() opcode requires an input flist that includes all the fields that an object of that storable class requires.
The Opcode Flist Reference contains the input and output flist specifications for each opcode, defining the following parameters for each field in the flist:
-
The mnemonic field names used by applications to reference the field
-
The data type and size for the field
-
The permissions, which specify if a field is mandatory (M) or optional (O)
The flist specifications use the following syntax to define each field in an flist:
class depth field ( type = data_type perms = permission permission ..., );
where:
-
class specifies whether it is a field, array, or a substruct.
-
depth contains an asterisk for each nesting level of the field.
-
field specifies the name of the field.
Examples:
field PIN_FLD_NAME ( type = PIN_FLDT_STR(255), perms = M, ); array * PIN_FLD_INHERITED_INFO ( type = PIN_FLDT_ARRAY, perms = O, );
The flist specifications specify whether a field is mandatory or optional.
About Creating and Using an Flist
You create and manipulate flists with the flist manipulation macros in the Portal Information Network (PIN) library. You can add, remove, and modify fields using the flist field-handling macros. For more information, see "Flist Management Macros" and "Flist Field-Handling Macros" in BRM Developer's Reference.
Each opcode has an input and output flist specification. Create an flist for each opcode you call for the data you want to pass in. When you create an input or an output flist for an opcode, follow the flist specifications. See the flist specifications in the individual opcode descriptions.
Flists are dynamically allocated data structures. When a field is added to an flist, the value is either already dynamically allocated in memory or copied into dynamic memory as it is added.
Note:
Destroy the flist you create in your programs to reclaim the memory that the flist occupies. For details, see "Destroying Flists".
Adding Information to Flists
You add data to flists by replacing pointers to data using these flist management macros:
-
PIN_FLIST_ELEM_PUT
-
PIN_FLIST_FLD_PUT
-
PIN_FLIST_SUBSTR_PUT
Note:
(Release 15.0.1 or later) You can pass a compilation switch from your custom source code's build scripts, which causes pointers to objects that are PUT to flists to be set to NULL, preventing them from accidentally being destroyed (causing a double-free error) in later code.
For details on these macros, see "Flist Management Macros" in BRM Developer's Reference.
You add data to flists by replacing the data itself using these flist management macros:
-
PIN_FLIST_ELEM_SET
-
PIN_FLIST_FLD_SET
-
PIN_FLIST_SUBSTR_SET
For details on these macros, see "Flist Management Macros" in BRM Developer's Reference.
Removing Data (Pointers) from an Flist
You remove a pointer to data from an flist and add it to another flist by using these flist management macros:
-
PIN_FLIST_ELEM_TAKE
-
PIN_FLIST_FLD_TAKE
-
PIN_FLIST_SUBSTR_TAKE
Note:
These macros overwrite existing pointers to data, not the data itself. To free the memory used by the old data, destroy the memory location using PIN_FLIST_DESTROY_EX. Otherwise, a memory leak may occur.
You usually use the TAKE macros when you want to take data from an flist and change it.
Note:
When you use a TAKE macro to remove a pointer to data, you must free the memory when finished. If the memory is not freed, memory leaks may occur.
For details on these macros, see "Flist Management Macros" in BRM Developer's Reference.
Copying Data from an Flist
You copy a pointer to data from one flist to another by using these flist management macros:
-
PIN_FLIST_ELEM_GET
-
PIN_FLIST_FLD_GET
-
PIN_FLIST_SUBSTR_GET
You usually use the GET macros when you want to copy data but not change it.
Note:
You should treat any data that you GET using these macros as read-only because the original program may also need it.
For details on these macros, see "Flist Management Macros" in BRM Developer's Reference.
Destroying Flists
You destroy an flist and free its memory by using these flist management macros in BRM Developer's Reference:
Flists use dynamically allocated memory and must be destroyed to free that memory and prevent memory leaks. These macros first determine whether the flist has a NULL value. If so, they do nothing. If the flist exists, these macros destroy its entire contents, including all fields.
PIN_FLIST_DESTROY_EX sets the reference to the flist to a NULL value after it destroys the flist.
PIN_FLIST_DESTROY frees the memory for the flist field-value pairs, but does not set a reference to that flist to NULL. If another program subsequently attempts to destroy this flist (with freed memory but a valid flist pointer), unexpected behavior and core dumps can result.
For details on these macros, see "Flist Management Macros" in BRM Developer's Reference.
Flist Creation Samples
BRM SDK includes flist creation samples in C, C++, Java, and Perl. Three samples are provided for each language: one to create a simple flist, another to create an flist with a nested array, and a third to create an flist with a substructure. For information about installing and using BRM SDK, see "About BRM SDK".
You can view the following sample programs in BRM Developer's Reference:
These documents also include information about compiling and running the programs.
Using Compile-Time Flags to Avoid Errors in Flists (Release 15.0.1 or later)
The PUT family of flist macros effectively transfers ownership of an object to the target flist. The object could be a decimal object, another flist, a POID, or so on. During this process, the source pointer may no longer contain a reliable reference to the original object that was PUT. For example, the target flist may subsequently be destroyed so that the pointer now refers to an area of memory that has been freed and contains garbage. Using that pointer after the PUT may result in a crash or other undefined behavior. This is a common source of defects in programs, which can be resolved by the following idiomatic coding style:
PIN_FLIST_ELEM_PUT(target_flistp, source_elemp, PIN_FLD_PRODUCTS, elemid, ebufp);
source_elemp = NULL;
This idiom helps avoid cases where source_elemp is used after the PUT where it may no longer be a valid pointer.
To reduce program errors and the burden on programmers to remember to set the source pointer variable to NULL, you can configure the BRM flist API to update the source pointer to NULL directly. This avoids errors where programmers forget to reset a pointer to NULL and simplifies the code, reducing verbosity.
To permit BRM to automatically set the source pointer to NULL during PUT operations in custom application code, set the compile-time flags in Table 3-2 in your custom code's Makefile.
Table 3-2 Compile-Time Flags for Each Flist Macro
Flist Macro | Compile-Time Flag |
---|---|
PIN_FLIST_SUBSTR_PUT |
-DASSIGN_NULL_AFTER_SUBSTR_PUT |
PIN_FLIST_ELEM_PUT |
-DASSIGN_NULL_AFTER_ELEM_PUT |
PIN_FLIST_FLD_PUT |
-DASSIGN_NULL_AFTER_FLD_PUT |
Note:
After enabling this feature, existing code may crash if it unsafely uses the original object pointer. However, this is not a cause for alarm. It simply means that the feature has exposed a potential code defect. To fix this, you can modify the code to remove the unsafe access, such as by deferring the PUT until later or reorganizing the logic to be safe with appropriate checks for a NULL pointer.
The following shows a sample error you could encounter after enabling one of the compile-time flags in your new or existing custom code and attempting to compile:
In file included from fm_ar_event_utils.c:31:0: fm_ar_event_utils.c: In function 'fm_bill_adjust_event_find_adjustments': /scratch/temp/portalbase/publish_linux_vob/publish_linux/include/pcm.h:2211:42: error: value required as left operand of assignment #define SET_NULL_TO_FLD_VALUE(valp) valp = NULL; /scratch/temp/portalbase/publish_linux_vob/publish_linux/include/pcm.h:2228:9: note: in expansion of macro 'SET_NULL_TO_FLD_VALUE' SET_NULL_TO_FLD_VALUE(valp); fm_ar_event_utils.c:1400:4: note: in expansion of macro 'PIN_FLIST_FLD_PUT' PIN_FLIST_FLD_PUT(read_flistp, PIN_FLD_POID, (void *)rerate_obj, ebufp);
Table 3-3 shows how to fix possible compilation error types in sample code using the PIN_FLIST_FLD_PUT macro.
Table 3-3 Fixing Sample Code with Compile Errors
Compilation Error Type | Problematic Code | Fixed Code |
---|---|---|
Type casting |
PIN_FLIST_FLD_PUT(srch_args_flistp,
PIN_FLD_POID, (void *)s_pdp, ebufp);
|
PIN_FLIST_FLD_PUT(srch_args_flistp,
PIN_FLD_POID, s_pdp, ebufp);
|
Putting an object that results from a function call |
PIN_FLIST_FLD_PUT(flistp, PIN_FLD_PERCENT, (void *)pbo_decimal_round(pct, precision, ROUND_HALF_UP, ebufp); |
pin_decimal_t *tmp_rounded_val = pbo_decimal_round(pct, precision, ROUND_HALF_UP, ebufp); PIN_FLIST_FLD_PUT(flistp, PIN_FLD_PERCENT, tmp_rounded_val, ebufp); |
Putting stack-allocated memory on an flist |
char str_msg[50]; PIN_FLIST_FLD_PUT(out_flistp, PIN_FLD_NAME, str_msg, ebufp); |
PIN_FLIST_FLD_SET(out_flistp, PIN_FLD_NAME, str_msg, ebufp);
Note: Use PIN_FLST_FLD_SET to copy the string onto the flist as heap-allocated memory since str_msg is allocated on the stack, not the heap, and therefore becomes invalid when exiting the current lexical scope. |
Putting NULL |
PIN_FLIST_FLD_PUT(flistp, PIN_FLD_STATUS, NULL, ebufp); |
PIN_FLIST_FLD_SET(flistp, PIN_FLD_STATUS, NULL, ebufp);
Note: Use PIN_FLIST_FLD_SET, since NULL is not dynamically allocated memory. |
Flist Management Rules
Follow these rules when creating programs that manipulate flists:
-
The calling applications are responsible for allocating memory for input flists. This includes cases where you use a wrapper opcode to call another opcode.
-
The opcode being called is responsible for allocating memory for output flists. For more information, see "About Creating and Using an Flist".
-
The calling applications are responsible for destroying both input and output flists.
-
The opcode being called is responsible for destroying both input and output flists if the opcode utilizes a wrapper opcode to call another opcode. For more information, see "Destroying Flists".
-
You should never destroy an input flist within an opcode, because a calling application may need it.
-
These flist management macros in BRM Developer's Reference allocate new memory:
You must explicitly free a newly created flist using the PIN_FLIST_DESTROY_EX macro unless that flist will be used for the opcode output.
Example
You use the following syntax to copy an flist:
*out_flistpp = PIN_FLIST_COPY(in_flistp, ebufp);
Flist Field Memory Management Guidelines
You use the following guidelines when creating your programs to avoid memory problems:
-
The following macros in BRM Developer's Reference allocate new memory for a passed value before putting it in an flist:
These macros add or replace items in an flist by copying them; no memory ownership is transferred. When you use the SET macros, memory is allocated to copy the values and is owned by the flist. Do not explicitly free this memory.
-
The following macros do not allocate new memory for a value before putting that value in an flist:
These macros add or replace items in the flist, storing the data previously owned by the caller. The allocation memory is transferred to the flist. These macros link the memory occupied by the value to the named flist. You cannot apply these for local scope (auto) variables if you want to put them into the return flist.
-
The following macros take ownership of the memory owned by the flist for a retrieved value:
These macros remove items from the flist, return pointers, and turn ownership of the allocated memory over to the caller.
-
The following macros do not allocate new memory for a retrieved value:
A pointer to the allocated segment of memory that belongs to the flist returns. To maintain the integrity of the flist, you should never apply the PIN_FLIST_DESTROY macro to an flist pointer returned by these macros. The flist retains ownership of its allocated memory.
-
The following macros move fields from one flist to another:
Memory ownership of the field changes from the source flist to the destination flist.
-
The following field management macros allocate new memory:
All of the preceding macros return the memory owned to the caller.
-
The memory allocated by the macros in these guidelines is owned by the flist. Typically, all flists created by code must be destroyed using PIN_FLIST_DESTROY or PIN_FLIST_DESTROY_EX.
-
Any memory allocated using pin_malloc must be freed unless it is added to an flist using a PUT macro.
-
All flists must be destroyed eventually, either directly or by nesting them in another flist as a child and giving memory ownership to that flist.
-
In Facilities Modules (FMs), the output flist is the only flist that does not need to be explicitly destroyed by the FM, because the Connection Manager (CM) framework destroys it after use.
For details on these macros, see "Flist Management Macros" in BRM Developer's Reference.
Handling Errors
All flist routines take a pointer to pin_ebuf_t as the final argument. This pointer, called ebufp, must point to a preallocated pin_ebuf_t into which error information is written.
The BRM APIs use the pin_ebuf_t structure to pass back detailed information about the error. BRM includes standard logging routines that print formatted log messages using the date in pin_ebuf_t. You can use these log messages to determine where the error occurred.
See "Finding Errors in Your Code" for more information on how BRM handles error messages.