Initializing an array with vector data

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
Post Reply
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Initializing an array with vector data

Post by Don_Kirby »

I have some string vector data that is read from an external file at compile time. I have found the need to move the data at run time to another array in RAM. I know I can get the address of the first item in the array, but I am unsure at to how I can assign the data to a RAM array.

Along the same line of thought, what marks the beginning or end of an array item for string vector data? I am assuming that each indices can be a different length, and if so, is there a specific character that denotes the beginning of a new index?

-Don
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Initializing an array with vector data

Post by dkinzer »

Don_Kirby wrote:I know I can get the address of the first item in the array, but I am unsure at to how I can assign the data to a RAM array.
For a single-dimension array (other than string types), you can just copy the data from ProgramMemory to RAM using GetProgMem. For multi-dimension arrays and string types, you'll have to copy the data element-by-element. The reason that multi-dimension arrays are an issue is that the row/column order is different between RAM-based and Program Memory-based arrays. For string type arrays, you can economize on RAM use by using the ProgMem string type along with the string's address in Program Memory. An example program for doing this appears below.
Don_Kirby wrote:what marks the beginning or end of an array item for string vector data?
Because the strings can be of different lengths, a StringVectorData item is compiled to an index table followed by the individual strings as illustrated below.

ZBasic Code:

Code: Select all

Dim strData as StringVectorData ({ "able", "baker", "charlie" })
Resulting VM mode code:

Code: Select all

             Dim strData as StringVectorData ({ "able", "baker", "charlie" })
0029 41    prg:strData (28 bytes, 3 entries)
0029 2f 00 35 00 3c 00 04 00  61 62 6c 65 05 00 62 61
0039 6b 65 72 07 00 63 68 61  72 6c 69 65
As you can see, the index table consists of the starting address for each string in the table. The strings themselves are stored in the same manner as a BoundedString: <string length><zero byte><string characters>

This example code populates a RAM-based string array with data from a ProgMem-based string array. See Section 3.26.2 of the ZBasic Reference Manual for details on string implementation.

Code: Select all

Dim strData as StringVectorData &#40;&#123; "able", "baker", "charlie" &#125;&#41;
Dim strArray&#40;1 to UBound&#40;strData&#41;&#41; as String

Sub Main&#40;&#41;
  Call copyStrings&#40;strArray, UBound&#40;strArray&#41;, strData.DataAddress&#41;
  Dim i as Integer
  For i = 1 to UBound&#40;strArray&#41;
    Debug.Print strArray&#40;i&#41;
  Next i
  Debug.Print "Done"
End Sub

Sub copyStrings&#40;ByRef sa&#40;&#41; as String, ByVal cnt as Integer, ByVal progMemAddr as Long&#41;
  Const ProgMemStrType as Byte = &He2
  Structure zxString
    Dim strLen as Byte
    Dim strType as Byte
    Dim strAddr as UnsignedInteger
  End Structure
  Dim i as Integer
  Dim strAddr as UnsignedInteger
  strAddr = sa.DataAddress
  For i = 1 to cnt
    Dim addr as UnsignedInteger
    Dim zxStr as zxString Based strAddr
    Dim strLen as Byte
    
    ' get the address of the string element, read the string length
    Call GetProgMem&#40;progMemAddr, addr, 2&#41;
    Call GetProgMem&#40;CLng&#40;addr&#41;, strLen, 1&#41;

    ' copy the string data to the RAM-based array
    zxStr.strLen = strLen
    zxStr.strType = ProgMemStrType
    zxStr.strAddr = addr + 2

    ' advance the addresses for the next pass
    strAddr = strAddr + CUint&#40;SizeOf&#40;zxStr&#41;&#41;
    progMemAddr = progMemAddr + 2
  Next i
End Sub
- Don Kinzer
Don_Kirby
Posts: 341
Joined: 15 October 2006, 3:48 AM
Location: Long Island, New York

Post by Don_Kirby »

Thanks Don, that's exactly what I was looking for.

-Don
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Don_Kirby wrote:that's exactly what I was looking for.
We discovered that there is a code generation error for the program above when compiling for native mode. The issue is that strData.DataAddress produces the address for the string data of the first element instead of the address of the index table. We haven't yet found a clean workaround but the one below corrects the address by subtracting the size of the index table. Again, this is only for native mode.

Code: Select all

Call copyStrings&#40;strArray, UBound&#40;strArray&#41;, strData.DataAddress - CLng&#40;UBound&#40;strArray&#41; * 2&#41;&#41;
This workaround can be made a little cleaner if you're using v2.6.5 or later of the compiler. Those versions support the Version.Value built-in constant that makes it easy to enable code that is required only for certain compiler versions. The modified workaround might look like this:

Code: Select all

  Dim pmAddr as Long
  pmAddr = strData.DataAddress
#if &#40;Version.Value < &H020609&#41; And &#40;Option.TargetCode = "Native"&#41;
  ' this corrects a native mode code generation error prior to v2.6.9
  pmAddr = pmAddr - CLng&#40;UBound&#40;strArray&#41; * 2&#41;
#endif
  Call copyStrings&#40;strArray, UBound&#40;strArray&#41;, pmAddr&#41;
The other observation is that the original code has a potential memory leak. The problem is that if the RAM-based string array contains any elements where the string value is held in the string heap, overwriting those entries with the ProgMem array data would leak those string elements. Of course, if you've never assigned any values to the RAM-based array this won't be an issue. The way to avoid the potential problem is to assign a zero length string to each array element just before populating that element from the ProgMem array as shown below.

Code: Select all

' assign a zero-length string to avoid a memory leak
sa&#40;i&#41; = ""

' copy the string data to the RAM-based array
zxStr.strLen = strLen
- Don Kinzer
Post Reply