1031 lines
31 KiB
Plaintext
1031 lines
31 KiB
Plaintext
|
{
|
||
|
$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;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
FAnsiEncoding: IIdTextEncoding;
|
||
|
{$ENDIF}
|
||
|
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 Extract(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ExtractToString()'{$ENDIF};{$ENDIF}
|
||
|
function ExtractToString(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): 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 ExtractToByte(const AIndex : Integer): UInt8; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ExtractToUInt8()'{$ENDIF};{$ENDIF}
|
||
|
function ExtractToUInt16(const AIndex : Integer): UInt16;
|
||
|
function ExtractToWord(const AIndex : Integer): UInt16; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ExtractToUInt16()'{$ENDIF};{$ENDIF}
|
||
|
function ExtractToUInt32(const AIndex : Integer): UInt32;
|
||
|
function ExtractToLongWord(const AIndex : Integer): UInt32; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'Use ExtractToUInt32()'{$ENDIF};{$ENDIF}
|
||
|
function ExtractToUInt64(const AIndex : Integer): TIdUInt64;
|
||
|
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
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): 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
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
); 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: TIdUInt64; const ADestIndex: Integer = -1); overload;
|
||
|
procedure Write(const AValue: UInt32; const ADestIndex: Integer = -1); overload;
|
||
|
procedure Write(const AValue: UInt16; const ADestIndex: Integer = -1); 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;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
property AnsiEncoding: IIdTextEncoding read FAnsiEncoding write FAnsiEncoding;
|
||
|
{$ENDIF}
|
||
|
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,
|
||
|
IdStream,
|
||
|
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('%s (%d/%d)', [RSNotEnoughDataInBuffer, VByteCount, Size]); {do not localize}
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdBuffer.Clear;
|
||
|
begin
|
||
|
SetLength(FBytes, 0);
|
||
|
FHeadIndex := 0;
|
||
|
FSize := Length(FBytes);
|
||
|
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;
|
||
|
|
||
|
{$I IdDeprecatedImplBugOff.inc}
|
||
|
function TIdBuffer.Extract(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): string;
|
||
|
{$I IdDeprecatedImplBugOn.inc}
|
||
|
{$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
|
||
|
begin
|
||
|
Result := ExtractToString(AByteCount, AByteEncoding{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF});
|
||
|
end;
|
||
|
|
||
|
function TIdBuffer.ExtractToString(AByteCount: Integer = -1; AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ADestEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): 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;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
if ADestEncoding = nil then begin
|
||
|
ADestEncoding := FAnsiEncoding;
|
||
|
EnsureEncoding(ADestEncoding, encOSDefault);
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
ExtractToBytes(LBytes, AByteCount);
|
||
|
Result := BytesToString(LBytes, AByteEncoding
|
||
|
{$IFDEF STRING_IS_ANSI}, ADestEncoding{$ENDIF}
|
||
|
);
|
||
|
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
|
||
|
LIndex : Integer;
|
||
|
LBytes : TIdBytes;
|
||
|
begin
|
||
|
if AByteCount < 0 then begin
|
||
|
AByteCount := Size;
|
||
|
end;
|
||
|
LIndex := IndyMax(AIndex, 0);
|
||
|
if AIndex < 0 then
|
||
|
begin
|
||
|
// TODO: remove CompactHead() here and pass FHeadIndex to TIdStreamHelper.Write():
|
||
|
{
|
||
|
CheckByteCount(AByteCount, FHeadIndex);
|
||
|
TIdStreamHelper.Write(AStream, FBytes, AByteCount, FHeadIndex);
|
||
|
Remove(AByteCount);
|
||
|
}
|
||
|
CompactHead;
|
||
|
CheckByteCount(AByteCount, LIndex);
|
||
|
TIdStreamHelper.Write(AStream, FBytes, AByteCount);
|
||
|
Remove(AByteCount);
|
||
|
end else
|
||
|
begin
|
||
|
// TODO: remove CopyTIdBytes() here and pass FBytes and AIndex to TIdStreamHelper.Write():
|
||
|
{
|
||
|
CheckByteCount(AByteCount, LIndex);
|
||
|
TIdStreamHelper.Write(AStream, FBytes, AByteCount, AIndex);
|
||
|
}
|
||
|
CheckByteCount(AByteCount, LIndex);
|
||
|
SetLength(LBytes, AByteCount);
|
||
|
CopyTIdBytes(FBytes, AIndex, LBytes, 0, AByteCount);
|
||
|
TIdStreamHelper.Write(AStream, 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
|
||
|
LAdded: Integer;
|
||
|
LLength: Integer;
|
||
|
begin
|
||
|
if AByteCount < 0 then begin
|
||
|
// Copy remaining
|
||
|
LAdded := AStream.Size - AStream.Position;
|
||
|
end else if AByteCount = 0 then begin
|
||
|
// Copy all
|
||
|
AStream.Position := 0;
|
||
|
LAdded := AStream.Size;
|
||
|
end else begin
|
||
|
LAdded := IndyMin(AByteCount, AStream.Size - AStream.Position);
|
||
|
end;
|
||
|
if LAdded > 0 then begin
|
||
|
LLength := Size;
|
||
|
CheckAdd(LAdded, 0);
|
||
|
CompactHead;
|
||
|
SetLength(FBytes, LLength + LAdded);
|
||
|
TIdStreamHelper.ReadBytes(AStream, FBytes, LAdded, LLength);
|
||
|
Inc(FSize, LAdded);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdBuffer.IndexOf(const AString: string; AStartPos: Integer = 0;
|
||
|
AByteEncoding: IIdTextEncoding = nil
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
): Integer;
|
||
|
begin
|
||
|
if AByteEncoding = nil then begin
|
||
|
AByteEncoding := FByteEncoding;
|
||
|
end;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
if ASrcEncoding = nil then begin
|
||
|
ASrcEncoding := FAnsiEncoding;
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
Result := IndexOf(
|
||
|
ToBytes(AString, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}),
|
||
|
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
|
||
|
if Length(ABytes) = 0 then begin
|
||
|
raise EIdException.Create(RSBufferMissingTerminator);
|
||
|
end;
|
||
|
if (AStartPos < 0) or (AStartPos >= Size) then begin
|
||
|
raise EIdException.Create(RSBufferInvalidStartPos);
|
||
|
end;
|
||
|
BytesLen := Length(ABytes);
|
||
|
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);
|
||
|
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
|
||
|
{$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
|
||
|
);
|
||
|
begin
|
||
|
if AByteEncoding = nil then begin
|
||
|
AByteEncoding := FByteEncoding;
|
||
|
end;
|
||
|
{$IFDEF STRING_IS_ANSI}
|
||
|
if ASrcEncoding = nil then begin
|
||
|
ASrcEncoding := FAnsiEncoding;
|
||
|
end;
|
||
|
{$ENDIF}
|
||
|
Write(
|
||
|
ToBytes(AString, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}),
|
||
|
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}
|
||
|
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}
|
||
|
end;
|
||
|
if (AIndex < 0) or (AIndex >= Size) then begin
|
||
|
raise EIdException.Create('Index out of bounds.'); {do not localize}
|
||
|
end;
|
||
|
Result := FBytes[FHeadIndex + AIndex];
|
||
|
end;
|
||
|
|
||
|
procedure TIdBuffer.SaveToStream(const AStream: TStream);
|
||
|
begin
|
||
|
CompactHead(False);
|
||
|
TIdStreamHelper.Write(AStream, 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): TIdUInt64;
|
||
|
var
|
||
|
LIndex : Integer;
|
||
|
begin
|
||
|
if AIndex < 0 then begin
|
||
|
LIndex := FHeadIndex;
|
||
|
end else begin
|
||
|
LIndex := AIndex;
|
||
|
end;
|
||
|
Result := BytesToUInt64(FBytes, LIndex);
|
||
|
Result := GStack.NetworkToHost(Result);
|
||
|
if AIndex < 0 then begin
|
||
|
Remove(8);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdBuffer.ExtractToUInt32(const AIndex: Integer): UInt32;
|
||
|
var
|
||
|
LIndex : Integer;
|
||
|
begin
|
||
|
if AIndex < 0 then begin
|
||
|
LIndex := FHeadIndex;
|
||
|
end else begin
|
||
|
LIndex := AIndex;
|
||
|
end;
|
||
|
Result := BytesToUInt32(FBytes, LIndex);
|
||
|
Result := GStack.NetworkToHost(Result);
|
||
|
if AIndex < 0 then begin
|
||
|
Remove(4);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{$I IdDeprecatedImplBugOff.inc}
|
||
|
function TIdBuffer.ExtractToLongWord(const AIndex: Integer): UInt32;
|
||
|
{$I IdDeprecatedImplBugOn.inc}
|
||
|
{$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
|
||
|
begin
|
||
|
Result := ExtractToUInt32(AIndex);
|
||
|
end;
|
||
|
|
||
|
function TIdBuffer.ExtractToUInt16(const AIndex: Integer): UInt16;
|
||
|
var
|
||
|
LIndex : Integer;
|
||
|
begin
|
||
|
if AIndex < 0 then begin
|
||
|
LIndex := FHeadIndex;
|
||
|
end else begin
|
||
|
LIndex := AIndex;
|
||
|
end;
|
||
|
Result := BytesToUInt16(FBytes, LIndex);
|
||
|
Result := GStack.NetworkToHost(Result);
|
||
|
if AIndex < 0 then begin
|
||
|
Remove(2);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
{$I IdDeprecatedImplBugOff.inc}
|
||
|
function TIdBuffer.ExtractToWord(const AIndex: Integer): UInt16;
|
||
|
{$I IdDeprecatedImplBugOn.inc}
|
||
|
{$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
|
||
|
begin
|
||
|
Result := ExtractToUInt16(AIndex);
|
||
|
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;
|
||
|
|
||
|
{$I IdDeprecatedImplBugOff.inc}
|
||
|
function TIdBuffer.ExtractToByte(const AIndex: Integer): UInt8;
|
||
|
{$I IdDeprecatedImplBugOn.inc}
|
||
|
{$IFDEF USE_CLASSINLINE}inline;{$ENDIF}
|
||
|
begin
|
||
|
Result := ExtractToUInt8(AIndex);
|
||
|
end;
|
||
|
|
||
|
procedure TIdBuffer.Write(const AValue: UInt16; const ADestIndex: Integer);
|
||
|
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 := GStack.HostToNetwork(AValue);
|
||
|
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: TIdUInt64; const ADestIndex: Integer);
|
||
|
var
|
||
|
LVal: TIdUInt64;
|
||
|
LIndex: Integer;
|
||
|
begin
|
||
|
if ADestIndex < 0 then
|
||
|
begin
|
||
|
LIndex := FHeadIndex + Size;
|
||
|
SetLength(FBytes, LIndex + 8);
|
||
|
end else
|
||
|
begin
|
||
|
LIndex := ADestIndex;
|
||
|
end;
|
||
|
LVal := GStack.HostToNetwork(AValue);
|
||
|
CopyTIdUInt64(LVal, FBytes, LIndex);
|
||
|
if LIndex >= FSize then begin
|
||
|
FSize := LIndex + 8;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdBuffer.Write(const AValue: UInt32; const ADestIndex: Integer);
|
||
|
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 := GStack.HostToNetwork(AValue);
|
||
|
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
|
||
|
{$IFDEF STRING_IS_ANSI}, FAnsiEncoding{$ENDIF}
|
||
|
);
|
||
|
end;
|
||
|
|
||
|
end.
|
||
|
|
||
|
|