cwstring.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2013 by Yury Sidorov,
  4. member of the Free Pascal development team.
  5. Wide string support for Android
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$mode objfpc}
  13. {$inline on}
  14. {$implicitexceptions off}
  15. unit cwstring;
  16. interface
  17. procedure SetCWidestringManager;
  18. implementation
  19. uses dynlibs;
  20. type
  21. UErrorCode = SizeInt;
  22. int32_t = longint;
  23. uint32_t = longword;
  24. PUConverter = pointer;
  25. PUCollator = pointer;
  26. UBool = LongBool;
  27. var
  28. hlibICU: TLibHandle;
  29. hlibICUi18n: TLibHandle;
  30. LibVer: ansistring;
  31. ucnv_open: function (converterName: PAnsiChar; var pErrorCode: UErrorCode): PUConverter; cdecl;
  32. ucnv_close: procedure (converter: PUConverter); cdecl;
  33. ucnv_setSubstChars: procedure (converter: PUConverter; subChars: PAnsiChar; len: byte; var pErrorCode: UErrorCode); cdecl;
  34. ucnv_setFallback: procedure (cnv: PUConverter; usesFallback: UBool); cdecl;
  35. ucnv_fromUChars: function (cnv: PUConverter; dest: PAnsiChar; destCapacity: int32_t; src: PUnicodeChar; srcLength: int32_t; var pErrorCode: UErrorCode): int32_t; cdecl;
  36. ucnv_toUChars: function (cnv: PUConverter; dest: PUnicodeChar; destCapacity: int32_t; src: PAnsiChar; srcLength: int32_t; var pErrorCode: UErrorCode): int32_t; cdecl;
  37. u_strToUpper: function (dest: PUnicodeChar; destCapacity: int32_t; src: PUnicodeChar; srcLength: int32_t; locale: PAnsiChar; var pErrorCode: UErrorCode): int32_t; cdecl;
  38. u_strToLower: function (dest: PUnicodeChar; destCapacity: int32_t; src: PUnicodeChar; srcLength: int32_t; locale: PAnsiChar; var pErrorCode: UErrorCode): int32_t; cdecl;
  39. u_strCompare: function (s1: PUnicodeChar; length1: int32_t; s2: PUnicodeChar; length2: int32_t; codePointOrder: UBool): int32_t; cdecl;
  40. u_strCaseCompare: function (s1: PUnicodeChar; length1: int32_t; s2: PUnicodeChar; length2: int32_t; options: uint32_t; var pErrorCode: UErrorCode): int32_t; cdecl;
  41. u_getDataDirectory: function(): PAnsiChar; cdecl;
  42. u_setDataDirectory: procedure(directory: PAnsiChar); cdecl;
  43. ucol_open: function(loc: PAnsiChar; var status: UErrorCode): PUCollator; cdecl;
  44. ucol_close: procedure (coll: PUCollator); cdecl;
  45. ucol_strcoll: function (coll: PUCollator; source: PUnicodeChar; sourceLength: int32_t; target: PUnicodeChar; targetLength: int32_t): int32_t; cdecl;
  46. ucol_setStrength: procedure (coll: PUCollator; strength: int32_t); cdecl;
  47. threadvar
  48. ThreadDataInited: boolean;
  49. DefConv, LastConv: PUConverter;
  50. LastCP: TSystemCodePage;
  51. DefColl: PUCollator;
  52. function OpenConverter(const name: ansistring): PUConverter;
  53. var
  54. err: UErrorCode;
  55. begin
  56. err:=0;
  57. Result:=ucnv_open(PAnsiChar(name), err);
  58. if Result <> nil then begin
  59. ucnv_setSubstChars(Result, '?', 1, err);
  60. ucnv_setFallback(Result, True);
  61. end;
  62. end;
  63. procedure InitThreadData;
  64. var
  65. err: UErrorCode;
  66. col: PUCollator;
  67. begin
  68. if (hlibICU = 0) or ThreadDataInited then
  69. exit;
  70. ThreadDataInited:=True;
  71. DefConv:=OpenConverter('utf8');
  72. err:=0;
  73. col:=ucol_open(nil, err);
  74. if col <> nil then
  75. ucol_setStrength(col, 2);
  76. DefColl:=col;
  77. end;
  78. function GetConverter(cp: TSystemCodePage): PUConverter;
  79. var
  80. s: ansistring;
  81. begin
  82. if hlibICU = 0 then begin
  83. Result:=nil;
  84. exit;
  85. end;
  86. InitThreadData;
  87. if (cp = CP_UTF8) or (cp = CP_ACP) then
  88. Result:=DefConv
  89. else begin
  90. if cp <> LastCP then begin
  91. Str(cp, s);
  92. LastConv:=OpenConverter('cp' + s);
  93. LastCP:=cp;
  94. end;
  95. Result:=LastConv;
  96. end;
  97. end;
  98. procedure Unicode2AnsiMove(source: PUnicodeChar; var dest: RawByteString; cp: TSystemCodePage; len: SizeInt);
  99. var
  100. len2: SizeInt;
  101. conv: PUConverter;
  102. err: UErrorCode;
  103. begin
  104. if len = 0 then begin
  105. dest:='';
  106. exit;
  107. end;
  108. conv:=GetConverter(cp);
  109. if (conv = nil) and not ( (cp = CP_UTF8) or (cp = CP_ACP) ) then begin
  110. // fallback implementation
  111. DefaultUnicode2AnsiMove(source,dest,cp,len);
  112. exit;
  113. end;
  114. len2:=len*3;
  115. SetLength(dest, len2);
  116. err:=0;
  117. if conv <> nil then
  118. len2:=ucnv_fromUChars(conv, PAnsiChar(dest), len2, source, len, err)
  119. else begin
  120. // Use UTF-8 conversion from RTL
  121. cp:=CP_UTF8;
  122. len2:=UnicodeToUtf8(PAnsiChar(dest), len2, source, len) - 1;
  123. end;
  124. if len2 > Length(dest) then begin
  125. SetLength(dest, len2);
  126. err:=0;
  127. if conv <> nil then
  128. len2:=ucnv_fromUChars(conv, PAnsiChar(dest), len2, source, len, err)
  129. else
  130. len2:=UnicodeToUtf8(PAnsiChar(dest), len2, source, len) - 1;
  131. end;
  132. if len2 < 0 then
  133. len2:=0;
  134. SetLength(dest, len2);
  135. SetCodePage(dest, cp, False);
  136. end;
  137. procedure Ansi2UnicodeMove(source:pchar;cp : TSystemCodePage;var dest:unicodestring;len:SizeInt);
  138. var
  139. len2: SizeInt;
  140. conv: PUConverter;
  141. err: UErrorCode;
  142. begin
  143. if len = 0 then begin
  144. dest:='';
  145. exit;
  146. end;
  147. conv:=GetConverter(cp);
  148. if (conv = nil) and not ( (cp = CP_UTF8) or (cp = CP_ACP) ) then begin
  149. // fallback implementation
  150. DefaultAnsi2UnicodeMove(source,cp,dest,len);
  151. exit;
  152. end;
  153. len2:=len;
  154. SetLength(dest, len2);
  155. err:=0;
  156. if conv <> nil then
  157. len2:=ucnv_toUChars(conv, PUnicodeChar(dest), len2, source, len, err)
  158. else
  159. // Use UTF-8 conversion from RTL
  160. len2:=Utf8ToUnicode(PUnicodeChar(dest), len2, source, len) - 1;
  161. if len2 > Length(dest) then begin
  162. SetLength(dest, len2);
  163. err:=0;
  164. if conv <> nil then
  165. len2:=ucnv_toUChars(conv, PUnicodeChar(dest), len2, source, len, err)
  166. else
  167. len2:=Utf8ToUnicode(PUnicodeChar(dest), len2, source, len) - 1;
  168. end;
  169. if len2 < 0 then
  170. len2:=0;
  171. SetLength(dest, len2);
  172. end;
  173. function UpperUnicodeString(const s : UnicodeString) : UnicodeString;
  174. var
  175. len, len2: SizeInt;
  176. err: UErrorCode;
  177. begin
  178. if hlibICU = 0 then begin
  179. // fallback implementation
  180. Result:=UnicodeString(UpCase(AnsiString(s)));
  181. exit;
  182. end;
  183. len:=Length(s);
  184. SetLength(Result, len);
  185. if len = 0 then
  186. exit;
  187. err:=0;
  188. len2:=u_strToUpper(PUnicodeChar(Result), len, PUnicodeChar(s), len, nil, err);
  189. if len2 > len then begin
  190. SetLength(Result, len2);
  191. err:=0;
  192. len2:=u_strToUpper(PUnicodeChar(Result), len2, PUnicodeChar(s), len, nil, err);
  193. end;
  194. SetLength(Result, len2);
  195. end;
  196. function LowerUnicodeString(const s : UnicodeString) : UnicodeString;
  197. var
  198. len, len2: SizeInt;
  199. err: UErrorCode;
  200. begin
  201. if hlibICU = 0 then begin
  202. // fallback implementation
  203. Result:=UnicodeString(LowerCase(AnsiString(s)));
  204. exit;
  205. end;
  206. len:=Length(s);
  207. SetLength(Result, len);
  208. if len = 0 then
  209. exit;
  210. err:=0;
  211. len2:=u_strToLower(PUnicodeChar(Result), len, PUnicodeChar(s), len, nil, err);
  212. if len2 > len then begin
  213. SetLength(Result, len2);
  214. err:=0;
  215. len2:=u_strToLower(PUnicodeChar(Result), len2, PUnicodeChar(s), len, nil, err);
  216. end;
  217. SetLength(Result, len2);
  218. end;
  219. function _CompareStr(const S1, S2: UnicodeString): PtrInt;
  220. var
  221. count, count1, count2: SizeInt;
  222. begin
  223. result := 0;
  224. Count1 := Length(S1);
  225. Count2 := Length(S2);
  226. if Count1>Count2 then
  227. Count:=Count2
  228. else
  229. Count:=Count1;
  230. result := CompareByte(PUnicodeChar(S1)^, PUnicodeChar(S2)^, Count*SizeOf(UnicodeChar));
  231. if result=0 then
  232. result:=Count1 - Count2;
  233. end;
  234. function CompareUnicodeString(const s1, s2 : UnicodeString; Options : TCompareOptions) : PtrInt;
  235. const
  236. U_COMPARE_CODE_POINT_ORDER = $8000;
  237. var
  238. err: UErrorCode;
  239. begin
  240. if hlibICU = 0 then begin
  241. // fallback implementation
  242. Result:=_CompareStr(s1, s2);
  243. exit;
  244. end;
  245. if (coIgnoreCase in Options) then begin
  246. err:=0;
  247. Result:=u_strCaseCompare(PUnicodeChar(s1), Length(s1), PUnicodeChar(s2), Length(s2), U_COMPARE_CODE_POINT_ORDER, err);
  248. end
  249. else begin
  250. InitThreadData;
  251. if DefColl <> nil then
  252. Result:=ucol_strcoll(DefColl, PUnicodeChar(s1), Length(s1), PUnicodeChar(s2), Length(s2))
  253. else
  254. Result:=u_strCompare(PUnicodeChar(s1), Length(s1), PUnicodeChar(s2), Length(s2), True);
  255. end;
  256. end;
  257. function UpperAnsiString(const s : AnsiString) : AnsiString;
  258. begin
  259. Result:=AnsiString(UpperUnicodeString(UnicodeString(s)));
  260. end;
  261. function LowerAnsiString(const s : AnsiString) : AnsiString;
  262. begin
  263. Result:=AnsiString(LowerUnicodeString(UnicodeString(s)));
  264. end;
  265. function CompareStrAnsiString(const s1, s2: ansistring): PtrInt;
  266. begin
  267. Result:=CompareUnicodeString(UnicodeString(s1), UnicodeString(s2), []);
  268. end;
  269. function StrCompAnsi(s1,s2 : PChar): PtrInt;
  270. begin
  271. Result:=CompareUnicodeString(UnicodeString(s1), UnicodeString(s2), []);
  272. end;
  273. function AnsiCompareText(const S1, S2: ansistring): PtrInt;
  274. begin
  275. Result:=CompareUnicodeString(UnicodeString(s1), UnicodeString(s2), [coIgnoreCase]);
  276. end;
  277. function AnsiStrIComp(S1, S2: PChar): PtrInt;
  278. begin
  279. Result:=CompareUnicodeString(UnicodeString(s1), UnicodeString(s2), [coIgnoreCase]);
  280. end;
  281. function AnsiStrLComp(S1, S2: PChar; MaxLen: PtrUInt): PtrInt;
  282. var
  283. as1, as2: ansistring;
  284. begin
  285. SetString(as1, S1, MaxLen);
  286. SetString(as2, S2, MaxLen);
  287. Result:=CompareUnicodeString(UnicodeString(as1), UnicodeString(as2), []);
  288. end;
  289. function AnsiStrLIComp(S1, S2: PChar; MaxLen: PtrUInt): PtrInt;
  290. var
  291. as1, as2: ansistring;
  292. begin
  293. SetString(as1, S1, MaxLen);
  294. SetString(as2, S2, MaxLen);
  295. Result:=CompareUnicodeString(UnicodeString(as1), UnicodeString(as2), [coIgnoreCase]);
  296. end;
  297. function AnsiStrLower(Str: PChar): PChar;
  298. var
  299. s, res: ansistring;
  300. begin
  301. s:=Str;
  302. res:=LowerAnsiString(s);
  303. if Length(res) > Length(s) then
  304. SetLength(res, Length(s));
  305. Move(PAnsiChar(res)^, Str, Length(res) + 1);
  306. Result:=Str;
  307. end;
  308. function AnsiStrUpper(Str: PChar): PChar;
  309. var
  310. s, res: ansistring;
  311. begin
  312. s:=Str;
  313. res:=UpperAnsiString(s);
  314. if Length(res) > Length(s) then
  315. SetLength(res, Length(s));
  316. Move(PAnsiChar(res)^, Str, Length(res) + 1);
  317. Result:=Str;
  318. end;
  319. function CodePointLength(const Str: PChar; MaxLookAead: PtrInt): Ptrint;
  320. var
  321. c: byte;
  322. begin
  323. // Only UTF-8 encoding is supported
  324. c:=byte(Str^);
  325. if c = 0 then
  326. Result:=0
  327. else begin
  328. Result:=1;
  329. if c < $80 then
  330. exit; // 1-byte ASCII char
  331. while c and $C0 = $C0 do begin
  332. Inc(Result);
  333. c:=c shl 1;
  334. end;
  335. if Result > 6 then
  336. Result:=1 // Invalid code point
  337. else
  338. if Result > MaxLookAead then
  339. Result:=-1; // Incomplete code point
  340. end;
  341. end;
  342. function GetStandardCodePage(const stdcp: TStandardCodePageEnum): TSystemCodePage;
  343. begin
  344. Result := CP_UTF8; // Android always uses UTF-8
  345. end;
  346. procedure SetStdIOCodePage(var T: Text); inline;
  347. begin
  348. case TextRec(T).Mode of
  349. fmInput:TextRec(T).CodePage:=DefaultSystemCodePage;
  350. fmOutput:TextRec(T).CodePage:=DefaultSystemCodePage;
  351. end;
  352. end;
  353. procedure SetStdIOCodePages; inline;
  354. begin
  355. SetStdIOCodePage(Input);
  356. SetStdIOCodePage(Output);
  357. SetStdIOCodePage(ErrOutput);
  358. SetStdIOCodePage(StdOut);
  359. SetStdIOCodePage(StdErr);
  360. end;
  361. procedure Ansi2WideMove(source:pchar; cp:TSystemCodePage; var dest:widestring; len:SizeInt);
  362. var
  363. us: UnicodeString;
  364. begin
  365. Ansi2UnicodeMove(source,cp,us,len);
  366. dest:=us;
  367. end;
  368. function UpperWideString(const s : WideString) : WideString;
  369. begin
  370. Result:=UpperUnicodeString(s);
  371. end;
  372. function LowerWideString(const s : WideString) : WideString;
  373. begin
  374. Result:=LowerUnicodeString(s);
  375. end;
  376. function CompareWideString(const s1, s2 : WideString; Options : TCompareOptions) : PtrInt;
  377. begin
  378. Result:=CompareUnicodeString(s1, s2, Options);
  379. end;
  380. Procedure SetCWideStringManager;
  381. Var
  382. CWideStringManager : TUnicodeStringManager;
  383. begin
  384. CWideStringManager:=widestringmanager;
  385. With CWideStringManager do
  386. begin
  387. Wide2AnsiMoveProc:=@Unicode2AnsiMove;
  388. Ansi2WideMoveProc:=@Ansi2WideMove;
  389. UpperWideStringProc:=@UpperWideString;
  390. LowerWideStringProc:=@LowerWideString;
  391. CompareWideStringProc:=@CompareWideString;
  392. UpperAnsiStringProc:=@UpperAnsiString;
  393. LowerAnsiStringProc:=@LowerAnsiString;
  394. CompareStrAnsiStringProc:=@CompareStrAnsiString;
  395. CompareTextAnsiStringProc:=@AnsiCompareText;
  396. StrCompAnsiStringProc:=@StrCompAnsi;
  397. StrICompAnsiStringProc:=@AnsiStrIComp;
  398. StrLCompAnsiStringProc:=@AnsiStrLComp;
  399. StrLICompAnsiStringProc:=@AnsiStrLIComp;
  400. StrLowerAnsiStringProc:=@AnsiStrLower;
  401. StrUpperAnsiStringProc:=@AnsiStrUpper;
  402. Unicode2AnsiMoveProc:=@Unicode2AnsiMove;
  403. Ansi2UnicodeMoveProc:=@Ansi2UnicodeMove;
  404. UpperUnicodeStringProc:=@UpperUnicodeString;
  405. LowerUnicodeStringProc:=@LowerUnicodeString;
  406. CompareUnicodeStringProc:=@CompareUnicodeString;
  407. GetStandardCodePageProc:=@GetStandardCodePage;
  408. CodePointLengthProc:=@CodePointLength;
  409. end;
  410. SetUnicodeStringManager(CWideStringManager);
  411. end;
  412. procedure UnloadICU;
  413. begin
  414. if DefColl <> nil then
  415. ucol_close(DefColl);
  416. if DefConv <> nil then
  417. ucnv_close(DefConv);
  418. if LastConv <> nil then
  419. ucnv_close(LastConv);
  420. if hlibICU <> 0 then begin
  421. UnloadLibrary(hlibICU);
  422. hlibICU:=0;
  423. end;
  424. if hlibICUi18n <> 0 then begin
  425. UnloadLibrary(hlibICUi18n);
  426. hlibICUi18n:=0;
  427. end;
  428. end;
  429. function GetIcuProc(const Name: AnsiString; out ProcPtr; libId: longint = 0): boolean; [public, alias: 'CWSTRING_GET_ICU_PROC'];
  430. var
  431. p: pointer;
  432. hLib: TLibHandle;
  433. begin
  434. Result:=False;
  435. if libId = 0 then
  436. hLib:=hlibICU
  437. else
  438. hLib:=hlibICUi18n;
  439. if hLib = 0 then
  440. exit;
  441. p:=GetProcedureAddress(hlib, Name + LibVer);
  442. if p = nil then
  443. exit;
  444. pointer(ProcPtr):=p;
  445. Result:=True;
  446. end;
  447. function LoadICU: boolean;
  448. const
  449. ICUver: array [1..12] of ansistring = ('3_8', '4_2', '44', '46', '48', '50', '51', '53', '55', '56', '58', '60');
  450. TestProcName = 'ucnv_open';
  451. var
  452. i: longint;
  453. s: ansistring;
  454. dir: PAnsiChar;
  455. begin
  456. Result:=False;
  457. {$ifdef android}
  458. hlibICU:=LoadLibrary('libicuuc.so');
  459. hlibICUi18n:=LoadLibrary('libicui18n.so');
  460. {$else}
  461. hlibICU:=LoadLibrary('icuuc40.dll');
  462. hlibICUi18n:=LoadLibrary('icuin40.dll');
  463. LibVer:='_4_0';
  464. {$endif android}
  465. if (hlibICU = 0) or (hlibICUi18n = 0) then begin
  466. UnloadICU;
  467. exit;
  468. end;
  469. // Finding ICU version using known versions table
  470. for i:=High(ICUver) downto Low(ICUver) do begin
  471. s:='_' + ICUver[i];
  472. if GetProcedureAddress(hlibICU, TestProcName + s) <> nil then begin
  473. LibVer:=s;
  474. break;
  475. end;
  476. end;
  477. if LibVer = '' then begin
  478. // Finding unknown ICU version
  479. Val(ICUver[High(ICUver)], i);
  480. repeat
  481. Inc(i);
  482. Str(i, s);
  483. s:='_' + s;
  484. if GetProcedureAddress(hlibICU, TestProcName + s) <> nil then begin
  485. LibVer:=s;
  486. break;
  487. end;
  488. until i >= 100;
  489. end;
  490. if LibVer = '' then begin
  491. // Trying versionless name
  492. if GetProcedureAddress(hlibICU, TestProcName) = nil then begin
  493. // Unable to get ICU version
  494. SysLogWrite(ANDROID_LOG_ERROR, 'cwstring: Unable to get ICU version.');
  495. UnloadICU;
  496. exit;
  497. end;
  498. end;
  499. if not GetIcuProc('ucnv_open', ucnv_open) then exit;
  500. if not GetIcuProc('ucnv_close', ucnv_close) then exit;
  501. if not GetIcuProc('ucnv_setSubstChars', ucnv_setSubstChars) then exit;
  502. if not GetIcuProc('ucnv_setFallback', ucnv_setFallback) then exit;
  503. if not GetIcuProc('ucnv_fromUChars', ucnv_fromUChars) then exit;
  504. if not GetIcuProc('ucnv_toUChars', ucnv_toUChars) then exit;
  505. if not GetIcuProc('u_strToUpper', u_strToUpper) then exit;
  506. if not GetIcuProc('u_strToLower', u_strToLower) then exit;
  507. if not GetIcuProc('u_strCompare', u_strCompare) then exit;
  508. if not GetIcuProc('u_strCaseCompare', u_strCaseCompare) then exit;
  509. if not GetIcuProc('u_getDataDirectory', u_getDataDirectory) then exit;
  510. if not GetIcuProc('u_setDataDirectory', u_setDataDirectory) then exit;
  511. if not GetIcuProc('ucol_open', ucol_open, 1) then exit;
  512. if not GetIcuProc('ucol_close', ucol_close, 1) then exit;
  513. if not GetIcuProc('ucol_strcoll', ucol_strcoll, 1) then exit;
  514. if not GetIcuProc('ucol_setStrength', ucol_setStrength, 1) then exit;
  515. // Checking if ICU data dir is set
  516. dir:=u_getDataDirectory();
  517. if (dir = nil) or (dir^ = #0) then
  518. u_setDataDirectory('/system/usr/icu');
  519. Result:=True;
  520. end;
  521. var
  522. oldm: TUnicodeStringManager;
  523. initialization
  524. GetUnicodeStringManager(oldm);
  525. DefaultSystemCodePage:=GetStandardCodePage(scpAnsi);
  526. DefaultUnicodeCodePage:=CP_UTF16;
  527. if LoadICU then begin
  528. SetCWideStringManager;
  529. {$ifdef android}
  530. SetStdIOCodePages;
  531. {$endif android}
  532. end;
  533. finalization
  534. SetUnicodeStringManager(oldm);
  535. UnloadICU;
  536. end.