![]() |
Ringspeicher als TStream
Hi,
hat jemand schonmal einen Ringspeicher auf TStream Basis geschrieben? Irgendwie bekomme ich das ganze nicht hin. Der Hintergrund ist, dass ich LibVLC einen Stream übergeben will, den ich in einem separaten Thread befülle. Peter |
AW: Ringspeicher als TStream
Zitat:
um Daten entkoppelt rein- rauszuschreiben. Darin ist mit WriteBuffer / ReadBuffer die Basisfunktion gemacht, aber auch andere übliche Varianten Write/Read habe ich implementiert. Man muss dann noch eine Menge drumrum bauen um das thread sicher zu machen, und falls nötig dynamisches Allozieren zu ermöglichen. Funktioniert aber mit TMemoryStream hervorragend. Hatte mal das hier als Basis genommen. ![]() |
AW: Ringspeicher als TStream
Nja, erstmal braucht dein Stream zwei Positionszeiger. Einer für Lesen und einer für Schreiben.
Das Size müsste man auch verdoppeln, für Lesen und Schreiben. Jenachdem ob die externe Klasse lesen oder schreiben soll (hierfür Lesen), würde dann Size und Position auf den Lesezeiger gehen und bis zum Schreibzeiger die Größe. Für deinen Schreibzugriff dann über neue SizeIrgendwas/PositionIrgendwas-Property auf den Schreibzeiger, bis zum Lesezeiger gehend. Ein zussätzliches SizeNochwas oder einfach das Capacity für die Größe des Speichers. Kannst hierfür ja den TMemoryStream oder TBytesStream verwenden. Bei Denen könnte man auch Extern nur den einen Zugriff zulassen (hier Lesen) und das Schreiben verbieten (Datenmenge=0 oder Exception beim Zugriff) und da direkt in den Speicher (.Memory bzw. .Bytes) reinschreiben. Alternativ könnte man auch zwei Stream-Klassen verbinden: Eine zum Schreiben und die Andere zum Lesen, als Subkomponente, mit gemeinsamen Speicher. Man könnte aber auch zwei "unabhängige" THandleStream verwenden und z.B. mit einer Pipe bzw. Named-Pipe verbinden. (die Pipe hat dabei in sich quasi sowas wie einen Ring-Puffer ... jenachdem wie Windows den Speicher behandlet: Verschieben oder im Kreis) |
AW: Ringspeicher als TStream
Danke, ich habe das jetzt wie folgt umgesetzt:
Delphi-Quellcode:
Ob es geht, wird sich zeigen :)
unit URingBuffer;
{$mode objfpc}{$H+} interface uses Classes, SysUtils, SyncObjs; type TCircleBuffer = class(TStream) private FData: PByte; FSize: integer; FLock: TCriticalSection; protected FReadPosition: integer; FWritePosition: integer; FCanReadCount: integer; FCanWriteCount: integer; function GetSize: int64; override; public constructor Create(const ABuffSize: integer); destructor Destroy(); override; function Read(var Buffer; Count: longint): longint; override; function Write(const Buffer; Count: longint): longint; override; procedure SaveToFile(const AFilename: string); end; implementation { TCircleBuffer } constructor TCircleBuffer.Create(const ABuffSize: integer); begin FSize := ABuffSize; GetMem(FData, FSize); FWritePosition := 0; FReadPosition := 0; FCanWriteCount := ABuffSize; FCanReadCount := 0; Fillchar(FData^, FSize, 0); FLock := TCriticalSection.Create; end; destructor TCircleBuffer.Destroy; begin inherited; FLock.Free; FreeMem(FData, FSize); end; function TCircleBuffer.Read(var Buffer; Count: longint): longint; var P: PByte; Src: PByte; Len, DataLen: integer; begin FLock.Enter; try Result := 0; if FCanReadCount <= 0 then begin Exit; end; if Count > FCanReadCount then DataLen := FCanReadCount else DataLen := Count; src := FData; Inc(src, FReadPosition mod FSize); Result := DataLen; move(src^, buffer, DataLen); Dec(FCanReadCount, Result); Dec(Count, Result); if (Count > 0) and (FCanReadCount > 0) then begin DataLen := Count; if DataLen > FCanReadCount then DataLen := FCanReadCount; src := FData; P := @Buffer; Inc(P, Result); Len := DataLen; move(src^, p^, DataLen); Inc(Result, Len); Dec(FCanReadCount, Len); end; Inc(FCanWriteCount, Result); if FCanWriteCount > FSize then FCanWriteCount := FSize; Inc(FReadPosition, Result); if FReadPosition > FSize then Dec(FReadPosition, FSize); finally FLock.Leave; end; end; function TCircleBuffer.Write(const Buffer; Count: longint): longint; var Len, DataLen: integer; dst: PByte; P: PByte; begin FLock.Enter; try Result := 0; if FCanWriteCount <= 0 then Exit; if Count > FCanWriteCount then DataLen := FCanWriteCount else DataLen := Count; dst := FData; Inc(dst, FWritePosition mod FSize); P := @Buffer; Result := DataLen; move(Buffer, dst^, DataLen); P := FData; if P = nil then Exit; Dec(Count, Result); Dec(FCanWriteCount, Result); if (Count > 0) and (FCanWriteCount > 0) then begin P := @Buffer; Inc(P, Result); Len := FReadPosition - 0; if Count > Len then DataLen := Len else DataLen := Count; dst := Fdata; move(p^, dst^, DataLen); Len := DataLen; Inc(Result, Len); Dec(FCanWriteCount, Len); end; Inc(FCanReadCount, Result); if FCanReadCount > FSize then FCanReadCount := FSize; Inc(FWritePosition, Result); if FWritePosition > FSize then FWritePosition := FWritePosition - FSize; finally FLock.Leave; end; end; function TCircleBuffer.GetSize: int64; begin FLock.Enter; try Result := FCanReadCount; finally FLock.Leave; end; end; procedure TCircleBuffer.SaveToFile(const AFilename: string); var ms: TMemoryStream; begin ms := TMemoryStream.Create; ms.Write(FData^, FWritePosition); ms.SaveToFile(AFilename); ms.Free; end; end. |
AW: Ringspeicher als TStream
Alternativ könnte man einfach Unit-Tests schreiben, das ist irgendwie besser als zu gucken ob es geht
|
AW: Ringspeicher als TStream
Zitat:
Zitat:
bzw. in einigen Unterforen kann man das bei jedem Thread auch nochmal einzeln angeben, denn es macht es den Helfenden auch einfacher die Anzwortmöglichkeiten entsprechend abzuwägen. In "etwas neueren" Delphis gibt es z.B.
Delphi-Quellcode:
, womit man wunderschön ByteArrays speichern/laden kann.
TFile.WriteAllBytes
TMemoryStream bzw. TBytesStream als Vorfahre und du kannst direkt dessen Speicher benutzen, anstatt selbst mit Pointer rumzuhantieren. Statt wilden Pointern verwende ich selbst auch fast nur noch TBytes, bzw. TArray<Byte>, was sich den Speicher automatisch freigeben lässt und auch beim Debuggen einen schöneren Einblick gewährt. |
AW: Ringspeicher als TStream
Ich würde noch die Zeiger konsequent mit den "Atomic" Funktionen abfragen und setzen.
Delphi-Quellcode:
So kann man die aus einigen Lock-Blöcken rausziehen, und vermeidet unnötiges Locken.
FReadPosition: integer;
FWritePosition: integer; FCanReadCount: integer; FCanWriteCount: integer; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:55 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz