diff --git a/Lgi_vs2019.vcxproj b/Lgi_vs2019.vcxproj --- a/Lgi_vs2019.vcxproj +++ b/Lgi_vs2019.vcxproj @@ -1,471 +1,632 @@  Debug x64 ReleaseNoOptimize x64 Release x64 Lgi {95DF9CA4-6D37-4A85-A648-80C2712E0DA1} 10.0 DynamicLibrary v142 false Unicode DynamicLibrary v142 false Unicode DynamicLibrary v142 false Unicode <_ProjectFileVersion>12.0.30501.0 .\Lib\ $(Platform)$(Configuration)19\ false $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include Lgi19x64 .\Lib\ $(Platform)$(Configuration)19\ true $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include Lgi19x64d .\Lib\ $(Platform)$(Configuration)19\ false $(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include Lgi19x64nop NDEBUG;%(PreprocessorDefinitions) true true X64 .\Release/Lgi.tlb MinSpace OnlyExplicitInline include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;..\..\scribe\libs\build-x64\libiconv-1.17\include;%(AdditionalIncludeDirectories) WIN64;NDEBUG;WINDOWS;LGI_RES;LGI_LIBRARY;%(PreprocessorDefinitions) true MultiThreadedDLL true true $(IntDir)$(TargetName).pch $(IntDir) $(IntDir) $(IntDir) Level2 true ProgramDatabase Default NDEBUG;%(PreprocessorDefinitions) 0x0c09 ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies) $(OutDir)$(TargetFileName) true true $(OutDir)$(TargetName).pdb Windows true false $(OutDir)$(TargetName).lib MachineX64 _DEBUG;%(PreprocessorDefinitions) true true X64 .\Debug/Lgi.tlb Disabled include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;..\..\scribe\libs\build-x64\libiconv-1.17\include;%(AdditionalIncludeDirectories) WIN64;LGI_LIBRARY;_DEBUG;WINDOWS;LGI_RES;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL true $(IntDir)$(TargetName).pch $(IntDir) $(IntDir) $(IntDir) Level3 true ProgramDatabase Default + false _DEBUG;%(PreprocessorDefinitions) 0x0c09 ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies) NotSet $(OutDir)$(TargetFileName) true true $(OutDir)$(TargetName).pdb Windows false $(OutDir)$(TargetName).lib MachineX64 NDEBUG;%(PreprocessorDefinitions) true true X64 .\Release/Lgi.tlb MinSpace OnlyExplicitInline include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;..\..\scribe\libs\build-x64\libiconv-1.17\include;%(AdditionalIncludeDirectories) WIN64;NDEBUG;WINDOWS;LGI_RES;LGI_LIBRARY;%(PreprocessorDefinitions) true MultiThreadedDLL true true $(IntDir)$(TargetName).pch $(IntDir) $(IntDir) $(IntDir) Level2 true ProgramDatabase Default + false NDEBUG;%(PreprocessorDefinitions) 0x0c09 ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies) $(OutDir)$(TargetFileName) true true $(OutDir)$(TargetName).pdb Windows true false $(OutDir)$(TargetName).lib MachineX64 false true true false false false true true true true true true false false false true true true true true true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Lgi_vs2019.vcxproj.filters b/Lgi_vs2019.vcxproj.filters --- a/Lgi_vs2019.vcxproj.filters +++ b/Lgi_vs2019.vcxproj.filters @@ -1,782 +1,1255 @@  {afe8cb77-9ad1-4536-bbdd-3c127e7ed08c} cpp;c;cxx;rc;def;r;odl;idl;hpj;bat {66a64573-871b-4499-ae26-c19e9e2a514a} {3fc23ef0-f144-4f1f-a9b4-18d3392bb63d} {c6cd6d73-d33c-4413-ade1-9dad78e2dc9c} {c8684fc7-2e3c-4f15-8284-9d44b044f6c6} {87b1c801-b9ce-4f6c-97ab-a8f89aee9594} {c06c25f2-2c07-4900-a517-4a6a324069e9} {2c01a737-36cf-4197-bfa1-20395060263f} {1e4cd802-8b94-4328-930e-37bfbfbedff5} {01075698-dde2-4ed0-808f-7dd54414b597} {4a6845a8-e6ec-47d5-8f6c-aa0cfdbc68df} {f567a76b-edd5-434d-b0d9-d51481f34845} {3dee0237-b01c-44e8-a730-08dc661df82f} {bbaaace6-0ac7-4e76-90ae-9e5d5a0cb899} {71e7b970-a070-40a7-a99f-88c215e14e44} {6e115ea1-09fb-492b-82b6-428afe17fed9} {719ef36f-419f-46f9-aef9-2f8158e4d106} {fb221556-3700-4dd8-ba9a-10b5d66bfe54} {8e9f0321-56ae-4485-a338-e87d714c7f50} {c6050f41-574b-4a92-9ce5-860be2719ecf} {a07cd969-801e-4ec9-9394-e34912a3271d} {e067fae0-ef98-4e7a-8434-6f36590ba0e6} {0a3e2151-b13a-407a-8cd9-ccb20f15cacf} {c72248a4-c2f0-43b9-950d-89d09bfddbb3} {dad60202-c763-4f32-9dfb-fe7def4637ee} {532dfa4a-58d3-4133-9ed6-a9fbf7f1556e} {5409aca4-2a55-4b2f-a719-d3db4e3cd7e4} {e3a3aadd-47ef-4723-9bcc-7db1f75a18b0} {c4327acf-78c3-4ef1-b8bc-3aac9ea52b41} {b3c906b8-595e-4641-8eec-8ad03ab13f6f} {baf0a65b-4a9c-4b11-850d-a957c19a22bf} {6e349f5b-36a8-4821-9574-4040a3784204} {ddfdebae-8bcf-4224-8938-2716aba03822} {a126c55a-edee-489f-a852-25cbd1b433b2} {ab5fd4a0-3862-42fd-b4ff-d5d8b0443f53} {048d5e0a-220f-4911-999d-96965eb53691} {258aef64-8cd0-4838-8131-147196656a99} {814a5d81-3fd5-461b-a4a3-cda593ea404b} {5fe450b8-5fa9-440e-9fb0-03860b3548d0} h;hpp;hxx;hm;inl Source Files\Core\Resources Source Files\Core\Resources Source Files\Core\Skin Source Files\General\Hash Source Files\General\Hash Source Files\Graphics Source Files\Graphics\Applicators Source Files\Graphics\Applicators Source Files\Graphics\Applicators Source Files\Graphics\Applicators Source Files\Graphics\Applicators Source Files\Graphics\Applicators Source Files\Graphics\Applicators Source Files\Graphics\Applicators Source Files\Dialogs Source Files\Interface Source Files\Interface Source Files\Interface Source Files\Interface Source Files\Dialogs Source Files\Dialogs Source Files\Text Source Files\Widgets\Native Windows Source Files\Widgets\Native Windows Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Graphics\Font Source Files\General\Clipboard Source Files\Graphics Source Files\Graphics\Colour Reduction Source Files\Widgets\Xp Source Files\Widgets\Native Windows Source Files\Core\Memory Subsystem Source Files\Text Source Files\Text Source Files\Core\DateTime Source Files\Widgets\Native Windows Source Files\Graphics\Font Source Files\Dialogs Source Files\Core\Drag and Drop Source Files\Widgets\Xp Source Files\Widgets\Native Windows Source Files\General Source Files\Core\File Source Files\Core\File Source Files\Graphics\Filters Source Files\Graphics\Font Source Files\Graphics\Font Source Files\Graphics\Font Source Files\Graphics\Font Source Files\Graphics Source Files\Graphics\Gdi Leak Source Files\General Source Files\Graphics Source Files\Dialogs Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Core\Libraries Source Files\General Source Files\General Source Files\General Source Files\Widgets\Xp Source Files\Dialogs Source Files\Text Source Files\Core\Memory Subsystem Source Files\Graphics\Surfaces Source Files\Core\Memory Subsystem Source Files\Core\Menus Source Files\Core\Menus Source Files\General Source Files\General\Mru Source Files\Core\Mutex Source Files\Network Source Files\Network Source Files\General Source Files\Core\File Source Files\Widgets\Xp Source Files\General Source Files\Graphics Source Files\Widgets\Xp Source Files\Graphics\Surfaces Source Files\Graphics Source Files\Widgets\Native Windows Source Files\Dialogs Source Files\Widgets\Xp Source Files\Widgets\Native Windows Source Files\General Source Files\Graphics Source Files\Graphics\Surfaces Source Files\Widgets\Xp Source Files\Widgets\Native Windows Source Files\Widgets\Xp Source Files\Widgets\Native Windows Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Core\Memory Subsystem Source Files\Text Source Files\Widgets\Xp Source Files\Core\Process Source Files\Graphics\Surfaces Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Core\Threads Source Files\Core\Threads Source Files\Core\Threads Source Files\Text Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Widgets\Xp Source Files\Graphics\Font Source Files\Text Source Files\Text Source Files\Network Source Files\Text Source Files\Core\Variant Source Files\Interface Source Files\Interface Source Files\Interface Source Files\Interface Source Files\Text Source Files\Widgets\Xp Source Files\Core\Resources Source Files\Core\Resources Source Files\General\Hash Source Files\General\Hash Source Files\Graphics Source Files\Graphics\Gdi Leak - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Source Files\Graphics\Font Source Files\Interface - - Header Files - - - Header Files - - - Header Files - Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files Header Files Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/include/lgi/common/Containers.h b/include/lgi/common/Containers.h --- a/include/lgi/common/Containers.h +++ b/include/lgi/common/Containers.h @@ -1,1148 +1,1159 @@ /** \file \author Matthew Allen \date 27/11/1996 \brief Container class header.\n Copyright (C) 1996-2003, Matthew Allen */ #ifndef _CONTAIN_H_ #define _CONTAIN_H_ #include #include "lgi/common/LgiInc.h" #include "LgiOsDefs.h" #include "lgi/common/Stream.h" #include "lgi/common/Profile.h" #include /// Template for using DLinkList with a type safe API. #define ITEM_PTRS 64 #define LGI_LIST_VALIDATION 0 LgiFunc bool UnitTest_ListClass(); #ifdef _DEBUG #define VALIDATE() Validate() #else #define VALIDATE() #endif template class List { struct LstBlk { LstBlk *Next, *Prev; uint8_t Count; T *Ptr[ITEM_PTRS]; LstBlk() { Next = Prev = NULL; Count = 0; ZeroObj(Ptr); } bool Full() { return Count >= ITEM_PTRS; } int Remaining() { return ITEM_PTRS - Count; } }; public: class Iter { public: const List *Lst; LstBlk *i; int Cur, Ver; OsThreadId Thread; bool CheckThread() const { #if 1 return true; #else auto Cur = GetCurrentThreadId(); bool Ok = Thread == Cur; LAssert(Ok); return Ok; #endif } #ifdef _DEBUG #define CHECK_THREAD CheckThread(); #else #define CHECK_THREAD #endif Iter(const List *lst) { Lst = lst; i = 0; Cur = 0; Ver = lst->Ver; Thread = GetCurrentThreadId(); } Iter(const List *lst, LstBlk *item, int c) { Lst = lst; i = item; Cur = c; Ver = lst->Ver; Thread = GetCurrentThreadId(); } bool operator ==(const Iter &it) const { CHECK_THREAD int x = (int)In() + (int)it.In(); if (x == 2) return (i == it.i) && (Cur == it.Cur); return x == 0; } bool operator !=(const Iter &it) const { CHECK_THREAD return !(*this == it); } bool operator <(const Iter &it) const { CHECK_THREAD return Cur < it.Cur; } bool operator <=(const Iter &it) const { CHECK_THREAD return Cur <= it.Cur; } bool operator >(const Iter &it) const { CHECK_THREAD return Cur > it.Cur; } bool operator >=(const Iter &it) const { CHECK_THREAD return Cur >= it.Cur; } bool In() const { CHECK_THREAD if (Ver != Lst->Ver) { if (!Lst->ValidBlock(i)) return false; } return i && Cur >= 0 && Cur < i->Count; } operator T*() const { CHECK_THREAD return In() ? i->Ptr[Cur] : NULL; } T *operator *() const { CHECK_THREAD return In() ? i->Ptr[Cur] : NULL; } Iter &operator =(LstBlk *item) { CHECK_THREAD i = item; if (!i) Cur = 0; return *this; } Iter &operator =(int c) { CHECK_THREAD Cur = c; return *this; } Iter &operator =(Iter *iter) { CHECK_THREAD Lst = iter->Lst; i = iter->i; Cur = iter->Cur; return *this; } int GetIndex(int Base) { CHECK_THREAD if (i) return Base + Cur; return -1; } bool Next() { CHECK_THREAD if (i) { if (!In()) return false; Cur++; if (Cur >= i->Count) { i = i->Next; if (i) { Cur = 0; return i->Count > 0; } } else return true; } return false; } bool Prev() { CHECK_THREAD if (i) { if (!In()) return false; Cur--; if (Cur < 0) { i = i->Prev; if (i && i->Count > 0) { Cur = i->Count - 1; return true; } } else return true; } return false; } bool Delete() { CHECK_THREAD if (i) { LAssert(Lst); i->Delete(Cur, i); return true; } return false; } Iter &operator ++() { Next(); return *this; } Iter &operator --() { Prev(); return *this; } Iter &operator ++(int) { Next(); return *this; } Iter &operator --(int) { Prev(); return *this; } }; typedef Iter I; // typedef int (*CompareFn)(T *a, T *b, NativeInt data); protected: size_t Items; int Ver; LstBlk *FirstObj, *LastObj; bool ValidBlock(LstBlk *b) const { for (LstBlk *i = FirstObj; i; i = i->Next) if (i == b) return true; return false; } LstBlk *NewBlock(LstBlk *Where) { LstBlk *i = new LstBlk; LAssert(i != NULL); if (!i) return NULL; if (Where) { i->Prev = Where; if (i->Prev->Next) { // Insert i->Next = Where->Next; i->Prev->Next = i->Next->Prev = i; } else { // Append i->Prev->Next = i; LAssert(LastObj == Where); LastObj = i; } } else { // First object LAssert(FirstObj == 0); LAssert(LastObj == 0); FirstObj = LastObj = i; } return i; } bool DeleteBlock(LstBlk *i) { if (!i) { LAssert(!"No ptr."); return false; } if (i->Prev != 0 && i->Next != 0) { LAssert(FirstObj != i); LAssert(LastObj != i); } if (i->Prev) { i->Prev->Next = i->Next; } else { LAssert(FirstObj == i); FirstObj = i->Next; } if (i->Next) { i->Next->Prev = i->Prev; } else { LAssert(LastObj == i); LastObj = i->Prev; } delete i; Ver++; // This forces the iterators to recheck their block ptr return true; } bool Insert(LstBlk *i, T *p, ssize_t Index = -1) { if (!i) return false; if (i->Full()) { if (!i->Next) { // Append a new LstBlk if (!NewBlock(i)) return false; } if (Index < 0) return Insert(i->Next, p, Index); // Push last pointer into Next if (i->Next->Full()) NewBlock(i); // Create an empty Next if (!Insert(i->Next, i->Ptr[ITEM_PTRS-1], 0)) return false; i->Count--; Items--; // We moved the item... not inserted it. // Fall through to the local "non-full" insert... } LAssert(!i->Full()); if (Index < 0) Index = i->Count; else if (Index < i->Count) memmove(i->Ptr+Index+1, i->Ptr+Index, (i->Count-Index) * sizeof(p)); i->Ptr[Index] = p; i->Count++; Items++; LAssert(i->Count <= ITEM_PTRS); return true; } Iter GetIndex(size_t Index, size_t *Base = NULL) const { size_t n = 0; for (LstBlk *i = FirstObj; i; i = i->Next) { if (Index >= n && Index < n + i->Count) { if (Base) *Base = n; return Iter(this, i, (int) (Index - n)); } n += i->Count; } if (Base) *Base = 0; return Iter(this); } Iter GetPtr(T *Ptr, size_t *Base = NULL) { size_t n = 0; for (LstBlk *i = FirstObj; i; i = i->Next) { for (int k=0; kCount; k++) { if (i->Ptr[k] == Ptr) { if (Base) *Base = n; return Iter(this, i, k); } } n += i->Count; } if (Base) *Base = 0; return Iter(this); } public: List() { FirstObj = LastObj = NULL; Items = 0; Ver = 0; } ~List() { VALIDATE(); Empty(); } size_t Length() const { return Items; } bool Length(size_t Len) { if (Len == 0) return Empty(); else if (Len == Items) return true; VALIDATE(); bool Status = false; if (Len < Items) { // Decrease list size... size_t Base = 0; Iter i = GetIndex(Len, &Base); if (i.i) { size_t Offset = Len - Base; if (!(Offset <= i.i->Count)) { LAssert(!"Offset error"); } i.i->Count = (uint8_t) (Len - Base); LAssert(i.i->Count >= 0 && i.i->Count < ITEM_PTRS); while (i.i->Next) { DeleteBlock(i.i->Next); } Items = Len; } else LAssert(!"Iterator invalid."); } else { // Increase list size... LAssert(!"Impl me."); } VALIDATE(); return Status; } bool Empty() { VALIDATE(); LstBlk *n; for (LstBlk *i = FirstObj; i; i = n) { n = i->Next; delete i; } FirstObj = LastObj = NULL; Items = 0; Ver++; VALIDATE(); return true; } bool DeleteAt(size_t i) { VALIDATE(); Iter p = GetIndex(i); if (!p.In()) return false; bool Status = Delete(p); VALIDATE(); return Status; } bool Delete(Iter &Pos) { if (!Pos.In()) return false; int &Index = Pos.Cur; LstBlk *&i = Pos.i; if (Index < i->Count-1) memmove(i->Ptr+Index, i->Ptr+Index+1, (i->Count-Index-1) * sizeof(T*)); Items--; if (--i->Count == 0) { // This Item is now empty, remove and reset current // into the next Item LstBlk *n = i->Next; bool Status = DeleteBlock(i); Pos.Cur = 0; Pos.i = n; return Status; } else if (Index >= i->Count) { // Carry current item over to next Item Pos.i = Pos.i->Next; Pos.Cur = 0; } return true; } bool Delete(T *Ptr) { VALIDATE(); Iter It = GetPtr(Ptr); if (!It.In()) return false; bool Status = Delete(It); VALIDATE(); return Status; } bool Insert(T *p, ssize_t Index = -1) { VALIDATE(); if (!LastObj) { LstBlk *b = NewBlock(NULL); if (!b) return false; b->Ptr[b->Count++] = p; Items++; VALIDATE(); return true; } bool Status; size_t Base; Iter Pos(this); if (Index < 0) Status = Insert(LastObj, p, Index); else { Pos = GetIndex(Index, &Base); if (Pos.i) Status = Insert(Pos.i, p, (int) (Index - Base)); else Status = Insert(LastObj, p, -1); } VALIDATE(); LAssert(Status); return Status; } bool Add(T *p) { return Insert(p); } T *operator [](size_t Index) const { VALIDATE(); auto it = GetIndex(Index); VALIDATE(); return it; } ssize_t IndexOf(T *p) { VALIDATE(); size_t Base = -1; auto It = GetPtr(p, &Base); LAssert(Base != -1); ssize_t Idx = It.In() ? Base + It.Cur : -1; VALIDATE(); return Idx; } bool HasItem(T *p) { VALIDATE(); Iter Pos = GetPtr(p); bool Status = Pos.In(); VALIDATE(); return Status; } T *ItemAt(ssize_t i) { VALIDATE(); Iter It = GetIndex(i); VALIDATE(); return It; } /// Sorts the list template void Sort ( /// The callback function used to compare 2 pointers int (*Compare)(T *a, T *b, User data), /// User data that is passed into the callback User Data = 0 ) { if (Items < 1) return; VALIDATE(); // LProfile prof("ListSort"); // Save the List to an Array LArray a; a.Length(Length()); T **p = a.AddressOf(); for (LstBlk *i = FirstObj; i; i = i->Next) for (int n=0; nCount; n++) *p++ = i->Ptr[n]; // prof.Add("Compare"); struct UserData { int (*Compare)(T *a, T *b, User data); User Data; } ud = {Compare, Data}; #if !defined(WINDOWS) && !defined(HAIKU) && !defined(LINUX) #define USER_DATA_FIRST 1 #else #define USER_DATA_FIRST 0 #endif #if defined(WINDOWS) /* _ACRTIMP void __cdecl qsort_s(void* _Base, rsize_t _NumOfElements, rsize_t _SizeOfElements, int (__cdecl* _PtFuncCompare)(void*, void const*, void const*), void* _Context); */ qsort_s #else qsort_r #endif ( a.AddressOf(), a.Length(), sizeof(T*), #if USER_DATA_FIRST &ud, #endif #if defined(HAIKU) || defined(LINUX) // typedef int (*_compare_function_qsort_r)(const void*, const void*, void*); // extern void qsort_r(void* base, size_t numElements, size_t sizeOfElement, _compare_function_qsort_r, void* cookie); [](const void *a, const void *b, void *ud) -> int #else [](void *ud, const void *a, const void *b) -> int #endif { auto *user = (UserData*)ud; return user->Compare(*(T**)a, *(T**)b, user->Data); } #if !USER_DATA_FIRST , &ud #endif ); // prof.Add("Copy"); // Copy back to the List p = a.AddressOf(); for (LstBlk *i = FirstObj; i; i = i->Next) for (int n=0; nCount; n++) i->Ptr[n] = *p++; VALIDATE(); } /// Delete all pointers in the list as dynamically allocated objects void DeleteObjects() { VALIDATE(); LstBlk *n; for (LstBlk *i = FirstObj; i; i = n) { n = i->Next; for (int n=0; nCount; n++) { if (i->Ptr[n]) { #ifdef _DEBUG size_t Objs = Items; #endif delete i->Ptr[n]; #ifdef _DEBUG if (Objs != Items) LAssert(!"Do you have self deleting objects?"); #endif i->Ptr[n] = NULL; } } delete i; } FirstObj = LastObj = NULL; Items = 0; Ver++; VALIDATE(); } /// Delete all pointers in the list as dynamically allocated arrays void DeleteArrays() { VALIDATE(); LstBlk *n; for (LstBlk *i = FirstObj; i; i = n) { n = i->Next; for (int n=0; nCount; n++) { delete [] i->Ptr[n]; i->Ptr[n] = NULL; } delete i; } FirstObj = LastObj = NULL; Items = 0; Ver++; VALIDATE(); } void Swap(List &other) { LSwap(FirstObj, other.FirstObj); LSwap(LastObj, other.LastObj); LSwap(Items, other.Items); LSwap(Ver, other.Ver); } /// Assign the contents of another list to this one #if 0 List &operator=(const List &lst) { Empty(); for (auto i : lst) Add(i); return *this; } #else List &operator =(const List &lst) { VALIDATE(); // Make sure we have enough blocks allocated size_t i = 0; // Set the existing blocks to empty... for (LstBlk *out = FirstObj; out; out = out->Next) { out->Count = 0; i += ITEM_PTRS; } // If we don't have enough, add more... while (i < lst.Length()) { LstBlk *out = NewBlock(LastObj); if (out) i += ITEM_PTRS; else { LAssert(!"Can't allocate enough blocks?"); return *this; } } // If we have too many, free some... while (LastObj && i > lst.Length() + ITEM_PTRS) { DeleteBlock(LastObj); i -= ITEM_PTRS; } // Now copy over the block's contents. LstBlk *out = FirstObj; Items = 0; for (LstBlk *in = lst.FirstObj; in; in = in->Next) { for (int pos = 0; pos < in->Count; ) { if (!out->Remaining()) { out = out->Next; if (!out) { LAssert(!"We should have pre-allocated everything..."); return *this; } } int Cp = MIN(out->Remaining(), in->Count - pos); LAssert(Cp > 0); memcpy(out->Ptr + out->Count, in->Ptr + pos, Cp * sizeof(T*)); out->Count += Cp; pos += Cp; Items += Cp; } } VALIDATE(); return *this; } #endif Iter begin(ssize_t At = 0) { return GetIndex(At); } Iter rbegin(ssize_t At = 0) { return GetIndex(Length()-1); } Iter end() { return Iter(this, NULL, -1); } bool Validate() const { if (FirstObj == NULL && LastObj == NULL && Items == 0) return true; #if LGI_LIST_VALIDATION size_t n = 0; LstBlk *Prev = NULL; for (LstBlk *i = FirstObj; i; i = i->Next) { for (int k=0; kCount; k++) { if (!i->Ptr[k]) { LAssert(!"NULL pointer in LstBlk."); return false; } else { n++; } } if (i == FirstObj) { if (i->Prev) { LAssert(!"First object's 'Prev' should be NULL."); return false; } } else if (i == LastObj) { if (i->Next) { LAssert(!"Last object's 'Next' should be NULL."); return false; } } else { if (i->Prev != Prev) { LAssert(!"Middle LstBlk 'Prev' incorrect."); return false; } } Prev = i; } if (Items != n) { LAssert(!"Item count cache incorrect."); return false; } #endif return true; } }; /// \brief Data storage class. /// /// Allows data to be buffered in separate memory /// blocks and then written to a continuous block for processing. Works /// as a first in first out que. You can (and should) provide a suitable /// PreAlloc size to the constructor. This can reduce the number of blocks /// of memory being used (and their associated alloc/free time and /// tracking overhead) in high volume situations. class LgiClass LMemQueue : public LStream { protected: /// Data block. These can contain a mix of 3 types of data: /// 1) Bytes that have been read (always at the start of the block) /// 2) Bytes that have been written (always in the middle) /// 3) Unwritten buffer space (always at the end) /// /// Initially a block starts out as completely type 3 bytes, just garbage /// data waiting to be written to. Then something writes into the pipe and bytes /// are stored into this free space, and the 'Used' variable is set to show /// how much of the available buffer is used. At this point we have type 2 and /// type 3 bytes in the block. The buffer can fill up completely in which /// case Used == Size and there are no type 3 bytes left. Also at some point /// something can start reading bytes out of the block which causes the 'Next' /// value to be increased, at which point the block starts with 'Next' bytes /// of type 1. Crystal? struct Block { /// Number of bytes in this block that have been read /// Type 1 or 'read' bytes are in [0,Next-1]. - int Next; + int Next = 0; + /// Number of bytes that are used in this block include read bytes. /// Type 2 or 'used' bytes are in [Next,Used-1]. - int Used; + int Used = 0; + /// Total size of the memory block /// Type 3 or 'unused' bytes are in [Used,Size-1]. - int Size; - uint8_t *Ptr() { return (uint8_t*) (this + 1); } + int Size = 0; - Block() + uint8_t *Ptr() + { + return (uint8_t*) (this + 1); + } + + uint8_t *Start() { - Next = 0; - Size = 0; - Used = 0; + return Ptr() + Next; } + + ssize_t Length() + { + return Used - Next; + } + + void Free(); }; ssize_t PreAlloc; List Mem; + bool _debug = false; public: /// Constructor LMemQueue ( /// Sets the block size, which means allocating ahead and then joining /// together smaller inserts into 1 continuous block. size_t PreAlloc = 0 ); /// Destructor virtual ~LMemQueue(); LMemQueue &operator =(LMemQueue &p); /// Empties the container freeing any memory used. virtual void Empty(); /// Returns a dynamically allocated block that contains all the data in the container /// in a continuous block. virtual void *New ( /// If this is > 0 then the function add's the specified number of bytes containing /// the value 0 on the end of the block. This is useful for NULL terminating strings /// or adding buffer space on the end of the block returned. - int AddBytes = 0 + ssize_t AddBytes = 0 ); /// Reads data from the start of the container without removing it from /// the que. Returns the bytes copied. virtual int64 Peek ( /// Buffer for output uchar *Ptr, /// Bytes to look at - int Size + ssize_t Size ); /// Gets the total bytes in the container int64 GetSize() override; /// Reads bytes off the start of the container ssize_t Read(void *Buffer, ssize_t Size, int Flags = 0) override; /// Writes bytes to the end of the container ssize_t Write(const void *Buffer, ssize_t Size, int Flags = 0) override; bool Write(const LString &s) { return Write(s.Get(), s.Length()) == s.Length(); } /// Search for a substring and return it's index, or -1 if not found. ssize_t Find(LString str, bool caseSensitive = true); /// Iterate over the data in the container /// Callback should return true to keep iterating... void Iterate(std::function callback, bool reverse = false); /// For processes that read from one source and then write into this container, it is better /// to not have to copy the data into some local buffer and then again into the mem queue. /// So this allows allocating internal blocks and returning them directly to the writer. After /// the source has written into the internal block the client calls Buffer::Commit to tell the /// LMemQueue how much data has been written. class LgiClass Buffer { friend class LMemQueue; LMemQueue *mq = NULL; Block *blk = NULL; public: uint8_t *ptr = NULL; size_t len = 0; Buffer(LMemQueue *memq) : mq(memq) {} // Call this after writing data into the 'ptr' buffer. bool Commit(size_t bytes); }; /// Find a buffer and return it. Call Buffer::Commit after writting data to the start of the block. /// On error Buffer.ptr is NULL. Buffer GetBuffer(); }; /// A version of GBytePipe for strings. Adds some special handling for strings. class LgiClass LStringPipe : public LMemQueue { ssize_t LineChars(); ssize_t SaveToBuffer(char *Str, ssize_t BufSize, ssize_t Chars); public: /// Constructs the object LStringPipe ( /// Number of bytes to allocate per block. int PreAlloc = -1 ) : LMemQueue(PreAlloc) {} ~LStringPipe() {} virtual ssize_t Pop(char *Str, ssize_t Chars); virtual ssize_t Pop(LArray &Buf); virtual LString Pop(); virtual ssize_t Push(const char *Str, ssize_t Chars = -1); virtual ssize_t Push(const char16 *Str, ssize_t Chars = -1); char *NewStr() { return (char*)New(sizeof(char)); } LString NewGStr(); char16 *NewStrW() { return (char16*)New(sizeof(char16)); } LStringPipe &operator +=(const LString &s) { Write(s.Get(), s.Length()); return *this; } #ifdef _DEBUG static bool UnitTest(); #endif }; -#define GMEMFILE_BLOCKS 8 -class LgiClass GMemFile : public LStream +#define LMEMFILE_BLOCKS 8 +class LgiClass LMemFile : public LStream { struct Block { size_t Offset; size_t Used; uint8_t Data[1]; }; // The current file pointer - uint64 CurPos; - + uint64 CurPos; /// Number of blocks in use int Blocks; /// Data payload of blocks int BlockSize; /// Some blocks are built into the struct, no memory alloc needed. - Block *Local[GMEMFILE_BLOCKS]; + Block *Local[LMEMFILE_BLOCKS]; // Buf if they run out we can alloc some more. LArray Extra; Block *Get(int Index); bool FreeBlock(Block *b); Block *GetLast() { return Get(Blocks-1); } Block *Create(); int CurBlock() { return (int) (CurPos / BlockSize); } public: - GMemFile(int BlkSize = 256); - ~GMemFile(); + LMemFile(int BlkSize = 256); + ~LMemFile(); void Empty(); int64 GetSize() override; int64 SetSize(int64 Size) override; int64 GetPos() override; int64 SetPos(int64 Pos) override; ssize_t Read(void *Ptr, ssize_t Size, int Flags = 0) override; ssize_t Write(const void *Ptr, ssize_t Size, int Flags = 0) override; }; #endif diff --git a/include/lgi/common/Range.h b/include/lgi/common/Range.h --- a/include/lgi/common/Range.h +++ b/include/lgi/common/Range.h @@ -1,105 +1,119 @@ #ifndef _GRANGE_H_ #define _GRANGE_H_ #include #ifndef MAX #define MAX(a,b) ((a) > (b) ? a : b) #endif #ifndef MIN #define MIN(a,b) ((a) < (b) ? a : b) #endif struct LRange { ssize_t Start; ssize_t Len; // No arguments LRange() { Start = 0; Len = 0; } // One argument = length LRange(ssize_t len) { Start = 0; Len = len; } // Both arguments LRange(ssize_t start, ssize_t len) { Start = start; Len = len; } LRange &Set(ssize_t s, ssize_t l) { Start = s; Len = l; return *this; } LRange Overlap(const LRange &r) { LRange o; if (r.Start >= End()) return o; if (r.End() <= Start) return o; ssize_t e = MIN(End(), r.End()); o.Start = MAX(r.Start, Start); o.Len = e - o.Start; return o; } bool Overlap(ssize_t Val) { return (Val == Start) || (Val >= Start && Val < End()); } ssize_t End() const { return Start + Len; } bool Valid() const { return Start >= 0 && Len > 0; } bool operator ==(const LRange &r) const { return Start == r.Start && Len == r.Len; } bool operator !=(const LRange &r) const { return Start != r.Start || Len != r.Len; } bool operator >(const LRange &r) const { return Start > r.Start; } bool operator >=(const LRange &r) const { return Start >= r.Start; } bool operator <(const LRange &r) const { return Start < r.Start; } bool operator <=(const LRange &r) const { return Start < r.Start; } LRange &operator -=(const LRange &del) { LRange o = Overlap(del); if (o.Valid()) { assert(o.Len <= Len); Len -= o.Len; if (del.Start < o.Start) Start = del.Start; } // else nothing happens return *this; } LRange &operator =(const LRange &r) { this->Start = r.Start; this->Len = r.Len; return *this; } + + #ifdef LPrintfSSizeT + const char *GetStr() + { + static const int strs = 4; + static char s[strs][32]; + static int idx = 0; + + char *buf = s[idx++]; + sprintf_s(buf, 32, LPrintfSSizeT "," LPrintfSSizeT, Start, Len); + if (idx >= strs) idx = 0; + return buf; + } + #endif }; #endif \ No newline at end of file diff --git a/include/lgi/win/LgiOsDefs.h b/include/lgi/win/LgiOsDefs.h --- a/include/lgi/win/LgiOsDefs.h +++ b/include/lgi/win/LgiOsDefs.h @@ -1,309 +1,307 @@ // // FILE: LgiOsDefs.h (Win32) // AUTHOR: Matthew Allen // DATE: 24/9/99 // DESCRIPTION: Lgi Win32 OS defines // // Copyright (C) 1999, Matthew Allen // fret@memecode.com // -#ifndef __LGI_OS_DEFS_H -#define __LGI_OS_DEFS_H +#pragma once #pragma warning(disable : 4251 4275) #define WIN32GTK 0 #define WINNATIVE 1 #define LGI_VIEW_HANDLE 1 #define LGI_VIEW_HASH 0 #ifndef WINDOWS #error "Define WINDOWS in your project" #endif #ifdef _WIN64 #define LGI_64BIT 1 #ifndef WIN64 #define WIN64 1 #endif #else #define LGI_32BIT 1 #ifndef WIN32 #define WIN32 1 #endif #endif #include #include #include "lgi/common/LgiInc.h" #include "lgi/common/LgiDefs.h" #define _MSC_VER_VS2019 1920 // MSVC++ 16.0 (really all 192x values) #define _MSC_VER_VS2017 1910 // MSVC++ 15.0 #define _MSC_VER_VS2015 1900 // MSVC++ 14.0 #define _MSC_VER_VS2013 1800 // MSVC++ 12.0 #define _MSC_VER_VS2012 1700 // MSVC++ 11.0 #define _MSC_VER_VS2010 1600 // MSVC++ 10.0 #define _MSC_VER_VS2008 1500 // MSVC++ 9.0 #define _MSC_VER_VS2005 1400 // MSVC++ 8.0 #define _MSC_VER_VS2003 1310 // MSVC++ 7.1 #define _MSC_VER_VC7 1300 // MSVC++ 7.0 #define _MSC_VER_VC6 1200 // MSVC++ 6.0 #define _MSC_VER_VC5 1100 // MSVC++ 5.0 #if _MSC_VER >= _MSC_VER_VS2019 #define _MSC_VER_STR "16" #define _MSC_YEAR_STR "19" #elif _MSC_VER >= _MSC_VER_VS2017 #define _MSC_VER_STR "15" #define _MSC_YEAR_STR "17" #elif _MSC_VER >= _MSC_VER_VS2015 #define _MSC_VER_STR "14" #define _MSC_YEAR_STR "15" #elif _MSC_VER >= _MSC_VER_VS2013 #define _MSC_VER_STR "12" #define _MSC_YEAR_STR "13" #elif _MSC_VER >= _MSC_VER_VS2012 #define _MSC_VER_STR "11" #define _MSC_YEAR_STR "12" #elif _MSC_VER >= _MSC_VER_VS2010 #define _MSC_VER_STR "10" #define _MSC_YEAR_STR "10" #else #define _MSC_VER_STR "9" #endif ////////////////////////////////////////////////////////////////// // Includes #define WIN32_LEAN_AND_MEAN #include "winsock2.h" #include "windows.h" #include "SHELLAPI.H" #include "COMMDLG.H" #include ////////////////////////////////////////////////////////////////// // Typedefs typedef HWND OsWindow; typedef HWND OsView; typedef HANDLE OsThread; typedef HANDLE OsProcess; typedef char16 OsChar; typedef HBITMAP OsBitmap; typedef HDC OsPainter; typedef HFONT OsFont; #if _MSC_VER <= _MSC_VER_VC6 typedef unsigned long ULONG_PTR, *PULONG_PTR; #define sprintf_s _snprintf #endif typedef BOOL (__stdcall *pSHGetSpecialFolderPathA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate); typedef BOOL (__stdcall *pSHGetSpecialFolderPathW)(HWND hwndOwner, LPWSTR lpszPath, int nFolder, BOOL fCreate); typedef int (__stdcall *pSHFileOperationA)(LPSHFILEOPSTRUCTA lpFileOp); typedef int (__stdcall *pSHFileOperationW)(LPSHFILEOPSTRUCTW lpFileOp); typedef int (__stdcall *p_vscprintf)(const char *format, va_list argptr); #include "lgi/common/Message.h" -#include "lgi/common/LgiUiBase.h" class LgiClass OsAppArguments { LAutoWString CmdLine; void _Default(); public: HINSTANCE hInstance; DWORD Pid; char16 *lpCmdLine; int nCmdShow; OsAppArguments(); OsAppArguments(int Args, const char **Arg); OsAppArguments &operator =(OsAppArguments &p); void Set(char *Utf); void Set(int Args, const char **Arg); }; ////////////////////////////////////////////////////////////////// // Defines #define IsWin9x (LApp::Win9x) #define DefaultOsView(t) NULL #define GWL_LGI_MAGIC 8 #define GWL_EXTRA_BYTES 12 // Key redefs enum LgiKeys { LK_TAB = VK_TAB, LK_RETURN = VK_RETURN, LK_SPACE = VK_SPACE, LK_DELETE = VK_DELETE, LK_ESCAPE = VK_ESCAPE, LK_PAGEUP = VK_PRIOR, LK_PAGEDOWN = VK_NEXT, LK_BACKSPACE = VK_BACK, LK_F1 = VK_F1, LK_F2 = VK_F2, LK_F3 = VK_F3, LK_F4 = VK_F4, LK_F5 = VK_F5, LK_F6 = VK_F6, LK_F7 = VK_F7, LK_F8 = VK_F8, LK_F9 = VK_F9, LK_F10 = VK_F10, LK_F11 = VK_F11, LK_F12 = VK_F12, LK_LEFT = VK_LEFT, LK_RIGHT = VK_RIGHT, LK_UP = VK_UP, LK_DOWN = VK_DOWN, LK_HOME = VK_HOME, LK_END = VK_END, LK_INSERT = VK_INSERT, LK_SHIFT = VK_SHIFT, LK_ALT = VK_MENU, LK_CTRL = VK_CONTROL, LK_DECIMAL = VK_DECIMAL, LK_COMMA = VK_OEM_COMMA, // , . LK_MINUS = VK_OEM_MINUS, // - _ LK_EQUALS = VK_OEM_PLUS, // = + LK_SEMI_COLON = VK_OEM_1, // ; : LK_SLASH = VK_OEM_2, // / ? LK_TILDE = VK_OEM_3, // ~ LK_OPEN_BRACKET = VK_OEM_4, // [ { LK_BACK_SLASH = VK_OEM_5, // \ | LK_CLOSE_BRACKET = VK_OEM_6, // ] } LK_SINGLE_QUOTE = VK_OEM_7, // ' " }; // Sleep the current thread LgiFunc void LSleep(DWORD i); // Process typedef DWORD OsProcessId; LgiExtern HINSTANCE _lgi_app_instance; -#define LProcessInst() _lgi_app_instance +#define LProcessInst() _lgi_app_instance extern p_vscprintf lgi_vscprintf; #define LGetCurrentProcess() GetCurrentProcessId() // Threads typedef DWORD OsThreadId; typedef CRITICAL_SECTION OsSemaphore; -#define LGetCurrentThread() GetCurrentThread() +#define LGetCurrentThread() GetCurrentThread() // #define GetCurrentThreadId() GetCurrentThreadId() // Socket/Network #define ValidSocket(s) ((s) != INVALID_SOCKET) typedef SOCKET OsSocket; #define LGI_GViewMagic 0x14412662 #define LGI_FileDropFormat "CF_HDROP" #define LGI_StreamDropFormat CFSTR_FILEDESCRIPTORW #define LGI_WideCharset "ucs-2" #define LPrintfInt64 "%I64i" #define LPrintfHex64 "%I64x" + #if LGI_64BIT #define LPrintfSizeT "%I64u" #define LPrintfSSizeT "%I64d" #else #define LPrintfSizeT "%u" #define LPrintfSSizeT "%d" #endif #define LGI_IllegalFileNameChars "\t\r\n/\\:*?\"<>|" #define MK_LEFT MK_LBUTTON #define MK_RIGHT MK_RBUTTON #define MK_MIDDLE MK_MBUTTON #define MK_CTRL MK_CONTROL // Stupid mouse wheel defines don't work. hmmm #define WM_MOUSEWHEEL 0x020A #define WHEEL_DELTA 120 #ifndef SPI_GETWHEELSCROLLLINES #define SPI_GETWHEELSCROLLLINES 104 #endif // Window flags #define GWF_VISIBLE WS_VISIBLE #define GWF_DISABLED WS_DISABLED #define GWF_BORDER WS_BORDER #define GWF_TABSTOP WS_TABSTOP #define GWF_FOCUS 0x00000001 #define GWF_OVER 0x00000002 #define GWF_DROP_TARGET 0x00000004 #define GWF_SUNKEN 0x00000008 #define GWF_FLAT 0x00000010 #define GWF_RAISED 0x00000020 #define GWF_DIALOG 0x00000040 #define GWF_DESTRUCTOR 0x00000080 #define GWF_QUIT_WND 0x00000100 #define GWF_SYS_BORDER 0x00000200 // ask the system to draw the border // Widgets #define DialogToPixelX(i) (((i)*Bx)/4) #define DialogToPixelY(i) (((i)*By)/8) #define PixelToDialogX(i) (((i)*4)/Bx) #define PixelToDialogY(i) (((i)*8)/By) #define DIALOG_X 1.56 #define DIALOG_Y 1.85 #define CTRL_X 1.50 #define CTRL_Y 1.64 // Directories #define DIR_CHAR '\\' #define DIR_STR "\\" #define EOL_SEQUENCE "\r\n" #define LGI_PATH_SEPARATOR ";" #define LGI_ALL_FILES "*.*" #define LGI_LIBRARY_EXT "dll" #define LGI_EXECUTABLE_EXT ".exe" ///////////////////////////////////////////////////////////////////////////////////// // Typedefs typedef HWND OsView; ///////////////////////////////////////////////////////////////////////////////////// // Externs LgiFunc class LViewI *LWindowFromHandle(OsView hWnd); LgiFunc int GetMouseWheelLines(); LgiFunc int WinPointToHeight(int Pt, HDC hDC = NULL); LgiFunc int WinHeightToPoint(int Ht, HDC hDC = NULL); LgiExtern class LString WinGetSpecialFolderPath(int Id); /// Convert a string d'n'd format to an OS dependant integer. LgiFunc int FormatToInt(LString s); /// Convert a Os dependant integer d'n'd format to a string. LgiFunc char *FormatToStr(int f); extern bool LgiToWindowsCursor(OsView Hnd, LCursor Cursor); #ifdef _MSC_VER #define snprintf _snprintf //#define vsnprintf _vsnprintf #define vsnwprintf _vsnwprintf #define stricmp _stricmp #define strlwr _strlwr #define strnicmp _strnicmp #define vsnprintf _vsnprintf_s // #define getcwd _getcwd #endif #define atoi64 _atoi64 #ifdef __GNUC__ // #define stricmp strcasecmp // #define strnicmp strncasecmp #define vsnprintf_s vsnprintf #define swprintf_s snwprintf #define vsprintf_s vsnprintf #if !defined(_TRUNCATE) #define _TRUNCATE ((size_t)-1) #endif #endif -#endif diff --git a/src/common/Gdc2/Font/Charset.cpp b/src/common/Gdc2/Font/Charset.cpp --- a/src/common/Gdc2/Font/Charset.cpp +++ b/src/common/Gdc2/Font/Charset.cpp @@ -1,1715 +1,1711 @@ #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Font.h" #include "lgi/common/Charset.h" struct UnicodeMappings { int Unicode; char Ascii; } MissingMaps[] = { {0x2019, '\''}, {0x201C, '\"'}, {0x201D, '\"'}, {0, 0} }; typedef uint32_t iso2022jp_block[16]; iso2022jp_block *iso2022jp_map[128]; iso2022jp_block iso2022jp_blocks[] = { {0,0,0x10000000,0,0,0x53118c,0x800000,0x800000,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0,0,0,0,0xfffe0000,0xfffe03fb,0x3fb,0}, {0xffff0002,0xffffffff,0x2ffff,0,0,0,0,0,0,0,0,0,0,0,0,0}, {0x33510000,0x80d0063,0,0,0,0,0,0,0x8,0x800,0,0,0xf0000,0,0x140000,0}, {0x6404098d,0x20301f81,0x40000,0xcc3,0xcc,0x20,0,0,0x40000,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0x3999900f,0x99999939,0x804,0,0,0x300c0003,0xc8c0,0x8000}, {0x60,0,0x5,0xa400,0,0,0,0,0,0,0,0,0,0,0,0}, {0x103fffef,0,0xfffffffe,0xffffffff,0x780fffff,0xfffffffe,0xffffffff,0x787fffff,0,0,0,0,0,0,0,0}, {0x43f36f8b,0x9b462442,0xe3e0e82c,0x400a0004,0xdb365f65,0x4497977,0xe3f0ecd7,0x8c56038,0x3403e602,0x35518000,0x7eabe0c8,0x98698200,0x2942a948,0x8060e803,0xad93441c,0x4568c03a}, {0x8656aa60,0x2403f7a,0x14618388,0x21741020,0x7022021,0x40bc3000,0x4462a624,0xa2060a8,0x85740217,0x9c840402,0x14157bfb,0x11e27f24,0x2efb665,0x20ff1f75,0x38403a70,0x676326c3}, {0x20924dd9,0xfc946b0,0x4850bc98,0xa03f8638,0x88162388,0x52323e09,0xe3a422aa,0xc72c00dd,0x26e1a166,0x8f0a840b,0x559e27eb,0x89bbc241,0x85400014,0x8496361,0x8ad07f0c,0x5cfff3e}, {0xa803ff1a,0x7b407a41,0x80024745,0x38eb0500,0x5d851,0x710c9934,0x1000397,0x24046366,0x5180d0,0x430ac000,0x30c89071,0x58000008,0xf7000e99,0x415f80,0x941000b0,0x62800018}, {0x9d00240,0x1568200,0x8015004,0x5101d10,0x1084c1,0x10504025,0x4d8a410f,0xa60d4009,0x914cab19,0x98121c0,0x3c485,0x80000652,0x80b04,0x9041d,0x905c4849,0x16900009}, {0x22200c65,0x24338412,0x47960c03,0x42250a04,0x90880028,0x4f084900,0xd3aa14a2,0x3e87d830,0x1f618604,0x41867ea4,0x5b3c390,0x211857a5,0x2a48241e,0x4a041128,0x161b0a40,0x88400d60}, {0x9502020a,0x10608221,0x4000243,0x80001444,0xc040000,0x70000000,0xc11a06,0xc00024a,0x401a00,0x40451404,0xbdb30029,0x52b0a78,0xbfa0bba9,0x8379407c,0xe81d12fc,0xc5694bf6}, {0x44aeff6,0xff022115,0x402bed63,0x242d033,0x131000,0x59ca1b02,0x20000a0,0x2c41a703,0x8ff24880,0x204,0x10055800,0x489200,0x20011894,0x34805004,0x684c3200,0x68be49ea}, {0x2e42184c,0x21c9a820,0x80b050b9,0xff7c001e,0x14e0849a,0x1e028c1,0xac49870e,0xdddb130f,0x89fbbe1a,0x51a2a2e0,0x32ca5502,0x928b3e46,0x438f1dbf,0x32186703,0x33c03028,0xa9230811}, {0x3a65c000,0x4028fe3,0x86252c4e,0xa1bf3d,0x8cd43a1a,0x317c06c9,0x950a00e0,0xedb018b,0x8c20e34b,0xf0101182,0xa7287d94,0x40fbc9ac,0x6534484,0x44445a90,0x13fc8,0xf5d40048}, {0xec577701,0x891dc442,0x49286b83,0xd2424109,0x59fe061d,0x3a221800,0x3b9fb7e4,0xc0eaf003,0x82021386,0xe4008980,0x10a1b200,0xcc44b80,0x8944d309,0x48341faf,0xc458259,0x450420a}, {0x10c8a040,0x44503140,0x1004004,0x5408280,0x442c0108,0x1a056a30,0x51420a6,0x645690cf,0x31000021,0xcbf09c18,0x63e2a120,0x1b5104c,0x9a83538c,0x3281b8b2,0xa84987a,0xc0233e7}, {0x9018d4cc,0x9070a1a1,0xe0048a1e,0x451c3d4,0x21c2439a,0x53104844,0x36400292,0xf3bd0241,0xe8f0ab09,0xa5d27dc0,0xd24bc242,0xd0afa43f,0x34a11aa0,0x3d88247,0x651bc452,0xc83ad294}, {0x40c8001c,0x33140e06,0xb21b614f,0xc0d00088,0xa898a02a,0x166ba1c5,0x85b42e50,0x604c08b,0x1e04f933,0xa251056e,0x76380400,0x73b8ec07,0x18324406,0xc8164081,0x63097c8a,0xaa042980}, {0xca9c1c24,0x27604e0e,0x83000990,0x81040046,0x10816011,0x908540d,0xcc0a000e,0xc000500,0xa0440430,0x6784008b,0x8a195288,0x8b18865e,0x41602e59,0x9cbe8c10,0x891c6861,0x89800}, {0x89a8100,0x41900018,0xe4a14007,0x640d0505,0xe4d310e,0xff0a4806,0x2aa81632,0xb852e,0xca841800,0x696c0e20,0x16000032,0x3905658,0x1a285120,0x11248000,0x432618e1,0xeaa5d52}, {0xae280fa0,0x4500fa7b,0x89406408,0xc044c880,0xb1419005,0x24c48424,0x603a1a34,0xc1949000,0x3a8246,0xc106180d,0x99100022,0x1511e050,0x824057,0x20a041a,0x8930004f,0x444ad813}, {0xed228a02,0x400510c0,0x1021000,0x31018808,0x2044600,0x708f000,0xa2008900,0x22020000,0x16100200,0x10400042,0x2605200,0x200052f4,0x82308510,0x42021100,0x80b54308,0x9a2070e1}, {0x8012040,0xfc653500,0xab0419c1,0x62140286,0x440087,0x2449085,0xa85405c,0x33803207,0xb8c00400,0xc0d0ce20,0x80c030,0xd250508,0x400a90,0x80c0200,0x40006505,0x41026421}, {0x268,0x847c0024,0xde200002,0x40498619,0x40000808,0x20010084,0x10108400,0x1c742cd,0xd52a7038,0x1d8f1968,0x3e12be50,0x81d92ef5,0x2412cec4,0x732e0828,0x4b3424ac,0xd41d020c}, {0x80002a02,0x8110097,0x114411c4,0x7d451786,0x64949d9,0x87914000,0xd8c4254c,0x491444ba,0xc8001b92,0x15800271,0xc000081,0xc200096a,0x40024800,0xba493021,0x1c802080,0x1008e2ac}, {0x341004,0x841400e1,0x20000020,0x10149800,0x4aa70c2,0x54208688,0x4130c62,0x20109180,0x2064082,0x54001c40,0xe4e90383,0x84802125,0x2000e433,0xe60944c0,0x81260a03,0x80112da}, {0x97906901,0xf8864001,0x81e24d,0xa6510a0e,0x81ec011a,0x8441c600,0xb62cadb8,0x8741a46f,0x4b028d54,0x2681161,0x2057bb60,0x43350a0,0xb7b4a8c0,0x1122402,0x20009ad3,0xc82271}, {0x809e2081,0xe1800c8a,0x8151b009,0x40281031,0x89a52a0e,0x620e69b6,0xd1444425,0x4d548085,0x1fb12c75,0x862dd807,0x4841d87c,0x226e414e,0x9e088200,0xed37f80c,0x75268c80,0x8149313}, {0xc8040e32,0x6ea6484e,0x66702c4a,0xba0126c0,0x185dd30c,0,0,0,0,0x5400000,0x81337020,0x3a54f81,0x641055ec,0x2344c318,0x341462,0x1a090a43}, {0x13a5187b,0xa8480102,0xc5440440,0xe2dd8106,0x2d481af0,0x416b626,0x6e405058,0x31128032,0xc0007e4,0x420a8208,0x803b4840,0x87134860,0x3428850d,0xe5290319,0x870a2345,0x5c1825a9}, {0xd9c577a6,0x3e85e00,0xa7000081,0x41c6cd54,0xa2042800,0x2b0ab860,0xda9e0020,0xe1a08ea,0x11c0427c,0x3768908,0x1058621,0x18a80000,0xc44846a0,0x20220d05,0x91485422,0x28978a01}, {0x87898,0x31221605,0x8804240,0x6a2fa4e,0x92110814,0x9b042002,0x6432e52,0x90105000,0x85ba0041,0x20203042,0x5a04f0b,0x40802708,0x1a930591,0x600df50,0x3021a202,0x4e800630}, {0x4c80cc4,0x8001a004,0xd4316000,0xa020880,0x281c00,0x418e18,0xca106ad0,0x4b00f210,0x1506274d,0x88900220,0x82a85a00,0x81504549,0x80002004,0x2c088804,0x508d1,0x4ac48001}, {0x62e020,0xa42008e,0x6a8c3055,0xe0a5090e,0x42c42906,0x80b34814,0xb330803e,0x731c0102,0x600d1494,0x9400c20,0xc040301a,0xc094a451,0x5c88dca,0xa40c96c2,0x34040001,0x11000c8}, {0xa9c9550d,0x1c5a2428,0x48370142,0x100f7a4d,0x452a32b4,0x9205317b,0x5c44b894,0x458a68d7,0x2ed15097,0x42081943,0x9d40d202,0x20979840,0x64d5409,0,0,0}, {0,0x84800000,0x4215542,0x17001c06,0x61107624,0xb9ddff87,0x5c0a659f,0x3c00245d,0x59adb0,0,0,0x9b28d0,0x2000422,0x44080108,0xac409804,0x90288d0a}, {0xe0018700,0x310400,0x82211794,0x10540019,0x21a2cb2,0x40039c02,0x88043d60,0x7900080c,0xba3c1628,0xcb088640,0x90807274,0x1e,0xd8000000,0x9c87e188,0x4124034,0x2791ae64}, {0xe6fbe86b,0x5366408f,0x537feea6,0xb5e4e32b,0x2869f,0x1228548,0x8004402,0x20a02116,0x2040004,0x52000,0x1547e00,0x1ac162c,0x10852a84,0x5308c14,0xb943fbc3,0x906000ca}, {0x40326000,0x80901200,0x4c810b30,0x40020054,0x1d6a0029,0x2802000,0x48000,0x150c2610,0x7018040,0xc24d94d,0x18502810,0x50205001,0x4d01000,0x2017080,0x21c30108,0x132}, {0x7190088,0x5600802,0x4c0e0012,0xf0a10405,0x2,0,0,0,0,0,0,0x800000,0x35a8e8d,0x5a0421bd,0x11703488,0x26}, {0x10000000,0x8804c502,0xf801b815,0x25ed147c,0x1bb0ed60,0x1bd70589,0x1a627af3,0xac50d0c,0x524ae5d1,0x63050490,0x52440354,0x16122b57,0x1101a872,0x182949,0x10080948,0x886c6000}, {0x58f916e,0x39903012,0x4930f840,0x1b8880,0,0x428500,0x98000058,0x7014ea04,0x611d1628,0x60005113,0xa71a24,0,0x3c00000,0x10187120,0xa9270172,0x89066004}, {0x20cc022,0x40810900,0x8ca0202d,0xe34,0,0x11012100,0xc11a8011,0x892ec4c,0x85000040,0x1806c7ac,0x512e03e,0x108000,0x80ce4008,0x2106d01,0x8568641,0x27011e}, {0x83d3750,0x4e05e032,0x48401c0,0x1400081,0,0,0,0x591aa0,0x882443c8,0xc8001d48,0x72030152,0x4049013,0x4008280,0xd148a10,0x2088056,0x2704a040}, {0x4c000000,0,0,0xa3200000,0xa0ae1902,0xdf002660,0x7b15f010,0x3ad08121,0x284180,0x48001003,0x8014cc00,0xc414cf,0x30202000,0x1,0,0}, {0,0,0,0,0,0,0,0,0xffffdf7a,0xefffffff,0x3fffffff,0,0,0,0,0x2} }; class LgiIso2022Jp { public: LgiIso2022Jp() { int n, o = 0, i = 0; for (n=0; n<3; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++]; o += 13; for (n=0; n<4; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++];; o += 4; iso2022jp_map[o++] = &iso2022jp_blocks[i++];; o += 14; for (n=0; n<41; n++) iso2022jp_map[o++] = &iso2022jp_blocks[i++];; o += 47; iso2022jp_map[o++] = &iso2022jp_blocks[i++];; LAssert(o == 128); } bool CanEncode(char16 *s, ssize_t l) { if (s) { if (l < 0) l = StrlenW(s); for (int i=0; i> 9]; if (!*block) { return false; } u &= 0x1ff; if ( ( *block[u >> 5] & (1 << (u & 0x1f)) ) == 0 ) { return false; } } return true; } return false; } } Iso2022Jp; ///////////////////////////////////////////////////////////////////////////////////// bool LIsUtf8(const char *s, ssize_t len) { #define LenCheck(Need) \ if (len >= 0 && (len - (s - Start)) < Need) \ goto Utf8Error; #define TrailCheck() \ if (!IsUtf8_Trail(*s)) \ goto Utf8Error; \ s++; if (!s || *s == 0) return true; - /* - { - ssize_t Len = len >= 0 ? len : Strlen(s); - printf("Utf: %p %i\n", s, (int)Len); - for (int i=0; i= end) + || + *s == 0 + ) + break; + LgiTrace("%02.2x,", (uint8_t)*s++); } LgiTrace("\n"); #endif return false; } ///////////////////////////////////////////////////////////////////////////////////// short _gdc_usascii_mapping[128] = { // 0x80 - 0x8f 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, // 0x90 - 0x9f 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, 0xff, 0xd6, 0xdc, 0xa2, 0xa3, 0xa5, 0x20a7, 0x192, // 0xa0 - 0xaf 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xb2, 0xb0, 0xbf, 0x2310, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, // 0xb0 - 0xbf 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, // 0xc0 - 0xcf 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, // 0xd0 - 0xdf 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, // 0xe0 - 0xef 0x3b1, 0x3b2, 0x393, 0x3a0, 0x3a3, 0x3c3, 0x3bc, 0x3c4, 0x3a6, 0x398, 0x3a9, 0x3b4, 0x221e, 0xd8, 0x3b6, 0x2229, // 0xf0 - 0xff 0x2261, 0xb1, 0x2265, 0x2264, 0x2320, 0x2321, 0xf7, 0x2248, 0xb0, 0x2022, 0x2219, 0x221a, 0x207f, 178, 0x25a0, 0x25a1 }; // This mapping just NUL's out the characters between 0x80 and 0x9f which aren't defined // in the ISO spec. The rest of the characters map to themselves. short _gdc_ISO_8859_identity_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; short _gdc_ISO_8859_2_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa0, 0x104, 0x2d8, 0x141, 0xa4, 0x13d, 0x15a, 0xa7, 0xa8, 0x160, 0x15e, 0x164, 0x179, 0xad, 0x17d, 0x17b, 0xb0, 0x105, 0x2db, 0x142, 0xb4, 0x13e, 0x15b, 0x2c7, 0xb8, 0x161, 0x15f, 0x165, 0x17a, 0x2dd, 0x17e, 0x17c, 0x154, 0xc1, 0xc2, 0x102, 0xc4, 0x139, 0x106, 0xc7, 0x10c, 0xc9, 0x118, 0xcb, 0x11a, 0xcd, 0xce, 0x10e, 0x110, 0x143, 0x147, 0xd3, 0xd4, 0x150, 0xd6, 0xd7, 0x158, 0x16e, 0xda, 0x170, 0xdc, 0xdd, 0x162, 0xdf, 0x155, 0xe1, 0xe2, 0x103, 0xe4, 0x13a, 0x107, 0xe7, 0x10d, 0xe9, 0x119, 0xeb, 0x11b, 0xed, 0xee, 0x10f, 0x111, 0x144, 0x148, 0xf3, 0xf4, 0x151, 0xf6, 0xf7, 0x159, 0x16f, 0xfa, 0x171, 0xfc, 0xfd, 0x163, 0x2d9 }; short _gdc_ISO_8859_3_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xa0, 0x126, 0x2d8, 0xa3, 0xa4, 0, 0x124, 0xa7, 0xa8, 0x130, 0x15e, 0x11e, 0x134, 0xad, 0, 0x17b, 0xb0, 0x127, 0xb2, 0xb3, 0xb4, 0xb5, 0x125, 0xb7, 0xb8, 0x131, 0x15f, 0x11f, 0x135, 0xbd, 0, 0x17c, 0xc0, 0xc1, 0xc2, 0, 0xc4, 0x10a, 0x108, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0, 0xd1, 0xd2, 0xd3, 0xd4, 0x120, 0xd6, 0xd7, 0x11c, 0xd9, 0xda, 0xdb, 0xdc, 0x16c, 0x15c, 0xdf, 0xe0, 0xe1, 0xe2, 0, 0xe4, 0x10b, 0x109, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0, 0xf1, 0xf2, 0xf3, 0xf4, 0x121, 0xf6, 0xf7, 0x11d, 0xf9, 0xfa, 0xfb, 0xfc, 0x16d, 0x15d, 0x2d9 }; short _gdc_ISO_8859_4_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0A0, 0x104, 0x138, 0x156, 0x0A4, 0x128, 0x13B, 0x0A7, 0x0A8, 0x160, 0x112, 0x122, 0x166, 0x0AD, 0x17D, 0x0AF, 0x0B0, 0x105, 0x2DB, 0x157, 0x0B4, 0x129, 0x13C, 0x2C7, 0x0B8, 0x161, 0x113, 0x123, 0x167, 0x14A, 0x17E, 0x14B, 0x100, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x12E, 0x10C, 0x0C9, 0x118, 0x0CB, 0x116, 0x0CD, 0x0CE, 0x12A, 0x110, 0x145, 0x14C, 0x136, 0x0D4, 0x0D5, 0x0D6, 0x0D7, 0x0D8, 0x172, 0x0DA, 0x0DB, 0x0DC, 0x168, 0x16A, 0x0DF, 0x101, 0x0E1, 0x0E2, 0x0E3, 0x0E4, 0x0E5, 0x0E6, 0x12F, 0x10D, 0x0E9, 0x119, 0x0EB, 0x117, 0x0ED, 0x0EE, 0x12B, 0x111, 0x146, 0x14D, 0x137, 0x0F4, 0x0F5, 0x0F6, 0x0F7, 0x0F8, 0x173, 0x0FA, 0x0FB, 0x0FC, 0x169, 0x16B, 0x2D9 }; short _gdc_ISO_8859_5_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0A0, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x0AD, 0x40E, 0x40F, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x2116, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x0A7, 0x45E, 0x45F }; short _gdc_ISO_8859_6_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0, 0, 0, 0xA4, 0, 0, 0, 0, 0, 0, 0, 0x60C, 0xAD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x61B, 0, 0, 0, 0x61F, 0, 0x621, 0x622, 0x623, 0x624, 0x625, 0x626, 0x627, 0x628, 0x629, 0x62A, 0x62B, 0x62C, 0x62D, 0x62E, 0x62F, 0x630, 0x631, 0x632, 0x633, 0x634, 0x635, 0x636, 0x637, 0x638, 0x639, 0x63A, 0, 0, 0, 0, 0, 0x640, 0x641, 0x642, 0x643, 0x644, 0x645, 0x646, 0x647, 0x648, 0x649, 0x64A, 0x64B, 0x64C, 0x64D, 0x64E, 0x64F, 0x650, 0x651, 0x652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; short _gdc_ISO_8859_7_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0A0, 0x2BD, 0x2BC, 0x0A3, 0, 0, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0, 0x0AB, 0x0AC, 0x0AD, 0, 0x015, 0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x384, 0x385, 0x386, 0x0B7, 0x388, 0x389, 0x38A, 0x0BB, 0x38C, 0x0BD, 0x38E, 0x38F, 0x390, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x3AB, 0x3AC, 0x3AD, 0x3AE, 0x3AF, 0x3B0, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C2, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x3CB, 0x3CC, 0x3CD, 0x3CE, 0 }; short _gdc_ISO_8859_8_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0A0, 0, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x0D7, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x203E, 0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x0B9, 0x0F7, 0x0BB, 0x0BC, 0x0BD, 0x0BE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2017, 0x5D0, 0x5D1, 0x5D2, 0x5D3, 0x5D4, 0x5D5, 0x5D6, 0x5D7, 0x5D8, 0x5D9, 0x5DA, 0x5DB, 0x5DC, 0x5DD, 0x5DE, 0x5DF, 0x5E0, 0x5E1, 0x5E2, 0x5E3, 0x5E4, 0x5E5, 0x5E6, 0x5E7, 0x5E8, 0x5E9, 0x5EA, 0, 0, 0, 0, 0 }; short _gdc_ISO_8859_9_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0x11E, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0x130, 0x15E, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0x11F, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x131, 0x15F, 0xFF }; short _gdc_ISO_8859_13_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0x201D, 0xA2, 0xA3, 0xA4, 0x201E, 0xA6, 0xA7, 0xD8, 0xA9, 0x156, 0xAB, 0xAC, 0xAD, 0xAE, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0x201C, 0xB5, 0xB6, 0xB7, 0xF8, 0xB9, 0x157, 0xBB, 0xBC, 0xBD, 0xBE, 0xE6, 0x104, 0x12E, 0x100, 0x106, 0xC4, 0xC5, 0x118, 0x112, 0x10C, 0xC9, 0x179, 0x116, 0x122, 0x136, 0x12A, 0x13B, 0x160, 0x143, 0x145, 0xD3, 0x14C, 0xD5, 0xD6, 0xD7, 0x172, 0x141, 0x15A, 0x16A, 0xDC, 0x17B, 0x17D, 0xDF, 0x105, 0x12F, 0x101, 0x107, 0xE4, 0xE5, 0x119, 0x113, 0x10D, 0xE9, 0x17A, 0x117, 0x123, 0x137, 0x12B, 0x13C, 0x161, 0x144, 0x146, 0xF3, 0x14D, 0xF5, 0xF6, 0xF7, 0x173, 0x142, 0x15B, 0x16B, 0xFC, 0x17C, 0x17E, 0x2019 }; short _gdc_ISO_8859_15_mapping[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0xA1, 0xA2, 0xA3, 0x20AC, 0xA5, 0x160, 0xA7, 0x161, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0x17D, 0xB5, 0xB6, 0xB7, 0x17E, 0xB9, 0xBA, 0xBB, 0x152, 0x153, 0x178, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; short _gdc_win_874_mapping[128] = { 0x20AC, 0, 0, 0, 0, 0x2026, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0, 0, 0, 0, 0, 0, 0, 0xA0, 0xE01, 0xE02, 0xE03, 0xE04, 0xE05, 0xE06, 0xE07, 0xE08, 0xE09, 0xE0A, 0xE0B, 0xE0C, 0xE0D, 0xE0E, 0xE0F, 0xE10, 0xE11, 0xE12, 0xE13, 0xE14, 0xE15, 0xE16, 0xE17, 0xE18, 0xE19, 0xE1A, 0xE1B, 0xE1C, 0xE1D, 0xE1E, 0xE1F, 0xE20, 0xE21, 0xE22, 0xE23, 0xE24, 0xE25, 0xE26, 0xE27, 0xE28, 0xE29, 0xE2A, 0xE2B, 0xE2C, 0xE2D, 0xE2E, 0xE2F, 0xE30, 0xE31, 0xE32, 0xE33, 0xE34, 0xE35, 0xE36, 0xE37, 0xE38, 0xE39, 0xE3A, 0, 0, 0, 0, 0xE3F, 0xE40, 0xE41, 0xE42, 0xE43, 0xE44, 0xE45, 0xE46, 0xE47, 0xE48, 0xE49, 0xE4A, 0xE4B, 0xE4C, 0xE4D, 0xE4E, 0xE4F, 0xE50, 0xE51, 0xE52, 0xE53, 0xE54, 0xE55, 0xE56, 0xE57, 0xE58, 0xE59, 0xE5A, 0xE5B, 0, 0, 0, 0, }; short _gdc_win_1250_mapping[128] = { 0x20AC, 0, 0x201A, 0, 0x201E, 0x2026, 0x2020, 0x2021, 0, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A, 0xA0, 0x02C7, 0x02D8, 0x0141, 0xA4, 0x0104, 0xA6, 0xA7, 0xA8, 0xA9, 0x015E, 0xAB, 0xAC, 0xAD, 0xAE, 0x017B, 0xB0, 0xB1, 0x02DB, 0x0142, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0x0105, 0x015F, 0xBB, 0x013D, 0x02DD, 0x013E, 0x017C, 0x0154, 0xC1, 0xC2, 0x0102, 0xC4, 0x0139, 0x0106, 0xC7, 0x010C, 0xC9, 0x0118, 0xCB, 0x011A, 0xCD, 0xCE, 0x010E, 0x0110, 0x0143, 0x0147, 0xD3, 0xD4, 0x0150, 0xD6, 0xD7, 0x0158, 0x016E, 0xDA, 0x0170, 0xDC, 0xDD, 0x0162, 0xDF, 0x0155, 0xE1, 0xE2, 0x0103, 0xE4, 0x013A, 0x0107, 0xE7, 0x010D, 0xE9, 0x0119, 0xEB, 0x011B, 0xED, 0xEE, 0x010F, 0x0111, 0x0144, 0x0148, 0xF3, 0xF4, 0x0151, 0xF6, 0xF7, 0x0159, 0x016F, 0xFA, 0x0171, 0xFC, 0xFD, 0x0163, 0x02D9 }; short _gdc_win_1251_mapping[128] = { 0x402, 0x403, 0x201A, 0x453, 0x201E, 0x2026, 0x2020, 0x2021, 0x20AC, 0x2030, 0x409, 0x2039, 0x40A, 0x40C, 0x40B, 0x40F, 0x452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0x2122, 0x459, 0x203A, 0x45A, 0x45C, 0x45B, 0x45F, 0x0A0, 0x40E, 0x45E, 0x408, 0x0A4, 0x490, 0x0A6, 0x0A7, 0x401, 0x0A9, 0x404, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x407, 0x0B0, 0x0B1, 0x406, 0x456, 0x491, 0x0B5, 0x0B6, 0x0B7, 0x451, 0x2116, 0x454, 0x0BB, 0x458, 0x405, 0x455, 0x457, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F }; short _gdc_win_1252_mapping[128] = { 0x20AC, 0, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0, 0x017D, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0, 0x017E, 0x0178, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF }; short _gdc_win_1253_mapping[128] = { 0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0, 0x2030, 0, 0x2039, 0, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0x2122, 0, 0x203A, 0, 0, 0, 0, 0xA0, 0x385, 0x386, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0, 0xAB, 0xAC, 0xAD, 0xAE, 0x2015, 0xB0, 0xB1, 0xB2, 0xB3, 0x384, 0xB5, 0xB6, 0xB7, 0x388, 0x389, 0x38A, 0xBB, 0x38C, 0xBD, 0x38E, 0x38F, 0x390, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x3AB, 0x3AC, 0x3AD, 0x3AE, 0x3AF, 0x3B0, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C2, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x3CB, 0x3CC, 0x3CD, 0x3CE, 0 }; short _gdc_win_1254_mapping[128] = { 0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x2C6, 0x2030, 0x160, 0x2039, 0x152, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x2DC, 0x2122, 0x161, 0x203A, 0x153, 0, 0, 0x178, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0x11E, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0x130, 0x15E, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0x11F, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x131, 0x15F, 0xFF, }; short _gdc_win_1255_mapping[128] = { 0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x2C6, 0x2030, 0, 0x2039, 0, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x2DC, 0x2122, 0, 0x203A, 0, 0, 0, 0, 0xA0, 0xA1, 0xA2, 0xA3, 0x20AA, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xD7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xF7, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0x5B0, 0x5B1, 0x5B2, 0x5B3, 0x5B4, 0x5B5, 0x5B6, 0x5B7, 0x5B8, 0x5B9, 0, 0x5BB, 0x5BC, 0x5BD, 0x5BE, 0x5BF, 0x5C0, 0x5C1, 0x5C2, 0x5C3, 0x5F0, 0x5F1, 0x5F2, 0x5F3, 0x5F4, 0, 0, 0, 0, 0, 0, 0, 0x5D0, 0x5D1, 0x5D2, 0x5D3, 0x5D4, 0x5D5, 0x5D6, 0x5D7, 0x5D8, 0x5D9, 0x5DA, 0x5DB, 0x5DC, 0x5DD, 0x5DE, 0x5DF, 0x5E0, 0x5E1, 0x5E2, 0x5E3, 0x5E4, 0x5E5, 0x5E6, 0x5E7, 0x5E8, 0x5E9, 0x5EA, 0, 0, 0x200E, 0x200F, 0, }; short _gdc_win_1256_mapping[128] = { 0x20AC, 0x67E, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x2C6, 0x2030, 0x679, 0x2039, 0x152, 0x686, 0x698, 0x688, 0x6AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x6A9, 0x2122, 0x691, 0x203A, 0x153, 0x200C, 0x200D, 0x6BA, 0xA0, 0x60C, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0x6BE, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0x61B, 0xBB, 0xBC, 0xBD, 0xBE, 0x61F, 0x6C1, 0x621, 0x622, 0x623, 0x624, 0x625, 0x626, 0x627, 0x628, 0x629, 0x62A, 0x62B, 0x62C, 0x62D, 0x62E, 0x62F, 0x630, 0x631, 0x632, 0x633, 0x634, 0x635, 0x636, 0xD7, 0x637, 0x638, 0x639, 0x63A, 0x640, 0x641, 0x642, 0x643, 0xE0, 0x644, 0xE2, 0x645, 0x646, 0x647, 0x648, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0x649, 0x64A, 0xEE, 0xEF, 0x64B, 0x64C, 0x64D, 0x64E, 0xF4, 0x64F, 0x650, 0xF7, 0x651, 0xF9, 0x652, 0xFB, 0xFC, 0x200E, 0x200F, 0x6D2, }; short _gdc_win_1257_mapping[128] = { 0x20AC, 0, 0x201A, 0, 0x201E, 0x2026, 0x2020, 0x2021, 0, 0x2030, 0, 0x2039, 0, 0xA8, 0x2C7, 0xB8, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0, 0x2122, 0, 0x203A, 0, 0xAF, 0x2DB, 0, 0xA0, 0, 0xA2, 0xA3, 0xA4, 0, 0xA6, 0xA7, 0xD8, 0xA9, 0x156, 0xAB, 0xAC, 0xAD, 0xAE, 0xC6, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xF8, 0xB9, 0x157, 0xBB, 0xBC, 0xBD, 0xBE, 0xE6, 0x104, 0x12E, 0x100, 0x106, 0xC4, 0xC5, 0x118, 0x112, 0x10C, 0xC9, 0x179, 0x116, 0x122, 0x136, 0x12A, 0x13B, 0x160, 0x143, 0x145, 0xD3, 0x14C, 0xD5, 0xD6, 0xD7, 0x172, 0x141, 0x15A, 0x16A, 0xDC, 0x17B, 0x17D, 0xDF, 0x105, 0x12F, 0x101, 0x107, 0xE4, 0xE5, 0x119, 0x113, 0x10D, 0xE9, 0x17A, 0x117, 0x123, 0x137, 0x12B, 0x13C, 0x161, 0x144, 0x146, 0xF3, 0x14D, 0xF5, 0xF6, 0xF7, 0x173, 0x142, 0x15B, 0x16B, 0xFC, 0x17C, 0x17E, 0x2D9, }; short _gdc_win_1258_mapping[128] = { 0x20AC, 0, 0x201A, 0x192, 0x201E, 0x2026, 0x2020, 0x2021, 0x2C6, 0x2030, 0, 0x2039, 0x152, 0, 0, 0, 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 0x2DC, 0x2122, 0, 0x203A, 0x153, 0, 0, 0x178, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0x102, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0x300, 0xCD, 0xCE, 0xCF, 0x110, 0xD1, 0x309, 0xD3, 0xD4, 0x1A0, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0x1AF, 0x303, 0xDF, 0xE0, 0xE1, 0xE2, 0x103, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0x301, 0xED, 0xEE, 0xEF, 0x111, 0xF1, 0x323, 0xF3, 0xF4, 0x1A1, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0x1B0, 0x20AB, 0xFF, }; short _gdc_koi8r_mapping[128] = { 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2219, 0x221A, 0x2248, 0x2264, 0x2265, 0xA0, 0x2321, 0xB0, 0xB2, 0xB7, 0xF7, 0x2550, 0x2551, 0x2552, 0x451, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x255C, 0x255D, 0x255E, 0x255F, 0x2560, 0x2561, 0x401, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x256B, 0x256C, 0xA9, 0x44E, 0x430, 0x431, 0x446, 0x434, 0x435, 0x444, 0x433, 0x445, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x44F, 0x440, 0x441, 0x442, 0x443, 0x436, 0x432, 0x44C, 0x44B, 0x437, 0x448, 0x44D, 0x449, 0x447, 0x44A, 0x42E, 0x410, 0x411, 0x426, 0x414, 0x415, 0x424, 0x413, 0x425, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x42F, 0x420, 0x421, 0x422, 0x423, 0x416, 0x412, 0x42C, 0x42B, 0x417, 0x428, 0x42D, 0x429, 0x427, 0x42A }; short _gdc_koi8u_mapping[128] = { 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2019, 0x221A, 0x2248, 0x2264, 0x2265, 0xA0, 0x2321, 0xB0, 0xB2, 0xB7, 0xF7, 0x2550, 0x2551, 0x2552, 0x451, 0x454, 0x2554, 0x456, 0x457, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x491, 0x255D, 0x255E, 0x255F, 0x2560, 0x2561, 0x401, 0x404, 0x2563, 0x406, 0x407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x490, 0x256C, 0xA9, 0x44E, 0x430, 0x431, 0x446, 0x434, 0x435, 0x444, 0x433, 0x445, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x44F, 0x440, 0x441, 0x442, 0x443, 0x436, 0x432, 0x44C, 0x44B, 0x437, 0x448, 0x44D, 0x449, 0x447, 0x44A, 0x42E, 0x410, 0x411, 0x426, 0x414, 0x415, 0x424, 0x413, 0x425, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x42F, 0x420, 0x421, 0x422, 0x423, 0x416, 0x412, 0x42C, 0x42B, 0x417, 0x428, 0x42D, 0x429, 0x427, 0x42A }; short _gdc_koi8ru_mapping[128] = { 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x201C, 0x25A0, 0x2219, 0x201D, 0x2014, 0x2116, 0x2122, 0xA0, 0xBB, 0xAE, 0xAB, 0xB7, 0xA4, 0x2550, 0x2551, 0x2552, 0x451, 0x454, 0x2554, 0x456, 0x457, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x491, 0x45E, 0x255E, 0x255F, 0x2560, 0x2561, 0x401, 0x404, 0x2563, 0x406, 0x407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x490, 0x40E, 0xA9, 0x44E, 0x430, 0x431, 0x446, 0x434, 0x435, 0x444, 0x433, 0x445, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x44F, 0x440, 0x441, 0x442, 0x443, 0x436, 0x432, 0x44C, 0x44B, 0x437, 0x448, 0x44D, 0x449, 0x447, 0x44A, 0x42E, 0x410, 0x411, 0x426, 0x414, 0x415, 0x424, 0x413, 0x425, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x42F, 0x420, 0x421, 0x422, 0x423, 0x416, 0x412, 0x42C, 0x42B, 0x417, 0x428, 0x42D, 0x429, 0x427, 0x42A, }; #if defined WIN32 #define WinDef(d) d // Map the windows codepages to their real names. #define WINDOWS_1250 EASTEUROPE_CHARSET #define WINDOWS_1252 ANSI_CHARSET #define WINDOWS_1251 RUSSIAN_CHARSET #define WINDOWS_1253 GREEK_CHARSET #define WINDOWS_1254 TURKISH_CHARSET #define WINDOWS_1255 HEBREW_CHARSET #define WINDOWS_1256 ARABIC_CHARSET #define WINDOWS_1257 BALTIC_CHARSET #define WINDOWS_932 SHIFTJIS_CHARSET #define WINDOWS_936 GB2313_CHARSET #define WINDOWS_949 HANGEUL_CHARSET #define WINDOWS_950 CHINESEBIG5_CHARSET #else #define WinDef(d) 0 #endif LCharset::LCharset(const char *cp, const char *des, short *map, const char *alt) { Charset = cp; Description = des; UnicodeMap = map; IconvName = 0; AlternateNames = alt; Type = CpNone; if (cp) { if (stricmp(cp, "utf-8") == 0) { Type = CpUtf8; } else if (stricmp(cp, "utf-16") == 0) { Type = CpUtf16; } else if (stricmp(cp, "utf-32") == 0) { Type = CpUtf32; } else if (stricmp(cp, "ucs-2") == 0) { Type = CpUtf16; IconvName = "UCS-2-INTERNAL"; } else if (UnicodeMap) { Type = CpMapped; } #ifdef WIN32 else if (strnicmp(cp, "windows-", 8) == 0) { Type = CpWindowsDb; } #endif else { Type = CpIconv; } } } bool LCharset::IsUnicode() { return (Type == CpUtf8) || (Type == CpUtf16) || (Type == CpUtf32); } const char *LCharset::GetIconvName() { return IconvName ? IconvName : Charset; } bool LCharset::IsAvailable() { if (Type != CpIconv) return true; #ifndef LGI_STATIC LFontSystem *FontSys = LFontSystem::Inst(); if (FontSys) return FontSys->HasIconv(true); #endif // LGI_STATIC return false; } static LCharset LgiCharsets[] = { // Good 'ol ascii LCharset("us-ascii", "ASCII", _gdc_usascii_mapping, "ascii-us,iso-ir-6,ANSI_X3.4-1986,ISO_646.irv,ASCII,ISO646-US,us,IBM367,cp367,csASCII"), // Unicode (up here so they get found first) LCharset("utf-8", "Utf-8"), LCharset("utf-16", "Utf-16"), LCharset("utf-32", "Utf-32"), LCharset("ucs-2", "Ucs-2"), // ISO (get prefered over windows charsets by being first in this list) LCharset("iso-8859-1", "ISO 8859-1 (West Europe)", _gdc_ISO_8859_identity_mapping, "iso-ir-100,ISO_8859-1,latin1,l1,IBM819,CP819,csISOLatin1,iso8859-1"), LCharset("iso-8859-2", "ISO 8859-2 (East Europe)", _gdc_ISO_8859_2_mapping, "iso-ir-101,ISO_8859-2,latin2,l2,csISOLatin2"), LCharset("iso-8859-3", "ISO 8859-3 (Latin Script)", _gdc_ISO_8859_3_mapping, "iso-ir-109,ISO_8859-3,latin3,l3,csISOLatin3"), LCharset("iso-8859-4", "ISO 8859-4 (Baltic)", _gdc_ISO_8859_4_mapping, "iso-ir-110,ISO_8859-4,latin4,l4,csISOLatin4"), LCharset("iso-8859-5", "ISO 8859-5 (Russian)", _gdc_ISO_8859_5_mapping, "iso-ir-144,ISO_8859-5,cyrillic,csISOLatinCyrillic"), LCharset("iso-8859-6", "ISO 8859-6 (Arabic)", _gdc_ISO_8859_6_mapping, "iso-ir-127,ISO_8859-6,ECMA-114,ASMO-708,arabic,csISOLatinArabic"), LCharset("iso-8859-7", "ISO 8859-7 (Greek)", _gdc_ISO_8859_7_mapping, "iso-ir-126,ISO_8859-7,ELOT_928,ECMA-118,greek,greek8,csISOLatinGreek"), LCharset("iso-8859-8", "ISO 8859-8 (Hebrew)", _gdc_ISO_8859_8_mapping, "iso-ir-138,ISO_8859-8,hebrew,csISOLatinHebrew,iso-8859-8-i"), LCharset("iso-8859-9", "ISO 8859-9 (Turkish)", _gdc_ISO_8859_9_mapping, "iso-ir-148,ISO_8859-9,latin5,l5,csISOLatin5"), LCharset("iso-8859-13", "ISO 8859-13 (Baltik)", _gdc_ISO_8859_13_mapping, "ISO_8859-9"), LCharset("iso-8859-15", "ISO 8859-15 (Latic 9)", _gdc_ISO_8859_15_mapping, "ISO_8859-15"), // Windows LCharset("windows-874", "Windows 874 (Thai)", _gdc_win_874_mapping, "iso-8859-11,cp874"), LCharset("windows-932", "Windows 932 (Japanese)"), LCharset("windows-936", "Windows 936 (Chinese)"), LCharset("windows-949", "Windows 949 (Korean)"), LCharset("windows-950", "Windows 950 (Chinese)"), LCharset("windows-1250", "Windows 1250 (Latin 2)", _gdc_win_1250_mapping, "x-cp1250,cp1250"), LCharset("windows-1251", "Windows 1251 (Cyrillic)", _gdc_win_1251_mapping, "x-cp1251,cp1251"), LCharset("windows-1252", "Windows 1252 (Latin 1)", _gdc_win_1252_mapping, "x-cp1252,cp1252"), LCharset("windows-1253", "Windows 1253 (Greek)", _gdc_win_1253_mapping, "x-cp1253,cp1253"), LCharset("windows-1254", "Windows 1254 (Turkish)", _gdc_win_1254_mapping, "x-cp1254,cp1254"), LCharset("windows-1255", "Windows 1255 (Hebrew)", _gdc_win_1255_mapping, "x-cp1255,cp1255"), LCharset("windows-1256", "Windows 1256 (Arabic)", _gdc_win_1256_mapping, "x-cp1256,cp1256"), LCharset("windows-1257", "Windows 1257 (Baltic)", _gdc_win_1257_mapping, "x-cp1257,cp1257"), LCharset("windows-1258", "Windows 1258 (Veitnam)", _gdc_win_1258_mapping, "x-cp1258,cp1258"), // Russian LCharset("koi8-r", "KOI8-R", _gdc_koi8r_mapping, "csKOI8R"), LCharset("koi8-u", "KOI8-U", _gdc_koi8u_mapping, "csKOI8U"), LCharset("koi8-ru", "KOI8-RU", _gdc_koi8ru_mapping, "csKOI8RU"), LCharset("koi8-t", "KOI8-T (Tajik)"), // Codepages LCharset("cp850", "Cp850", 0, "IBM850,850,csPC850Multilingual"), LCharset("cp862", "Cp862", 0, "IBM862,862,csPC862LatinHebrew"), LCharset("cp866", "Cp866", 0, "IBM866,866,csIBM866"), LCharset("cp1133", "Cp1133 (Laotian)"), // Japanese LCharset("euc-jp", "EUC-JP", 0, "csEUCPkdFmtJapanese"), LCharset("shift_jis", "SHIFT_JIS", 0, "MS_Kanji,csShiftJIS"), LCharset("cp932", "cp932", 0, 0), LCharset("iso-2022-jp", "ISO-2022-JP", 0, "csISO2022JP"), LCharset("iso-2022-jp-1", "ISO-2022-JP-1"), LCharset("iso-2022-jp-2", "ISO-2022-JP-2", 0, "csISO2022JP2"), // Chinese LCharset("euc-cn", "EUC-CN (Chinese)"), LCharset("hz-gb-2312", "HZ (Chinese)", 0, "hz"), LCharset("gbk", "GBK (Chinese)", 0, "CP936,MS936,windows-936,x-gbk,gb2312,GB-2312,csGB2312,GB2312_CHARSET"), LCharset("gb18030", "GB18030 (Chinese)"), LCharset("euc-tw", "EUC-TW (Chinese)"), LCharset("big5", "BIG5 (Chinese)", 0, "csBig5"), LCharset("big5-hkscs", "BIG5-HKSCS (Chinese)"), // LCharset("gb2312", "GB-2312 (Chinese)", 0, "GB-2312,csGB2312"), LCharset("iso-2022-cn", "ISO-2022-CN (Chinese)"), LCharset("iso-2022-cn-eXT","ISO-2022-CN-EXT (Chinese)"), // Korean LCharset("euc-kr", "EUC-KR", 0, "csEUCKR"), LCharset("iso-2022-kr", "ISO-2022-KR", 0, "csISO2022KR"), LCharset("johab", "JOHAB"), LCharset("cp949", "CP949", 0, "ks_c_5601-1987,ks_c_5601"), // Armenian // LCharset("armscii-8", "ARMSCII-8 (Armenian)"), // Georgian LCharset("Georgian-Academy","Georgian-Academy"), LCharset("Georgian-PS", " Georgian-PS"), // Thai LCharset("tis-620", "TIS-620 (Thai)"), // Laotian LCharset("mulelao-1", "MuleLao-1"), // Vietnamese LCharset("viscii", "VISCII (Vietnamese)", 0, "csVISCII"), LCharset("tcvn", "TCVN (Vietnamese)"), // EOF marker LCharset() }; static LCharsetSystem CharsetSystem; LCharsetSystem *LCharsetSystem::Inst() { return &CharsetSystem; } LCharset *LGetCpInfo(const char *Cs) { return CharsetSystem.GetCsInfo(Cs); } ///////////////////////////////////////////////////////////////////////////// // Utf-16 conversion int LCpToAnsi(char *cp) { int Ansi = 0; if (cp && strnicmp(cp, "windows-", 8) == 0) { Ansi = atoi(cp+9); } return Ansi; } ssize_t LBufConvertCp(void *Out, const char *OutCp, ssize_t OutLen, const void *&In, const char *InCp, ssize_t &InLen) { int Status = 0; if (Out && OutCp && In && InCp) { LCharset *InInfo = LGetCpInfo(InCp); LCharset *OutInfo = LGetCpInfo(OutCp); if (InInfo && OutInfo) { char *In8 = (char*)In; uchar *Out8 = (uchar*)Out; if (InLen < 0) { switch (InInfo->Type) { case CpMapped: case CpUtf8: case CpIconv: InLen = (int)strlen((char*)In); break; case CpUtf16: case CpWindowsDb: InLen = StringLen((uint16*)In) << 1; break; case CpUtf32: InLen = StringLen((uint32_t*)In) << 2; break; default: LAssert(0); return 0; } } #ifdef WIN32 if (InInfo->Type == CpWindowsDb && OutInfo->Type == CpUtf16) { // mb -> unicode char Cp[32]; sprintf_s(Cp, sizeof(Cp), ".%s", InInfo->Charset + 8); setlocale(LC_ALL, Cp); void *Start = Out; while (OutLen >= sizeof(char16) && InLen > 0) { int s = mbtowc((char16*)Out, (char*)In, min(InLen, MB_CUR_MAX)); if (s > 0) { ((char*&)In) += s; InLen -= s; ((char16*&)Out)++; OutLen -= sizeof(char16); } else break; } return (NativeInt)Out-(NativeInt)Start; } else if (InInfo->Type == CpUtf16 && OutInfo->Type == CpWindowsDb) { // unicode -> mb char Cp[32]; sprintf_s(Cp, sizeof(Cp), ".%s", OutInfo->Charset + 8); setlocale(LC_ALL, Cp); void *Start = Out; while (OutLen >= MB_CUR_MAX && InLen > sizeof(char16) ) { #if 1 int s = 0; errno_t err = wctomb_s(&s, (char*)Out, OutLen, ((char16*)In)[0]); if (err || s == 0) break; #else int s = wctomb((char*)Out, ((char16*)In)[0] ); if (s > 0) #endif { ((char16*&)In)++; InLen -= sizeof(char16); ((char*&)Out) += s; OutLen -= s; } } return (NativeInt)Out-(NativeInt)Start; } else #endif if (InInfo->Type == CpIconv || OutInfo->Type == CpIconv) { #ifndef LGI_STATIC LFontSystem *Fs = LFontSystem::Inst(); if (Fs) return Fs->IconvConvert(OutInfo->GetIconvName(), (char*)Out, OutLen, InInfo->GetIconvName(), (const char*&)In, InLen); #else LAssert(!"No iconv in static build"); #endif } else { // Mapped or Utf conversion uint32_t Utf32 = 0; while (OutLen > 0 && InLen > 0) { char *RewindIn = In8; ptrdiff_t RewindInLen = InLen; // Convert input char to Utf-32 switch (InInfo->Type) { case CpMapped: { if (*In8) { uchar i = (uchar)*In8++; InLen--; if (i & 0x80) { Utf32 = InInfo->UnicodeMap[i - 0x80]; if (!Utf32) Utf32 = '?'; } else { Utf32 = i; } } else { Utf32 = 0; InLen = 0; } break; } case CpUtf8: { Utf32 = LgiUtf8To32((uint8_t *&)In8, InLen); break; } case CpUtf16: { Utf32 = LgiUtf16To32((const uint16_t *&)In8, InLen); if (Utf32 == 0xfeff || Utf32 == 0xfffe) continue; break; } case CpUtf32: { Utf32 = *((uint32_t*&)In8)++; InLen -= 4; break; } default: LAssert(0); break; } if (!Utf32) { break; } // Convert Utf-32 into output format switch (OutInfo->Type) { case CpMapped: { if (Utf32 & ~0x7f) { int n; for (n=0; n<128; n++) { if (OutInfo->UnicodeMap[n] == Utf32) { *Out8++ = 0x80 + n; break; } } if (n >= 128) { for (n=0; MissingMaps[n].Unicode; n++) { if (MissingMaps[n].Unicode == Utf32) { *Out8++ = MissingMaps[n].Ascii; break; } } if (!MissingMaps[n].Unicode) { *Out8++ = '?'; } } } else { *Out8++ = Utf32; } OutLen--; break; } case CpUtf8: { // uchar *PrevOut8 = Out8; if (!LgiUtf32To8(Utf32, (uint8_t*&) Out8, OutLen)) { // Not enough buffer to encode the character In8 = RewindIn; InLen = RewindInLen; OutLen = 0; } break; } case CpUtf16: { LgiUtf32To16(Utf32, (uint16_t*&)Out8, OutLen); break; } case CpUtf32: { *((uint32_t*&)Out8)++ = Utf32; OutLen -= 4; break; } default: { break; } } } In = (void*)In8; Status = (int) (Out8 - (uchar*)Out); } } else { // printf("%s:%i - LBufConvertCp failed '%s' -> '%s'.\n", __FILE__, __LINE__, InCp, OutCp); } } return Status; } template T *DupeString(T *s, ssize_t Len = -1) { if (!s) return NULL; if (Len < 0) { Len = 0; while (s[Len]) Len++; } T *ns = new T[Len+1]; if (!ns) return NULL; memcpy(ns, s, sizeof(T) * Len); ns[Len] = 0; return ns; } LString LStrConvertCp(const char *OutCp, const void *In, const char *InCp, ssize_t InLen) { if (!OutCp || !In || !InCp) return LString(); LCharset *InInfo = LGetCpInfo(InCp); LCharset *OutInfo = LGetCpInfo(OutCp); if (!InInfo || !OutInfo) return LString(); if (InLen < 0) { switch (InInfo->Type) { case CpMapped: case CpUtf8: case CpIconv: InLen = (int)strlen((char*)In); break; case CpUtf16: case CpWindowsDb: InLen = StringLen((uint16*)In) << 1; break; case CpUtf32: InLen = StringLen((uint32_t*)In) << 2; break; default: return LString(); } } switch (OutInfo->Type) { case CpMapped: case CpUtf8: case CpIconv: break; case CpUtf16: case CpWindowsDb: case CpUtf32: default: LAssert(!"LString doesn't >8bit char (yet)."); return LString(); } if (!stricmp(InCp, OutCp)) return LString((char*)In, InLen); LStringPipe b; if (InInfo->Type == CpIconv || OutInfo->Type == CpIconv) { #ifndef LGI_STATIC LFontSystem *Fs = LFontSystem::Inst(); if (Fs) { auto InCs = InInfo->GetIconvName(); auto OutCs = OutInfo->GetIconvName(); if (Fs->IconvConvert(OutCs, &b, InCs, (const char*&)In, InLen)) return b.NewGStr(); InCp = "iso-8859-1"; } #else LAssert(!"No inconv in static build"); #endif } char Buf[2 << 10]; while (InLen > 0) { ssize_t Bytes = LBufConvertCp(Buf, OutCp, sizeof(Buf), In, InCp, InLen); if (Bytes > 0) b.Write((uchar*)Buf, (int)Bytes); else break; } return b.NewGStr(); } void *LNewConvertCp(const char *OutCp, const void *In, const char *InCp, ssize_t InLen) { if (!OutCp || !In || !InCp) return NULL; LCharset *InInfo = LGetCpInfo(InCp); LCharset *OutInfo = LGetCpInfo(OutCp); if (!InInfo || !OutInfo) return NULL; LMemQueue b; if (InLen < 0) { switch (InInfo->Type) { case CpMapped: case CpUtf8: case CpIconv: InLen = (int)strlen((char*)In); break; case CpUtf16: case CpWindowsDb: InLen = StringLen((uint16*)In) << 1; break; case CpUtf32: InLen = StringLen((uint32_t*)In) << 2; break; default: LAssert(0); return NULL; } } int NullSize; switch (OutInfo->Type) { case CpMapped: case CpUtf8: case CpIconv: NullSize = 1; break; case CpUtf16: case CpWindowsDb: NullSize = 2; break; case CpUtf32: NullSize = 4; break; default: LAssert(0); return NULL; } if (!stricmp(InCp, OutCp)) { if (InInfo->Type == CpUtf16) return DupeString((uint16*)In, InLen/sizeof(uint16)); else if (InInfo->Type == CpUtf32) return DupeString((uint32_t*)In, InLen/sizeof(uint32_t)); else return NewStr((char*)In, InLen); } if (InInfo->Type == CpIconv || OutInfo->Type == CpIconv) { #ifndef LGI_STATIC LFontSystem *Fs = LFontSystem::Inst(); if (Fs) { const char *InCs = InInfo->GetIconvName(); const char *OutCs = OutInfo->GetIconvName(); if (!Fs->IconvConvert(OutCs, &b, InCs, (const char*&)In, InLen)) { InCp = "iso-8859-1"; goto BufConvert; } } #else LAssert(!"No inconv in static build"); #endif } else { BufConvert: char Buf[2 << 10]; while (InLen > 0) { ssize_t Bytes = LBufConvertCp(Buf, OutCp, sizeof(Buf), In, InCp, InLen); if (Bytes > 0) { b.Write((uchar*)Buf, (int)Bytes); } else { break; } } } return b.GetSize() ? b.New(NullSize) : 0; } int LCharLen(const void *Str, const char *Cp, int Bytes) { if (Str && Cp) { LCharset *InInfo = LGetCpInfo(Cp); if (InInfo) { switch (InInfo->Type) { default: case CpMapped: { return (int)strlen((char*)Str); } case CpUtf8: { uchar *s = (uchar*)Str; int Len = 0; if (Bytes > 0) { uchar *e = s + Bytes; while (*s && s < e) { LgiNextUtf8((char*&)s); Len++; } } else { while (*s) { LgiNextUtf8((char*&)s); Len++; } } return Len; } case CpUtf16: { return StringLen((uint16*)Str); } case CpUtf32: { return StringLen((uint32_t*)Str); } } } } return 0; } bool LIsCpImplemented(char *Cp) { return LGetCpInfo(Cp) != 0; } const char *LAnsiToLgiCp(int AnsiCodePage) { if (AnsiCodePage < 0) { #ifdef WIN32 AnsiCodePage = GetACP(); #else return "utf-8"; #endif } #define WinCp(i) case i: return "windows-" #i; switch (AnsiCodePage) { WinCp(874) WinCp(932) WinCp(936) WinCp(949) WinCp(950) WinCp(1250) WinCp(1251) WinCp(1252) WinCp(1253) WinCp(1254) WinCp(1255) WinCp(1256) WinCp(1257) WinCp(1258) case 20127: return "us-ascii"; case 28591: return "iso-8859-1"; case 28592: return "iso-8859-2"; case 28593: return "iso-8859-3"; case 28594: return "iso-8859-4"; case 28595: return "iso-8859-5"; case 28596: return "iso-8859-6"; case 28597: return "iso-8859-7"; case 28598: return "iso-8859-8"; case 28599: return "iso-8859-9"; case 28600: return "ISO-8859-10"; case 28605: return "ISO-8859-15"; case 50220: case 50221: return "iso-2022-jp"; case 51932: return "euc-jp"; case 51949: return "euc-kr"; case 65001: return "utf-8"; } #undef WinCp return 0; } char *LSeekUtf8(const char *Ptr, ssize_t D, char *Start) { uchar *p = (uchar*)Ptr; if (p) { if (D >= 0) { for (int i=0; i(uchar*)Start; i++) { p--; while (p>(uchar*)Start && IsUtf8_Trail(*p)) p--; } } else { // You must pass a start point to move backwards in // the utf-8 string, otherwise you can run off the // beginning of the array. LAssert(0); } } return (char*)p; } bool LMatchCharset(short *Map, char16 *Utf, bool &Has8Bit) { if (Map && Utf) { // Test Charset because we have a map of all the chars in it... char16 *c; for (c = Utf; *c; c++) { if (*c > 0x7f) { // Check char Has8Bit = true; int i; for (i=0; i<128; i++) { if (Map[i] == *c) break; } if (i >= 128) { // Char not found return false; } } } if (Has8Bit) { if (!*c) { return true; } } } return false; } const char *LUnicodeToCharset(const char *Utf8, ssize_t Len, List *Prefs) { const char *Status = "utf-8"; // The default.. LAutoWString Utf((char16*)LNewConvertCp(LGI_WideCharset, Utf8, "utf-8", Len)); if (Utf) { if (Prefs) { for (auto p: *Prefs) { LCharset *Cp = CharsetSystem.GetCsInfo(p); if (Cp && stricmp(Cp->Charset, "us-ascii") != 0 && Cp->UnicodeMap) { bool Has8Bit = false; if (LMatchCharset(Cp->UnicodeMap, Utf, Has8Bit)) { return Cp->Charset; } if (!Has8Bit) { return "us-ascii"; } } } } for (LCharset *Cp = LgiCharsets + 1; Cp->Charset; Cp++) { if (Cp->UnicodeMap) { bool Has8Bit = false; if (LMatchCharset(Cp->UnicodeMap, Utf, Has8Bit)) { return Cp->Charset; } if (!Has8Bit) { return "us-ascii"; } } } } return Status; } LString LToNativeCp(const char *In, ssize_t InLen) { const char *Cp = LAnsiToLgiCp(); LString s; #ifdef WIN32 LCharset *CpInfo = LGetCpInfo(Cp); if (!CpInfo || CpInfo->Type == CpWindowsDb) { if (In) { // Double byte charset conversion, don't rely on iconv // being around to do the conversion. setlocale(LC_ALL, ".ACP"); if (InLen < 0) InLen = strlen(In); LAutoWString Wide(Utf8ToWide(In, InLen)); if (Wide) { size_t Converted; auto Len = wcstombs_s(&Converted, NULL, 0, Wide, 0); if (s.Length(Len)) { wcstombs_s(&Converted, s.Get(), Len+1, Wide, Len+1); s.Get()[Len] = 0; } } } } #endif if (!s) s = LStrConvertCp(Cp, In, "utf-8", InLen); return s; } LString LFromNativeCp(const char *In, ssize_t InLen) { const char *Cp = LAnsiToLgiCp(); LString s; #ifdef WIN32 LCharset *CpInfo = LGetCpInfo(Cp); if (!CpInfo || CpInfo->Type == CpWindowsDb) { if (In) { // Double byte charset conversion, don't rely on iconv // being around to do the conversion. setlocale(LC_ALL, ".ACP"); if (InLen < 0) { #ifdef __GNUC__ // FIXME InLen = strlen(In); #else InLen = _mbstrlen(In); #endif } else { // Work out how many chars 'InLen' bytes is ssize_t Bytes = InLen; const char *i = In; int Chars = 0; while (*i && Bytes > 0) { int n = mblen(i, MB_CUR_MAX); if (n > 0) { Chars++; Bytes -= n; i += n; } else break; } InLen = Chars; } size_t Converted; size_t Len = mbstowcs_s(&Converted, NULL, 0, In, 0); if (Len) { LAutoWString Buf(new char16[Len+1]); if (Buf) { mbstowcs_s(&Converted, Buf, Len, In, Len); Buf[Len] = 0; s = Buf; } } } } #endif if (!s) s = LStrConvertCp("utf-8", In, Cp, InLen); return s; } /////////////////////////////////////////////////////////////////////////// struct LCharsetSystemPriv { LCharset *Utf8; LCharset *Utf16; LHashTbl, LCharset*> Charsets; LCharsetSystemPriv() : Charsets(512) { Utf8 = 0; Utf16 = 0; } }; LCharsetSystem::LCharsetSystem() { char l[256]; // Charset setup, store all the charset pointers // in a hash table for O(1) lookup. d = new LCharsetSystemPriv; LAssert(LgiCharsets->Charset != NULL); for (LCharset *Cs = LgiCharsets; Cs->Charset; Cs++) { strcpy_s(l, sizeof(l), Cs->Charset); #ifdef _MSC_VER _strlwr_s(l, sizeof(l)); #else strlwr(l); #endif if (!stricmp(l, "utf-8")) d->Utf8 = Cs; else if (!stricmp(l, "utf-16")) d->Utf16 = Cs; d->Charsets.Add(l, Cs); auto a = LString(Cs->AlternateNames).SplitDelimit(","); for (int n=0; nCharsets.Add(l, Cs); } } } LCharsetSystem::~LCharsetSystem() { DeleteObj(d); } LCharset *LCharsetSystem::GetCsInfo(const char *Cp) { if (Cp && d) { // Lookup the charset in the hash table char l[256]; strcpy_s(l, sizeof(l), Cp); #ifdef _MSC_VER _strlwr_s(l, sizeof(l)); #else strlwr(l); #endif if (!stricmp(l, "utf-8")) return d->Utf8; else if (!stricmp(l, "utf-16")) return d->Utf16; LCharset *Cs = (LCharset*) d->Charsets.Find(l); if (Cs) { return Cs; } else { // printf("%s:%i - No charset '%s' in font sub system.\n", __FILE__, __LINE__, l); // printf("Charsets=%i\n", Charsets->GetUsed()); } } return 0; } LCharset *LGetCsInfo(const char *Cs) { return CharsetSystem.GetCsInfo(Cs); } LCharset *LCharsetSystem::GetCsList() { return LgiCharsets; } LCharset *LGetCsList() { return LgiCharsets; } diff --git a/src/common/General/Containers.cpp b/src/common/General/Containers.cpp --- a/src/common/General/Containers.cpp +++ b/src/common/General/Containers.cpp @@ -1,835 +1,852 @@ /** * \file * \author Matthew Allen * \date 27/7/1995 * \brief Container classes.\n Copyright (C) 1995-2003, Matthew Allen */ #ifdef LINUX #define _ISOC99_SOURCE 1 #include #endif #include #include #include #include #include "lgi/common/Mem.h" #include "lgi/common/Containers.h" #include "lgi/common/LgiString.h" #include "lgi/common/LgiCommon.h" ///////////////////////////////////////////////////////////////////////////////////////////////// int ACmp(LString *a, LString *b) { return stricmp(*a, *b); } int LCmp(char *a, char *b, NativeInt d) { return stricmp(a, b); } void UnitTest_CreateList(LArray &a, List &l, int sz = 100) { a.Empty(); for (int i=0; i &a, List &l) { if (a.Length() != l.Length()) return UnitTest_Err("Wrong size."); int n = 0; for (auto s : l) { LString t = s; if (t.Equals(a[n])) ;//printf("%i: %s\n", n, s); else return UnitTest_Err("Wrong value."); n++; } return true; } #define CHECK(val) if (!(val)) return false bool UnitTest_ListClass() { LArray a; List l; // Create test.. UnitTest_CreateList(a, l, 100); CHECK(UnitTest_Check(a, l)); // Delete tests.. l.Delete(a[3].Get()); a.DeleteAt(3, true); CHECK(UnitTest_Check(a, l)); while (a.Length() > 60) { a.DeleteAt(60, true); l.DeleteAt(60); } CHECK(UnitTest_Check(a, l)); // Sort test.. a.Sort([](auto a, auto b) { return stricmp(*a, *b); }); l.Sort(LCmp); CHECK(UnitTest_Check(a, l)); return true; } //////////////////////////////////////////////////////////////////////////////////////// +#define Q_TRACE(...) if (_debug) LgiTrace(__VA_ARGS__) + LMemQueue::LMemQueue(size_t prealloc) { PreAlloc = prealloc; } LMemQueue::~LMemQueue() { Empty(); } +void LMemQueue::Block::Free() +{ + free(this); +} + LMemQueue &LMemQueue::operator=(LMemQueue &p) { Empty(); PreAlloc = p.PreAlloc; for (auto b: p.Mem) { int Alloc = sizeof(Block) + b->Size; Alloc = LGI_ALLOC_ALIGN(Alloc); Block *n = (Block*) malloc(Alloc); if (n) { memcpy(n, b, sizeof(Block) + b->Size); Mem.Insert(n); } else break; } return *this; } void LMemQueue::Empty() { for (auto b: Mem) - free(b); + { + Q_TRACE("Empty() b=%p\n", b); + b->Free(); + } Mem.Empty(); } int64 LMemQueue::GetSize() { int64 Size = 0; for (auto b: Mem) - Size += b->Used - b->Next; + Size += b->Length(); return Size; } -int64 LMemQueue::Peek(uchar *Ptr, int Size) +int64 LMemQueue::Peek(uchar *Ptr, ssize_t Size) { int64 Status = 0; if (Ptr && Size <= GetSize()) { for (auto b: Mem) { if (Size <= 0) break; - int Copy = MIN(Size, b->Size - b->Next); + auto Copy = MIN(Size, b->Length()); if (Copy > 0) { - memcpy(Ptr, b->Ptr() + b->Next, Copy); + memcpy(Ptr, b->Start(), Copy); + Ptr += Copy; Size -= Copy; Status += Copy; } } } return Status; } -void *LMemQueue::New(int AddBytes) +void *LMemQueue::New(ssize_t AddBytes) { - int64 Len = GetSize(); - uchar *Data = Len > 0 ? new uchar[Len+AddBytes] : 0; + auto Len = GetSize(); + uchar *Data = Len > 0 ? new uchar[Len + AddBytes] : NULL; if (Data) { ssize_t Rd = Read(Data, Len); if (Rd >= 0 && AddBytes) { - memset(Data+Len, 0, AddBytes); + memset(Data + Len, 0, AddBytes); } } return Data; } ssize_t LMemQueue::Read(void *Ptr, ssize_t Size, int Flags) { - int Status = 0; + ssize_t Status = 0; if (Ptr && Size > 0) { for (auto b: Mem) { if (Size <= 0) break; - int Copy = (int) MIN(Size, b->Used - b->Next); + auto Copy = MIN(Size, b->Length()); + // Q_TRACE("Read, copy=%i\n", (int)Copy); if (Copy > 0) { - memcpy(Ptr, b->Ptr() + b->Next, Copy); + memcpy(Ptr, b->Start(), Copy); ((uchar*&)Ptr) += Copy; Size -= Copy; - b->Next += Copy; + b->Next += (int)Copy; Status += Copy; } } while (Mem.Length() > 0) { auto it = Mem.begin(); auto b = *it; if (b->Next < b->Used) break; Mem.Delete(b); - free(b); + // Q_TRACE("Read, free=%p\n", b); + b->Free(); } } return Status; } -ssize_t LMemQueue::Write(const void *Ptr, ssize_t Size, int Flags) +ssize_t LMemQueue::Write(const void *_Ptr, ssize_t Size, int Flags) { ssize_t Status = 0; + auto Ptr = (const uint8_t *)_Ptr; - if (Ptr && Size > 0) + if (!Ptr || Size <= 0) + return Status; + + // Is there any space in the last block? + auto it = Mem.rbegin(); + Block *Last = *it; + if (Last) { - if (PreAlloc > 0) + auto Len = MIN(Size, Last->Size - Last->Used); + Q_TRACE("Write, last.len=%i\n", (int)Len); + if (Len > 0) { - auto it = Mem.rbegin(); - Block *Last = *it; - if (Last) - { - int Len = (int) MIN(Size, Last->Size - Last->Used); - if (Len > 0) - { - memcpy(Last->Ptr() + Last->Used, Ptr, Len); - Last->Used += Len; - Size -= Len; - ((uchar*&)Ptr) += Len; - Status += Len; - } - } + memcpy(Last->Ptr() + Last->Used, Ptr, Len); + Last->Used += (int)Len; + LAssert(Last->Used <= Last->Size); + Size -= Len; + Ptr += Len; + Status += Len; } + } - if (Size > 0) + if (Size > 0) + { + ssize_t Bytes = MAX(PreAlloc, Size); + ssize_t Alloc = sizeof(Block) + Bytes; + Alloc = LGI_ALLOC_ALIGN(Alloc); + Block *b = (Block*) malloc(Alloc); + if (b) { - ssize_t Bytes = MAX(PreAlloc, Size); - ssize_t Alloc = sizeof(Block) + Bytes; - Alloc = LGI_ALLOC_ALIGN(Alloc); - Block *b = (Block*) malloc(Alloc); - if (b) - { - void *p = b->Ptr(); - memcpy(p, Ptr, Size); - b->Size = (int)Bytes; - b->Used = (int)Size; - b->Next = 0; - Mem.Insert(b); - Status += Size; - } + b->Size = (int)Bytes; + b->Used = (int)Size; + b->Next = 0; + Q_TRACE("Write, ptr=%p size=%i\n", b, (int)Size); + memcpy(b->Ptr(), Ptr, Size); + Mem.Insert(b); + Status += Size; } } return Status; } ssize_t LMemQueue::Find(LString str, bool caseSensitive) { if (!str.Get()) return -1; if (!caseSensitive) str = str.Lower(); ssize_t pos = 0; char *s = str.Get(); ssize_t si = 0; for (auto b: Mem) { auto p = b->Ptr() + b->Next; auto e = b->Ptr() + b->Used; while (p < e) { if ( (caseSensitive && (s[si] == *p)) || (!caseSensitive && (s[si] == ToLower(*p))) ) { si++; if (si == str.Length()) return pos - si + 1; } else si = 0; p++; pos++; } } return -1; } void LMemQueue::Iterate(std::function callback, bool reverse) { if (!callback) { LAssert(!"No callback."); return; } if (reverse) { for (auto it = Mem.rbegin(); it >= Mem.begin(); --it) { - auto p = (*it)->Ptr(); - if (!callback(p, (*it)->Used)) + auto b = (*it); + if (!callback(b->Start(), b->Length())) break; } } else { for (auto b: Mem) { - auto p = b->Ptr(); - if (!callback(p, b->Used)) + Q_TRACE("Iter b=%p, blk:%p,%i,%i,%i, start=%p, len=%i\n", + b, b->Ptr(), b->Next, b->Used, b->Size, + b->Start(), b->Length()); + if (!callback(b->Start(), b->Length())) break; } } } LMemQueue::Buffer LMemQueue::GetBuffer() { Buffer b(this); if (Mem.Length()) { // Check if last block has space left: auto it = Mem.rbegin(); b.blk = *it; if (b.blk->Used < b.blk->Size) { b.ptr = b.blk->Ptr() + b.blk->Used; b.len = b.blk->Size - b.blk->Used; #ifdef _DEBUG memset(b.ptr, 0xcd, b.len); #endif return b; } } // Otherwise allocate a block auto sz = PreAlloc > 0 ? PreAlloc : 1024/* some reasonable default? */; auto alloc = sizeof(Block) + sz; alloc = LGI_ALLOC_ALIGN(alloc); b.blk = (Block*) malloc(alloc); if (b.blk) { b.blk->Next = 0; b.blk->Used = 0; b.blk->Size = (int)sz; b.ptr = b.blk->Ptr(); b.len = sz; #ifdef _DEBUG memset(b.ptr, 0xcd, b.len); #endif Mem.Insert(b.blk); } return b; } bool LMemQueue::Buffer::Commit(size_t bytes) { if (!blk || !ptr) { LAssert(!"Invalid param."); return false; } if (blk->Used + bytes > blk->Size) { LAssert(!"Wrote too much data?"); blk->Used = blk->Size; return false; } blk->Used += (int)bytes; return true; } ////////////////////////////////////////////////////////////////////////// LString LStringPipe::NewGStr() { LString s; int64 Sz = GetSize(); if (Sz > 0) { if (s.Length(Sz)) { ssize_t Rd = Read(s.Get(), s.Length()); if (Rd > 0 && Rd <= Sz) { s.Get()[Rd] = 0; } } else s.Empty(); } return s; } ssize_t LStringPipe::LineChars() { ssize_t Len = -1; for (auto m: Mem) { uint8_t *p = m->Ptr(); for (int i = m->Next; i < m->Used; i++) { Len++; if (p[i] == '\n') return Len; } } return Len; } ssize_t LStringPipe::SaveToBuffer(char *Start, ssize_t BufSize, ssize_t Chars) { char *Str = Start; char *End = Str + BufSize; // Not including NULL auto it = Mem.begin(); Block *m = *it; while (m) { for ( char *MPtr = (char*)m->Ptr(); m->Next < m->Used; m->Next++) { if (Str < End) *Str++ = MPtr[m->Next]; if (MPtr[m->Next] == '\n') { m->Next++; goto EndPop; } } if (m->Next >= m->Used) { Mem.Delete(it); - free(m); + Q_TRACE("SaveToBuffer, free=%p\n", m); + m->Free(); it = Mem.begin(); m = *it; } } EndPop: *Str = 0; return Str - Start; } ssize_t LStringPipe::Pop(LArray &Buf) { ssize_t Chars = LineChars(); if (Chars < 0) return 0; Chars++; // For the '\n' if ((ssize_t)Buf.Length() < Chars + 1) if (!Buf.Length(Chars + 1)) return -1; SaveToBuffer(Buf.AddressOf(), Chars, Chars); return Chars; } LString LStringPipe::Pop() { LString s; ssize_t Chars = LineChars(); if (Chars < 0) return s; s.Length(Chars); SaveToBuffer(s.Get(), Chars, Chars); return s; } ssize_t LStringPipe::Pop(char *Str, ssize_t BufSize) { if (!Str) return 0; ssize_t Chars = LineChars(); if (Chars < 0) return 0; return SaveToBuffer(Str, BufSize-1 /* for the NULL */, Chars); } ssize_t LStringPipe::Push(const char *Str, ssize_t Chars) { if (!Str) return 0; if (Chars < 0) Chars = strlen(Str); return Write((void*)Str, Chars); } ssize_t LStringPipe::Push(const char16 *Str, ssize_t Chars) { if (!Str) return 0; if (Chars < 0) Chars = StrlenW(Str); return Write((void*)Str, Chars * sizeof(char16)); } #ifdef _DEBUG bool LStringPipe::UnitTest() { char Buf[16]; memset(Buf, 0x1, sizeof(Buf)); LStringPipe p(8); const char s[] = "1234567890abc\n" "abcdefghijklmn\n"; p.Write(s, sizeof(s)-1); ssize_t rd = p.Pop(Buf, 10); int cmp = memcmp(Buf, "123456789\x00\x01\x01\x01\x01\x01\x01", 16); if (cmp) return false; rd = p.Pop(Buf, 10); cmp = memcmp(Buf, "abcdefghi\x00\x01\x01\x01\x01\x01\x01", 16); if (cmp) return false; p.Empty(); p.Write(s, sizeof(s)-1); LString r; r = p.Pop(); if (!r.Equals("1234567890abc")) return false; r = p.Pop(); if (!r.Equals("abcdefghijklmn")) return false; return true; } #endif ////////////////////////////////////////////////////////////////////////////////////////////////// -GMemFile::GMemFile(int BlkSize) +LMemFile::LMemFile(int BlkSize) { CurPos = 0; Blocks = 0; BlockSize = BlkSize < 16 ? 16 : BlkSize; ZeroObj(Local); } -GMemFile::~GMemFile() +LMemFile::~LMemFile() { Empty(); } -GMemFile::Block *GMemFile::Get(int Index) +LMemFile::Block *LMemFile::Get(int Index) { if (Blocks == 0 || Index < 0) return NULL; - if (Index < GMEMFILE_BLOCKS) + if (Index < LMEMFILE_BLOCKS) return Local[Index]; - if (Index - GMEMFILE_BLOCKS >= Extra.Length()) + if (Index - LMEMFILE_BLOCKS >= Extra.Length()) { LAssert(!"Index beyond last block"); return NULL; } - return Extra[Index - GMEMFILE_BLOCKS]; + return Extra[Index - LMEMFILE_BLOCKS]; } -GMemFile::Block *GMemFile::Create() +LMemFile::Block *LMemFile::Create() { int Alloc = sizeof(Block) + BlockSize - 1; Alloc = LGI_ALLOC_ALIGN(Alloc); Block *b = (Block*) malloc(Alloc); if (!b) return NULL; b->Used = 0; b->Offset = Blocks * BlockSize; - if (Blocks < GMEMFILE_BLOCKS) + if (Blocks < LMEMFILE_BLOCKS) Local[Blocks] = b; else Extra.Add(b); Blocks++; return b; } -void GMemFile::Empty() +void LMemFile::Empty() { CurPos = 0; - for (int i=0; iUsed; } -bool GMemFile::FreeBlock(Block *b) +bool LMemFile::FreeBlock(Block *b) { if (!b) return false; ssize_t Idx = b->Offset / BlockSize; - if (Idx < GMEMFILE_BLOCKS) + if (Idx < LMEMFILE_BLOCKS) { // Local block if (Local[Idx] != b || Idx < Blocks-1) { LAssert(!"Block index error."); return false; } free(b); Local[Idx] = NULL; Blocks--; return true; } // Extra block - ssize_t Off = Idx - GMEMFILE_BLOCKS; + ssize_t Off = Idx - LMEMFILE_BLOCKS; if (Off != Extra.Length() - 1) { LAssert(!"Block index error."); return false; } free(b); Extra.DeleteAt(Off, true); Blocks--; return true; } -int64 GMemFile::SetSize(int64 Size) +int64 LMemFile::SetSize(int64 Size) { if (Size <= 0) { Empty(); } else { int64 CurSize = GetSize(); if (Size > CurSize) { // Increase size... int64 Diff = Size - CurSize; Block *b = GetLast(); if (b->Used < BlockSize) { // Add size to last incomplete block ssize_t Remaining = BlockSize - b->Used; ssize_t Add = MIN(Diff, Remaining); b->Used += Add; Diff -= Add; } while (Diff > 0) { // Add new blocks to cover the size... ssize_t Add = MIN(BlockSize, Diff); b = Create(); b->Used = Add; Diff -= Add; } } else { // Decrease size... uint64 Diff = CurSize - Size; while (Diff > 0 && Blocks > 0) { Block *b = GetLast(); if (!b) break; ssize_t Sub = MIN(b->Used, Diff); b->Used -= Sub; Diff -= Sub; if (b->Used == 0) FreeBlock(b); } } } return GetSize(); } -int64 GMemFile::GetPos() +int64 LMemFile::GetPos() { return CurPos; } -int64 GMemFile::SetPos(int64 Pos) +int64 LMemFile::SetPos(int64 Pos) { if (Pos <= 0) return CurPos = 0; // Off the start of the structure ssize_t BlockIndex = Pos / BlockSize; if (BlockIndex >= Blocks) return CurPos = GetSize(); // Off the end of the structure if (BlockIndex >= 0 && BlockIndex < Blocks - 1) return CurPos = Pos; // Inside a full block Block *Last = GetLast(); uint64 Offset = Pos - Last->Offset; if (Offset >= Last->Used) return CurPos = Last->Offset + Last->Used; // End of last block return CurPos = Pos; // Inside the last block } -ssize_t GMemFile::Read(void *Ptr, ssize_t Size, int Flags) +ssize_t LMemFile::Read(void *Ptr, ssize_t Size, int Flags) { if (!Ptr || Size < 1) return 0; uint8_t *p = (uint8_t*) Ptr; uint8_t *end = p + Size; while (p < end) { int Cur = CurBlock(); if (Cur >= Blocks) break; Block *b = Get(Cur); // Where are we in the current block? ssize_t BlkOffset = CurPos - b->Offset; LAssert(b && BlkOffset >= 0 && BlkOffset <= (ssize_t)b->Used); ssize_t Remaining = b->Used - BlkOffset; if (Remaining > 0) { ssize_t Common = MIN(Remaining, end - p); memcpy(p, b->Data + BlkOffset, Common); CurPos += Common; p += Common; } else break; LAssert(p <= end); if (p >= end) // End of read buffer reached? break; // Exit loop } return p - (uint8_t*) Ptr; } -ssize_t GMemFile::Write(const void *Ptr, ssize_t Size, int Flags) +ssize_t LMemFile::Write(const void *Ptr, ssize_t Size, int Flags) { if (!Ptr || Size < 1) return 0; uint8_t *p = (uint8_t*) Ptr; ssize_t len = Size; Block *b = GetLast(); if (b && b->Used < BlockSize) { // Any more space in the last block? ssize_t Remaining = BlockSize - b->Used; ssize_t Common = MIN(Remaining, Size); if (Common > 0) { memcpy(b->Data + b->Used, p, Common); p += Common; len -= Common; b->Used += Common; } } // Store remaining data into new blocks while (len > 0) { b = Create(); if (!b) break; ssize_t Common = MIN(BlockSize, len); memcpy(b->Data, p, Common); b->Used = Common; p += Common; len -= Common; } return Size - len; } diff --git a/src/common/General/DateTime.cpp b/src/common/General/DateTime.cpp --- a/src/common/General/DateTime.cpp +++ b/src/common/General/DateTime.cpp @@ -1,2187 +1,2187 @@ /* ** FILE: LDateTime.cpp ** AUTHOR: Matthew Allen ** DATE: 11/11/98 ** DESCRIPTION: Scribe Date Time Object ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #define _DEFAULT_SOURCE #include #include #include #include #include #if defined(MAC) #include #endif #ifdef WINDOWS #include #endif #include "lgi/common/Lgi.h" #include "lgi/common/DateTime.h" #include "lgi/common/DocView.h" #if !defined(WINDOWS) constexpr const char *LDateTime::WeekdaysShort[]; constexpr const char *LDateTime::WeekdaysLong[]; constexpr const char *LDateTime::MonthsShort[]; constexpr const char *LDateTime::MonthsLong[]; #define MIN_YEAR 1800 #endif ////////////////////////////////////////////////////////////////////////////// uint16 LDateTime::DefaultFormat = GDTF_DEFAULT; char LDateTime::DefaultSeparator = '/'; uint16 LDateTime::GetDefaultFormat() { if (DefaultFormat == GDTF_DEFAULT) { #ifdef WIN32 TCHAR s[80] = _T("1"); GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDATE, s, CountOf(s)); switch (_tstoi(s)) { case 0: DefaultFormat = GDTF_MONTH_DAY_YEAR; break; default: case 1: DefaultFormat = GDTF_DAY_MONTH_YEAR; break; case 2: DefaultFormat = GDTF_YEAR_MONTH_DAY; break; } GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, s, sizeof(s)); if (_tstoi(s) == 1) { DefaultFormat |= GDTF_24HOUR; } else { DefaultFormat |= GDTF_12HOUR; } if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDATE, s, sizeof(s))) DefaultSeparator = (char)s[0]; if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, s, sizeof(s))) { char Sep[] = { DefaultSeparator, '/', '\\', '-', '.', 0 }; LString Str = s; auto t = Str.SplitDelimit(Sep); for (int i=0; i= low && (v) <= high) bool LDateTime::IsValid() const { return InRange(_Day, 1, 31) && InRange(_Year, 1600, 2100) && InRange(_Thousands, 0, 999) && InRange(_Month, 1, 12) && InRange(_Seconds, 0, 59) && InRange(_Minutes, 0, 59) && InRange(_Hours, 0, 23) && - InRange(_Tz, -720, 720); + InRange(_Tz, -780, 780); } void LDateTime::SetTimeZone(int NewTz, bool ConvertTime) { if (ConvertTime && NewTz != _Tz) { // printf("SetTimeZone: %i\n", NewTz - _Tz); AddMinutes(NewTz - _Tz); } _Tz = NewTz; } int LDateTime::SystemTimeZone(bool ForceUpdate) { if (ForceUpdate || CurTz == NO_ZONE) { CurTz = 0; CurTzOff = 0; #ifdef MAC #ifdef LGI_COCOA NSTimeZone *timeZone = [NSTimeZone localTimeZone]; if (timeZone) { NSDate *Now = [NSDate date]; CurTz = (int) [timeZone secondsFromGMTForDate:Now] / 60; CurTzOff = [timeZone daylightSavingTimeOffsetForDate:Now] / 60; CurTz -= CurTzOff; } #elif defined LGI_CARBON CFTimeZoneRef tz = CFTimeZoneCopySystem(); CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); Boolean dst = CFTimeZoneIsDaylightSavingTime(tz, now); if (dst) { CFAbsoluteTime next = CFTimeZoneGetNextDaylightSavingTimeTransition(tz, now); CurTz = CFTimeZoneGetSecondsFromGMT(tz, next + 100) / 60; } else { CurTz = CFTimeZoneGetSecondsFromGMT(tz, now) / 60; } CurTzOff = CFTimeZoneGetDaylightSavingTimeOffset(tz, now) / 60; CFRelease(tz); #endif #elif defined(WIN32) timeb tbTime; ftime(&tbTime); CurTz = -tbTime.timezone; TIME_ZONE_INFORMATION Tzi; if (GetTimeZoneInformation(&Tzi) == TIME_ZONE_ID_DAYLIGHT) CurTzOff = -Tzi.DaylightBias; #elif defined(LINUX) || defined(HAIKU) int six_months = (365 * 24 * 60 * 60) / 2; time_t now = 0, then = 0; time (&now); then = now - six_months; tm now_tz, then_tz; tm *t = localtime_r(&now, &now_tz); if (t) { localtime_r(&then, &then_tz); CurTz = now_tz.tm_gmtoff / 60; if (now_tz.tm_isdst) { CurTzOff = (now_tz.tm_gmtoff - then_tz.tm_gmtoff) / 60; CurTz = then_tz.tm_gmtoff / 60; } else // This is not DST so there is no offset right? CurTzOff = 0; // (then_tz.tm_gmtoff - now_tz.tm_gmtoff) / 60; } else return NO_ZONE; #else #error "Impl me." #endif } return CurTz + CurTzOff; } int LDateTime::SystemTimeZoneOffset() { if (CurTz == NO_ZONE) SystemTimeZone(); return CurTzOff; } #if defined WIN32 LDateTime ConvertSysTime(SYSTEMTIME &st, int year) { LDateTime n; if (st.wYear) { n.Year(st.wYear); n.Month(st.wMonth); n.Day(st.wDay); } else { n.Year(year); n.Month(st.wMonth); // Find the 'nth' matching weekday, starting from the first day in the month n.Day(1); LDateTime c = n; for (int i=0; iCompare(b); } #elif defined(LINUX) static bool ParseValue(char *s, LAutoString &var, LAutoString &val) { if (!s) return false; char *e = strchr(s, '='); if (!e) return false; *e++ = 0; var.Reset(NewStr(s)); val.Reset(NewStr(e)); *e = '='; return var != 0 && val != 0; } #endif /* Testing code... LDateTime Start, End; LArray Info; Start.Set("1/1/2010"); End.Set("31/12/2014"); LDateTime::GetDaylightSavingsInfo(Info, Start, &End); LStringPipe p; for (int i=0; i,int> { MonthHash() { for (int i=0; i &Info, LDateTime &Start, LDateTime *End) { bool Status = false; #if defined(WIN32) TIME_ZONE_INFORMATION Tzi; auto r = GetTimeZoneInformation(&Tzi); if (r > TIME_ZONE_ID_UNKNOWN) { Info.Length(0); // Find the dates for the previous year from Start. This allows // us to cover the start of the current year. LDateTime s = ConvertSysTime(Tzi.StandardDate, Start.Year() - 1); LDateTime d = ConvertSysTime(Tzi.DaylightDate, Start.Year() - 1); // Create initial Info entry, as the last change in the previous year auto *i = &Info.New(); if (s < d) { // Year is: Daylight->Standard->Daylight LDateTime tmp = d; i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); } else { // Year is: Standard->Daylight->Standard LDateTime tmp = s; i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts();; } for (auto y=Start.Year(); y<=(End?End->Year():Start.Year()); y++) { if (s < d) { // Cur year, first event: end of DST i = &Info.New(); auto tmp = ConvertSysTime(Tzi.StandardDate, y); i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); // Cur year, second event: start of DST i = &Info.New(); tmp = ConvertSysTime(Tzi.DaylightDate, y); i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); } else { // Cur year, first event: start of DST i = &Info.New(); auto tmp = ConvertSysTime(Tzi.DaylightDate, Start.Year()); i->Offset = -(Tzi.Bias + Tzi.DaylightBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); // Cur year, second event: end of DST i = &Info.New(); tmp = ConvertSysTime(Tzi.StandardDate, Start.Year()); i->Offset = -(Tzi.Bias + Tzi.StandardBias); tmp.AddMinutes(-i->Offset); i->UtcTimeStamp = tmp.Ts(); } } Status = true; } #elif defined(MAC) LDateTime Before = Start; Before.AddMonths(-6); NSTimeZone *tz = [NSTimeZone systemTimeZone]; NSDate *startDate = [[NSDate alloc] initWithTimeIntervalSince1970:(Before.Ts() / Second64Bit) - Offset1800]; for (int n=0; n<6; n++) { NSDate *next = [tz nextDaylightSavingTimeTransitionAfterDate:startDate]; auto &i = Info.New(); i.UtcTimeStamp = ([next timeIntervalSince1970] + Offset1800) * Second64Bit; i.Offset = (int)([tz secondsFromGMTForDate:[next dateByAddingTimeInterval:60]]/60); #if DEBUG_DST_INFO { LDateTime dt; dt.Set(i.UtcTimeStamp); LgiTrace("%s:%i - Ts=%s Off=%i\n", _FL, dt.Get().Get(), i.Offset); } #endif [startDate release]; startDate = next; } #elif defined(LINUX) if (!Zdump.Length()) { FILE *f = popen("zdump -v /etc/localtime", "r"); if (f) { char s[256]; size_t r; LStringPipe p(1024); while ((r = fread(s, 1, sizeof(s), f)) > 0) { p.Write(s, (int)r); } fclose(f); LString ps = p.NewGStr(); Zdump = ps.Split("\n"); } } MonthHash Lut; LDateTime Prev; int PrevOff = 0; for (auto Line: Zdump) { auto l = Line.SplitDelimit(" \t"); if (l.Length() >= 16 && l[0].Equals("/etc/localtime")) { // /etc/localtime Sat Oct 3 15:59:59 2037 UTC = Sun Oct 4 01:59:59 2037 EST isdst=0 gmtoff=36000 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #if DEBUG_DST_INFO printf("DST: %s\n", Line); #endif LDateTime Utc; Utc.Year(l[5].Int()); auto Tm = l[4].SplitDelimit(":"); if (Tm.Length() != 3) { #if DEBUG_DST_INFO printf("%s:%i - Tm '%s' has wrong parts: %s\n", _FL, l[4], Line); #endif continue; } Utc.Hours(Tm[0].Int()); Utc.Minutes(Tm[1].Int()); Utc.Seconds(Tm[2].Int()); if (Utc.Minutes() < 0) { #if DEBUG_DST_INFO printf("%s:%i - Mins is zero: %s\n", _FL, l[4]); #endif continue; } int m = Lut.Find(l[2]); if (!m) { #if DEBUG_DST_INFO printf("%s:%i - Unknown month '%s'\n", _FL, l[2]); #endif continue; } Utc.Day(l[3].Int()); Utc.Month(m); LAutoString Var, Val; if (!ParseValue(l[14], Var, Val) || stricmp(Var, "isdst")) { #if DEBUG_DST_INFO printf("%s:%i - Unknown value for isdst\n", _FL); #endif continue; } if (!ParseValue(l[15], Var, Val) || stricmp(Var, "gmtoff")) { #if DEBUG_DST_INFO printf("%s:%i - Unknown value for isdst\n", _FL); #endif continue; } int Off = atoi(Val) / 60; if (Utc.Ts() == 0) continue; if (Prev.Year() && Prev < Start && Start < Utc) { // Emit initial entry for 'start' auto &inf = Info.New(); inf.UtcTimeStamp = Prev; inf.Offset = PrevOff; #if DEBUG_DST_INFO printf("Info: Start=%s %i\n", Prev.Get().Get(), inf.Offset); #endif } if (Utc > Start) { // Emit furthur entries for DST events between start and end. auto &inf = Info.New(); inf.UtcTimeStamp = Utc; inf.Offset = Off; #if DEBUG_DST_INFO printf("Info: Next=%s %i\n", Utc.Get().Get(), inf.Offset); #endif if (End && Utc > *End) { // printf("Utc after end: %s > %s\n", Utc.Get().Get(), End->Get().Get()); break; } } Prev = Utc; PrevOff = Off; } } Status = Info.Length() > 1; #else LAssert(!"Not implemented."); #endif return Status; } bool LDateTime::DstToLocal(LArray &Dst, LDateTime &dt) { if (dt.GetTimeZone()) { LAssert(!"Should be a UTC date."); return true; } // LgiTrace("DstToLocal: %s\n", dt.Get().Get()); LAssert(Dst.Length() > 1); // Needs to have at least 2 entries...? for (size_t i=0; i %s\n", (int)i, start.Get().Get(), end.Get().Get()); if (dt >= start && dt < end) { dt.SetTimeZone(a.Offset, true); return true; } } auto Last = Dst.Last(); LDateTime d; d.Set(Last.UtcTimeStamp); if (dt >= d && dt.Year() == d.Year()) { // If it's after the last DST change but in the same year... it's ok... // Just use the last offset. dt.SetTimeZone(Last.Offset, true); return true; } LAssert(!"No valid DST range for this date."); return false; } int LDateTime::DayOfWeek() const { int Index = 0; int Day = IsLeapYear() ? 29 : 28; switch (_Year / 100) { case 19: { Index = 3; break; } case 20: { Index = 2; break; } } // get year right int y = _Year % 100; int r = y % 12; Index = (Index + (y / 12) + r + (r / 4)) % 7; // get month right if (_Month % 2 == 0) { // even month if (_Month > 2) Day = _Month; } else { // odd month switch (_Month) { case 1: { Day = 31; if (IsLeapYear()) { Index = Index > 0 ? Index - 1 : Index + 6; } break; } case 11: case 3: { Day = 7; break; } case 5: { Day = 9; break; } case 7: { Day = 11; break; } case 9: { Day = 5; break; } } } // get day right int Diff = Index - (Day - _Day); while (Diff < 0) Diff += 7; return Diff % 7; } LDateTime LDateTime::Now() { LDateTime dt; dt.SetNow(); return dt; } LDateTime &LDateTime::SetNow() { #ifdef WIN32 SYSTEMTIME stNow; FILETIME ftNow; GetSystemTime(&stNow); SystemTimeToFileTime(&stNow, &ftNow); uint64 i64 = ((uint64)ftNow.dwHighDateTime << 32) | ftNow.dwLowDateTime; Set(i64); #else time_t now; time(&now); struct tm *time = localtime(&now); if (time) *this = time; #ifndef LGI_STATIC else { LgiTrace("%s:%i - Error: localtime failed, now=%u\n", _FL, now); } #endif #endif return *this; } #define Convert24HrTo12Hr(h) ( (h) == 0 ? 12 : (h) > 12 ? (h) % 12 : (h) ) #define Convert24HrToAmPm(h) ( (h) >= 12 ? "p" : "a" ) LString LDateTime::GetDate() const { char s[32]; int Ch = GetDate(s, sizeof(s)); return LString(s, Ch); } int LDateTime::GetDate(char *Str, size_t SLen) const { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_DATE_MASK) { case GDTF_MONTH_DAY_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%2.2i" :"%i" , _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; default: case GDTF_DAY_MONTH_YEAR: Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%2.2i" :"%i" , _Day); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, "%c%i", DefaultSeparator, _Year); break; case GDTF_YEAR_MONTH_DAY: Ch += sprintf_s(Str+Ch, SLen-Ch, "%i", _Year); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_MONTH_LEADINGZ?"%c%2.2i":"%c%i", DefaultSeparator, _Month); Ch += sprintf_s(Str+Ch, SLen-Ch, _Format&GDTF_DAY_LEADINGZ ?"%c%2.2i":"%c%i", DefaultSeparator, _Day); break; } } return Ch; } LString LDateTime::GetTime() const { char s[32]; int Ch = GetTime(s, sizeof(s)); return LString(s, Ch); } int LDateTime::GetTime(char *Str, size_t SLen) const { int Ch = 0; if (Str && SLen > 0) { switch (_Format & GDTF_TIME_MASK) { case GDTF_12HOUR: default: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i%s", Convert24HrTo12Hr(_Hours), _Minutes, _Seconds, Convert24HrToAmPm(_Hours)); break; } case GDTF_24HOUR: { Ch += sprintf_s(Str, SLen, "%i:%2.2i:%2.2i", _Hours, _Minutes, _Seconds); break; } } } return Ch; } uint64 LDateTime::Ts() const { uint64 ts = 0; Get(ts); return ts; } bool LDateTime::SetUnix(uint64 s) { #if defined(WINDOWS) return Set(s * LDateTime::Second64Bit + 116445168000000000LL); #else return Set((s + Offset1800) * LDateTime::Second64Bit); #endif } bool LDateTime::Set(uint64 s) { #if defined WIN32 FILETIME Utc; SYSTEMTIME System; // Adjust to the desired timezone uint64 u = s + ((int64)_Tz * 60 * Second64Bit); Utc.dwHighDateTime = u >> 32; Utc.dwLowDateTime = u & 0xffffffff; if (!FileTimeToSystemTime(&Utc, &System)) return false; _Year = System.wYear; _Month = System.wMonth; _Day = System.wDay; _Hours = System.wHour; _Minutes = System.wMinute; _Seconds = System.wSecond; _Thousands = System.wMilliseconds; return true; #else time_t t = (time_t) (((int64)(s / Second64Bit)) - Offset1800); Set(t); _Thousands = s % Second64Bit; return true; #endif } bool LDateTime::Set(time_t tt) { struct tm *t; #if !defined(_MSC_VER) || _MSC_VER < _MSC_VER_VS2005 if (_Tz) tt += _Tz * 60; t = gmtime(&tt); if (t) #else struct tm tmp; if (_localtime64_s(t = &tmp, &tt) == 0) #endif { _Year = t->tm_year + 1900; _Month = t->tm_mon + 1; _Day = t->tm_mday; _Hours = t->tm_hour; _Minutes = t->tm_min; _Seconds = t->tm_sec; _Thousands = 0; // _Tz = SystemTimeZone(); return true; } return false; } uint64_t LDateTime::OsTime() const { #ifdef WINDOWS FILETIME Utc; SYSTEMTIME System; System.wYear = _Year; System.wMonth = limit(_Month, 1, 12); System.wDay = limit(_Day, 1, 31); System.wHour = limit(_Hours, 0, 23); System.wMinute = limit(_Minutes, 0, 59); System.wSecond = limit(_Seconds, 0, 59); System.wMilliseconds = limit(_Thousands, 0, 999); System.wDayOfWeek = DayOfWeek(); if (SystemTimeToFileTime(&System, &Utc)) { uint64_t s = ((uint64_t)Utc.dwHighDateTime << 32) | Utc.dwLowDateTime; if (_Tz) // Adjust for timezone s -= (int64)_Tz * 60 * Second64Bit; return s; } else { DWORD Err = GetLastError(); LAssert(!"SystemTimeToFileTime failed."); } #else if (_Year < MIN_YEAR) return 0; struct tm t; ZeroObj(t); t.tm_year = _Year - 1900; t.tm_mon = _Month - 1; t.tm_mday = _Day; t.tm_hour = _Hours; t.tm_min = _Minutes; t.tm_sec = _Seconds; t.tm_isdst = -1; time_t sec = timegm(&t); if (sec == -1) return 0; if (_Tz) { // Adjust the output to UTC from the current timezone. sec -= _Tz * 60; } return sec; #endif return 0; } bool LDateTime::OsTime(uint64_t ts) { return Set((time_t)ts); } bool LDateTime::Get(uint64 &s) const { #ifdef WINDOWS if (!IsValid()) { LAssert(!"Needs a valid date."); return false; } s = OsTime(); if (!s) return false; return true; #else if (_Year < MIN_YEAR) return false; auto sec = OsTime(); s = (uint64)(sec + Offset1800) * Second64Bit + _Thousands; return true; #endif } LString LDateTime::Get() const { char buf[32]; int Ch = GetDate(buf, sizeof(buf)); buf[Ch++] = ' '; Ch += GetTime(buf+Ch, sizeof(buf)-Ch); return LString(buf, Ch); } void LDateTime::Get(char *Str, size_t SLen) const { if (Str) { GetDate(Str, SLen); size_t len = strlen(Str); if (len < SLen - 1) { Str[len++] = ' '; GetTime(Str+len, SLen-len); } } } bool LDateTime::Set(const char *Str) { if (!Str) return false; char Local[256]; strcpy_s(Local, sizeof(Local), Str); char *Sep = strchr(Local, ' '); if (Sep) { *Sep++ = 0; if (!SetTime(Sep)) return false; } if (!SetDate(Local)) return false; return true; } void LDateTime::Month(char *m) { int i = IsMonth(m); if (i >= 0) _Month = i + 1; } int DateComponent(const char *s) { int64 i = Atoi(s); return i ? (int)i : LDateTime::IsMonth(s); } bool LDateTime::SetDate(const char *Str) { bool Status = false; if (Str) { auto T = LString(Str).SplitDelimit("/-.,_\\"); if (T.Length() == 3) { int i[3] = { DateComponent(T[0]), DateComponent(T[1]), DateComponent(T[2]) }; int fmt = _Format & GDTF_DATE_MASK; // Do some guessing / overrides. // Don't let _Format define the format completely. if (i[0] > 1000) { fmt = GDTF_YEAR_MONTH_DAY; } else if (i[2] > 1000) { if (i[0] > 12) fmt = GDTF_DAY_MONTH_YEAR; else if (i[1] > 12) fmt = GDTF_MONTH_DAY_YEAR; } switch (fmt) { case GDTF_MONTH_DAY_YEAR: { _Month = i[0]; _Day = i[1]; _Year = i[2]; break; } case GDTF_DAY_MONTH_YEAR: { _Day = i[0]; _Month = i[1]; _Year = i[2]; break; } case GDTF_YEAR_MONTH_DAY: { _Year = i[0]; _Month = i[1]; _Day = i[2]; break; } default: { _Year = i[2]; if ((DefaultFormat & GDTF_DATE_MASK) == GDTF_MONTH_DAY_YEAR) { // Assume m/d/yyyy _Day = i[1]; _Month = i[0]; } else { // Who knows??? // Assume d/m/yyyy _Day = i[0]; _Month = i[1]; } break; } } if (_Year < 100) { LAssert(_Day < 1000 && _Month < 1000); if (_Year >= 80) _Year += 1900; else _Year += 2000; } Status = true; } else { // Fall back to fuzzy matching auto T = LString(Str).SplitDelimit(" ,"); MonthHash Lut; int FMonth = 0; int FDay = 0; int FYear = 0; for (unsigned i=0; i 0) { if (i >= 1000) { FYear = i; } else if (i < 32) { FDay = i; } } } else { int i = Lut.Find(p); if (i) FMonth = i; } } if (FMonth && FDay) { Day(FDay); Month(FMonth); } if (FYear) { Year(FYear); } else { LDateTime Now; Now.SetNow(); Year(Now.Year()); } } } return Status; } bool LDateTime::SetTime(const char *Str) { if (!Str) return false; auto T = LString(Str).SplitDelimit(":."); if (T.Length() < 2 || T.Length() > 4) return false; _Hours = (int)T[0].Int(); _Minutes = (int)T[1].Int(); if (_Hours < 0 || _Minutes < 0) return false; char *s = T[2]; if (s) _Seconds = atoi(s); else _Seconds = 0; _Thousands = 0; s = T.Last(); if (s) { if (strchr(s, 'p') || strchr(s, 'P')) { if (_Hours != 12) _Hours += 12; } else if (strchr(s, 'a') || strchr(s, 'A')) { if (_Hours == 12) _Hours -= 12; } } if (T.Length() > 3) { LString t = "0."; t += s; _Thousands = (int) (t.Float() * 1000); } return true; } int LDateTime::IsWeekDay(const char *s) { for (unsigned n=0; n= 4) { Year((int)t[0].Int()); Month((int)t[1].Int()); Day((int)t[2].Int()); } else if (t[2].Length() >= 4) { Day((int)t[0].Int()); Month((int)t[1].Int()); Year((int)t[2].Int()); } else { LAssert(!"Unknown date format?"); return false; } } } else if (a[i].Length() == 4) Year((int)a[i].Int()); else if (!Day()) Day((int)a[i].Int()); } else if (IsAlpha(*c)) { int WkDay = IsWeekDay(c); if (WkDay >= 0) continue; int Mnth = IsMonth(c); if (Mnth >= 0) Month(Mnth + 1); } else if (*c == '-' || *c == '+') { c++; if (strlen(c) == 4) { // Timezone.. int64 Tz = a[i].Int(); int Hrs = (int) (Tz / 100); int Min = (int) (Tz % 100); SetTimeZone(Hrs * 60 + Min, false); } } } return IsValid(); } int LDateTime::Sizeof() { return sizeof(int) * 7; } bool LDateTime::Serialize(LFile &f, bool Write) { int32 i; if (Write) { #define wf(fld) i = fld; f << i; wf(_Day); wf(_Month); wf(_Year); wf(_Thousands); wf(_Seconds); wf(_Minutes); wf(_Hours); } else { #define rf(fld) f >> i; fld = i; rf(_Day); rf(_Month); rf(_Year); rf(_Thousands); rf(_Seconds); rf(_Minutes); rf(_Hours); } return true; } /* bool LDateTime::Serialize(ObjProperties *Props, char *Name, bool Write) { #ifndef LGI_STATIC if (Props && Name) { struct _Date { uint8_t Day; uint8_t Month; int16_t Year; uint8_t Hour; uint8_t Minute; uint16_t ThouSec; }; LAssert(sizeof(_Date) == 8); if (Write) { _Date d; d.Day = _Day; d.Month = _Month; d.Year = _Year; d.Hour = _Hours; d.Minute = _Minutes; d.ThouSec = (_Seconds * 1000) + _Thousands; return Props->Set(Name, &d, sizeof(d)); } else // Read { void *Ptr; int Len; if (Props->Get(Name, Ptr, Len) && sizeof(_Date) == Len) { _Date *d = (_Date*) Ptr; _Day = d->Day; _Month = d->Month; _Year = d->Year; _Hours = d->Hour; _Minutes = d->Minute; _Seconds = d->ThouSec / 1000; _Thousands = d->ThouSec % 1000; return true; } } } #endif return false; } */ int LDateTime::Compare(const LDateTime *Date) const { // this - *Date auto ThisTs = IsValid() ? Ts() : 0; auto DateTs = Date->IsValid() ? Date->Ts() : 0; // If these ever fire, the cast to int64_t will overflow LAssert((ThisTs & 0x800000000000000) == 0); LAssert((DateTs & 0x800000000000000) == 0); int64_t Diff = (int64_t)ThisTs - DateTs; if (Diff < 0) return -1; return Diff > 0 ? 1 : 0; } #define DATETIME_OP(op) \ bool LDateTime::operator op(const LDateTime &dt) const \ { \ auto a = Ts(); \ auto b = dt.Ts(); \ return a op b; \ } DATETIME_OP(<) DATETIME_OP(<=) DATETIME_OP(>) DATETIME_OP(>=) bool LDateTime::operator ==(const LDateTime &dt) const { return _Year == dt._Year && _Month == dt._Month && _Day == dt._Day && _Hours == dt._Hours && _Minutes == dt._Minutes && _Seconds == dt._Seconds && _Thousands == dt._Thousands; } bool LDateTime::operator !=(const LDateTime &dt) const { return _Year != dt._Year || _Month != dt._Month || _Day != dt._Day || _Hours != dt._Hours || _Minutes != dt._Minutes || _Seconds != dt._Seconds || _Thousands != dt._Thousands; } int LDateTime::DiffMonths(const LDateTime &dt) { int a = (Year() * 12) + Month(); int b = (dt.Year() * 12) + dt.Month(); return b - a; } LDateTime LDateTime::operator -(const LDateTime &dt) { uint64 a, b; Get(a); dt.Get(b); /// Resolution of a second when using 64 bit timestamps int64 Sec = Second64Bit; int64 Min = 60 * Sec; int64 Hr = 60 * Min; int64 Day = 24 * Hr; int64 d = (int64)a - (int64)b; LDateTime r; r._Day = (int16) (d / Day); d -= r._Day * Day; r._Hours = (int16) (d / Hr); d -= r._Hours * Hr; r._Minutes = (int16) (d / Min); d -= r._Minutes * Min; r._Seconds = (int16) (d / Sec); #ifdef WIN32 d -= r._Seconds * Sec; r._Thousands = (int16) (d / 10000); #else r._Thousands = 0; #endif return r; } LDateTime LDateTime::operator +(const LDateTime &dt) { LDateTime s = *this; s.AddMonths(dt.Month()); s.AddDays(dt.Day()); s.AddHours(dt.Hours()); s.AddMinutes(dt.Minutes()); // s.AddSeconds(dt.Seconds()); return s; } LDateTime &LDateTime::operator =(const LDateTime &t) { _Day = t._Day; _Year = t._Year; _Thousands = t._Thousands; _Month = t._Month; _Seconds = t._Seconds; _Minutes = t._Minutes; _Hours = t._Hours; _Tz = t._Tz; _Format = t._Format; return *this; } LDateTime &LDateTime::operator =(struct tm *time) { if (time) { _Seconds = time->tm_sec; _Minutes = time->tm_min; _Hours = time->tm_hour; _Day = time->tm_mday; _Month = time->tm_mon + 1; _Year = time->tm_year + 1900; } else Empty(); return *this; } bool LDateTime::IsSameDay(LDateTime &d) const { return Day() == d.Day() && Month() == d.Month() && Year() == d.Year(); } bool LDateTime::IsSameMonth(LDateTime &d) const { return Day() == d.Day() && Month() == d.Month(); } bool LDateTime::IsSameYear(LDateTime &d) const { return Year() == d.Year(); } bool LDateTime::IsLeapYear(int Year) const { if (Year < 0) Year = _Year; if (Year % 4 != 0) { return false; } if (Year % 400 == 0) { return true; } if (Year % 100 == 0) { return false; } return true; } int LDateTime::DaysInMonth() const { if (_Month == 2 && IsLeapYear()) { return 29; } short DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; return _Month >= 1 && _Month <= 12 ? DaysInMonth[_Month-1] : 0; } #define MinutesInDay (60*24) void LDateTime::AddSeconds(int64 Seconds) { uint64 i; if (Get(i)) { i += Seconds * Second64Bit; Set(i); } } void LDateTime::AddMinutes(int64 Minutes) { uint64 i; if (Get(i)) { int64 delta = Minutes * 60 * Second64Bit; uint64 n = i + delta; // printf("AddMin " LPrintfInt64 " + " LPrintfInt64 " = " LPrintfInt64 "\n", i, delta, n); Set(n); #if 0 uint64 i2; Get(i2); int64 diff = (int64)i2-(int64)i; #endif } } void LDateTime::AddHours(int64 Hours) { uint64 i; if (Get(i)) { i += Hours * LDateTime::HourLength * Second64Bit; Set(i); } } bool LDateTime::AddDays(int64 Days) { if (!Days) return true; uint64 Ts; if (!Get(Ts)) return false; Ts += Days * LDateTime::DayLength * Second64Bit; bool b = Set(Ts); return b; } void LDateTime::AddMonths(int64 Months) { int64 m = _Month + Months; do { if (m < 1) { _Year--; m += 12; } else if (m > 12) { _Year++; m -= 12; } else { break; } } while (1); _Month = (int16) m; if (_Day > DaysInMonth()) _Day = DaysInMonth(); } LString LDateTime::DescribePeriod(double seconds) { int mins = (int) (seconds / 60); seconds -= mins * 60; int hrs = mins / 60; mins -= hrs * 60; int days = hrs / 24; hrs -= days * 24; LString s; if (days > 0) s.Printf("%id %ih %im %is", days, hrs, mins, (int)seconds); else if (hrs > 0) s.Printf("%ih %im %is", hrs, mins, (int)seconds); else if (mins > 0) s.Printf("%im %is", mins, (int)seconds); else s.Printf("%is", (int)seconds); return s; } LString LDateTime::DescribePeriod(LDateTime to) { auto ThisTs = Ts(); auto ToTs = to.Ts(); auto diff = ThisTs < ToTs ? ToTs - ThisTs : ThisTs - ToTs; auto seconds = (double)diff / LDateTime::Second64Bit; return DescribePeriod(seconds); } int LDateTime::MonthFromName(const char *Name) { if (Name) { for (int m=0; m<12; m++) { if (strnicmp(Name, MonthsShort[m], strlen(MonthsShort[m])) == 0) { return m + 1; break; } } } return -1; } bool LDateTime::Decode(const char *In) { // Test data: // // Tue, 6 Dec 2005 1:25:32 -0800 Empty(); if (!In) { LAssert(0); return false; } bool Status = false; // Tokenize delimited by whitespace LString::Array T = LString(In).SplitDelimit(", \t\r\n"); if (T.Length() < 2) { if (T[0].IsNumeric()) { // Some sort of timestamp? uint64_t Ts = Atoi(T[0].Get()); if (Ts > 0) { return SetUnix(Ts); } else return false; } else { // What now? return false; } } else { bool GotDate = false; for (unsigned i=0; i 31) { // Y/M/D? Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else if (Date[2].Int() > 31) { // D/M/Y? Day((int)Date[0].Int()); Year((int)Date[2].Int()); } else { // Ambiguous year... bool YrFirst = true; if (Date[0].Length() == 1) YrFirst = false; // else we really can't tell.. just go with year first if (YrFirst) { Year((int)Date[0].Int()); Day((int)Date[2].Int()); } else { Day((int)Date[0].Int()); Year((int)Date[2].Int()); } LDateTime Now; Now.SetNow(); if (Year() + 2000 <= Now.Year()) Year(2000 + Year()); else Year(1900 + Year()); } if (Date[1].IsNumeric()) Month((int)Date[1].Int()); else { int m = MonthFromName(Date[1]); if (m > 0) Month(m); } GotDate = true; Status = true; } else if (s.Find(":") >= 0) { // whole time // Do some validation bool Valid = true; for (char *c = s; *c && Valid; c++) { if (!(IsDigit(*c) || *c == ':')) Valid = false; } if (Valid) { LString::Array Time = s.Split(":"); if (Time.Length() == 2 || Time.Length() == 3) { // Hour int i = (int) Time[0].Int(); if (i >= 0) Hours(i); if (s.Lower().Find("p") >= 0) { if (Hours() < 12) Hours(Hours() + 12); } // Minute i = (int) Time[1].Int(); if (i >= 0) Minutes(i); if (Time.Length() == 3) { // Second i = (int) Time[2].Int(); if (i >= 0) Seconds(i); } Status = true; } } } else if (IsAlpha(s(0))) { // text int m = MonthFromName(s); if (m > 0) Month(m); } else if (strchr("+-", *s)) { // timezone DoTimeZone: LDateTime Now; double OurTmz = (double)Now.SystemTimeZone() / 60; if (s && strchr("-+", *s) && strlen(s) == 5) { #if 1 int i = atoi(s); int hr = i / 100; int min = i % 100; SetTimeZone(hr * 60 + min, false); #else // adjust for timezone char Buf[32]; memcpy(Buf, s, 3); Buf[3] = 0; double TheirTmz = atof(Buf); memcpy(Buf+1, s + 3, 2); TheirTmz += (atof(Buf) / 60); if (Tz) { *Tz = TheirTmz; } double AdjustHours = OurTmz - TheirTmz; AddMinutes((int) (AdjustHours * 60)); #endif } else { // assume GMT AddMinutes((int) (OurTmz * 60)); } } else if (s.IsNumeric()) { int Count = 0; for (char *c = s; *c; c++) { if (!IsDigit(*c)) break; Count++; } if (Count <= 2) { if (Day()) { // We already have a day... so this might be // a 2 digit year... LDateTime Now; Now.SetNow(); int Yr = atoi(s); if (2000 + Yr <= Now.Year()) Year(2000 + Yr); else Year(1900 + Yr); } else { // A day number (hopefully)? Day((int)s.Int()); } } else if (Count == 4) { if (!Year()) { // A year! Year((int)s.Int()); Status = true; } else { goto DoTimeZone; } // My one and only Y2K fix // d.Year((Yr < 100) ? (Yr > 50) ? 1900+Yr : 2000+Yr : Yr); } } } } return Status; } bool LDateTime::GetVariant(const char *Name, LVariant &Dst, char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case DateYear: // Type: Int32 Dst = Year(); break; case DateMonth: // Type: Int32 Dst = Month(); break; case DateDay: // Type: Int32 Dst = Day(); break; case DateHour: // Type: Int32 Dst = Hours(); break; case DateMinute: // Type: Int32 Dst = Minutes(); break; case DateSecond: // Type: Int32 Dst = Seconds(); break; case DateDate: // Type: String { char s[32]; GetDate(s, sizeof(s)); Dst = s; break; } case DateTime: // Type: String { char s[32]; GetTime(s, sizeof(s)); Dst = s; break; } case TypeString: // Type: String case DateDateAndTime: // Type: String { char s[32]; Get(s, sizeof(s)); Dst = s; break; } case TypeInt: // Type: Int64 case DateTimestamp: // Type: Int64 { uint64 i = 0; Get(i); Dst = (int64)i; break; } case DateSecond64Bit: { Dst = Second64Bit; break; } default: { return false; } } return true; } bool LDateTime::SetVariant(const char *Name, LVariant &Value, char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case DateYear: Year(Value.CastInt32()); break; case DateMonth: Month(Value.CastInt32()); break; case DateDay: Day(Value.CastInt32()); break; case DateHour: Hours(Value.CastInt32()); break; case DateMinute: Minutes(Value.CastInt32()); break; case DateSecond: Seconds(Value.CastInt32()); break; case DateDate: SetDate(Value.Str()); break; case DateTime: SetTime(Value.Str()); break; case DateDateAndTime: Set(Value.Str()); break; case DateTimestamp: Set((uint64)Value.CastInt64()); break; default: return false; } return true; } bool LDateTime::CallMethod(const char *Name, LVariant *ReturnValue, LArray &Args) { switch (LStringToDomProp(Name)) { case DateSetNow: SetNow(); if (ReturnValue) *ReturnValue = true; break; case DateSetStr: if (Args.Length() < 1) return false; bool Status; if (Args[0]->Type == GV_INT64) Status = Set((uint64) Args[0]->Value.Int64); else Status = Set(Args[0]->Str()); if (ReturnValue) *ReturnValue = Status; break; case DateGetStr: { char s[256] = ""; Get(s, sizeof(s)); if (ReturnValue) *ReturnValue = s; break; } default: return false; } return true; } #ifdef _DEBUG #define DATE_ASSERT(i) \ if (!(i)) \ { \ LAssert(!"LDateTime unit test failed."); \ return false; \ } bool LDateTime_Test() { // Check 64bit get/set LDateTime t("1/1/2017 0:0:0"); uint64 i; DATE_ASSERT(t.Get(i)); LgiTrace("Get='%s'\n", t.Get().Get()); uint64 i2 = i + (24ULL * 60 * 60 * LDateTime::Second64Bit); LDateTime t2; t2.SetFormat(GDTF_DAY_MONTH_YEAR); t2.Set(i2); LString s = t2.Get(); LgiTrace("Set='%s'\n", s.Get()); DATE_ASSERT(!stricmp(s, "2/1/2017 12:00:00a") || !stricmp(s, "2/01/2017 12:00:00a")); t.SetNow(); LgiTrace("Now.Local=%s Tz=%.2f\n", t.Get().Get(), t.GetTimeZoneHours()); t2 = t; t2.ToUtc(); LgiTrace("Now.Utc=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); t2.ToLocal(); LgiTrace("Now.Local=%s Tz=%.2f\n", t2.Get().Get(), t2.GetTimeZoneHours()); DATE_ASSERT(t == t2); return true; } #endif diff --git a/src/common/Lgi/MemStream.cpp b/src/common/Lgi/MemStream.cpp --- a/src/common/Lgi/MemStream.cpp +++ b/src/common/Lgi/MemStream.cpp @@ -1,354 +1,379 @@ #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Stream.h" ////////////////////////////////////////////////////////////////////////////// void LMemStream::_Init() { GrowBlockSize = 0; Len = Pos = Alloc = 0; Mem = 0; Own = true; } LMemStream::LMemStream() { _Init(); } LMemStream::LMemStream(LStreamI *Src, int64 Start, int64 len) { _Init(); if (Src) { bool Error = false; if (Start >= 0) { - int64 Result = Src->SetPos(Start); - if (Result >= 0 && Result != Start) + auto Result = Src->SetPos(Start); + if (Result < 0) + { + // Stream doesn't support streaming... + // Read enough bytes to find the start... + char buf[1024]; + for (ssize_t i = 0; i < Start; ) + { + auto remain = Start - i; + auto len = MIN(remain, sizeof(buf)); + auto rd = Src->Read(buf, len); + if (rd != len) + { + Error = true; + break; + } + i += rd; + } + } + else if (Result != Start) + { Error = true; + } } + if (Error) { LAssert(!"Failed to set stream position"); } else { - if (len < 0 && Src->GetSize() > 0) + if (len >= 0) + { + Len = len; + } + else if (Src->GetSize() > 0) { Len = Src->GetSize(); if (Start > 0) Len -= Start; } - if (Len >= 0) + if (Len > 0) { if ((Mem = new char[Alloc = Len])) { ssize_t r; while ( Pos < Len && (r = Src->Read(Mem + Pos, Len - Pos)) > 0) { Pos += r; } Pos = 0; } } else { char Buf[512]; LMemQueue p(4 << 10); ssize_t r; while ((r = Src->Read(Buf, sizeof(Buf))) > 0) { p.Write(Buf, r); } Len = Alloc = p.GetSize(); Mem = (char*)p.New(0); } } } } LMemStream::LMemStream(const void *mem, int64 len, bool copy) { _Init(); Len = len; if ((Own = copy)) { if ((Mem = new char[(Alloc = Len)])) memcpy(Mem, mem, Len); else LAssert(!"Alloc error"); } else { Mem = (char*)mem; } } LMemStream::LMemStream(int growBlockSize) { _Init(); GrowBlockSize = growBlockSize; } LMemStream::~LMemStream() { Close(); } int LMemStream::Open(const char *Str, int Int) { LFile f; if (f.Open(Str, O_READ)) { Close(); Len = f.GetSize(); Mem = new char[Alloc = Len]; if (Mem) { Len = f.Read(Mem, Len); } return Len > 0; } else LAssert(!"Open error"); return 0; } int LMemStream::Close() { if (Own) DeleteArray(Mem); int Sz = GrowBlockSize; _Init(); GrowBlockSize = Sz; return true; } int64 LMemStream::SetSize(int64 Size) { if (Mem) { int64 Common = MIN(Len, Size); char *NewMem = new char[Alloc = Size]; if (NewMem) { memcpy(NewMem, Mem, Common); DeleteArray(Mem); Mem = NewMem; Len = Size; } else { LAssert(!"Alloc error"); return -1; } } else { Close(); Len = Size; Mem = new char[Alloc = Len]; } if (Pos > Size) Pos = Size ? Size - 1 : 0; return Len; } bool LMemStream::IsOk() { return Len == 0 || Mem != 0; } ssize_t LMemStream::Read(void *Buffer, ssize_t Size, int Flags) { ssize_t Bytes = 0; if (Buffer && Pos >= 0 && Pos < Len) { Bytes = MIN(Len - Pos, Size); memcpy(Buffer, Mem + Pos, Bytes); Pos += Bytes; } return Bytes; } ssize_t LMemStream::Write(const void *Buffer, ssize_t Size, int Flags) { if (!Buffer || Size <= 0) return 0; ssize_t Bytes = 0; if (GrowBlockSize) { // Allow mem stream to grow char *Ptr = (char*)Buffer; while (Size) { if (Pos >= Alloc) { // Grow the mem block int64 NewSize = Pos + Size; ssize_t Blocks = (NewSize + GrowBlockSize - 1) / GrowBlockSize; int64 NewAlloc = Blocks * GrowBlockSize; char *NewMem = new char[NewAlloc]; if (!NewMem) return Bytes; memcpy(NewMem, Mem, Pos); if (Own) DeleteArray(Mem); Mem = NewMem; Alloc = NewAlloc; } if (Pos < Alloc) { // Fill available mem first... ssize_t Remaining = Alloc - Pos; ssize_t Copy = MIN(Size, Remaining); memcpy(Mem + Pos, Ptr, Copy); Size -= Copy; Ptr += Copy; Bytes += Copy; Pos += Copy; Len = MAX(Pos, Len); } else break; } } else if (Pos >= 0 && Pos < Len) { // Fill fixed space... Bytes = MIN(Len - Pos, Size); memcpy(Mem + Pos, Buffer, Bytes); Pos += Bytes; } return Bytes; } LStreamI *LMemStream::Clone() { return new LMemStream(Mem, Len, true); } ssize_t LMemStream::Write(LStream *Out, ssize_t Size) { ssize_t Wr = -1; if (Out && Size > 0) { ssize_t Common = MIN(Size, ((int)(Len-Pos))); Wr = Out->Write(Mem, Common); } return Wr; } ////////////////////////////////////////////////////////////////////////////////////////// LTempStream::LTempStream(const char *tmp, int maxMemSize) : LProxyStream(0) { s = &Null; MaxMemSize = maxMemSize; if (tmp) TmpFolder = tmp; else TmpFolder = LGetSystemPath(LSP_TEMP); Tmp = 0; Mem = 0; } LTempStream::~LTempStream() { Empty(); } int64 LTempStream::GetSize() { if (s == (LStreamI*)&Null) return 0; return s->GetSize(); } void LTempStream::Empty() { DeleteObj(Mem); if (Tmp) { char s[MAX_PATH_LEN]; strcpy_s(s, sizeof(s), Tmp->GetName()); DeleteObj(Tmp); FileDev->Delete(s, false); } s = 0; } ssize_t LTempStream::Write(const void *Buffer, ssize_t Size, int Flags) { if (s == &Null) { s = Mem = new LMemStream(16 << 10); } ssize_t Status = LProxyStream::Write(Buffer, Size, Flags); if (Mem != 0 && s->GetSize() > MaxMemSize) { // Convert stream from memory to disk as it's getting too large char c[MAX_PATH_LEN], f[32]; int i=0; do { sprintf_s(f, sizeof(f), "LTempStream-%x.tmp", (int)(((NativeInt)this)+i++)); if (!TmpFolder) TmpFolder = LGetSystemPath(LSP_TEMP); if (!TmpFolder) break; LMakePath(c, sizeof(c), TmpFolder, f); } while (LFileExists(c)); LAssert(Tmp == 0); if ((Tmp = new LFile)) { int64 Len = Mem->GetSize(); if (!Tmp->Open(c, O_READWRITE)) { // Fallback to keeping stuff in memory DeleteObj(Tmp); } else { Mem->SetPos(0); if (Mem->Write(Tmp, Len) != Len) { // Copy failed... fall back to mem DeleteObj(Tmp); FileDev->Delete(c, false); } else { // Copy ok... DeleteObj(Mem); s = Tmp; Tmp->SetPos(Tmp->GetSize()); } } } } return Status; } diff --git a/src/win/General/Mem.cpp b/src/win/General/Mem.cpp --- a/src/win/General/Mem.cpp +++ b/src/win/General/Mem.cpp @@ -1,1020 +1,1021 @@ /*hdr ** FILE: Memory.cpp ** AUTHOR: Matthew Allen ** DATE: 10/4/98 ** DESCRIPTION: Memory subsystem ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #include #include #include #include #include "lgi/common/Mem.h" +#include "lgi/common/LgiUiBase.h" #include #ifdef LGI_MEM_DEBUG #include "lgi/common/LgiDefs.h" #include "ImageHlp.h" #include #include "LSymLookup.h" #undef malloc #undef realloc #undef free #define MEM_TRACKING 1 #define MEM_STACK_SIZE 8 #define MEM_FILL 0xcccccccc #ifndef _WIN64 #define mem_assert(c) if (!(c)) _asm int 3 #else #define mem_assert LAssert #endif struct stack_addr { UNativeInt Ip; }; struct block { block *Next, *Prev; uint32 Size; stack_addr Stack[MEM_STACK_SIZE]; uint32 PreBytes; }; static bool DoLeakCheck = true; static bool MemInit = false; static uint BlockCount = 0; static block *First = 0; static block *Last = 0; static CRITICAL_SECTION Lock; void LSetLeakDetect(bool On) { DoLeakCheck = On; } #pragma warning(disable:4074) // warning C4074: initializers put in compiler reserved initialization area #pragma init_seg(compiler) struct LLeakCheck { LLeakCheck() { InitializeCriticalSection(&Lock); MemInit = true; } ~LLeakCheck() { char DefName[] = "leaks.mem"; if (First && DoLeakCheck) { LDumpMemoryStats(DefName); } else { unlink(DefName); } DeleteCriticalSection(&Lock); MemInit = false; } } _LeakCheck; typedef struct _CALL_FRAME { struct _CALL_FRAME* Next; void* ReturnAddress; } CALL_FRAME, * PCALL_FRAME; LONG StackwalkExceptionHandler(PEXCEPTION_POINTERS ExceptionPointers) { if (ExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) return EXCEPTION_EXECUTE_HANDLER; return EXCEPTION_CONTINUE_SEARCH; } void *lgi_malloc(size_t size) { #if MEM_TRACKING LAssert(MemInit); if (size > 0) { void *Ret = 0; EnterCriticalSection(&Lock); int total = sizeof(block) + sizeof(int32) + size; block *b = (block*) malloc(total); if (b) { // Init the structure b->Size = size; b->PreBytes = MEM_FILL; memset(b->Stack, 0, sizeof(b->Stack)); // Save the stack trace void *_ebp = 0; #ifndef _WIN64 _asm { mov eax, ebp mov _ebp, eax } #if 1 PCALL_FRAME frame = (PCALL_FRAME)_ebp; __try { for (int i=0; i<=MEM_STACK_SIZE; i++) { if (!frame || (NativeInt)frame < 0x1000 || (*((unsigned*)frame) & 0x3)) break; if (i) b->Stack[i-1].Ip = (UNativeInt) *((uint8_t**)frame + 1); frame = frame->Next; } } __except(StackwalkExceptionHandler(GetExceptionInformation())) { } #else for (int i=-1; i= 0) b->Stack[i].Ip = (uint) *((uint8_t**)RegEbp + 1); RegEbp = *(uint*)RegEbp; } #endif #else USHORT r = CaptureStackBackTrace(2, MEM_STACK_SIZE, (PVOID*)b->Stack, NULL); #endif // Add to linked list if (Last) { Last->Next = b; b->Prev = Last; Last = b; } else { First = Last = b; b->Prev = 0; } b->Next = 0; BlockCount++; // Return the data area Ret = (b + 1); } LeaveCriticalSection(&Lock); return Ret; } #else if (size > 0) return malloc(size); #endif return 0; } void *lgi_realloc(void *ptr, size_t size) { #if MEM_TRACKING if (ptr) { void *ret = 0; EnterCriticalSection(&Lock); block *b = ((block*)ptr) - 1; if (b) { block *prev = b->Prev; block *next = b->Next; int total = sizeof(block) + sizeof(int32) + size; block *n = (block*)realloc(b, total); if (n) { n->Size = size; if (prev) prev->Next = n; else First = n; LAssert(n->Prev == prev); if (next) next->Prev = n; else Last = n; LAssert(n->Next == next); ret = n + 1; } } LeaveCriticalSection(&Lock); return ret; } #endif return 0; } void lgi_free(void *ptr) { #if MEM_TRACKING if (ptr) { EnterCriticalSection(&Lock); block *b = ((block*)ptr) - 1; if (b) { // Check struct if (b->PreBytes != MEM_FILL) { mem_assert(!"Pre-fill bytes wrong"); } // Remove from list if (b == First) { mem_assert(b->Prev == 0); if (b->Next) { b->Next->Prev = 0; } First = b->Next; } else if (b == Last) { mem_assert(b->Next == 0); if (b->Prev) { b->Prev->Next = 0; } Last = b->Prev; } else { mem_assert(b->Next != 0); if (b->Next) { b->Next->Prev = b->Prev; } mem_assert(b->Prev != 0); if (b->Prev) { b->Prev->Next = b->Next; } } BlockCount--; // Free the memory free(b); } LeaveCriticalSection(&Lock); } #else if (ptr) free(ptr); #endif } typedef BOOL (__stdcall *SYMINITIALIZEPROC)( HANDLE, LPSTR, BOOL ); typedef BOOL (__stdcall *SYMCLEANUPPROC)( HANDLE ); typedef BOOL (__stdcall * STACKWALKPROC) ( DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID, PREAD_PROCESS_MEMORY_ROUTINE,PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE ); typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)( HANDLE, DWORD ); typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)( HANDLE, DWORD ); typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC) ( HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL ); typedef BOOL (__stdcall *proc_SymGetLineFromAddr)( HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line); typedef DWORD (__stdcall *proc_SymGetOptions)(VOID); typedef DWORD (__stdcall *proc_SymSetOptions)(DWORD SymOptions); bool LDumpMemoryStats(char *filename) { bool Status = true; HMODULE DbgHelp = LoadLibraryA("dbghelp.dll"); if (DbgHelp) { HANDLE hProcess = GetCurrentProcess(); SYMINITIALIZEPROC SymInitialize; SYMCLEANUPPROC SymCleanup; STACKWALKPROC StackWalk; SYMFUNCTIONTABLEACCESSPROC SymFunctionTableAccess; SYMGETMODULEBASEPROC SymGetModuleBase; #ifdef _WIN64 pSymFromAddr SymFromAddr; pSymGetLineFromAddr64 SymGetLineFromAddr64; #else SYMGETSYMFROMADDRPROC SymGetSymFromAddr; proc_SymGetLineFromAddr SymGetLineFromAddr; #endif proc_SymGetOptions SymGetOptions; proc_SymSetOptions SymSetOptions; SymInitialize = (SYMINITIALIZEPROC) GetProcAddress(DbgHelp, "SymInitialize"); SymCleanup = (SYMCLEANUPPROC) GetProcAddress(DbgHelp, "SymCleanup"); StackWalk = (STACKWALKPROC) GetProcAddress(DbgHelp, "StackWalk"); SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC) GetProcAddress(DbgHelp, "SymFunctionTableAccess"); SymGetModuleBase = (SYMGETMODULEBASEPROC) GetProcAddress(DbgHelp, "SymGetModuleBase"); #ifdef _WIN64 SymFromAddr = (pSymFromAddr) GetProcAddress(DbgHelp, "SymFromAddr"); SymGetLineFromAddr64 = (pSymGetLineFromAddr64) GetProcAddress(DbgHelp, "SymGetLineFromAddr64"); #else SymGetSymFromAddr = (SYMGETSYMFROMADDRPROC) GetProcAddress(DbgHelp, "SymGetSymFromAddr"); SymGetLineFromAddr = (proc_SymGetLineFromAddr) GetProcAddress(DbgHelp, "SymGetLineFromAddr"); #endif SymGetOptions = (proc_SymGetOptions) GetProcAddress(DbgHelp, "SymGetOptions"); SymSetOptions = (proc_SymSetOptions) GetProcAddress(DbgHelp, "SymSetOptions"); DWORD dwOpts = SymGetOptions(); DWORD Set = SymSetOptions(dwOpts | SYMOPT_LOAD_LINES | SYMOPT_OMAP_FIND_NEAREST); char s[512] = ""; GetModuleFileNameA(NULL, s, sizeof(s)); char *end = strrchr(s, '\\'); if (end) { *end = 0; } char *path = getenv("path"); int pathlen = strlen(path); char *all = (char*)malloc(pathlen + strlen(s) + 256); if (all) { strcpy(all, path); if (all[strlen(all)-1] != ';') strcat(all, ";"); strcat(all, s); if (SymInitialize(hProcess, all, true)) { char FullPath[MAX_PATH_LEN]; _getcwd(FullPath, sizeof(FullPath)); strcat(FullPath, "\\"); strcat(FullPath, filename ? filename : "stats.mem"); FILE *f = fopen(FullPath, "w"); if (f) { block *b; uint TotalBytes = 0; for (b = First; b; b = b->Next) { TotalBytes += b->Size; } sprintf_s(s, sizeof(s), "%i bytes (%.1f MB) in %i blocks:\n%s\n", TotalBytes, (double)TotalBytes / 1024 / 1024, BlockCount, FullPath); fwrite(s, 1, strlen(s), f); OutputDebugStringA(s); int64 Start = GetTickCount(); int64 Last = Start; uint Current = 0; for (b = First; b; b = b->Next, Current++) { int64 Now = GetTickCount(); if (Now > Last + 1000) { Last = Now; double Sec = (double)(Now-Start)/1000; sprintf_s( s, sizeof(s), "Dumping %.1f%%, %i of %i (%i/sec)\n", (double)Current * 100 / BlockCount, Current, BlockCount, (int)(Current/Sec)); OutputDebugStringA(s); } uint8_t *Data = (uint8_t*)(b + 1); int ch = sprintf_s(s, sizeof(s), "Block %p, %i bytes = {", b + 1, b->Size); for (int n=0; nSize && n<16; n++) { if (Data[n] >= ' ' && Data[n] < 127) { ch += sprintf_s(s+ch, sizeof(s)-ch, "'%c', ", Data[n]); } else { ch += sprintf_s(s+ch, sizeof(s)-ch, "0x%02.2x, ", (int)Data[n]); } } if (b->Size > 16) { ch += sprintf_s(s+ch, sizeof(s)-ch, "...}\n"); } else { ch += sprintf_s(s+ch, sizeof(s)-ch, "}\n"); } fwrite(s, 1, ch, f); for (int i=0; b->Stack[i].Ip && iStack[i].Ip, &mbi, sizeof(mbi)); HINSTANCE Pid = (HINSTANCE)mbi.AllocationBase; char module[256] = ""; if (GetModuleFileNameA(Pid, module, sizeof(module))) { DWORD Offset = b->Stack[i].Ip - (UNativeInt)Pid; #ifdef _WIN64 BYTE symbolBuffer[ sizeof(SYMBOL_INFO) + 512 ]; PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)symbolBuffer; IMAGEHLP_LINE64 Line; ZeroObj(symbolBuffer); pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); pSymbol->MaxNameLen = 512; #else BYTE symbolBuffer[ sizeof(IMAGEHLP_SYMBOL) + 512 ]; PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)symbolBuffer; IMAGEHLP_LINE Line; pSymbol->SizeOfStruct = sizeof(symbolBuffer); pSymbol->MaxNameLength = 512; #endif memset(&Line, 0, sizeof(Line)); Line.SizeOfStruct = sizeof(Line); char *mod = strrchr(module, '\\'); if (mod) mod++; else mod = module; #ifdef _WIN64 DWORD64 symDisplacement = 0; if (SymFromAddr(hProcess, b->Stack[i].Ip, &symDisplacement, pSymbol)) { DWORD Displacement = symDisplacement; if (SymGetLineFromAddr64(hProcess, b->Stack[i].Ip, &Displacement, &Line)) sprintf_s(s, sizeof(s), "\t%s %s:%i\n", mod, Line.FileName, Line.LineNumber); else { if (pSymbol->Name[0] == '$') break; sprintf_s(s, sizeof(s), "\t%s %s+0x" LPrintfHex64 "\n", mod, pSymbol->Name, symDisplacement); // dumpBuffer.Printf("%p: %s Offset: 0x%X (%hs)\n", caller, module, symDisplacement, pSymbol->Name); } } else sprintf_s(s, sizeof(s), "\t%s 0x" LPrintfHex64 "\n", mod, b->Stack[i].Ip); #else DWORD symDisplacement = 0; if (SymGetSymFromAddr(hProcess, b->Stack[i].Ip, &symDisplacement, pSymbol)) { if (SymGetLineFromAddr(hProcess, b->Stack[i].Ip, &symDisplacement, &Line)) sprintf_s(s, sizeof(s), "\t%s %s:%i\n", mod, Line.FileName, Line.LineNumber); else { if (pSymbol->Name[0] == '$') break; sprintf_s(s, sizeof(s), "\t%s %s+0x%x\n", mod, pSymbol->Name, symDisplacement); // dumpBuffer.Printf("%p: %s Offset: 0x%X (%hs)\n", caller, module, symDisplacement, pSymbol->Name); } } else sprintf_s(s, sizeof(s), "\t%s 0x%x\n", mod, b->Stack[i].Ip); #endif fwrite(s, 1, strlen(s), f); } } fwrite("\n", 1, 1, f); } fclose(f); } else Status = false; SymCleanup(hProcess); } else Status = false; } FreeLibrary(DbgHelp); } else Status = false; return Status; } void *operator new(size_t size) { return lgi_malloc(size); } void *operator new[](size_t size) { return lgi_malloc(size); } void operator delete(void *p) { lgi_free(p); } void operator delete[](void *p) { lgi_free(p); } #else #include #define nNoMansLandSize 4 typedef struct _CrtMemBlockHeader { struct _CrtMemBlockHeader * pBlockHeaderNext; struct _CrtMemBlockHeader * pBlockHeaderPrev; char * szFileName; int nLine; #ifdef _WIN64 /* These items are reversed on Win64 to eliminate gaps in the struct * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is * maintained in the debug heap. */ int nBlockUse; size_t nDataSize; #else /* _WIN64 */ size_t nDataSize; int nBlockUse; #endif /* _WIN64 */ long lRequest; unsigned char gap[nNoMansLandSize]; /* followed by: * unsigned char data[nDataSize]; * unsigned char anotherGap[nNoMansLandSize]; */ } _CrtMemBlockHeader; bool LDumpMemoryStats(char *filename) { #ifdef _DEBUG _CrtMemState state = {0}; _CrtMemCheckpoint(&state); _CrtMemDumpStatistics(&state); FILE *f; #ifdef _MSC_VER auto ret = fopen_s(&f, #else f = fopen( #endif filename ? filename : "memdump.txt", "w"); if (!f) return false; char buf[512]; int i = 0; for (auto blk = state.pBlockHeader; blk; blk = blk->pBlockHeaderNext) { int ch = sprintf_s( buf, sizeof(buf), "%i,%s:%i,%i," LPrintfSizeT ",%li\n", i++, blk->szFileName, blk->nLine, blk->nBlockUse, blk->nDataSize, blk->lRequest); fwrite(buf, 1, ch, f); if (i % 10000 == 0) LgiTrace("Dumping %i..\n", i); } fclose(f); return true; #else return false; #endif } void LSetLeakDetect(bool On) { } #endif // LGI_MEM_DEBUG /* #ifdef MEMORY_DEBUG #if 0 #define _malloc(s) VirtualAlloc(0, s, MEM_COMMIT, PAGE_READWRITE) #define _free(p) VirtualFree(p, 0, MEM_RELEASE); #else #include #define abs(a) ((a)>0 ? (a) : -(a)) #define _malloc(s) malloc(s) #define _free(p) free(p); #endif char *_vmem_file = 0; int _vmem_line = 0; #include #define _VMEM_MAGIC 0x12345678 #define _VMEM_BUFFER 16 #define _VMEM_FILL 0xcdcdcdcd #ifdef _METRICS class _vmem_metrics { class _metric { public: char *File; int Line; int Count; _metric() { File = 0; Line = 0; Count = 0; } }; int Length; _metric *m; LMutex Lock; public: _vmem_metrics() { Length = 16 << 10; int Bytes = sizeof(_metric) * Length; m = (_metric*) malloc(Bytes); if (m) { memset(m, 0, Bytes); } } ~_vmem_metrics() { for (int i=0; i<10; i++) { _metric *Max = m; for (int n=0; n Max->Count) { Max = m + n; } } if (Max->Count) { char s[256]; sprintf_s(s, sizeof(s), "Metrics: %s,%i Count: %i\n", Max->File, Max->Line, Max->Count); OutputDebugString(s); Max->Count = 0; } } free(m); } void OnAlloc(char *file, int line) { if (Lock.Lock()) { int i; for (i=0; i= ' ') *e++ = Data[i]; else *e++ = '.'; } strcpy(e, "')\n"); OutputDebugString(s); } }; int _used = 0; int _max_used = 0; _vmem_info *_First, *_Last; class _VMemCleanup { public: _VMemCleanup() { _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_WNDW); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_WNDW); } ~_VMemCleanup() { LAssert(LCheckHeap()); char s[256]; if (_First) { sprintf_s(s, sizeof(s), "Memory Leaks: %i (Max blocks %i)\n", _used, _max_used); OutputDebugString(s); int k=0; for (_vmem_info *i=_First; i; k++) { i->Dump(); void *p = i; i = i->Next; _free(p); } } else { sprintf_s(s, sizeof(s), "No Memory Leaks. (Max blocks %i)\n", _max_used); OutputDebugString(s); } } } VMemCleanup; int _Allocated = 0; void *_vmem_alloc(size_t Size, char *file, int line) { int TotalSize = Size + (_VMEM_BUFFER * 2); _vmem_info *p = (_vmem_info*)_malloc(sizeof(_vmem_info) + TotalSize); if (p) { #ifdef _METRICS VmemMetrics.OnAlloc(file, line); #endif // set the members p->Magic = _VMEM_MAGIC; p->File = file; p->Line = line; p->Size = Size; _used++; _max_used = max(_used, _max_used); // insert into the list if (!_First) { _First = _Last = p; p->Prev = p->Next = 0; } else { p->Prev = 0; p->Next = _First; _First->Prev = p; _First = p; } return ((uchar*)(p+1)) + _VMEM_BUFFER; } else { // out of memory _asm int 3 } return 0; } void _vmem_free(void *ptr) { if (ptr) { _vmem_info *p = ((_vmem_info*) ( ((uchar*)ptr) - _VMEM_BUFFER) ) - 1; if (p->Magic == _VMEM_MAGIC) { // Check fill if (!p->CheckFill()) { _asm int 3 } // Remove from the list if (p == _First && p == _Last) { _First = _Last = 0; } else if (p == _First) { if (p->Prev) _asm int 3 _First = p->Next; if (_First) _First->Prev = 0; } else if (p == _Last) { if (p->Next) _asm int 3 _Last = p->Prev; if (p->Prev) p->Prev->Next = 0; } else { if (!p->Next || !p->Prev) _asm int 3 p->Next->Prev = p->Prev; p->Prev->Next = p->Next; } p->Prev = p->Next = 0; _used--; _free(p); } else { // this pointer wasn't allocated by us _asm int 3 } } else { // no pointer _asm int 3 } } #endif */ bool LCheckHeap() { #ifdef MEMORY_DEBUG _vmem_info *i = 0; int n = 0; int MinD = 0x7fffffff; try { for (i=_First; i; i=i->Next, n++) { char c = i->File[0]; if (i->Magic != _VMEM_MAGIC || !i->CheckFill()) { _asm int 3 } } } catch (...) { _asm int 3 return false; } #endif #if 0 // defined _MSC_VER && defined _CRT_USE_WINAPI_FAMILY_DESKTOP_APP if (_heapchk() != _HEAPOK) { assert(!"Heap not ok."); return false; } #endif return true; } bool LgiCanReadMemory(void *p, int Len) { /* try { int i = 0; for (char *Ptr = (char*)p; Len > 0; Len--) { i += *Ptr++; } } catch (...) { // I guess not... then.. return false; } */ return true; }