Recent

Author Topic: [SOLVED]How to convert an Integer Array to a Byte Array use the same memory?  (Read 8941 times)

tomitomy

  • Full Member
  • ***
  • Posts: 223
这里可以说中文吗?我的英语很差。

我想把一个整数数组存入数据库,但是数据库只接受 TBytes,请问如何把整数数组转换为字节数组?

为了提高读写速度,我不想申请新的内存,而是直接使用整数数组的内存,这可以实现吗?

我目前的做法是直接在 TBytes 中读写整数,不过这样的代码不清晰,很难读,是否有更好的方法?

下面是我用的方法:

Code: Pascal  [Select][+][-]
  1. function GetIntFromBytes(index: integer; bytes: TBytes): integer;
  2. begin
  3.   Result := PInteger(@bytes[index * Sizeof(integer)])^;
  4. end;
  5.  
  6. procedure SetIntToBytes(index: integer; bytes: TBytes; data: integer);
  7. begin
  8.   PInteger(@bytes[index * Sizeof(integer)])^ := data;
  9. end;
  10.  
  11. function CountIntsInBytes(bytes: TBytes): integer;
  12. begin
  13.   Result := Length(bytes) div Sizeof(integer);
  14. end;
  15.  
  16. function InsertIntIntoBytes(data: integer; refData: integer; bytes: TBytes;
  17.   before: boolean = True): TBytes;
  18. var
  19.   i, j: integer;
  20. begin
  21.   // Get insert point
  22.   for i := 0 to CountIntsInBytes(bytes) - 1 do
  23.     if GetIntFromBytes(i, bytes) = refData then
  24.       Break;
  25.   // Resize TBytes
  26.   SetLength(bytes, (CountIntsInBytes(bytes) + 1) * Sizeof(integer));
  27.   // Move data
  28.   if not before then
  29.     i := i + 1;
  30.   for j := CountIntsInBytes(bytes) - 1 downto i + 1 do
  31.     SetIntToBytes(j, bytes, GetIntFromBytes(j - 1, bytes));
  32.   // Insert data
  33.   SetIntToBytes(i, bytes, data);
  34.   Result := bytes;
  35. end;
  36.  
  37. procedure Test;
  38. var
  39.   i: integer;
  40.   bytes: TBytes;
  41. begin
  42.   bytes := InsertIntIntoBytes(1, 0, bytes);
  43.   bytes := InsertIntIntoBytes(2, 1, bytes);
  44.   bytes := InsertIntIntoBytes(3, 1, bytes);
  45.   bytes := InsertIntIntoBytes(4, 1, bytes, False);
  46.   bytes := InsertIntIntoBytes(5, 1, bytes, False);
  47.   for i := 0 to CountIntsInBytes(bytes) - 1 do
  48.     ShowMessage(IntToStr(GetIntFromBytes(i, bytes)));
  49. end;
           
------------------------------------------------------------

Translated by Google:

Can I speak Chinese here? My English is poor.

I want to put an integer array into a database, but the database only accept TBytes, how to convert an integer array into a byte array?

In order to improve read and write speed, I do not want to allocate new memory, but direct use the momory of integer array, which can be achieved?

My current practice is to read and write integers directly in TBytes, but this code is not clear, it is difficult to read, is there a better way?

Here's my practice:

Code: Pascal  [Select][+][-]
  1. function GetIntFromBytes(index: integer; bytes: TBytes): integer;
  2. begin
  3.   Result := PInteger(@bytes[index * Sizeof(integer)])^;
  4. end;
  5.  
  6. procedure SetIntToBytes(index: integer; bytes: TBytes; data: integer);
  7. begin
  8.   PInteger(@bytes[index * Sizeof(integer)])^ := data;
  9. end;
  10.  
  11. function CountIntsInBytes(bytes: TBytes): integer;
  12. begin
  13.   Result := Length(bytes) div Sizeof(integer);
  14. end;
  15.  
  16. function InsertIntIntoBytes(data: integer; refData: integer; bytes: TBytes;
  17.   before: boolean = True): TBytes;
  18. var
  19.   i, j: integer;
  20. begin
  21.   // Get insert point
  22.   for i := 0 to CountIntsInBytes(bytes) - 1 do
  23.     if GetIntFromBytes(i, bytes) = refData then
  24.       Break;
  25.   // Resize TBytes
  26.   SetLength(bytes, (CountIntsInBytes(bytes) + 1) * Sizeof(integer));
  27.   // Move data
  28.   if not before then
  29.     i := i + 1;
  30.   for j := CountIntsInBytes(bytes) - 1 downto i + 1 do
  31.     SetIntToBytes(j, bytes, GetIntFromBytes(j - 1, bytes));
  32.   // Insert data
  33.   SetIntToBytes(i, bytes, data);
  34.   Result := bytes;
  35. end;
  36.  
  37. procedure Test;
  38. var
  39.   i: integer;
  40.   bytes: TBytes;
  41. begin
  42.   bytes := InsertIntIntoBytes(1, 0, bytes);
  43.   bytes := InsertIntIntoBytes(2, 1, bytes);
  44.   bytes := InsertIntIntoBytes(3, 1, bytes);
  45.   bytes := InsertIntIntoBytes(4, 1, bytes, False);
  46.   bytes := InsertIntIntoBytes(5, 1, bytes, False);
  47.   for i := 0 to CountIntsInBytes(bytes) - 1 do
  48.     ShowMessage(IntToStr(GetIntFromBytes(i, bytes)));
  49. end;
« Last Edit: October 12, 2017, 12:30:13 pm by tomitomy »

taazz

  • Hero Member
  • *****
  • Posts: 5365
Re: How to convert an integer array to a byte array use the same memory?
« Reply #1 on: October 09, 2017, 08:11:47 am »
tbytes is a dynamic array that means that the length and size of the array are managed internally with hidden fields it is impossible to cast one dynamic array to an other and have the rtl function properly, it might be easier to extend the database component to support a pbyte and a length parameter instead of a tbytes.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

Thaddy

  • Hero Member
  • *****
  • Posts: 10707
Re: How to convert an integer array to a byte array use the same memory?
« Reply #2 on: October 09, 2017, 09:25:16 am »
As long as the size of the integer is known (2,4,8? bytes) Simply cast it or use absolute.
example:
Code: Pascal  [Select][+][-]
  1. program program1;
  2. var
  3.   a:array[0..9] of integer = (0,1,2,3,4,5,6,7,8,9);
  4.   b:array[0..9*SizeOf(Integer)] of byte absolute a;
  5.   c:byte;
  6. begin
  7.   for c in b do writeln(c);// should write some extra zeros depending on int size and compiler mode...
  8. end.
  9.  

- A standard compile will write one extra zero (integer =16 bit = 2 bytes in TP or default mode)
- Mode Delphi or ObjFPC will write 3 extra zero's (integer = 32 bit = 4 )
« Last Edit: October 09, 2017, 11:22:44 am by Thaddy »

tomitomy

  • Full Member
  • ***
  • Posts: 223
Re: How to convert an integer array to a byte array use the same memory?
« Reply #3 on: October 09, 2017, 11:26:25 am »
tbytes is a dynamic array that means that the length and size of the array are managed internally with hidden fields it is impossible to cast one dynamic array to an other and have the rtl function properly, it might be easier to extend the database component to support a pbyte and a length parameter instead of a tbytes.

I do not know how to implement, is there a sample code?

Thaddy

  • Hero Member
  • *****
  • Posts: 10707
Re: How to convert an integer array to a byte array use the same memory?
« Reply #4 on: October 09, 2017, 11:33:20 am »
tbytes is a dynamic array that means that the length and size of the array are managed internally with hidden fields it is impossible to cast one dynamic array to an other and have the rtl function properly, it might be easier to extend the database component to support a pbyte and a length parameter instead of a tbytes.

I do not know how to implement, is there a sample code?
Yes, but for your purpose you should not use TBytes. See my example for sharing an integer and a byte array in the exact same memory.
That can not be guaranteed with a dynamic array (they can be relocated...).

Usually I use absolute if array size is known.

@Taazz plz read subject! A better way is maybe a variant record with an array of integer and an array of byte.... He needs the same memory, w/o copy.
Code: Pascal  [Select][+][-]
  1. program program1;
  2. {$mode fpc}
  3. var
  4.   overlay: packed record
  5.   case boolean of
  6.     false:(a:array[0..9] of integer);
  7.     true :(b:array[0..9*SizeOf(Integer)] of byte);
  8.   end;
  9. begin
  10. overlay.a[0]:=1;
  11. writeln(overlay.b[0],#9,overlay.b[1]);
  12.  
  13. overlay.a[0]:=256;
  14. writeln(overlay.b[0],#9,overlay.b[1]);
  15.  
  16. end.

Same can be done with pointers, of course, but that is an excercise.
« Last Edit: October 09, 2017, 11:57:52 am by Thaddy »

tomitomy

  • Full Member
  • ***
  • Posts: 223
Re: How to convert an integer array to a byte array use the same memory?
« Reply #5 on: October 09, 2017, 12:00:03 pm »
tbytes is a dynamic array that means that the length and size of the array are managed internally with hidden fields it is impossible to cast one dynamic array to an other and have the rtl function properly, it might be easier to extend the database component to support a pbyte and a length parameter instead of a tbytes.

I do not know how to implement, is there a sample code?
Yes, but for your purpose you should not use TBytes. See my example for sharing an integer and a byte array in the exact same memory.
That can not be guaranteed with a dynamic array (they can be relocated...).

@Taazz plz read subject! A better way is maybe a variant record with an array of integer and an array of byte.... He needs the same memory, w/o copy.

But my integer array is a dynamic array, I need to dynamically add and delete elements, and I want write it to a SQLite database, if I don't use TBytes, I don't know how to write it to the database.
« Last Edit: October 09, 2017, 12:03:38 pm by tomitomy »

taazz

  • Hero Member
  • *****
  • Posts: 5365
Re: How to convert an integer array to a byte array use the same memory?
« Reply #6 on: October 09, 2017, 12:02:04 pm »
tbytes is a dynamic array that means that the length and size of the array are managed internally with hidden fields it is impossible to cast one dynamic array to an other and have the rtl function properly, it might be easier to extend the database component to support a pbyte and a length parameter instead of a tbytes.

I do not know how to implement, is there a sample code?
I need to see the code that uses the TBytes in order to provide a sample.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

tomitomy

  • Full Member
  • ***
  • Posts: 223
Re: How to convert an integer array to a byte array use the same memory?
« Reply #7 on: October 09, 2017, 12:51:20 pm »
tbytes is a dynamic array that means that the length and size of the array are managed internally with hidden fields it is impossible to cast one dynamic array to an other and have the rtl function properly, it might be easier to extend the database component to support a pbyte and a length parameter instead of a tbytes.

I do not know how to implement, is there a sample code?
I need to see the code that uses the TBytes in order to provide a sample.

this is my code (Simplified):

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, sqlite3conn, sqldb;
  9.  
  10. type
  11.   TTestDB = class(TObject)
  12.     SQLConn: TSQLite3Connection;
  13.     SQLQuery: TSQLQuery;
  14.     SQLTran: TSQLTransaction;
  15.   private
  16.     procedure CreateTable;
  17.   public
  18.     constructor Create;
  19.     destructor Destroy; override;
  20.  
  21.     function OpenDB(FileName: string): boolean;
  22.     procedure CloseDB(Save: boolean = True);
  23.  
  24.     function GetData(ID: integer): TBytes;
  25.     procedure SetData(ID: integer; Data: TBytes);
  26.  
  27.     function NewRecord(Data: TBytes): integer;
  28.   end;
  29.  
  30.   { TForm1 }
  31.  
  32.   TForm1 = class(TForm)
  33.     procedure FormCreate(Sender: TObject);
  34.   end;
  35.  
  36. var
  37.   Form1: TForm1;
  38.  
  39. implementation
  40.  
  41. {$R *.lfm}
  42.  
  43. procedure Test;
  44. var
  45.   FileName: string;
  46.   bytes: TBytes;
  47.   TestDB: TTestDB;  
  48.   i: integer;
  49. begin
  50.   FileName := ExtractFilePath(Application.ExeName);
  51.   FileName := ConcatPaths([FileName, 'test.db']);
  52.   DeleteFile(FileName);
  53.   TestDB := TTestDB.Create;
  54.   TestDB.OpenDB(FileName);
  55.   SetLength(bytes, 5 * sizeof(integer));
  56.   for i := 0 to 4 do
  57.     PInteger(@bytes[i * Sizeof(integer)])^ := i + 1;
  58.   // Write
  59.   TestDB.NewRecord(bytes);
  60.   // Read
  61.   bytes := TestDB.GetData(1);
  62.   for i := 0 to 4 do
  63.   begin
  64.     ShowMessage(IntToStr(PInteger(@bytes[i * Sizeof(integer)])^));
  65.   end;
  66. end;
  67.  
  68. procedure TForm1.FormCreate(Sender: TObject);
  69. begin
  70.   Test;
  71. end;
  72.  
  73. constructor TTestDB.Create;
  74. var
  75.   LibFile: string;
  76.   AppPath: string;
  77. begin
  78.   inherited Create;
  79.   SQLConn := TSQLite3Connection.Create(nil);
  80.   SQLTran := TSQLTransaction.Create(nil);
  81.   SQLQuery := TSQLQuery.Create(nil);
  82.  
  83.   SQLConn.Transaction := SQLTran;
  84.   SQLTran.DataBase := SQLConn;
  85.   SQLQuery.DataBase := SQLConn;
  86.  
  87.   AppPath := ExtractFilePath(Application.ExeName);
  88. {$ifdef MSWINDOWS}
  89.   LibFile := ConcatPaths([AppPath, 'lib', 'sqlite3.dll']);
  90. {$else}
  91.   LibFile := ConcatPaths([AppPath, 'lib', 'libsqlite3.so']);
  92. {$endif}
  93.   if FileExists(LibFile) then
  94.     SQLiteLibraryName := LibFile;
  95. end;
  96.  
  97. destructor TTestDB.Destroy;
  98. begin
  99.   FreeAndNil(SQLQuery);
  100.   FreeAndNil(SQLTran);
  101.   FreeAndNil(SQLConn);
  102.   inherited Destroy;
  103. end;
  104.  
  105. procedure TTestDB.CreateTable;
  106. begin
  107.   SQLConn.ExecuteDirect('Create Table "main"("data" Blob);');
  108. end;
  109.  
  110. function TTestDB.OpenDB(FileName: string): boolean;
  111. begin
  112.   SQLConn.DatabaseName := FileName;
  113.   SQLConn.Open;
  114.   SQLTran.Active := True;
  115.   CreateTable;
  116. end;
  117.  
  118. procedure TTestDB.CloseDB(Save: boolean = True);
  119. begin
  120.   if SQLTran.Active then
  121.     if Save then
  122.       SQLTran.Commit
  123.     else
  124.       SQLTran.Rollback;
  125.   SQLConn.Close(True);
  126. end;
  127.  
  128. function TTestDB.NewRecord(Data: TBytes): integer;
  129. begin
  130.   SQLQuery.SQL.Text :=
  131.     'Insert Into main (data) Values (:data)';
  132.   SQLQuery.Params.ParamByName('data').AsBytes := Data;
  133.   SQLQuery.ExecSQL;
  134.   SQLQuery.SQL.Text := 'Select last_insert_rowid() From main';
  135.   SQLQuery.Open;
  136.   Result := SQLQuery.Fields[0].AsInteger;
  137.   SQLQuery.Close;
  138. end;
  139.  
  140. function TTestDB.GetData(ID: integer): TBytes;
  141. begin
  142.   SQLQuery.SQL.Text := 'Select data From main where rowid=:rowid';
  143.   SQLQuery.Params.ParamByName('rowid').AsInteger := ID;
  144.   SQLQuery.Open;
  145.   Result := SQLQuery.Fields[0].AsBytes;
  146.   SQLQuery.Close;
  147. end;
  148.  
  149. procedure TTestDB.SetData(ID: integer; Data: TBytes);
  150. begin
  151.   SQLQuery.SQL.Text := 'Update main Set data=:data Where rowid=:rowid';
  152.   SQLQuery.Params.ParamByName('data').AsBytes := Data;
  153.   SQLQuery.Params.ParamByName('rowid').AsInteger := ID;
  154.   SQLQuery.ExecSQL;
  155. end;
  156.  
  157. end.

taazz

  • Hero Member
  • *****
  • Posts: 5365
Re: How to convert an integer array to a byte array use the same memory?
« Reply #8 on: October 09, 2017, 10:12:39 pm »
tbytes is a dynamic array that means that the length and size of the array are managed internally with hidden fields it is impossible to cast one dynamic array to an other and have the rtl function properly, it might be easier to extend the database component to support a pbyte and a length parameter instead of a tbytes.

I do not know how to implement, is there a sample code?
I need to see the code that uses the TBytes in order to provide a sample.

this is my code (Simplified):

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, sqlite3conn, sqldb;
  9.  
  10. type
  11.   TTestDB = class(TObject)
  12.     SQLConn: TSQLite3Connection;
  13.     SQLQuery: TSQLQuery;
  14.     SQLTran: TSQLTransaction;
  15.   private
  16.     procedure CreateTable;
  17.   public
  18.     constructor Create;
  19.     destructor Destroy; override;
  20.  
  21.     function OpenDB(FileName: string): boolean;
  22.     procedure CloseDB(Save: boolean = True);
  23.  
  24.     function GetData(ID: integer): TBytes;
  25.     procedure SetData(ID: integer; Data: TBytes);
  26.  
  27.     function NewRecord(Data: TBytes): integer;
  28.   end;
  29.  
  30.   { TForm1 }
  31.  
  32.   TForm1 = class(TForm)
  33.     procedure FormCreate(Sender: TObject);
  34.   end;
  35.  
  36. var
  37.   Form1: TForm1;
  38.  
  39. implementation
  40.  
  41. {$R *.lfm}
  42.  
  43. procedure Test;
  44. var
  45.   FileName: string;
  46.   bytes: TBytes;
  47.   TestDB: TTestDB;  
  48.   i: integer;
  49. begin
  50.   FileName := ExtractFilePath(Application.ExeName);
  51.   FileName := ConcatPaths([FileName, 'test.db']);
  52.   DeleteFile(FileName);
  53.   TestDB := TTestDB.Create;
  54.   TestDB.OpenDB(FileName);
  55.   SetLength(bytes, 5 * sizeof(integer));
  56.   for i := 0 to 4 do
  57.     PInteger(@bytes[i * Sizeof(integer)])^ := i + 1;
  58.   // Write
  59.   TestDB.NewRecord(bytes);
  60.   // Read
  61.   bytes := TestDB.GetData(1);
  62.   for i := 0 to 4 do
  63.   begin
  64.     ShowMessage(IntToStr(PInteger(@bytes[i * Sizeof(integer)])^));
  65.   end;
  66. end;
  67.  
  68. procedure TForm1.FormCreate(Sender: TObject);
  69. begin
  70.   Test;
  71. end;
  72.  
  73. constructor TTestDB.Create;
  74. var
  75.   LibFile: string;
  76.   AppPath: string;
  77. begin
  78.   inherited Create;
  79.   SQLConn := TSQLite3Connection.Create(nil);
  80.   SQLTran := TSQLTransaction.Create(nil);
  81.   SQLQuery := TSQLQuery.Create(nil);
  82.  
  83.   SQLConn.Transaction := SQLTran;
  84.   SQLTran.DataBase := SQLConn;
  85.   SQLQuery.DataBase := SQLConn;
  86.  
  87.   AppPath := ExtractFilePath(Application.ExeName);
  88. {$ifdef MSWINDOWS}
  89.   LibFile := ConcatPaths([AppPath, 'lib', 'sqlite3.dll']);
  90. {$else}
  91.   LibFile := ConcatPaths([AppPath, 'lib', 'libsqlite3.so']);
  92. {$endif}
  93.   if FileExists(LibFile) then
  94.     SQLiteLibraryName := LibFile;
  95. end;
  96.  
  97. destructor TTestDB.Destroy;
  98. begin
  99.   FreeAndNil(SQLQuery);
  100.   FreeAndNil(SQLTran);
  101.   FreeAndNil(SQLConn);
  102.   inherited Destroy;
  103. end;
  104.  
  105. procedure TTestDB.CreateTable;
  106. begin
  107.   SQLConn.ExecuteDirect('Create Table "main"("data" Blob);');
  108. end;
  109.  
  110. function TTestDB.OpenDB(FileName: string): boolean;
  111. begin
  112.   SQLConn.DatabaseName := FileName;
  113.   SQLConn.Open;
  114.   SQLTran.Active := True;
  115.   CreateTable;
  116. end;
  117.  
  118. procedure TTestDB.CloseDB(Save: boolean = True);
  119. begin
  120.   if SQLTran.Active then
  121.     if Save then
  122.       SQLTran.Commit
  123.     else
  124.       SQLTran.Rollback;
  125.   SQLConn.Close(True);
  126. end;
  127.  
  128. function TTestDB.NewRecord(Data: TBytes): integer;
  129. begin
  130.   SQLQuery.SQL.Text :=
  131.     'Insert Into main (data) Values (:data)';
  132.   SQLQuery.Params.ParamByName('data').AsBytes := Data;
  133.   SQLQuery.ExecSQL;
  134.   SQLQuery.SQL.Text := 'Select last_insert_rowid() From main';
  135.   SQLQuery.Open;
  136.   Result := SQLQuery.Fields[0].AsInteger;
  137.   SQLQuery.Close;
  138. end;
  139.  
  140. function TTestDB.GetData(ID: integer): TBytes;
  141. begin
  142.   SQLQuery.SQL.Text := 'Select data From main where rowid=:rowid';
  143.   SQLQuery.Params.ParamByName('rowid').AsInteger := ID;
  144.   SQLQuery.Open;
  145.   Result := SQLQuery.Fields[0].AsBytes;
  146.   SQLQuery.Close;
  147. end;
  148.  
  149. procedure TTestDB.SetData(ID: integer; Data: TBytes);
  150. begin
  151.   SQLQuery.SQL.Text := 'Update main Set data=:data Where rowid=:rowid';
  152.   SQLQuery.Params.ParamByName('data').AsBytes := Data;
  153.   SQLQuery.Params.ParamByName('rowid').AsInteger := ID;
  154.   SQLQuery.ExecSQL;
  155. end;
  156.  
  157. end.
The only thing you can do is to convert your data to TBytes or to a string and pass that to the parameter. I would recomend converting it to a string, for the correct procedure of doing so see the method TParam.GetAsString in the db unit, because this is done for your data internally when you execute it, this way you avoid a 3 way convertion.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

tomitomy

  • Full Member
  • ***
  • Posts: 223
Re: How to convert an integer array to a byte array use the same memory?
« Reply #9 on: October 10, 2017, 07:34:52 am »
thanks @taazz and @Thaddy, all your replies are helpful, thank you very much.

I don't want allocate new memory, so i don't want to convert my data to another type, I just want to use the same memory of my data.

now, with your help, I have two ways to meet my needs, the one is only use 'absolute', another is change the hidden field which stone the length of the array.

method 1 (only use 'absolute'):

Code: Pascal  [Select][+][-]
  1. type
  2.   TInts = array of integer;
  3.  
  4. procedure Test;
  5. var
  6.   FileName: string;
  7.   TestDB: TTestDB;
  8.   i: integer;
  9.   bytes: TBytes;
  10.   ints: TInts absolute bytes;
  11.   HighInt: integer;
  12. begin
  13.   FileName := ExtractFilePath(Application.ExeName);
  14.   FileName := ConcatPaths([FileName, 'test.db']);
  15.   DeleteFile(FileName);
  16.   TestDB := TTestDB.Create;
  17.   TestDB.OpenDB(FileName);
  18.  
  19.   // Initialize Ints
  20.   HighInt := 4;
  21.   // don't use SetLength(ints, HighInt +1)
  22.   // Bytes are larger in length than ints, they are use the same length field.
  23.   SetLength(bytes, (HighInt + 1) * sizeof(integer));
  24.   for i := Low(Ints) to HighInt do
  25.     Ints[i] := i + 1;
  26.   // Write Ints
  27.   TestDB.NewRecord(bytes);
  28.   // Clear Ints
  29.   SetLength(Ints,0);  
  30.   // Read Ints
  31.   bytes := TestDB.GetData(1);
  32.   for i := Low(Ints) to HighInt do
  33.   begin
  34.     ShowMessage(IntToStr(Ints[i]));
  35.   end;
  36. end;

method 2 (change the hidden field):

Code: Pascal  [Select][+][-]
  1. type
  2.   TInts = array of integer;
  3.  
  4. function IntsToBytes(ints: TInts):TBytes;
  5. var
  6.   bytes: TBytes absolute ints;
  7.   p: PQword;
  8. begin
  9.   // Get hidden field address
  10.   p := @ints[0];
  11.   Dec(p);
  12.   // Change hidden field
  13.   p^ := Length(Ints) * Sizeof(integer) - 1;
  14.   Result := bytes;
  15. end;
  16.  
  17. function BytesToInts(bytes: TBytes):TInts;
  18. var
  19.   ints: TInts absolute bytes;
  20.   p: PQword;
  21. begin
  22.   // Get hidden field address
  23.   p := @ints[0];
  24.   Dec(p);
  25.   // Change hidden field
  26.   p^ := Length(bytes) div Sizeof(integer) - 1;
  27.   Result := ints;
  28. end;
  29.  
  30. procedure Test;
  31. var
  32.   FileName: string;
  33.   TestDB: TTestDB;
  34.   i: integer;
  35.   ints: TInts;
  36. begin
  37.   FileName := ExtractFilePath(Application.ExeName);
  38.   FileName := ConcatPaths([FileName, 'test.db']);
  39.   DeleteFile(FileName);
  40.   TestDB := TTestDB.Create;
  41.   TestDB.OpenDB(FileName);
  42.  
  43.   // Initialize Ints
  44.   SetLength(ints, 5);
  45.   for i := Low(Ints) to High(ints) do
  46.     Ints[i] := i + 1;
  47.   // Write Ints
  48.   TestDB.NewRecord(IntsToBytes(Ints));
  49.   // Clear Ints
  50.   SetLength(Ints,0);  
  51.   // Read Ints
  52.   Ints := BytesToInts(TestDB.GetData(1));
  53.   for i := Low(Ints) to High(ints) do
  54.   begin
  55.     ShowMessage(IntToStr(Ints[i]));
  56.   end;
  57. end;

I don't know if the second method is correct, but it can work properly.
« Last Edit: October 10, 2017, 07:38:11 am by tomitomy »

ASerge

  • Hero Member
  • *****
  • Posts: 1742
Re: How to convert an integer array to a byte array use the same memory?
« Reply #10 on: October 11, 2017, 12:05:18 am »
I don't know if the second method is correct, but it can work properly.
A little safer with the test:
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2. {$MODE OBJFPC}
  3. program project1;
  4.  
  5. uses SysUtils;
  6.  
  7. function GetArrHigh(P: Pointer): PDynArrayIndex; inline;
  8. begin
  9.   Result := P - SizeOf(IntPtr);  // See TDynArray in dynarr.inc
  10. end;
  11.  
  12. function IntsToBytes(const Ints: TBoundArray): TBytes;
  13. begin
  14.   if Assigned(Ints) then
  15.     GetArrHigh(@Ints[0])^ := Length(Ints) * SizeOf(Ints[0]) - 1;
  16.   Result := TBytes(Ints);
  17. end;
  18.  
  19. function BytesToInts(const Bytes: TBytes): TBoundArray;
  20. begin
  21.   if Assigned(Bytes) then
  22.     GetArrHigh(@Bytes[0])^ := Length(Bytes) div SizeOf(TBoundArray[0]) - 1;
  23.   Result := TBoundArray(Bytes);
  24. end;
  25.  
  26. procedure PrintInts(const Ints: TBoundArray);
  27. var
  28.   i: Integer;
  29. begin
  30.   Write(Format('Ints  %p: ', [@Ints[0]]));
  31.   for i := Low(Ints) to High(Ints) do
  32.     Write(Ints[i], ' ');
  33.   Writeln;
  34. end;
  35.  
  36. procedure PrintBytes(const Bytes: TBytes);
  37. var
  38.   i: Integer;
  39. begin
  40.   Write(Format('Bytes %p: ', [@Bytes[0]]));
  41.   for i := Low(Bytes) to High(Bytes) do
  42.     Write(Bytes[i], ' ');
  43.   Writeln;
  44. end;
  45.  
  46. procedure Test;
  47. var
  48.   Ints: TBoundArray;
  49.   Bytes: TBytes;
  50.   i: Integer;
  51. begin
  52.   SetLength(Ints, 5);
  53.   for i := Low(Ints) to High(ints) do
  54.     Ints[i] := i + 1;
  55.   PrintInts(Ints);
  56.   Bytes := IntsToBytes(Ints);
  57.   //Ints := nil;
  58.   PrintBytes(Bytes);
  59.   Ints := BytesToInts(Bytes);
  60.   //Bytes := nil;
  61.   PrintInts(Ints);
  62. end;
  63.  
  64. begin
  65.   Test;
  66.   Readln;
  67. end.

tomitomy

  • Full Member
  • ***
  • Posts: 223
Re: How to convert an integer array to a byte array use the same memory?
« Reply #11 on: October 11, 2017, 03:21:32 am »
A little safer with the test:

Your code is very clear and safer, I can use it with confidence, thank you very much.

 

TinyPortal © 2005-2018