LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

DSSetHSzClr - memory size calculation

Solved!
Go to solution

Hi, I am trying to change the size of the array of clusters inside the DLL. In the C code, the array of clusters is defined:

typedef struct {
	int32_t dimSize;
	double Item[1];
	} DataArray;
typedef DataArray **DataArrayPtr;

typedef struct {
	LStrHandle Name;
	LStrHandle String;
	double Type;
	DataArrayPtr Data;
	} Cluster;

typedef struct {
	int32_t dimSize;
	Cluster Variable[1];
	} Buffer;
typedef Buffer **BufferPtr;

 In the FP, the array of cluster looks : array of clusters.png

I would like to add lets say one new item to the cluster array by using the DSSetHSzClr function described e.g. here . I am trying to follow the instruction on Lava link, however, I am not able to calculate the correct size of the new memory block. Could anyone help me to do it and explain the solution ... 

0 Kudos
Message 1 of 14
(3,896 Views)

Hi Charlie87,

 

Do you get any error about this? According to the manual, the error handler should return: 

NoErr No error.
MFullErr Not enough memory to perform the operation.
mZoneErr Handle or pointer not in specified zone.

What do you receive? 


How about when you use sizeof() function? Does it return you the size of the cluster?


Best regards,

IR

0 Kudos
Message 2 of 14
(3,839 Views)

That is not so easy to do. You have in fact a hierarchical structure which contains handles inside the top level cluster . So basically you have to allocated each individual string and array as independent handle too.

 

Basically your outermost array would be sizeof(int32_t) + n * sizeof(Cluster)

 

with n being the number of cluster elements. Then you need to assign proper handles for the Name, String and Data elements too, unless you are fine with empty arrays, as a NULL pointer is a valid value for LabVIEW handles, indicating an empty string or array.

 

The Name and String elements are obvious as their size is sizeof(int32_t) + x * sizeof(uint8_t)

 

The data array would have to be calculate as sizeof(int32_t) + y * sizeof(double)

 

with x being the number of characters in the string and y being the number of Item elements.

 

If you increase the number of elements you must make sure to initialize all the handle elements after the resize call with either NULL (for empty handles) or a correctly allocated valid handle. If you decrease the size of the array you have to make sure to first deallocate any handles in the elements that are going to be removed and are not NULL, before you can call the DSSetHSzClr() function or otherwise you create a memory leak.

Rolf Kalbermatter
My Blog
0 Kudos
Message 3 of 14
(3,828 Views)

Thank you for your answer. It is a little bit late, nevertheless I would like to continue discussing this topic in more detail.

I implemented the solution:

When I want to add 1 new element to the array, I use this at first:

size = (*p)->dimSize + 1;
if( mgNoErr != (err = DSSetHSzClr(p, Offset(Buffer,Variable) + sizeof(Cluster)*size))) return 0;

 Then I need to allocate the memory for the elements in the structure (Name, (String)):

Cluster *var;
size = strlen(theNewString);
var = &(*p)->Variable[idxOfNewElement]; var->Name = (LStrHandle)DSNewHClr(sizeof(LStrHandle)+ sizeof(uChar)*size+1);

 and DataArray:

size = numberOfElementsInDoubleArray;    
if( NULL == ((*var).Data = (DataArrayPtr)DSNewHClr(sizeof(DataArray) + size*sizeof(double)))) return 0;

 I think the solution is same as you posted, but please check it.

I am not sure about your last advice:

 

If you decrease the size of the array you have to make sure to first deallocate any handles in the elements that are going to be removed and are not NULL, before you can call the DSSetHSzClr() function or otherwise you create a memory leak

 

How to deallocate the handles ???

 

Another question: I suppose to edit the mentioned cluster array in the DLL as well inside the LabView executable ... does it include any issue ?

0 Kudos
Message 4 of 14
(3,820 Views)

First I do not like to combine error handling and function calls into the same statement. That makes everything quite hard to understand. Following is the code as I would start with for adding one element to the array.

 

NOTE: This code has been typed directly into the post, was not compiled and therefore can contain syntax errors and also logical errors. It nevertheless shows the principle in a step by step way.

 

int32_t size, dimSize = (*p)->dimSize + 1;

/* Look out! The Offset(Buffer, Variable) does correctly account for alignment issues for the first element, but not for subsequent elements (not an issue for 32 bit LabVIEW but very important to review that the Cluster is really a multiple of 64 Bits in size as the Name element is a Handle (pointer to pointer) and will be aligned to a 64 Bit boundary for 64 Bit LabVIEW. The Cluster in the original post is safe in this respect as it will amount properly to 20 bytes size in 32 Bit LabVIEW and 32 bytes in 64 Bit LabVIEW, aligning the Name handle always on the natural pointer size boundary of 4 and 8 respectively. */
err = DSSetHSzClr(p, Offset(Buffer, Variable) + sizeof(Cluster) * dimSize);
if (mgNoErr != err)
return err;

var = &(*p)->Variable[(*p)->dimSize];
size = strlen(theNewName);
if (size > 0
{
/* No need to account for any NULL character at the end for LabVIEW strings */
var->Name = (LStrHandle)DSNewHClr(sizeof(int32_t)+ sizeof(uChar) * size);
if (NULL == var->Name)
return mFullErr;
MoveBlock(theNewName, LStrBuf(*(var->Name)), size);
   LStrLen(*var->Name) = size;
}

size = strlen(theNewString);
if (size > 0)
{
   /* No need to account for any NULL character at the end for LabVIEW strings */
   var->String = (LStrHandle)DSNewHClr(sizeof(int32_t)+ sizeof(uChar) * size);
   if (NULL == var->Name)
       return mFullErr;
MoveBlock(theNewString, LStrBuf(*(var->String)), size);
LStrLen(*var->String) = size;
}

size = numberOfElementsInDoubleArray;
if (size > 0)
{
var->Data = (DataArrayPtr)DSNewHClr(Offset(Buffer, Item) + size * sizeof(double))  
    if (NULL == var->Data)
{
return mFullErr;
}
MoveBlock(theDoubleArray, (*(var->Data))->Item, size * sizeof(double));
(*(var->Data))->dimSize = size;
}

/* Last but not least update the array size */
(*p)->dimSize = dimSize

As to your question about when making the array smaller you would have to deallocate every single handle in every Cluster record that you want to remove by calling DSDisposeHandle() on it and if the handle is an array of clusters containing itself one or more handles deallocate those as well, and so on.

 

Above is what LabVIEW has to do when you do a Resize Array node plus some more error handling than this plus the opposite code when making the array smaller. I think it is clear to see that doing C programming is magnitudes more involved than LabVIEW. Smiley LOL and this is just theoretical code. One still has to compile it, syntax check it and also make sure there are no bugs anymore, which I do not guarantee for this kind of quick and dirty only proof of concept pseudo code.

Rolf Kalbermatter
My Blog
0 Kudos
Message 5 of 14
(3,811 Views)

Thank you Rolf again. I am going to analyze and use your code suggestion. However, I would like to ask some more questions dealing with this issue I met.

I am working with strings as you mentioned (String, Name) and I would like have an array of strings char** stringArray and point to all Names (LStrHandle) in the cluster array. But when I want to access the element in the stringArray I have a problem, because the string (Name->str) is not null terminated. I am using another functions which requires null terminated string such as strcmp, ....  I solved this problem by adding a zero value to the end of the original string which is pointed by the Name handle. But I feel that this solution is not correct...  

 

Another question deals with generating errors. Is it possible to set the error code of the error cluster which comes from the Call Library Function ?

0 Kudos
Message 6 of 14
(3,798 Views)

@charlie87 wrote:

Thank you Rolf again. I am going to analyze and use your code suggestion. However, I would like to ask some more questions dealing with this issue I met.

I am working with strings as you mentioned (String, Name) and I would like have an array of strings char** stringArray and point to all Names (LStrHandle) in the cluster array. But when I want to access the element in the stringArray I have a problem, because the string (Name->str) is not null terminated. I am using another functions which requires null terminated string such as strcmp, ....  I solved this problem by adding a zero value to the end of the original string which is pointed by the Name handle. But I feel that this solution is not correct...  


 

Usually strings are used in LabVIEW or in your C code but not both together. However it is of course possible to allocate a string handle with one byte more than what you want to fill in and make sure the last byte is set to null and then you can indeed pass the resulting pointer from the LStrBuff(*handle) macro call to any C runtime library that expects a NULL terminated ASCII string. You still need to fill in the dimSize element in the String handle ( LStrLen(*handle) ) with the correct number of characters exclusive the NULL byte in order for LabVIEW to be able to interpret the string correctly.

 

 


Another question deals with generating errors. Is it possible to set the error code of the error cluster which comes from the Call Library Function ?


No you can't do that. You either can return the error code as function return value (what I usually do) or pass the error cluster as extra parameter to the function and deal with it there.

 

Rolf Kalbermatter
My Blog
0 Kudos
Message 7 of 14
(3,790 Views)

What about your command to create the double array:

var->Data = (DataArrayPtr)DSNewHClr(Offset(Buffer, Item) + size * sizeof(double))  

 I think the memory for the handler should be allocated, shouldn't ?

var->Data = (DataArrayPtr)DSNewHClr(Offset(DataArray, Item) + sizeof(DataArray) + size * sizeof(double))  

 I have also question about the string handles. I suppose that when I want to change the size of the string I need to dispose the memory also. However, the LV crashs when I want to do it. Lets see the code.

int changeVarString(Cluster* var, mxArray* mxstr) {
     int size = mxGetNumberOfElements(mxstr);
     
     if(size+1 >= LStrLen(*var->String))
     {
/*the size of the new string is equal to the previous size */ if(mgNoErr != DSSetHSzClr((*var).String, sizeof(LStrHandle)+ sizeof(uChar)*size+1)) return 0; } else { DSDisposeHandle((*var).String); if(mgNoErr != DSSetHSzClr((*var).String, sizeof(LStrHandle)+ sizeof(uChar)*size+1)) return 0; } //var->String[0]->cnt = size; LStrLen(*var->String) = size+1; mxGetString(mxstr, (char*)&var->String[0]->str, size); var->Type = STRING; return 1; }

 

I suppose to terminate the string with extra null character, therefore there is size+1 ...

Same probem when I want to change the size of the DataArray in the cluster element (see the cluster structure at the beginning)

0 Kudos
Message 8 of 14
(3,780 Views)

I'm not sure what you mean with the memory for the handler that should be allocated. You need to allocate simply a handle that has space for the initial int32_t dimSize plus any data that the handle is supposed to contain.

 

As to your next problem if you pass in a default cluster from LabVIEW or increased the array of clusters by one or more elements, the according handles in the cluster will be NULL which is a valid handle for LabVIEW indicating an empty array or string. But the memory manager functions for resizing handles can not work on NULL handles. A little source code level debugging would have shown you that easily.

 

And your code to first dispose the handle and then trying to resize it is totally wrong anyways. You can't resize a non-existing handle at all. It has to be allocated properly first before you can attempt to resize it.

 

Instead you have to create them rather than resize them. You can do that explicitedly yourself in a similar way as follows:

 

if ((*var)->String == NULL)

{

    (*var)->String = DSNewHClr(sizeof(int32_t) + size);

    if ((*var)->String == NULL)

         err = mFullErr;

}

else

{

    err = DSSetHSzClr((*var).String, sizeof(int32_t) + sizeof(uChar) * size);

}

if (err)

    return err;

MoveBlock(source, LStrBuf(*((*var).String)), size);

LStrLen(*((*var).String)) = size;

 

or you can let the LabVIEW memory manager do the calculation for you:

 

err = NumericArrayResize(uB, 1, (UHandle*)&((*var)->String), size);

if (err)

    return err;

MoveBlock(source, LStrBuf(*((*var).String)), size);

LStrLen(*((*var).String)) = size;

 

If you want to add an extra NULL character then you can of course do so in the call to DSNewHClr(), DSSetHSzClr() or NumericArrayResize().

 

Most of the errors you are now doing are however simply standard C errors and have little to do with LabVIEW specific issues, so the question arises if this is maybe a little to complicated to get going. While you can do quite well in LabVIEW with limited knowledge and in the worst case you simply get a wrong result, C programming is a lot more demanding. One byte of can mean a working solution or something that constantly crashes and worse yet it can work 1000 times seemingly right and then crash suddenly, preferably after you finished the whole development and deployed the final result to a remote location on the other side of earth.

Rolf Kalbermatter
My Blog
0 Kudos
Message 9 of 14
(3,765 Views)

Hi,

 

I am back again with new challange...

 

I change my baseline Cluster to 
cluster.png

 

where I replaced the String with Array of Strings.

 

The C representation of those cluster is:

typedef struct {
	int32_t dimSize[2];
	double Item[1];
	} DataArray;
typedef DataArray **DataArrayPtr;

typedef struct {
	int32_t dimSize;
	LStrHandle String[1];
	} DataStrings;
typedef DataStrings **DataStringsPtr;

typedef struct {
	LStrHandle Name;
	DataArrayPtr Data;
    uint16_t Type;
    DataStringsPtr Strings;
	} Cluster;

typedef struct {
	int32_t dimSize;
	Cluster Variable[1];
	} Buffer;
typedef Buffer **BufferPtr;

 Now I try to write data to the String Array Strings, but I have not been able to work it out for a few days.

I tryed it this way.

 

At fist I enlarge the Cluster Array:

DSSetHSzClr(p, Offset(Buffer,Variable) + sizeof(Cluster)*size

 where the size is oldsize + 1, p is the Cluster Array. That worked fine for the previous version without the string array.

 

Then I take the last new element of the cluster array:

 

setVarString(&(*p)->Variable[size-1], field)

 



int setVarString(Cluster* var, ....) {
    
     int strSize = ...;
      
     var->Strings = (DataStringsPtr)DSNewHClr(sizeof(DataStrings) + Offset(DataStrings,String) + sizeof(LStrHandle) + sizeof(uChar)*(strSize+1));

     if(var->Strings == NULL) // array allocation failed
         return 0;
     
     (*(var->Strings[0]->String[0]))->str = ....

     LStrLen((*(var->Strings[0]->String[0]))) = strSize+1;
     var->Type = STRING;
     var->Data = NULL;
     return 1;
}

 It seems that I cannot set the memory size by DSNewHClr properly. Any idea ??

 

0 Kudos
Message 10 of 14
(3,655 Views)