IdStream.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10351: IdStream.pas
  11. {
  12. { Rev 1.0 2002.11.12 10:54:04 PM czhower
  13. }
  14. unit IdStream;
  15. {
  16. 2002-04-10 -Andrew P.Rybin
  17. -Read*, Write*, ReadLn optimization (for many strings use TIdReadLineStreamProxy)
  18. 2002-04-16 -Andrew P.Rybin
  19. -TIdStreamSafe, TIdStreamLight, TIdReadLineStreamProxy, optimization, misc
  20. }
  21. {$I IdCompilerDefines.inc}
  22. interface
  23. uses
  24. Classes,
  25. IdException, IdGlobal;
  26. type
  27. EIdEndOfStream = class(EIdException);
  28. TIdStream = class(TStream)
  29. // IMPORTANT!!!!!!!!
  30. // NO data members may exist in this class
  31. // This class is used to "hackcast" a TStream to add functionality
  32. public
  33. function ReadLn(AMaxLineLength: Integer = -1; AExceptionIfEOF: Boolean = FALSE): String;
  34. class function FindEOL(ABuf: PChar; var VLineBufSize: Integer; var VCrEncountered: Boolean): Integer;{Ret: StringSize}
  35. procedure Write(const AData: string); reintroduce; overload;
  36. procedure WriteLn(const AData: string = ''); overload; {Do not Localize}
  37. procedure WriteLn(const AData: string; const AArgs: array of const); overload;
  38. function This: TIdStream; // Result := SELF; "THIS Object"
  39. function BOF: Boolean; {Begin of Stream}
  40. function EOF: Boolean; {End of Stream}
  41. procedure Skip(ASize: Integer);
  42. function ReadInteger (const AConvert: Boolean = TRUE): Integer; //network order
  43. procedure WriteInteger(AValue: Integer; const AConvert: Boolean = True);
  44. function ReadString (const AConvert: Boolean = TRUE): String;
  45. procedure WriteString(const AStr: String; const AConvert: Boolean = True);
  46. End;//TIdStream
  47. implementation
  48. uses
  49. IdResourceStrings,
  50. IdStack,
  51. IdTCPConnection,
  52. IdTCPStream,
  53. SysUtils;
  54. const
  55. LBUFMAXSIZE = 2048;
  56. EOLArray = [CR,LF];
  57. WCSize = SizeOf(WideChar);
  58. { TIdStream }
  59. class function TIdStream.FindEOL(ABuf: PChar; var VLineBufSize: Integer; var VCrEncountered: Boolean): Integer;
  60. var
  61. i: Integer;
  62. begin
  63. Result := VLineBufSize; //EOL not found => use all
  64. i := 0; //[0..ALineBufSize-1]
  65. while i < VLineBufSize do begin
  66. case ABuf[i] of
  67. LF:
  68. begin
  69. Result := i; {string size}
  70. VCrEncountered := TRUE;
  71. VLineBufSize := i+1;
  72. BREAK;
  73. end;//LF
  74. CR:
  75. begin
  76. Result := i; {string size}
  77. VCrEncountered := TRUE;
  78. inc(i); //crLF?
  79. if (i < VLineBufSize) and (ABuf[i] = LF) then begin
  80. VLineBufSize := i+1;
  81. end
  82. else begin
  83. VLineBufSize := i;
  84. end;
  85. BREAK;
  86. end;//CR
  87. end;//case
  88. Inc(i);
  89. end;//while
  90. End;//FindEOL
  91. function TIdStream.ReadLn(AMaxLineLength: Integer = -1; AExceptionIfEOF: Boolean = FALSE): String;
  92. //TODO: Continue to optimize this function. Its performance severely impacts
  93. // the coders
  94. var
  95. LBufSize, LStringLen, LResultLen: Integer;
  96. LBuf: packed array [0..LBUFMAXSIZE] of Char;
  97. LStrmPos, LStrmSize: Integer; //LBytesToRead = stream size - Position
  98. LCrEncountered: Boolean;
  99. begin
  100. // 'is' does not work here - compiler error
  101. if InheritsFrom(TIdTCPStream) then begin
  102. Result := TIdTCPStream(Self).Connection.ReadLn(LF,-1,AMaxLineLength);
  103. end
  104. else begin
  105. if AMaxLineLength < 0 then begin
  106. AMaxLineLength := MaxInt;
  107. end;//if
  108. LCrEncountered := FALSE;
  109. Result := '';
  110. { we store the stream size for the whole routine to prevent
  111. so do not incur a performance penalty with TStream.Size. It has
  112. to use something such as Seek each time the size is obtained}
  113. {LStrmPos := SrcStream.Position; LStrmSize:= SrcStream.Size; 4 seek vs 3 seek}
  114. LStrmPos := Seek(0, soFromCurrent); //Position
  115. LStrmSize:= Seek(0, soFromEnd); //Size
  116. Seek(LStrmPos, soFromBeginning); //return position
  117. if (LStrmSize - LStrmPos) > 0 then begin
  118. while (LStrmPos < LStrmSize) and NOT LCrEncountered do begin
  119. LBufSize := Min(LStrmSize - LStrmPos, LBUFMAXSIZE);
  120. ReadBuffer(LBuf, LBufSize);
  121. LStringLen := FindEOL(LBuf,LBufSize,LCrEncountered);
  122. Inc(LStrmPos,LBufSize);
  123. LResultLen := Length(Result);
  124. if (LResultLen + LStringLen) > AMaxLineLength then begin
  125. LStringLen := AMaxLineLength - LResultLen;
  126. LCrEncountered := TRUE;
  127. Dec(LStrmPos,LBufSize);
  128. Inc(LStrmPos,LStringLen);
  129. end;//if
  130. SetLength(Result, LResultLen + LStringLen);
  131. Move(LBuf[0], PChar(Result)[LResultLen], LStringLen);
  132. end;//while
  133. Position := LStrmPos;
  134. end
  135. else begin
  136. if AExceptionIfEOF then begin
  137. raise EIdEndOfStream.Create(Format('End of stream: %s at %d',[ClassName,LStrmPos])); //LOCALIZE
  138. end;
  139. end;//if NOT EOF
  140. end;//if
  141. End;//ReadLn
  142. {function TIdStream.ReadLn: string;
  143. //TODO: Continue to optimize this function. Its performance severely impacts
  144. // the coders
  145. var
  146. i: Integer;
  147. LBuf : String;
  148. LBufSize, LBufPos : Integer;
  149. LBytesToRead : Integer; //stream size - Position
  150. LLn: Integer;
  151. LStrmPos, LStrmSize : Integer;
  152. LCrEncountered : Boolean;
  153. begin
  154. LCrEncountered := False;
  155. // 'is' does not work here - compiler error
  156. if InheritsFrom(TIdTCPStream) then begin
  157. Result := TIdTCPStream(Self).Connection.ReadLn;
  158. end else begin
  159. Result := '';
  160. LStrmPos := Position;
  161. { we store the stream size for the whole routine to prevent
  162. so do not incur a performance penalty with TStream.Size. It has
  163. to use something such as Seek each time the size is obtained
  164. }
  165. { LStrmSize := Size;
  166. LBytesToRead := LStrmSize - LStrmPos;
  167. if LBytesToRead > 0 then begin
  168. LBufPos := 0;
  169. while (LStrmPos < LStrmSize) and (LCrEncountered = False) do
  170. // while (LStrmPos <= LBytesToRead) and (LCrEncountered = False) do
  171. begin
  172. if LBufPos < LBytesToRead then
  173. begin
  174. LBufSize := Min(LBytesToRead - LBufPos,LBUFMAXSIZE);
  175. SetLength(LBuf, LBufSize);
  176. ReadBuffer(LBuf[1], LBufSize);
  177. for i := 1 to LBufSize do
  178. begin
  179. case LBuf[i] of
  180. CR : begin
  181. lln := i;
  182. LBufSize := i+1;
  183. if (i < LBufSize) and (LBuf[LBufSize]<>LF) then
  184. begin
  185. Dec(LBufSize);
  186. end;
  187. LCrEncountered := True;
  188. Break;
  189. end;
  190. LF : begin
  191. lln := i;
  192. LBufSize := i+1;
  193. if (i < LBufSize) and (LBuf[LBufSize]<>CR) then
  194. begin
  195. Dec(LBufSize);
  196. end;
  197. LCrEncountered := True;
  198. Break;
  199. end;
  200. end;
  201. end;
  202. if LCrEncountered then
  203. begin
  204. Dec(lln);
  205. SetLength(LBuf,lln);
  206. end;
  207. Inc(LStrmPos,LBufSize);
  208. Result := Result + LBuf;
  209. end;
  210. end;
  211. Position := LStrmPos;
  212. end;
  213. end;
  214. end; }
  215. {nction TIdStream.ReadLn: string;
  216. //TODO: Continue to optimize this function. Its performance severely impacts
  217. // the coders
  218. var
  219. i: Integer;
  220. LBuf : String;
  221. LBufSize, LBufPos : Integer;
  222. LBytesToRead : Integer; //stream size - Position
  223. LLn: Integer;
  224. LStrmPos, LStrmSize : Integer;
  225. LCrEncountered : Boolean;
  226. begin
  227. LCrEncountered := False;
  228. // 'is' does not work here - compiler error
  229. if InheritsFrom(TIdTCPStream) then begin
  230. Result := TIdTCPStream(Self).Connection.ReadLn;
  231. end else begin
  232. Result := '';
  233. LStrmPos := Position;
  234. { we store the stream size for the whole routine to prevent
  235. so do not incur a performance penalty with TStream.Size. It has
  236. to use something such as Seek each time the size is obtained
  237. }
  238. { LStrmSize := Size;
  239. LBytesToRead := LStrmSize - LStrmPos;
  240. if LBytesToRead > 0 then begin
  241. LBufPos := 0;
  242. while (LStrmPos < LStrmSize) and (LCrEncountered = False) do
  243. // while (LStrmPos <= LBytesToRead) and (LCrEncountered = False) do
  244. begin
  245. if LBufPos < LBytesToRead then
  246. begin
  247. LBufSize := LBytesToRead - LBufPos;
  248. if LBufSize > LBUFMAXSIZE then
  249. begin
  250. LBufSize := LBUFMAXSIZE;
  251. end;
  252. SetLength(LBuf, LBufSize);
  253. ReadBuffer(LBuf[1], LBufSize);
  254. lln := IndyPos(LF, LBuf);
  255. i := IndyPos(CR, LBuf);
  256. LCrEncountered := (lln > 0) or (i > 0);
  257. if LCrEncountered then
  258. begin
  259. //we only want i and lln not to equal zero unless both are zero
  260. //The reason is that some broken things might return just a CR or a LF
  261. //instead of both
  262. if lln = 0 then
  263. begin
  264. lln := i;
  265. end;
  266. if i = 0 then
  267. begin
  268. i := lln;
  269. end;
  270. //we do these two tests to make sure the CR and LF are together.
  271. //if they are appart, we assume they are two different line endings.
  272. if (lln > (i+1)) then
  273. begin
  274. lln := i;
  275. end;
  276. if (i > (lln+1)) then
  277. begin
  278. i := lln;
  279. end;
  280. LBufSize := IdGlobal.Max(lln,i);
  281. end;
  282. Inc(LStrmPos,LBufSize);
  283. Result := Result + LBuf;
  284. if LCrEncountered then
  285. begin
  286. SetLength(Result,Min(lln,i)-1);
  287. end;
  288. end;
  289. end;
  290. Position := LStrmPos;
  291. end;
  292. end;
  293. end; }
  294. procedure TIdStream.Write(const AData: string);
  295. var
  296. LDataLen: Integer;
  297. begin
  298. LDataLen := Length(AData);
  299. if LDataLen > 0 then begin
  300. WriteBuffer(Pointer(AData)^, LDataLen);
  301. end;
  302. end;
  303. procedure TIdStream.WriteLn(const AData: string = ''); {Do not Localize}
  304. begin
  305. Write(AData + sLineBreak);
  306. end;
  307. procedure TIdStream.WriteLn(const AData: string; const AArgs: array of const);
  308. Begin
  309. WriteLn(Format(AData, AArgs));
  310. End;//
  311. function TIdStream.This: TIdStream;
  312. Begin
  313. Result := SELF;
  314. End;//
  315. function TIdStream.BOF: Boolean;
  316. Begin
  317. Result := Seek(0,soFromCurrent)<=0; //Stream.Position
  318. End;
  319. function TIdStream.EOF: Boolean;
  320. var
  321. LPos: Int64;
  322. Begin
  323. LPos := Seek(0,soFromCurrent);
  324. Result := LPos>=Seek(0,soFromEnd);
  325. Seek(LPos,soFromBeginning);
  326. End;//EOF
  327. procedure TIdStream.Skip(ASize: Integer);
  328. Begin
  329. Seek(ASize, soFromCurrent);
  330. End;//Skip
  331. function TIdStream.ReadInteger(const AConvert: Boolean): Integer;
  332. begin
  333. ReadBuffer(Result, SizeOf(Result));
  334. if AConvert then begin
  335. Result := Integer(GStack.WSNToHL(LongWord(Result)));
  336. end;
  337. end;
  338. procedure TIdStream.WriteInteger(AValue: Integer; const AConvert: Boolean = True);
  339. begin
  340. if AConvert then begin
  341. AValue := Integer(GStack.WSHToNL(LongWord(AValue)));
  342. end;
  343. WriteBuffer(AValue, SizeOf(AValue));
  344. end;
  345. function TIdStream.ReadString(const AConvert: Boolean = TRUE): String;
  346. var
  347. L: Integer;
  348. Begin
  349. L := ReadInteger(AConvert);
  350. if L>0 then begin
  351. SetString(Result, NIL, L);
  352. ReadBuffer(Pointer(Result)^,L);
  353. end
  354. else begin
  355. Result := '';
  356. end;
  357. End;//ReadString
  358. procedure TIdStream.WriteString(const AStr: String; const AConvert: Boolean = True);
  359. var
  360. L: Integer;
  361. Begin
  362. L:= Length(AStr);
  363. WriteInteger(L, AConvert);
  364. if L>0 then begin
  365. WriteBuffer(Pointer(AStr)^,L);
  366. end;
  367. End;//WriteS
  368. END.