Page 1 of 1

copying & freeing structures with strings

Posted: Tue Aug 12, 2008 7:13 am
by Demivec
Code updated for 5.20+ (same as InitializeStructure(), CopyStructure(), ClearStructure())

This topic has come up before many times. Here's my version of the age old questions on how to copy a structure with strings and how to delete the same type of structure without causing a memory leak.

Th code should be very explanatory, including a simple demonstration.

A brief bit of explanation. It requires info about each structure that will be operated on. This information includes mainly the offsets to the strings within the structure and the size of string arrays. It then relies on this information when copying or deleting the structure. I will probably modify it a little bit more, though it currently works as advertised. It relies on a method posted by freak (Timo).

Code: Select all

;Program: CopyDeleteStringStruct.pb v1.0
;Author: Demivec (Jared), method for freeing string memory supplied by freak (Timo)
;Date: 08/11/2008
;Using: PureBasic v4.20
;Description:  the program requires data to be prepared in a DataSection regarding structure types.
;It provides an Enumeration outline to produce type#'s and 3 procedures. 
;  - registerStructs() initializes the system with data regarding structure types and the offsets
;within them of strings and string arrays.  This procedure must be called first before it can be used.
;  - deleteStruct() takes a pointer to a registered structure type and frees the memory and strings allocated to it.
;  - copyStruct() takes a pointer to a registered structure type and copies it, making true copies of its
;strings that are placed at unique addresses.  If a destination pointer to a pre-existing registered structure type
;is not passed it will allocate a new structure to contain the copy.  It returns the address of the structure copy
;upon completion.
;
;If a structure doesn't contain any strings it can still be registered and used with the procedures
;so that it can be treated in a consistent manner as the other structures.  This is otherwise not
;necessary because it doesn't have the complication of strings.
;
;These procedures are expressly written to properly handle structures with strings or string arrays as subelements.
;They do not handle stuctures within structures, nor do they handle pointers to structures with a structure, nor
;arrays of structures that contain strings.

;sample of 4 structures to be registered
Structure custom
  l.b[4]
  b.q
  d.s
  e.s
  f.w
EndStructure

Structure custom2
  a.s
  b.i
  d.s
  f.w
  g.s
  h.s
EndStructure

Structure custom3
  b.i
EndStructure

Structure custom4
  b.i
  e.s[11]
  f.i
EndStructure

Enumeration  ;structure type#'s, for use with StructData and deleteStruct()
  #typ_custom   ;include an entry for each type of structure to be freed this way
  #typ_custom2
  #typ_custom3
  #typ_custom4
  #numOfStructs ;this has to be the last item in this set of enumerations
EndEnumeration

Structure structInfo 
  size.i         ;size of structure ;currently not used
  count.i        ;number of strings to free
  arrayCount.i   ;count of string arrays present
  offset.i [11]  ;structure offset to each string, needs to be dimmed to the maximum possible (count + arraycount)
  arraySize.i[6] ;the Offset and Dimension of each array (i.e. [0] = offset to 1st array,[1] = dimensions of 1st array)
EndStructure

Global Dim structInfo.structInfo(#numOfStructs)

DataSection
  StructData:
  Data.i #numOfStructs ;number of structures in this section followed by an entry for each structure
  ;structures enumerated type#, structure size, count of strings, count of string arrays
  ;structure offset of each string(if any)..., structure offset of each string array (if any)...,
  ;# of elements in each string array(if any)...
  ;(i.e. minimum entry = data.i type, size, 0, 0)
  Data.i #typ_custom, SizeOf(custom), 2, 0, OffsetOf(custom\d), OffsetOf(custom\e)
  Data.i #typ_custom2, SizeOf(custom2), 4, 0, OffsetOf(custom2\a), OffsetOf(custom2\d), OffsetOf(custom2\g), OffsetOf(custom2\h)
  Data.i #typ_custom3, SizeOf(custom3), 0, 0
  Data.i #typ_custom4, SizeOf(custom4), 0, 1, OffsetOf(custom4\e), 11
EndDataSection

Procedure registerStructs() ;this is called once to initialize structInfo() for the deleteStruct() procedure
  Protected type, countStruct, i, j
  
  Restore StructData
  Read.i countStruct
  For i = 1 To countStruct
    
    Read.i type
    Read.i structInfo(type)\size
    Read.i structInfo(type)\count 
    Read.i structInfo(type)\arrayCount 
    
    For j = 0 To structInfo(type)\count + structInfo(type)\arrayCount - 1
      Read.i structInfo(type)\offset[j]
    Next 
    
    For j = 0 To structInfo(type)\arrayCount - 1
      Read.i structInfo(type)\arraySize[j] ;size of string array, = # of elements in array
    Next 
    
  Next 
EndProcedure

Procedure deleteStruct(type, *p)   ;frees memory and strings allocated to a registered structure of the enumerated "type"
  ;frees strings followed by the structure's allocated memory
  Protected count = structInfo(type)\count - 1, arrayCount = structInfo(type)\arrayCount - 1, i, j
  
  If *p <> 0
    ;handles single strings
    If count <> -1
      Protected Dim String.String(count)
      For i = 0 To count
        PokeI(@String(i), PeekI(*p + structInfo(type)\offset[i]))
      Next 
      Dim String.String(0)
    Else
      count = 0
    EndIf
    
    If arrayCount <> -1
      For j = 0 To arrayCount
        Protected Dim String.String(structInfo(type)\arraySize[j])
        For i = 0 To structInfo(type)\arraySize[j] - 1
          PokeI(@String(i), PeekI(*p + structInfo(type)\offset[count + j] + i * SizeOf(String)))
        Next 
        Dim String.String(0)
      Next 
    EndIf
    FreeMemory(*p)
    *p = 0
  EndIf 
EndProcedure

Procedure copyStruct(type, *s, *d = 0) ;return the address (*d) to a string safe copy of structure (*s) of the enumerated "type"
  ;the destination structure *d can be preexisting, in which case it's contents will be overwritten
  Protected *ss.String, *ds.String, count = structInfo(type)\count - 1, arrayCount = structInfo(type)\arrayCount - 1, i, j

  ;copy entire structure
  If *d = 0
    *d = AllocateMemory(structInfo(type)\size)
  EndIf 
  CopyMemory(*s, *d, structInfo(type)\size)
  
  ;establish new string pointers in copy
  If *s <> 0
    ;handles single strings
    If count <> -1
      For i = 0 To count
        *ss = *s + structInfo(type)\offset[i]
        *ds = *d + structInfo(type)\offset[i]
        PokeI(*ds, 0)
        If PeekI(*ss) <> 0 ;avoid creating empty string out of a null string
          *ds\S = *ss\S
        EndIf 
      Next 
    Else
      count = 0
    EndIf
    
    If arrayCount <> -1
      For j = 0 To arrayCount
        For i = 0 To structInfo(type)\arraySize[j] - 1
          *ss = *s + structInfo(type)\offset[count + i] + i * SizeOf(String)
          *ds = *d + structInfo(type)\offset[count + i] + i * SizeOf(String)
          PokeI(*ds, 0)
          If PeekI(*ss) <> 0 ;avoid creating empty string out of a null string
            *ds\S = *ss\S
          EndIf 
        Next 
      Next 
    EndIf
  EndIf 
  
  ProcedureReturn *d ;return address of copy
EndProcedure

;-an attempt at a demo
registerStructs() ;needs to be called first

;create some sample structures
Define *g.custom4 = AllocateMemory(SizeOf(custom4))
;assign a string to distinguish our structure
*g\e[5] = "still here"
Define *h.custom4 = AllocateMemory(SizeOf(custom4))
 ;assign a string to show this is a different structure
*h\e[3] = "different place"

;prepare a pre-existing structure to copy into
Define *g2.custom4 = AllocateMemory(SizeOf(custom4))

Debug "pre-existing address for copies: " + Str(*g2)
Debug "------contents of 2 elements before-----"
Debug *g2\e[3] : Debug *g2\e[5]  ;empty
Debug "--------------------------"
Debug "address of copy #1: " + Str(copyStruct(#typ_custom4, *g, *g2))
Debug "------contents of 2 elements after copy#1-----"
Debug *g2\e[3] : Debug *g2\e[5]
Debug "--------------------------"
 ;make a copy to pre-existing structure overwriting it's data
Debug "address of copy #2: " + Str(copyStruct(#typ_custom4 , *h, *g2))
Debug "------contents of 2 elements after copy#2-----"
Debug *g2\e[3] : Debug *g2\e[5]
;delete 2 of the 3 structures
deleteStruct(#typ_custom4, *g)
deleteStruct(#typ_custom4, *h)

Debug "-==============-"

Define *f.custom = AllocateMemory(SizeOf(custom))
 ;assign a string to demonstrate
*f\d = "hello"
Debug "the original structure's string element:" + Chr(34) + *f\d + Chr(34)
;copy without preallocating the structure
Debug "copies without preallocating"
Define *f2.custom = copyStruct(#typ_custom, *f)

Debug "address of original: " + Str(*f)
Debug "address of new copy: " + Str(*f2)
Debug "the copy structure's string element:" + Chr(34) + *f2\d + Chr(34)
Debug "---------------------------"
;change original string element
*f\d = "goodbye"
Debug "changed the original structure's string element to:" + Chr(34) + *f\d + Chr(34)
Debug "the copy structure's string element remains unchanged:" + Chr(34) + *f2\d + Chr(34)

;delete the original structures
deleteStruct(#typ_custom, *f)