unit mmaptextreaderunit;
{$MODE OBJFPC}{$H+}
{$MODESWITCH ADVANCEDRECORDS}
//{$MODESWITCH UNICODESTRINGS}
{$CODEPAGE UTF8}

{
    Memory map text file reader unit.
    For GNU/Linux 64 bit version.
    Version: 1.
    Written on FreePascal (https://freepascal.org/).
    Copyright (C) 2024-2025  Artyomov Alexander
    http://self-made-free.ru/
    aralni@mail.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
}

interface

uses BaseUnix,msetypes;

type
  TmmapTextFile = record
    Data: PByte;
    Size, Old: Int64;
    IsMapped: Boolean;
    FFileHandle: cint;
  end;

function mmapTextOpen(var fp: TmmapTextFile;fn:msestring):bytebool;
procedure mmapTextClose(var fp:TmmapTextFile);
function mmapTextNextLine(var fp:TmmapTextFile; var s1:msestring):bytebool;

implementation

function mmapTextOpen(var fp: TmmapTextFile;fn:msestring):bytebool;
begin
  fp.IsMapped := False;

  fp.FFileHandle := FpOpen(fn, O_RDONLY or O_LARGEFILE);
  if fp.FFileHandle = -1 then begin
    WriteLn('Cannot open file ', fn);
    Exit(false);
  end;

  fp.Size := FpLSeek(fp.FFileHandle, 0, SEEK_END);
  FpLSeek(fp.FFileHandle, 0, SEEK_SET);
  if fp.Size = 0 then begin
    FpClose(fp.FFileHandle);
    fp.Data:=nil;
    Exit(false);
  end;

  fp.Data := PByte(FpMMap(nil, fp.Size, PROT_READ, MAP_SHARED, fp.FFileHandle, 0));
  if fp.Data = Pointer(-1) then begin
    FpClose(fp.FFileHandle);
    WriteLn('MMap failed');
    Exit(false);
  end;
  
  fp.IsMapped := True;

fp.Old := 0;
Exit(true);
end;

procedure mmapTextClose(var fp:TmmapTextFile);
begin
  if fp.IsMapped then begin
    FpMUnMap(fp.Data, fp.Size);
    FpClose(fp.FFileHandle);
    fp.IsMapped := false;
  end;
end;

function mmapTextNextLine(var fp:TmmapTextFile; var s1:msestring):bytebool;
var f:Int64;
s:string;
begin
s := ''; s1 := '';
if fp.Old > fp.Size-1 then Exit(false);

for f := fp.Old to fp.Size-1 do
 if (fp.Data[f]=byte(#10)) then begin
  SetLength(s, f-fp.Old);
  if Length(s) > 0 then Move(fp.Data[fp.Old], s[1], Length(s));
  fp.Old:=f+1;
s1:=s;
  Exit(true);
 end; {end if}

SetLength(s, f-fp.Old+1);
if Length(s) > 0 then Move(fp.Data[fp.Old], s[1], Length(s));
fp.Old:=f+1;
s1:=s;
Exit(true);
end;

end.