Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Bit-Schiebereien (https://www.delphipraxis.net/180297-bit-schiebereien.html)

Kostas 8. Mai 2014 16:47

Bit-Schiebereien
 
Hallo Zusammen,

ich bekomme über TCP von einem Device Bytes gesendet und muss die Nutzdaten herausziehen und zwar möglichst schnell.
Auf Assemblerebene muss nicht sein das ich mich damit eh nicht auskenne.
Zu meiner Schande muss ich gestehen dass ich nie mit Bit-Schiebereien zu tun habe und deshalb nicht weis wie das geht.

Ich habe z.b. 0x30 das ist binär 110000 davon muss ich die letzten zwei bits entfernen also bleibt 1100 das wiederum
dezimal umgewandelt ergibt 12. Der Punkt ist, es muss schnell gehen. Ich muss aus einem Byte LSB, MSB oder auch z.b.
bit 2-6 auslesen und dezimal umwandeln.

Kann mir jemand dabei helfen?

Gruß Kostas

DeddyH 8. Mai 2014 17:08

AW: Bit-Schiebereien
 
Hilft Dir das hier weiter?

Kostas 9. Mai 2014 09:33

AW: Bit-Schiebereien
 
vom Lehrbuch her ist mir das schon bekannt.
Wenn ich ein "komplettes" Byte habe ist das noch machbar.

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var b:Byte;
begin
  b := $30; //110000
  Label1.caption := IntToStr(Ord(b shr 2)); //x shr 2 macht daraus 001100 und Ord die gewünschte 12 fertig
end;
Doch wie gehe ich hier vor:
Ich bekomme eine Reihe von ca. 60 Bytes (480Bits) und muss sie eigentlich im Ganzen betrachten.
von diesem String muss ich Bit 173-178 herausziehen und dezimal ausgeben.
Muss ich berechnen in welchem Byte(s) die Bits 173-178 stecken? sie werden sich sicherlich in mehrere Bits aufteilen.
Ich kann ja nicht 21,6 bis 22,26 Bytes ausschneiden.
Die Möglichkeit den String könnte ich Binär umwandeln und die Bits herausziehen. Das dauert zu lange.
Die Frage ist, ob es eine effiziente Möglichkeit gibt aus einem beliebig langen String bestimmte Bits herauszuziehen.

Gruß Kostas

P.S. Übrigens, danke für den Link. Wird einiges besser erklärt wie in Lehrbüchern.

Sir Rufo 9. Mai 2014 09:37

AW: Bit-Schiebereien
 
Was bekommst du denn jetzt? Bytes oder Strings?

Gut, irgendwie bekommt man ja immer nur Bytes und ein String ist dann nur die Interpretation der Bytes ;)

DeddyH 9. Mai 2014 09:41

AW: Bit-Schiebereien
 
Mehr als 32 bzw. 64 Bit (je nach OS) kannst Du eh nicht am Stück betrachten, es sei denn, jemand kennt einen primitiven Datentypen, der mehr aufnehmen kann. Da wirst Du um Umrechnungen kaum herumkommen, aber wenn es sich um Strings aus Zeichen fester Größe handelt, ist das ja noch zu verschmerzen.

Kostas 9. Mai 2014 09:48

AW: Bit-Schiebereien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258381)
Was bekommst du denn jetzt? Bytes oder Strings?

Gut, irgendwie bekommt man ja immer nur Bytes und ein String ist dann nur die Interpretation der Bytes ;)

Ich bekomme einzelne Bytes. Es ist vorher klar wie viele Bytes kommen werden. Es ist auch definiert welche Bits
aus diesem Byte-Paket die Nutzinformation tragen. Manchmal ist die Nutzinformation in einem Byte, manchmal über mehrere
Bytes verteilt. Was ich habe ist der Index von-bis der Nutzinformation aus dem Byte-Paket. Jetzt geht es darum wie
komme ich am schnellsten an die Nutzinformation heran.

Gruß Kostas

Mavarik 9. Mai 2014 09:52

AW: Bit-Schiebereien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258381)
irgendwie bekommt man ja immer nur Bytes

Genau...

TCP/IP? Also hast Du doch sicher ein TIDBytes Buffer, oder?

Wenn die zu untersuchenden Bit's innerhalb einer Byte-Grenze liegen... Prima
(Byte AND Maske) SHR fertig...
Wenn Dein Pattern über eine Byte-Grenze hinaus geht...

Delphi-Quellcode:
type
    TBuffer : packet Record
               Byte1,
               Byte2   : byte;
               Treffer : word;
               Byte3   : byte;
              end;
var
    Buffer : TBuffer;
begin
  Move(IDBuffer[0],Buffer,length(Buffer));
  Caption := inttostr((Buffer.Treffer and Maske) shr whatever);
end;
Zum Beispiel...

Mavarik

PS: Oder nimm Buffer direkt als TCP/IP Zielpuffer!

Kostas 9. Mai 2014 10:29

AW: Bit-Schiebereien
 
Hallo Mavarik,

Jedes Byte-Paket wandert in einer Queue damit ich es sequenziell abarbeiten kann.
Ein Paket enthält an Vielzahl an Nutzdaten. Die meisten überschreiten die Bytegrenzen.
Am einfachsten scheint mir die Möglichkeit das gesamte Paket in einem langen Binär-String umzuwandeln und
danach die einzelnen Bits herausziehen. Dann ist es mir Egal ob eine Bytegrenze überschritten wurde oder nicht.
Was mich dabei stört ist, das dauert relativ lange.

Die Version mit dem packet Record hab ich nicht verstanden.
Was machst du da genau?
Was hast du angenommen für IDBuffer?

Gruß Kostas

Klaus01 9. Mai 2014 10:51

AW: Bit-Schiebereien
 
.. sind die Daten (60Byte) immer gleich aufgebaut?
Eventuell würde sich dann ja variante Records anbieten.

In etwas so:
Delphi-Quellcode:
type
  TDataType = record
    case boolean of
      true: (rawData: ARRAY[0..59] of Byte);
      false: ( data1: ...
               data2: ...
             )
  end;
Grüße
Klaus

Kostas 9. Mai 2014 11:04

AW: Bit-Schiebereien
 
leider nicht, die Paketgröße variiert stark. Jedes Paket hat im Byte1 eine Typisierung und in Byte2 eine Länge.

Sir Rufo 9. Mai 2014 11:15

AW: Bit-Schiebereien
 
Mit der Typisierung und der Länge kann man schon eine Factory aufbauen, die dann aus einem Stream entsprechende Instanzen erzeugt.

Das Grundproblem wird dadurch aber nicht gelöst.

Wie wäre es mit so einer Lösung
http://stackoverflow.com/questions/2...delphi-records

Würde ich in deinem Fall aber eher als Klasse aufbauen, das Prinzip ist aber gleich

p80286 9. Mai 2014 11:15

AW: Bit-Schiebereien
 
vergiß es, Sir Rufo hat da was viel besseres.

Gruß
K-H

Kostas 9. Mai 2014 11:31

AW: Bit-Schiebereien
 
Hallo Zusammen,

den Record habe ich noch nicht verstanden.
In rawData in mein Paket.
Wie habt ihr die Verarbeitung gedacht?
Wie gesagt, eine Nutzinformation kann sich von einem Bit bis hin zu mehrere Bytes ausbreiten.
Was ich habe ist immer nur der Index für Start und Ende einer Nurtinformation innerhalb eines Paketes in rawData.
Ist die binäre Interpretation von rawData und dann einfach über den Index bequem zugreifen oder doch
berechnen in welchem Byte der Anfang und das Ende der Nurtinformation steckt, die Bytes herausziehen,
die Bits aus dem Anfang-Byte extrahieren, die Bytes mitten drin dazu packen, die Bits vom letzten Byte extrahieren
und dazupacken.

Kostas 9. Mai 2014 11:32

AW: Bit-Schiebereien
 
Zitat:

Zitat von p80286 (Beitrag 1258401)
vergiß es, Sir Rufo hat da was viel besseres.

Gruß
K-H

Die Veriante muss ich noch durchgehen...

Mavarik 9. Mai 2014 11:53

AW: Bit-Schiebereien
 
Zitat:

Zitat von p80286 (Beitrag 1258401)
vergiß es, Sir Rufo hat da was viel besseres.

emm er sprach von schnellem Zugriff... nicht für jedes Bit einen Call...

Sir Rufo 9. Mai 2014 11:55

AW: Bit-Schiebereien
 
Zitat:

Zitat von Mavarik (Beitrag 1258408)
Zitat:

Zitat von p80286 (Beitrag 1258401)
vergiß es, Sir Rufo hat da was viel besseres.

emm er sprach von schnellem Zugriff... nicht für jedes Bit einen Call...

Der Name
Delphi-Quellcode:
GetBits
ist schlecht gewählt, denn mit einem Call wird der gesamte Wert ausgelesen.

Mavarik 9. Mai 2014 11:56

AW: Bit-Schiebereien
 
Nimm einfach einen Varianten Record... Da kopierst Du Dein Status Byte an die richtige Stelle und hast direkten Zugriff auf alle Bit-Kombinationen...

Mavarik

[OT für alte Hasen]
PS.: Boh... Warum lassen uns die Kiddies hier immer rum rätzeln, anstatt direkt mal die Struktur zu posten...
So könnte man die meisten Fragen in einer Antwort beantworten...
[/OT]

Mavarik 9. Mai 2014 11:58

AW: Bit-Schiebereien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258409)
Der Name
Delphi-Quellcode:
GetBits
ist schlecht gewählt, denn mit einem Call wird der gesamte Wert ausgelesen.

OK, trotzdem Rekord Index durch Compiler ist immer noch schneller als ne Property

Mavarik

Sir Rufo 9. Mai 2014 12:21

AW: Bit-Schiebereien
 
Zitat:

Zitat von Mavarik (Beitrag 1258412)
Zitat:

Zitat von Sir Rufo (Beitrag 1258409)
Der Name
Delphi-Quellcode:
GetBits
ist schlecht gewählt, denn mit einem Call wird der gesamte Wert ausgelesen.

OK, trotzdem Rekord Index durch Compiler ist immer noch schneller als ne Property

Mavarik

Ja, könnte man auch nehmen, wenn denn dann die Werte Byte-Weise abgegriffen werden könnten. Das geht aber eben nicht.
01234567
0000000000000010010000000000000000000000000000000000000000000000
Wie holt man denn daraus jetzt den Wert 9 ("1001") per Record Index?
Der Wert muss aus den Bits (von links 0-basierend gezählt) 14-17 kommen.

p80286 9. Mai 2014 12:31

AW: Bit-Schiebereien
 
Zitat:

Zitat von Mavarik (Beitrag 1258412)
OK, trotzdem Rekord Index durch Compiler ist immer noch schneller als ne Property

Dann nagelst Du die "richtige" Struktur darunter und Du bist fein raus. Immer noch besser als aus 60Bytes einen String mit Nullen und Einsen zu machen, und dann womöglich noch die Indexierung durcheinander zu werfen.

@Kostas
Der Trick bei dem varianten Record ist, daß unter verschiedenen Namen, die gleichen Daten erreichbar sind. (OK ist nicht die reine Lehre)

Delphi-Quellcode:
TDataType = record
     case integer of
       0: (rawData: Array[0..59] of Byte);
       1: (data1  : Array[0..29] of Word;
       3: record Tuep:byte; Lang:byte; Wert1:longint;wert2:byte.....;
       4: record Tip :byte; Long:byte; filler1:byte; wert11:word end;
              )
end;
var
  meinwert:TDataType;
Du kannst jetzt über meinwert.rawData[0] meinwert.Tuep und meinwert.Tip auf das erste Byte zugreifen, und es ist immer der selbe Wert.
Mit der Variante 4 hast Du Zugriff auf RawData[3] und RawData[4], Du mußt nur auf die ByteOrder aufpassen. (Ich kann's mir nicht merken)

Gruß
K-H

himitsu 9. Mai 2014 12:32

AW: Bit-Schiebereien
 
Um wieviele Bit wird denn verschoben?

Bei maximal 8 und verschieben nacht rechts:
- man nimmt ein Byte und verschiebt dessen Bits um X nach rechts
- dazu kommen noch die Bits des vorherrigen Bytes, welche um (8-X) Bits nach links verschoben werden
- das Beides dann mit OR verbinden
- und nun nur noch das Ganze für jedes andere Byte machen (vom rechten Byte, bis zum Linken)


Das läßt sich natürlich noch bis Integer/Int64 aufweiten, wobei man da auf "ungerade" Bytes achten muß, welche keine vollen Interger/Int64 ergeben.

gammatester 9. Mai 2014 12:32

AW: Bit-Schiebereien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258400)
Wie wäre es mit so einer Lösung
http://stackoverflow.com/questions/2...delphi-records

Würde ich in deinem Fall aber eher als Klasse aufbauen, das Prinzip ist aber gleich

Allerdings sollte man es dann auch richtig machen. 32 Bits in einem WORD zu verwalten, ist doch wohl etwas vermessen (und keiner der Kommentatoren und Upvoter hat's bemerkt).

Dejan Vu 9. Mai 2014 12:39

AW: Bit-Schiebereien
 
Wie wäre es mit einer kleinen Klasse?
Delphi-Quellcode:
Type
  TBits = Class
  public
    constructor Create (data : TBytes);
    function GetBits (BitFrom, BitTo : Integer) : TBytes;
  End;
Und 'GetBits' macht nichts anderes, als die Bytes aus data von Index BitsFrom/8-BitsTo/8 zu kopieren und um BitsFrom mod 8 zu shiften (den Überlauf vom folgenden Byte einmaskieren), und vom letzten Byte müssen die Bits (BitsTo mod 8 - BitsFrom mod 8) weggeschnippelt werden.

Irgendwie so jedenfalls. Mit Papier und Stift sind die Shiftoffsets und das letzte Maskieren doch schnell zusammengezählt.

Alternativ (aber saulangsam) überführt man den Byte-Stream in ein Array of Boolean und bei der Abfrage erzeugt man aus dem Bits dann wieder einen Byte-Stream.

p80286 9. Mai 2014 12:39

AW: Bit-Schiebereien
 
Zitat:

Zitat von gammatester (Beitrag 1258418)
Allerdings sollte man es dann auch richtig machen. 32 Bits in einem WORD zu verwalten, ist doch wohl etwas vermessen (und keiner der Kommentatoren und Upvoter hat's bemerkt).

Die Idee zählt!
Die praktische Umsetzung ist der zweite Schritt.

Gruß
K-H

himitsu 9. Mai 2014 14:02

AW: Bit-Schiebereien
 
Und wenn man groß hinaus will:
Es gibt unzählige Mathebibliotheken für "große" Zahlen, welche man auch verwenden könnte.

Sir Rufo 9. Mai 2014 14:18

AW: Bit-Schiebereien
 
Wie schnell muss dass denn sein?

Ich frage einen 4-Bit Wert aus 2 Bytes
Code:
1111111001111111
genau 1.000.000 ab und das dauert hier so 18ms.

Wenn das schnell genug ist, dann kann ich den Code hier mal reinstellen.

p80286 9. Mai 2014 14:56

AW: Bit-Schiebereien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258432)
Wenn das schnell genug ist, dann kann ich den Code hier mal reinstellen.

Tu das, ich kann z.Zt. so schlecht tippen:mrgreen:

Gruß
K-H

Sir Rufo 9. Mai 2014 15:52

AW: Bit-Schiebereien
 
Ok, hier der Code.

Durch das Verschieben sollten keine Wert mit mehr als 25bit abgerufen werden, da es sonst zu komischen Ergebnissen kommen kann :)

Es erfolgt keine Überprüfung der Index-Werte ... wer das möchte, der kann sich das ja noch einbauen :)

UPDATE
Ich habe nochmals ca. 2ms weniger (bei 1.000.000 Zugriffen) herausgekitzelt durch eine Änderung in der
Delphi-Quellcode:
GetData
Methode :)
Delphi-Quellcode:
program dp_180297;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.Generics.Collections,
  System.Classes,
  System.SysUtils,
  System.Diagnostics,
  BaseData in 'BaseData.pas',
  BaseDataFactory in 'BaseDataFactory.pas';

type
  TData1 = class( TBaseData )
  public
    property Value1 : Integer index $000604 read GetData; // Offset 6 Bit-Width 4
    property Value2 : Integer index $000010 read GetData; // Offset 0 Bit-Width 16
  end;

procedure ValueCheck( AInstance : TData1 );
begin
  Assert( AInstance.Value1 = 9 );
  Assert( AInstance.Value2 = 65151 );
end;

procedure PerformanceCheck( AInstance : TData1 );
const
  C_ROUNDS = 1000000;
var
  LWatch : TStopwatch;
  LValue : Integer;
  LIdx : Integer;
begin
  LWatch := TStopwatch.StartNew;
  for LIdx := 1 to C_ROUNDS do
    begin
      LValue := AInstance.Value1;
    end;
  LWatch.Stop;

  Writeln( C_ROUNDS, ' Rounds in ', LWatch.ElapsedMilliseconds, 'ms' );
end;

procedure FactoryTest;
var
  LStream : TStream;
  LData : TBytes;
  LInstance : TBaseData;
begin
  TBaseDataFactory.RegisterType( $00, TData1 );

  LStream := TMemoryStream.Create;
  try
    LData := TBytes.Create( $00 { Type } , $02 { Size of Data } , $FE, $7F );
    LStream.Write( LData, Length( LData ) );
    LStream.Seek( 0, soFromBeginning );
    LInstance := TBaseDataFactory.CreateFromStream( LStream );
    try
      ValueCheck( LInstance as TData1 );
    finally
      LInstance.Free;
    end;
  finally
    LStream.Free;
  end;
end;

procedure Main;
var
  LData : TBytes;
  LInstance : TData1;
begin

  LData := TBytes.Create( $FE, $7F );
  // 1111111001111111
  // ......====......
  // Value1 ^ = 1001(bin) = 9(decimal)

  LInstance := TData1.Create( LData );
  try
    ValueCheck( LInstance );
    PerformanceCheck( LInstance );
  finally
    LInstance.Free;
  end;
end;

begin
  ReportMemoryLeaksOnShutdown := True;

  try
    Main;
    FactoryTest;
  except
    on E : Exception do
      Writeln( E.ClassName, ': ', E.Message );
  end;

  ReadLn;

end.
Delphi-Quellcode:
unit BaseData;

interface

uses
  System.SysUtils;

type
  TBaseData = class
  private
    FData : TBytes;
  protected
    function GetData( const Index : integer ) : integer;
  public
    constructor Create( AData : TBytes );
  end;

implementation

{ TBaseData }

constructor TBaseData.Create( AData : TBytes );
begin
  inherited Create;
  FData := AData;
end;

function TBaseData.GetData( const Index : integer ) : integer;
var
  LBitCount : integer;
  LBitOffset : integer;
  LByteOffset : integer;
  LByteIdx : PByte;
  LMask : Byte;
begin
  LBitCount := index and $FF;
  LBitOffset := ( index shr 8 ) and $FFFF;
  LByteOffset := LBitOffset div 8;
  LBitOffset := LBitOffset - LByteOffset * 8;

  LByteIdx := @FData[LByteOffset];

  LMask := ( 1 shl ( 8 - LBitOffset ) ) - 1;
  Result := ( LByteIdx^ and LMask );
  LBitCount := LBitCount - ( 8 - LBitOffset );

  while LBitCount > 0 do
    begin
      Inc( LByteIdx );
      Result := Result shl 8;
      Result := Result or LByteIdx^;
      LBitCount := LBitCount - 8;
    end;

  if LBitCount < 0
  then
    Result := Result shr ( -LBitCount );

end;

end.
Delphi-Quellcode:
unit BaseDataFactory;

interface

uses
  System.Generics.Collections,
  System.Classes,
  System.SysUtils,
  BaseData;

type
  TBaseDataClass = class of TBaseData;

  TBaseDataFactory = class
  private
    class var _Types : TDictionary<Byte, TBaseDataClass>;
  protected
    class constructor Create;
    class destructor Destroy;
  public
    class function CreateFromStream( AStream : TStream ) : TBaseData;
    class procedure RegisterType( AType : Byte; AClass : TBaseDataClass );
  end;

implementation

{ TBaseDataFactory }

class constructor TBaseDataFactory.Create;
begin
  _Types := TDictionary<Byte, TBaseDataClass>.Create;
end;

class function TBaseDataFactory.CreateFromStream( AStream : TStream ) : TBaseData;
var
  LType : Byte;
  LSize : Byte;
  LData : TBytes;
  LClass : TBaseDataClass;
begin
  AStream.Read( LType, SizeOf( LType ) );
  AStream.Read( LSize, SizeOf( LSize ) );
  SetLength( LData, LSize );
  AStream.Read( LData, LSize );

  LClass := _Types[LType]; // Bei einem unbekannten Typen wird hier eine Exception geworfen
  Result := LClass.Create( LData );
end;

class destructor TBaseDataFactory.Destroy;
begin
  _Types.Free;
end;

class procedure TBaseDataFactory.RegisterType( AType : Byte; AClass : TBaseDataClass );
begin
  _Types.AddOrSetValue( AType, AClass );
end;

end.

p80286 9. Mai 2014 16:22

AW: Bit-Schiebereien
 
!Danke!:-D

K-H

Sir Rufo 9. Mai 2014 16:29

AW: Bit-Schiebereien
 
Zitat:

Zitat von p80286 (Beitrag 1258457)
!Danke!:-D

K-H

Bitte :)

Ein kleines Performance-Update habe ich auch noch eingebaut :)

Mavarik 10. Mai 2014 12:48

AW: Bit-Schiebereien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258460)
Zitat:

Zitat von p80286 (Beitrag 1258457)
!Danke!:-D

K-H

Bitte :)

Ein kleines Performance-Update habe ich auch noch eingebaut :)

Sehr nett... Coole Idee, sehr Variable, aber auf keinen Fall so schnell wie ein Varianten Record...

Aber da wir immer noch nicht die Struktur kennen...

Mavarik

himitsu 10. Mai 2014 14:17

AW: Bit-Schiebereien
 
Zitat:

Zitat von Sir Rufo (Beitrag 1258450)
Delphi-Quellcode:
  LClass := _Types[LType]; // Bei einem unbekannten Typen wird hier eine Exception geworfen
  Result := LClass.Create(LData);

Nur, wenn es den Speicherbereich zufällig nicht gibt, oder wenn die Index-Prüfung im Compiler aktiviert wurde.

Aber die Chance ist recht groß, daß es beim Create knallt.

Sir Rufo 10. Mai 2014 22:02

AW: Bit-Schiebereien
 
Zitat:

Zitat von himitsu (Beitrag 1258501)
Zitat:

Zitat von Sir Rufo (Beitrag 1258450)
Delphi-Quellcode:
  LClass := _Types[LType]; // Bei einem unbekannten Typen wird hier eine Exception geworfen
  Result := LClass.Create(LData);

Nur, wenn es den Speicherbereich zufällig nicht gibt, oder wenn die Index-Prüfung im Compiler aktiviert wurde.

Aber die Chance ist recht groß, daß es beim Create knallt.

Öhm, das ist mir bei einem
Delphi-Quellcode:
TDictionary
noch nicht aufgefallen.
Kannst du das mal erläutern?

Sir Rufo 12. Mai 2014 08:59

AW: Bit-Schiebereien
 
Sodele, ich habe die Methode nochmals überarbeitet und jetzt kann man auch die vollen 32bit für einen Integer auslesen, ohne dass es einem um die Ohren fliegt ;)

Im Debug-Mode bleibt es bei ca. 16ms und im Release-Mode sind es ca. 7ms (jeweils für 1.000.000 Abfragen). Ich denke mal, das müsste von der Geschwindigkeit ausreichen :stupid:

Delphi-Quellcode:
unit BaseData;

interface

uses
  System.SysUtils;

type
  TBaseData = class
  private
    FData : TBytes;
  protected
    function GetData( const Index : integer ) : integer;
  public
    constructor Create( AData : TBytes );
  end;

implementation

{ TBaseData }

constructor TBaseData.Create( AData : TBytes );
begin
  inherited Create;
  FData := AData;
end;

function TBaseData.GetData( const Index : integer ) : integer;
var
  LBitCount : integer;
  LBitOffset : integer;
  LByteOffset : integer;
  LByteIdx : PByte;
  LMask : Byte;
begin
  LBitCount := index and $FF;

  if LBitCount > 32
  then
    raise EArgumentOutOfRangeException.CreateFmt( 'Index %x contains invalid BitWidth %x', [index, LBitCount] );

  LBitOffset := ( index shr 8 ) and $FFFFFF;
  LByteOffset := LBitOffset div 8;
  LBitOffset := LBitOffset - LByteOffset * 8;
  LByteIdx := @FData[LByteOffset];

  { TODO : RangeCheck for LByteOffset }

  if LBitCount > 0
  then
    begin
      LMask := ( 1 shl ( 8 - LBitOffset ) ) - 1;
      Result := ( LByteIdx^ and LMask );
      LBitCount := LBitCount - ( 8 - LBitOffset );
    end
  else
    begin
      Result := 0;
      Exit;
    end;

  while LBitCount >= 8 do
    begin
      Inc( LByteIdx );
      Result := Result shl 8;
      Result := Result or LByteIdx^;
      LBitCount := LBitCount - 8;
    end;

  if LBitCount > 0
  then
    begin
      Inc( LByteIdx );
      Result := Result shl LBitCount;
      LMask := ( 1 shl ( 8 - LBitOffset ) ) - 1;

      Result := Result or ( ( LByteIdx^ shr ( 8 - LBitCount ) ) and LMask );
    end;

end;

end.

Blup 12. Mai 2014 14:36

AW: Bit-Schiebereien
 
Ich vermute in der Methode noch Fehler.
Code:
LBitOffset = 11
LBiCount = 18
Byte       0------- 1------- 2------- 3-------
Bits       01234567 89012345 67890123 45678901
Info       00000000 00010000 11111101 11111000
            $00      $08      $BF     $1F
Erg.       10000111 11101111 11000000 00000000
            $E1      $F7      $03      $00
Ergebnis sollte meiner Meinung nach $0003F7E1 sein, tatsächliches Ergebnis $000117E3.
Scheinbar werden am Anfang und Ende die falschen Bits ausgewählt und auch die Reihenfolge der Byte LSB..MSB stimmt nicht.

Delphi-Quellcode:
const
  FData: array[0..3] of Byte = ($00, $08, $BF, $1F);

function TBaseData.GetData( const Index : integer ) : integer;
var
  LBitCount : integer;
  LBitOffset : integer;
  LByteOffset : integer;
  LByteIdx : PByte;
  LMask : integer;
  LShift: integer;
begin
  LBitCount := Index and $FF;

  if LBitCount > 32
  then
    raise EArgumentOutOfRangeException.CreateFmt( 'Index %x contains invalid BitWidth %x', [index, LBitCount] );

  if LBitCount = 0
  then
    begin
      Result := 0;
      Exit;
    end;

  LBitOffset := ( Index shr 8 ) and $FFFFFF;
  LMask     := $FFFFFFFF shr ( 32 - LBitCount );

  LByteOffset := LBitOffset div 8;
  LBitOffset := LBitOffset mod 8;

  { RangeCheck for LByteOffset }
  if (Length(FData) - LByteOffSet) < ((7 + LBitCount) div 8)
  then
    raise EArgumentOutOfRangeException.CreateFmt( 'Index %x contains invalid BitOffset %x for BitWidth %x', [Index, LBitOffset, LBitCount] );

  LByteIdx := @FData[LByteOffset];

  Result := LByteIdx^ shr LBitOffset;
  LShift := ( 8 - LBitOffset );
  Dec( LBitCount, LShift);
  Inc( LByteIdx );

  while LBitCount > 0 do
    begin
{$R-}
      Result := Result or (Integer(LByteIdx^) shl LShift);
{$R+}
      Inc( LShift, 8);
      Dec( LBitCount, 8);
      Inc( LByteIdx );
    end;

  Result := Result and LMask;
end;

Dejan Vu 12. Mai 2014 16:48

AW: Bit-Schiebereien
 
Sollte man den Offset und den Index nicht lieber doch als zwei Parameter übergeben, anstatt das irgendwie von hinten durch die Brust ins Auge umständlich kodieren zu müssen? So richtig einfach und klar ist das ja nicht.

Blup 13. Mai 2014 08:32

AW: Bit-Schiebereien
 
Der Index liegt scheinbar in den Daten bereits in dieser Form codiert vor.
Das Decodieren könnte man sicher in eine eigene Methode auslagern.
Delphi-Quellcode:
procedure TBaseData.DecodeIndex(const AIndex : DWord; out ABitOffset, ABitCount: DWord);
begin
  ABitOffset := (AIndex shr 8) and $FFFFFF;
  ABitCount := AIndex and $FF;
end;
Als Datentyp würde ich für diese Anwendungsfall Integer generell auch durch vorzeichenlose Typen ersetzen.

Mavarik 13. Mai 2014 11:39

AW: Bit-Schiebereien
 
@Kostas Boh ej....

Schon 38 Nachrichten in diesem Thread und wir rätzeln hier immer noch um den Brei rum...

Gib uns doch einfach mal die Struktur die "da ankommt" und Du hast sicherlich in 2 Min den Record um das aus zu lesen...

Mavarik


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:43 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz