[Implemented] CopyStructure()

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
Karbon
PureBasic Expert
PureBasic Expert
Posts: 2010
Joined: Mon Jun 02, 2003 1:42 am
Location: Ashland, KY
Contact:

[Implemented] CopyStructure()

Post by Karbon »

I didn't realize until just now that there is no way to copy a structure (with it's data)..

It'd be nice to have a CopyStructure() and some way to compare whole structures (and the data within).
-Mitchell
Check out kBilling for all your billing software needs!
http://www.k-billing.com
Code Signing / Authenticode Certificates (Get rid of those Unknown Publisher warnings!)
http://codesigning.ksoftware.net
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Agreed.
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Post by dell_jockey »

in C I would do something like

Code: Select all

 memcopy(source, dest, sizeof(structure));

// source & dest being pointers to previously allocated memory here
Can't something similar be done in Pure?
Last edited by dell_jockey on Sun Mar 28, 2004 11:23 pm, edited 1 time in total.
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

Here's a workaround, but it doesn't work with strings atm.

Code: Select all

Structure mystruct ; must not contain strings
  a.b
  b.w
  c.l
  d.l[20]
EndStructure

s1.mystruct
s1\a = 1
s1\b = 2
s1\c = 3
s1\d[11] = 4

*s2.mystruct = AllocateMemory(SizeOf(mystruct))
CopyMemory(@s1, *s2, SizeOf(mystruct))

s1\a = 11
s1\b = 12
s1\c = 13
s1\d[11] = 14

Debug s1\a
Debug s1\b
Debug s1\c
Debug s1\d[11]

Debug *s2\a
Debug *s2\b
Debug *s2\c
Debug *s2\d[11]
But I would apprentice a CopyStructure()-command, too !
%1>>1+1*1/1-1!1|1&1<<$1=1
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Post by dell_jockey »

Froggerprogger ist FAST ! :D 8O
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

Yes, I answered your question in the same minute you posted it :D
%1>>1+1*1/1-1!1|1&1<<$1=1
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Allocate memory isn't needed, coz when you define a variable, you are allocating memory for it.
Keep in mind that a structure is a variable with one or more fields data stored SEQUENTIALLY in mem. So, Froggerproger's example should be more coherent:

Code: Select all

Structure mystruct ; can contain strings but keep in mind that a string variable is in fact, a pointer to a characters string.
  a.b 
  b.w 
  df.s[10]
  c.l 
  d.l[20] 
EndStructure 

s1.mystruct 
s1\a = 1 
s1\b = 2 
s1\df[5]="hola tu"
s1\c = 3 
s1\d[11] = 4 

s2.mystruct
CopyMemory(@s1, @s2, SizeOf(mystruct)) 

s1\a = 11 
s1\b = 12 
s1\c = 13 
s1\d[11] = 14 

Debug s1\a 
Debug s1\b 
Debug s1\c 
Debug s1\d[11] 

Debug s2\a 
Debug s2\b 
Debug s2\df[5]
Debug s2\c 
Debug s2\d[11]
...and of course, if you define a linked list with whatever data structure, you can copy one existing element to another using CopyMemory(@list1(), @list2(), memoryamount).
And also you can copy only one or more data fields inside a structure, or copy a field to another, etc.; for example: CopyMemory(@list1()+4, @list2()+8, 4).
dmoc
Enthusiast
Enthusiast
Posts: 739
Joined: Sat Apr 26, 2003 12:40 am

Post by dmoc »

Think you'll find a problem with the string: the structure copy still points to original strings. This fact may cause you severe problems.
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

@Psycho
Yes, of course, this method you mentioned is the more elegant way (and the first way that should come to mind - normally) :)

But does anybody has an idea how to solve the string-problem ?
I think there's no NextStructureElement() and TypeOf() - command to step sequentially through the structure to find all strings and duplicate them manually.
So is there any general workaround for that?

(The CopyStructure() should do this of course)
%1>>1+1*1/1-1!1|1&1<<$1=1
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

I am confused. (Situation normal)

If the structure is copied as is, and the structure contains addresses of strings, surely everything is ok as the string addresses remain the same.

So if "ABC" is at address 1,650,000 and the structure field points to 1,650,000, then moving the structure (and therefore the field) to another address has no impact. The field still points to 1,650,000

What am I missing here?
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

What am I missing here?
What if some code changes the value in s2. It will also update s1.

At the moment the behaviour appears to be as follows:

If the new string fits into the space allocated for the original string both s1 and s2 will contain the new value.

If the string is larger than the space allocated then s2 will contain the new
value but s1 will contain something unpredictable. When the memory is deallocated it wil contain a null value. However, that memory may be reallocated elsewhere.

Code: Select all

Structure mystruct ; can contain strings but keep in mind that a string variable is in fact, a pointer to a characters string. 
  a.b 
  b.w 
  df.s[10] 
  c.l 
  d.l[20] 
EndStructure 

s1.mystruct 
s1\a = 1 
s1\b = 2 
s1\df[5]="hola tu" 
s1\c = 3 
s1\d[11] = 4 

s2.mystruct 
CopyMemory(@s1, @s2, SizeOf(mystruct)) 

s1\a = 11 
s1\b = 12 
s1\c = 13 
s1\d[11] = 14 

Debug s2\df[5]
Debug @s2\df[5]
Debug s1\df[5]
Debug @s1\df[5]

s2\df[5]="changed the string"

Debug s2\df[5]
Debug @s2\df[5]
Debug s1\df[5]
Debug @s1\df[5]
User avatar
GedB
Addict
Addict
Posts: 1313
Joined: Fri May 16, 2003 3:47 pm
Location: England
Contact:

Post by GedB »

The following link explains how it is done in C++, using copy-on-write:

http://www.cppinaction.com/book/tech/6lib.html

Good a similar scheme be used in Purebasic, Fred?

Perhaps the compiler could flag strings within copied structures. When one of these is written too then a new string is allocated and just that structure is updated. I'm sure all of this could be done at compile time with no runtime cost.
Karbon
PureBasic Expert
PureBasic Expert
Posts: 2010
Joined: Mon Jun 02, 2003 1:42 am
Location: Ashland, KY
Contact:

Post by Karbon »

The problem is that the struct only stores the pointer to the string (long value, 4 bytes) so you are never actually copying the data - only a pointer!

According to Bericko there is no solution as yet, that's why I suggested the feature!
-Mitchell
Check out kBilling for all your billing software needs!
http://www.k-billing.com
Code Signing / Authenticode Certificates (Get rid of those Unknown Publisher warnings!)
http://codesigning.ksoftware.net
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

What am I missing here?
Sooo ..

.. when the string is updated, it is moved and the pointers now fail.

The deafening sound you just heard was the world's biggest penny dropping. :? :)
Froggerprogger
Enthusiast
Enthusiast
Posts: 423
Joined: Fri Apr 25, 2003 5:22 pm
Contact:

Post by Froggerprogger »

Okay, I had the time and made a workaround:

The following procedure can duplicate a structure including all data-types, arrays and further structures, but it has one important limitation:
It is designed is this way, that you must specify an offset-position, before that all data is interpretated as numerical values (so it's just copied), and all further data is interpretated as stringpointers.
So you have to sort your structure in this way, that first all numeric data is mentioned, and then all the strings.
(Only if you definitely want to copy the stringpointer, not the stringdata, you could left this string inside the 'numerical' area)



The biggest problem was, that pb doesn't simply allocate fixed 64k for each string, but handles the position and the allocated length dynamically. So you could easily ran into problems with using PokeS() on Stringspointer that show to shorter strings. e.g. the following crashes:

Code: Select all

str.s = "test"
PokeS(@str, Space(50000))
The trick now is, that the procedure calls the Space()-function on the targetstring before calling PokeS(). But to keep it general this had to happen from ASM, because we cannot explicite call the string's variablename.
So, blahblah, here it is (it's not THAT much code, the most of it is the example and comments)

Code: Select all

Declare CopyStructure(*posfrom.l, *posto.l, structuresize.l, stringoffset.l)

;- CopyStructure(*posfrom.l, *posto.l, structuresize.l, stringoffset.l)
;-   copies the content of one structure to another of same type
;-   It may include all types inclusive strings, arrays and other structures.
;-   IMPORTANT: The whole structure (at it's 'highest level' - if it contains also structures) 
;-   must be sorted in this way, that first >all< the numeric values are listed,
;-   which are then followed by >all< the strings. 
;-
;-   *posfrom = pointer to the structure you want to copy from
;-   *posto = pointer to the structure/memoryarea you want to copy to
;-   structuresize = SizeOf('Structure')
;-   stringoffset = OffsetOf(FirstStringInStructure) In fact you may use a higher value than this,
;-                  if you explicitely want to copy only the pointer to the string, so that it would not
;-                  be duplicated.

dummystring.s = Space(0) ;this one is NECCESSARY unless you call the Space()-function anywhere else in your code
Procedure CopyStructure(*posfrom.l, *posto.l, structuresize.l, stringoffset.l)
  Protected tempS.s, tempSLen.l, *actposto.l ; [esp+20], [esp+24], [esp+28]
  
  ; first copy all the numeric values inside the structure
  CopyMemory(*posfrom, *posto, stringoffset)
  
  ; now process all the strings
  While stringoffset < structuresize
    ; read in the actual string. If it is not specified until now, interpret it as an empty string
    If PeekL(*posfrom + stringoffset) <> 0
      tempS = PeekS(PeekL(*posfrom + stringoffset))
    Else
      tempS = ""
    EndIf
    ; store it's length
    tempSLen = Len(tempS)
    ; calculate the position of the targetstring-pointer
    *actposto = *posto + stringoffset
   
    ; The following block calls the Space()-function via ASM for the target-string,
    ; that returns a pointer to a new string of sufficient allocated memory
    ; and probably deletes the previous one.
    ; If this code is left out, the program would crash in some cases (e.g. sourcesize >> allocated stringsize)
    ; for the 'PB_Space'-call-environment it is neccessary, that somewhere else in code Space() is called
    !PUSH   dword [PB_StringBase]
    !MOV    eax, dword [esp+24] ; size for Space() = tempSLen
    !CALL   PB_Space
    !MOV    ecx, [esp+28]
    !POP    edx
    !CALL   SYS_AllocateString
      
    ; now write the string to the pointer stored by the above Space()-call
    PokeS(PeekL(*actposto), tempS)
      
    stringoffset + 4 ; 4 = SizeOf(STRING)
  Wend
EndProcedure

;-
;-  an example:
;-

Structure numericOnly
  num1.l
  num2.l
EndStructure

Structure stringsOnly
  str1.s
  str2.s
EndStructure

Structure mystr
  num1.f
  num2.b
  num3.w
  num4.l
  nums.l[10]
  morenums.numericOnly  ; until here no strings
  str1.s                ; from here on no numeric values
  str2.s
  strs.s[10]
  morestr.stringsOnly
EndStructure

; define a new instance of this structure and fill it with some data
; (it is not neccessary for CopyStructure() that the values have been defined before calling it)
s1.mystr

s1\num1 = 1.111
s1\num2 = 2
s1\num3 = 3
s1\num4 = 4
For i=0 To 9
  s1\nums[i] = 5+i
Next
s1\morenums\num1 = 15
s1\morenums\num2 = 16
s1\str1 = "Hello"
s1\str2 = "World"+Space(30000)
For i=0 To 9
  s1\strs[i] = "Number "+Str(i)
Next
s1\morestr\str1 = "structure in structure string 1"
s1\morestr\str2 = "structure in structure string 2"

; define another one and fill it with some data
s2.mystr
s2\num2 = 21
s2\str2 = "Happy New Year"

; Now call the Copy-procedure
CopyStructure(@s1, @s2, SizeOf(mystr), OffsetOf(mystr, str1))

; Let's see what has 'arrived' in s2
Debug s2\num1
Debug s2\num2
Debug s2\num3
Debug s2\num4
For i=0 To 9
  Debug s2\nums[i]
Next
Debug s2\morenums\num1
Debug s2\morenums\num2
Debug s2\str1
Debug s2\str2
For i=0 To 9
  Debug s2\strs[i]
Next
Debug s2\morestr\str1
Debug s2\morestr\str2

; destroy the data in s1
Debug ""
Debug "xxxxxxxxxxxxxx     scrumbling the data in s1................."
Debug ""

For i=0 To 1000
  PokeB(@s1 + Random(SizeOf(mystr)), Random(255))
Next

; Let's see what's left in s2
Debug s2\num1
Debug s2\num2
Debug s2\num3
Debug s2\num4
For i=0 To 9
  Debug s2\nums[i]
Next
Debug s2\morenums\num1
Debug s2\morenums\num2
Debug s2\str1
Debug s2\str2
For i=0 To 9
  Debug s2\strs[i]
Next
Debug s2\morestr\str1
Debug s2\morestr\str2

End
%1>>1+1*1/1-1!1|1&1<<$1=1
Post Reply