Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Frage zum Buffer eines Streams (https://www.delphipraxis.net/96564-frage-zum-buffer-eines-streams.html)

alleinherrscher 26. Jul 2007 21:53


Frage zum Buffer eines Streams
 
Hi!

Ganz simple Frage diesesmal:

Der Read und Write Procedure eines Streams kann man einen Buffer (Void Typ) übergeben, der die Daten enthält, die zu lesen und schreiben sind.

Was ist dieser Buffer intern? Ein Array of Byte?
Kann ich einen Buffer vordefinierter Länge manuell erzeugen und dann an verschiedenen Stellen des Buffers selber Daten reinschreiben? Also wenn ich z.B. 3 "Buffer" haben...kann ich die irgendwie zu einem Buffer vereinigen?


Danke und viele Grüße
Euer Alleinherrscher

sirius 26. Jul 2007 22:01

Re: Frage zum Buffer eines Streams
 
Der Buffer ist einfach ein Stück Spiecher, der dir gehört; den du also reserviert hast. Das kann ein array sein, ein string oder einfach nur ein pointer auf ein stück freien Speicher.

Was willst du denn genau machen?

alleinherrscher 26. Jul 2007 22:13

Re: Frage zum Buffer eines Streams
 
Stell dir vor, ich hätte z.B. 3 verschiedene MemoryStreams,
meine Prozedur soll jedem Stream ein Stückchen "Buffer" lesen können, und am Schluss die drei aus dem Speicher eingelesenen Teile zusammenfügen.

Ich könnte zwar erst aus den drei Streams die jeweiligen Stückchen per Copystream in einen neuen Stream kopieren und dann diesen komplett lesen, aber das erscheint mir etwas doppelt gemoppelt und Speicherauswendig, wenn die Streams groß sind.

Grüße

sirius 26. Jul 2007 22:18

Re: Frage zum Buffer eines Streams
 
Und was ist deine Zielvariable?

Also für string dürfte es so aussehen:
Delphi-Quellcode:
setlength(s,30);
stream1.read(s[1],10);
stream2.read(s[11],10);
stream3.read(s[21],10);
Ähnlich sieht es für arrays aus.

alleinherrscher 26. Jul 2007 22:26

Re: Frage zum Buffer eines Streams
 
Meine Zielvariablen sind unterschiedlich. Einmal nen String, dann wieder n Integer, dann Daten die nen ganzes Bild enthalten und so... kann man das irgendwie "allgemein" machen?

alleinherrscher 28. Jul 2007 15:45

Re: Frage zum Buffer eines Streams
 
*push* ;)

sirius 28. Jul 2007 16:00

Re: Frage zum Buffer eines Streams
 
Du musst nicht nur pushen, sondern deine Frage mal genauer stellen, wenn bisher keiner geantwortet hat :wink:
Ich weis nämlich nicht, was du noch willst.

3_of_8 28. Jul 2007 17:13

Re: Frage zum Buffer eines Streams
 
Prinzipiell ist ein Buffer ein Stück Speicher, von dem du die Anfangsposition und die Länge weißt. Ein Buffer kann ein Integer sein, ein String, ein Record, ein statisches oder dynamisches Array - kurz, alles, was Speicher braucht.

Wenn du mehrere Variablen hast, geht das so:

Delphi-Quellcode:
var a, b: Integer;
    c: String;
    d: TMyRecord;
    e: Single;
    f: array of Integer;
    g: array[0..2] of Single;
    s: TStream;
begin
  //...
  s.Write(a, 4); //oder s.Write(a, sizeof(a)); oder s.Write(a, sizeof(Integer));
  s.Write(b, 4);
  s.Write(c, length(c)); //Aufpassen bei Multibyte-Char-Strings
  s.Write(d, sizeof(d));
  s.Write(e, 4); //oder s.Write(e, sizeof(e)); usw.
  s.Write(f[0], length(f)*4); //oder s.Write(f[0], length(f)*sizeof(Integer));
  s.Write(g[0], length(g)*4); //oder s.Write(g, length(g)*4); oder s.Write(g, length(g)*sizeof(Single));
  //...
end;

alleinherrscher 28. Jul 2007 19:35

Re: Frage zum Buffer eines Streams
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich kann euch ja mal meinen Quelltext so far zuschicken. Es geht um einen etwas erweiteren Stream. Man soll ihn aus Dateien einlesen können und dann in den Stream schreiben können, ohne dabei die Datei selbst zu ändern oder die gesamte Datei in den Arbeitsspeicher zu laden.

Ich hatte schonmal früher einen Thread dazu gestartet: hier

Delphi-Quellcode:
unit BufferedStream;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;


  type
   aPart = ^TPart;
   TPart = record
      PositionInStream:integer;
      Length:integer;
      FindAtPosition:integer;
   end;

   aBuffer=record
    MemoryStream:TMemoryStream;
    FileStream:TFilestream;
    Swaped:Boolean;
    PartsInfo:array of aPart;
   end;

   TBufferedStream = class(TStream)
     Constructor Create(OpenFromFilename:string;TempDirectory:string);
     function   write(Const Buffer;Count:integer):integer;
     procedure  ManageStreamType;
     procedure  ReadBuffer(var Buffer;Count:integer);
     procedure  WriteBuffer(Const Buffer;Count:integer);
     function   read(var Buffer;Count:integer):integer;
  private
    { Private-Deklarationen }
    aFileStream:TFilestream;
    Buffer:aBuffer;
    swapsize:integer;
    TempFileName:string;
    TempDirectory:string;
    procedure SetPos(newpos:int64);
    procedure setsize(newsize:int64);
  public
    gSize:int64;
    Pos:int64;
    property Position: int64 read Pos write SetPos;
    property Size:    int64 read gSize write Setsize;
    { Public-Deklarationen }
  end;

implementation

function GetTempFileName(TempDirectory:String):string;
var filename:string;
    i:integer;
begin
i:=0;
filename:=inttostr(i)+'.part';
while fileexists(TempDirectory+filename) do
begin
  inc(i);
  filename:=inttostr(i)+'.part';
end;
result:=TempDirectory+filename;
end;

procedure TBufferedStream.setsize(newsize:int64);
begin
  gSize:=newsize;
end;

procedure  TBufferedStream.SetPos(newpos:int64);
begin
  pos:=newpos;
end;

procedure  TBufferedStream.ManageStreamType;
begin
  with Buffer do
  begin
    if Swaped then
      begin
        if FileStream.size<(2/3)*Swapsize then
          begin
            MemoryStream:=TMemoryStream.Create;
            FileStream.Position:=0;
            MemoryStream.CopyFrom(FileStream,FileStream.size);
            freeandnil(Filestream);
            DeleteFile(TempFileName);
            Swaped:=False;
          end
      end
    else
      if MemoryStream.size>Swapsize then
         begin
           Filestream.Create(TempFileName,fmCreate);
           MemoryStream.Position:=0;
           FileStream.CopyFrom(MemoryStream,MemoryStream.size);
           freeandnil(MemoryStream);
           Swaped:=True;
         end;
  end;
end;

procedure  TBufferedStream.WriteBuffer(Const Buffer;Count:integer);
var addsize:integer;
begin

with self.Buffer do
begin
  SetLength(PartsInfo,Length(PartsInfo)+1);
  PartsInfo[Length(PartsInfo)-1]:=new(aPart);
  PartsInfo[Length(PartsInfo)-1].PositionInStream:=Pos;
  PartsInfo[Length(PartsInfo)-1].Length:=Count;

if Swaped then
  begin
    FileStream.Position:=FileStream.size;
    PartsInfo[Length(PartsInfo)-1].FindAtPosition:=Filestream.Position;
    FileStream.Write(Buffer,count);
  end
else
  begin
    if MemoryStream.Size+Count>SwapSize then
      begin
           Filestream:=TFilestream.Create(TempFileName,fmCreate);
           MemoryStream.Position:=0;
           FileStream.CopyFrom(MemoryStream,MemoryStream.size);
           freeandnil(MemoryStream);
           Swaped:=True;
           FileStream.Position:=FileStream.size;
           PartsInfo[Length(PartsInfo)-1].FindAtPosition:=Filestream.Position;
           FileStream.Write(Buffer,count);
      end
    else
      begin
        MemoryStream.Position:=MemoryStream.Size;
        PartsInfo[Length(PartsInfo)-1].FindAtPosition:=MemoryStream.Position;
        MemoryStream.Write(Buffer,Count);
      end;
  end;

end;

addsize:=Count-(gSize-Pos);
Pos:=Pos+Count;

if addsize>0 then
  gSize:=gSize+addsize;

end;




procedure  TBufferedStream.ReadBuffer(var Buffer;Count:integer);
var i:integer;
    len:integer;
    Start:integer;
    Ende:integer;
    PartStart:integer;
    PartEnde:Integer;
    InvolvedParts:TList;
    MyPart:aPart;
    BufferStream:Tmemorystream;
    offset:integer;
    ReadStart,ReadEnd:integer;
begin

//Zunächst alle Parts finden, die ganz oder Teilweise im zu lesenden Streamabschnitt stecken:

InvolvedParts:=TList.Create;

with self.Buffer do
for i:=0 to length(PartsInfo)-1 do
begin
  Start:=Pos;
  Ende:=Pos+Count;
  PartStart:=PartsInfo[i].PositionInStream;
  PartEnde:=PartsInfo[i].PositionInStream+PartsInfo[i].length;
  if  ((PartStart>=Start) and (PartStart<Ende))
    or ((PartEnde>=Start) and (PartEnde<Ende))
    or ((PartStart<=Start) and (PartEnde>=Ende))
  then
     InvolvedParts.Add(PartsInfo[i]);

end;


//Jetzt den Stream zusammensetzten

BufferStream:=TMemorystream.create;

BufferStream.SetSize(count);

if self.aFileStream<>nil then
  begin
    aFileStream.Position:=Pos;
    BufferStream.copyfrom(aFilestream,count);
  end;


while InvolvedParts.count>0 do
begin

  MyPart:=InvolvedParts.First;
  PartStart:=MyPart.PositionInStream;
  PartEnde:=PartStart+MyPart.Length;

  If Start<PartStart then ReadStart:=PartStart
                        else ReadStart:=Start;

  If Ende<PartEnde     then ReadEnd:=Ende
                        else ReadEnd:=PartEnde;

 len:= ReadEnd-ReadStart;


 Bufferstream.position:= Start-ReadStart;


 offset:=PartStart-ReadStart;

 if offset>0 then offset:=0;

 if self.Buffer.swaped then
   begin
    self.Buffer.FileStream.position:=MyPart.FindAtPosition-offset;
    BufferStream.CopyFrom(Self.Buffer.FileStream,len);
   end
 else
   begin
    self.Buffer.MemoryStream.position:=MyPart.FindAtPosition-offset;
    BufferStream.CopyFrom(Self.Buffer.MemoryStream,len);
   end;


 InvolvedParts.Delete(InvolvedParts.IndexOf(MyPart));

end;

Pos:=Pos+count;

BufferStream.Position:=0;
BufferStream.Read(Buffer,count);

freeandnil(BufferStream);


end;














function   TBufferedStream.read(var Buffer;Count:integer):integer;
begin

readbuffer(Buffer,count);

end;




function TBufferedStream.write(Const Buffer;Count:integer):integer;
begin


  WriteBuffer(Buffer,Count);

end;





Constructor TBufferedStream.Create(OpenFromFilename:string;TempDirectory:string);
var i: integer;
begin

Self.TempDirectory:=TempDirectory;

SwapSize:=5*1024;

Pos:=0;
gSize:=0;

TempFileName:=GetTempFileName(TempDirectory);

aFileStream:=Nil;

if OpenFromFileName<>'' then
begin
  aFileStream:=TFilestream.Create(OpenFromFileName,fmOpenRead);
  gSize:=aFilestream.Size;
  aFilestream.Position:=0;
end;

with Buffer do
begin
  MemoryStream:=TMemoryStream.Create;
  Swaped:=False;
  SetLength(PartsInfo,0);
end;





end;

end.

Muetze1 28. Jul 2007 19:50

Re: Frage zum Buffer eines Streams
 
Aber wäre dafür nicht eine Kapselung von Memory Mapped Files (MMF) besser geeignet? Vor allem weniger Aufwand und dabei Erfüllung aller deiner Anforderungen... :gruebel:

alleinherrscher 29. Jul 2007 11:47

Re: Frage zum Buffer eines Streams
 
Mit MemoryMappedFiles kenne ich mich gar nicht aus. Aber ich hab mal ein bisschen gesucht. Irgendjemand meinte hier im Forum mal, dass die eigentlich für andere zwecke eingesetzt werden sollen.

Unterdessen habe ich mal ein bisschen weiterprogrammiert. Das schreiben in meinen BufferedStream und das Lesen funktionieren jetzt eigentlich ganz gut. Ich füge keine Buffer zusammen (wonach ich ursprünglich oben gefragt hatte, sondern ich nehme noch einen zusätzlichen Hilfstream, in den ich alle Speicherteile einfüge und den ich dann am schluss einmal komplett auslese -> dieser Buffer wird dann als result übergeben. (hm schwer zu beschreiben)

Allerdings bekomme ich richtig probleme, wenn ich versuch über die Stream.CopyFrom Funktion Daten aus meinem BufferedStream in einen anderen, z.B: MemoryStream zu kopieren. Dann kommt eine Böse Speicherzugriffsverletzung.

Weiß jemand, welche Prozeduren von TStream aufgerufen werden, wenn man die TStream.CopyFrom(BufferedStream,0) Prozedur aufruft?

Desweiteren habe ich etwas eigenartiges in der Delphi Hilfe gelesen:

Zitat:

TStream ist eine abstrakte bzw, rein virtuelle (C++ Terminologie) Klasse. Erstellen Sie keine Instanzen von TStream. Dieser Typ enthält abstrakte bzw. virtuelle Methoden, die überschrieben werden müssen. Abgeleitete Stream-Objekte wie Speicher- und Datei-Streams, die für Stream-Operationen mit Komponenten eingesetzt werden, werden beim Einsatz der globalen Funktionen ReadComponentRes und WriteComponentRes automatisch erstellt. Wenn andere Arten von Informationen in den Stream gestellt werden sollen, wählen Sie eine abgeleitete Klasse, die den jeweiligen Daten- und Speicherbedarf erfüllt.
Warum soll ich keine Instanzen von TStream erzeugen? Heißt das, dass das was ich vorhab eh nicht funktionieren wird?

Grüße, und vielen Dank für eure Antworten,

Alleinherrscher

sirius 29. Jul 2007 12:36

Re: Frage zum Buffer eines Streams
 
1. TStream ist insofern eine abstrakte Klasse, als dass die wichtigsten Methoden (auf die auch alle anderen mit zugreifen) abstrakt deklariert sind. Ausserdem legt TStream sich noch nicht auf einen Speicherplatz fest (hat also keine Variable o.ä. wo es irgendetwas speichern könnte). Das bedeutet, erst wenn man dem TStream sagt, dass es als Memorystream arbeiten soll, gibt es da eine Pointervariable, die auf den Speicherplatz zeigt und ausgefüllte read und write Methoden, die dort Speichern können. Ähnliches gilt für TFilestream. Also, erst wenn TStream zum Filestream wird, erhält die Sache zum speichern eine Datei und die entsprechenden read und write Methoden.
Dagegen sind andere Methoden, wie Readbuffer und Writebuffer, ReadComponent, etc. pp. schon in TStream vollständig implementiert. Sie benutzen eben immer read und write und ihnen ist dann völlig egal, ob dahinter ein FileHandle oder ein "Memory"-Pointer, oder ein String (TStringStream) liegt.
Neben read und write ist noch SetSize und Seek abstrakt.
Wenn du von TStream ableitest, musst du alle abstrakten Methoden überschreiben (override) und kannst dir eben bei Read und Write deinen eigenen Speichertyp aussuchen. Das ist ja so in etwas das, was du auch willst.

Du hast es nur verkehrtherum gemacht. Du musst nicht readbuffer und writebuffer überschreiben, sondern nur read und write.

2. CopyFrom ruft readbuffer und writebuffer auf. Du kannst doch auch im Source nachschauen.

Edit: :pale: Du kannst doch nicht im Source nachschauen. Hol dir doch mal Turbo Delphi...
Edit2: Achtung beim überschreiben von setsize und seek. Die Methoden sind überladen.

alleinherrscher 29. Jul 2007 13:02

Re: Frage zum Buffer eines Streams
 
Danke, sirius, ich hoffe jetzt komme ich erstmal weiter... ich werd mal versuchen deine tipps umzusetzten und mich dann nochmal melden!
Muss ich also alle überladenen Methoden überschreiben? wie sieht da die deklaration aus?

3_of_8 29. Jul 2007 13:13

Re: Frage zum Buffer eines Streams
 
Delphi-Quellcode:
procedure/function <Name und Parameter>; override;

sirius 29. Jul 2007 13:46

Re: Frage zum Buffer eines Streams
 
Ich habe nochmal (genauer) nachgeschaut.
Nee, nicht alle überladenen Methoden. Die int64 Varianten prüfen !derzeit! nur ob der Bereich innerhalb von longint ist und rufen dann die LongInt varianten auf (ansonsten gibts ne Exception). ==> Es reichen die LongInt Varianten.

Übrigens: SetSize und Seek sind zwar nicht abstrakt, sie haben nur keinen wirklichen Inhalt. Also SetSize(Longint) ist tatsächlich leer und seek überprüft nur ob es eine neue Implementation von Seek gibt. Wenn nicht wird ne Exceptuion geworfen...hätte man es da nicht gleich abstrakt machen können :gruebel:

(Alle Auskünfte ... so weit ich den Code verstanden habe)

alleinherrscher 29. Jul 2007 14:07

Re: Frage zum Buffer eines Streams
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hm, ich hab es jetzt mal so umprogrammiert (ich hänge die entsprechende Unit mal an, ich glaub das ist besser als die hier einzukopieren.

Allerdings bleibt die Zugriffsverletzung (wenn ich z.B TMemoryStream.CopyFrom(TBufferedStream,0) ) teste.

Kannst du evtl. mal meine Deklarationen checken, sirius? Ich hab leider nicht so viel Ahnung von Klassenableitungen und überladen von Funktionen?

Bekommst auch ein Eis von mir ausgegeben, wenn das wetter besser ist und du mal in meiner Nähe bist :D

Tausend Dank!!!!!!!!!!!!!!!!

//edit: Jetzt sagt nicht, ich muss die Read Function auch überladen?!?!?! In der Borland Hilfe stehen ungefähr 15 überladene Funkionen davon... :wall:

sirius 29. Jul 2007 16:30

Re: Frage zum Buffer eines Streams
 
Hmm..ich weis nicht, was du mit den ganzen parts so machst.
Bevor ich da durchsehe und das auseinanderbaue, habe ich mal versucht, deine Absicht in einer anderen Klasse niederzuschreiben.
(Allein, der Sinn fehlt mir noch, aber egal)

Delphi-Quellcode:
unit Unit2;

interface

uses classes;

type tBufferStream=class(TStream)
         constructor create(const OpenFromFileName:string;TempDirectory:string); reintroduce;
         destructor Destroy; override;
       private
         FMemory:TStream;
         TempDir,TempFile:string;
         swapsize:integer;
       protected
         procedure SetSize(NewSize: Longint); override;
       public
         function Read(var Buffer; Count: Longint): Longint; override;
         function Write(const Buffer; Count: Longint): Longint; override;
         function Seek(Offset: Longint; Origin: Word): Longint; override;
end;



implementation

uses sysutils;

function GetTempFileName(TempDirectory:String):string;
var filename:string;
    i:integer;
begin
i:=0;
filename:=inttostr(i)+'.part';
while fileexists(TempDirectory+filename) do
begin
  inc(i);
  filename:=inttostr(i)+'.part';
end;
result:=TempDirectory+filename;
end;


constructor TBufferStream.create(const OpenFromFileName:string;TempDirectory:string);
var f:Tfilestream;
begin

  TempDir:=TempDirectory;

  swapsize:=5*1024;

  if openfromfilename<>'' then
  begin
    f:=Tfilestream.Create(OpenFromFileName,fmopenread);
    if f.size>swapsize then FMemory:=TFilestream.Create(gettempfilename(tempdir),fmcreate)
                       else Fmemory:=TMemoryStream.Create;
    Fmemory.CopyFrom(f,f.size);
    f.free;
  end else Fmemory:=nil;



end;

function Tbufferstream.Write(const Buffer;Count: Longint): Longint;
var tmp:TStream;
begin
  if Fmemory=nil then Fmemory:=Tmemorystream.Create;
  if (Fmemory is TMemoryStream) and
     (Fmemory.Size+count>swapsize) then
  begin
    tempfile:=gettempfilename(tempdir);
    tmp:=TfileStream.Create(tempfile,fmcreate);
    Fmemory.Seek(0,sofrombeginning);
    tmp.CopyFrom(Fmemory,fmemory.size);
    Fmemory.Free;
    FMemory:=tmp;
  end;
  result:=Fmemory.Write(buffer,count)
end;
function TBufferStream.read(var Buffer;Count: Longint): Longint;
begin
  result:=FMemory.Read(buffer,count);
end;
function TbufferStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
  result:=Fmemory.Seek(offset,origin);
end;

procedure Tbufferstream.SetSize(Newsize:longint);
var tmp:TStream;
begin
  if FMemory=nil then Fmemory:=Tmemorystream.Create;

  Fmemory.Size:=newsize;
  if (newsize<swapsize*2/3) and
      (Fmemory is TFilestream) then
  begin
    tmp:=Tmemorystream.Create;
    Fmemory.Seek(0,sofrombeginning);
    tmp.CopyFrom(FMemory,newsize);
    fmemory.Free;
    fmemory:=tmp;
    deletefile(tempfile);
  end;

  if (newsize>swapsize) and
      (Fmemory is TMemorystream) then
  begin
    tempfile:=gettempfilename(tempdir);
    tmp:=TFilestream.Create(tempfile,fmcreate);
    Fmemory.Seek(0,sofrombeginning);
    tmp.CopyFrom(FMemory,newsize);
    fmemory.Free;
    fmemory:=tmp;
  end;

end;

destructor TbufferStream.Destroy;
begin
  if Fmemory<>nil then
  begin
    if FMemory is TFileStream then
    begin
      Fmemory.Free;
      deletefile(TempFile);
    end
    else Fmemory.Free;
  end;
end;

end.
Jetzt ist mir auch übrigens klar, was deine Eingangsfrage war. Wenn der Buffer untypisiert ist, dann musst du eben auch ganz untypisiert rangehen:
Delphi-Quellcode:
var buffer:pointer;
begin
  GetMem(Buffer,10);
  Stream.ReadBuffer(Buffer^,10);
  //...

  FreeMem(Buffer,10);
end;

alleinherrscher 29. Jul 2007 17:55

Re: Frage zum Buffer eines Streams
 
DANKE!!!

Ich werds direkt probieren!!!!! Mensch, wenn das klappt, das wäre richtig toll...

Wenn du magst schreib ich dir nochmal was zu dem "Sinn und Zweck", jedenfalls so wie ich mir das gedacht habe. Aber ich versuche erstmal weiterzukommen ;)

Lg, Alleinherrscher

//edit: Du kopierst am Anfang die Daten einmal in einen neuen Stream! Im grunde eine gute Idee, aber was passiert, wenn die Datei, die in den Stream soll, richtig groß, z.B. 1GB ist? dann muss er doch den gesamten 1GB File praktisch komplett kopieren oder? Genau das gilt es ja zu verhindern um schneller arbeiten zu können...aber ich werde es einfach mal testen, vielleicht ists so ja auch schnell!

edit2: ich habe mal schnell eine kleine Grafik gemacht, was ich überhaupt genau will...

http://lanfs.la.funpic.de/Privat/Unbenannt.jpg



Vielen vielen Dank für die ganze Mühe!!!

sirius 29. Jul 2007 18:29

Re: Frage zum Buffer eines Streams
 
Ahhh, ok.
Sehr interessante Komponente. die Aufgabe erfüllt meine allerdings nicht. Ich hatte dem aFileStream nicht so ein große Beachtung geschenkt, wie es notwendig wäre.

Was wäre, wenn du in den MemoryStream auch gleich die Positionen mit speichern würdest?

Edit: Es haben sich grad so ziemlich alle Fragen meinerseits bezüglich deines Codes geklärt. :wink:
Aber einiges musst du noch ändern. CopyFrom liefert z.B. Fehler, wenn du über das Ende des Streams hinausliest:
Delphi-Quellcode:
aFileStream.Position:=Pos;
BufferStream.copyfrom(aFilestream,count);
Hier ist (so weit ich sehe) nicht sichergestellt, dass Pos+Count < aFileStream.size.

Und bei mir siehst du wofür abstrakte Methoden nützlich sind. Ich brauche nicht ein Memorystream und einen Filestream extra. ich habe einfach nur einen Stream und trotzdem kann ich die Methoden read und write benutzen, da ich den Stream mit TMemorystream oder eben TFilestream erzeuge. Und ich bruache auch keine Variable swapped, da ich meine Stream-Variable abfragen kann, was sie gerade ist.

alleinherrscher 29. Jul 2007 18:34

Re: Frage zum Buffer eines Streams
 
Genau das hatte ich auch zuerst vor, aber macht die sache nicht wesentlich einfacher oder? *grübel* naja, ich versuche jetzt mal, in deine struktur meine Idee mit dem zusätzlichen stream einzubauen... hoffe es klappt irgendwie...

sirius 29. Jul 2007 18:43

Re: Frage zum Buffer eines Streams
 
Zitat:

Zitat von alleinherrscher
Genau das hatte ich auch zuerst vor, aber macht die sache nicht wesentlich einfacher oder? *grübel* naja, ich versuche jetzt mal, in deine struktur meine Idee mit dem zusätzlichen stream einzubauen... hoffe es klappt irgendwie...

Da ist die Frage, ob deins oder meins als Grundlage jetzt besser ist :gruebel:


Edit: Wer ruft den die Read-Methode auf?
Wahrscheinlich wäre es noch sinnvoll eine savetoFile etc. Methode zu implementieren.

alleinherrscher 29. Jul 2007 18:52

Re: Frage zum Buffer eines Streams
 
@sirius:

Hm allerdings ist meins etwas unverständlich oder? Blickst du da durch?

zu deinem edit: beides wäre brauchbar: ein Read und ein SavetoFile. Aber wenn du das Read hast, ist das Savetofile doch vom selben Prinzip oder?


Und jap du hast recht, die Abstrakte Lösung ist wirklich knackig kurz und besser :)

//edit: die READ methode brauche ich später im "externen Programm"...oder wie meinst du die frage?

alzaimar 29. Jul 2007 18:54

Re: Frage zum Buffer eines Streams
 
Ich würde das in mehreren Schritten angehen:
1. Speichern der Änderungen in einer separaten Struktur:
Delphi-Quellcode:
TSavedItem = Record
  siPos,
  siSize : Cardinal;
  siData : Pointer;
End;
Wenn man einen weiteren Wert der Größe N an Stelle P speichert, und es existiert ein 'Saved Item' S an der Position P-N, dann kann man S erweitern (siPos += N). Existiert ein saved Item an der Stelle S.siPos = P+N, dann kann man S wieder erweitern (siPos -= N, siSize += N).

2. Sicherstellen, das bei einem READ auf dem Stream die unveränderten und veränderten (Saved Item Liste) korrekt zurückgeliefert werden.
3. Speichern der Saved Item Liste in einem Stream, allerdings würde ich hier die Liste im Speicher, und nur die siData-Teile auslagern. Dies dürfte sehr komplex werden, wenn es optimal performant werden soll.

Eigentlich handelt es sich um einen Stream, der mit Transaktionen arbeitet. Es gibt so eine Komponente schon, ich meine, ich hätte sie bei Torry gesehen...

alleinherrscher 29. Jul 2007 18:57

Re: Frage zum Buffer eines Streams
 
Zitat:

Zitat von alzaimar
m einen Stream, der mit Transaktionen arbeitet. Es gibt so eine Komponente schon, ich meine, ich hätte sie bei Torry gesehen...

Wenn es sowas gibt, wäre das natürlich super.

Achja und:

Zitat:

1. Speichern der Änderungen in einer separaten Struktur:
Delphi-Quellcode: markieren
TSavedItem = Record
siPos,
siSize : Cardinal;
siData : Pointer;
End;

Wenn man einen weiteren Wert der Größe N an Stelle P speichert, und es existiert ein 'Saved Item' S an der Position P-N, dann kann man S erweitern (siPos += N). Existiert ein saved Item an der Stelle S.siPos = P+N, dann kann man S wieder erweitern (siPos -= N, siSize += N).

Guck dir mal meinen Quelltext an. Genauso ists gemacht ;)

sirius 29. Jul 2007 19:42

Re: Frage zum Buffer eines Streams
 
Zitat:

Zitat von alleinherrscher
Hm allerdings ist meins etwas unverständlich oder? Blickst du da durch?

Jetzt, wo ich das Ziel sehe, schon deutlich besser :mrgreen:
Zitat:

Zitat von alleinherrscher
zu deinem edit: beides wäre brauchbar: ein Read und ein SavetoFile. Aber wenn du das Read hast, ist das Savetofile doch vom selben Prinzip oder?

Naja, wenn man von Performance spricht, hatte ich mir gedacht, dass ein savetofile aus deinem Bilderbeispiel besser ohne Read und copyFrom auskommen sollte. (Aber vielleicht geht das gar nicht)


Zitat:

Zitat von alleinherrscher
//edit: die READ methode brauche ich später im "externen Programm"...oder wie meinst du die frage?

Dein Read killt performanctechnisch doch so ziemlich alles, was du dir vorher gedacht hattest. aber wenn du sie brauchst, würde ich gerne nochmal auf deine Eingangsfrage zurückkommen. so einfach, wie meine Read-Methode geht es natürlich nicht. Aber ein extra BufferStream sollte man hier evtl. auch vermeiden.

Also, wie in einen Buffer schreiben?
Delphi-Quellcode:
procedure xyz.read(var buffer);
var buf:pointer;
begin
  buf:=@buffer;
 
  filestream.read(buf^,10);
  buf:=pointer(cardinal(buf)+10);
  filestream.seek(100,sofromcurrent);
  filestream.read(buf^,3);
  buf:=pointer(cardinal(buf)+3);
  ...
end;

alleinherrscher 30. Jul 2007 13:09

Re: Frage zum Buffer eines Streams
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hey@all und Hi Sirius!

Ich hab die Änderungen mal eingebaut. Muss sagen, bis jetzt haben alle meine Tests funktioniert. Wenn du magst, kannst du (natürlich auch gerne alle anderen) mal testen, obs noch Bugs gibt. Falls dies okay ist, fehlen natürlich noch eine optimierte Version von SaveToFile.

Wie würdet ihr die angehen, ohne Read und CopyFrom zu benutzten...und ohne die Performence zu zerstören?

Grüße Alleinherrscher

//edit: noch einen Fehler entdeckt, bei CopyFrom wenn man eine Datei als Filestream eingeladen hat, verändert, dann den gesamtem Stream kopiert sind die änderungen weg...ich werd mich mal dranbegeben...

sirius 30. Jul 2007 13:42

Re: Frage zum Buffer eines Streams
 
1. Den Filestream musst du noch im Destructor freigeben
2. Die (meine) Funktion setsize funktioniert so nicht mehr. Besonders in dem Fall, wenn die Größe verkleinert wird. Denn dann kannst du ja Teile aus dem FStream löschen bzw. den Filestream kürzen.
3. Wolltest du nicht kleine Dateien statt in den FileStream erstmal in FStream laden?
4. in seek musst du evtl noch mySize anpassen (Wenn myPosition>mysize)

Zu edit (Denk daran, dass du auch bei der Anwendung von copyFrom Fehler machen kannst (ich vergesse meistens die Position zu verändern)


zu2:
Delphi-Quellcode:
procedure  TBufferstream.SetSize(Newsize:longint);
begin
  if Newsize<mysize then begin
    //Speicher freigeben (ist allerdings nicht notwendig)

   if Filestream.size<newsize then Filestream.size:=newsize; //evtl, oder du lässt die Datei bestehen
   
   //und hier jetzt für jeden Part in FStream, schauen ob er gekürzt werden kann
 
  end;
  mysize:=newsize;
end;

alleinherrscher 30. Jul 2007 13:51

Re: Frage zum Buffer eines Streams
 
Okay, das werde ich sofort ändern, danke!

//edit zu deinem Punkt drei:
Ja, aber ich glaube so ist es einfacher, weil wir nicht noch zusätzlich die Fälle unterscheiden müssen , ob der Stream jetzt "bearbeitet werden darf" oder nicht... arg: Deutsche Sprache ist zu beschränkt ;)

Noch ne grundsätzliche verständnisfrage zu den Pointern (die Deklaration vom Buffer):

Ich lese ja im Augenblick in der READ function erst den Filestream mit

Filestream.read(buf^,count);

Will ich jetzt an einer bestimmten Position ein Datenpacket aus dem MemoryStream einsetzten, wie geht das?

Ich hab es ja im Augenblick so gelöst:

Delphi-Quellcode:

  BufStart:=cardinal(buf);

if FileStream<>nil then
  begin
    FileStream.Seek(MyPosition,soFromBeginning);
    NumberOfReadByte:=Filestream.Read(buf^,count);
  end;

//und dann um das Paket irgendwie in die Mitte vom Buffer zu setzen:

buf:=pointer(BufStart+(Start-ReadStart));

//...

 FMemory.position:=MyPart.FindAtPosition-offset;
 FMemory.Read(buf^,len);
Aber scheinbar funktioniert dies nicht sauber?!

Grüße

sirius 30. Jul 2007 14:02

Re: Frage zum Buffer eines Streams
 
Oh ja, da scheint mir noch folgende Zeile zu fehlen:
Delphi-Quellcode:
if FileStream<>nil then
    begin
      FileStream.Seek(MyPosition,soFromBeginning);
      NumberOfReadByte:=Filestream.Read(buf^,count);
     
      buf:=pointer(cardinal(buf)+NumberOfReadByte); //<--- die hier
    end;
Und an der Zeile ist auch noch was komsich:
Delphi-Quellcode:
result:=NumberOfReadByte
Was ist wenn ein Block aus FMemory über die Länge von Filestream hinausgeht?
Was ist wenn von außen die erwartete Größe (=mySize) ungleich NumberOfReadByte ist? (Dann gibt übrigens copyfrom irgendsoein EReadError.)

alleinherrscher 30. Jul 2007 14:11

Re: Frage zum Buffer eines Streams
 
OMG, ja das is ja ein Dummer Fehler, ich war bis jetzt immer davon ausgegangen, dass mein Filestream länger ist, als alles andere... Aber man kann ja auch daten HINTER den Filestream schreiben...dummdummdumm... sorry! :wall:

Aber wenn ichs so mache, wie du oben getextet hast:

Delphi-Quellcode:
if FileStream<>nil then
    begin
      FileStream.Seek(MyPosition,soFromBeginning);
      NumberOfReadByte:=Filestream.Read(buf^,count);
     
      buf:=pointer(cardinal(buf)+NumberOfReadByte); //<--- die hier
    end;
Dann würden doch bei einem weiteren Read vorgang die Daten an das ENDE von Buffer weitergeschrieben, richtig? Gibt es keine Möglichkeit (so ähnlich wie beim Stream selbst), mitten in den Buffer nochmal was reinzuschreiben bzw einlesen zu lassen?

Ach, sonst muss ich es eben etwas umschreiben...ich schau mal, danke erstmal! Das is noch ne Menge Arbeit, merke ich grade...

sirius 30. Jul 2007 14:17

Re: Frage zum Buffer eines Streams
 
Zitat:

Zitat von alleinherrscher
Dann würden doch bei einem weiteren Read vorgang die Daten an das ENDE von Buffer weitergeschrieben, richtig? Gibt es keine Möglichkeit (so ähnlich wie beim Stream selbst), mitten in den Buffer nochmal was reinzuschreiben bzw einlesen zu lassen?

:wall: Schon wieder vergessen, dass die einzelnen Blöcke ja auch den Filestream "überschreiben" können.
Dann ist die Zeile natürlich Quark.

Dann dürfte doch alles funktionieren (wenn die Rechnung mit Start und ReadStart stimmt). Du kannst problemlos überschreiben.

Buf (bzw. buffer) zeigen doch nur auf ein Stück Hauptspeicher. Und dort schreibst du rein ohne dich für den Inhalt oder für die Speicherreservierung zu kümmern. Der Inhalt ist dir eh wurscht und die Speicherreserveirung muss bei allen Streams die aufrufende Funktion machen.

alleinherrscher 30. Jul 2007 16:37

Re: Frage zum Buffer eines Streams
 
Liste der Anhänge anzeigen (Anzahl: 1)
Okay, fehler in der Positionsberechnung von ReadStart behoben. Also bei mir läufts jetzt ganz gut...

SaveToFile ist allerdings noch buggy...

//edit: wieder ein paar Fehler bzgl. Setsize gefunden...

shmia 30. Jul 2007 17:00

Re: Frage zum Buffer eines Streams
 
Die Klasse TBufferStream sollte so konstruiert sein, dass man ein beliebiges TStream-Objekt übergibt (anstelle eine Dateinamen).
Wie man's richtig macht kann man z.B. in der Unit JclStreams der JCL nachschauen.

alzaimar 2. Aug 2007 06:55

Re: Frage zum Buffer eines Streams
 
Erstmal muss ich sagen: Echt nette Komponente. :thumb:

Ein paar Sachen fallen mir auf:

Wieso swapt ihr eigentlich selber FMemory auf die Platte? Kann man das nicht auch Windows überlassen? Ich meine, das Windows das bessere Memory management hat, oder irre ich mich? Ich halte das auch für eine Performancebremse, denn entweder habt ihr ALLES im RAM, oder ALLES auf der Platte...

Ich hab den Code nur überflogen, aber mir fällt da eine kleine Schwachstelle auf:
Wenn ich mehrmals an die gleiche Stelle etwas schreibe, dann hängt ihr trotzdem immer einen 'aPart' an die PartsInfo-Liste an. Das ist überflüssig. Das ist mit der (zugegebenermaßen ziemlich einfach wie genialen) Methode mit dem einen FMemory - Stream nicht optimal lösbar. Wenn man jedoch davon ausgeht, das in dem Stream nicht sonderlich viel überschrieben wird, ist das nicht so wild.

Die Routine GetTempName ist blöd, nehmt lieber die WinAPI-Version
Delphi-Quellcode:
Function TempPath: String;
Var
  i: integer;

Begin
  SetLength(Result, MAX_PATH);
  i := GetTempPath(Length(Result), PChar(Result));
  SetLength(Result, i);
  IncludeTrailingPathDelimiter(Result);
End;

Function TempFilename: String;
Begin
  SetLength(Result, MAX_PATH + 1);
  GetTempFileName(PChar(TempPath), 'CSI', 0, PChar(result));
End;
In der SaveToFile-Methode steckt ein Denkfehler:
Delphi-Quellcode:
...
   if i>=PartsInfo.Count-1 then //alle Elemente in FMemory sind Sortiert hinterheinander
       if (FMemory is TMemoryStream) then // => FMemory ist bereits fertiger Stream!  DENKFEHLER!!!
          TMemoryStream(FMemory).SaveToFile(Filename)
...
Also: Ich schreibe an Pos:1 und Pos:100 jeweils ein Byte (z.B. A und B). Dann steht in PartsInfo (Pos:1, Size:1, FindAtPosition:0) sowie (Pos:100, Size:1, FindAtPosition:1) und der FMemory-Stream enthält 'AB'. Das ist aber nicht der fertige Stream, oder irre ich mich?

Ihr könntet die einzelnen Parts aus Partinfo jeweils mit
Delphi-Quellcode:
  FMemory.Seek (FindAtPositon, soFromBeginning);
  stDestStream.Seek (PositionInStream, soFromBeginning);
  stDestStream.CopyFrom (FMemory, Length);
in den Ziel-Stream schreiben

Na ja, und dann wären da noch ein paar kosmetische Kleinigkeiten: aPart ist ein *schlechter* Bezeichner für einen Datentypen. Auch würde ich daraus eine Klasse machen.

Ansonsten: Echt Cool.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:08 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