View Issue Details

IDProjectCategoryView StatusLast Update
0037042FPCRTLpublic2020-05-19 12:55
ReporterBi0T1N Assigned ToSven Barth  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version3.3.1 
Fixed in Version3.3.1 
Summary0037042: [Patch] Add TBitConverter class
DescriptionThis patch provides an implementation for the TBitConverter class from Delphi (http://docwiki.embarcadero.com/Libraries/Rio/en/System.Types.TBitConverter). Basic tests are also provided.
Additional InformationIt only gives correct results with base types but due to the generic nature it can also be used with e.g. AnsiString but that leads to wrong results. Thus I've used IsManagedType() to explicitly prevent the use but maybe there is a way to throw an error at compile time?
TagsNo tags attached.
Fixed in Revision45409
FPCOldBugId
FPCTarget-
Attached Files

Activities

Bi0T1N

2020-05-09 20:15

reporter  

TBitConverter_tests.pas (6,987 bytes)   
program test;

{$mode Delphi}

uses
  SysUtils, Types;

type
  TMyRecord = record
    Number: Integer;
    SmallNumber: Word;
    UseIt: Boolean;
  end;

  TMyPackedRecord = packed record
    Number: Integer;
    SmallNumber: Word;
    UseIt: Boolean;
  end;

procedure TestFromInteger;
var
  arr: TBytes;
  item: Byte;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(Integer));
  TBitConverter.From<Integer>(1038, arr);
  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 14 then halt(1);
  if arr[1] <> 4 then halt(1);
  if arr[2] <> 0 then halt(1);
  if arr[3] <> 0 then halt(1);

  writeln('');
end;

procedure TestFromDouble;
var
  arr: TBytes;
  item: Byte;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(Double));
  TBitConverter.From<Double>(3.14, arr);
  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if PDouble(@arr[0])^ <> 3.14 then halt(2);

  writeln('');
end;

procedure TestFromTMyRecord;
var
  arr: TBytes;
  item: Byte;
  rec: TMyRecord;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(TMyRecord));

  rec.Number := 42;
  rec.SmallNumber := 5;
  rec.UseIt := True;
  TBitConverter.From<TMyRecord>(rec, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 42 then halt(3);
  if arr[1] <> 0 then halt(3);
  if arr[2] <> 0 then halt(3);
  if arr[3] <> 0 then halt(3);
  if arr[4] <> 5 then halt(3);
  if arr[5] <> 0 then halt(3);
  if arr[6] <> 1 then halt(3);
  if arr[7] <> 0 then halt(3);

  writeln('');
end;

procedure TestFromTMyPackedRecord;
var
  arr: TBytes;
  item: Byte;
  rec: TMyPackedRecord;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(TMyPackedRecord));

  rec.Number := 42;
  rec.SmallNumber := 289;
  rec.UseIt := True;
  TBitConverter.From<TMyPackedRecord>(rec, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 42 then halt(4);
  if arr[1] <> 0 then halt(4);
  if arr[2] <> 0 then halt(4);
  if arr[3] <> 0 then halt(4);
  if arr[4] <> 33 then halt(4);
  if arr[5] <> 1 then halt(4);
  if arr[6] <> 1 then halt(4);

  writeln('');
end;

procedure TestFromAnsiChar;
var
  arr: TBytes;
  item: Byte;
  c: AnsiChar;
  i: Integer;
begin
  i := 0;
  c := 'A';
  SetLength(arr, SizeOf(c));
  TBitConverter.From<AnsiChar>(c, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 65 then halt(5);

  writeln('');
end;

procedure TestFromUnicodeChar;
var
  arr: TBytes;
  item: Byte;
  c: UnicodeChar;
  i: Integer;
begin
  i := 0;
  c := 'A';
  SetLength(arr, SizeOf(c));
  TBitConverter.From<UnicodeChar>(c, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 65 then halt(6);
  if arr[1] <> 0 then halt(6);

  writeln('');
end;

procedure TestInToInteger;
var
  arr: TBytes;
  num: Integer;
begin
  arr := TArray<Byte>.Create(1, 0, 1, 0);

  num := TBitConverter.InTo<Integer>(arr, 0);
  writeln(num.ToString);

  if num <> 65537 then halt(7);

  writeln('');
end;

procedure TestInToDouble;
var
  arr: TBytes;
  num: Double;
begin
  arr := TArray<Byte>.Create($1C, $C7, $71, $1C, $C7, $71, $BC, $3F);

  num := TBitConverter.InTo<Double>(arr, 0);
  writeln(num.ToString);

  if num <> 0.1111111111111111111 then halt(8);

  writeln('');
end;

procedure TestInToTMyRecord;
var
  arr: TBytes;
  rec: TMyRecord;
begin
  arr := TArray<Byte>.Create(66, 0, 0, 0, 15, 0, 0, 0);

  rec := TBitConverter.InTo<TMyRecord>(arr, 0);
  writeln(rec.Number.ToString);
  writeln(rec.SmallNumber.ToString);
  writeln(BoolToStr(rec.UseIt, True));

  if rec.Number <> 66 then halt(9);
  if rec.SmallNumber <> 15 then halt(9);
  if rec.UseIt <> False then halt(9);

  writeln('');
end;

procedure TestInToTMyPackedRecord;
var
  arr: TBytes;
  rec: TMyPackedRecord;
begin
  arr := TArray<Byte>.Create(255, 1, 0, 0, 15, 0, 1);

  rec := TBitConverter.InTo<TMyPackedRecord>(arr, 0);
  writeln(rec.Number.ToString);
  writeln(rec.SmallNumber.ToString);
  writeln(BoolToStr(rec.UseIt, True));

  if rec.Number <> 511 then halt(10);
  if rec.SmallNumber <> 15 then halt(10);
  if rec.UseIt <> True then halt(10);

  writeln('');
end;

procedure TestInToAnsiChar;
var
  arr: TBytes;
  c: AnsiChar;
begin
  arr := TArray<Byte>.Create(65);

  c := TBitConverter.InTo<AnsiChar>(arr, 0);
  writeln(c);

  if c <> 'A' then halt(11);

  writeln('');
end;

procedure TestInToUnicodeChar;
var
  arr: TBytes;
  c: UnicodeChar;
begin
  arr := TArray<Byte>.Create(66, 0);

  c := TBitConverter.InTo<UnicodeChar>(arr, 0);
  writeln(c);

  if c <> 'B' then halt(12);

  writeln('');
end;

procedure TestFromIntegerOffset;
var
  arr: TBytes;
  item: Byte;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(Integer) + 2);
  TBitConverter.From<Integer>(257, arr, 2);
  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 0 then halt(13);
  if arr[1] <> 0 then halt(13);
  if arr[2] <> 1 then halt(13);
  if arr[3] <> 1 then halt(13);
  if arr[4] <> 0 then halt(13);
  if arr[5] <> 0 then halt(13);

  writeln('');
end;

procedure TestInToIntegerOffset;
var
  arr: TBytes;
  num: Integer;
begin
  arr := TArray<Byte>.Create(2, 0, 2, 0, 1, 0, 2, 0);

  num := TBitConverter.InTo<Integer>(arr, 2);
  writeln(num.ToString);

  if num <> 65538 then halt(14);

  writeln('');
end;
{
procedure TestInToAnsiString;
var
  arr: TBytes;
  str: AnsiString;
begin
  arr := TArray<Byte>.Create(44, 44, 45, 67, 66);
  
  str := TBitConverter.InTo<AnsiString>(arr, 0);
  writeln(str);

  if str <> 'hello' then halt(15);

  writeln('');
end;

procedure TestFromAnsiString;
var
  arr: TBytes;
  item: Byte;
  str: AnsiString;
  i: Integer;
begin
  i := 0;
  str := 'hello world!';
  SetLength(arr, Length(str));
  TBitConverter.From<AnsiString>(str, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  writeln('');
end;
}
begin
  {* testing TBitConverter.From<T> *}
  TestFromInteger;
  TestFromDouble;
  TestFromTMyRecord;
  TestFromTMyPackedRecord;
  TestFromAnsiChar;
  TestFromUnicodeChar;

  {* testing TBitConverter.InTo<T> *}
  TestInToInteger;
  TestInToDouble;
  TestInToTMyRecord;
  TestInToTMyPackedRecord;
  TestInToAnsiChar;
  TestInToUnicodeChar;

  {* testing offset *}
  TestFromIntegerOffset;
  TestInToIntegerOffset;

  {* non base types *}
  //TestInToAnsiString;
  //TestFromAnsiString;

  readln;
end.
TBitConverter_tests.pas (6,987 bytes)   
01-Add_TBitConverter_class.patch (2,122 bytes)   
diff --git rtl/objpas/types.pp rtl/objpas/types.pp
index 7bfec96bbf..5c2f923698 100644
--- rtl/objpas/types.pp
+++ rtl/objpas/types.pp
@@ -341,6 +341,14 @@ function InflateRect(var Rect: TRect; dx: Integer; dy: Integer): Boolean;
 function Size(AWidth, AHeight: Integer): TSize; inline;
 function Size(const ARect: TRect): TSize;
 
+type
+  TBitConverter = class
+    generic class procedure UnsafeFrom<T>(const ASrcValue: T; var ADestination: Array of Byte; AOffset: Integer = 0); static;
+    generic class procedure From<T>(const ASrcValue: T; var ADestination: Array of Byte; AOffset: Integer = 0); static;
+    generic class function UnsafeInTo<T>(const ASource: Array of Byte; AOffset: Integer = 0): T; static;
+    generic class function InTo<T>(const ASource: Array of Byte; AOffset: Integer = 0): T; static;
+  end;
+
 implementation
 
 Uses Math;
@@ -712,4 +720,42 @@ begin
   bottom:=bottom+dy; top:=top+dy;
 end;
 
+generic class procedure TBitConverter.UnsafeFrom<T>(const ASrcValue: T; var ADestination: Array of Byte; AOffset: Integer = 0);
+begin
+  move(ASrcValue, ADestination[AOffset], SizeOf(T));
+end;
+
+generic class procedure TBitConverter.From<T>(const ASrcValue: T; var ADestination: Array of Byte; AOffset: Integer = 0);
+begin
+  if AOffset < 0 then
+    System.Error(reRangeError);
+
+  if IsManagedType(T) then
+    System.Error(reRangeError);
+
+  if Length(ADestination) < (SizeOf(T) + AOffset) then
+    System.Error(reRangeError);
+
+  TBitConverter.specialize UnsafeFrom<T>(ASrcValue, ADestination, AOffset);
+end;
+
+generic class function TBitConverter.UnsafeInTo<T>(const ASource: Array of Byte; AOffset: Integer = 0): T;
+begin
+  move(ASource[AOffset], Result, SizeOf(T));
+end;
+
+generic class function TBitConverter.InTo<T>(const ASource: Array of Byte; AOffset: Integer = 0): T;
+begin
+  if AOffset < 0 then
+    System.Error(reRangeError);
+
+  if IsManagedType(T) then
+    System.Error(reRangeError);
+
+  if Length(ASource) < (SizeOf(T) + AOffset) then
+    System.Error(reRangeError);
+
+  Result := TBitConverter.specialize UnsafeInTo<T>(ASource, AOffset);
+end;
+
 end.

Sven Barth

2020-05-09 23:46

manager   ~0122689

Does Delphi behave the same for managed types?

Following remarks:
- maybe better use reInvalidCast instead of reRangeError if IsManagedType fails, this way the user can better pinpoint what their problem is
- you need to disable that class with {$ifndef ver3_0}, because as a first step the RTL is compiled with the release compiler (which still is 3.0.4 currently)
- you could declare InTo<> and From<> as inline, maybe the other two as well

Bi0T1N

2020-05-10 17:03

reporter   ~0122700

Last edited: 2020-05-17 18:47

View 3 revisions

Delphi prints garbage for e.g. AnsiStrings which is obviously because SizeOf(AnsiString) doesn't reflect the amount of characters stored in the String. Thus you'll never get the bytes of the whole String (seems like you don't get them at all) as you'd expect if you didn't read the documentation carefully.
But I'm also fine with using the same approach as Delphi and don't check for managed types.

Btw adding inline gives: Note: "open array" not yet supported inside inline procedure/function
02-Add_TBitConverter_class.patch (2,196 bytes)   
diff --git rtl/objpas/types.pp rtl/objpas/types.pp
index 7bfec96bbf..0e6b4f4732 100644
--- rtl/objpas/types.pp
+++ rtl/objpas/types.pp
@@ -341,6 +341,16 @@ function InflateRect(var Rect: TRect; dx: Integer; dy: Integer): Boolean;
 function Size(AWidth, AHeight: Integer): TSize; inline;
 function Size(const ARect: TRect): TSize;
 
+{$ifndef VER3_0}
+type
+  TBitConverter = class
+    generic class procedure UnsafeFrom<T>(const ASrcValue: T; var ADestination: Array of Byte; AOffset: Integer = 0); static; inline;
+    generic class procedure From<T>(const ASrcValue: T; var ADestination: Array of Byte; AOffset: Integer = 0); static;
+    generic class function UnsafeInTo<T>(const ASource: Array of Byte; AOffset: Integer = 0): T; static; inline;
+    generic class function InTo<T>(const ASource: Array of Byte; AOffset: Integer = 0): T; static;
+  end;
+{$endif}
+
 implementation
 
 Uses Math;
@@ -712,4 +722,44 @@ begin
   bottom:=bottom+dy; top:=top+dy;
 end;
 
+{$ifndef VER3_0}
+generic class procedure TBitConverter.UnsafeFrom<T>(const ASrcValue: T; var ADestination: Array of Byte; AOffset: Integer = 0);
+begin
+  move(ASrcValue, ADestination[AOffset], SizeOf(T));
+end;
+
+generic class procedure TBitConverter.From<T>(const ASrcValue: T; var ADestination: Array of Byte; AOffset: Integer = 0);
+begin
+  if AOffset < 0 then
+    System.Error(reRangeError);
+
+  if IsManagedType(T) then
+    System.Error(reInvalidCast);
+
+  if Length(ADestination) < (SizeOf(T) + AOffset) then
+    System.Error(reRangeError);
+
+  TBitConverter.specialize UnsafeFrom<T>(ASrcValue, ADestination, AOffset);
+end;
+
+generic class function TBitConverter.UnsafeInTo<T>(const ASource: Array of Byte; AOffset: Integer = 0): T;
+begin
+  move(ASource[AOffset], Result, SizeOf(T));
+end;
+
+generic class function TBitConverter.InTo<T>(const ASource: Array of Byte; AOffset: Integer = 0): T;
+begin
+  if AOffset < 0 then
+    System.Error(reRangeError);
+
+  if IsManagedType(T) then
+    System.Error(reInvalidCast);
+
+  if Length(ASource) < (SizeOf(T) + AOffset) then
+    System.Error(reRangeError);
+
+  Result := TBitConverter.specialize UnsafeInTo<T>(ASource, AOffset);
+end;
+{$endif}
+
 end.

Sven Barth

2020-05-15 17:04

manager   ~0122820

While integrating this I noticed two things:
- in your test you need to use a Double typecast for the constants (otherwise it will fail on i386 where constants are evaluated as Extended)
- how does this behave on Big Endian systems?

Bi0T1N

2020-05-15 19:45

reporter   ~0122824

Typecasting like I did in the attached patch? Currently I only have 64-bit compiler installed.
The test is only for little endian, I never came across a big endian system. Thus the tests will fail but beside of that everything should work as expected on big endian - just with a different ordering in the bits.
TBitConverter_tests_casts.pas (7,011 bytes)   
program test;

{$mode Delphi}

uses
  SysUtils, Types;

type
  TMyRecord = record
    Number: Integer;
    SmallNumber: Word;
    UseIt: Boolean;
  end;

  TMyPackedRecord = packed record
    Number: Integer;
    SmallNumber: Word;
    UseIt: Boolean;
  end;

procedure TestFromInteger;
var
  arr: TBytes;
  item: Byte;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(Integer));
  TBitConverter.From<Integer>(1038, arr);
  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 14 then halt(1);
  if arr[1] <> 4 then halt(1);
  if arr[2] <> 0 then halt(1);
  if arr[3] <> 0 then halt(1);

  writeln('');
end;

procedure TestFromDouble;
var
  arr: TBytes;
  item: Byte;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(Double));
  TBitConverter.From<Double>(Double(3.14), arr);
  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if PDouble(@arr[0])^ <> Double(3.14) then halt(2);

  writeln('');
end;

procedure TestFromTMyRecord;
var
  arr: TBytes;
  item: Byte;
  rec: TMyRecord;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(TMyRecord));

  rec.Number := 42;
  rec.SmallNumber := 5;
  rec.UseIt := True;
  TBitConverter.From<TMyRecord>(rec, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 42 then halt(3);
  if arr[1] <> 0 then halt(3);
  if arr[2] <> 0 then halt(3);
  if arr[3] <> 0 then halt(3);
  if arr[4] <> 5 then halt(3);
  if arr[5] <> 0 then halt(3);
  if arr[6] <> 1 then halt(3);
  if arr[7] <> 0 then halt(3);

  writeln('');
end;

procedure TestFromTMyPackedRecord;
var
  arr: TBytes;
  item: Byte;
  rec: TMyPackedRecord;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(TMyPackedRecord));

  rec.Number := 42;
  rec.SmallNumber := 289;
  rec.UseIt := True;
  TBitConverter.From<TMyPackedRecord>(rec, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 42 then halt(4);
  if arr[1] <> 0 then halt(4);
  if arr[2] <> 0 then halt(4);
  if arr[3] <> 0 then halt(4);
  if arr[4] <> 33 then halt(4);
  if arr[5] <> 1 then halt(4);
  if arr[6] <> 1 then halt(4);

  writeln('');
end;

procedure TestFromAnsiChar;
var
  arr: TBytes;
  item: Byte;
  c: AnsiChar;
  i: Integer;
begin
  i := 0;
  c := 'A';
  SetLength(arr, SizeOf(c));
  TBitConverter.From<AnsiChar>(c, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 65 then halt(5);

  writeln('');
end;

procedure TestFromUnicodeChar;
var
  arr: TBytes;
  item: Byte;
  c: UnicodeChar;
  i: Integer;
begin
  i := 0;
  c := 'A';
  SetLength(arr, SizeOf(c));
  TBitConverter.From<UnicodeChar>(c, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 65 then halt(6);
  if arr[1] <> 0 then halt(6);

  writeln('');
end;

procedure TestInToInteger;
var
  arr: TBytes;
  num: Integer;
begin
  arr := TArray<Byte>.Create(1, 0, 1, 0);

  num := TBitConverter.InTo<Integer>(arr, 0);
  writeln(num.ToString);

  if num <> 65537 then halt(7);

  writeln('');
end;

procedure TestInToDouble;
var
  arr: TBytes;
  num: Double;
begin
  arr := TArray<Byte>.Create($1C, $C7, $71, $1C, $C7, $71, $BC, $3F);

  num := TBitConverter.InTo<Double>(arr, 0);
  writeln(num.ToString);

  if num <> Double(0.1111111111111111111) then halt(8);

  writeln('');
end;

procedure TestInToTMyRecord;
var
  arr: TBytes;
  rec: TMyRecord;
begin
  arr := TArray<Byte>.Create(66, 0, 0, 0, 15, 0, 0, 0);

  rec := TBitConverter.InTo<TMyRecord>(arr, 0);
  writeln(rec.Number.ToString);
  writeln(rec.SmallNumber.ToString);
  writeln(BoolToStr(rec.UseIt, True));

  if rec.Number <> 66 then halt(9);
  if rec.SmallNumber <> 15 then halt(9);
  if rec.UseIt <> False then halt(9);

  writeln('');
end;

procedure TestInToTMyPackedRecord;
var
  arr: TBytes;
  rec: TMyPackedRecord;
begin
  arr := TArray<Byte>.Create(255, 1, 0, 0, 15, 0, 1);

  rec := TBitConverter.InTo<TMyPackedRecord>(arr, 0);
  writeln(rec.Number.ToString);
  writeln(rec.SmallNumber.ToString);
  writeln(BoolToStr(rec.UseIt, True));

  if rec.Number <> 511 then halt(10);
  if rec.SmallNumber <> 15 then halt(10);
  if rec.UseIt <> True then halt(10);

  writeln('');
end;

procedure TestInToAnsiChar;
var
  arr: TBytes;
  c: AnsiChar;
begin
  arr := TArray<Byte>.Create(65);

  c := TBitConverter.InTo<AnsiChar>(arr, 0);
  writeln(c);

  if c <> 'A' then halt(11);

  writeln('');
end;

procedure TestInToUnicodeChar;
var
  arr: TBytes;
  c: UnicodeChar;
begin
  arr := TArray<Byte>.Create(66, 0);

  c := TBitConverter.InTo<UnicodeChar>(arr, 0);
  writeln(c);

  if c <> 'B' then halt(12);

  writeln('');
end;

procedure TestFromIntegerOffset;
var
  arr: TBytes;
  item: Byte;
  i: Integer;
begin
  i := 0;
  SetLength(arr, SizeOf(Integer) + 2);
  TBitConverter.From<Integer>(257, arr, 2);
  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  if arr[0] <> 0 then halt(13);
  if arr[1] <> 0 then halt(13);
  if arr[2] <> 1 then halt(13);
  if arr[3] <> 1 then halt(13);
  if arr[4] <> 0 then halt(13);
  if arr[5] <> 0 then halt(13);

  writeln('');
end;

procedure TestInToIntegerOffset;
var
  arr: TBytes;
  num: Integer;
begin
  arr := TArray<Byte>.Create(2, 0, 2, 0, 1, 0, 2, 0);

  num := TBitConverter.InTo<Integer>(arr, 2);
  writeln(num.ToString);

  if num <> 65538 then halt(14);

  writeln('');
end;
{
procedure TestInToAnsiString;
var
  arr: TBytes;
  str: AnsiString;
begin
  arr := TArray<Byte>.Create(44, 44, 45, 67, 66);
  
  str := TBitConverter.InTo<AnsiString>(arr, 0);
  writeln(str);

  if str <> 'hello' then halt(15);

  writeln('');
end;

procedure TestFromAnsiString;
var
  arr: TBytes;
  item: Byte;
  str: AnsiString;
  i: Integer;
begin
  i := 0;
  str := 'hello world!';
  SetLength(arr, Length(str));
  TBitConverter.From<AnsiString>(str, arr);

  for item in arr do
  begin
    Inc(i);
    writeln(i.ToString + ': ' + item.ToString);
  end;

  writeln('');
end;
}
begin
  {* testing TBitConverter.From<T> *}
  TestFromInteger;
  TestFromDouble;
  TestFromTMyRecord;
  TestFromTMyPackedRecord;
  TestFromAnsiChar;
  TestFromUnicodeChar;

  {* testing TBitConverter.InTo<T> *}
  TestInToInteger;
  TestInToDouble;
  TestInToTMyRecord;
  TestInToTMyPackedRecord;
  TestInToAnsiChar;
  TestInToUnicodeChar;

  {* testing offset *}
  TestFromIntegerOffset;
  TestInToIntegerOffset;

  {* non base types *}
  //TestInToAnsiString;
  //TestFromAnsiString;

  readln;
end.
TBitConverter_tests_casts.pas (7,011 bytes)   

Sven Barth

2020-05-17 23:01

manager   ~0122893

Last edited: 2020-05-17 23:01

View 2 revisions

I've adjusted the test to handle big endian as well. Also I've disabled the inline directive for now until we've added support for open array parameters (especially if they're simply passed through).

Thank you for the patch. Please test and close if okay.

Issue History

Date Modified Username Field Change
2020-05-09 20:15 Bi0T1N New Issue
2020-05-09 20:15 Bi0T1N File Added: TBitConverter_tests.pas
2020-05-09 20:15 Bi0T1N File Added: 01-Add_TBitConverter_class.patch
2020-05-09 23:46 Sven Barth Note Added: 0122689
2020-05-10 17:03 Bi0T1N Note Added: 0122700
2020-05-10 17:03 Bi0T1N File Added: 02-Add_TBitConverter_class.patch
2020-05-10 17:04 Bi0T1N Note Edited: 0122700 View Revisions
2020-05-15 15:30 Sven Barth Assigned To => Sven Barth
2020-05-15 15:30 Sven Barth Status new => assigned
2020-05-15 17:04 Sven Barth Note Added: 0122820
2020-05-15 19:45 Bi0T1N Note Added: 0122824
2020-05-15 19:45 Bi0T1N File Added: TBitConverter_tests_casts.pas
2020-05-17 18:47 Bi0T1N Note Edited: 0122700 View Revisions
2020-05-17 23:01 Sven Barth Status assigned => resolved
2020-05-17 23:01 Sven Barth Resolution open => fixed
2020-05-17 23:01 Sven Barth Fixed in Version => 3.3.1
2020-05-17 23:01 Sven Barth Fixed in Revision => 45409
2020-05-17 23:01 Sven Barth FPCTarget => -
2020-05-17 23:01 Sven Barth Note Added: 0122893
2020-05-17 23:01 Sven Barth Note Edited: 0122893 View Revisions
2020-05-19 12:55 Bi0T1N Status resolved => closed