File: MappingFile.pas

package info (click to toggle)
doublecmd 0.7.7-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 32,764 kB
  • ctags: 34,016
  • sloc: pascal: 273,752; ansic: 5,938; sh: 733; makefile: 194; python: 116; xml: 107
file content (349 lines) | stat: -rw-r--r-- 9,237 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
{ **************************************************** }
{ MappingFile unit v1.0 for Delphi                     }
{ Copyright (c) Razumikhin Dmitry, 2005                }
{ E-mail: razumikhin_d@mail.ru                         }
{                                                      }
{ Use under terms LGPL license:                        }
{ http://www.gnu.org/copyleft/lesser.html              }
{ **************************************************** }
unit MappingFile;

interface
uses
  SysUtils, Windows, Classes, RTLConsts;

type
  EFileMappingError = class(Exception);

  TMappingFile = class
  private
    fHandle: Integer;        //file handle
    fFileName: string;       //file name
    fMode: Word;             //file open mode
    fMappingHandle: THandle; //handle of mapping file
    fBaseAddress: PChar;     //address of file image in memory
    fPos: Integer;           //current position
    fSize: Integer;          //size of real data
    fCapacity: Integer;      //size of allocated memory
    fExtraMem: Integer;       
    function GetChar(Index: Integer): Char;
    procedure SetSize(const Value: Integer);
    procedure SetChar(Index: Integer; const Value: Char);
    procedure TryMount;
    procedure ReMount;
    procedure SetCapacity(const Value: Integer);
    procedure SetPos(const Value: Integer);
  public
    property BaseAddress: PChar read fBaseAddress;
    property Size: Integer read fSize write SetSize;
    property Capacity: Integer read fCapacity write SetCapacity;
    property ExtraMem: Integer read fExtraMem write fExtraMem;
    property Ch[Index: Integer]: Char read GetChar write SetChar;
    property Position: Integer read fPos write SetPos;

    function Seek(Offset, Origin: Integer): Integer;
    //read functions
    function ReadCh(out Ch: Char): Boolean;
    function ReadStr(out Str: string; Len: Integer): Boolean; overload;
    function ReadStr(const Index, Len: Integer): string; overload;

    function Find(Ch: Char; StartIndex: Integer = 0): Integer;
    //write functions
    procedure WriteCh(const Ch: Char);
    procedure WriteStr(const Str: string); overload;
    procedure WriteStr(const Str: string; Index: Integer); overload;
    procedure WriteBuffer(const Buf: PChar; Count: Integer); overload;
    procedure WriteBuffer(const Buf: PChar; Index, Count: Integer); overload;

    //insert functions (expand + write)
    procedure InsertBuffer(const Buf: PChar; Count: Integer); overload;
    procedure InsertBuffer(const Buf: PChar; Index, Count: Integer); overload;
    procedure InsertStr(const Str: string); overload;
    procedure InsertStr(const Str: string; Index: Integer); overload;

    constructor Create(const FileName: string; Mode: Word);
    destructor Destroy; override;
  end;

function FileResize(Handle: THandle; Size: Integer): LongBool;
function FileMount(var MappingHandle: THandle; const FileHandle: Integer; ReadOnly: Boolean = True): Pointer;
procedure FileUmount(MappingHandle: THandle; BaseAddress: Pointer);

implementation

{ TMappingFile }

constructor TMappingFile.Create(const FileName: string; Mode: Word);
begin
  inherited Create;
  fFileName:=FileName;
  fMode:=Mode;
  fPos:=0;
  fSize:=0;
  fCapacity:=0;
  fExtraMem:=1024;
  fBaseAddress:=nil;
  if Mode = fmCreate then
    begin
    fHandle:=FileCreate(FileName);
    if fHandle < 0 then
      raise EFCreateError.CreateResFmt(@SFCreateErrorEx, [ExpandFileName(FileName), SysErrorMessage(GetLastError)]);
    end
  else
    begin
    fHandle:=FileOpen(FileName, Mode);
    if fHandle < 0 then
      raise EFOpenError.CreateResFmt(@SFOpenErrorEx, [ExpandFileName(FileName), SysErrorMessage(GetLastError)]);
    end;
  fSize:=GetFileSize(fHandle, nil);
  fCapacity:=fSize;
  TryMount;
end;

destructor TMappingFile.Destroy;
begin
  FileUmount(fMappingHandle, fBaseAddress);
  if fSize <> fCapacity then
    FileResize(fHandle, fSize);
  if fHandle >=0 then FileClose(fHandle);
  inherited;
end;

function TMappingFile.Find(Ch: Char; StartIndex: Integer): Integer;
var
  i: Integer;
begin
  for i:=StartIndex to fSize-1 do
    if Ch = PChar(fBaseAddress + i)^ then
      begin
      Result:=i;
      Exit;
      end;
  Result:=-1;
end;

function TMappingFile.GetChar(Index: Integer): Char;
begin
  Result:=PChar(fBaseAddress + Index)^; //Not control the bounds
end;

procedure TMappingFile.InsertBuffer(const Buf: PChar; Count: Integer);
begin
  InsertBuffer(Buf, fPos, Count);
  Inc(fPos, Count);
end;

procedure TMappingFile.InsertBuffer(const Buf: PChar; Index,
  Count: Integer);
var
  MoveCount: Integer;
begin
  if Count <> 0 then
    begin
    MoveCount:=fSize - Index;
    SetSize(fSize + Count);
    Move(PChar(fBaseAddress + Index)^, PChar(fBaseAddress + Index + Count)^, MoveCount);
    Move(Buf^, PChar(fBaseAddress + Index)^, Count);
    end;
end;

procedure TMappingFile.InsertStr(const Str: string);
begin
  InsertBuffer(PChar(Str), Length(Str));
end;

procedure TMappingFile.InsertStr(const Str: string; Index: Integer);
begin
  InsertBuffer(PChar(Str), Index, Length(Str));
end;

function TMappingFile.ReadCh(out Ch: Char): Boolean;
begin
  Result:=fPos < fSize;
  if Result then
    begin
    Ch:=PChar(fBaseAddress + fPos)^;
    Inc(fPos, SizeOf(Char));
    end
  else
    Ch:=#0;
end;

function TMappingFile.ReadStr(out Str: string; Len: Integer): Boolean;
begin
  Result:=(fPos + Len) <= fSize;
  SetLength(Str, Len);
  Move(PChar(fBaseAddress + fPos)^, Str[1], Len);
  Inc(fPos, Len);
end;

function TMappingFile.ReadStr(const Index, Len: Integer): string;
begin
  SetLength(Result, Len);
  Move(PChar(fBaseAddress + Index)^, Result[1], Len);
end;

procedure TMappingFile.Remount;
begin
  if Assigned(fBaseAddress) then
    FileUmount(fMappingHandle, fBaseAddress);
  TryMount;
end;

function TMappingFile.Seek(Offset, Origin: Integer): Integer;
var
  NewPos: Integer;
begin
  Result:=-1;
  case Origin of
    0:
      begin
      if Offset >= 0 then
        begin
        if (Offset > fSize) then
          SetSize(Offset);
        fPos:=Offset;
        Result:=Offset;
        end;
      end;
    1:
      begin
      NewPos:= fPos + Offset;
      if NewPos >=0 then
        begin
        if (NewPos > fSize) then
          SetSize(NewPos);
        fPos:=NewPos;
        Result:=NewPos;
        end;
      end;
    2:
      begin
      NewPos:=fSize - Offset - 1;
      if NewPos >=0 then
        begin
        if (NewPos > fSize) then
          SetSize(NewPos);
        fPos:=NewPos;
        Result:=NewPos;
        end;
      end;
  end;
end;

procedure TMappingFile.SetCapacity(const Value: Integer);
begin
  if fCapacity <> Value then
    begin
    fCapacity := Value;
    FileResize(fHandle, fCapacity);
    Remount;
    end;
end;

procedure TMappingFile.SetChar(Index: Integer; const Value: Char);
begin
  PChar(fBaseAddress + Index)^:=Value;  //Not control the bounds
end;

procedure TMappingFile.SetPos(const Value: Integer);
begin
  Seek(Value, 0);
end;

procedure TMappingFile.SetSize(const Value: Integer);
begin
  if fSize <> Value then
    begin
    fSize := Value;
    if fPos >= fSize then fPos:=fSize - 1;
    if fSize > fCapacity then
      SetCapacity(fSize + fExtraMem);
    end;
end;

procedure TMappingFile.TryMount;
begin
  if fSize > 0 then
    begin
    fBaseAddress:=FileMount(fMappingHandle, fHandle, fMode = fmOpenRead);
    if not Assigned(fBaseAddress) then
      raise EFileMappingError.CreateFmt('Could not mapped file ''%s''',[fFileName]);
    end;
end;

procedure TMappingFile.WriteBuffer(const Buf: PChar;
  Count: Integer);
begin
  if (fPos + Count) > fSize then
    SetSize(fPos + Count);
  Move(Buf^, PChar(fBaseAddress + fPos)^, Count);
  fPos:=fPos + Count;
end;

procedure TMappingFile.WriteBuffer(const Buf: PChar; Index,
  Count: Integer);
begin
  if (Index + Count) > fSize then
    SetSize(Index + Count);
  Move(Buf^, PChar(fBaseAddress + Index)^, Count);
end;

procedure TMappingFile.WriteCh(const Ch: Char);
begin
  WriteBuffer(@Ch, SizeOf(Char));
end;

procedure TMappingFile.WriteStr(const Str: string);
begin
  WriteBuffer(PChar(Str), Length(Str));
end;

procedure TMappingFile.WriteStr(const Str: string; Index: Integer);
begin
  WriteBuffer(PChar(Str), Index, Length(Str));
end;

//-----------------------------------------------------------------------

function FileMount(var MappingHandle: THandle; const FileHandle: Integer; ReadOnly: Boolean = True): Pointer;
var
  FileMappingMode,
  MapViewMode: DWORD;
begin
  if ReadOnly then
    begin
    FileMappingMode:=PAGE_READONLY;
    MapViewMode:=FILE_MAP_READ;
    end
  else
    begin
    FileMappingMode:=PAGE_READWRITE;
    MapViewMode:=FILE_MAP_READ + FILE_MAP_WRITE;
    end;

  MappingHandle:=CreateFileMapping(FileHandle, nil, FileMappingMode, 0, 0, nil);
  if MappingHandle <> 0 then
    begin
    Result:=MapViewOfFile(MappingHandle, MapViewMode, 0, 0, 0);
    end
  else
    Result:=nil;
end;

procedure FileUmount(MappingHandle: THandle; BaseAddress: Pointer);
begin
  if Assigned(BaseAddress) then
    UnmapViewOfFile(BaseAddress);
  if MappingHandle <> 0 then
    CloseHandle(MappingHandle);
end;

function FileResize(Handle: THandle; Size: Integer): LongBool;
begin
  FileSeek(Handle, Size, 0);
  Result:=SetEndOfFile(Handle);
end;

end.