| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968 |
- {
- $Project$
- $Workfile$
- $Revision$
- $DateUTC$
- $Id$
- This file is part of the Indy (Internet Direct) project, and is offered
- under the dual-licensing agreement described on the Indy website.
- (http://www.indyproject.org/)
- Copyright:
- (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
- }
- {
- $Log$
- }
- {
- Rev 1.47 1/24/2005 7:35:54 PM JPMugaas
- Fixed CopyTIdIPV6Address.
- Rev 1.46 1/17/2005 7:28:44 PM JPMugaas
- Added Index parameter to several functions so you can use TIdBuffer in a
- random access manner instead of in a sequential manner. This is good for
- some fixed-packet or data types.
- Added functions for reading and writing various types to TIdBuffer which use
- Byte Order functions. This should facilitate a lot of development as this
- gets used more.
- Rev 1.45 27.08.2004 21:58:18 Andreas Hausladen
- Speed optimization ("const" for string parameters)
- Rev 1.44 2004.07.03 19:41:34 czhower
- UTF8, SaveToStream
- Rev 1.43 6/11/2004 8:48:12 AM DSiders
- Added "Do not Localize" comments.
- Rev 1.42 6/9/04 7:46:26 PM RLebeau
- Updated ExtractToBytes() to allocate the output buffer only if the buffer
- length is smaller than the requested number of bytes.
- Rev 1.41 5/29/04 10:44:58 PM RLebeau
- Updated ExtractToBytes() to allocate the output buffer regardless of the
- AAppend parameter
- Added empty string return value to Extract() when AByteCount <= 0
- Rev 1.40 2004.05.20 11:39:06 AM czhower
- IdStreamVCL
- Rev 1.39 2004.05.10 1:19:18 PM czhower
- Removed unneeded code.
- Rev 1.38 5/3/2004 12:57:00 PM BGooijen
- Fixes for 0-based
- Rev 1.37 2004.05.03 11:15:42 AM czhower
- Changed Find to IndexOf and made 0 based to be consistent.
- Rev 1.36 2004.05.01 4:26:52 PM czhower
- Added PeekByte
- Rev 1.35 2004.04.16 11:30:26 PM czhower
- Size fix to IdBuffer, optimizations, and memory leaks
- Rev 1.34 2004.04.08 7:06:44 PM czhower
- Peek support.
- Rev 1.33 2004.04.08 3:56:24 PM czhower
- Fixed bug with Intercept byte count. Also removed Bytes from Buffer.
- Rev 1.32 2004.04.08 2:03:34 AM czhower
- Fixes to Bytes.
- Rev 1.31 2004.04.07 3:59:44 PM czhower
- Bug fix for WriteDirect.
- Rev 1.30 2004.04.07 3:46:30 PM czhower
- Compile fix.
- Rev 1.29 4/7/2004 1:02:14 PM BGooijen
- when extract* is called with -1 or no parameters all data it extracted
- Rev 1.28 2004.03.29 9:58:38 PM czhower
- Is now buffered. Now takes 2/3rds the time as before.
- Rev 1.27 23/03/2004 18:33:44 CCostelloe
- Bug fix: ReadLn returns a previously-read line if FBytes also accessed
- in-between (causes MoveHeadToStartIfNecessary to be called)
- Rev 1.26 18/03/2004 20:24:26 CCostelloe
- Speed improvement by adding FHeadIndex: 10 MB base64 decode reduced from 10
- hours to 62 seconds.
- Rev 1.25 2004.03.03 11:55:02 AM czhower
- IdStream change
- Rev 1.24 3/1/04 7:33:12 PM RLebeau
- Updated Remove() to call the OnBytesRemoved event handler.
- Rev 1.23 2004.02.03 4:17:14 PM czhower
- For unit name changes.
- Rev 1.22 1/11/2004 5:48:48 PM BGooijen
- Added AApend parameter to ExtractToBytes
- Rev 1.21 1/7/2004 8:36:32 PM BGooijen
- Arguments were in wrong order
- Rev 1.20 22/11/2003 10:35:04 PM GGrieve
- Reverse copy direction in TIdBuffer.ExtractToStream
- Rev 1.19 2003.10.24 10:44:54 AM czhower
- IdStream implementation, bug fixes.
- Rev 1.18 10/15/2003 1:03:40 PM DSiders
- Created resource strings for TIdBuffer.Find exceptions.
- Rev 1.17 2003.10.14 1:27:06 PM czhower
- Uupdates + Intercept support
- Rev 1.16 2003.10.11 5:47:00 PM czhower
- -VCL fixes for servers
- -Chain suport for servers (Super core)
- -Scheduler upgrades
- -Full yarn support
- Rev 1.15 10/5/2003 10:24:20 PM BGooijen
- Changed WriteBytes(var ...) to WriteBytes(const ...)
- Rev 1.14 10/3/2003 10:46:38 PM BGooijen
- Fixed Range Check Exception, and fixed ExtractToStream
- Rev 1.13 2003.10.02 8:29:12 PM czhower
- Changed names of byte conversion routines to be more readily understood and
- not to conflict with already in use ones.
- Rev 1.12 2003.10.02 12:44:58 PM czhower
- Comment added
- Rev 1.11 10/2/2003 5:23:14 PM GGrieve
- make Bytes a public property
- Rev 1.10 10/2/2003 5:00:38 PM GGrieve
- Fix bug in find - can't find last char
- Rev 1.9 2003.10.02 10:37:00 AM czhower
- Comments
- Rev 1.8 10/2/2003 3:54:06 PM GGrieve
- Finish cleaning up - no $IFDEFs but still optimal on both win32 and DontNet
- Rev 1.7 10/1/2003 10:58:38 PM BGooijen
- Removed unused var
- Rev 1.6 10/1/2003 8:15:58 PM BGooijen
- Fixed Range Check Error on D7
- Rev 1.5 10/1/2003 8:02:22 PM BGooijen
- Removed some ifdefs and improved code
- Rev 1.4 10/1/2003 10:49:02 PM GGrieve
- Rework buffer for Octane Compability
- Rev 1.3 2003.10.01 2:30:44 PM czhower
- .Net
- Rev 1.2 2003.10.01 1:37:32 AM czhower
- .Net
- Rev 1.1 2003.10.01 1:12:32 AM czhower
- .Net
- Rev 1.0 2003.09.30 10:33:56 PM czhower
- Readd after accidental delete.
- Rev 1.14 2003.09.30 10:33:16 PM czhower
- Updates
- Rev 1.13 2003.07.16 5:05:06 PM czhower
- Phase 1 of IdBuffer changes for compat.
- Rev 1.12 6/29/2003 10:56:22 PM BGooijen
- Removed .Memory from the buffer, and added some extra methods
- Rev 1.11 2003.06.25 4:29:06 PM czhower
- Free --> FreeAndNil
- Rev 1.10 2003.01.17 2:18:36 PM czhower
- Rev 1.9 12-14-2002 22:08:24 BGooijen
- Changed FMemory to FMemory.Memory in some places
- Rev 1.8 12-14-2002 22:02:34 BGooijen
- changed Memory to FMemory in some places, to remove some issues
- Rev 1.7 12/11/2002 04:27:02 AM JPMugaas
- Fixed compiler warning.
- Rev 1.6 12/11/2002 03:53:44 AM JPMugaas
- Merged the buffer classes.
- Rev 1.5 2002.12.07 12:26:18 AM czhower
- Rev 1.4 12-6-2002 20:34:06 BGooijen
- Now compiles on Delphi 5
- Rev 1.3 6/12/2002 11:00:14 AM SGrobety
- Rev 1.2 12/5/2002 02:55:44 PM JPMugaas
- Added AddStream method for reading a stream into the buffer class.
- Rev 1.1 23.11.2002 12:59:48 JBerg
- fixed packbuffer
- Rev 1.0 11/13/2002 08:38:32 AM JPMugaas
- }
- unit IdBuffer;
- {$I IdCompilerDefines.inc}
- {
- .Net forces us to perform copies from strings to Bytes so that it can do the
- proper unicode and other conversions.
- IdBuffer is for storing data we cannot deal with right now and we do not know
- the size. It must be optimized for adding to the end, and extracting from the
- beginning. First pass we are just making it work, later using bubbling we will
- optimize it for such tasks.
- The copy is a separate issue and we considered several options. For .net we will
- always have to copy data to send or to receive to translate it to binary. For
- example if we have a string it must be converted to bytes. This conversion
- requires a copy. All strings are Unicode and must be converted to single
- bytes by a convertor. This is not limited to strings.
- In VCL previously all strings were AnsiString so we used a pointer and just
- accessed the memory directly from the string. This avoided the overhead of a
- copy.
- We have come up with several ideas on how to allow the copy on .net, while
- avoiding the copy on VCL to keep the performance benefit. However we must do
- it in a single source manner and in a manner that does not impact the code
- negatively.
- For now for VCL we also do a copy. This has the advantage that Byte arrays are
- reference counted and automaticaly handled by Delphi. For example:
- WriteBytes(StringToBytes(s));
- The array returned by this function will automatically be freed by Delphi.
- There are other options that are nearly as transparent but have the additional
- overhead of requiring class creation. These classes can be used to copy for .net
- and proxy on VCL. It all works very nice and has low memory overhead. The
- objects can then be freed by default in methods that accept them.
- However after analysis, copy on VCL may not be that bad after all. The copy
- only really impacts strings. The overhead to copy strings is minimal and only
- used in commands etc. The big transfers come from files, streams, or other.
- Such transfers have to be mapped into memory in VCL anyways, and if we map
- directly into the byte array instead of the previous classes peformance should
- be fine.
- In short - copy under VCL should be acceptable if we watch for bottlenecks and
- fix them appropriately without having to creat proxy classes. The only problem
- remains for transmitting large memory blocks. But if this is done against a
- fixed copy buffer the performance hit will be neglible and it is not a common
- task to transmit large memory blocks.
- For such transfers from streams, etc the user can declare a persistent array
- of bytes that is not freed between each call to WriteBytes.
- -Kudzu
- }
- interface
- uses
- Classes,
- IdException,
- IdGlobal,
- SysUtils;
- type
- EIdNotEnoughDataInBuffer = class(EIdException);
- EIdTooMuchDataInBuffer = class(EIdException); // only 2GB is allowed -
- TIdBufferBytesRemoved = procedure(ASender: TObject; ABytes: Integer) of object;
- // TIdBuffer is used as an internal buffer to isolate Indy from pointers and
- // memory allocations. It also allows optimizations to be kept in a single place.
- //
- // TIdBuffer is primarily used as a read/write buffer for the communication layer.
- TIdBuffer = class(TObject)
- private
- function GetAsString: string;
- protected
- FBytes: TIdBytes;
- FByteEncoding: IIdTextEncoding;
- FGrowthFactor: Integer;
- FHeadIndex: Integer;
- FOnBytesRemoved: TIdBufferBytesRemoved;
- FSize: Integer;
- //
- procedure CheckAdd(AByteCount : Integer; const AIndex : Integer);
- procedure CheckByteCount(var VByteCount : Integer; const AIndex : Integer);
- function GetCapacity: Integer;
- procedure SetCapacity(AValue: Integer);
- public
- procedure Clear;
- constructor Create; overload;
- constructor Create(AOnBytesRemoved: TIdBufferBytesRemoved); overload;
- constructor Create(AGrowthFactor: Integer); overload;
- constructor Create(const ABytes : TIdBytes; const ALength : Integer = -1); overload;
- procedure CompactHead(ACanShrink: Boolean = True);
- destructor Destroy; override;
- {
- Most of these now have an AIndex parameter. If that is less than 0,
- we are accessing data sequentially. That means, read the data from the HeadIndex
- and "remove" the data you read.
- If AIndex is 0 or greater, the HeadIndex is disregarded and no deletion is done.
- You are just reading from a particular location in a random access manner.
- }
- // will extract number of bytes and decode as specified
- function ExtractToString(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil): string;
- // all 3 extract routines append to existing data, if any
- procedure ExtractToStream(const AStream: TStream; AByteCount: Integer = -1; const AIndex: Integer = -1);
- procedure ExtractToIdBuffer(ABuffer: TIdBuffer; AByteCount: Integer = -1; const AIndex : Integer = -1);
- procedure ExtractToBytes(var VBytes: TIdBytes; AByteCount: Integer = -1;
- AAppend: Boolean = True; AIndex : Integer = -1);
- function ExtractToUInt8(const AIndex : Integer): UInt8;
- function ExtractToUInt16(const AIndex : Integer; AConvert: Boolean = True): UInt16;
- function ExtractToUInt32(const AIndex : Integer; AConvert: Boolean = True): UInt32;
- function ExtractToUInt64(const AIndex : Integer; AConvert: Boolean = True): UInt64;
- procedure ExtractToIPv6(const AIndex : Integer; var VAddress: TIdIPv6Address);
- function IndexOf(const AByte: Byte; AStartPos: Integer = 0): Integer; overload;
- function IndexOf(const ABytes: TIdBytes; AStartPos: Integer = 0): Integer; overload;
- function IndexOf(const AString: string; AStartPos: Integer = 0; AByteEncoding: IIdTextEncoding = nil): Integer; overload;
- function PeekByte(AIndex: Integer): Byte;
- procedure Remove(AByteCount: Integer);
- procedure SaveToStream(const AStream: TStream);
- { Most of these now have an ADestIndex parameter. If that is less than 0,
- we are writing data sequentially.
- If ADestIndex is 0 or greater, you are setting bytes in a particular
- location in a random access manner.
- }
- // Write
- procedure Write(const AString: string; AByteEncoding: IIdTextEncoding = nil; const ADestIndex: Integer = -1); overload;
- procedure Write(const ABytes: TIdBytes; const ADestIndex: Integer = -1); overload;
- procedure Write(const ABytes: TIdBytes; const ALength, AOffset : Integer; const ADestIndex: Integer = -1); overload;
- procedure Write(AStream: TStream; AByteCount: Integer = 0); overload;
- procedure Write(const AValue: UInt64; const ADestIndex: Integer = -1; AConvert: Boolean = True); overload;
- procedure Write(const AValue: UInt32; const ADestIndex: Integer = -1; AConvert: Boolean = True); overload;
- procedure Write(const AValue: UInt16; const ADestIndex: Integer = -1; AConvert: Boolean = True); overload;
- procedure Write(const AValue: UInt8; const ADestIndex: Integer = -1); overload;
- procedure Write(const AValue: TIdIPv6Address; const ADestIndex: Integer = -1); overload;
- //
- //Kudzu: I have removed the Bytes property. Do not add it back - it allowed "internal" access
- // which caused compacting or internal knowledge. Access via Extract or other such methods
- // instead. Bytes could also be easily confused with FBytes internally and cause issues.
- //
- // Bytes also allowed direct acces without removing which could cause concurrency issues if
- // the reference was kept.
- //
- property Capacity: Integer read GetCapacity write SetCapacity;
- property Encoding: IIdTextEncoding read FByteEncoding write FByteEncoding;
- property GrowthFactor: Integer read FGrowthFactor write FGrowthFactor;
- property Size: Integer read FSize;
- //useful for testing. returns buffer as string without extraction.
- property AsString: string read GetAsString;
- end;
- implementation
- uses
- IdResourceStringsCore,
- IdStack; //needed for byte order functions
- procedure TIdBuffer.CheckAdd(AByteCount : Integer; const AIndex : Integer);
- begin
- if (MaxInt - AByteCount) < (Size + AIndex) then begin
- raise EIdTooMuchDataInBuffer.Create(RSTooMuchDataInBuffer);
- end;
- end;
- procedure TIdBuffer.CheckByteCount(var VByteCount : Integer; const AIndex : Integer);
- begin
- if VByteCount = -1 then begin
- VByteCount := Size+AIndex;
- end
- else if VByteCount > (Size+AIndex) then begin
- raise EIdNotEnoughDataInBuffer.CreateFmt(RSNotEnoughDataInBuffer, [VByteCount, Size]); {do not localize}
- end;
- end;
- procedure TIdBuffer.Clear;
- begin
- SetLength(FBytes, 0);
- FHeadIndex := 0;
- FSize := 0;
- end;
- constructor TIdBuffer.Create(AGrowthFactor: Integer);
- begin
- Create;
- FGrowthFactor := AGrowthFactor;
- end;
- constructor TIdBuffer.Create(AOnBytesRemoved: TIdBufferBytesRemoved);
- begin
- Create;
- FOnBytesRemoved := AOnBytesRemoved;
- end;
- constructor TIdBuffer.Create(const ABytes: TIdBytes; const ALength: Integer);
- begin
- Create;
- if ALength < 0 then
- begin
- FBytes := ABytes;
- FSize := Length(ABytes);
- end else
- begin
- SetLength(FBytes, ALength);
- if ALength > 0 then
- begin
- CopyTIdBytes(ABytes, 0, FBytes, 0, ALength);
- FSize := ALength;
- end;
- end;
- end;
- destructor TIdBuffer.Destroy;
- begin
- Clear;
- inherited Destroy;
- //do only at the last moment
- TIdStack.DecUsage;
- end;
- function TIdBuffer.ExtractToString(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil): string;
- var
- LBytes: TIdBytes;
- begin
- if AByteCount < 0 then begin
- AByteCount := Size;
- end;
- if AByteCount > 0 then
- begin
- if AByteEncoding = nil then begin
- AByteEncoding := FByteEncoding;
- EnsureEncoding(AByteEncoding);
- end;
- // TODO: convert directly from FBytes without allocating a local TIdBytes anymore...
- {
- CheckByteCount(AByteCount, 0);
- try
- Result := BytesToString(FBytes, FHeadIndex, AByteCount, AByteEncoding);
- finally
- Remove(AByteCount);
- end;
- }
- ExtractToBytes(LBytes, AByteCount);
- Result := BytesToString(LBytes, AByteEncoding);
- end else begin
- Result := '';
- end;
- end;
- procedure TIdBuffer.ExtractToBytes(var VBytes: TIdBytes; AByteCount: Integer = -1;
- AAppend: Boolean = True; AIndex : Integer = -1);
- var
- LOldSize: Integer;
- LIndex : Integer;
- begin
- if AByteCount < 0 then begin
- AByteCount := Size;
- end;
- LIndex := IndyMax(AIndex, 0);
- if AByteCount > 0 then begin
- CheckByteCount(AByteCount, LIndex);
- if AAppend then begin
- LOldSize := Length(VBytes);
- SetLength(VBytes, LOldSize + AByteCount);
- end else begin
- LOldSize := 0;
- if Length(VBytes) < AByteCount then begin
- SetLength(VBytes, AByteCount);
- end;
- end;
- if AIndex < 0 then
- begin
- CopyTIdBytes(FBytes, FHeadIndex, VBytes, LOldSize, AByteCount);
- Remove(AByteCount);
- end else
- begin
- CopyTIdBytes(FBytes, AIndex, VBytes, LOldSize, AByteCount);
- end;
- end;
- end;
- procedure TIdBuffer.ExtractToIdBuffer(ABuffer: TIdBuffer; AByteCount: Integer = -1;
- const AIndex: Integer = -1);
- var
- LBytes: TIdBytes;
- begin
- if AByteCount < 0 then begin
- AByteCount := Size;
- end;
- //TODO: Optimize this routine to directly copy from one to the other
- ExtractToBytes(LBytes, AByteCount, True, AIndex);
- ABuffer.Write(LBytes);
- end;
- procedure TIdBuffer.ExtractToStream(const AStream: TStream; AByteCount: Integer = -1;
- const AIndex: Integer = -1);
- var
- LBytes : TIdBytes;
- begin
- if AByteCount < 0 then begin
- AByteCount := Size;
- end;
- if AIndex < 0 then
- begin
- // TODO: remove CompactHead() here and pass FHeadIndex to AStream.WriteBuffer():
- {
- CheckByteCount(AByteCount, FHeadIndex);
- AStream.WriteBuffer(FBytes[FHeadIndex], AByteCount);
- Remove(AByteCount);
- }
- CompactHead;
- CheckByteCount(AByteCount, 0);
- AStream.WriteBuffer(PByte(FBytes)^, AByteCount);
- Remove(AByteCount);
- end else
- begin
- CheckByteCount(AByteCount, AIndex);
- // TODO: remove CopyTIdBytes() here and pass FBytes and AIndex to AStream.WriteBuffer():
- //AStream.WriteBuffer(FBytes[AIndex], AByteCount);
- SetLength(LBytes, AByteCount);
- CopyTIdBytes(FBytes, AIndex, LBytes, 0, AByteCount);
- AStream.WriteBuffer(PByte(LBytes)^, AByteCount);
- end;
- end;
- procedure TIdBuffer.Remove(AByteCount: Integer);
- begin
- if AByteCount >= Size then begin
- Clear;
- end else begin
- Inc(FHeadIndex, AByteCount);
- Dec(FSize, AByteCount);
- if FHeadIndex > GrowthFactor then begin
- CompactHead;
- end;
- end;
- if Assigned(FOnBytesRemoved) then begin
- FOnBytesRemoved(Self, AByteCount);
- end;
- end;
- procedure TIdBuffer.CompactHead(ACanShrink: Boolean = True);
- begin
- // Only try to compact if needed.
- if FHeadIndex > 0 then begin
- CopyTIdBytes(FBytes, FHeadIndex, FBytes, 0, Size);
- FHeadIndex := 0;
- if ACanShrink and ((Capacity - Size - FHeadIndex) > GrowthFactor) then begin
- SetLength(FBytes, FHeadIndex + Size + GrowthFactor);
- end;
- end;
- end;
- procedure TIdBuffer.Write(const ABytes: TIdBytes; const ADestIndex: Integer = -1);
- {$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
- begin
- Write(ABytes, Length(ABytes), 0, ADestIndex);
- end;
- procedure TIdBuffer.Write(AStream: TStream; AByteCount: Integer);
- var
- LToAdd: Integer;
- LLength: Integer;
- begin
- // TODO: handle sizes > 2GB...
- if AByteCount < 0 then begin
- // Copy remaining
- LToAdd := AStream.Size - AStream.Position;
- end else if AByteCount = 0 then begin
- // Copy all
- AStream.Position := 0;
- LToAdd := AStream.Size;
- end else begin
- LToAdd := IndyMin(AByteCount, AStream.Size - AStream.Position);
- end;
- //Assert(LToAdd <= MaxInt);
- if LToAdd > 0 then begin
- LLength := Size;
- CheckAdd(LToAdd, 0);
- CompactHead;
- SetLength(FBytes, LLength + LToAdd);
- AStream.ReadBuffer(FBytes[LLength], LToAdd);
- Inc(FSize, LToAdd);
- end;
- end;
- function TIdBuffer.IndexOf(const AString: string; AStartPos: Integer = 0;
- AByteEncoding: IIdTextEncoding = nil): Integer;
- begin
- if AByteEncoding = nil then begin
- AByteEncoding := FByteEncoding;
- end;
- Result := IndexOf(ToBytes(AString, AByteEncoding), AStartPos);
- end;
- function TIdBuffer.IndexOf(const ABytes: TIdBytes; AStartPos: Integer = 0): Integer;
- var
- i, j, LEnd, BytesLen: Integer;
- LFound: Boolean;
- begin
- Result := -1;
- // Dont search if it empty
- if Size > 0 then begin
- BytesLen := Length(ABytes);
- if BytesLen = 0 then begin
- raise EIdException.Create(RSBufferMissingTerminator); // TODO: create a new Exception class for this
- end;
- if (AStartPos < 0) or (AStartPos >= Size) then begin
- raise EIdException.Create(RSBufferInvalidStartPos); // TODO: create a new Exception class for this
- end;
- LEnd := FHeadIndex + Size;
- for i := FHeadIndex + AStartPos to LEnd - BytesLen do begin
- LFound := True;
- for j := 0 to BytesLen - 1 do begin
- if (i + j) >= LEnd then begin
- Break;
- end;
- if FBytes[i + j] <> ABytes[j] then begin
- LFound := False;
- Break;
- end;
- end;
- if LFound then begin
- Result := i - FHeadIndex;
- Break;
- end;
- end;
- end;
- end;
- function TIdBuffer.IndexOf(const AByte: Byte; AStartPos: Integer = 0): Integer;
- var
- i: Integer;
- begin
- Result := -1;
- // Dont search if it empty
- if Size > 0 then begin
- if (AStartPos < 0) or (AStartPos >= Size) then begin
- raise EIdException.Create(RSBufferInvalidStartPos); // TODO: create a new Exception class for this
- end;
- for i := (FHeadIndex + AStartPos) to (FHeadIndex + Size - 1) do begin
- if FBytes[i] = AByte then begin
- Result := i - FHeadIndex;
- Break;
- end;
- end;
- end;
- end;
- procedure TIdBuffer.Write(const AString: string; AByteEncoding: IIdTextEncoding = nil;
- const ADestIndex : Integer = -1);
- begin
- if AByteEncoding = nil then begin
- AByteEncoding := FByteEncoding;
- end;
- Write(ToBytes(AString, AByteEncoding), ADestIndex);
- end;
- function TIdBuffer.GetCapacity: Integer;
- begin
- Result := Length(FBytes);
- end;
- procedure TIdBuffer.SetCapacity(AValue: Integer);
- begin
- if AValue < Size then begin
- raise EIdException.Create('Capacity cannot be smaller than Size'); {do not localize} // TODO: add a resource string, and create a new Exception class for this
- end;
- CompactHead;
- SetLength(FBytes, AValue);
- end;
- constructor TIdBuffer.Create;
- begin
- inherited Create;
- FGrowthFactor := 2048;
- Clear;
- TIdStack.IncUsage;
- end;
- function TIdBuffer.PeekByte(AIndex: Integer): Byte;
- begin
- if Size = 0 then begin
- raise EIdException.Create('No bytes in buffer.'); {do not localize} // TODO: add a resource string, and create a new Exception class for this
- end;
- if (AIndex < 0) or (AIndex >= Size) then begin
- raise EIdException.Create('Index out of bounds.'); {do not localize} // TODO: add a resource string, and create a new Exception class for this
- end;
- Result := FBytes[FHeadIndex + AIndex];
- end;
- procedure TIdBuffer.SaveToStream(const AStream: TStream);
- begin
- // TODO: remove CompactHead here and pass FBytes to AStream.WriteBuffer():
- //AStream.WriteBuffer(FBytes[FHeadIndex], Size);
- CompactHead(False);
- AStream.WriteBuffer(PByte(FBytes)^, Size);
- end;
- procedure TIdBuffer.ExtractToIPv6(const AIndex: Integer; var VAddress: TIdIPv6Address);
- var
- LIndex : Integer;
- begin
- if AIndex < 0 then begin
- LIndex := FHeadIndex;
- end else begin
- LIndex := AIndex;
- end;
- BytesToIPv6(FBytes, VAddress, LIndex);
- VAddress := GStack.NetworkToHost(VAddress);
- if AIndex < 0 then begin
- Remove(16);
- end;
- end;
- function TIdBuffer.ExtractToUInt64(const AIndex: Integer; AConvert: Boolean = True): UInt64;
- var
- LIndex : Integer;
- begin
- if AIndex < 0 then begin
- LIndex := FHeadIndex;
- end else begin
- LIndex := AIndex;
- end;
- Result := BytesToUInt64(FBytes, LIndex);
- if AIndex < 0 then begin
- Remove(8);
- end;
- if AConvert then begin
- Result := GStack.NetworkToHost(Result);
- end;
- end;
- function TIdBuffer.ExtractToUInt32(const AIndex: Integer; AConvert: Boolean = True): UInt32;
- var
- LIndex : Integer;
- begin
- if AIndex < 0 then begin
- LIndex := FHeadIndex;
- end else begin
- LIndex := AIndex;
- end;
- Result := BytesToUInt32(FBytes, LIndex);
- if AIndex < 0 then begin
- Remove(4);
- end;
- if AConvert then begin
- Result := GStack.NetworkToHost(Result);
- end;
- end;
- function TIdBuffer.ExtractToUInt16(const AIndex: Integer; AConvert: Boolean = True): UInt16;
- var
- LIndex : Integer;
- begin
- if AIndex < 0 then begin
- LIndex := FHeadIndex;
- end else begin
- LIndex := AIndex;
- end;
- Result := BytesToUInt16(FBytes, LIndex);
- if AIndex < 0 then begin
- Remove(2);
- end;
- if AConvert then begin
- Result := GStack.NetworkToHost(Result);
- end;
- end;
- function TIdBuffer.ExtractToUInt8(const AIndex: Integer): UInt8;
- var
- LIndex : Integer;
- begin
- if AIndex < 0 then begin
- LIndex := FHeadIndex;
- end else begin
- LIndex := AIndex;
- end;
- Result := FBytes[LIndex];
- if AIndex < 0 then begin
- Remove(1);
- end;
- end;
- procedure TIdBuffer.Write(const AValue: UInt16; const ADestIndex: Integer; AConvert: Boolean = True);
- var
- LVal : UInt16;
- LIndex : Integer;
- begin
- if ADestIndex < 0 then
- begin
- LIndex := FHeadIndex + Size;
- SetLength(FBytes, LIndex+2);
- end else
- begin
- LIndex := ADestIndex;
- end;
- LVal := AValue;
- if AConvert then begin
- LVal := GStack.HostToNetwork(LVal);
- end;
- CopyTIdUInt16(LVal, FBytes, LIndex);
- if LIndex >= FSize then begin
- FSize := LIndex+2;
- end;
- end;
- procedure TIdBuffer.Write(const AValue: UInt8; const ADestIndex: Integer);
- var
- LIndex : Integer;
- begin
- if ADestIndex < 0 then
- begin
- LIndex := FHeadIndex + Size;
- SetLength(FBytes, LIndex+1);
- end else
- begin
- LIndex := ADestIndex;
- end;
- FBytes[LIndex] := AValue;
- if LIndex >= FSize then begin
- FSize := LIndex+1;
- end;
- end;
- procedure TIdBuffer.Write(const AValue: TIdIPv6Address; const ADestIndex: Integer);
- var
- LVal : TIdIPv6Address;
- LIndex : Integer;
- begin
- if ADestIndex < 0 then
- begin
- LIndex := FHeadIndex + Size;
- SetLength(FBytes, LIndex + 16);
- end else
- begin
- LIndex := ADestIndex;
- end;
- LVal := GStack.HostToNetwork(AValue);
- CopyTIdIPV6Address(LVal, FBytes, LIndex);
- if LIndex >= FSize then begin
- FSize := LIndex+16;
- end;
- end;
- procedure TIdBuffer.Write(const AValue: UInt64; const ADestIndex: Integer; AConvert: Boolean = True);
- var
- LVal: UInt64;
- LIndex: Integer;
- begin
- if ADestIndex < 0 then
- begin
- LIndex := FHeadIndex + Size;
- SetLength(FBytes, LIndex + 8);
- end else
- begin
- LIndex := ADestIndex;
- end;
- LVal := AValue;
- if AConvert then begin
- LVal := GStack.HostToNetwork(LVal);
- end;
- CopyTIdUInt64(LVal, FBytes, LIndex);
- if LIndex >= FSize then begin
- FSize := LIndex + 8;
- end;
- end;
- procedure TIdBuffer.Write(const AValue: UInt32; const ADestIndex: Integer; AConvert: Boolean = True);
- var
- LVal : UInt32;
- LIndex : Integer;
- begin
- if ADestIndex < 0 then
- begin
- LIndex := FHeadIndex + Size;
- SetLength(FBytes, LIndex + 4);
- end else
- begin
- LIndex := ADestIndex;
- end;
- LVal := AValue;
- if AConvert then begin
- LVal := GStack.HostToNetwork(LVal);
- end;
- CopyTIdUInt32(LVal, FBytes, LIndex);
- if LIndex >= FSize then begin
- FSize := LIndex+4;
- end;
- end;
- procedure TIdBuffer.Write(const ABytes: TIdBytes; const ALength, AOffset : Integer;
- const ADestIndex: Integer = -1);
- var
- LByteLength: Integer;
- LIndex : Integer;
- begin
- LByteLength := IndyLength(ABytes, ALength, AOffset);
- if LByteLength = 0 then begin
- Exit;
- end;
- LIndex := IndyMax(ADestIndex, 0);
- CheckAdd(LByteLength, LIndex);
- if Size = 0 then begin
- FHeadIndex := 0;
- if ADestIndex < 0 then
- begin
- FBytes := ToBytes(ABytes, LByteLength, AOffset);
- FSize := LByteLength;
- end else
- begin
- FSize := ADestIndex + LByteLength;
- SetLength(FBytes, FSize);
- CopyTIdBytes(ABytes, AOffset, FBytes, ADestIndex, LByteLength);
- end;
- end
- else if ADestIndex < 0 then
- begin
- CompactHead(False);
- if (Capacity - Size - FHeadIndex) < LByteLength then begin
- SetLength(FBytes, Size + LByteLength + GrowthFactor);
- end;
- CopyTIdBytes(ABytes, AOffset, FBytes, FHeadIndex + Size, LByteLength);
- Inc(FSize, LByteLength);
- end else
- begin
- CopyTIdBytes(ABytes, AOffset, FBytes, LIndex, LByteLength);
- if LIndex >= FSize then begin
- FSize := LIndex + LByteLength;
- end;
- end;
- end;
- function TIdBuffer.GetAsString: string;
- begin
- Result := BytesToString(FBytes, FByteEncoding);
- end;
- end.
|