Page 1 of 1

Copy and Swap of Structured Elements

Posted: Mon May 28, 2007 11:49 pm
by Kaeru Gaman
It would be nice to have a Swap and a Copy command for structured elements (e.g. within an array).

the swap should work via registers, not using a buffer.

should be a short and simple ASM-loop, easy to implement.

sure it demands using elements of the same structure,
but I think we could live with "professional" commands that just check for the size.

Posted: Mon May 28, 2007 11:56 pm
by AND51
I agree. :)

But, it should also work with structuredLinkedList-Elemnts and structures Variables!

Posted: Tue May 29, 2007 12:02 am
by Kaeru Gaman
erm, yes, sure.
sorry, I just pointed out the (probasble) main use.
first post edited.
anyways, the routine should be the same.
for the loop it's no matter if the pointer points into a list, an array or on a single variable.

Posted: Tue May 29, 2007 12:31 am
by AND51
Sorry, if I misunderstood you somewhere... :?

Posted: Sun Aug 19, 2007 9:17 pm
by Ollivier
Now, it's partially possible...

It's only usuable for strings' structured arrays /!\

Between two arrays (thanks kaeru :D ), I wrote(edition of the ASM commented file) this procedure named ssCopy() (String Structure Copy)

Code: Select all

Procedure ssCopy(*a,*b, Size.L) 
  Goto OnPasseLInutile 
  UnTrucCommeCa.S = UneChoseCommeCa.S 
OnPasseLInutile: 
  Global PrivateValueA0.L 
  For PrivateValueA0 = 0 To Size Step 4 
    !  MOV    ecx, dword [v_PrivateValueA0] 
    !  MOV    ebp,dword [esp + 0x10] 
    !  MOV    edx,dword [ebp + ecx] 
    !  PUSH   dword [_PB_StringBasePosition] 
    !  CALL  _SYS_CopyString@0 
    !  MOV    ecx, dword [v_PrivateValueA0] 
    !  MOV    ebp,dword [esp + 0x10]
    !  LEA    ecx,[ebp+ecx] 
    !  POP    edx 
    !  CALL   SYS_AllocateString 
  Next 
EndProcedure 

Structure ID 
  Nom.S 
  Prenom.S 
  Adresse.S 
EndStructure 

Dim Perso.ID(99) 

Perso(0)\Nom = "Durand" 
Perso(0)\Prenom = "Jack" 
Perso(0)\Adresse = "3 Rue des stockosses 25000 Fleury" 

   ;La ligne qui couine dans la boucle For ci-dessous... 
   ;Perso(I) = Perso(0) 

For I = 1 To 5 
  ssCopy(Perso(I),Perso(0),SizeOf(ID)) ; ...remplacée par ssCopy() 
Next 

   ;...Et qui équivaudrait à ça: 

; Perso(I)\Nom = Perso(0)\Nom 
; Perso(I)\Prenom = Perso(0)\Prenom 
; Perso(I)\Adresse = Perso(0)\Adresse 

; On remplace la source pour vérifier que ce ne sont pas seulement les pointeurs qui ont été copiés... 
Perso(0)\Nom = "Dupont" 
Perso(0)\Prenom = "Patrick" 
Perso(0)\Adresse = "2020 rue du fin fin" 

For Index = 0 To 5 
; ...Et on affiche le résultat  
  Debug Perso(Index)\Nom 
  Debug Perso(Index)\Prenom 
  Debug Perso(Index)\Adresse 
  Debug "" 
  
Next

Posted: Sun Aug 19, 2007 11:56 pm
by technicorn
Maybe this will help for a start:

Code: Select all

EnableExplicit

Macro SwapStructure(element1, element2, struc)
  _SwapStructure(@element1, @element2, SizeOf(struc))
EndMacro

Procedure _SwapStructure(*element1, *element2, elementSize.l)
  !mov    ecx, [p.v_elementSize]
  !mov    eax, [p.p_element1]
  !mov    edx, [p.p_element2]
  !pushfd
  !push   edi
  !push   esi
  !cld
  !mov    edi, edx
  !mov    edx, $FFFFFFFC
  !mov    esi, eax
  !and    edx, ecx
  !jz     swapstructure_no_dword
  !
  !shr    edx, 2
  !swapstructure_dword_loop:
  !mov    eax, [edi]
  !xchg   eax, [esi]
  !stosd
  !dec    edx
  !lea    esi, [esi + 4]
  !jnz    swapstructure_dword_loop
  !
  !swapstructure_no_dword:
  !and    ecx, 3
  !jz     swapstructure_no_byte
  !
  !swapstructure_byte_loop:
  !mov    al, [edi]
  !xchg   al, [esi]
  !stosb
  !dec    ecx
  !lea    esi, [esi + 1]
  !jnz    swapstructure_byte_loop
  !
  !swapstructure_no_byte:
  !pop    esi
  !pop    edi
  !popfd
EndProcedure

Structure st1
  l.l
  s.s
  d.d
EndStructure

Define i.l
Dim a1.st1(3)

a1(1)\l = 12
a1(1)\s = "Swap1"
a1(1)\d = 34

a1(2)\l = 56
a1(2)\s = "Swap2"
a1(2)\d = 78


SwapStructure(a1(1), a1(2), st1)
For i = 0 To 3
  Debug "Index " + Str(i)
  Debug a1(i)\l
  Debug "'" + a1(i)\s + "'"
  Debug a1(i)\d
  Debug ""
Next i

Debug ""
SwapStructure(a1(0), a1(2), st1)
For i = 0 To 3
  Debug "Index " + Str(i)
  Debug a1(i)\l
  Debug "'" + a1(i)\s + "'"
  Debug a1(i)\d
  Debug ""
Next i

Posted: Mon Aug 20, 2007 12:23 am
by Ollivier
Excellent!

And happy to discover your work :D

Posted: Mon Aug 20, 2007 7:21 am
by technicorn
Small improvement on the code, and I removed the "@" from the parameters,
the compiler will automaticly take the address for structures.
And now you can also swap elements of the same list by saving on liste-element address
and using this and the element you want swap directly.

But unfortunately, I can't offer a copy function,
there's no way to know what structure-element is a string,
and copying just the pointer would mean disaster.

Code: Select all

EnableExplicit

Macro SwapStructure(element1, element2, struc)
  _SwapStructure(element1, element2, SizeOf(struc))
EndMacro

Procedure _SwapStructure(*element1, *element2, elementSize.l)
  !pushfd
  !push   edi
  !push   esi
  !mov    ecx, [p.v_elementSize + 12]
  !mov    esi, [p.p_element1 + 12]
  !mov    edi, [p.p_element2 + 12]
  !cld
  !mov    edx, $FFFFFFFC
  !and    edx, ecx
  !jz     swapstructure_no_dword
  !
  !shr    edx, 2
  !swapstructure_dword_loop:
  !mov    eax, [edi]
  !xchg   eax, [esi]
  !stosd
  !dec    edx
  !lea    esi, [esi + 4]
  !jnz    swapstructure_dword_loop
  !
  !swapstructure_no_dword:
  !and    ecx, 3
  !jz     swapstructure_no_byte
  !
  !swapstructure_byte_loop:
  !mov    al, [edi]
  !xchg   al, [esi]
  !stosb
  !dec    ecx
  !lea    esi, [esi + 1]
  !jnz    swapstructure_byte_loop
  !
  !swapstructure_no_byte:
  !pop    esi
  !pop    edi
  !popfd
EndProcedure

Structure st1
  l.l
  s.s
  d.d
  b.b
EndStructure

Define i.l, *p
Dim a1.st1(3)
NewList l1.st1()

a1(1)\l = 12
a1(1)\s = "SwapArray1"
a1(1)\d = 34
a1(1)\b = -1

a1(2)\l = 56
a1(2)\s = "SwapArray2"
a1(2)\d = 78
a1(1)\b = -2

AddElement(l1())
AddElement(l1())
l1()\l = 120
l1()\s = "SwapList1"
l1()\d = 340
l1()\b = -3
AddElement(l1())
l1()\l = 560
l1()\s = "SwapList2"
l1()\d = 780
l1()\b = -4
AddElement(l1())

Debug "Swapping array elements:"
SwapStructure(a1(1), a1(2), st1)
For i = 0 To 3
  Debug "Array index " + Str(i) + ": " + Str(a1(i)\l) + ", '" + a1(i)\s + "', " + StrD(a1(i)\d) + ", " + Str(a1(i)\b)
Next i

Debug ""
SwapStructure(a1(0), a1(2), st1)
For i = 0 To 3
  Debug "Array index " + Str(i) + ": " + Str(a1(i)\l) + ", '" + a1(i)\s + "', " + StrD(a1(i)\d) + ", " + Str(a1(i)\b)
Next i

Debug "":Debug ""
Debug "Swapping list elements:"
SelectElement(l1(), 1)
; If you want to swap elements of the same list,
; you have to save on of the addresses
*p = @l1()  ; Save address for first element to swap
NextElement(l1())
SwapStructure(*p, l1(), st1)
ForEach l1()
  Debug "List index " + Str(ListIndex(l1())) + ": " + Str(l1()\l) + ", '" + l1()\s + "', " + StrD(l1()\d) + ", " + Str(l1()\b)
Next

Debug "":Debug ""
Debug "Swapping list/array elements:"
SelectElement(l1(), 1)
SwapStructure(a1(0), l1(), st1)
ForEach l1()
  Debug "List index  " + Str(ListIndex(l1())) + ": " + Str(l1()\l) + ", '" + l1()\s + "', " + StrD(l1()\d) + ", " + Str(l1()\b)
  i + 1
Next
Debug ""
For i = 0 To 3
  Debug "Array index " + Str(i) + ": " + Str(a1(i)\l) + ", '" + a1(i)\s + "', " + StrD(a1(i)\d) + ", " + Str(a1(i)\b)
Next i
Happy coding,
technicorn

Posted: Tue Aug 21, 2007 9:12 pm
by Ollivier
Thank you!

I think the real problem is the confusion between a long, a float, a pointor and a string (which is really a pointor too). Same size : 4 bytes.

One solution consists replacing the parameter 'size' by a string.
This string would be created at the same time that the structure writing.
Example:

Code: Select all

Structure Struc
Byte.B
Word.W
String.S
*Pointor
LongsTable.L[10]
EndStructure
StrucStruc.S = "BWS*L[10]"
To copy, we must specify this string 'StrucStruc' instead of specifying the global size of one field in the structure. We haven't better way.

What do you think so?

Posted: Wed Aug 22, 2007 8:05 am
by technicorn
But I think, that's more hassle to scan that string in a procedure to copy
things, than writing a special proc. for every struc. you want to copy.

To take your example:

Code: Select all

Procedure CopyStruc(*struc1.struc, *struc2.struc)
  Protected memSize.l

  *struc2\b = *struc1\b
  *struc2\w = *struc1\w
  *struc2\String = *struc1\String
  If *struc1\Pointer
    memSize = MemorySize(*struc1\Pointer)
    *struc2\Pointer = ReAllocateMemory(*struc2\Pointer, memSize)
    CopyMemory(*struc1\Pointer, *struc2\Pointer, memSize)
  Else
    FreeMemory(*struc2\Pointer)
    *struc2\Pointer = 0
  EndIf
  CopyMemory(@*struc1\LongsTable[0], @*struc2\LongsTable[0], 40)
EndProcedure
But it would make your code just more readable, not faster, indeed it would slow it down,
with to overhead of proc. calling.
It would be faster to insert that code directly where you want to copy,
you could use a Macro to save typing and make code better readable.

That would still leave the problem of a pointer in the struc. pointing to another struc. with strings in it, than this code will shurely crash your prog.

And I wouldn't even consider to touch a struc. like this with a general copy routine:

Code: Select all

Structure NoCopyStruc
  StructureUnion
    st.AnotherStruc
    s.s
    b.b
    d.d
  EndStructureUnion
EndStructure
:wink:

Posted: Wed Aug 22, 2007 4:56 pm
by Demivec
Since strings are special a solution would be to indicate which parts of the structure are strings and handle those seperately?

Code: Select all

Structure foo
  a.l
  b.l
  c.w
  d.b
  string1.s
  string2.s
  e.l
  f.d
EndStructure

numStrings=2
CopyStruc(struc1.foo,struc2.foo,OffsetOf(foo\string1),numStrings)
Or, perhaps the string copy can be handled in a seperate procedure altogether. You would first copy the structure (and its string pointers) then create new copies of the strings and overwrite the copied string pointers. This would requre having a list of which elements were strings(their offsets) in the structure similiar to what I gave above. I haven't come up with a calling method for parameters where strings are not grouped in a structure though.

Posted: Thu Aug 23, 2007 8:57 pm
by Ollivier
@technicorn

>> To speed and not to speed :D
>> 'StructureUnion' :( It's over! Like 'extends'

@demivec

I thank about 'compiling' image structure. It's near you are describing.

Syntax:

Code: Select all

*ImageStructure = Struc(StructureDescription.S)
With it, structure description string changes into image structure memory area.

For example:

1) Basic structure

Code: Select all

Structure X
Byte.B
Word.W
StringA.S
StringB.S
LongA.L
LongB.L
String.S[10]
Long.L[3]
EndStructure
2) Structure Description String (That the programmer must recreate from basic structure)

Code: Select all

DescStruc.S = "BWSSLLS[10]L[3]"
3) Structure image (Stored in area memory) created by the procedure Struc()
[long]

Code: Select all

[Size of the structure (bytes) ]
[Number of string areas]
Area #1:
[Number of strings]
[Offset of the first string]
Area #2:
[Number of strings]
[Offset of the first string]
...
Area #n
[Number of strings]
[Offset of the first string]

Posted: Thu Aug 23, 2007 9:29 pm
by Ollivier
@demivec

Your solution is the best. I don't see other choice.

Posted: Sun Aug 26, 2007 8:20 am
by Ollivier
Now, there's the copy procedure named sCopy().
As Technicorn said, speed of its execution is not as fast as a CopyMemory() but it can be used in lots of applications.

Code: Select all

Procedure Struc(Input.S) 
  Structure StringAreaStruc
    AreaStringQty.L
    AreaOffset.L
  EndStructure
  Input = UCase(Input) 
  Static Intermed.S = "" 
  Static LInput.L 
  Static Char.S{1} 
  Static Actual.S{1} 
  Static Number.S = "" 
  Static DuppMode.L 
  Static i.L 
  Static j.L 
  Static Output.S
  LInput = Len(Input) 
  For i = 1 To LInput 
    Char = Mid(Input, i, 1) 
    If Char = "]" 
      DuppMode = 0 
      Actual = Right(Intermed, 1) 
      For j = 2 To Val(Number) 
        Intermed + Actual 
      Next 
      Number = "" 
    Else 
      If Char = "["
        DuppMode = 1 
      Else 
        If DuppMode 
          Number + Char 
        Else 
          Intermed + Char 
        EndIf 
      EndIf 
    EndIf 
  Next
  Global NewList Area.StringAreaStruc()
  Static StructureSize.L
  Static IsString.L  
  Static StringQty.L
  Static StringAreaQty.L
  StringAreaQty = 0
  Input = Intermed
  LInput = Len(Input) 
  For i = 1 To LInput 
    Char = Mid(Input, i, 1)
    If Char <> "S"
      IsString = 0
    EndIf
    Select Char
      Case "B"
        StructureSize + 1
      Case "W"
        StructureSize + 2
      Case "F"
        StructureSize + 4
      Case "L"
        StructureSize + 4
      Case "D"
        StructureSize + 8
      Case "Q"
        StructureSize + 8
      Case "*"
        StructureSize + 4
      Case "S"
        If IsString = 0
          If i = LInput
            StringQty = 1
          EndIf 
          For j = i + 1 To LInput
            If Mid(Input, j, 1) <> "S"
              StringQty = j - i
              Break
            EndIf
            If j = LInput
              StringQty = (LInput - i) + 1             
            EndIf
          Next
          AddElement(Area() )
          Area()\AreaStringQty = StringQty
          Area()\AreaOffset = StructureSize
          StringAreaQty + 1
          IsString = 1
          StringQty = 0
        EndIf
        StructureSize + 4      
    EndSelect 
  Next
  *Output = AllocateMemory(8 + StringAreaQty << 3)
  *Start = *Output
  PokeL(*Start, StructureSize)
  *Start + 4
  PokeL(*Start, StringAreaQty)
  *Start + 4
  ForEach Area()
    PokeL(*Start, Area()\AreaStringQty)
    *Start + 4
    PokeL(*Start, Area()\AreaOffset)
    *Start + 4
  Next
  ClearList(Area() )
  ProcedureReturn *Output  
EndProcedure

Procedure ssCopy(*a,*b) 
Global ssCopyOffset.L 
  Goto OnPasseLInutile 
  UnTrucCommeCa.S = UneChoseCommeCa.S 
OnPasseLInutile: 
    !  MOV    ecx, dword [v_ssCopyOffset] 
    !  MOV    ebp,dword [esp+16] 
    !  MOV    edx,dword [ebp+ecx] 
    !  PUSH   dword [_PB_StringBasePosition] 
    !  CALL  _SYS_CopyString@0 
    !  MOV    ecx, dword [v_ssCopyOffset]
    !  MOV    ebp,dword [esp+16] 
    !  LEA    ecx,[ebp+ecx] 
    !  POP    edx  
    !  CALL   SYS_AllocateString 
EndProcedure 

Procedure sCopy(*Source, *Dest, *x)
  *EndArea = 0
  *Start = *x + 8
  For Area = 1 To PeekL(*x + 4)
    CopyMemory(*Source + *EndArea, *Dest + *EndArea, PeekL(*Start + 4) - *EndArea)  
    *StartString = PeekL(*Start + 4)
    For String = 0 To PeekL(*Start) - 1
      ssCopyOffset = *StartString + String << 2
      ssCopy(*Dest, *Source)
    Next
    *EndArea = PeekL(*Start + 4) + 4 * PeekL(*Start)
    *Start + 8
  Next
 CopyMemory(*Source + *EndArea, *Dest + *EndArea, PeekL(*x) - *EndArea)  
EndProcedure

Example:
Structure Example
  ID.L
  Age.B
  xx.B
  yy.W
  Hobby.S[3]
  Born.L
  Comment.S
  Num.L
EndStructure
  *Ex = Struc("LBBWS[3]LSL")

Dim St.Example(3)
  St(0)\ID = 321
  St(0)\Age = 29
  
  St(0)\Hobby[0] = "Foot"
  St(0)\Hobby[1] = "Hand"
  St(0)\Hobby[2] = "Basket"
  St(0)\born = 1978
  St(0)\Comment = "No more informations"
  St(0)\Num = 303030
  
  sCopy(St(0), St(1), *Ex) ;<< Copy of the structure (Source, Dest and structure description pointor)
  St(0)\Comment = "Other comment (modified after copy)"
  St(0)\born = 1973
  For i = 0 To 1
  Debug "St(" + Str(i) + "):"
  Debug St(i)\ID
  Debug St(i)\Age
  Debug St(i)\Hobby[0]
  Debug St(i)\Hobby[1]
  Debug St(i)\Hobby[2]
  Debug St(i)\born
  Debug St(i)\Comment
  Debug St(i)\Num
  Debug " "
  Next