SRL Archive - RS3 Help

[LAPE] Memory leak, help!


bg5

2015-04-04 04:21:41

So, I'm dealing with one function:
procedure QuickSortArray (var AnyArray : Pointer; SizeOf_Index:integer; Pattern : TIntegerArray);
var
SoP,hArr,hP,i,j : integer;
tempArray : Pointer;
begin
Sop := sizeOf(Pointer);
hArr := integer((AnyArray-sop)^);
hP := High(Pattern);
if hArr <> hP then
begin
writeln('SortArray: Error: length of array is '+tostr(hArr+1)+' while length of pattern is '+tostr(hP+1));
exit;
end;

tempArray := AllocMem(SizeOf_Index*(hArr+1)); // temp copy of array
for i:=0 to hArr do
for j:=0 to SizeOf_Index-1 do
PtrByte(tempArray+(SizeOf_Index*i)+j)^ := PtrByte(AnyArray+(SizeOf_Index*i)+j)^;

for i:=0 to hArr do
for j:=0 to SizeOf_Index-1 do
PtrByte(AnyArray+(SizeOf_Index*i)+j)^ := PtrByte(tempArray+(SizeOf_Index*Pattern[i])+j)^;

writeln(SizeOf_Index*(hArr+1));
FreeMemSize(tempArray,SizeOf_Index*(hArr+1));
end;

and I've noticed memory leak of the size of an Array. Yes, this function works properly, except it's leaking. After some testing I've found out that if you comment out this section leak is gone:
 { for i:=0 to hArr do                          
for j:=0 to SizeOf_Index-1 do
PtrByte(AnyArray+(SizeOf_Index*i)+j)^ := PtrByte(tempArray+(SizeOf_Index*Pattern[i])+j)^; }

but I have no idea if it's original array or copy not being freed and also why this piece of code causes leaking.


Test script:

[SPOILER=Test script:]

program new;
type TCustomType = record
name:string;
TPA :TPointArray;
TSA : TStringArray;
ex:extended;
end;

type TCustomTypeArray = array of TcustomType;

procedure TCustomTypeArray.Create(size : integer);
var i,j :integer;
begin
SetLength(self,size);
for i:=0 to size-1 do
begin
self[i].name := tostr(i);
SetLength(self[i].TPA,i+1);
SetLength(self[i].TSA,i+1);
for j:=0 to i do
begin
self[i].TPA[j] := Point(i,j);
self[i].TSA[j] := 'MyName '+tostr(j);
end;
end;
// writeln(self);
end;
type PtrByte = ^byte;
procedure QuickSortArray (var AnyArray : Pointer; SizeOf_Index:integer; Pattern : TIntegerArray);
var
SoP,hArr,hP,i,j : integer;
tempArray : Pointer;
begin
Sop := sizeOf(Pointer);
hArr := integer((AnyArray-sop)^);
hP := High(Pattern);
if hArr <> hP then
begin
writeln('SortArray: Error: length of array is '+tostr(hArr+1)+' while length of pattern is '+tostr(hP+1));
exit;
end;

tempArray := AllocMem(SizeOf_Index*(hArr+1));
for i:=0 to hArr do
for j:=0 to SizeOf_Index-1 do
PtrByte(tempArray+(SizeOf_Index*i)+j)^ := PtrByte(AnyArray+(SizeOf_Index*i)+j)^;

for i:=0 to hArr do
for j:=0 to SizeOf_Index-1 do
PtrByte(AnyArray+(SizeOf_Index*i)+j)^ := PtrByte(tempArray+(SizeOf_Index*Pattern[i])+j)^;

writeln(SizeOf_Index*(hArr+1));
FreeMemSize(tempArray,SizeOf_Index*(hArr+1));
end;

var a,b: TCustomTypeArray;
TIA :TIntegerarray;

begin
a.Create(300);
setLength(TIA,300);
QuickSortArray(Pointer(a),sizeof(a[0]),TIA);

{
b.Create(4);
writeln(b);
QuickSortArray(Pointer(b),sizeof(b[0]),[3,1,2,0]);
writeln(b);
}
end.

[/SPOILER]

Please help ;)

Brandon

2015-04-04 09:35:26

From: bg5
..


That's not how you copy a structure at the byte/binary level.. If you separate the responsibilities to another function, you'll see the difference. You might have been overwriting the book-keeping that AllocMem does. You also should not be doing arithmetic on a void/opaque pointer. You need to cast it to a type and then do the arithmetic. It's undefined behaviour in many other languages. Not sure about Lape.

In other words:

var
ptr: Pointer;
begin
ptr := AllocMem(256);

//ptr + blah; What's the size of Ptr's elements? Is it a pointer to an int? a byte? a float? what?
//how much will it increase? sizeof(int) * blah? sizeof(byte) * blah?
//In lape it seems to increase by 1 no matter the pointer type. Simply horrible.

FreeMem(ptr);
end;


but then consider:


var
ptr: Pointer;
pitr: ^Integer;
TIA: TIntegerArray;
begin
TIA := [1, 2, 3, 4, 5];
ptr := @TIA[0];

Inc(ptr, sizeof(TIA[0]));
writeln(Integer(ptr^)); //prints second element.

pitr := @TIA[0];
Inc(pitr, sizeof(TIA[0]));

writeln(pitr^); //prints last element.
end.

One increments by sizeof(byte).. the other increments by sizeof(ptr^).. Without being very careful, I'd have gotten lost in your code as you're increasing an opaque pointer. A pointer with no size and somehow Lape thinks it's okay to increase by 1 instead of giving an exception.


Anyway, use the following to copy to the tempArray and final array..


Func:

procedure ByteCopy(a, b: ^UInt8; size_ptr: UInt32);
var
I: Integer;
begin
for I := 0 To size_ptr - 1 do
begin
b^ := a^;
inc(a, sizeof(a^));
inc(b, sizeof(b^));
end;
end;


Modified code (hopefully I got the logic right):

program new;
type TCustomType = record
name:string;
TPA :TPointArray;
TSA : TStringArray;
ex:extended;
end;

type TCustomTypeArray = array of TcustomType;

procedure TCustomTypeArray.Create(size : integer);
var i,j :integer;
begin
SetLength(self,size);
for i:=0 to size-1 do
begin
self[i].name := tostr(i);
SetLength(self[i].TPA,i+1);
SetLength(self[i].TSA,i+1);
for j:=0 to i do
begin
self[i].TPA[j] := Point(i,j);
self[i].TSA[j] := 'MyName '+tostr(j);
end;
end;
// writeln(self);
end;

type PtrByte = ^byte;


procedure ByteCopy(a, b: ^UInt8; size_ptr: UInt32);
var
I: Integer;
begin
for I := 0 To size_ptr - 1 do
begin
b^ := a^;
inc(a, sizeof(a^));
inc(b, sizeof(b^));
end;
end;

procedure QuickSortArray (var AnyArray : Pointer; SizeOf_Index:integer; Pattern : TIntegerArray);
var
SoP,hArr,hP,i,j : integer;
tempArray : PtrByte;
begin
Sop := sizeOf(Pointer); //4
hArr := integer((AnyArray-sop)^); //array size - 1.. Aka "high(AnyArray)".
hP := High(Pattern);
if hArr <> hP then
begin
writeln('SortArray: Error: length of array is '+tostr(hArr+1)+' while length of pattern is '+tostr(hP+1));
exit;
end;

tempArray := AllocMem(SizeOf_Index*(hArr+1)); //allocating hArray amount of Type elements.

for I := 0 to hArr do
ByteCopy(AnyArray, tempArray, SizeOf_Index); //copy each index of the AnyArray to the TempArray.. byte for byte.

for I := 0 to hArr do
ByteCopy(tempArray[Pattern[I]], AnyArray, SizeOf_Index);

FreeMem(tempArray);
end;

var a,b: TCustomTypeArray;
TIA :TIntegerarray;
I: Integer;

begin
a.Create(300);
setLength(TIA,300);
for I:= 0 to high(TIA) do
TIA[I] := I;

QuickSortArray(Pointer(a),sizeof(a[0]),TIA);
end.


I've written something similar before: http://pastebin.com/ta6R5QXt

bg5

2015-04-04 14:30:37

One increments by sizeof(byte).. the other increments by sizeof(ptr^).. Without being very careful, I'd have gotten lost in your code as you're increasing an opaque pointer. A pointer with no size and somehow Lape thinks it's okay to increase by 1 instead of giving an exception.


Yea, but everything is under control :D


I've run you code with:

begin
a.Create(3);
setLength(TIA,3);
for I:= 0 to high(TIA) do
TIA[I] := I;

QuickSortArray(Pointer(a),sizeof(a[0]),TIA);
writeln(a);
end.

gives me:

Exception TLPThread.Destroy: Access violation
Compiled successfully in 452 ms.
ERROR comes from a non-existing file (!addGlobalFuncBdy)
Error: Access violation at line 1
Execution failed
Exception TLPThread.Destroy: Access violation




procedure ByteCopy(a, b: ^UInt8; size_ptr: UInt32);
var
I: Integer;
begin
for I := 0 To size_ptr - 1 do
begin
b^ := a^;
inc(a, sizeof(a^)); // pointer is increased by size of index...so it's pointing to next index, not to next byte
inc(b, sizeof(b^));
end;
end;


What I think this function does, it copies X indexes forward ,where X = size_ptr . Propably it should do something else :p



if you create an array with AllocMem and later call
tempArray[i]
how compiler might know where is an index i ? It seems it made some presumption about sizeOf(i) ,but rather it shouldn't compile.

@Brandon

Brandon

2015-04-04 15:15:45

From: bg5
..


The access violation is probably from this line: ByteCopy(tempArray[Pattern], AnyArray, SizeOf_Index);

and not the ByteCopy function itself. tempArray was changed from pointer to byte pointer in the declaration: var tempArray: ^byte;
The compiler thinks its a byte array.

I probably messed up the arithmetic on that line OR the logic for the sort function itself is incorrect.



//a, b is a pointer to a UInt8 aka unsigned int8 aka byte.

procedure ByteCopy(a, b: ^UInt8; size_ptr: UInt32);
var
I: Integer;
begin
for I := 0 To size_ptr - 1 do //copy a whole index.. however large an index is. If an element is int, we loop 4 times to copy 4 bytes. If it's a long, we loop 8 times to copy 8 bytes. Effectively copying a to b.
begin
b^ := a^;
inc(a, sizeof(a^)); //Points to the next byte not index. sizeof(a) is 4. sizeof(a^) is 1. "a^" is a byte.
inc(b, sizeof(b^)); //Incrementing a byte pointer by sizeof(byte) will increment it by 1.
end;
end;



See here that it works:


procedure ByteCopy(a, b: ^UInt8; size_ptr: UInt32);
var
I: Integer;
begin
for I := 0 To size_ptr - 1 do
begin
b^ := a^;
inc(a, sizeof(a^));
inc(b, sizeof(b^));
end;
end;

procedure QuickSortArray (var AnyArray : Pointer; SizeOf_Index:integer; Pattern : TIntegerArray);
var
hArr, hP, i: integer;
tempArray : ^Byte;
begin
hArr := integer((AnyArray-sop)^); //array size - 1.. Aka "high(AnyArray)".
hP := High(Pattern);
if hArr <> hP then
begin
writeln('SortArray: Error: length of array is '+tostr(hArr+1)+' while length of pattern is '+tostr(hP+1));
exit;
end;

tempArray := AllocMem(SizeOf_Index*(hArr+1)); //allocating hArray amount of Type elements.

for I := 0 to hArr do
ByteCopy(AnyArray + (I * sizeof_Index), tempArray + (I * sizeof_Index), SizeOf_Index); //copy each index of the AnyArray to the TempArray.. byte for byte.

for I := 0 to hArr do
ByteCopy(tempArray + (Pattern[I] * sizeof_Index), AnyArray + (I * sizeof_Index), SizeOf_Index);

FreeMem(tempArray);
end;

var
A, TIA: TIntegerArray;
B: Array of Char;
begin
A := [3, 4, 2, 1, 5, 9, 7, 6, 8]; //Unsorted array.
TIA := [3, 2, 0, 1, 4, 7, 6, 8, 5]; //Array used to sort the above array "A".

QuickSortArray(@A[0], sizeof(a[0]), TIA); //Sort "A" using "TIA".

writeln(A); //Print our sorted array.


//------------------------------------------//

B := ['D', 'B', 'A', 'C']; //Unsorted Array.
TIA := [2, 1, 3, 0]; //Array used to sort the above array "B".

QuickSortArray(@B[0], sizeof(b[0]), TIA); //Sort array "B" using array "TIA".

writeln(B);
end.


Now that it works, you can try sorting other data types like your structure.

bg5

2015-04-04 15:36:31

From: Brandon

//Points to the next byte not index. sizeof(a) is 4. sizeof(a^)


Yeah right, I've messed it up.

Now, I've changed it to:
(just added indexes to iterations)
ByteCopy(AnyArray+SizeOf_Index*I, tempArray+SizeOf_Index*I, SizeOf_Index);



type TCustomType = record
name:string;
TPA :TPointArray;
TSA : TStringArray;
ex:extended;
end;

type TCustomTypeArray = array of TcustomType;

procedure TCustomTypeArray.Create(size : integer);
var i,j :integer;
begin
SetLength(self,size);
for i:=0 to size-1 do
begin
self[i].name := tostr(i);
SetLength(self[i].TPA,i+1);
SetLength(self[i].TSA,i+1);
for j:=0 to i do
begin
self[i].TPA[j] := Point(i,j);
self[i].TSA[j] := 'MyName '+tostr(j);
end;
end;
// writeln(self);
end;

type PtrByte = ^byte;


procedure ByteCopy(a, b: ^UInt8; size_ptr: UInt32);
var
I: Integer;
begin
for I := 0 To size_ptr - 1 do
begin
b^ := a^;
inc(a, sizeof(a^));
inc(b, sizeof(b^));
end;
end;

procedure QuickSortArray (var AnyArray : Pointer; SizeOf_Index:integer; Pattern : TIntegerArray);
var
SoP,hArr,hP,i,j : integer;
tempArray : PtrByte;
begin
Sop := sizeOf(Pointer); //4
hArr := integer((AnyArray-sop)^); //array size - 1.. Aka "high(AnyArray)".
hP := High(Pattern);
if hArr <> hP then
begin
writeln('SortArray: Error: length of array is '+tostr(hArr+1)+' while length of pattern is '+tostr(hP+1));
exit;
end;

tempArray := AllocMem(SizeOf_Index*(hArr+1)); //allocating hArray amount of Type elements.

for I := 0 to hArr do
ByteCopy(AnyArray+SizeOf_Index*I, tempArray+SizeOf_Index*I, SizeOf_Index); //copy each index of the AnyArray to the TempArray.. byte for byte.

for I := 0 to hArr do
ByteCopy(tempArray+SizeOf_Index*I, AnyArray+SizeOf_Index*I, SizeOf_Index);

FreeMem(tempArray);
end;

var a,b: TCustomTypeArray;
TIA :TIntegerarray;
I: Integer;

begin
a.Create(300);
setLength(TIA,300);
for I:= 0 to high(TIA) do
TIA[I] := high(TIA)-I;

QuickSortArray(Pointer(a),sizeof(a[0]),TIA);

end.

And it works great, no leak! :)
Thanks.

Anyway I still don't understand why putting copy byte-to-byte operation in separated function helped. Inside my function I did exactly the same thing using j loop.

Brandon

2015-04-04 16:00:10

From: bg5
..


Dunno either.. This works just fine too:


procedure QuickSortArray (var AnyArray : Pointer; SizeOf_Index:integer; Pattern : TIntegerArray);
type
pByte = ^Byte;
var
SoP,hArr,hP,i,j : integer;
tempArray : ^Byte;
begin
Sop := sizeOf(Pointer); //4
hArr := integer((AnyArray-sop)^); //array size - 1.. Aka "high(AnyArray)".
hP := High(Pattern);
if hArr <> hP then
begin
writeln('SortArray: Error: length of array is '+tostr(hArr+1)+' while length of pattern is '+tostr(hP+1));
exit;
end;

tempArray := AllocMem(SizeOf_Index*(hArr+1)); //allocating hArray amount of Type elements.

for I := 0 to hArr do
for J := 0 to sizeof_index - 1 do
PByte(tempArray + (I * sizeof_Index + J))^ := PByte(AnyArray + (I * sizeof_Index + J))^; //copy each index of the AnyArray to the TempArray.. byte for byte.

for I := 0 to hArr do
for J := 0 to sizeof_index - 1 do
PByte(AnyArray + (I * sizeof_Index + J))^ := PByte(tempArray + (Pattern[I] * sizeof_Index + J))^;

FreeMem(tempArray);
end;

var
A, TIA: TIntegerArray;
B: Array of Char;
begin
A := [3, 4, 2, 1, 5, 9, 7, 6, 8];
TIA := [3, 2, 0, 1, 4, 7, 6, 8, 5];

QuickSortArray(@A[0], sizeof(a[0]), TIA);

writeln(A);


B := ['D', 'B', 'A', 'C'];
TIA := [2, 1, 3, 0];

QuickSortArray(@B[0], sizeof(b[0]), TIA);

writeln(B);
end.


but for your structure, it leaks bad..


EDIT:

The above works with the following test:

type TCustomType = record
name:string;
TPA :TPointArray;
TSA : TStringArray;
ex:extended;
end;

type TCustomTypeArray = array of TcustomType;

procedure TCustomTypeArray.Create(size : integer);
var i,j :integer;
begin
SetLength(self,size);
for i:=0 to size-1 do
begin
self[i].name := tostr(i);
SetLength(self[i].TPA,i+1);
SetLength(self[i].TSA,i+1);
for j:=0 to i do
begin
self[i].TPA[j] := Point(i,j);
self[i].TSA[j] := 'MyName '+tostr(j);
end;
end;
// writeln(self);
end;

procedure QuickSortArray (var AnyArray : Pointer; SizeOf_Index:integer; Pattern : TIntegerArray);
type
pByte = ^Byte;
var
SoP,hArr,hP,i,j : integer;
tempArray : ^Byte;
begin
Sop := sizeOf(Pointer); //4
hArr := integer((AnyArray-sop)^); //array size - 1.. Aka "high(AnyArray)".
hP := High(Pattern);
if hArr <> hP then
begin
writeln('SortArray: Error: length of array is '+tostr(hArr+1)+' while length of pattern is '+tostr(hP+1));
exit;
end;

tempArray := AllocMem(SizeOf_Index*(hArr+1)); //allocating hArray amount of Type elements.

for I := 0 to hArr do
for J := 0 to sizeof_index - 1 do
PByte(tempArray + (I * sizeof_Index + J))^ := PByte(AnyArray + (I * sizeof_Index + J))^; //copy each index of the AnyArray to the TempArray.. byte for byte.

for I := 0 to hArr do
for J := 0 to sizeof_index - 1 do
PByte(AnyArray + (I * sizeof_Index + J))^ := PByte(tempArray + (Pattern[I] * sizeof_Index + J))^;

FreeMem(tempArray);
end;

var a,b: TCustomTypeArray;
TIA :TIntegerarray;
I, J: Integer;
begin
for J := 0 to 1000 do
begin
a.Create(300);
setLength(TIA,300);
for I:= 0 to high(TIA) do
TIA[I] := high(TIA)-I;

QuickSortArray(Pointer(a),sizeof(a[0]),TIA);
end;
end.


Must be someone really specific on the OP. I can't see it :S Only thing I see is FreeMem(tempArray); vs. FreeMemSize(tempArray, size); but it seems to make no difference..


EDIT: I see it. Your TIA in the OP isn't set to valid values. All values are "0".

bg5

2015-04-04 16:26:18

From: Brandon
EDIT: I see it. You're swapping index 0 every time. Your TIA in the OP isn't set to valid values. All values are "0".


I know it. In this case function should just fill whole array with value from index 0.

[SPOILER=Empty TIA and no leak]


type TCustomType = record
name:string;
TPA :TPointArray;
TSA : TStringArray;
ex:extended;
end;

type TCustomTypeArray = array of TcustomType;

procedure TCustomTypeArray.Create(size : integer);
var i,j :integer;
begin
SetLength(self,size);
for i:=0 to size-1 do
begin
self[i].name := tostr(i);
SetLength(self[i].TPA,i+1);
SetLength(self[i].TSA,i+1);
for j:=0 to i do
begin
self[i].TPA[j] := Point(i,j);
self[i].TSA[j] := 'MyName '+tostr(j);
end;
end;
// writeln(self);
end;

type PtrByte = ^byte;


procedure ByteCopy(a, b: ^UInt8; size_ptr: UInt32);
var
I: Integer;
begin
for I := 0 To size_ptr - 1 do
begin
b^ := a^;
inc(a, sizeof(a^));
inc(b, sizeof(b^));
end;
end;

procedure QuickSortArray (var AnyArray : Pointer; SizeOf_Index:integer; Pattern : TIntegerArray);
var
SoP,hArr,hP,i,j : integer;
tempArray : PtrByte;
begin
Sop := sizeOf(Pointer); //4
hArr := integer((AnyArray-sop)^); //array size - 1.. Aka "high(AnyArray)".
hP := High(Pattern);
if hArr <> hP then
begin
writeln('SortArray: Error: length of array is '+tostr(hArr+1)+' while length of pattern is '+tostr(hP+1));
exit;
end;

tempArray := AllocMem(SizeOf_Index*(hArr+1)); //allocating hArray amount of Type elements.

for I := 0 to hArr do
ByteCopy(AnyArray+SizeOf_Index*I, tempArray+SizeOf_Index*I, SizeOf_Index); //copy each index of the AnyArray to the TempArray.. byte for byte.

for I := 0 to hArr do
ByteCopy(tempArray+SizeOf_Index*I, AnyArray+SizeOf_Index*I, SizeOf_Index);

FreeMem(tempArray);
end;

var a,b: TCustomTypeArray;
TIA :TIntegerarray;
I: Integer;

begin
a.Create(300);
setLength(TIA,300);


QuickSortArray(Pointer(a),sizeof(a[0]),TIA);

end.
[/SPOILER]

,but yes, in those functions you posted above lack of initialisation really seems to cause leak, which disapeares when TIA is filled. I really don't know what to say, it must be something accidental, because it shouldn't have any influence. The algorith is very simple

tempArray := Copy(Array);
for i:=0 to High(Array)
Array[i] := tempArray[TIA[i]];


I can't see anything which would cause a problem.




@down

This function has drained my brain enough, I can't look at it anymore. But you know you would loss your money :redface:

[SPOILER=Pointers are not overwritten]
program new;
type TCustomType = record
name:string;
TPA :TPointArray;
TSA : TStringArray;
ex:extended;
end;

type TCustomTypeArray = array of TcustomType;

procedure TCustomTypeArray.Create(size : integer);
var i,j :integer;
begin
SetLength(self,size);
for i:=0 to size-1 do
begin
self[i].name := tostr(i);
SetLength(self[i].TPA,i+1);
SetLength(self[i].TSA,i+1);
for j:=0 to i do
begin
self[i].TPA[j] := Point(i,j);
self[i].TSA[j] := 'MyName '+tostr(j);
end;
end;
// writeln(self);
end;
type PtrByte = ^byte;
procedure QuickSortArray (var AnyArray : Pointer; SizeOf_Index:integer; Pattern : TIntegerArray);
var
SoP,hArr,hP,i,j : integer;
tempArray : Pointer;
begin
writeln(@AnyArray);
Sop := sizeOf(Pointer);
hArr := integer((AnyArray-sop)^);
hP := High(Pattern);
if hArr <> hP then
begin
writeln('SortArray: Error: length of array is '+tostr(hArr+1)+' while length of pattern is '+tostr(hP+1));
exit;
end;

tempArray := AllocMem(SizeOf_Index*(hArr+1));
writeln(@tempArray);

for i:=0 to hArr do
for j:=0 to SizeOf_Index-1 do
PtrByte(tempArray+(SizeOf_Index*i)+j)^ := PtrByte(AnyArray+(SizeOf_Index*i)+j)^;

for i:=0 to hArr do
for j:=0 to SizeOf_Index-1 do
PtrByte(AnyArray+(SizeOf_Index*i)+j)^ := PtrByte(tempArray+(SizeOf_Index*Pattern[i])+j)^;

writeln(@tempArray);
writeln(@AnyArray);
FreeMemSize(tempArray,SizeOf_Index*(hArr+1));
end;


var a,b: TCustomTypeArray;
TIA :TIntegerarray;
i :integer;
begin
a.Create(3);
setLength(TIA,3);

writeln(@a);
QuickSortArray(Pointer(a),sizeof(a[0]),TIA);
writeln(@a);

end.

[/SPOILER]

Propably we would be able to debug it better if there was something like GetLastError() in Lape

Brandon

2015-04-04 16:37:33

From: bg5
…


That code works fine. I can't see why it access violates without the extra function. But if I had to guess, I'd put my money on the pointer being overwritten or the arithmetic not being the same.

Either way, for now use the ByteCopy function and if I or someone else figures it out, they can post why the OP function isn't the same.