gmslStoreClass

The Store Service Class

The Store Service Class contains the methods needed to manage data organized into hierarchical fields and records. When dealing with language processing, program storage must be viewed as containing a wide variety of different structures; indexes, lists, fixed records, long unformatted records, variable length strings, and so on. In addition, it may be purely memory bound or it may be stored in a persistent file system. The role of this service class is to provide a single interface for storing and retrieving information. It is sufficiently robust to allow for the management of large files in the multiple gigabyte size range, while it is still efficient for use by purely memory bound applications.

Physically, a storage area consists of a series of relatively large fixed sized blocks whose boundaries cannot be crossed without software support. It is this unalterable property of persistent storage that must be dealt with explicitly if an efficient information management system is desired. The manner in which these blocks are created, maintained, and accessed is determined when the storage area is created or opened. At a logical level the storage area is viewed as consisting of a simple sequence of binary words -- long memory. All information is stored on a word boundary and all offsets are expressed in terms of words.

The following methods are in the Store Service class.

Store_AddGlobal Method

gmSL: int Store_AddGlobal(int root, int global)
gmVB: Function Store_AddGlobal(ByVal root As Integer, ByVal global As Integer) As Integer
gmNI: int Store_AddGlobal(int root, int global)

The method Store_AddGlobal adds the root offset of a component, indexed by its parent to a global index maintained within the storage area. This entry declares that the child component has global scope if and only if the parent component is an active reference. The integer parameter root is the root offset of a component that may have global scope and the integer parameter global is the offset of the parent external library or class whose active reference status triggers the global scope.

The gmBasic symbol table has a tree structure. The symbols within the tree need be unique along one branch only. As a result of this there can be many duplicates in the symbol table. A critical operation that must be performed by the compiler is to search the symbol tree for the correct occurrence of an identifier given the current context. This turns out to be a very difficult problem. The simplest approach is to go through each branch on the symbol table in current scope order and see if the identifier can be found on that branch. This approach would use the method StoreFindVector() to make this determination. Though this method is highly optimized and designed to be largely independent of the size of the symbol table, this approach degrades as the symbol table expands in direct proportion to the number of active branches in the tree. This expansion is made even worse because symbols that are relatively low in the symbol tree can be referenced directly, so the tool must be constantly traversing these low level branches for identifiers.

An alternative approach is to pre search the symbol tree so that questions like "Does the library X expose a public symbol Y in context Z" can be asked directly without requiring a sequential search. Internally a sorted index structure is used. The phrase "component Y is posted to host X" means that a index-value pair is added to the identifier of Y whose index is the root offset of the host component X and whose value is the root offset of the base component Y. At compile time when a given identifier is encountered the compiler can then very quickly move through the active host libraries in their precedence order and ask the question "Is this identifier present as a global within the scope of this host". This method adds a global - root to the sorted index structure associated with the identifier of the root.

As an example consider the VB6 shared module problem. In Vb6 only class files, not module files, are exposed in libraries; consequently, when multiple code projects use the same module file each project loads its own copy of that file. In moving these code projects to .NET there is no problem with exposing the static classes form by modules within a library; therefore, a common migration approach is to identify these shared source files, to include them in only one of the source files, and to reference them as external components in the other source files. The identification of the shared files is performed via the gmPL SharedFile utility statement. The problem with referencing the module components as external components is that module components have global scope; while external components do not. Changing that status is precisely what the method Store_AddGlobal does. The following code is a copy of the SharedFile_MakeModuleGlobalgmSL implemented method in the language file that actually performs this operation. It has been changed to display a log message describing the pairs that it is adding.


 <gmSL namespace="RuntimeDll" class="SharedFile" dialect="gms">
 void SharedFile_MakeModuleGlobal(int parent, int globalHost)
 {
    int childRoot;

    childRoot = Store_GetFirst(parent);
    while(childRoot)
    {
       Store_AddGlobal(childRoot,-globalHost);
       Write_LogMessage("Store_AddGlobal(" + Symbol_FullName(childRoot,-1) + ", " + Symbol_FullName(globalHost,-1) + ")" );
       childRoot = Store_GetNext(childRoot);
    }
 }
 </gmsl>
The code itself is stored in the GlobalSettings file for a small sample code that demonstrates the SharedFile migration. Here is the resultant listing.


 Store_AddGlobal(SharedHost.modSharedModule.myClass, SharedHostDLL.dll.SharedHost)
 Store_AddGlobal(SharedHost.modSharedModule.myAsnewClass, SharedHostDLL.dll.SharedHost)
 Store_AddGlobal(SharedHost.modSharedModule.pubFunc, SharedHostDLL.dll.SharedHost)
 Store_AddGlobal(SharedHost.modSharedModule.arrFunc, SharedHostDLL.dll.SharedHost)
 Store_AddGlobal(SharedHost.modSharedModule.useForm, SharedHostDLL.dll.SharedHost)
 Store_AddGlobal(SharedHost.Form1.whatForm, SharedHostDLL.dll.SharedHost)
The now external SharedHostDLL.dll.SharedHost now exposes the listed components for global reference which they would not be had SharedHost been a standard VB6 library.

Store_Close Method

gmSL: void Store_Close()
gmVB: Sub Store_Close()
gmNI: void StoreClose(void);

The Store_Close method closes the current storage area if that area is currently open. It does not change the currently selected storage area.

For example the following command code can be used


 <gmsl>Store_Close()</gmsl>
as an alternative to this gmPL Storage statement.


 <Storage Action="Close" />

Store_Create Method

gmSL: int Store_Create(string filename, int iUnit)
gmVB: Function Store_Create(ByVal filename As String, ByVal iUnit As Integer) As Integer
gmNI: int Store_Create(char* filename, int iUnit);

The Store_Create method selects the indicated storage unit and initializes it as a new empty area whose content will be optionally paged into a specified file. Any information currently in the storage are will be lost. Until another storage unit is selected via one of the three methods -- Store_Select, Store_Create, or Store_Open all storage requests will be directed to this currently selected storage unit.

If the storage is to be paged into a file, then the filename parameter contains the name of that file (without an extension). If the storage is to be purely memory-based, this parameter should be NULL.

The integer iUnit parameter is the number of the storage unit to be used. This must be a value between 1 and 16. Note that gmBasic uses units one through 5 internally. There is an enumeration StorageUnit that defines these units. When the intent is to refer to one of these gmBasi units then an entry from this enumeration should be used. They are as follows:

Entry Description of content
USER This is the storage area that is managed via the gmPL Storage statement.
LANG This is the storage area used to contain the language file controlling the translation.
TEMP This is a temporary storage area used within specific tasks. It is never left open, between tasks.
INFO This is a temporary storage area used by the gmPL utility statements like IncludeOrder to contain information being gathered,
CLIENT This is the storage area used to contain the GlobalSettings information.

For most applications the USER storage area should be used, as many of the authoring service methods in particular assume that they are using this area.

If all goes well this method returns a one (true). If there is a problem creating the storage unit, a zero (false) is returned.

As an example the following command mode code


 <pBasic>
    <gmsl>
    if(Select.UseLocalMemory)
    {
       Write_LogMessage("Using Local Memory")
       Store_Create(NULL, StorageUnit.USER)
    }
    else
    {
       Write_LogMessage("Using FmStock1.vbi")
       Store_Create("FmStock1", StorageUnit.USER)
    }
    </gmsl>
    <Select DevEnv="VS2010" />
      ...
performs the identical operation as the following gmPL code


 <pBasic>
    <Storage Action="Create" Identifier="FmStock1" />
    <Select DevEnv="VS2010" />
       ...
except that it issues additional logging messages describing the storage used.

Store_DeltaVector Method

gmSL: Handle Store_DeltaVector(int root)
gmVB: Function Store_DeltaVector(ByVal root As Integer) As Handle
gmNI: void* Store_DeltaVector(int root);

The Store_DeltaVector method gets a handle to a component information vector in the current storage area that it intends to change. In particular, this method gives read/write access to the information vector associated with the component whose root offset is specified via the integer root parameter. If there is any question about the type of component being accessed or about the validity of the root offset value, use the Store_GetObjectType method first (see the discussion there). Any changes made to the information will generally not be saved. Use the Store_GetVector method if the vector is simply to be read. The actual handle is optimized to make the retrieval of the component information as efficient as possible. As such it should only be used locally and should be refreshed after any other storage intensive operations have been performed. To use Store_DeltaVector the storage area being accessed have been opened using a CREATE or APPEND action.

The method returns a semi-permanent handle to the information vector. It is up to the user to ensure that the storage object type of the component is compatible with the structure type of the variable that is receiving the handle.

The following command code


 <gmBasic>
 <gmSL>
    int       varRoot;
    tVariable varInfo;

    Store_Open("fmstock1.vbi", StorageUnit.USER, True)
    varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0)
    Write_OpenFile(1, "Demo021.out", True, OutStyle.Text);
    AuditVbi.LocVariable(varRoot);
    varInfo = Store_DeltaVector(varRoot)
    varInfo.Private = False
    varInfo.Public = True
    varInfo.migComment = "Changed status to Public"
    AuditVbi.LocVariable(varRoot);
    Store_Close();
 </gmSL>
gmBasic>
opens one of the FmStocks existing storage areas with append being True. It then finds the root offset of a known variable. Using the language file method AuditVbi.LocVariable it audits the content of the variable, which is shown to be Private. The intent is to change this status to Public and to add a comment indicating that the change was made.

Within the gmBasic storage system changing the content of a component proceeds in two steps. First, it is accessed in change mode with its handle stored in a program variable whose structure type is declared or determinable via the type inference rules of gmSL. This access then allows the code not only to refer to the fields of the structure, but also to assign values to them using assignment statements. To change the status of a variable from Private to Public requires setting it boolean Private field to False and its boolean Publicfield to True. To change its string field migComment simply requires assigning a new string to it. Running the LocVariable method again then shows the result.


 Detailed Description of Variable FMStocks_DB.TxNew.m_dbh with root address 254799:
 Property                       | Content
 --------                       | -------
 Migrate Status                 | Referenced
 Migrate Flags                  | none
 Status Flags                   | Private | FixedType | Changed
 VB_Name                        | TxNew:11307
 Binary Type                    | User
 Reference                      | DBHelper:11875
 Dimensions                     | 0
 Number References              | 3

 Detailed Description of Variable FMStocks_DB.TxNew.m_dbh with root address 254799:
 Property                       | Content
 --------                       | -------
 Migrate Status                 | Referenced
 Migrate Flags                  | none
 Migrate Comment                | Changed status to Public:360422
 Status Flags                   | Public | FixedType | Changed
 VB_Name                        | TxNew:11307
 Binary Type                    | User
 Reference                      | DBHelper:11875
 Dimensions                     | 0
 Number References              | 3

Store_FindFirstChild Method

gmSL: int Store_FindFirstChild(int levels[], int iRoot)
gmVB: Function Store_FindFirstChild(ByVal levels As Integer[], ByVal iRoot As Integer) As Integer
gmNI: int Store_FindFirstChild(int* levels, int iRoot);

The Store_FindFirst method initiates a depth first or preorder search of the symbol tree. If the iRoot parameter is non-zero, then the node with this offset is visited first. If the iRoot parameter is zero, then the first node in the tree is visited first. See the description of the StoreFindNext for information on how the next node visited is determined. The method returns the offset of the first node visited or zero, if the symbol tree is empty.

The integer vector levels is used internally by the search algorithm. It must have at least two plus the maximum levels in the tree entries. Normal code structures have no more than four levels -- project, class, subprogram, and local variable; however, controls defined via forms can often be nested more deeply. A size of 20 is always safe.

As an example consider the following code


 <gmBasic>
 <gmSL>
    int       iRoot;
    int       levels[19];
    tVariable varInfo;

    Store_Open("fmstock1.vbi", StorageUnit.USER, False)
    Write_OpenFile(1, "Demo023.out", True, OutStyle.Text);
    Write_Line("Listing of ByRef arguments in " + Store_GetDataName() + " Storage Area")
    iRoot = Store_FindFirstChild(levels,0)
    while(iRoot != 0) {
       if(Store_GetObjectType(iRoot) == ObjectType.VARIABLE) {
          varInfo = Store_GetVector(iRoot);
          if(VarInfo.ByRef) {
             Write_Integer(levels(0) - 1, 3)
             Write_Integer(iRoot,8)
             Write_Text(" " + Symbol_FullName(iRoot,-1))
             Write_Record()
          }
       }
       iRoot = Store_FindNextChild(levels)
    }
    Store_Close()
 </gmSL>
 </gmBasic>
which scans a FmStocks translation file and list all of the ByRef arguments. The call to b>Store_FindFirstChild returns the offset of the first symbol in the symbol tree.

Store_FindLibVector Method

gmSL: int Store_FindLibVector(string identifier, int parent, int iCall)
gmVB: Function Store_FindLibVector(ByVal Identifier As String, ByVal parent As Integer, ByVal iCall As Integer) As Integer
gmNI: int Store_FindLibVector(char* identifier, int parent, int iCall)

The method Store_FindLibVector determines if an identifier is a member of a component in an external library. The symbol structure generated by Visual Basic source code is a fairly straight-forward tree structure. The external libraries, however, like MSADO have very complicated structures using interrelated coclasses and interfaces with sometimes intertwined parent-child relationships. This method searches an external library component whose root offset is specified via the integer parameter parent to determine if it has a member specified by the string attribute identifier. If so, it returns the root offset of that member. If not, it returns 0. The integer parameter iCall is a tracing value used to identify the particular call within the compilation process. Its value is arbitrary and is usually simply set to zero.

Here is a sample search


 <gmBasic> <!-- Unit test for Store_FindLibVector -->
 <Storage Action="Open" Identifier="fmstock1.vbi" />
 <Output Status="New" Filename="demo042.out" />
 <gmsl>
    int      coClassRoot;
    int      iRoot;

    coClassRoot = Symbol_FindIdentifier("ADODB.RecordSet", 0);
    iRoot = Store_FindLibVector("DataSource",coClassRoot,0);
    Write_Line("The component " + Symbol_FullName(iRoot,-1) + " is in the coClass " +
                Symbol_FullName(coClassRoot,-1));
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
which produces this result.


 The component ADODB.Recordset20.DataSource is in the coClass ADODB.Recordset

Store_FindNextChild Method

gmSL: int Store_FindNextChild(int levels[])
gmVB: Function Store_FindNextChild(ByVal levels As Integer[]) As Integer
gmNI: int Store_FindNextChild(int* levels);

The Store_FindNextChild method continues a depth first or preorder search of the symbol tree. The levels parameter must have been initialized by the Store_FindFirstChild method. In a depth first search, a visit to a node is followed immediately by visits to all descendants of the node. There is a simple recursive algorithm for describing a depth first search.


 void depth_first_tree_search(node v, int level, void* info)
 {
    node u;

    visit(v,level,info);
    level++;
    for(each child u of v) depth_first_tree_search(u,level,info);
 }
The method returns the offset of the next node visited or zero, if there are no more nodes to visit.

Continuing the example from Store_FindFirstChild the following code


 <gmBasic>
 <gmSL>
    int       iRoot;
    int       levels[19];
    tVariable varInfo;

    Store_Open("fmstock1.vbi", StorageUnit.USER, False)
    Write_OpenFile(1, "Demo023.out", True, OutStyle.Text);
    Write_Line("Listing of ByRef arguments in " + Store_GetDataName() + " Storage Area")
    iRoot = Store_FindFirstChild(levels,0)
    while(iRoot != 0) {
       if(Store_GetObjectType(iRoot) == ObjectType.VARIABLE) {
          varInfo = Store_GetVector(iRoot);
          if(VarInfo.ByRef) {
             Write_Integer(levels(0) - 1, 3)
             Write_Integer(iRoot,8)
             Write_Text(" " + Symbol_FullName(iRoot,-1))
             Write_Record()
          }
       }
       iRoot = Store_FindNextChild(levels)
    }
    Store_Close()
 </gmSL>
 </gmBasic>
which scans a FmStocks translation file and list all of the ByRef arguments. The call to Store_FindNextChild is what keeps the loop active until all nodes in the symbol tree have been visited. The output from this example then is as follows


 Listing of ByRef arguments in fmstock1.vbi Storage Area
   4  233070 FMStocks_DB.Helpers.ReportEvent.plpStrings
   4  233096 FMStocks_DB.Helpers.ReportEvent.lpRawData
   4  248267 FMStocks_DB.Helpers.CopyMemory.hpvDest
   4  248353 FMStocks_DB.Helpers.CopyMemory.hpvSource
   4  248782 FMStocks_DB.Helpers.GetComputerNameAPI.nSize
   4  254306 FMStocks_DB.Account.VerifyUser.AccountID
   4  254337 FMStocks_DB.Account.VerifyUser.FullName
   4  254601 FMStocks_DB.Account.GetAccountInfo.FullName
   4  254627 FMStocks_DB.Account.GetAccountInfo.Email
   4  254652 FMStocks_DB.Account.GetAccountInfo.Balance
   4  260018 FMStocks_DB.DBHelper.collectParams.cmd
Note that typical code structures have four levels -- project, class, subprogram, and local variable. The level of a symbol can be obtained from the levels parameter and as expected is 4 for each subprogram argument.

Store_FindRoot Method

gmSL: int Store_FindRoot(string identifier)
gmVB: Function Store_FindRoot(ByVal identifier As String) As Integer
gmNI: int Store_FindRoot(char* identifier);

The Store_FindRoot method finds an identified symbol root. This method searches the base index of the symbol table for a specified identifier and returns its root. This root is unique for each identifier. If no symbol exists in the base index with the specified identifier then a zero is returned.

The gmBasic symbol table has a tree structure. The symbols within the tree need be unique along one branch only. As a result of this, there can be many duplicates in the symbol table. The roles of the symbol retrieval methods are to search the symbol tree for the correct occurrence of an identifier given a current parent context -- i.e., the typical question asked is "Does a given parent node have a child with the specified identifier". This question must be asked for every parent in the current scope in scope order. This turns out to be a very difficult problem. The simplest approach is to go through each branch on the symbol table in current scope order and see if the identifier can be found on that branch. But there are many branches, especially if a global symbol is being sought, so this process can require many individual searches. To make matters even worse the required searches are string searches which require time consuming string comparisons as opposed to simple integer comparisons. The approach used by gmBasic separates the search into two parts. First there is a base index in the symbol table that contains one entry for each unique symbol defined anywhere in the current code set. This is the only index that uses string keys. Associated with this entry for each symbol there is a secondary index that uses integer parent node roots as the key and the root of the actual information vector for the identifier in its form within that parent.

The example here shows how it can be used to determine if a given identifier is defined within a storage area.


 <gmBasic>
 <gmSL>
    Store_Open("fmstock1.vbi", StorageUnit.USER, False)
    if(Store_FindRoot("hEventLog")) Write_LogMessage("There is a symbol hEventLog defined.");
    else Write_LogMessage("There is no symbol hEventLog defined.");
    if(Store_FindRoot("dwBytes")) Write_LogMessage("There is a symbol dwBytes defined.");
    else Write_LogMessage("There is no symbol dwBytes defined.");
    if(Store_FindRoot("byteCount")) Write_LogMessage("There is a symbol byteCount defined.");
    else Write_LogMessage("There is no symbol byteCount defined.");
    Store_Close()
 </gmSL>
 </gmBasic>
The output shows that in this storage area the first two identifiers are defined while the last is not.


 There is a symbol hEventLog defined.
 There is a symbol dwBytes defined.
 There is no symbol byteCount defined.

Store_FindVector Method

gmSL: int Store_FindVector(string identifier, int parent)
gmVB: Function Store_FindVector(ByVal identifier As String, ByVal parent As Integer) As Integer
gmNI: int Store_FindVector(char* identifier, int parent);

The method Store_FindVector searches a single branch within the hierarchical symbol table for a for a component with the identifier specified by the string identifier parameter. If the integer parent parameter is zero, then the root branch of the tree is searched; else the branch dominated by the parent is searched. If a component with the specified identifier is found within the specified branch, then the root offset of that component is returned. If no component can be found in the branch, then a zero is returned.

As an example here is an explicit search down a symbol tree for a subprogram argument


  <gmBasic>
  <gmSL>
     int project;
     int module;
     int method;
     int argument;

     Store_Open("fmstock1.vbi", StorageUnit.USER, False)
     project = Store_FindVector("C:\gmSrc\fmstocks\vb6\FMStocks_DB\FMStocks_DB.vbp", 0);
     module = Store_FindVector("C:\gmSrc\fmstocks\vb6\FMStocks_DB\Helpers.bas", project);
     method = Store_FindVector("ReportEvent", module);
     argument = Store_FindVector("plpStrings", method);
     if(argument) Write_LogMessage("The argument plpStrings has root offset " + argument);
     else Write_LogMessage("The argument plpStrings was not found.");
     Store_Close();
  </gmSL>
  </gmBasic>
As the log message indicates this argument was found in the symbol tree.


 The argument plpStrings has root offset 233070

Store_FirstReference Method

gmSL: tReference Store_FirstReference(int symbolRoot, int[] PostSet)
gmVB: Function Store_FirstReference(ByVal symbolRoot As Integer, ByVal PostSet As Integer[]) As tReference
gmNI: int* Store_FirstReference(int symbolRoot, tPosting* PostSet);

The method Store_FirstReference finds the first reference made to a symbol in the compiled code. During compilation symbol references are stored in a reference record set sorted in ascending order by the root of the symbol being referenced. The reference record, type tReference, has the following fields:

Field Description of content
MakesRef The root of the component containing the reference
BeingRefd The root of the component being referenced
RecNumber The text record number in the host file of the reference
CodeOffset The offset in the compiled code of the referencing code
HostFile The root of the information file containing the subprogram
Terminal If the reference treated the component as terminal then one, else zero

What Store_FirstReference actually returns is the first record whose BeingRefd field is greater than or equal to the value of the integer parameter symbolRoot. The integer vector PostSet is a private storage vector used by the retrieval methods. It must contain at least four entries. Note that the method Store_NextReference, which is used to iterate forward in the record set must be passed this same storage vector.

The simplest use of Store_FirstReference is to determine if a symbol is used at all, as shown here


 <gmBasic>
 <gmSL>
    int         iRoot;
    int         postSet[4];
    int         levels[19];
    tReference  refInfo;
    tVariable   varInfo;

    Store_Open("fmstock1.vbi", StorageUnit.USER, False)
    Write_OpenFile(1, "Demo026.out", True, OutStyle.Text);
    iRoot = Store_FindFirstChild(levels,0)
    while(iRoot != 0) {
       if(Store_GetObjectType(iRoot) == ObjectType.VARIABLE) {
          varInfo = Store_GetVector(iRoot);
          if(VarInfo.ByRef) {
             refInfo = Store_FirstReference(iRoot,postSet);
             if(refInfo.BeingRefd == iRoot) Write_Line("The ByRef argument " + Symbol_FullName(iRoot,-1) + " is Used")
             else Write_Line("The ByRef argument " + Symbol_FullName(iRoot,-1) + " is Not Used")
          }
       }
       iRoot = Store_FindNextChild(levels)
    }
    Store_Close()
 </gmSL>
 </gmBasic>
The code goes through all symbols in the symbol table looking for ByRef arguments. It then uses Store_FirstReference to find the first reference record that might contain a reference to the argument. If the refInfo.BeingRefd field equals the symbol root then the symbol is used. In this case, the output is


 The ByRef argument FMStocks_DB.Helpers.ReportEvent.plpStrings is Not Used
 The ByRef argument FMStocks_DB.Helpers.ReportEvent.lpRawData is Used
 The ByRef argument FMStocks_DB.Helpers.CopyMemory.hpvDest is Used
 The ByRef argument FMStocks_DB.Helpers.CopyMemory.hpvSource is Used
 The ByRef argument FMStocks_DB.Helpers.GetComputerNameAPI.nSize is Not Used
 The ByRef argument FMStocks_DB.Account.VerifyUser.AccountID is Used
 The ByRef argument FMStocks_DB.Account.VerifyUser.FullName is Used
 The ByRef argument FMStocks_DB.Account.GetAccountInfo.FullName is Used
 The ByRef argument FMStocks_DB.Account.GetAccountInfo.Email is Used
 The ByRef argument FMStocks_DB.Account.GetAccountInfo.Balance is Used
 The ByRef argument FMStocks_DB.DBHelper.collectParams.cmd is Used

Store_GetDataName Method

gmSL: string Store_GetDataName()
gmVB: Function Store_GetDataName() As String
gmNI: char* StoreGetDataName(void);

The method Store_GetDataName gets the name of the current storage area. When a file-based storage area is created or opened its name is stored in the storage control structure. This method returns that name. If the storage area is local memory based, then a null-string is returned.

As an example, consider this simple code


 <gmBasic> <!-- Unit test for Store_GetDataName -->
 <Storage Action="Create" Identifier="Demo028.vbi" />
 <gmsl>
    if(Store_GetDataName()) Write_LogMessage("The storage area " + Store_GetDataName() + " has been created.")
    else Write_LogMessage("The Application is using local memory")
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
if run with the Select UseLocalMemory attribute Off then it logs


 The storage area Demo028.vbi has been created.
But if run with that attribute On it logs


 The Application is using local memory

Store_GetFirst Method

gmSL: int Store_GetFirst(int parent)
gmVB: Function Store_GetFirst(ByVal parent As Integer) As Integer
gmNI: int Store_GetFirst(int parent);

The method Store_GetFirst returns the root of the first child of a parent component or of the root branch. This is the first component entered for the parent. In general, using this method in conjunction with the Store_GetNext method, will generate the sequence of components stored within some branch of the symbol table in their defined order. The integer parameter parent contains the root offset of the parent component or zero if the first member of the root branch in the symbol table is desired.

If there is a first child of the specified component, then this method returns its root offset. If there is no first child, then this method returns a zero.

As the following code shows the simplest use of Store_GetFirst is to determine whether a given component has any children or is nonterminal.


 <gmBasic> <!-- Unit test for Store_GetFirst -->
 <gmSL>
    int       iRoot;
    int       levels[19];

    Store_Open("fmstock1.vbi", StorageUnit.USER, False)
    Write_OpenFile(1, "Demo029.out", True, OutStyle.Text);
    Write_Line("Listing of Class declares with children " + Store_GetDataName() + " Storage Area")
    iRoot = Store_FindFirstChild(levels,0)
    while(iRoot != 0) {
       if(levels(0) == 4 && Store_GetFirst(iRoot) && Store_GetObjectType(iRoot) == ObjectType.Declaration)
       {
          Write_Text("The ")
          Symbol_DisplayInfo(iRoot,DisplayType.OBJECT_TYPE)
          Write_Line(" " + Symbol_FullName(iRoot,-1) + " Has children")
       }
       iRoot = Store_FindNextChild(levels)
    }
    Store_Close()
 </gmSL>
 </gmBasic>
Notice that Store_GetFirst, as opposed to Store_FindFirstChild, does not require a support vector. The result here is as follows.


 Listing of Class declares with children fmstock1.vbi Storage Area
 The Declaration FMStocks_DB.Helpers.RegisterEventSource Has children
 The Declaration FMStocks_DB.Helpers.DeregisterEventSource Has children
 The Declaration FMStocks_DB.Helpers.ReportEvent Has children
 The Declaration FMStocks_DB.Helpers.CopyMemory Has children
 The Declaration FMStocks_DB.Helpers.GlobalAlloc Has children
 The Declaration FMStocks_DB.Helpers.GlobalFree Has children
 The Declaration FMStocks_DB.Helpers.GetComputerNameAPI Has children

Store_GetGlobals Method

gmSL: int Store_GetGlobals(int symbolRoot,int[] globals,int nGlobal)
gmVB: Function Store_GetGlobals(ByVal symbolRoot As Integer, ByVal globals As Integer[], ByVal nGlobal As Integer) As Integer
gmNI: int Store_GetGlobals(int symbolRoot,int* globals,int nGlobal);

The method Store_GetGlobals returns the global, root pairs added to a symbol identifier via the Store_AddGlobal method. The description of that method contains a detailed description of these pairs. The integer parameter symbolRoot contains a root offset in the base symbol index as returned by the Store_FindRoot method. The integer vector parameter globals returns the pairs added to the specified symbol, and the integer parameter nGlobal defines the length of the globals vector. No more than nGlobal entries will be returned. The method returns the number of entries returned in the vector. This is two times the number of pairs.

The code sample here continues the VB6 shared module problem from the Store_AddGlobal description. It opens a shared module file and retrieves the global information associated with one of the symbols.


 <gmBasic> <!-- Unit test for Store_GetGlobals -->
 <gmSL>
    int  symbolRoot;
    int  globals[32];
    int  nGlobal;
    int  iGlobal;
    int  iRoot;

    Store_Open("SharedModule1.vbi", StorageUnit.USER, False)
    Write_OpenFile(1, "Demo045.out", True, OutStyle.Text);
    symbolRoot = Store_FindRoot("myClass");
    Write_Line("The symbol root for <myClass> is " + symbolRoot);
    nGlobal = Store_GetGlobals(symbolRoot,globals,32);
    Write_Line("The global vector for <myClass> contains " + nGlobal + " entries");
    iGlobal = 0;
    while(iGlobal < nGlobal)
    {
       iRoot = globals(iGlobal);
       if(iRoot < 0) iRoot = - iRoot;
       Write_Text("   Globals(" + iGlobal + ") = " + globals(iGlobal) + " : ");
       Symbol_DisplayInfo(iRoot,DisplayType.OBJECT_TYPE);
       Write_Line(" " + Symbol_FullName(iRoot,-1));
       iGlobal = iGlobal + 1;
    }
    Store_Close();
 </gmSL>
 </gmBasic>
The output is as follows. Notice that the pairs are shown in global, root order. In the actual index the global is the index and the root is the index associated value.


 The symbol root for <myClass> is 22987
 The global vector for <myClass> contains 2 entries
    Globals(0) = -3515 : Library SharedHostDLL.dll.SharedHost
    Globals(1) = 22992 : Lib_Property SharedHost.modSharedModule.myClass
The primary use of this global facility is to deal with multi-use classes whose referencing have special properties like excel with its Global multi use coclass which exposes the same identifiers like Application as both first level identifiers and as multi use components. Internally, gmBasic uses the negative of a global offset to mark it as a simple one as opposed to the one of these special ones. When using the output from Store_GetGlobals this negative condition must be checked for.

Store_GetHandle Method

gmSL: Handle StoreGetHandle()
gmVB: Function Store_GetHandle() As Handle
gmNI: void* Store_GetHandle(void);

The method StoreGetHandle gets the long memory handle of the current storage area. All storage areas are maintained within a long memory area. Though most access needs for that memory area are provided internally, it is occasionally necessary to use one of the specialized stream access methods directly. If there is a current storage area open, then the handle of its long memory provider is returned; else a NULL is returned.

Text streams are one of the specialized streams that require a long memory handle. The following code shows how it is obtained and used.


 <gmBasic> <!-- Unit test for Store_GetHandle -->
 <gmSL>
    int    textRoot;
    Handle textStream;

    Store_Create(NULL, StorageUnit.USER);
    Write_OpenFile(1, "Demo030.out", True, OutStyle.Text);
    Write_OpenStream();
    #TextStart
    Hello World
       This is a test of Store_GetHandle()
    GoodBye World
    #TextEnd
    textRoot = Write_CloseStream();
    textStream = Text_Open(Store_GetHandle(),textRoot);
    Write_TextStream(textStream,0,0);
    Text_Close(textStream);
    Store_Close();
 </gmSL>
 </gmBasic>
The output simply shows the context of the text stream


 Hello World
    This is a test of Store_GetHandle()
 GoodBye World

Store_GetIdent Method

gmSL: string Store_GetIdent(int root)
gmVB: Function Store_GetIdent(ByVal root As Integer) As String
gmNI: char* Store_GetIdent(int root,int* nIdent);

The method Store_GetIdent gets the source identifier of component via its root address. By definition, the "identifier" is the original identifier that was associated with the component when it was first placed in the symbol table by Store_Vector. See the Store_SetName and Store_GetName methods for information about changing how components are assigned alternative target identifiers.

As the following code shows the source identifier of a component remains unchanged when it is renamed and is always the identifier used to retrieve it.


 <gmBasic> <!-- Unit test for Store_GetIdent and Store_GetName -->
 <Storage Action="Append" Identifier="fmstock1.vbi" />
 <gmsl>
    int varRoot;

    varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0);
    Write_LogMessage("The variable ident is " + Store_GetIdent(varRoot));
    Write_LogMessage("The variable name is " + Store_GetName(varRoot));
 </gmsl>
 <Refactor>
    <Rename Identifier="FMStocks_DB.TxNew.m_dbh" Content="m_databaseHandle" />
 </Refactor>
 <gmsl>
    int varRoot;

    varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0);
    Write_LogMessage("The variable ident is " + Store_GetIdent(varRoot));
    Write_LogMessage("The variable name is " + Store_GetName(varRoot));
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
The output shows the identifier unchanged


 The variable ident is m_dbh
 The variable name is m_dbh
 The variable ident is m_dbh
 The variable name is m_databaseHandle

Store_GetInfo Method

gmSL: int Store_GetInfo(int offset, Handle info)
gmVB: Function Store_GetInfo(ByVal offset As Integer, ByVal info As Handle) As Integer
gmNI: int Store_GetInfo(int offset, void* info);

The method Store_GetInfo reads short blocks of information that were initially written using the method Store_String. A physical read is not required, since these short blocks do not cross block boundaries; however, in some instances reading them is needed to avoid downstream memory page faults. The most common use of this method is to read the list of interfaces associated with a class.

The handle parameter info is a handle to the area in memory to receive the information read, and the integer parameter offset is the offset of the start of the information to be read. This offset must be a value previously returned by the Store_String method.

The sample code mimics an almost identical code used to demonstrate the Store_WriteInfo method. It uses the Store_String to write a matrix of values and then uses this method to read them back in.


 <gmBasic> <!-- Unit test for Store_GetInfo -->
 <gmSL>
    int  levels[5];
    int  offsets[5];
    int  iOff;
    int  iLev;
    int  bootCell;

    Store_Create(NULL, StorageUnit.USER)
    Write_OpenFile(1, "Demo048.out", True, OutStyle.Text);
    iOff = 0;
    while(iOff < 5)
    {
       iLev = 0;
       while(iLev < 5)
       {
          levels(iLev) = (iOff+1) * 10 + iLev + 1;
          iLev = iLev + 1;
       }
       offsets(iOff) = Store_String(levels);
       iOff = iOff + 1;
    }
    iOff = 0;
    while(iOff < 5)
    {
       Store_GetInfo(offsets(iOff),levels);
       iLev = 0;
       while(iLev < 5)
       {
          Write_Integer(levels(iLev),5);
          iLev = iLev + 1;
       }
       Write_Record();
       iOff = iOff + 1;
    }
    Store_Close();
 </gmSL>
 </gmBasic>
The output is as expected.


    11   12   13   14   15
    21   22   23   24   25
    31   32   33   34   35
    41   42   43   44   45
    51   52   53   54   55

Store_GetLength Method

gmSL: int Store_GetLength(int offset)
gmVB: Function Store_GetLength(ByVal offset As Integer)
gmNI: int Store_GetLength(int offset);

The method Store_GetLength returns the overall length in words of the current storage area, if the integer parameter offset is zero. If offset is not zero, then the length in bytes of the information stored at the specified offset is returned. Regardless of the value of the parameter offset, if the current storage area is empty, then this method returns 0. Logic of the form If(Store_GetLength(0)) Then, is the standard way of checking if the storage area is empty.

Here is a simple example


 <gmBasic> <!-- Unit test for Store_GetLength -->
 <Storage Action="Open" Identifier="fmstock1.vbi" />
 <gmsl>
    int    subRoot;
    tVbSub subInfo;

    subRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.SetTxType", 0);
    subInfo = Store_GetVector(subRoot);
    Write_LogMessage("Length of storage area = " + Store_GetLength(0));
    Write_LogMessage("Bytes in compiled code = " + Store_GetLength(subInfo.cmpCodeStart));
    Write_LogMessage("Bytes in analysed code = " + Store_GetLength(subInfo.anaCodeStart));
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
that logs these messages


 Length of storage area = 372736
 Bytes in compiled code = 321
 Bytes in analysed code = 298

Store_GetName Method

gmSL: string Store_GetName(int root)
gmVB: Function Store_GetName(ByVal root As Integer) As String
gmNI: char* Store_GetName(int root,int* nIdent);

The method Store_GetName gets the target identifier of a component via its root address. Every component has two string representations -- its source "identifier" which the original identifier that was associated with the component when it was first placed in the symbol table by Store_Vector and its target "name" that was assigned to it directly via the Store_SetName method. If no name has been assigned to the component, then its name is the same as its identifier. Whenever a reference to a component is authored its name is used, but any retrieval of the component is always done via its identifier.

As the following code shows the source identifier of a component remains unchanged when it is renamed and is always the identifier used to retrieve it.


 <gmBasic> <!-- Unit test for Store_GetIdent and Store_GetName -->
 <Storage Action="Append" Identifier="fmstock1.vbi" />
 <gmsl>
    int varRoot;

    varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0);
    Write_LogMessage("The variable ident is " + Store_GetIdent(varRoot));
    Write_LogMessage("The variable name is " + Store_GetName(varRoot));
 </gmsl>
 <Refactor>
    <Rename Identifier="FMStocks_DB.TxNew.m_dbh" Content="m_databaseHandle" />
 </Refactor>
 <gmsl>
    int varRoot;

    varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0);
    Write_LogMessage("The variable ident is " + Store_GetIdent(varRoot));
    Write_LogMessage("The variable name is " + Store_GetName(varRoot));
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
The output shows the identifier unchanged. The gmPL Rename statement simply does a call to the Store_SetName method. Before its application, the name is simply the identifier, and after it is the new set value.


 The variable ident is m_dbh
 The variable name is m_dbh
 The variable ident is m_dbh
 The variable name is m_databaseHandle

Store_GetNext Method

gmSL: int Store_GetNext(int iChild)
gmVB: Function Store_GetNext(ByVal iChild As Integer) As Integer
gmNI: int Store_GetNext(int iChild);

The method Store_GetNext returns the next child of a parent component or of the root branch. This is the next component entered for the parent. In general, using this method in conjunction with the Store_GetFirst method, will generate the sequence of components stored within some branch of the symbol table in their defined order. The integer parameter iChild contains the root offset of the current child component as returned by Store_GetFirst or a previous call to Store_GetNext.

If there is a next child of the parent of the specified component then this method returns its root offset. If there is no next child then this method returns a zero.

The following code shows how to find all of the children of a subprogram


 <gmBasic> <!-- Unit test for Store_GetNext -->
 <gmSL>
    int    subRoot;
    int    iChild;

    Store_Open("fmstock1.vbi", StorageUnit.USER, False)
    Write_OpenFile(1, "Demo033.out", True, OutStyle.Text);
    subRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.SetTxType", 0);
    Write_Line("The following are the children of " + Symbol_FullName(subRoot,-1));
    iChild = Store_GetFirst(subRoot);
    while(iChild)
    {
       Symbol_DisplayInfo(iChild, DisplayType.OBJECT_TYPE);
       Write_Line(" " + Symbol_FullName(iChild, -1));
       iChild = Store_GetNext(iChild);
    }
    Store_Close()
 </gmSL>
 </gmBasic>
The output shows that the subprogram contains not just variables but also a statement label.


 The following are the children of FMStocks_DB.TxNew.SetTxType
 Variable FMStocks_DB.TxNew.SetTxType.TxID
 Variable FMStocks_DB.TxNew.SetTxType.TxTypeId
 StatementLabel FMStocks_DB.TxNew.SetTxType.errorHandler

Store_GetObjectType Method

gmSL: int Store_GetObjectType(int root)
gmVB: Function Store_GetObjectType(ByVal root As Integer) As Integer
gmNI: int Store_GetObjectType(int root);

The method Store_GetObjectType returns the symbol table object type code of a component. Whenever a component is entered into the symbol table, it is assigned an integer object type code. Each object type code has associated with it a fixed-length information vector used to describe components of the specified type. In the case of gmBasic the type code is either an entry number from the ObjectType enumeration in the language file, the opcode value of a built-in class if the component is a built-in control, or the root offset of an external class if the object is a control of that external class.

The integer parameter root contains the root offset of the component in storage. If root has a value less than or equal to zero, then this method displays the following message and returns a zero.


   SYSERR#5001: Object Type requested for invalid root %root%
If root has a value that exceeds the maximum possible offset, this method displays the following message and returns a zero.


   SYSERR#5002: Object Type requested for invalid root %root%
If root is not obviously malformed this method returns the object type code in the symbol table entry at the specified root offset. If there is any question about the type of a component being accessed or about the validity of the root offset value, use the Store_GetObjectType method first to check it before calling any of the other Store service class methods that take root offsets in their parameter lists.

An important point to remember about the implementation of the gmSL is that it is bootstrapped on the existing capabilities of gmBasic. It uses the gmBasic symbol table, compiler, storage, and runtime engine. The following code audits the symbols within its own implementation.


 <gmBasic> <!-- Unit test for Store_GetObjectType -->
 <gmSL>
    int       iRoot;
    int       levels[10];
    int       objType;

    Write_OpenFile(1, "Demo039.out", True, OutStyle.Text);
    Write_Line("Listing of Components in this gmSL command specification");
    Write_ChangeMargin(1);
    Store_Select(StorageUnit.TEMP);
    iRoot = Store_FindFirstChild(levels,0);
    while(iRoot != 0)
    {
       objType = Store_GetObjectType(iRoot);
       Symbol_DisplayInfo(iRoot,DisplayType.OBJECT_TYPE);
       Write_Line(" " + Symbol_FullName(iRoot,-1) + " has object type code " + objType);
       iRoot = Store_FindNextChild(levels);
    }
 </gmSL>
 </gmBasic>
The StorageUnit.TEMP area is used for gmSL command storage. To use the facilities of gmBasic to process this, a project file temp.slp, with name none, and class file temp.cls, with name temp, and subprogram Inline are all created to contain the actual command code.


 Listing of Components in this gmSL command specification
    ProjectFile temp.slp has object type code 2
    ClassFile temp.cls has object type code 5
    Subprogram none.temp.Inline has object type code 7
    Variable none.temp.Inline.iRoot has object type code 8
    Variable none.temp.Inline.levels has object type code 8
    Variable none.temp.Inline.objType has object type code 8
    Vb_Name none.temp has object type code 17
    Vb_Name none has object type code 17
    Vb_Name temp.exe has object type code 17
Since the temporary storage area is created for each use and destroyed after its use, there are no name-clash issues with the simple structure created to surround the code.

Store_GetParent Method

gmSL: int Store_GetParent(int root)
gmVB: Function Store_GetParent(ByVal root As Integer) As Integer
gmNI: int Store_GetParent(int root);

The method Store_GetParent returns the root offset of the parent of a component whose root offset is specified in the parameter. If the component is in the root branch of the symbol table, then a zero is returned.

The following code shows how the parent property moves up the symbol table


 <gmBasic> <!-- Unit test for Store_GetParent -->
 <gmSL>
    int    subRoot;
    int    parent;

    Store_Open("fmstock1.vbi", StorageUnit.USER, False)
    Write_OpenFile(1, "Demo034.out", True, OutStyle.Text);
    subRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.SetTxType", 0);
    Write_Line("The following are the Parents of " + Symbol_FullName(subRoot,-1));
    Write_ChangeMargin(1);
    parent = Store_GetParent(subRoot);
    while(parent)
    {
       Symbol_DisplayInfo(parent, DisplayType.OBJECT_TYPE);
       Write_Line(" " + Symbol_FullName(parent, -1));
       parent = Store_GetParent(parent);
    }
    Store_Close()
 </gmSL>
 </gmBasic>
The output shows both the fully qualified identifier of the parent and its component object type.


 The following are the Parents of FMStocks_DB.TxNew.SetTxType
    ClassFile TxNew.cls
    ProjectFile FMStocks_DB.vbp

Store_GetString Method

gmSL: string Store_GetString(int offset)
gmVB: Function Store_GetString(ByVal offset As Integer) As String
gmNI: char* Store_GetString(int offset,int* nString);

The method StoreGetString returns a string from the current storage area via its offset specified in the parameter. This offset must have been obtained via an earlier call to the method Store_String().

The sample code shows how three store strings can be retrieved via this method.


 <gmBasic> <!-- Unit test for Store_String, Store_GetString, Store_SetString -->
 <gmSL>
    int offset1;
    int offset2;
    int offset3;

    Store_Create(NULL, StorageUnit.USER);
    Write_OpenFile(1, "Demo035.out", True, OutStyle.Text);
    offset1 = Store_String("Hello World");
    offset2 = Store_String("This is Fred");
    offset3 = Store_String("GoodBye World");
    Write_Line("Content of strings before change");
    Write_ChangeMargin(1);
    Write_Line("String1 is: " + Store_GetString(offset1));
    Write_Line("String2 is: " + Store_GetString(offset2));
    Write_Line("String3 is: " + Store_GetString(offset3));
    Write_ChangeMargin(-1);
    Store_SetString(offset2, "This is Mark");
    Write_Line("Content of strings after change");
    Write_ChangeMargin(1);
    Write_Line("String1 is: " + Store_GetString(offset1));
    Write_Line("String2 is: " + Store_GetString(offset2));
    Write_Line("String3 is: " + Store_GetString(offset3));
    Store_Close();
 </gmSL>
 </gmBasic>
The output shows the strings as entered and then later as changed.


 Content of strings before change
    String1 is: Hello World
    String2 is: This is Fred
    String3 is: GoodBye World
 Content of strings after change
    String1 is: Hello World
    String2 is: This is Mark
    String3 is: GoodBye World

Store_GetSymbols Method

gmSL: int Store_GetSymbols(string identifier)
gmVB: Function Store_GetSymbols(ByVal identifier As String) As Integer
gmNI: int Store_GetSymbols(char* identifier);

The method Store_GetSymbols returns the root offset of a sequence that contains the root offsets of all symbols whose identifier matches the string parameter identifier. The gmBasic symbol table has a tree structure. The symbols within the tree need be unique along one branch only. As a result of this, there can be many duplicates in the symbol table. The roles of the symbol retrieval methods are to search the symbol tree for the correct occurrence of an identifier given a current parent context -- i.e., the typical question asked is "Does a given parent node have a child with the specified identifier". This question must be asked for every parent in the current scope in scope order. This turns out to be a very difficult problem. The simplest approach is to go through each branch on the symbol table in current scope order and see if the identifier can be found on that branch. But there are many branches, especially if a global symbol is being sought, so this process can require many individual searches. To make matters even worse the required searches are string searches which require time consuming string comparisons as opposed to simple integer comparisons. The approach used by gmBasic separates the search into two parts. First there is a base index in the symbol table that contains one entry for each unique symbol defined anywhere in the current code set. This is the only index that uses string keys. Associated with this entry for each symbol there is a secondary index that uses integer parent node roots as the key and the root of the actual information vector for the identifier in its form within that parent.

The method Store_GetSymbols searches the base index for a specified identifier. If found, it returns the root offset of a sequence maintained there that contains the root offsets of the symbols with that name stored in the order that they were defined. If no symbol with the specified name is present, a zero is returned.

The following code lists all symbols with the identifier "name" in one of the FmStocks codes.


 <gmBasic> <!-- Unit test for Store_GetSymbols -->
 <Storage Action="Open" Identifier="fmstock1.vbi" />
 <Output Status="New" Filename="demo040.out" />
 <gmsl>
    int      symbolRoot;
    Handle   symbols;
    int      nChild;
    int      iChild;
    int      iRoot;

    symbolRoot = Store_GetSymbols("Name");
    symbols = Sequence_Open(symbolRoot);
    nChild = Sequence_GetLength(symbols);
    Write_Line("Listing of " + nChild + " symbols with the identifier Name");
    Write_ChangeMargin(1);
    iChild = 1;
    while(iChild <= nChild)
    {
       iRoot = Sequence_Access(symbols, iChild);
       Write_Integer(iChild, 3);
       Write_Character(" ");
       Symbol_DisplayInfo(iRoot, DisplayType.OBJECT_TYPE);
       Write_Line(" " + Symbol_FullName(iRoot ,-1));
       iChild = iChild + 1;
    }
    Sequence_Close(symbols);
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
The output is as follows.


 Listing of 20 symbols with the identifier Name
      1 Lib_Property stdole.IFont.Name
      2 Lib_Property stdole.Font.Name
      3 Lib_Property ADODB.Property.Name
      4 Lib_Property ADODB.Command15.Name
      5 Lib_Argument ADODB.Command15.CreateParameter.Name
      6 Lib_Argument ADODB.Fields20._Append.Name
      7 Lib_Argument ADODB.Fields.Append.Name
      8 Lib_Property ADODB.Field20.Name
      9 Lib_Property ADODB._Parameter.Name
     10 Lib_Property ADODB.Field15.Name
     11 Lib_Argument COMSVCSLib.ISecurityCertificateColl.Item.name
     12 Lib_Argument COMSVCSLib.ISecurityIdentityColl.Item.name
     13 Lib_Argument COMSVCSLib.ISecurityCallContext.Item.name
     14 Lib_Argument COMSVCSLib.ObjectContext.Item.name
     15 Lib_Argument COMSVCSLib.ISharedPropertyGroup.CreateProperty.name
     16 Lib_Argument COMSVCSLib.ISharedPropertyGroup.Property.name
     17 Lib_Argument COMSVCSLib.ISharedPropertyGroupManager.CreatePropertyGroup.name
     18 Lib_Argument COMSVCSLib.ISharedPropertyGroupManager.Group.name
     19 Lib_Argument MigrationSupport.Utils.FontChangeName.Name
     20 Lib_Property MigrationSupport.Line.Name
Notice that Visual Basic is not case sensitive; therefore, both upper and lower case versions of Name end up being posted to the same entry in the base table. The case distinction is retained in the other areas of the symbol tables, unless changed by Store_SetName.

Store_GetVector Method

gmSL: Handle Store_GetVector(int root)
gmVB: Function Store_GetVector(ByVal root As Integer) As Handle
gmNI: void* Store_GetVector(int root);

The Store_GetVector method gets a handle to a component information vector in the current storage area. In particular, this method gives read only access to the information vector associated with the component whose root offset is specified via the integer root parameter. If there is any question about the type of component being accessed or about the validity of the root offset value, use the Store_GetObjectType method first (see the discussion there). Any changes made to the information will generally not be saved. Use the Store_DeltaVector method if the vector is to be changed. The actual handle is optimized to make the retrieval of the component information as efficient as possible. As such it should only be used locally and should be refreshed after any other storage intensive operations have been performed.

The method returns a semi-permanent handle to the information vector. It is up to the user to ensure that the storage object type of the component is compatible with the structure type of the variable that is receiving the handle.

The following command code


 <gmBasic>
 <gmSL>
    int       varRoot;
    tVariable varInfo;

    Store_Open("fmstock1.vbi", StorageUnit.USER, False)
    varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0)
    if(varRoot)
    {
       if(Store_GetObjectType(varRoot) == ObjectType.VARIABLE)
       {
          varInfo = Store_GetVector(varRoot);
          if(varInfo.Referenced)
          {
             Write_LogMessage("The variable " + Store_GetName(varRoot) + " has " +
                               varInfo.nReference + " references")
          }
       }
    }
    Store_Close()
 </gmSL>
 </gmBasic>
opens one of the FmStocks existing storage areas and retrieves the root offset of a known variable. It checks that the variable was found and that it is indeed a VARIABLE. It then retrieves its information vector, and if it was referenced at all, it displays the number of references. The message


 The variable m_dbh has 3 references
is logged which is the correct value. Note that other than the gmSL statement itself, gmPL script files can be written entirely using the gmSL notation.

Store_NextReference Method

gmSL: tReference Store_NextReference(int[] PostSet)
gmVB: Function Store_NextReference(ByVal PostSet As Integer[]) As tReference
gmNI: int* Store_NextReference(tPosting* PostSet);

The method Store_NextReference iterates forward in the references record set once a call to Store_FirstReference has established a starting record. During compilation symbol references are stored in a reference record set sorted in ascending order by the root of the symbol being referenced. The reference record, type tReference, has the following fields:

Field Description of content
MakesRef The root of the component containing the reference
BeingRefd The root of the component being referenced
RecNumber The text record number in the host file of the reference
CodeOffset The offset in the compiled code of the referencing code
HostFile The root of the information file containing the subprogram
Terminal If the reference treated the component as terminal then one, else zero

The integer vector PostSet is a private storage vector used by the retrieval methods. It must contain at least four entries and be initialized via a call to Store_FirstReference.

The Store_FirstReference method introduced code to find first references the ByRef arguments. The code here expands the inner loop of that code to use Store_NextReference to count the references.


 refInfo = Store_FirstReference(iRoot,postSet);
 if(refInfo.BeingRefd == iRoot)
 {
   count = 0;
   while(refInfo.BeingRefd == iRoot)
   {
     count = count + 1;
     refInfo = Store_NextReference(postSet);
   }
   Write_Line("The ByRef argument " + Symbol_FullName(iRoot,-1) + " is Used " + count + " Times")
 }
Once the code has established that a first reference record exists for the symbol, it simply moves forward in the sorted record set counting the number of record that have the desired symbol root. In this case, the output is


 The ByRef argument FMStocks_DB.Helpers.ReportEvent.plpStrings is Not Used
 The ByRef argument FMStocks_DB.Helpers.ReportEvent.lpRawData is Used 1 Times
 The ByRef argument FMStocks_DB.Helpers.CopyMemory.hpvDest is Used 1 Times
 The ByRef argument FMStocks_DB.Helpers.CopyMemory.hpvSource is Used 1 Times
 The ByRef argument FMStocks_DB.Helpers.GetComputerNameAPI.nSize is Not Used
 The ByRef argument FMStocks_DB.Account.VerifyUser.AccountID is Used 1 Times
 The ByRef argument FMStocks_DB.Account.VerifyUser.FullName is Used 1 Times
 The ByRef argument FMStocks_DB.Account.GetAccountInfo.FullName is Used 1 Times
 The ByRef argument FMStocks_DB.Account.GetAccountInfo.Email is Used 1 Times
 The ByRef argument FMStocks_DB.Account.GetAccountInfo.Balance is Used 1 Times
 The ByRef argument FMStocks_DB.DBHelper.collectParams.cmd is Used 2 Times

Store_Open Method

gmSL: int Store_Open(string fileName, int iUnit, bool append)
gmVB: Function Store_Open(ByVal fileName As String, ByVal iUnit As Integer, ByVal append As Boolean) As Integer
gmNI: int Store_Open(char* fileName, int iUnit, int append);

The StoreOpen opens an exiting file into a storage area. In particular it makes the indicated storage area the current one and initializes it with the contents of an existing file. Any information currently in the storage area will be lost. Until another storage area is selected via one of the three methods Store_Select, Store_Create, or Store_Open all storage requests will be directed to this currently selected storage area.

The string fileName parameter contains the name of the file (optionally, without an extension) to be opened for the storage area.

The integer iUnit parameter is the number of the storage unit to be used. This must be a value between 1 and 16. Note that gmBasic uses units one through 5 internally. There is an enumeration StorageUnit that defines these units. When the intent is to refer to one of these gmBasi units then an entry from this enumeration should be used. They are as follows:

Entry Description of content
USER This is the storage area that is managed via the gmPL Storage statement.
LANG This is the storage area used to contain the language file controlling the translation.
TEMP This is a temporary storage area used within specific tasks. It is never left open, between tasks.
INFO This is a temporary storage area used by the gmPL utility statements like IncludeOrder to contain information being gathered,
CLIENT This is the storage area used to contain the GlobalSettings information.

For most applications the USER storage area should be used, as many of the authoring service methods, in particular, assume that they are using this area.

The Boolean append, if True, then the file is opened with read/write access permission. If False, then the file is opened for read only access.

If all goes well a one (true) is returned. If there is a problem opening the storage area -- usually because the file does not exist -- a zero (false) is returned.

For example the following command code can be used


 <gmsl>Store_Open("fmstock1.vbi", StorageUnit.USER, False)</gmsl>
as an alternative to this gmPL Storage statement.


 <Storage Action="Open" Identifier="fmstock1.vbi" />

Store_ReadInfo Method

gmSL: int Store_ReadInfo(Handle info, int offset)
gmVB: Function Store_ReadInfo(ByVal info As Handle, ByVal offset As Integer) As Integer
gmNI: int Store_ReadInfo(void* info, int offset);

The method Store_ReadInfo reads long blocks of information that were initially written using the method Store_WriteInfo. A physical read is required, since these information vectors may cross block boundaries. The most common use of this method is to read the intermediate code associated with a component, though it may be used for any type of information.

The handle parameter info is a handle to the area in memory to receive the information read, and the integer parameter offset is the offset of the start of the information to be read. This offset must be a value previously returned by the Store_WriteInfo method.

The following code reads the analysed intermediate code associated with a method in the FmStocks application and dumps its content.


 <gmBasic> <!-- Unit test for Store_ReadInfo -->
 <Storage Action="Open" Identifier="fmstock1.vbi" />
 <Output Status="New" Filename="demo041.out" />
 <gmsl>
    int      subRoot;
    tVbSub   subInfo;
    Handle   codptr;
    int      nCode;

    subRoot = Symbol_FindIdentifier("FMStocks_DB.Version.Version", 0);
    subInfo = Store_GetVector(subRoot);
    codptr = Opcode_GetCode();
    nCode = Store_ReadInfo(codptr,subInfo.anaCodeStart);
    Write_Line("The method FMStocks_DB.Version.Version has " + nCode + " Bytes of analysed code as follows:");
    Opcode_SetLength(nCode);
    Opcode_DumpCode(0,nCode);
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
The result is a standard code dump listing.


 The method FMStocks_DB.Version.Version has 21 Bytes of analysed code as follows:
 Offset |  Sl.Start |  Ql.Start | Quantity type        | Opcode | Operation support information
 ------ |  -------- |  -------- | -------------        | ------ | -----------------------------
      0 |           |           |                      | NEW    | 30
      3 |           |           |                      | LEV    | Nest0
      5 |       1.5 |       1.5 | String               | LDA    | Subprogram:GetVersionNumber:252074
     10 |       1.5 |       1.5 | String               | CUF    | Args0
     12 |       1.5 |       1.5 | String               | REF    | Subprogram:GetVersionNumber:252074
     17 |       1.5 |           |                      | ARG    | String
     19 |           |           |                      | EXI    | Function

Store_Reference Method

gmSL: void Store_Reference(tReference reference)
gmVB: Sub Store_Reference(ByVal reference As tReference)
gmNI: void Store_Reference(int* reference);

The method Store_Reference stores a reference record in the current storage area. The storage area maintains a sorted list of reference records sorted by the offset of the symbol being referenced. During the compilation process symbols are encountered by a referencing component. This method is used to order them by the component being referenced so that those references can be used by the analyser. This method stores an individual reference record stored in the parameter reference, which has the following content:

Field Description of content
MakesRef The root of the component containing the reference
BeingRefd The root of the component being referenced
RecNumber The text record number in the host file of the reference
CodeOffset The offset in the compiled code of the referencing code
HostFile The root of the information file containing the subprogram
Terminal If the reference treated the component as terminal then one, else zero

The code below shows the actual code used by gmBasic to locate and then write reference records. It is contained in the Utility class of the language file.


 void Vb6StoreReferences(int iStart,int nCode,int subRoot,int hostFile)
 {
    tReference  reference
    int         icode;
    int         nRef;
    int         opc;
    int         subc;
    tOpcInfo    opcInfo;
    Handle      codptr;
    int         opcType;
    int         icd;

    reference.MakesRef = subRoot;
    reference.BeingRefd = 0;
    reference.RecNumber = 0;
    reference.CodeOffset = 0;
    reference.HostFile = hostFile;
    reference.Terminal = 0;

    nRef = 0;
    icode = iStart;
    codptr = Opcode_GetCode();
    while(icode >= 0)
    {
       opc = Opcode_GetOperation(codptr,icode,subc);
       opcInfo = Opcode_GetInfo(opc);
       opcType = opcInfo.Type;
       if(opc != Opcode.DPS && opc != Opcode.BIF && opc != Opcode.REF && subc > 0)
       {
          if(opc == Opcode.NEW)
          {
             reference.RecNumber = subc;
          }
          else if(opcInfo.type == opcTypes.SymbolAddr || opcType == opcTypes.LibraryAdr || opcType == opcTypes.LibPattern)
          {
             reference.BeingRefd = subc;
             reference.CodeOffset = icode - iStart;
             reference.Terminal = 0;
             icd = Opcode_GetNext(codptr,icode,nCode);
             if(icd > 0)
             {
                opc = Opcode_GetOperation(codptr,icd,subc);
                if(opc == Opcode.MEM) icd = Opcode_GetNext(codptr,icd,nCode);
                if(icd > 0)
                {
                   opc = Opcode_GetOperation(codptr,icd,subc);
                   opcInfo = Opcode_GetInfo(opc);
                   opcType = opcInfo.Type;
                   if(opcType != opcTypes.SymbolAddr && opcType != opcTypes.LibraryAdr &&
                      opcType != opcTypes.LibPattern) reference.Terminal = 1;
                }
             }
             Store_Reference(reference);
             nRef = nRef + 1;
          }
       }
       icode = Opcode_GetNext(codptr,icode,nCode);
    }
 }

Store_RewriteInfo Method

gmSL: void Store_RewriteInfo(Handle info, int nByte, int offset)
gmVB: Sub Store_RewriteInfo(ByVal info As Handle, ByVal nByte As Integer, ByVal offset As Integer)
gmNI: void Store_RewriteInfo(void* info, int nByte, int offset);

The method Store_RewriteInfo rewrites long information vectors that were initially written using the method StoreWriteInfo. The number of bytes in the new vector may not exceed the original length of this information. If they do then the following system error warning is displayed.


 SYSERROR#50003: Rewriting "current" bytes over "previous" bytes.
The handle parameter info is a handle to the area in memory that contains the information to be written, the integer parameter nByte is the number of bytes to be written, and the integer parameter offset is the offset of the start of the information to be rewritten. This offset must be a value previously returned by the Store_WriteInfo method.

The following code continues the example from the Store_WriteInfo method. It reads the matrix written back in, displays it, multiplies it by 2, and then rewrites it.


 <gmBasic> <!-- Unit test for Store_RewriteInfo -->
 <gmSL>
    int  levels[5];
    int  offsets[5];
    int  iOff;
    int  iLev;
    int  iRoot;
    int  bootCell;

    Store_Open("Demo043.vbi", StorageUnit.USER, True)
    Write_OpenFile(1, "Demo044.out", True, OutStyle.Text);
    iRoot = Store_FindVector("offsets",0);
    bootCell = Store_GetObjectType(iRoot);
    Store_ReadInfo(offsets,bootCell);
    iOff = 0;
    while(iOff < 5)
    {
       Store_ReadInfo(levels, offsets(iOff));
       iLev = 0;
       while(iLev < 5)
       {
          Write_Integer(levels(iLev),5);
          levels(iLev) = levels(iLev) * 2;
          iLev = iLev + 1;
       }
       Write_Record();
       Store_RewriteInfo(levels,20,offsets(iOff));
       iOff = iOff + 1;
    }
    Store_Close();
 </gmSL>
 </gmBasic>
Upon the initial call the values read match those originally written.


    11   12   13   14   15
    21   22   23   24   25
    31   32   33   34   35
    41   42   43   44   45
    51   52   53   54   55
The second call shows then multiplied by two and so on.


    22   24   26   28   30
    42   44   46   48   50
    62   64   66   68   70
    82   84   86   88   90
   102  104  106  108  110

Store_Select Method

gmSL: void Store_Select(int iUnit)
gmVB: Sub Store_Select(ByVal iUnit As Integer)
gmNI: void Store_Select(int iUnit);

The method Store_Select selects one the available storage areas as the current storage area. All successive storage area accesses will be performed to this area until another call to this method is made. Note that this service only selects the area, it does not change it in any way. Note also that when made via gmSL only the storage area used within subsequent gmSL code is effected. The storage area being used by mainline gmBasic is not effected.

The integer parameter iUnit is the number of the storage unit to be used. This must be a value between 1 and 16. Note that gmBasic uses units one through five internally. There is an enumeration StorageUnit that defines these units. When the intent is to refer to one of these gmBasic units then an entry from this enumeration should be used. They are as follows:

Entry Description of content
USER This is the storage area that is managed via the gmPL Storage statement.
LANG This is the storage area used to contain the language file controlling the translation.
TEMP This is a temporary storage area used within specific tasks. It is never left open, between tasks. It is also the unit used to store command mode gmSL code.
INFO This is a temporary storage area used by the gmPL utility statements like IncludeOrder to contain information being gathered,
CLIENT This is the storage area used to contain the GlobalSettings information.

The following code moves through the various storage areas and displays their lengths.


 <gmBasic> <!-- Unit test for Store_Select -->
 <Storage Action="Open" Identifier="fmstock1.vbi" />
 <Select GlobalSettings="GmslCode.vbi" />
 <LoadEnvironment />
 <Output Status="New" Filename="demo038.out" />
 <gmsl>
    Store_Select(StorageUnit.USER);
    Write_Line("Length of user storage area(" + Store_Unit() + ") = " + Store_GetLength(0));
    Store_Select(StorageUnit.LANG);
    Write_Line("Length of language storage area(" + Store_Unit() + ") = " + Store_GetLength(0));
    Store_Select(StorageUnit.TEMP);
    Write_Line("Length of temporary storage area(" + Store_Unit() + ") = " + Store_GetLength(0));
    Store_Select(StorageUnit.INFO);
    Write_Line("Length of Information storage area(" + Store_Unit() + ") = " + Store_GetLength(0));
    Store_Select(StorageUnit.CLIENT);
    Write_Line("Length of client storage area(" + Store_Unit() + ") = " + Store_GetLength(0));
    Store_Select(6);
    Write_Line("Length of user(6) storage area(" + Store_Unit() + ") = " + Store_GetLength(0));
    Store_Select(15);
    Write_Line("Length of user(15) storage area(" + Store_Unit() + ") = " + Store_GetLength(0));
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
The output shows four units in use.


 Length of user storage area(1) = 372736
 Length of language storage area(2) = 509123
 Length of temporary storage area(3) = 20577
 Length of Information storage area(4) = 0
 Length of client storage area(5) = 37016
 Length of user(6) storage area(6) = 0
 Length of user(15) storage area(15) = 0
The user and language storage areas are always in use. Among other things, the temporary storage is used to store gmSL command mode code; therefore, it is in use. The client storage area is used for global settings information, which was loaded via the gmPL LoadEnviroment statement.

Store_SetName Method

gmSL: void Store_SetName(int root, string* Name)
gmVB: Sub Store_SetName(ByVal root As Integer, ByVal Name As String)
gmNI: void Store_SetName(int root, char* Name, int nName);

The method Store_SetName sets the target name of a component to the string parameter Name. The component is specified via its root offset. Whenever a component is stored in the symbol table using the Store_Vector method that component is given an identifier by the caller. This identifier can then used to retrieve the component, and by default is used by the author whenever references to the component are being written in the target code. If references are to be authored with a alternate identifier (referred to as the name of the component) then this method is used to associate that name with the component.

The following shows the distinction between the identifier of the component and its name.


 <gmBasic> <!-- Unit test for Store_SetName -->
 <Storage Action="Append" Identifier="fmstock1.vbi" />
 <Output Status="New" Filename="demo036.out" />
 <gmsl>
    int varRoot;

    varRoot = Symbol_FindIdentifier("FMStocks_DB.TxNew.m_dbh", 0);
    Write_Line("Ident and Name before Store_SetName");
    Write_ChangeMargin(1);
    Write_Line("The variable ident is " + Store_GetIdent(varRoot));
    Write_Line("The variable name is " + Store_GetName(varRoot));
    Write_ChangeMargin(-1);
    Store_SetName(varRoot, "m_databaseHandle");
    Write_Line("Ident and Name after Store_SetName");
    Write_ChangeMargin(1);
    Write_Line("The variable ident is " + Store_GetIdent(varRoot));
    Write_Line("The variable name is " + Store_GetName(varRoot));
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
The Store_SetName method is in fact used by the gmPL Rename statement. The one here is equivalent.


 <Refactor>
    <Rename Identifier="FMStocks_DB.TxNew.m_dbh" Content="m_databaseHandle" />
 </Refactor>
The output shows how the name changes, but not the identifier.


 Ident and Name before Store_SetName
    The variable ident is m_dbh
    The variable name is m_dbh
 Ident and Name after Store_SetName
    The variable ident is m_dbh
    The variable name is m_databaseHandle

Store_SetObjectType

gmSL:
gmVB:
gmNI:

Store_SetString Method

gmSL: void Store_SetString(int offset, string strValue)
gmVB: Sub Store_SetString(ByVal offset As Integer, ByVal strValue As String)
gmNI: void Store_SetString(int offset, char* strValue, int length)

The method Store_SetString changes the value of an existing string whose length has not increased.

The following code shows this in operation.


 <gmBasic> <!-- Unit test for Store_String, Store_GetString, Store_SetString -->
 <gmSL>
    int offset1;
    int offset2;
    int offset3;

    Store_Create(NULL, StorageUnit.USER);
    Write_OpenFile(1, "Demo035.out", True, OutStyle.Text);
    offset1 = Store_String("Hello World");
    offset2 = Store_String("This is Fred");
    offset3 = Store_String("GoodBye World");
    Write_Line("Content of strings before change");
    Write_ChangeMargin(1);
    Write_Line("String1 is: " + Store_GetString(offset1));
    Write_Line("String2 is: " + Store_GetString(offset2));
    Write_Line("String3 is: " + Store_GetString(offset3));
    Write_ChangeMargin(-1);
    Store_SetString(offset2, "This is Mark");
    Write_Line("Content of strings after change");
    Write_ChangeMargin(1);
    Write_Line("String1 is: " + Store_GetString(offset1));
    Write_Line("String2 is: " + Store_GetString(offset2));
    Write_Line("String3 is: " + Store_GetString(offset3));
    Store_Close();
 </gmSL>
 </gmBasic>
Again the offset of the string remains unchanged, while its value is updated.


 Content of strings before change
    String1 is: Hello World
    String2 is: This is Fred
    String3 is: GoodBye World
 Content of strings after change
    String1 is: Hello World
    String2 is: This is Mark
    String3 is: GoodBye World

Store_String Method

gmSL: int Store_String(string strValue)
gmVB: Function Store_String(ByVal strValue As String) As Integer
gmNI: int Store_String(char* strValue, int nString);

The method StoreString stores a character string in the current storage area and returns the offset in the storage area of the string. This offset can be used later to retrieve the string.

The following code shows this process.


 <gmBasic> <!-- Unit test for Store_String, Store_GetString, Store_SetString -->
 <gmSL>
    int offset1;
    int offset2;
    int offset3;

    Store_Create(NULL, StorageUnit.USER);
    Write_OpenFile(1, "Demo035.out", True, OutStyle.Text);
    offset1 = Store_String("Hello World");
    offset2 = Store_String("This is Fred");
    offset3 = Store_String("GoodBye World");
    Write_Line("Content of strings before change");
    Write_ChangeMargin(1);
    Write_Line("String1 is: " + Store_GetString(offset1));
    Write_Line("String2 is: " + Store_GetString(offset2));
    Write_Line("String3 is: " + Store_GetString(offset3));
    Write_ChangeMargin(-1);
    Store_SetString(offset2, "This is Mark");
    Write_Line("Content of strings after change");
    Write_ChangeMargin(1);
    Write_Line("String1 is: " + Store_GetString(offset1));
    Write_Line("String2 is: " + Store_GetString(offset2));
    Write_Line("String3 is: " + Store_GetString(offset3));
    Store_Close();
 </gmSL>
 </gmBasic>
The output shows the retrieval.


 Content of strings before change
    String1 is: Hello World
    String2 is: This is Fred
    String3 is: GoodBye World
 Content of strings after change
    String1 is: Hello World
    String2 is: This is Mark
    String3 is: GoodBye World

Store_Unit Method

gmSL: int Store_Unit()
gmVB: Function Store_Unit() As Integer
gmNI: int Store_Unit(void);

The method Store_Unit returns the unit number of the current storage area. This value is often gotten before moving to another storage area. It can then be used to return to this current area.

The code below shows the number and length of the currently open user storage area


 <gmBasic> <!-- Unit test for Store_GetUnit -->
 <Storage Action="Open" Identifier="fmstock1.vbi" />
 <Output Status="New" Filename="demo037.out" />
 <gmsl>
    Write_Line("Length of storage area(" + Store_Unit() + ") = " + Store_GetLength(0));
 </gmsl>
 <Storage Action="Close" />
 </gmBasic>
which is


 Length of storage area(1) = 372736

Store_Vector Method

gmSL: int Store_Vector(string identifier, int parent, int length, int type)
gmVB: Function Store_Vector(ByVal identifier As String, ByVal parent As Integer, ByVal length As Integer, ByVal type As Integer) As Integer
gmNI: int Store_Vector(char* identifier, int parent, int length, int type);

The StoreVector method stores a component information vector associated with an identifier as a child of a specified parent. It is the primary method used to enter components into the hierarchical symbol table. To enter a symbol, four things must be known. First, it must have a unique identifier relative to the parent of the symbol; as specified by the string parameter identifier. Second, it must have a parent which can either be another symbol already entered into storage or can be the root branch of the symbol tree; as specified by the integer parameter parent which is either the root offset of the parent or zero if being entered into the root branch of the tree. Third, it must have a information storage vector length, which may be zero; as specified by the integer parameter length. If non-zero, a zero-filled area of storage is allocated to the component with the specified length. Fourth. it must have an object type code, which specifies the type of component being entered; as specified by the integer parameter type.

If there already is a component with the same identifier stored in the specified branch of the symbol table, then a zero is returned; else the nonzero root offset of the component is returned. It is this root offset which must be used to manipulate or retrieve any information for the component.

The actual values of the parameter type are a gmBasic convention. The type code is either an entry number from the ObjectType enumeration in the language file, the opcode value of a built-in class if the component is a built-in control, or the root offset of an external class if the object is a control of that external class. Other than storing it and returning its value via the Store_GetObjectType method this code has no significance within the Store service class.

To emphasize the above point, the example below uses a code also used by the Store_WriteInfo method.


 <gmBasic> <!-- Unit test for Store_WriteInfo and Store_Vector -->
 <gmSL>
    int  levels[5];
    int  offsets[5];
    int  iOff;
    int  iLev;
    int  bootCell;

    Store_Create("Demo043", StorageUnit.USER)
    Write_OpenFile(1, "Demo043.out", True, OutStyle.Text);
    iOff = 0;
    while(iOff < 5)
    {
       iLev = 0;
       while(iLev < 5)
       {
          levels(iLev) = (iOff+1) * 10 + iLev + 1;
          iLev = iLev + 1;
       }
       offsets(iOff) = Store_WriteInfo(levels,20);
       iOff = iOff + 1;
    }
    iOff = 0;
    bootCell = Store_WriteInfo(offsets,20);
    Store_Vector("Offsets",0,0,bootCell);
    while(iOff < 5)
    {
       Store_ReadInfo(levels, offsets(iOff));
       iLev = 0;
       while(iLev < 5)
       {
          Write_Integer(levels(iLev),5);
          iLev = iLev + 1;
       }
       Write_Record();
       iOff = iOff + 1;
    }
    Store_Close();
 </gmSL>
 </gmBasic>
This code creates a matrix of values whose ultimate retrieval offset ends up in a local variable bootCell. Other applications, like the one described under Store_RewriteInfo need this offset to read the matrix back into memory. The Store_Vector method is ideal. It simply adds an offset component to the root of the symbol tree and sets its type value to bootCell. Users of this matrix can get this value by simply saying


    iRoot = Store_FindVector("offsets",0);
    bootCell = Store_GetObjectType(iRoot);

Store_WriteInfo Method

gmSL: int Store_WriteInfo(Handle info, int nByte)
gmVB: Function Store_WriteInfo(ByVal info As Handle, ByVal nByte As Integer) As Integer
gmNI: int Store_WriteInfo(void* info, int nByte);

The method Store_WriteInfo writes long information vectors into storage. There is no limit to the length of this information. Since block boundaries may occur within the storage area used, pointers should not be used to access the record, rather the methods Store_ReadInfo and Store_RewriteInfo should be used. The most common use of this method is to write the intermediate code associated with a component, though it may be used for any type of information.

The handle parameter info is a handle to the area in memory that contains the information to be written, and the integer parameter nByte is the number of bytes to be written. If the number of bytes to be written is not well-formed -- i.e., is less than or equal to zero, this method displays the following message and returns a zero.


 SYSERR#5004: Empty or negative length (%nbyte%) record being written.
Normally, the method returns the offset of the start of the storage area for the information. This offset must be used later to retrieve the information.

The following code shows how Store_WriteInfo can be used to write a [5][5] matrix.


 <gmBasic> <!-- Unit test for Store_WriteInfo and Store_Vector -->
 <gmSL>
    int  levels[5];
    int  offsets[5];
    int  iOff;
    int  iLev;
    int  bootCell;

    Store_Create("Demo043", StorageUnit.USER)
    Write_OpenFile(1, "Demo043.out", True, OutStyle.Text);
    iOff = 0;
    while(iOff < 5)
    {
       iLev = 0;
       while(iLev < 5)
       {
          levels(iLev) = (iOff+1) * 10 + iLev + 1;
          iLev = iLev + 1;
       }
       offsets(iOff) = Store_WriteInfo(levels,20);
       iOff = iOff + 1;
    }
    iOff = 0;
    bootCell = Store_WriteInfo(offsets,20);
    Store_Vector("Offsets",0,0,bootCell);
    while(iOff < 5)
    {
       Store_ReadInfo(levels, offsets(iOff));
       iLev = 0;
       while(iLev < 5)
       {
          Write_Integer(levels(iLev),5);
          iLev = iLev + 1;
       }
       Write_Record();
       iOff = iOff + 1;
    }
    Store_Close();
 </gmSL>
 </gmBasic>
The method Store_ReadMethod reads the values in and the displayed results are as follows.


    11   12   13   14   15
    21   22   23   24   25
    31   32   33   34   35
    41   42   43   44   45
    51   52   53   54   55

Table of Contents