How accessing to the array's descriptor structure? (ONLY FOR FBC VERSION < 1.08)

General FreeBASIC programming questions.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How accessing to the array's descriptor structure? (ONLY FOR FBC VERSION < 1.08)

Post by fxm »

Munair wrote:Speaking of bug reports, the first one in the list dates back to October 2012
I can not find it.
What is its number?
Last edited by fxm on Sep 04, 2019 18:42, edited 1 time in total.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How accessing to the array's descriptor structure?

Post by Munair »

Although off topic, it seems like TYPE with Field = 1 still ends every string field with a null-terimator byte. This code is a quick rewrite of bug report 372:

Code: Select all

dim z as string * 20 = "GIF89a" + chr(32) + mkshort(10) + chr(32) + mkshort(11)

type hdr field = 1
	sig as string * 6
	wid as string * 2
	hei as string * 2
end type

dim h as hdr ptr
h = cast(hdr ptr, strptr(z))
print h->sig
print cvshort(h->wid)
print cvshort(h->hei)
Last edited by Munair on Dec 21, 2017 8:31, edited 2 times in total.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How accessing to the array's descriptor structure?

Post by Munair »

fxm wrote:
Munair wrote:Speaking of bug reports, the first one in the list dates back to October 2012
I can not find it.
What is its number?
372
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How accessing to the array's descriptor structure? (ONLY FOR FBC VERSION < 1.08)

Post by fxm »

Munair wrote:Although off topic, it seems like TYPE with Field = 1 still ends every string field with a null-terimator byte.
No.
The 'sig' string termination corresponds to the first encountered 'chr(0)' which is provided by the second character of the 'mkshort(10)' string.
The 'wid' string termination corresponds to the first encountered 'chr(0)' which is provided by the second character of the 'mkshort(10)' string.
The 'wid' string termination corresponds to the first encountered 'chr(0)' which is provided by the second character of the 'mkshort(11)' string.
Last edited by fxm on Sep 04, 2019 18:42, edited 1 time in total.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How accessing to the array's descriptor structure?

Post by Munair »

fxm wrote:
Munair wrote:Although off topic, it seems like TYPE with Field = 1 still ends every string field with a null-terimator byte.
No.
The 'sig' string termination corresponds to the first encountered 'chr(0)' which is provided by the second character of the 'mkshort(10)' string.
The 'wid' string termination corresponds to the first encountered 'chr(0)' which is provided by the second character of the 'mkshort(10)' string.
The 'wid' string termination corresponds to the first encountered 'chr(0)' which is provided by the second character of the 'mkshort(11)' string.
I don't think so. The following example gives a "01" for 'wid' instead of "10" from a clean string:

Code: Select all

dim z as string * 20 = "GIF89a1011"

type hdr field = 1
   sig as string * 6
   wid as string * 2
   hei as string * 2
end type

dim h as hdr ptr
h = cast(hdr ptr, strptr(z))
print h->sig 'GIF89a
print h->wid '10 ?
print h->hei '11 ?
In order to have the fields populated properly, additional characters should be inserted, like "GIF89a 10 11". Clearly the fixed-length strings are null-terminated, even with field=1
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How accessing to the array's descriptor structure? (ONLY FOR FBC VERSION < 1.08)

Post by fxm »

Excuse me, I reasoned as if they were 'Zstring * n' and not 'String * n' :

Code: Select all

dim z as string * 20 = "GIF89a1011"

type hdr field = 1
   sig as zstring * 6
   wid as zstring * 2
   hei as zstring * 2
end type

dim h as hdr ptr
h = cast(hdr ptr, strptr(z))
print h->sig ' "GIF89a1011"
print h->wid ' "1011"
print h->hei ' "11"
For 'String * n', do not forget that in fact 'n+1' ubytes are reserved in memory :

Code: Select all

dim z as string * 20 = "GIF89a1011"

type hdr field = 1
   sig as string * 6
   wid as string * 2
   hei as string * 2
end type

dim h as hdr ptr
h = cast(hdr ptr, strptr(z))
print h->sig ' "GIF89a"
print h->wid ' "01"
print h->hei ' ""
Last edited by fxm on Sep 04, 2019 18:43, edited 1 time in total.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How accessing to the array's descriptor structure?

Post by Munair »

ZSTRING was nowhere in my example. But you can have it anyway you like; I already did the same test with ZSTRING, with the same result. Only when inserting separator characters to account for the null-termination of the TYPE fields, the fields get properly populated:

Code: Select all

dim z as zstring * 20 = "GIF89a 10 11"  'note that "GIF89a1011" will not populate the type fields correctly.

type hdr field = 1
   sig as string * 6
   wid as string * 2
   hei as string * 2
end type

dim h as hdr ptr
h = cast(hdr ptr, strptr(z))

print h->sig 'output: "GIF89a"
print h->wid 'output: "10"
print h->hei 'output: "11"
end
Given the BMP example that comes with the FIELD documentation, it seems that only strings have this issue (which I find perfectly acceptable).
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How accessing to the array's descriptor structure? (ONLY FOR FBC VERSION < 1.08)

Post by fxm »

I would highlight the fact that there is no information in the array descriptor to discriminate between a var-len array and a fix-len array (perhaps missing?).

This explains that when an array is passed to a procedure (in fact, its descriptor is passed), the simple syntax which tries to 'Redim' a fix-len array (defined out of the procedure scope) induces in general no run-time error as long as the number of dimensions of the array (from its passed descriptor) is compatible with the 'Redim' parameters (in general, that gets bad as soon as the 'Preserve' qualifier is requested).
In addition, when returning from the procedure, the modified descriptor has no impact on the fix-len array in its definition scope because the compiler does not use it because knowing the fixed characteristics of the array (descriptor is only created to pass the array information to a procedure when array is passed as argument or when array is shared).

At opposite, for var-len arrays, the array descriptor is the only defining the array characteristics.
Last edited by fxm on Sep 04, 2019 18:43, edited 1 time in total.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How accessing to the array's descriptor structure? (ONLY FOR FBC VERSION < 1.08)

Post by fxm »

How to return an array by a function return and pass it as a procedure argument for a requested parameter of array type?

Now play a little with the array descriptors:
How to return a string array defined inside a simple function 'f() As datatype' (without passed array neither shared array, but with only return variable), and pass it to a procedure 's(() As String)' (by passing the function return as an argument).

Code: Select all

' useful function to return the descriptor address of any array
Function ptrFunction (Byval p As Any Ptr) As Any Ptr
  Return p
End function

Function returnArray () As Any Ptr
  ' defining a var-len array with static storage
  Static As String array()
  Redim array (1 To 9, 1 To 5)
  For I As Integer = Lbound(array, 1) To Ubound(array, 1)
    For J As Integer = Lbound(array, 2) To Ubound(array, 2)
      array(I, J) = Str(I * 10 + J)
    Next J
  Next I
  ' returning a pointer to the array descriptor
  Dim As Function (() As String) As Any Ptr f = Cast(Function (() As String) As Any Ptr, @ptrFunction)
  Return f(array())
End Function

Sub receiveArray (array() As String)
  ' printing the array
  For I As Integer = Lbound(array, 1) To Ubound(array, 1)
    For J As Integer = Lbound(array, 2) To Ubound(array, 2)
      Print "'" & array(I, J) & "'",
    Next J
    Print
  Next I
  ' erasing the array
  Erase array
End Sub

' passing the array to the sub by means of a pointer to its descriptor
Dim As Sub(Byval As Any Ptr) s = Cast(Sub(Byval As Any Ptr), @receiveArray)
s(returnarray())

Sleep

Code: Select all

'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'
'61'          '62'          '63'          '64'          '65'
'71'          '72'          '73'          '74'          '75'
'81'          '82'          '83'          '84'          '85'
'91'          '92'          '93'          '94'          '95'
Last edited by fxm on Sep 04, 2019 18:44, edited 1 time in total.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: How accessing to the array's descriptor structure?

Post by Munair »

Thanks for sharing.
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How accessing to the array's descriptor structure? (ONLY FOR FBC VERSION < 1.08)

Post by fxm »

Similar to first post example, but with a better definition of the array descriptor structure:

Code: Select all

Type arrayDimensionDescriptor
  Dim As Uinteger nbOfElements  ' number of elements: (highBound-lowBound+1)
  Dim As Integer lowBound       ' lbound
  Dim As Integer highBound      ' ubound
End Type

Type arrayDescriptor
  Dim As Any Ptr nullIndexesAddress  ' pointer to the real or virtual element: @array(0, 0,...)
  Dim As Any Ptr minIndexesAddress   ' pointer to the first real element: @array(lbound1, lbound2,...)
  Dim As Uinteger globalSize         ' "global" size in bytes: (ubound1-lbound1+1)*(ubound2-lbound2+1).....*(size of 1 element)
  Dim As Uinteger elementSize        ' size of one element in bytes
  Dim As Uinteger nbOfDimensions     ' number of dimensions
  Dim As arrayDimensionDescriptor arrayDimensions(1 to 8)  ' max number of dimensions = 8
End Type                                                   '   (numbered from 1 to 8)

private Function arrayDescriptorGetPtrFunction (Byval p As Any Ptr) As Any Ptr
  Return p
End function

#macro arrayDescriptorPtr(array, p)
  Scope
    Dim As Function (() As Typeof((array))) As Any Ptr f
    f = Cast(Function (() As Typeof((array))) As Any Ptr, @arrayDescriptorGetPtrFunction)
    p = f(array())
  End Scope
#endmacro

'------------------------------------------------------------------------------------

Sub printArrayDescriptor (Byval p As Any Ptr)
  Dim As arrayDescriptor Ptr pu = p
  Print "[@array descriptor: "; pu; "]"
  Print "  @array(all_null_indexes)   ="; pu->nullIndexesAddress
  Print "  @array(all_min_indexes)    ="; pu->minIndexesAddress
  Print "  array_total_size_in_bytes  ="; pu->globalSize
  Print "  array_element_size_in_bytes="; pu->elementSize
  Print "  number_of_array_dimensions ="; pu->nbOfDimensions
  For i As Integer = 1 to pu->nbOfDimensions
    Print "  [dimension number:"; i; "]"
    Print "    number_of_elements="; pu->arrayDimensions(i).nbOfElements
    Print "    min_index         ="; pu->arrayDimensions(i).lowBound
    Print "    max_index         ="; pu->arrayDimensions(i).highBound
  Next i
End Sub

'------------------------------------------------------------------------------------

Dim As Longint test1(0 to 9, 1 to 100)
Dim Shared As Longint test2(0 to 9, 1 to 100)
Dim As Longint test3()
Dim Shared As Longint test4()
Type UDT
  Dim As Longint test1(0 to 9, 1 to 100)
  Dim As Longint test2(Any, Any)
End Type
Dim As UDT u

Screen 0
Width , 30

Dim p As Any Ptr

arrayDescriptorPtr(test1, p)
printArrayDescriptor(p)
Sleep
Cls
arrayDescriptorPtr(test2, p)
printArrayDescriptor(p)
Sleep
Cls
arrayDescriptorPtr(test3, p)
printArrayDescriptor(p)
Print
Redim test3(0 to 9, 1 to 100)
arrayDescriptorPtr(test3, p)
printArrayDescriptor(p)
Sleep
Cls
arrayDescriptorPtr(test4, p)
printArrayDescriptor(p)
Print
Redim test4(0 to 9, 1 to 100)
arrayDescriptorPtr(test4, p)
printArrayDescriptor(p)
Sleep
Cls
arrayDescriptorPtr(u.test1, p)
printArrayDescriptor(p)
Sleep
Cls
arrayDescriptorPtr(u.test2, p)
printArrayDescriptor(p)
Print
Redim u.test2(0 to 9, 1 to 100)
arrayDescriptorPtr(u.test2, p)
printArrayDescriptor(p)

Sleep
[edit]
Changing the data-type of two fields (lowBound and highBound) from Uinteger to Integer because they may have negative values.
Last edited by fxm on Sep 04, 2019 18:44, edited 2 times in total.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How accessing to the array's descriptor structure?

Post by dodicat »

Tested a simplified version of the array descriptor code, but I get a pile of gcc warnings if I don't use -exx switch.
The idea is to save any array type of any dimensions to file and retrieve the array back from the file.

Code: Select all

 

'must compile with -exx to remove errors
#include "crt.bi"
'fxm stuff
Function arrayDescriptorPtrFunction (Byval p As Any Ptr) As Any Ptr
    Return p
End Function
#Define arrayDescriptorPtr(array) _
Cast(Function (() As Typeof((array))) As Any Ptr, @arrayDescriptorPtrFunction)(array())

'new
#macro GetArrayPointer(a,address)
Scope
    Dim As Integer Ptr pt=arraydescriptorptr(a)
    address=pt[1]  'get the proper address of data start
End Scope
#endmacro

#macro GetSize(array,d)
d=Ubound(array,0)
For n As Integer=1 To d
    If n=1 Then d=1
    d=d*(Ubound(array,n)-Lbound(array,n)+1)
Next
d=d*Sizeof(array) 'size in bytes
#endmacro

#macro save(filename,array)
Scope
    Dim As file Ptr f = fopen(filename, "wb")
    Var size=0
    getsize(array,size)
    Dim As Integer ap
    GetArrayPointer(array,ap)
    fwrite(Cptr(Typeof(array) Ptr,ap),size,1,f) 
    fclose(f)
End Scope
#endmacro

#macro load(filename,array)
Scope
    Dim As file Ptr f = fopen(filename, "rb")
    Var size=0
    getsize(array,size)
    Dim As Integer ap
    GetArrayPointer(array,ap)
    fread(Cptr(Typeof(array) Ptr,ap),size,1,f) 
    fclose(f)
End Scope
#endmacro



Dim As Ulong limit=600

Dim As Ushort s(limit,limit)
Dim As Long d
Print "array size (ushort) ";
getsize(s,d)
Print d
s(501,500)=1234
Print "original  "; s(501,500)
save("test.dat",s)

Redim As Ushort res(limit,limit)
load("test.dat",res)
Print "from file "; res(501,500)
Print



Dim As String sout(1 To 4,30,-2 To 5)
Print "array size (string)  ";
getsize(sout,d)
Print d
sout(3,20,1)="Hello"
Print "original  "; sout(3,20,1)
save("test.dat",sout)
Dim As String sreturn(1 To 4,30,-2 To 5)
load("test.dat",sreturn)
Print "from file "; sreturn(3,20,1)
print

type udt
    as string * 20 s
    as integer i
end type

dim as udt z(4,4)
Print "array size (udt)  ";
getsize(z,d)
Print d
z(2,2).s="Hi "
z(2,2).i=2019
Print "original  "; z(2,2).s;z(2,2).i
save("test.dat",z)
dim as udt zout(4,4)
load("test.dat",zout)
Print "from file "; zout(2,2).s;zout(2,2).i
Sleep 

Kill "test.dat"



 
fxm
Moderator
Posts: 12083
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: How accessing to the array's descriptor structure? (ONLY FOR FBC VERSION < 1.08)

Post by fxm »

That is why to avoid warnings gcc (see #889), I proposed a less condensed version (as above) to get the address of a descriptor, but less convenient because the '#define' became a '#macro'.
Last edited by fxm on Sep 04, 2019 18:44, edited 1 time in total.
dodicat
Posts: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: How accessing to the array's descriptor structure?

Post by dodicat »

Thanks fxm.
Your updated code is OK.
Looks like saving/loading arrays via the crt functions can handle var length strings, which I find strange!
Even using filelen can pull out a correct size for loading a var length string udt array.
Your array descriptor is an excellent tool for generalising, I hope it will be included in future builds.
updated:

Code: Select all

'
#include "crt.bi"
#include "file.bi"
'fxm stuff
Function arrayDescriptorGetPtrFunction (Byval p As Any Ptr) As Any Ptr
    Return p
End Function

#macro arrayDescriptorPtr(array, p)
Scope
    Dim As Function (() As Typeof((array))) As Any Ptr f
    f = Cast(Function (() As Typeof((array))) As Any Ptr, @arrayDescriptorGetPtrFunction)
    p = f(array())
End Scope
#endmacro

'new
#macro GetArrayPointer(a,address)
Scope
    Dim As Integer Ptr pt
    arrayDescriptorPtr(a,pt)
    address=pt[1]  
End Scope
#endmacro

#macro GetSize(array,d)
d=Ubound(array,0)
For n As Integer=1 To d
    If n=1 Then d=1
    d=d*(Ubound(array,n)-Lbound(array,n)+1)
Next
d=d*Sizeof(array) 
#endmacro

#macro save(filename,array)
Scope
    Dim As file Ptr f = fopen((filename), "wb")
    Var size=0
    getsize(array,size)
    Dim As Integer ap
    GetArrayPointer(array,ap)
    fwrite(Cptr(Typeof(array) Ptr,ap),size,1,f) 
    fclose(f)
End Scope
#endmacro

#macro load(filename,array)
Scope
    Dim As file Ptr f = fopen((filename), "rb")
    Var size=0
    getsize(array,size)
    Dim As Integer ap
    GetArrayPointer(array,ap)
    fread(Cptr(Typeof(array) Ptr,ap),size,1,f) 
    fclose(f)
End Scope
#endmacro

'====================================
Dim As Ulong limit=600

Dim As Ushort s(limit,limit)
Dim As Long d
Print "array size (ushort) ";
getsize(s,d)
Print d
s(599,599)=1234
Print "original  "; s(599,599)
save("test.dat",s)

Redim As Ushort res(limit,limit)
load("test.dat",res)
Print "from file "; res(599,599)
Print


Redim As String sout(1 To 4,30,-2 To 5)
Print "array size (string)  ";
getsize(sout,d)
Print d
sout(3,20,1)="Hello"
Print "original  "; sout(3,20,1)
save("test.dat",sout)
Dim As String sreturn(1 To 4,30,-2 To 5)
load("test.dat",sreturn)
Print "from file "; sreturn(3,20,1)
Print

Type udt
    As String * 20 s
    As Integer i
End Type

Dim As udt z(4,4)
Print "array size (udt)  ";
getsize(z,d)
Print d
z(2,2).s="Hi "
z(2,2).i=2019
Print "original  "; z(2,2).s;z(2,2).i
save("test.dat",z)
Dim As udt zout(4,4)
load("test.dat",zout)
Print "from file "; zout(2,2).s;zout(2,2).i
print


type stringudt
    as string  s
end type

dim as stringudt g(3,1 to 8)
getsize(g,d)
print "array size (stringudt)  ";d
g(3,8).s="Done"

save("test.dat",g)

dim as integer length=filelen("test.dat")\sizeof(string)

dim as stringudt u(1 to length)
load("test.dat",u)
print "file size "; filelen("test.dat")
print u(ubound(u)).s

Sleep 

Kill "test.dat"


  
Post Reply