// // This function include is meant to be used as an inner function! // // It requires the following variables in the outer scope: // var byteBuffer: AnsiString; bufByteCount, byteBufChPos: Integer; // // These variables must be initialized as follows in the outer code before calling ReadNextLine: // SetLength(byteBuffer, cDfltCopyBufferSize); // bufByteCount := 0; // byteBufChPos := 1; // function ReadNextLine(const ASrcStream: ISequentialStream; const ACharKind: TGMCharKind; var ALine: TGMString; var ALineEnd: EGMLineEndKind; const ACaller: TObject): Boolean; const cLineEndLF: array [Boolean] of EGMLineEndKind = (lekLF, leLFCR); cLineEndCR: array [Boolean] of EGMLineEndKind = (lekCR, lekCRLF); var byteLine: RawByteString; unicodeLine: TGMString; readMore: Boolean; // // Does NOT recognize single CR as a line break! All other kinds of line breaks will be recognized: CRLF | LFCR | LF // //function ReadAnsiLine: AnsiString; //var lnStart, lnEnd: PAnsiChar; lenInBytes, addOffs: PtrInt; // procedure StripLastChar; // (ACharCount: Integer); // begin // Inc(addOffs, SizeOf(AnsiChar)); // Dec(lenInBytes, SizeOf(AnsiChar)); // end; //begin // addOffs := 0; // lnStart := @byteBuffer[byteBufChPos]; // lnEnd := GMStrLScanA(lnStart, #10, Length(byteBuffer) - byteBufChPos + 1); // if lnEnd = nil then // begin // lenInBytes := Length(byteBuffer) - byteBufChPos + 1; // //if (lnStart + (lenInBytes div SizeOf(AnsiChar)) - 1)^ = #13 then StripLastChar; // <- line end chars split by buffer size // if (lnStart + lenInBytes - 1)^ = #13 then StripLastChar; // <- line end chars split by buffer size // readMore := True; // end // else // begin // lenInBytes := lnEnd - lnStart; // addOffs := SizeOf(AnsiChar); // if (lnEnd > lnStart) and ((lnEnd - 1)^ = #13) then StripLastChar else // if ((lnEnd + 1)^ = #13) then Inc(addOffs, SizeOf(AnsiChar)); // end; // // SetLength(Result, lenInBytes); // if lenInBytes > 0 then System.Move(byteBuffer[byteBufChPos], Result[1], lenInBytes); // Inc(byteBufChPos, lenInBytes + addOffs); //end; // // Recognizes all kinds of line endings, not slower than "GMStrLScanA" version // function ReadByteLine: RawByteString; const cLineEndSize: array [Boolean] of PtrInt = (SizeOf(AnsiChar), 2 * SizeOf(AnsiChar)); var lnStart, lnEnd: PAnsiChar; lenInBytes, addOffs: PtrInt; i: LongWord; twoChs: Boolean; begin lnStart := Pointer(@byteBuffer[byteBufChPos]); lnEnd := lnStart; addOffs := 0; //while True do for i:=0 to High(i) do case lnEnd^ of #0: begin lnEnd := nil; Break; end; #10: begin twoChs := (lnEnd + 1)^ = #13; Inc(addOffs, cLineEndSize[twoChs]); ALineEnd := cLineEndLF[twoChs]; Break; end; #13: begin twoChs := (lnEnd + 1)^ = #10; Inc(addOffs, cLineEndSize[twoChs]); ALineEnd := cLineEndCR[twoChs]; Break; end; else Inc(lnEnd); end; if lnEnd <> nil then lenInBytes := (lnEnd - lnStart) * SizeOf(AnsiChar) else begin lenInBytes := Length(byteBuffer) - byteBufChPos + 1; readMore := True; end; SetLength(Result, lenInBytes); if lenInBytes > 0 then System.Move(byteBuffer[byteBufChPos], Result[1], lenInBytes); Inc(byteBufChPos, lenInBytes + addOffs); end; // // Does NOT recognize single CR as a line break! All other kinds of line breaks will be recognized: CRLF | LFCR | LF // //function ReadUnicodeLine: UnicodeString; //var lnStart, lnEnd: PWideChar; lenInBytes, addOffs: PtrInt; // procedure StripLastChar; // (ACharCount: Integer); // begin // Inc(addOffs, SizeOf(UnicodeChar)); // Dec(lenInBytes, SizeOf(UnicodeChar)); // end; //begin // addOffs := 0; // lnStart := Pointer(@byteBuffer[byteBufChPos]); // lnEnd := GMStrLScanW(lnStart, #10, (Length(byteBuffer) - byteBufChPos + 1) div SizeOf(UnicodeChar)); // if lnEnd = nil then // begin // lenInBytes := Length(byteBuffer) - byteBufChPos + 1; // if (lnStart + (lenInBytes div SizeOf(UnicodeChar)) - 1)^ = #13 then StripLastChar; // <- line end chars split by buffer size // readMore := True; // end // else // begin // lenInBytes := (lnEnd - lnStart) * SizeOf(UnicodeChar); // addOffs := SizeOf(UnicodeChar); // if (lnEnd > lnStart) and ((lnEnd - 1)^ = #13) then StripLastChar else // if ((lnEnd + 1)^ = #13) then Inc(addOffs, SizeOf(UnicodeChar)); // end; // // SetLength(Result, lenInBytes div SizeOf(UnicodeChar)); // if lenInBytes > 0 then System.Move(byteBuffer[byteBufChPos], Result[1], lenInBytes); // Inc(byteBufChPos, lenInBytes + addOffs); //end; // // Recognizes all kinds of line endings, not slower than "GMStrLScanW" version // function ReadUnicodeLine: UnicodeString; const cLineEndSize: array [Boolean] of PtrInt = (SizeOf(UnicodeChar), 2 * SizeOf(UnicodeChar)); var lnStart, lnEnd: PWideChar; lenInBytes, addOffs: PtrInt; i: LongWord; twoChs: Boolean; begin lnStart := Pointer(@byteBuffer[byteBufChPos]); lnEnd := lnStart; addOffs := 0; //while True do for i:=0 to High(i) do case lnEnd^ of #0: begin lnEnd := nil; Break; end; #10: begin twoChs := (lnEnd + 1)^ = #13; Inc(addOffs, cLineEndSize[twoChs]); ALineEnd := cLineEndLF[twoChs]; Break; end; #13: begin twoChs := (lnEnd + 1)^ = #10; Inc(addOffs, cLineEndSize[twoChs]); ALineEnd := cLineEndCR[twoChs]; Break; end; else Inc(lnEnd); end; if lnEnd <> nil then lenInBytes := (lnEnd - lnStart) * SizeOf(UnicodeChar) else begin lenInBytes := Length(byteBuffer) - byteBufChPos + 1; readMore := True; end; SetLength(Result, lenInBytes div SizeOf(UnicodeChar)); if lenInBytes > 0 then System.Move(byteBuffer[byteBufChPos], Result[1], lenInBytes); Inc(byteBufChPos, lenInBytes + addOffs); end; begin if ASrcStream = nil then begin Result := False; Exit; end; byteLine := ''; unicodeLine := ''; repeat readMore := False; if byteBufChPos > bufByteCount then begin GMHrCheckObj(ASrcStream.Read(PAnsiChar(byteBuffer), Length(byteBuffer), Pointer(@bufByteCount)), ACaller, {$I %CurrentRoutine%}); byteBufChPos := 1; end; Result := byteBufChPos <= bufByteCount; if Result then case ACharKind of ckAnsi, ckUtf8: byteLine := byteLine + ReadByteLine; ckUtf16LE: unicodeLine := unicodeLine + ReadUnicodeLine; end; until not readMore or not Result; //if Result then case ACharKind of ckAnsi: ALine := byteLine; ckUtf8: ALine := Utf8Decode(byteLine); ckUtf16LE: ALine := unicodeLine; else ALine := ''; end; Result := (bufByteCount > 0) or (Length(ALine) > 0); end;