dcclassesutf8.pas 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. {
  2. Double commander
  3. -------------------------------------------------------------------------
  4. This module contains classes with UTF8 file names support.
  5. Copyright (C) 2008-2024 Alexander Koblov ([email protected])
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. }
  18. unit DCClassesUtf8;
  19. {$mode objfpc}{$H+}
  20. interface
  21. uses
  22. Classes, RtlConsts, SysUtils, IniFiles;
  23. type
  24. { TFileStreamEx class }
  25. TFileStreamEx = class(THandleStream)
  26. private
  27. FDirty: Int64;
  28. FAutoSync: Boolean;
  29. FDirtyLimit: Int64;
  30. procedure SetAutoSync(AValue: Boolean);
  31. protected
  32. FFileName: String;
  33. procedure Sync(AWritten: Int64);
  34. procedure SetCapacity(const NewCapacity: Int64);
  35. public
  36. constructor Create(const AFileName: String; Mode: LongWord); virtual; overload;
  37. destructor Destroy; override;
  38. function Flush: Boolean;
  39. function Read(var Buffer; Count: LongInt): LongInt; override;
  40. function Write(const Buffer; Count: LongInt): LongInt; override;
  41. property DirtyLimit: Int64 read FDirtyLimit write FDirtyLimit;
  42. property AutoSync: Boolean read FAutoSync write SetAutoSync;
  43. property Capacity: Int64 write SetCapacity;
  44. property FileName: String read FFileName;
  45. end;
  46. { TStringListEx }
  47. TStringListEx = class(TStringList)
  48. protected
  49. function DoCompareText(const S1, S2: String): PtrInt; override;
  50. public
  51. function IndexOfValue(const Value: String): Integer;
  52. procedure LoadFromFile(const FileName: String); override;
  53. procedure SaveToFile(const FileName: String); override;
  54. end;
  55. { TIniFileEx }
  56. TIniFileEx = class(TMemIniFile)
  57. private
  58. FReadOnly: Boolean;
  59. public
  60. constructor Create(const AFileName: String; Mode: Word; AOptions: TIniFileOptions = []); virtual;
  61. constructor Create(const AFileName: String; AOptions: TIniFileOptions = []); override;
  62. procedure UpdateFile; override;
  63. public
  64. property ReadOnly: Boolean read FReadOnly;
  65. end;
  66. implementation
  67. uses
  68. DCOSUtils, LazUTF8;
  69. { TFileStreamEx }
  70. procedure TFileStreamEx.SetAutoSync(AValue: Boolean);
  71. const
  72. DIRTY_LIMIT = 1024 * 1024;
  73. begin
  74. FAutoSync:= AValue;
  75. if AValue and (FDirtyLimit = 0) then
  76. begin
  77. FDirtyLimit:= DIRTY_LIMIT;
  78. end;
  79. end;
  80. procedure TFileStreamEx.Sync(AWritten: Int64);
  81. const
  82. TARGET_LATENCY_LOW = 900;
  83. TARGET_LATENCY_HIGH = 1100;
  84. DIRTY_LIMIT_LOW = 512 * 1024;
  85. DIRTY_LIMIT_HIGH = MaxLongInt + 1;
  86. var
  87. T1, T2: QWord;
  88. Elapsed: Double;
  89. Slowdown: Double;
  90. begin
  91. FDirty+= AWritten;
  92. if FDirty < FDirtyLimit then
  93. Exit;
  94. FDirty:= 0;
  95. T1:= GetTickCount64;
  96. if not FileFlushData(Handle) then
  97. Exit;
  98. T2:= GetTickCount64;
  99. Elapsed:= (T2 - T1);
  100. if (Elapsed > TARGET_LATENCY_HIGH) then
  101. begin
  102. if (FDirtyLimit > DIRTY_LIMIT_LOW) then
  103. begin
  104. Slowdown:= Elapsed / TARGET_LATENCY_HIGH;
  105. if (Slowdown > 2) then
  106. FDirtyLimit := Round(FDirtyLimit / Slowdown)
  107. else begin
  108. FDirtyLimit := Round(FDirtyLimit * 0.7);
  109. end;
  110. if (FDirtyLimit < DIRTY_LIMIT_LOW) then
  111. FDirtyLimit := DIRTY_LIMIT_LOW
  112. else begin
  113. FDirtyLimit := (FDirtyLimit div 4096 * 4096);
  114. end;
  115. end;
  116. end
  117. else if (Elapsed < TARGET_LATENCY_LOW) then
  118. begin
  119. if FDirtyLimit < DIRTY_LIMIT_HIGH then
  120. begin
  121. FDirtyLimit := Round(FDirtyLimit * 1.3);
  122. if (FDirtyLimit > DIRTY_LIMIT_HIGH) then
  123. FDirtyLimit := DIRTY_LIMIT_HIGH
  124. else begin
  125. FDirtyLimit := (FDirtyLimit div 4096 * 4096);
  126. end;
  127. end;
  128. end;
  129. end;
  130. procedure TFileStreamEx.SetCapacity(const NewCapacity: Int64);
  131. begin
  132. FileAllocate(Handle, NewCapacity);
  133. end;
  134. constructor TFileStreamEx.Create(const AFileName: String; Mode: LongWord);
  135. var
  136. AHandle: System.THandle;
  137. begin
  138. if (Mode and fmCreate) <> 0 then
  139. begin
  140. AHandle:= mbFileCreate(AFileName, Mode);
  141. if AHandle = feInvalidHandle then
  142. raise EFCreateError.CreateFmt(SFCreateError + LineEnding + mbSysErrorMessage, [AFileName])
  143. else
  144. inherited Create(AHandle);
  145. end
  146. else
  147. begin
  148. AHandle:= mbFileOpen(AFileName, Mode);
  149. if AHandle = feInvalidHandle then
  150. raise EFOpenError.CreateFmt(SFOpenError + LineEnding + mbSysErrorMessage , [AFilename])
  151. else
  152. inherited Create(AHandle);
  153. end;
  154. FFileName:= AFileName;
  155. end;
  156. destructor TFileStreamEx.Destroy;
  157. begin
  158. inherited Destroy;
  159. // Close handle after destroying the base object, because it may use Handle in Destroy.
  160. if Handle <> feInvalidHandle then FileClose(Handle);
  161. end;
  162. function TFileStreamEx.Flush: Boolean;
  163. begin
  164. Result:= FileFlush(Handle);
  165. end;
  166. function TFileStreamEx.Read(var Buffer; Count: LongInt): LongInt;
  167. begin
  168. Result:= FileRead(Handle, Buffer, Count);
  169. if Result = -1 then
  170. raise EReadError.Create(mbSysErrorMessage(GetLastOSError));
  171. end;
  172. function TFileStreamEx.Write(const Buffer; Count: LongInt): LongInt;
  173. begin
  174. Result:= inherited Write(Buffer, Count);
  175. if FAutoSync and (Result > 0) then Sync(Result);
  176. end;
  177. { TStringListEx }
  178. function TStringListEx.DoCompareText(const S1, S2: String): PtrInt;
  179. begin
  180. if CaseSensitive then
  181. Result:= UTF8CompareStr(S1, S2)
  182. else
  183. Result:= UTF8CompareText(S1, S2);
  184. end;
  185. function TStringListEx.IndexOfValue(const Value: String): Integer;
  186. var
  187. iStart: LongInt;
  188. sTemp: String;
  189. begin
  190. CheckSpecialChars;
  191. Result:= 0;
  192. while (Result < Count) do
  193. begin
  194. sTemp:= Strings[Result];
  195. iStart:= Pos(NameValueSeparator, sTemp) + 1;
  196. if (iStart > 0) and (DoCompareText(Value, Copy(sTemp, iStart, MaxInt)) = 0) then
  197. Exit;
  198. Inc(result);
  199. end;
  200. Result:= -1;
  201. end;
  202. procedure TStringListEx.LoadFromFile(const FileName: String);
  203. var
  204. fsFileStream: TFileStreamEx;
  205. begin
  206. fsFileStream:= TFileStreamEx.Create(FileName, fmOpenRead or fmShareDenyNone);
  207. try
  208. LoadFromStream(fsFileStream);
  209. finally
  210. fsFileStream.Free;
  211. end;
  212. end;
  213. procedure TStringListEx.SaveToFile(const FileName: String);
  214. var
  215. AMode: LongWord;
  216. fsFileStream: TFileStreamEx;
  217. begin
  218. if not mbFileExists(FileName) then
  219. AMode:= fmCreate
  220. else begin
  221. AMode:= fmOpenWrite or fmShareDenyWrite;
  222. end;
  223. fsFileStream:= TFileStreamEx.Create(FileName, AMode);
  224. try
  225. SaveToStream(fsFileStream);
  226. if (AMode <> fmCreate) then fsFileStream.Size:= fsFileStream.Position;
  227. finally
  228. fsFileStream.Free;
  229. end;
  230. end;
  231. { TIniFileEx }
  232. constructor TIniFileEx.Create(const AFileName: String; Mode: Word;
  233. AOptions: TIniFileOptions);
  234. var
  235. slLines : TStringListEx;
  236. begin
  237. FReadOnly := ((Mode and $03) = fmOpenRead);
  238. inherited Create(EmptyStr, AOptions);
  239. if ((Mode and $03) <> fmOpenWrite) then
  240. begin
  241. if mbFileExists(AFileName) then
  242. begin
  243. slLines := TStringListEx.Create;
  244. try
  245. slLines.LoadFromFile(AFileName);
  246. SetStrings(slLines);
  247. finally
  248. slLines.Free;
  249. end;
  250. end;
  251. end;
  252. Rename(AFileName, False);
  253. end;
  254. constructor TIniFileEx.Create(const AFileName: String; AOptions: TIniFileOptions);
  255. var
  256. Mode: Word;
  257. begin
  258. if not mbFileExists(AFileName) then
  259. Mode := fmOpenWrite or fmShareDenyWrite
  260. else if mbFileAccess(AFileName, fmOpenReadWrite or fmShareDenyWrite) then
  261. Mode := fmOpenReadWrite or fmShareDenyWrite
  262. else begin
  263. Mode := fmOpenRead or fmShareDenyNone;
  264. end;
  265. Create(AFileName, Mode, AOptions);
  266. end;
  267. procedure TIniFileEx.UpdateFile;
  268. var
  269. slLines: TStringListEx;
  270. begin
  271. if not FReadOnly then
  272. begin
  273. slLines := TStringListEx.Create;
  274. try
  275. GetStrings(slLines);
  276. slLines.SaveToFile(FileName);
  277. PBoolean(@Dirty)^:= False;
  278. finally
  279. slLines.Free;
  280. end;
  281. end;
  282. end;
  283. end.