unicode variable length string type?

General discussion for topics related to the FreeBASIC project or its community.
marpon
Posts: 342
Joined: Dec 28, 2012 13:31
Location: Paris - France

Re: unicode variable length string type?

Post by marpon »

Thanks caseih for your answers and for your condolances

As i've said , i'll continue this subject on more specific part on the forum : in General programming

the direct link http://www.freebasic.net/forum/viewtopi ... =3&t=24145

I'll continue the subject and obviouly count also on the community to support...

thanks again
marpon
Posts: 342
Joined: Dec 28, 2012 13:31
Location: Paris - France

Re: unicode variable length string type?

Post by marpon »

last evolution of Dyn_Wstring.bi v 1.02

some corrected, simplified parts
added some more functions/sub
everything is now posted here :
https://github.com/marpon/uStringW

I'm still digging , but noticed very few interested guys on the subject ...
I don't understand why, that kind of unicode string was not natively implemented.

As i'm not able to overload the existing functions like mid ; left ; right ; instr ...
i created there conterparts as u_mid ; u_left ... even added u_replace ; u_parse ; u_reverse
and the convertion function to assign ; concat and cast

still interrested on linux experience as i'm only working on windows ,
but normally the functions will work also with linux coded utf32 internally
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: unicode variable length string type?

Post by caseih »

Like many people, I'm mildly interested in this, but am constrained in time and effort. That's always going to be a problem for a small project like FB.

Can you speak more to your architecture? I've looked over your code, but it's quite long for something that one might think, naively, is rather simple. In your readme file you might speak to things like how memory is managed, where is it freed, etc. Also how does it interact with FB built-ins like file and console i/o commands, which is where I'd expect to see the most use of unicode decoding and encoding. What built-in commands have you provided equivalents for (just a list). All this is in the code, but it takes some time to parse and appreciate what you've already done.
marpon
Posts: 342
Joined: Dec 28, 2012 13:31
Location: Paris - France

Re: unicode variable length string type?

Post by marpon »

new version Dyn_Wstring.bi v1.03

More simple , with new constructors , operators and destructor .
The destructor is in charge of deallocating automatically the uStringW constructed on the Sub/function, that will reduce the amount of allocated memory, and reduce also the job on monitoring the allocated adresses.

@caseih
added an information.txt file , wich explains how it is done and how to use it

https://github.com/marpon/uStringW

for all , questions and remarks needed
thanks...
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: unicode variable length string type?

Post by caseih »

Am I understanding correctly that your class allocates chunks of memory as needed, and stores pointers to the chunks in a linked list? Or have I misunderstood entirely the point of the list?
marpon
Posts: 342
Joined: Dec 28, 2012 13:31
Location: Paris - France

Re: unicode variable length string type?

Post by marpon »

Am I understanding correctly that your class allocates chunks of memory as needed, and stores pointers to the chunks in a linked list? Or have I misunderstood entirely the point of the list?
it is almost that , in fact it is not a real linked list ,
in reality it is a shared string array dynamically adapting its ubound according the needs ,
but it works in a similar way.

it is just to take the trace of the allocated memory and been able to free these mem blocks if error or in special case in demand.
I've put that feature from the first version to insure clean exit , in the first version the type did not have either explicit constructor nor destructor, i wanted to avoid them but i think now they give more advantages than desturbance.

As I explain storing the adress of the allocated mem block it is not essential now, as the destructor will normally free the not used block automatically , it is just an other possibility to insure clean exit.

it put a define to have a conditionnal compilation with or without that feature , obviously if performance is needed it can be without


I still have questions that i do not really know :
in chinese , arabic or more generaly speaking in non ansi character set:
is the keyboard input , in console mode, able to send unicode char , and how ?
I can figure out in windows mode, it is managed by some interpretor tool but in console mode ?
or is the keyboard input always limited to 255 char ?
marpon
Posts: 342
Joined: Dec 28, 2012 13:31
Location: Paris - France

variable length wstring type - reactivated discussion

Post by marpon »

Hi

I'm reactivating here, the Dynamic Wstring subject :

A lot of work has been done with the expertize of José Roca (thanks a lot), and i'm pleased to post here my last evolution on that subject.

The intention is to have an almost 'native' type in that case DWSTR as Dynamic Wstring using a class

That type is intended to work for Windows and for Linux ( not tested for Linux, I count on some of you to do it)

Under that link you can have the class file : DWSTR.inc and 2 files to test it , 1 specific to Windows, the other for Linux (not tested)

https://dl.dropboxusercontent.com/u/104 ... _1-0-0.rar

and some extract informations :

Code: Select all

Topics - Behavior explanations

 VarPtr(dwstr)	: gives the ptr of dwstr   (as dwstr ptr)  		'as normal
 @dwstr			:  same as above but simplified way	'as normal

 -dwstr  		: gives the pointer of the internal buffer 		( as wstring ptr  ) use the '-' overloaded operator line 917
 strptr (dwstr)	: same as above, to be similar as with string

 *dwstr		: dereferences the internal pointer to wstring	( byref as wstring)  
						' to ease the typing dwstr.get or even dwstr.m_pBuffer

 & operators : to concatenate directly dwstr with wstring (both sides) or other dwstr  ( overloaded operators)

 wstring functions : Must use *cwstr form !!!!  to insure the native wstring functions work always correctly
		e.g.   Trim(*DWSTR1)     or  Ltrim (*DWSTR1, " ,;")    or  InStr(4, *DWSTR1, Any *DWSTR2)

 for simplicity some functions have been overloaded :  RIGHT, LEFT, VAL

 but all the native functions using wstring will work , you only have to put * before the DWSTR var
		remark , sometime it is not needed because the implicit cast can work correctly (some not)
		but it is a good habit to always use the *dwstr form when using native wstring functions with dwstr!!!


new specific functions : Dw_Acs , Dw_Chr , Dw_String  (counterpart to Asc, Chr, String )

conversion from / to    wstring / string
	Str or Wstr  will work  e.g.    Str(*cwstr)   ;  wstr(*dwstr)  ! again notice with *

	but 2 new extended functions , with optionnal codepage param to set the codepage/charset  (see list: charset.txt)
		Dw_Str( dwstr1, codepage)  converts dwstr to string;    string will be coded according the defined codepage
		Dw_Wstr( str1, codepage)	converts string to dwstr;    string supposed coded according the defined codepage

## in Windows mode, codepage is a numeric var ( e.g 1252 for cp-1252  , CP_UTF8  when utf8 )
	it is assumed the default charset is not utf8
		Dw_Wstr extended Wstr function to convert to DWSTR, can use a codepage or CP_UTF8 to convert the string input
		e.g.   Dw_Wstr ("Êàêèå-òî êðàêîçÿáðû", 1251)   or    dw_wstr("Дми́трий Дми́триевич", CP_UTF8)

		Dw_Str  extended Str function to convert to string, can use a codepage or CP_UTF8 to convert the DWSTR input
		e.g.   Dw_Str (DWSTR1, 1251)     or  Dw_Str (DWSTR1, CP_UTF8)

## in Linux mode,  codepage is a string var  ( e.g  "de_DE.UTF-8"  or for Locale "fr_FR.iso-8859-1")
	it is assumed the default charset is always utf8 coded , is it true? need confimation ??? 
		Dw_Wstr extended Wstr function to convert to DWSTR, can use "codepage" to convert the string input
		e.g.   Dw_Wstr ("Êàêèå-òî êðàêîçÿáðû", " xxx.yyyy " )   or    dw_wstr("Дми́трий Дми́триевич", "xxx.UTF-8")

		Dw_Str  extended Str function to convert to string, can use a "codepage" to convert the DWSTR input
		e.g.   Dw_Str (DWSTR1, " xxx.yyyy ")     or  Dw_Str (DWSTR1, "xxx.UTF-8")

			****	has to be confirmed what values " xxx.yyyy "   or "xxx.UTF-8" ****   
 
=========================================================================================
the LINUX part is still not tested , just simulated under windows : need your comments/remarks

must be confirmed : the codepage under Linux,
		tested with  : setlocale(LC_CTYPE, "")  to get the current system codepage
=========================================================================================
With that class, you have an almost 'native' dynamic wstring , the only difference compared with string is:
the compiler does not know that type and is not able to know what cast to use with certain wstring functions (ambigous situation) , so it is recommended to always use the *dwstr form with wstring functions. The compiler could also be adapted to it, but it is not under my control :)

All your remarks / comments are welcome, thanks in advance.

note: the Linux part is not tested , (i don't have Linux), and all the conversions process for that OS are only done according, i've got through internet
hope it is working as i understood.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: variable length wstring type - reactivated discussion

Post by Tourist Trap »

I hope this will be popular. This looks very smart coded, and with the famous José Rocca additional seal of quality.

I can think of an example that would be interesting if implemented. What about reading a Chinese webpage, for instance with SNC and your datatype?
marpon
Posts: 342
Joined: Dec 28, 2012 13:31
Location: Paris - France

Re: unicode variable length string type?

Post by marpon »

@tourist Trap

Happy you like it, did you test under Linux?

I still did not test the behavior of that OS , that's why, i would need some support at least reaction from the Linux Freebasic community.

As you can see the class is working as a native string type, it will ease the unicode program coding, the speed is comparable to the string
even it holds 2 * bytes at least( 4 in linux) by char.

For your proposal > reading a Chinese webpage : I'm not familiar at all with chinese charsets.
Tourist Trap
Posts: 2958
Joined: Jun 02, 2015 16:24

Re: unicode variable length string type?

Post by Tourist Trap »

marpon wrote: did you test under Linux?
Unfortunately I'm under windoes.

About the char-set, any demo showing how reading a webpage with odd characters and render it on the fb console would be great.
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: unicode variable length string type?

Post by caseih »

I'm using Linux and I'll test it when I get a chance in the next weeks.

As for Chinese characters, some of those are outside the basic multilingual plane. This means that on Windows some characters will take up more than two bytes. This means Len() and mid() won't always work right even with the base wstring type.
marpon
Posts: 342
Joined: Dec 28, 2012 13:31
Location: Paris - France

Re: unicode variable length string type?

Post by marpon »

@caseih
I'm using Linux and I'll test it when I get a chance in the next weeks.
Wait for your test, because unfortunatly I'm not able to do it, I only have an old XP computer!

For windows : yes when code units are outside the basic multilingual plane >h&10000, the surrogate pairs use 2 Wstrings for these unit codes.

So sure, Len is affected, and the majority of the string manipulation functions could be also, (have to be recreated), not so difficult,
you can see some i've done on my previous versions, i've no problem to recreate them.

The reason , I'm reactivating the subject and I'm posting the class here, is to show it is quite easy to implement a dynamic Wstring type, all what have been done on the class could be reproduced easily on the rtlib ( on c of course ) to give a native implementation of dynamic wstring, its just a matter of interest/need to have it done.

With that resulting native dynamic wstring, the compiler would know it, so it could correctly play with the wstring functions more easily than the class is doing ; today better to use *dwstr to play with wstring functions because the implicit cast byref wstring is not able to 'connect' correctly the right overloaded function.

Of course, dynamic wstring is not a full unicode string but it ease at least the wstring management, as string is doing for zstring , and at least for Windows( i don't know for linux because default UTF8) its the easiest 'basement' for unicode.

If it is some interest on the community, it is quite simple to have it, no real road block...

here the updated class , added & operators to concatenate with numeric and added some conversion functions

https://dl.dropboxusercontent.com/u/104 ... _1-1-0.rar
StringEpsilon
Posts: 42
Joined: Apr 09, 2015 20:49

Re: unicode variable length string type?

Post by StringEpsilon »

Testing on Linux (64 bit) here.

FreeBASIC Compiler - Version 1.06.0 (02-12-2016), built for linux-x86_64 (64bit)

I get a few warnings and 2 errors when trying to compile.
fbc "linux_test_1_dwstr.bas"
DWSTR.bi(485) warning 3(1): Passing different pointer types, at parameter 4 of UTFTOWCHAR()
DWSTR.bi(713) warning 3(1): Passing different pointer types, at parameter 4 of UTFTOWCHAR()
DWSTR.bi(790) warning 3(1): Passing different pointer types, at parameter 4 of UTFTOWCHAR()
DWSTR.bi(1101) warning 3(1): Passing different pointer types, at parameter 4 of UTFTOWCHAR()
DWSTR.bi(1157) warning 3(1): Passing different pointer types, at parameter 5 of WCHARTOUTF()
DWSTR.bi(1317) error 28: Expected pointer in 'return *Dw_Wstr(st1, codePage)'
DWSTR.bi(1323) error 57: Type mismatch, at parameter 2 (nCodePage) of DW_STR() in 'return Dw_Str(wst1, codePage)'convert to string using codepage'
I briefly thought about fixing those myself, but your code is pretty illegible to me (I'm not used to abbreviated names like that).

Edit: Tested again with an up-to date compiler (latest git), same warnings / errors. I'll report back with 1.05 stable.

P.S.: The rename to DWSTR.bi was me.
marpon
Posts: 342
Joined: Dec 28, 2012 13:31
Location: Paris - France

Re: unicode variable length string type?

Post by marpon »

@stringEPsilon

Thanks for the test :

Warnings : in 64 , it should be integer ptr (i've tested with win 32, integer and long are the same not in 64) : done now!

errors , my mistake : corrected now

Hope everything is working now

The lastest release , with some more helper conversion functions
https://dl.dropboxusercontent.com/u/104 ... _1-2-0.rar

yes, you can change dwstr.inc to dwstr.bi , i did not use .bi because it is more inside than declarations, types,...
it could be .bas but also i prefer to have only 1 .bas :

.inc is an habit from powerbasic (include file)
But in reality it can be named as you want!

hope every thing is working, and thanks for testing and reporting.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: unicode variable length string type?

Post by MrSwiss »

@marpon,
marpon wrote:Warnings : in 64 , it should be integer ptr (i've tested with win 32, integer and long are the same not in 64) : done now!
Not quite ...
There where still some missing (or incorrect) Cast's.
Also a missing ByRef/ByVAL on one of the Functions (Warning)

Fixed for WIN x64:

Code: Select all

'	DWSTR class : Dynamic Wstring class

' ########################################################################################
' Can be used with Microsoft Windows or Linux
' Implements a dynamic data type null terminated WSTRING, to simplify the use of unicode,
' intended to give the same behavior as dynamic STRING gives for ansi.
' Compiler: Free Basic 32 & 64 bit
' This code is an evolution of the original CWSTR class from José Roca , the structure is similar,
' is has been simplified is some aspects and extended in others features to "mimic" as much
' as possible the STRING type (dynamic).
' Regarding the CWSTR class, they are not interchageable! But can be easily "casted".
' Copyright (c) 2016 Paul Squires & José Roca & Marc Pons. Freeware. Use at your own risk.
' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
' ########################################################################################

#pragma ONCE

#INCLUDE ONCE "crt/string.bi"		' for memcopy
#INCLUDE ONCE "utf_conv.bi"		' for utf  conversions : utf8 string to DWSTR and reverse


#IFDEF __FB_LINUX__
	#INCLUDE ONCE "crt/stdlib.bi"
	#DEFINE _MY_SIZE_WSTRING_ 4	' sizeof(wstring)
	TYPE _MY_W_CHAR_ AS ULONG 		' equivalent type
	#INCLUDE ONCE "crt/locale.bi" ' is it already included in LINUX ? not sure: if yes can be deleted
	Dim as string _LOCAL_VERIF_1234567890 = *SetLocale(LC_CTYPE, 0)  ' it is needed, probably SetLocale is already done?
	#IFDEF _DWSTR_DEBUG_
		print: print " SetLocale info = " & _LOCAL_VERIF_1234567890 : print: print
	#ENDIF
	if left(_LOCAL_VERIF_1234567890, 10) = "LC_COLLATE" THEN SetLocale(LC_CTYPE, "")	'init local if not done before
#ELSE
	#IFDEF __FB_DOS__
		TYPE DWSTR AS STRING
	#ELSE
		#INCLUDE ONCE "windows.bi"		' "crt/locale.bi"  is included somewhere !
		#DEFINE _MY_SIZE_WSTRING_ 2  	' sizeof(wstring)
		TYPE _MY_W_CHAR_ AS USHORT		' equivalent type
	#EndIf
#ENDIF


#IFNDEF __FB_DOS__
	' ========================================================================================
	' Macro for debug
	' To allow debugging, define _DWSTR_DEBUG_ 1 or more, in your application before including this file.
	' ========================================================================================
	#IFNDEF _DWSTR_DEBUG_
		#DEFINE _DWSTR_DEBUG_ 0
	#ENDIF
	#IFNDEF _DWSTR_DP_
		#DEFINE _DWSTR_DP_ 1
		#MACRO DWSTR_DP(st)
			#IF (_DWSTR_DEBUG_ > 0)
				print(st)
			#ENDIF
		#ENDMACRO
	#ENDIF

	#DEFINE _MY_TRAP_NUMBER_  	222  'to insure good input via ubyte constructor, better not change or always < 255
	' ========================================================================================

	' ########################################################################################
	'                                  *** DWSTR CLASS ***
	' ########################################################################################
	TYPE DWSTR

		Public:
			m_pBuffer AS wstring PTR = NULL 	' Pointer to the buffer
			m_BufferLen AS LONG = 0    		' Length in bytes of the current string in the buffer

			DECLARE CONSTRUCTOR
			DECLARE CONSTRUCTOR (BYVAL nCapacity AS Ulong)				' can create specific "fixed" sized buffer (can be lower than the initial 260)
			DECLARE CONSTRUCTOR (BYREF ansiStr AS STRING )
			DECLARE CONSTRUCTOR (BYVAL pwszStr AS WSTRING PTR)
			DECLARE CONSTRUCTOR (BYREF ust AS DWSTR)
			DECLARE CONSTRUCTOR (BYREF pub AS UBYTE PTR, BYVAL len1 AS LONG, BYVAL size1 AS LONG = 0)   '**** special interresting for speed
			DECLARE DESTRUCTOR
			DECLARE SUB ResizeBuffer (BYVAL nValue AS LONG)
			DECLARE PROPERTY Capacity () AS LONG
			DECLARE PROPERTY Capacity (BYVAL nValue AS LONG)
			DECLARE SUB Clear
			DECLARE SUB Add (BYREF ust AS DWSTR)
			DECLARE SUB Add (BYVAL pwszStr AS WSTRING PTR, BYVAL nLen AS LONG = -1)
			DECLARE SUB Add (BYREF ansiStr AS STRING)
			DECLARE PROPERTY Char(BYVAL nIndex AS LONG) AS _MY_W_CHAR_
			DECLARE PROPERTY Char(BYVAL nIndex AS LONG, BYVAL nValue AS _MY_W_CHAR_)
			DECLARE OPERATOR [] (BYVAL nIndex AS LONG) AS _MY_W_CHAR_
			DECLARE FUNCTION DelChars (BYVAL nIndex AS LONG, BYVAL nCount AS LONG) AS LONG
			DECLARE FUNCTION Insert (BYREF ansiStr AS STRING, BYVAL nIndex AS LONG) AS LONG
			DECLARE FUNCTION Insert (BYVAL pwszStr AS WSTRING PTR, BYVAL nIndex AS LONG) AS LONG
			DECLARE FUNCTION Insert (BYREF ust AS DWSTR, BYVAL nIndex AS LONG) AS LONG
			DECLARE FUNCTION Replace (BYREF ansiStr AS STRING, BYVAL nIndex AS LONG) AS LONG
			DECLARE FUNCTION Replace (BYVAL pwszStr AS WSTRING PTR, BYVAL nIndex AS LONG) AS LONG
			DECLARE FUNCTION Replace (BYREF ust AS DWSTR, BYVAL nIndex AS LONG) AS LONG
			DECLARE FUNCTION Get () BYREF AS WSTRING
			DECLARE OPERATOR CAST () BYREF AS WSTRING
			DECLARE OPERATOR CAST () AS ANY PTR
			DECLARE OPERATOR LET (BYREF ansiStr AS STRING)
			DECLARE OPERATOR LET (BYREF wszStr AS CONST WSTRING)
			DECLARE OPERATOR LET (BYREF pwszStr AS WSTRING PTR)
			DECLARE OPERATOR LET (BYREF ust AS DWSTR)
			DECLARE OPERATOR += (BYREF wszStr AS WSTRING)
			DECLARE OPERATOR += (BYREF ust AS DWSTR)
			DECLARE OPERATOR += (BYREF ansiStr AS STRING)
			DECLARE OPERATOR &= (BYREF wszStr AS WSTRING)
			DECLARE OPERATOR &= (BYREF ust AS DWSTR)
			DECLARE OPERATOR &= (BYREF ansiStr AS STRING)
			DECLARE OPERATOR &= (BYVAL num AS DOUBLE)       '***** new
		Private:
			m_Capacity 	AS LONG = 0        	' The total size of the buffer
			m_GrowSize 	AS LONG = 260   		' How much to grow the buffer by when required ! can be reduced but better to let as it is for speed
			m_Flag 		AS LONG = 0				' **** flag to avoid multiple alloc/copy of the same pointer and insure not deallocated prematurely
			'***** new								' set to 1 only via constructor ubyte ptr ... when size < 0 and check = _MY_TRAP_NUMBER_
			DECLARE SUB WriteBuffer (BYVAL addrMemory AS WSTRING PTR, BYVAL nNumBytes AS LONG)   '****
			DECLARE SUB AppendBuffer (BYVAL addrMemory AS WSTRING PTR, BYVAL nNumBytes AS LONG)
			DECLARE SUB InsertBuffer (BYVAL addrMemory AS WSTRING PTR, BYVAL nIndex AS LONG, BYVAL nNumBytes AS LONG)
			DECLARE SUB ReplaceBuffer (BYVAL addrMemory AS WSTRING PTR, BYVAL nIndex AS LONG, BYVAL nNumBytes AS LONG) 			'**** new

	END TYPE
	' ########################################################################################

	#IFDEF __FB_LINUX__
		DECLARE FUNCTION Dwstr_To_Str2 (byref DWsrc as DWSTR, byref codepage as string) as string
		DECLARE function MbsToWcs_2 (byref code as string, byval dest as wstring ptr, byval src as zstring ptr, byval nb as long )as long
	#ENDIF
	' ========================================================================================
	' DWSTR constructors
	' ========================================================================================
	PRIVATE CONSTRUCTOR DWSTR
		DWSTR_DP("+++BEGIN- DWSTR CONSTRUCTOR Default")
		this.ResizeBuffer(m_GrowSize)   ' Create the initial buffer
		DWSTR_DP("END DWSTR CONSTRUCTOR Default - " & WSTR(m_pBuffer))
	END CONSTRUCTOR
	' ========================================================================================
	PRIVATE CONSTRUCTOR DWSTR (BYVAL nCapacity AS Ulong)
		DWSTR_DP("+++BEGIN- DWSTR CONSTRUCTOR Capacity: " & WSTR(nCapacity))
		if nCapacity = 0 then nCapacity = 32 	' to insure a minimal buffer
		this.ResizeBuffer(nCapacity)   			' Create the nCapacity buffer
		m_BufferLen = 0								' clear if already exists
		m_Flag = 1										' to not be double allocated/deallocated
		DWSTR_DP("-END- DWSTR CONSTRUCTOR Capacity - " & WSTR(m_pBuffer))
	END CONSTRUCTOR
	' ========================================================================================
	PRIVATE CONSTRUCTOR DWSTR (BYVAL pwszStr AS WSTRING PTR)
		DWSTR_DP("+++BEGIN- DWSTR CONSTRUCTOR WSTRING PTR - " & WSTR(pwszStr))
		this.Add(pwszStr)               ' Add the passed WSTRING
		DWSTR_DP("-END- DWSTR CONSTRUCTOR WSTRING PTR - " & WSTR(m_pBuffer))
	END CONSTRUCTOR
	' ========================================================================================
	PRIVATE CONSTRUCTOR DWSTR (BYREF ansiStr AS STRING )
		DWSTR_DP("+++BEGIN- DWSTR CONSTRUCTOR STRING - " & WSTR(STRPTR(ansiStr)))
		this.Add(ansiStr)    ' Add the passed ansi string
		DWSTR_DP("-END- DWSTR CONSTRUCTOR STRING - " & WSTR(m_pBuffer))
	END CONSTRUCTOR
	' ========================================================================================
	PRIVATE CONSTRUCTOR DWSTR (BYREF ust AS DWSTR)
		DWSTR_DP("+++BEGIN- DWSTR CONSTRUCTOR DWSTR - " & WSTR(ust.m_pBuffer))
		if ust.m_Flag = 0 then
			this.Add(ust.m_pBuffer, ust.m_BufferLen)   ' Add the passed DWSTR
		else
			this.m_BufferLen = ust.m_BufferLen
			this.m_pBuffer = ust.m_pBuffer
			this.m_Capacity = ust.m_Capacity
		end if
		DWSTR_DP("-END- DWSTR CONSTRUCTOR DWSTR - " & WSTR(m_pBuffer))
	END CONSTRUCTOR
	' ========================================================================================
	PRIVATE CONSTRUCTOR DWSTR (BYREF pub AS UBYTE PTR, BYVAL len1 AS LONG, BYVAL size1 AS LONG = 0) '****
		DWSTR_DP("+++BEGIN- DWSTR CONSTRUCTOR UBYTE - " & WSTR(pub))
		IF pub = 0 or len1 = 0 THEN
			this.ResizeBuffer(m_GrowSize)
			DWSTR_DP("    - DWSTR CONSTRUCTOR UBYTE - INIT ONLY : " & WSTR(this.m_pBuffer))
		elseif size1 = -1 and pub[(len1 + 1) * _MY_SIZE_WSTRING_] = _MY_TRAP_NUMBER_ then   '**** trap if bad option
			this.m_BufferLen = len1
			this.m_pBuffer = cast(wstring ptr, pub)
			this.m_Capacity = len1
			this.m_Flag = 1 'to avoid deallocating the buffer on destructor and avoid double copy too in LET step
			DWSTR_DP("    - DWSTR CONSTRUCTOR UBYTE - ASSIGN ONLY  to " & WSTR(this.m_pBuffer))
		ELSE
			if size1 = 0 THEN
				size1 = len1 + 1
			else
				if len1 + 1 > size1 THEN size1 = len1 + 1
			END IF
			m_BufferLen = len1
			ResizeBuffer(size1)
			memcpy(m_pBuffer , cast(any ptr, pub), m_BufferLen * _MY_SIZE_WSTRING_)
			' Mark the end of the string with a null
			m_pBuffer[m_BufferLen] = 0
			DWSTR_DP("    - DWSTR CONSTRUCTOR UBYTE - COPY BUFFER  to " & WSTR(this.m_pBuffer))
		END IF
		DWSTR_DP("-END- DWSTR CONSTRUCTOR UBYTE - " & WSTR(this.m_pBuffer))
	END CONSTRUCTOR
	

	' ========================================================================================
	' Destructor
	' ========================================================================================
	PRIVATE DESTRUCTOR DWSTR
		DWSTR_DP("***DWSTR DESTRUCTOR - buffer: " & WSTR(m_pBuffer))
		IF m_pBuffer <> 0 and m_Flag = 0 THEN '**** if mFlag = 1 do not deallocate, because not allocated here
			Deallocate(m_pBuffer)
			m_pBuffer = NULL
		else
			DWSTR_DP("   DWSTR DESTRUCTOR - not deallocated: " & WSTR(m_pBuffer))
		end if
	END DESTRUCTOR
	' ========================================================================================

	' ========================================================================================
	' dereferencing DWSTR  same as *zstring  or as *wstring.
	'   in fact dereferencing the wstring ptr buffer, just a short way to get the value as byref as wstring
	' ========================================================================================
	PRIVATE OPERATOR *(BYREF ust AS DWSTR) BYREF AS WSTRING
		DWSTR_DP("DWSTR OPERATOR * buffer: " & WSTR(ust.m_pBuffer))
		RETURN *ust.m_pBuffer
	END OPERATOR

	' ========================================================================================
	' Returns the length of the DWSTR.
	' ========================================================================================
	PRIVATE OPERATOR LEN (BYREF ust AS DWSTR) AS LONG
		DWSTR_DP("DWSTR OPERATOR LEN - len: " & WSTR(ust.m_BufferLen ))
		RETURN (ust.m_BufferLen )
	END OPERATOR

	' ========================================================================================
	' Cast implicitly DWSTR to different types.
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.CAST () BYREF AS WSTRING
		DWSTR_DP("DWSTR CAST BYREF AS WSTRING - buffer: " & WSTR(m_pBuffer))
		RETURN *m_pBuffer
	END OPERATOR
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.CAST () AS ANY PTR
		DWSTR_DP("DWSTR CAST ANY PTR - buffer: " & WSTR(m_pBuffer))
		RETURN cast(ANY PTR, m_pBuffer)
	END OPERATOR

	' ========================================================================================
	' Assigns new text to the DWSTR.
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.Let (BYREF wszStr AS CONST WSTRING)
		DWSTR_DP("DWSTR LET WSTRING")
		this.Clear
		this.Add(wszStr)
	END OPERATOR

	' ========================================================================================
	PRIVATE OPERATOR DWSTR.Let (BYREF ansiStr AS STRING)
		DWSTR_DP("DWSTR LET STRING")
		this.Clear
		this.Add(ansiStr)
	END OPERATOR

	' ========================================================================================
	PRIVATE OPERATOR DWSTR.Let (BYREF pwszStr AS WSTRING PTR)
		DWSTR_DP("DWSTR LET WSTRING PTR")
		IF pwszStr = NULL THEN EXIT OPERATOR
		this.Clear
		this.Add(pwszStr)
	END OPERATOR

	' ========================================================================================
	PRIVATE OPERATOR DWSTR.Let (BYREF ust AS DWSTR)
		DWSTR_DP("DWSTR LET DWSTR")
		IF m_pBuffer = ust.m_pBuffer THEN EXIT OPERATOR   ' // Ignore ust = ust
		if ust.m_Flag = 1 THEN
			if m_pBuffer then
				DWSTR_DP("   DWSTR LET DWSTR : - deallocate buffer: " & WSTR(m_pBuffer))
				deallocate m_pBuffer
			end if
			m_pBuffer = ust.m_pBuffer
			m_BufferLen = ust.m_BufferLen
			m_Capacity = ust.m_capacity
			DWSTR_DP("   DWSTR LET DWSTR : - assign buffer: " & WSTR(m_pBuffer))
		else
			DWSTR_DP("   DWSTR LET DWSTR : - copy buffer: " & WSTR(m_pBuffer))
			this.Clear
			this.Add(ust.m_pBuffer, ust.m_BufferLen)
		end if
	END OPERATOR

	' ========================================================================================
	' Get the internal buffer 'content' as wstring
	' ========================================================================================
	PRIVATE FUNCTION DWSTR.Get() BYREF AS WSTRING
		DWSTR_DP("DWSTR Get content - buffer: " & WSTR(m_pBuffer))
		RETURN *m_pBuffer
	END FUNCTION

	' ========================================================================================
	' ResizeBuffer
	' Increase the size of the internal buffer capacity
	' ========================================================================================
	PRIVATE SUB DWSTR.ResizeBuffer (BYVAL nValue AS LONG)
		DWSTR_DP("DWSTR ResizeBuffer - Value = " & WSTR(nValue))
		' Allocate or reallocate buffer to the right size preversing data when exiting
		DWSTR_DP("DWSTR ResizeBuffer ;   old buffer = " & (WSTR(m_pBuffer)))
		IF m_pBuffer THEN
			m_pBuffer = reAllocate(m_pBuffer, (nValue + 1))
		else
			m_pBuffer = Allocate((nValue + 1))
		END IF
		DWSTR_DP("DWSTR ResizeBuffer ;   new buffer = " & (WSTR(m_pBuffer)))
		m_Capacity = nValue
		' Mark the end of the string with a null
		m_pBuffer[m_BufferLen] = 0
	END SUB

	' ========================================================================================
	' (Private) Write the number of bytes from the specified memory address to the buffer.     **** new function
	' ========================================================================================
	PRIVATE SUB DWSTR.WriteBuffer (BYVAL addrMemory AS WSTRING PTR, BYVAL nNumBytes AS LONG)
		m_BufferLen = nNumBytes  'assign m_BufferLen before modifying nNumBytes
		' the idea here is to have at least some buffer reserve to not have always to resize if append after
		if nNumBytes < m_GrowSize /2   THEN
			nNumBytes = m_GrowSize
		else
			nNumBytes = nNumBytes * 2
		end if
		DWSTR_DP("DWSTR WriteBuffer " & WSTR(nNumBytes))
		this.ResizeBuffer( nNumBytes )
		memcpy(m_pBuffer , addrMemory, m_BufferLen * _MY_SIZE_WSTRING_)
		' Mark the end of the string with a null
		m_pBuffer[m_BufferLen] = 0
		DWSTR_DP("--END - DWSTR WriteBuffer " & WSTR(m_BufferLen))
	END SUB

	' ========================================================================================
	' (Private) Append the number of bytes from the specified memory address to the end of the buffer.
	' ========================================================================================
	PRIVATE SUB DWSTR.AppendBuffer (BYVAL addrMemory AS WSTRING PTR, BYVAL nNumBytes AS LONG)
		DWSTR_DP("DWSTR AppendBuffer " & WSTR(m_BufferLen) & " " & WSTR(nNumBytes))
		IF (m_BufferLen + nNumBytes) > m_Capacity THEN this.ResizeBuffer((m_BufferLen + nNumBytes)* 2)
		memcpy(m_pBuffer + m_BufferLen, addrMemory, nNumBytes * _MY_SIZE_WSTRING_)
		m_BufferLen += nNumBytes
		' Mark the end of the string with a null
		m_pBuffer[m_BufferLen] = 0
		DWSTR_DP("--END - DWSTR AppendBuffer " & WSTR(m_BufferLen))
	END SUB

	' ========================================================================================
	' The string parameter is appended to the string held in the class. If the internal string
	' buffer overflows, the class will automatically extend it to an appropriate size.
	' ========================================================================================
	PRIVATE SUB DWSTR.Add (BYREF ust AS DWSTR)
		DWSTR_DP("DWSTR Add DWSTR - LEN = " & WSTR(ust.m_BufferLen ))
		' Incoming string is already in wide format, simply copy it to the buffer.
		IF ust.m_BufferLen = 0 THEN
			if m_pBuffer = NULL THEN
				if ust.m_Capacity = 0 THEN
					this.ResizeBuffer(m_GrowSize)'****  check if capacity
				else
					this.ResizeBuffer(ust.m_Capacity)
				END IF
			END IF
			RETURN
		END IF
		' Copy the string into the buffer and update the length
		IF m_pBuffer = NULL THEN
			this.WriteBuffer(ust.m_pBuffer, ust.m_BufferLen)
		ELSE
			this.AppendBuffer(ust.m_pBuffer, ust.m_BufferLen)
		END IF
	END SUB

	' ========================================================================================
	PRIVATE SUB DWSTR.Add (BYVAL pwszStr AS WSTRING PTR, BYVAL nLen AS LONG = -1)
		DWSTR_DP("DWSTR Add WSTRING")
		' Incoming string is already in wide format
		DIM AS LONG nLenString
		if nLen > -1 THEN
			nLenString = nLen
		else
			nLenString = .LEN(*pwszStr)
      END IF
		IF nLenString = 0 THEN
			IF m_pBuffer = NULL THEN this.ResizeBuffer(m_GrowSize)
			RETURN
		END IF
		' Copy the string into the buffer and update the length
		IF m_pBuffer = NULL THEN '***
			this.WriteBuffer(pwszStr, nLenString )
		ELSE
			this.AppendBuffer(pwszStr, nLenString)
		END IF
	END SUB

	' ========================================================================================
	PRIVATE SUB DWSTR.Add (BYREF ansiStr AS STRING)
		DWSTR_DP("DWSTR Add STRING ")
		DIM as ulong ulen = LEN(ansiStr)
		IF ulen = 0 THEN
			if m_pBuffer = NULL THEN this.ResizeBuffer(m_GrowSize)
			RETURN
		END IF
		DIM as ulong i1 = ulen + 1

		DIM s2 AS string
		DIM ps2 as wstring ptr
		' Create the wide string from the incoming ansi string
		s2 = string(i1 * _MY_SIZE_WSTRING_, 0)
		ps2 = cast(wstring ptr, strptr(s2))
		IF ps2 THEN
			#IFDEF __FB_LINUX__   	'	assumed it is always utf8 coded
				UTFToWChar(1, strptr(ansiStr), ps2, cast(integer ptr,@i1))
				ulen = i1
         #ELSE							'	assumed it is not utf8 coded
				MultiByteToWideChar(0, MB_PRECOMPOSED, STRPTR(ansiStr), ulen, ps2 , i1)
			#ENDIF
			' Copy the string into the buffer and update the length
			IF m_pBuffer = NULL THEN
				this.WriteBuffer(ps2, ulen )
			ELSE
				this.AppendBuffer(ps2, ulen )
			END IF
		ELSE
			RETURN
		END IF
	END SUB

	' ========================================================================================
	' Appends a wstring to the DWSTR
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.+= (BYREF wszStr AS WSTRING)
		DWSTR_DP("DWSTR OPERATOR += WSTRING")
		this.Add(wszStr)
	END OPERATOR

	' ========================================================================================
	' Appends a string to the DWSTR
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.+= (BYREF ansiStr AS STRING)
		DWSTR_DP("DWSTR OPERATOR += STRING")
		this.Add(ansiStr)
	END OPERATOR

	' ========================================================================================
	' Appends a DWSTR to the DWSTR
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.+= (BYREF ust AS DWSTR)
		DWSTR_DP("DWSTR OPERATOR += DWSTR")
		this.Add(ust.m_pBuffer, ust.m_BufferLen)
	END OPERATOR

	' ========================================================================================
	' Appends a WSTRING to the DWSTR
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.&= (BYREF wszStr AS WSTRING)
		DWSTR_DP("DWSTR OPERATOR &= WSTRING")
		this.Add(wszStr)
	END OPERATOR

	' ========================================================================================
	' Appends a string to the DWSTR
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.&= (BYREF ansiStr AS STRING)
		DWSTR_DP("DWSTR OPERATOR &= STRING")
		this.Add(ansiStr)
	END OPERATOR

	' ========================================================================================
	' Appends a DWSTR to the DWSTR
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.&= (BYREF ust AS DWSTR)
		DWSTR_DP("DWSTR OPERATOR &= DWSTR")
		this.Add(ust.m_pBuffer, ust.m_BufferLen)
	END OPERATOR

	' ========================================================================================
	' Appends a DOUBLE( or any numeric value) "string representation" to the DWSTR
	' can accept any kind of numeric type but better not use explicit float type because
	' the num value is implicitly converted to double and the fractionnal part is altered (if exists)
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.&= (BYVAL num AS DOUBLE)
		DWSTR_DP("DWSTR OPERATOR &= DOUBLE")
		this.Add(str(num))
	END OPERATOR

	' ========================================================================================
	' The size of the internal string buffer is retrieved and returned to the caller. The size
	' is the number of characters which can be stored without further expansion.
	' ========================================================================================
	PRIVATE PROPERTY DWSTR.Capacity() AS LONG
		DWSTR_DP("DWSTR PROPERTY GET Capacity")
		PROPERTY = m_Capacity
	END PROPERTY

	' ========================================================================================
	' The internal string buffer is expanded to the specified number of characters. If the new
	' capacity is smaller or equal to the current capacity, no operation is performed.
	' ========================================================================================
	PRIVATE PROPERTY DWSTR.Capacity (BYVAL nValue AS LONG)
		DWSTR_DP("DWSTR PROPERTY SET Capacity")
		IF nValue <= m_Capacity THEN EXIT PROPERTY
		this.ResizeBuffer(nValue)
	END PROPERTY

	' ========================================================================================
	' Returns the corresponding unicode integer representation of the character at the position
	' specified by the nIndex parameter (1 for the first character, 2 for the second, etc.).
	' If nIndex is beyond the current length of the string, a 0 is returned.
	' ========================================================================================
	PRIVATE PROPERTY DWSTR.Char (BYVAL nIndex AS LONG) AS _MY_W_CHAR_ '  index counted in wstrings not in bytes,
		DWSTR_DP("DWSTR PROPERTY GET Char")
		IF (nIndex < 1) OrElse (nIndex  > m_BufferLen) THEN RETURN 0
		' Get the numeric character code at position nIndex
		nIndex -= 1
		PROPERTY = PEEK(_MY_W_CHAR_, m_pBuffer + nIndex )
	END PROPERTY

	' ========================================================================================
	' Changes the corresponding unicode integer representation of the character at the position
	' specified by the nIndex parameter (1 for the first character, 2 for the second, etc.).
	' If nIndex is beyond the current length of the string, nothing is changed.
	' ========================================================================================
	PRIVATE PROPERTY DWSTR.Char (BYVAL nIndex AS LONG, BYVAL nValue AS _MY_W_CHAR_) 'index as position in the wstring
		DWSTR_DP("DWSTR PROPERTY SET Char")
		IF (nIndex < 1) OrElse (nIndex > m_BufferLen) THEN EXIT PROPERTY
		' Set the numeric character code at position nIndex (zero based)
		nIndex -= 1
		POKE _MY_W_CHAR_, m_pBuffer + nIndex, nValue
	END PROPERTY

	' ========================================================================================
	' Returns the corresponding ASCII or Unicode integer representation of the character at
	' the position specified by the nIndex parameter. Like the get Char property, but allowing
	' to use the [] syntax, e.g. value = cws[1]. Can't be used to change a value.
	' ========================================================================================
	PRIVATE OPERATOR DWSTR.[] (BYVAL nIndex AS LONG) AS _MY_W_CHAR_
		RETURN This.m_pBuffer[nIndex-1]
	END OPERATOR

	' ========================================================================================
	' All data in the class object is erased. Actually, we only set the buffer length to zero,
	' indicating no string in the buffer. The allocated memory for the buffer is deallocated
	' when the class is destroyed.
	' ========================================================================================
	PRIVATE SUB DWSTR.Clear
		DWSTR_DP("DWSTR Clear")
		m_BufferLen = 0
		' Mark the end of the string with a null
		m_pBuffer[m_BufferLen] = 0
	END SUB

	' ========================================================================================
	' nCount characters are removed starting at the position given by nIndex.
	' nIndex = 1 for the first character, 2 for the second, etc.
	' ========================================================================================
	PRIVATE FUNCTION DWSTR.DelChars (BYVAL nIndex AS LONG, BYVAL nCount AS LONG) AS LONG
		DWSTR_DP("DWSTR DelChars")
		IF (nIndex < 1) Or (nIndex > m_BufferLen) or (nCount < 1) THEN RETURN -1
		IF nCount > m_BufferLen - nIndex + 1 THEN nCount = m_BufferLen - nIndex + 1
		memcpy(m_pBuffer + nIndex - 1, m_pBuffer + (nIndex + nCount) - 1 , (m_BufferLen - nCount) * _MY_SIZE_WSTRING_)
		m_BufferLen -= (nCount )
		' Mark the end of the string with a null
		m_pBuffer[m_BufferLen] = 0
	END FUNCTION

	' ========================================================================================
	' (Private) Insert the number of bytes from the specified memory address into the buffer.
	' ========================================================================================
	PRIVATE SUB DWSTR.InsertBuffer (BYVAL addrMemory AS WSTRING PTR, BYVAL nIndex AS LONG, BYVAL nNumBytes AS LONG)
		DWSTR_DP("DWSTR InsertBuffer")
		nIndex -= 1
		' Determine the size of the new buffer
		IF m_BufferLen + nNumBytes > m_Capacity THEN m_Capacity = (m_BufferLen + nNumBytes) * 2
		DIM pNewBuffer AS WSTRING PTR = Allocate((m_Capacity + 1 )* _MY_SIZE_WSTRING_)
		IF pNewBuffer THEN
			' Copy the existing data into the new buffer
			memcpy(pNewBuffer, m_pBuffer, (nIndex * _MY_SIZE_WSTRING_))
			memcpy(pNewBuffer + nIndex, addrMemory, nNumBytes * _MY_SIZE_WSTRING_)
			memcpy(pNewBuffer + nIndex + nNumBytes, m_pBuffer + nIndex, (m_BufferLen - nIndex) * _MY_SIZE_WSTRING_)
			Deallocate m_pBuffer
		END IF
		m_pBuffer = pNewBuffer
		m_BufferLen += nNumBytes
		' Mark the end of the string with a null
		m_pBuffer[m_BufferLen] = 0
	END SUB

	' ========================================================================================
	' The incoming string parameter is inserted in the string starting at the position of wstring
	' given by nIndex. nIndex = 1 for the first character, 2 For the second, etc.
	' If nIndex is beyond the current length of the string +1, no operation is performed.
	' ========================================================================================
	PRIVATE FUNCTION DWSTR.Insert (BYVAL pwszStr AS WSTRING PTR, BYVAL nIndex AS LONG) AS LONG
		DWSTR_DP("DWSTR Insert WSTRING")
		IF (nIndex < 1) Or (nIndex > m_BufferLen) THEN RETURN -1
		DIM AS LONG nLenString = .LEN(*pwszStr)
		IF nLenString = 0 THEN RETURN 0
		this.InsertBuffer(pwszStr, nIndex, nLenString)
		RETURN 0
	END FUNCTION

	' ========================================================================================
	PRIVATE FUNCTION DWSTR.Insert (BYREF ust1 AS DWSTR, BYVAL nIndex AS LONG) AS LONG
		DWSTR_DP("DWSTR Insert DWSTR")
		IF (nIndex < 1) Or (nIndex > m_BufferLen) THEN RETURN -1
		IF ust1.m_BufferLen = 0 THEN RETURN 0
		this.InsertBuffer(ust1.m_pBuffer, nIndex, ust1.m_BufferLen)
		RETURN 0
	END FUNCTION

	' ========================================================================================
	PRIVATE FUNCTION DWSTR.Insert (BYREF ansiStr AS STRING, BYVAL nIndex AS LONG) AS LONG
		DWSTR_DP("DWSTR Insert STRING")
		IF (nIndex < 1) Or (nIndex > m_BufferLen ) THEN RETURN -1
		DIM AS LONG nLenString = .LEN(ansiStr)
		IF nLenString = 0 THEN RETURN 0
		DIM as ulong i1 = nLenString + 1

		DIM s2 AS string
		DIM ps2 as wstring ptr
		' Create the wide string from the incoming ansi string
		s2 = string(i1 * _MY_SIZE_WSTRING_, 0)
		ps2 = cast(wstring ptr, strptr(s2))
		IF ps2 THEN
			#IFDEF __FB_LINUX__		'	assumed it is always utf8 coded
				UTFToWChar(1, strptr(ansiStr), ps2, cast(integer ptr, @i1))
				nLenString = i1
			#ELSE							'	assumed it is not utf8 coded
				MultiByteToWideChar(0, MB_PRECOMPOSED, STRPTR(ansiStr), nLenString, ps2 , i1)
			#ENDIF
			' Copy the string into the buffer and update the length
			this.InsertBuffer(ps2, nIndex, nLenString)
		ELSE
			RETURN -1
		END IF
		RETURN 0
	END FUNCTION


	' ========================================================================================
	' (Private) Replace the number of bytes from the specified memory address into the buffer at specified nIndex
	' ========================================================================================
	PRIVATE SUB DWSTR.ReplaceBuffer (BYVAL addrMemory AS WSTRING PTR, BYVAL nIndex AS LONG, BYVAL nNumBytes AS LONG)   '**** new
		DWSTR_DP("DWSTR ReplaceBuffer")
		nIndex -= 1
		' Determine the size of the new buffer
		IF nIndex + nNumBytes > m_Capacity THEN
			m_Capacity = (nIndex + nNumBytes) * 2
			m_pBuffer = reallocate(m_pBuffer,(m_Capacity + 1 )* _MY_SIZE_WSTRING_)
		END IF
		' Copy the replacing data into the buffer
		IF m_pBuffer THEN
			memcpy(m_pBuffer + nIndex, addrMemory, nNumBytes * _MY_SIZE_WSTRING_)
			if m_BufferLen < nIndex + nNumBytes  THEN
				m_BufferLen = nIndex + nNumBytes
				' Mark the end of the string with a null
				m_pBuffer[m_BufferLen] = 0
			end if
		end if
		return
	END SUB

	' ========================================================================================
	' The incoming string parameter is replacing in the string starting at the position of wstring
	' given by nIndex. nIndex = 1 for the first character, 2 For the second, etc.
	' If nIndex is beyond the current length of the string +1, no operation is performed.
	' If the number of chars to replace exceeds the len of existing DWSRT it will be resized to accept the incoming text
	' ========================================================================================
	PRIVATE FUNCTION DWSTR.Replace (BYVAL pwszStr AS WSTRING PTR, BYVAL nIndex AS LONG) AS LONG     '**** new
		DWSTR_DP("DWSTR Replace WSTRING")
		IF (nIndex < 1) Or (nIndex > m_BufferLen) THEN RETURN -1
		DIM AS LONG nLenString = .LEN(*pwszStr)
		IF nLenString = 0 THEN RETURN 0
		this.ReplaceBuffer(pwszStr, nIndex, nLenString)
		RETURN 0
	END FUNCTION

	' ========================================================================================
	PRIVATE FUNCTION DWSTR.Replace (BYREF ust1 AS DWSTR, BYVAL nIndex AS LONG) AS LONG              '**** new
		DWSTR_DP("DWSTR Replace DWSTR")
		IF (nIndex < 1) Or (nIndex > m_BufferLen) THEN RETURN -1
		IF ust1.m_BufferLen = 0 THEN RETURN 0
		this.ReplaceBuffer(ust1.m_pBuffer, nIndex, ust1.m_BufferLen)
		RETURN 0
	END FUNCTION

	' ========================================================================================
	PRIVATE FUNCTION DWSTR.Replace(BYREF ansiStr AS STRING, BYVAL nIndex AS LONG) AS LONG 		'**** new
		DWSTR_DP("DWSTR Replace STRING")
		IF (nIndex < 1) Or (nIndex > m_BufferLen ) THEN RETURN -1
		DIM AS LONG nLenString = .LEN(ansiStr)
		IF nLenString = 0 THEN RETURN 0
		DIM as ulong i1 = nLenString + 1
		DIM s2 AS string
		DIM ps2 as wstring ptr
		' Create the wide string from the incoming ansi string
		s2 = string(i1 * _MY_SIZE_WSTRING_, 0)
		ps2 = cast(wstring ptr, strptr(s2))
		IF ps2 THEN
			#IFDEF __FB_LINUX__		'	assumed it is always utf8 coded
				UTFToWChar(1, strptr(ansiStr), ps2, cast(integer ptr, @i1))
				nLenString = i1
			#ELSE							'	assumed it is not utf8 coded
				MultiByteToWideChar(0, MB_PRECOMPOSED, STRPTR(ansiStr), nLenString, ps2 , i1)
			#ENDIF
			' Copy the string into the buffer and update the length
			this.ReplaceBuffer(ps2, nIndex, nLenString)
		ELSE
			RETURN -1
		END IF
		RETURN 0
	END FUNCTION

	' ========================================================================================
	' New & OPERATORS to play with Wstring and DWSTR
	' ========================================================================================
	PRIVATE OPERATOR & (BYREF ust1 AS DWSTR, byref ust2 AS DWSTR) AS DWSTR
		DWSTR_DP("OPERATOR & DWSTR :  DWSTR & DWSTR")
		DIM pNewBuffer AS WSTRING PTR

		pNewBuffer = allocate((ust1.m_BufferLen + ust2.m_BufferLen + 2) * _MY_SIZE_WSTRING_)
		IF pNewBuffer THEN
			memcpy(pNewBuffer, ust1.m_pBuffer, ust1.m_BufferLen * _MY_SIZE_WSTRING_)
			memcpy(pNewBuffer + ust1.m_BufferLen , ust2.m_pBuffer, ust2.m_BufferLen * _MY_SIZE_WSTRING_)
			pNewBuffer[ust1.m_BufferLen + ust2.m_BufferLen] = 0
			pNewBuffer[ust1.m_BufferLen + ust2.m_BufferLen + 1] = _MY_TRAP_NUMBER_  '****extra value to check validity
			RETURN DWSTR(cast(ubyte ptr, pNewBuffer), ust1.m_BufferLen + ust2.m_BufferLen, -1)
		END IF
		RETURN ""
	END OPERATOR

	PRIVATE OPERATOR & (BYREF wst1 AS WSTRING, BYREF ust2 AS DWSTR)AS DWSTR
		DWSTR_DP("OPERATOR & DWSTR :  WSTRING & DWSTR")
		DIM AS LONG l1 = len(wst1)
		DIM pNewBuffer AS WSTRING PTR

		pNewBuffer = allocate((l1 + ust2.m_BufferLen + 2) * _MY_SIZE_WSTRING_)
		IF pNewBuffer THEN
			memcpy(pNewBuffer, @wst1, l1 * _MY_SIZE_WSTRING_)
			memcpy(pNewBuffer + l1 ,ust2.m_pBuffer, ust2.m_BufferLen * _MY_SIZE_WSTRING_)
			pNewBuffer[l1 + ust2.m_BufferLen] = 0
			pNewBuffer[l1 + ust2.m_BufferLen + 1] = _MY_TRAP_NUMBER_		'****extra value to check validity
			RETURN DWSTR(cast(ubyte ptr, pNewBuffer), l1 + ust2.m_BufferLen, -1)
		END IF
		RETURN ""
	END OPERATOR

	PRIVATE OPERATOR & (BYREF ust1 AS DWSTR, byref wst2 AS WSTRING) AS DWSTR
		DWSTR_DP("OPERATOR & DWSTR :  DWSTR & WSTRING")
		DIM AS LONG l2 = len(wst2)
		DIM pNewBuffer AS WSTRING PTR

		pNewBuffer = allocate((ust1.m_BufferLen + l2 + 2) * _MY_SIZE_WSTRING_)
		IF pNewBuffer THEN
			memcpy(pNewBuffer, ust1.m_pBuffer, ust1.m_BufferLen * _MY_SIZE_WSTRING_)
			memcpy(pNewBuffer + ust1.m_BufferLen , @wst2, l2 * _MY_SIZE_WSTRING_)
			pNewBuffer[ust1.m_BufferLen + l2] = 0
			pNewBuffer[ust1.m_BufferLen + l2 + 1] = _MY_TRAP_NUMBER_		'****extra value to check validity
			RETURN DWSTR(cast(ubyte ptr, pNewBuffer), ust1.m_BufferLen + l2, -1)
		END IF
		RETURN ""
	END OPERATOR

	PRIVATE OPERATOR & (BYREF ust1 AS DWSTR, BYVAL d1 AS DOUBLE) AS DWSTR
		DWSTR_DP("OPERATOR & DWSTR :  DWSTR & DOUBLE")
		DIM as wstring *64 ws2 = wstr(d1) 'large enough buffer
		DIM AS Long l2 = len(ws2)
		DIM pNewBuffer AS WSTRING PTR

		pNewBuffer = allocate((ust1.m_BufferLen + l2 + 2) * _MY_SIZE_WSTRING_)
		IF pNewBuffer THEN
			memcpy(pNewBuffer, ust1.m_pBuffer, ust1.m_BufferLen * _MY_SIZE_WSTRING_)
			memcpy(pNewBuffer + ust1.m_BufferLen , @ws2, l2 * _MY_SIZE_WSTRING_)
			pNewBuffer[ust1.m_BufferLen + l2] = 0
			pNewBuffer[ust1.m_BufferLen + l2 + 1] = _MY_TRAP_NUMBER_		'****extra value to check validity
			RETURN DWSTR(cast(ubyte ptr, pNewBuffer), ust1.m_BufferLen + l2, -1)
		END IF
		RETURN ""
	END OPERATOR

	PRIVATE OPERATOR & (BYVAL d1 AS DOUBLE, BYREF ust1 AS DWSTR) AS DWSTR
		DWSTR_DP("OPERATOR & DWSTR :  DWSTR & DOUBLE")
		DIM as wstring *64 ws2 = wstr(d1) 'large enough buffer
		DIM AS Long l2 = len(ws2)
		DIM pNewBuffer AS WSTRING PTR

		pNewBuffer = allocate((ust1.m_BufferLen + l2 + 2) * _MY_SIZE_WSTRING_)
		IF pNewBuffer THEN
			memcpy(pNewBuffer, @ws2, l2 * _MY_SIZE_WSTRING_)
			memcpy(pNewBuffer + l2, ust1.m_pBuffer, ust1.m_BufferLen * _MY_SIZE_WSTRING_)
			pNewBuffer[ust1.m_BufferLen + l2] = 0
			pNewBuffer[ust1.m_BufferLen + l2 + 1] = _MY_TRAP_NUMBER_		'****extra value to check validity
			RETURN DWSTR(cast(ubyte ptr, pNewBuffer), ust1.m_BufferLen + l2, -1)
		END IF
		RETURN ""
	END OPERATOR

	' ========================================================================================
	' REDEFINITION / REPLACEMENT of STRPTR to be able to use that keyword with DWSTR
	' ========================================================================================
	#IFNDEF MY_STRPTR_REDEFINITION
		'#PRINT
		'#PRINT  ====>  REDEFINITION OF STRPTR ADDING DWSTR SUPPORT  <====
		'#PRINT
		#UNDEF STRPTR										' here is the trick undefining the normal STRPTR keyword

		TYPE MY_STRING_FB_								' type to mimic the string struct and get access to each element
			DIM data1 	AS ZSTRING PTR
			DIM len1 	AS LONG
			DIM size1 	AS LONG
		END TYPE

		'replacement functions and extension to be able to use strptr() even with DWSTR
		DECLARE FUNCTION STRPTR OVERLOAD( BYREF str1 AS CONST STRING ) AS ZSTRING PTR		'same
		DECLARE FUNCTION STRPTR OVERLOAD( BYREF ws1 AS CONST WSTRING ) AS ZSTRING PTR		'same, remark : gives same strange return
		DECLARE FUNCTION STRPTR OVERLOAD( BYREF DWSTR1 AS CONST DWSTR ) AS WSTRING PTR	'new, gives same behaviour as others
		DECLARE FUNCTION STRPTR OVERLOAD( BYREF str1 AS STRING ) AS ZSTRING PTR		'same
		DECLARE FUNCTION STRPTR OVERLOAD( BYREF ws1 AS WSTRING ) AS ZSTRING PTR		'same, remark : gives same strange return
		DECLARE FUNCTION STRPTR OVERLOAD( BYREF DWSTR1 AS DWSTR ) AS WSTRING PTR	'new, gives same behaviour as others

		PRIVATE FUNCTION STRPTR ( BYREF str1 AS CONST STRING ) AS ZSTRING PTR
			DWSTR_DP("STRPTR CONST STRING")
			if @str1 = NULL THEN RETURN NULL
			DIM my_lhs AS MY_STRING_FB_ PTR = CAST(MY_STRING_FB_ PTR, @str1)
			RETURN my_lhs->data1
		END FUNCTION

		PRIVATE FUNCTION STRPTR ( BYREF ws1 AS CONST WSTRING ) AS ZSTRING PTR
			DWSTR_DP("STRPTR CONST WSTRING")
			RETURN CAST(ZSTRING PTR, @ws1)
		END FUNCTION

		PRIVATE FUNCTION STRPTR ( BYREF DWSTR1 AS CONST DWSTR ) AS WSTRING PTR
			DWSTR_DP("STRPTR CONST DWSTR")
			RETURN CAST( WSTRING PTR, DWSTR1.m_Pbuffer)
		END FUNCTION

		PRIVATE FUNCTION STRPTR ( BYREF str1 AS STRING ) AS ZSTRING PTR
			DWSTR_DP("STRPTR STRING")
			if @str1 = NULL THEN RETURN NULL
			DIM my_lhs AS MY_STRING_FB_ PTR = CAST(MY_STRING_FB_ PTR, @str1)
			RETURN my_lhs->data1
		END FUNCTION

		PRIVATE FUNCTION STRPTR ( BYREF ws1 AS WSTRING ) AS ZSTRING PTR
			DWSTR_DP("STRPTR WSTRING")
			RETURN CAST(ZSTRING PTR, @ws1)
		END FUNCTION

		PRIVATE FUNCTION STRPTR ( BYREF DWSTR1 AS DWSTR ) AS WSTRING PTR
			DWSTR_DP("STRPTR DWSTR")
			RETURN CAST( WSTRING PTR, DWSTR1.m_Pbuffer)
		END FUNCTION

		#DEFINE MY_STRPTR_REDEFINITION
	#ENDIF

	' ========================================================================================
		' Returns the address of the DWSTR buffer.  Use the overloaded '-' global operator.
		' That operator is interresting because it does not need the () to work, and it is 'free' for that class
	' ========================================================================================
	PRIVATE OPERATOR - (BYREF dwstr1 as DWSTR)AS WSTRING PTR  'strptr equivalent form for dwstr
		DWSTR_DP( "OPERATOR  '-' for strptr  DWSTR :  WSTRING PTR = " & WSTR(dwstr1.m_pBuffer))
		return dwstr1.m_pBuffer
	END OPERATOR

	' ========================================================================================
	' RIGHT overloaded function , for DWSTR as input and output
	' ========================================================================================
	PRIVATE FUNCTION RIGHT( BYREF Ust AS DWSTR, BYVAL n AS LONG )AS DWSTR
		DWSTR_DP("DWSTR -RIGHT FUNCTION-")
		if Ust.m_BufferLen = 0 or n <= 0 THEN
			RETURN ""
		elseif n > Ust.m_BufferLen THEN
			RETURN Ust.m_pBuffer
		else
			RETURN Ust.m_pBuffer + Ust.m_BufferLen - n
		END IF
	END FUNCTION

	' ========================================================================================
	' LEFT overloaded function , for DWSTR as input and output
	' ========================================================================================
	PRIVATE FUNCTION LEFT( BYREF Ust AS DWSTR, BYVAL n AS LONG )AS DWSTR
		DWSTR_DP("DWSTR -LEFT FUNCTION-")
		IF Ust.m_BufferLen = 0 or n <= 0 THEN
			FUNCTION = ""
		ELSEIF  n > Ust.m_BufferLen THEN
			FUNCTION = Ust.m_pBuffer
		ELSE
			dim as ushort u1
			u1 = Ust.m_pBuffer[n]
			Ust.m_pBuffer[n] = 0
			FUNCTION = Ust.m_pBuffer
			Ust.m_pBuffer[n] = u1
		END IF
	END FUNCTION

'======================================================================================================
' just a test to see the impact on string speed process! , better to use mid with *    e.g.  mid(1, *dwstr, 8)
'======================================================================================================
	PRIVATE  Function Dw_Mid  ( BYREF ust AS DWSTR, BYVAL start as long, BYVAL n as long = &h7FFFFFFE )AS DWSTR
		if start < 1 or start > Ust.m_BufferLen or n < 1 THEN function = "" : exit function
		dim as _MY_W_CHAR_ nstop
		dim as wstring ptr pw1 = Ust.m_pBuffer + start - 1
		if n > Ust.m_BufferLen - start + 1 THEN
			function = pw1
		else
			nstop = 	Ust.m_pBuffer[start + n - 1]
			Ust.m_pBuffer[start + n - 1] = 0
			function = pw1
			Ust.m_pBuffer[start + n - 1] = nstop
      END IF
	END function

	' ========================================================================================
	' Val overloaded function , using DWSTR as input
	' ========================================================================================
	PRIVATE FUNCTION VAL (BYREF ust AS DWSTR) AS DOUBLE
		RETURN VAL(*(Ust.m_pBuffer))
	END FUNCTION

	' ========================================================================================
	#IFDEF __FB_LINUX__
		PRIVATE FUNCTION Dw_Wstr(BYREF ansiStr AS STRING, BYREF nCodePage AS STRING = "")BYREF AS WSTRING
			DIM as ulong i1 = len(ansiStr)
			if i1 = 0 THEN return wstr("")
			DIM ps2 as wstring ptr = allocate ( (i1 + 2) * _MY_SIZE_WSTRING_ )
			if ps2 THEN
				IF nCodePage = "" or instr(ucase(nCodePage),"UTF-8") > 0 THEN  'assuming the default charset is always UTF-8
					UTFToWChar(1, strptr(ansiStr), ps2, cast (integer ptr,@i1))
				ELSE
					MbsToWcs_2 (nCodePage, ps2, strptr(ansiStr), i1)
				END IF
				ps2[i1] = 0
				ps2[i1 + 1] = _MY_TRAP_NUMBER_		'**** extra value to check validity
				RETURN *DWSTR(cast(ubyte ptr, ps2), i1, -1)
			END IF
			return wstr("")
		END FUNCTION
	#ELSE
		PRIVATE FUNCTION Dw_Wstr(BYREF ansiStr AS STRING, BYVAL nCodePage AS LONG = 0)BYREF AS WSTRING
			DIM as ulong i1 = len(ansiStr)
			if i1 = 0 THEN return wstr("")
			DIM ps2 as wstring ptr = allocate ( (i1 + 2) * _MY_SIZE_WSTRING_ )
			if ps2 THEN
				IF nCodePage = CP_UTF8 THEN
					UTFToWChar(1, strptr(ansiStr), ps2, cast(integer ptr, @i1))  ' **** much faster than 2 times MultiByteToWideChar
				ELSE
					MultiByteToWideChar(nCodePage, MB_PRECOMPOSED, STRPTR(ansiStr), i1, ps2 , i1 + 2)
				END IF
				ps2[i1] = 0
				ps2[i1 + 1] = _MY_TRAP_NUMBER_		'**** extra value to check validity
				RETURN *DWSTR(cast(ubyte ptr, ps2), i1, -1)
			END IF
			return wstr("")
		END FUNCTION
	#ENDIF
	' ========================================================================================
	#IFDEF __FB_LINUX__
		PRIVATE FUNCTION Dw_Str(BYREF ust1 AS DWSTR, BYREF nCodePage AS STRING = "") AS STRING
			IF ust1.m_BufferLen = 0 THEN RETURN ""
			DIM ansiStr AS STRING
			IF nCodePage = "" or instr(ucase(nCodePage),"UTF-8") > 0 THEN  'assuming the default charset is always UTF-8
				dim i1 as integer = ust1.m_BufferLen * 5 + 1						'if all unicode chars use 5 bytes in utf8
				ansiStr = string(i1, 0)
				return *cast(zstring ptr,WCharToUTF(1, ust1.m_pBuffer, ust1.m_BufferLen, strptr(ansiStr), @i1))
			ELSE
				RETURN Dwstr_To_Str2(ust1, nCodePage)
			END IF
		END FUNCTION
	#ELSE
		PRIVATE FUNCTION Dw_Str(BYREF ust1 AS DWSTR, BYVAL nCodePage AS LONG = 0)AS STRING
			IF ust1.m_BufferLen = 0 THEN RETURN ""
			DIM ansiStr AS STRING
			IF nCodePage = CP_UTF8 THEN
				dim i1 as ulong = ust1.m_BufferLen * 5 +1 						'if all unicode chars use 5 bytes in utf8
				ansiStr = string(i1, 0)
				return *cast(zstring ptr,WCharToUTF(1, ust1.m_pBuffer, ust1.m_BufferLen, strptr(ansiStr), Cast(Integer Ptr, @i1)))'**** much faster than 2 times WideCharToMultiByte
			ELSE
				ansiStr = string(ust1.m_BufferLen , 0)
				WideCharToMultiByte(nCodePage, 0, cast(wstring ptr,ust1.m_pBuffer), ust1.m_BufferLen , STRPTR(ansiStr), ust1.m_BufferLen + 1 , NULL, NULL)
				RETURN ansiStr
			END IF
		END FUNCTION
	#ENDIF

	' ========================================================================================
	' Equivalent to string() function , creates a DWSTR with ncar  of specific icode "unicode char"
	' ========================================================================================
	PRIVATE FUNCTION Dw_String(BYVAL ncar AS LONG, BYREF icode AS ULONG = 0)BYREF AS WSTRING 'DWSTR  '****
		IF icode = 0 THEN RETURN *DWSTR(ncar)  'use capacity constructor just to allocate mem
		IF icode > &H10FFFF THEN RETURN wstr("")
		dim as wstring ptr pw1
		dim x as long
		#IFNDEF __FB_LINUX__
			if (icode >= &H10000 and icode <= &H10FFFF) then
				dim as ulong hi = ((icode - &H10000) / &H400) + &HD800
				dim as ulong lo = ((icode - &H10000) mod &H400) + &HDC00
				ncar = ncar * 2
				pw1 = allocate (( ncar  + 2) * _MY_SIZE_WSTRING_ )'cast(wstring ptr, strptr(str1))
				FOR x = 0 to (ncar - 1) step 2
					pw1[x] = hi
					pw1[x + 1] = lo
				next
			else
				pw1 = allocate (( ncar  + 2) * _MY_SIZE_WSTRING_ )'cast(wstring ptr, strptr(str1))
				FOR x = 0 to ncar - 1
					pw1[x] = icode
				NEXT
			end if
		#ELSE
			pw1 = allocate (( ncar  + 2) * _MY_SIZE_WSTRING_ )'cast(wstring ptr, strptr(str1))
			FOR x = 0 to ncar - 1
				pw1[x] = icode
			NEXT
		#ENDIF
		pw1[ncar] = 0
		pw1[ncar + 1] = _MY_TRAP_NUMBER_		'**** extra value to check validity
		RETURN *DWSTR(cast(ubyte ptr, pw1), ncar, -1)
	END FUNCTION

	' ========================================================================================
	' creates a DWSTR using a DWSTR copy with a specif ucode at position
	' ========================================================================================
	DECLARE FUNCTION Dw_Asc overload (byref dws1 as DWSTR , ByRef ucode As _MY_W_CHAR_ , ByVal position As long = 1  )AS DWSTR
	PRIVATE FUNCTION Dw_Asc (byref dws1 as DWSTR , ByRef ucode As _MY_W_CHAR_ , ByVal position As long = 1  )AS DWSTR	'****
		dws1.m_pBuffer[position-1] = ucode
		RETURN dws1.m_pBuffer
	END FUNCTION

	' ========================================================================================
	' gets "unicode" code char of a DWSTR at position
	' ========================================================================================
	PRIVATE FUNCTION Dw_Asc (byref dws1 as DWSTR , ByVal position As long = 1 ) As Ulong '****
		RETURN dws1.m_pBuffer[position - 1]
	END FUNCTION

	' ========================================================================================
	' creates a DWSTR with 1 unicode codepoint (also > FFFF,  makes surrogate pair)
	' ========================================================================================
	PRIVATE FUNCTION Dw_Chr(ByVal U1 as Ulong)BYREF AS WSTRING ' DWSTR '****
		dim hi                as Ulong
		dim lo                as Ulong
		DIM AS wstring ptr pw1 = allocate(4 * _MY_SIZE_WSTRING_)
		if pw1 THEN
			#IFDEF __FB_LINUX__
			if (U1 <= &H10FFFF) then
				pw1[0] = U1
				pw1[1] = 0
				pw1[2] = _MY_TRAP_NUMBER_		'**** extra value to check validity
				RETURN *DWSTR(cast(ubyte ptr, pw1), 1, -1)
			end if
			#ELSE   ' windows
			if (U1 >= &H10000 and U1 <= &H10FFFF) then
				hi = ((U1 - &H10000) / &H400) + &HD800
				lo = ((U1 - &H10000) mod &H400) + &HDC00
				pw1[0] = hi
				pw1[1] = lo
				pw1[2] = 0
				pw1[3] = _MY_TRAP_NUMBER_		'**** extra value to check validity
				RETURN *DWSTR(cast(ubyte ptr, pw1), 2, -1)
			elseif U1 < &H10000 then
				pw1[0] = U1
				pw1[1] = 0
				pw1[2] = _MY_TRAP_NUMBER_		'**** extra value to check validity
				RETURN *DWSTR(cast(ubyte ptr, pw1), 1, -1)
			end if
			#ENDIF    '__FB_LINUX__
		end if
		return wstr("")
	END FUNCTION

	#IFDEF __FB_LINUX__
		'function to convert DWSTR into string  (codepage dependant), using setlocale to modify the conversion behavior
		PRIVATE FUNCTION Dwstr_To_Str2 (byref DWsrc as DWSTR, byref codepage as string) as string
			dim as long iflag
			DIM loc0 as string = *setlocale(LC_CTYPE, 0) ' current codepage before , has to be restored if changed
			if ucase(loc0) <>  ucase(codepage) THEN 'verify if requested codepage is different than the current one
				setlocale(LC_CTYPE, codepage) 'change to new codepage
				iflag = 1 ' flag to remember to restore after
			END IF
			function = str(*DWsrc.m_pBuffer)'make convertion
			if iflag = 1 then setlocale(LC_CTYPE, loc0) 'restore the previous codepage
		END FUNCTION


		'function to convert zstring ptr into wstring ptr (codepage dependant), using setlocale to modify the conversion behavior
		PRIVATE FUNCTION MbsToWcs_2 (byref codepage as string, byval dest as wstring ptr, byval src as zstring ptr, byval nb as long )as long
			if nb < 0 or dest = NULL or src = NULL THEN RETURN -1
			if nb = 0 THEN return 0

			dim as long iflag
			DIM loc0 as string  = *setlocale(LC_CTYPE, 0)	' current codepage before , has to be restored if changed
			if ucase(loc0) <> ucase(codepage) THEN 'verify if requested codepage is different than the current one
				setlocale(LC_CTYPE, codepage) 'change to new codepage
				iflag = 1 ' flag to remember to restore after
			end if
			MbsToWcs(dest, src, nb)'make convertion
			if iflag = 1 then setlocale(LC_CTYPE, loc0) 'restore the previous codepage
			RETURN nb
		END FUNCTION

		'function to convert string to utf8-string, with optional codepage for input string
		PRIVATE FUNCTION StrToUtf8(BYREF st1 AS STRING, BYREF codePage as STRING = "") AS STRING
			dim len1 as long = len(st1)
			if len1 = 0 then return ""

			if codePage = "" or  instr(ucase(CodePage),"UTF-8") > 0 THEN 'assuming default is utf8
				return st1
			else
				return Dw_Str(Dw_Wstr(st1, codePage))
			END IF
		END FUNCTION

		'function to convert utf8-string to string, with optional codepage for output string
		PRIVATE FUNCTION Utf8ToStr(BYREF st1 AS STRING, BYREF codePage as STRING = "") AS STRING
			if st1 = "" or codepage = "" or instr(ucase(CodePage),"UTF-8") > 0 THEN return st1
			return Dw_Str(dw_wstr(st1), codePage)
		END FUNCTION

		'function to convert utf8-string to wstring
		PRIVATE FUNCTION Utf8ToWstr(BYREF st1 AS STRING)BYREF AS WSTRING
			return dw_wstr(st1, "")'convert to dwstr according UTF8,
		END FUNCTION

		'function to convert wstring to utf8-string
		PRIVATE FUNCTION WstrToUtf8(BYREF wst1 AS WSTRING)AS STRING
			return dw_str(wst1, "")'convert to dwstr according UTF8,
		END FUNCTION

		'function to convert string to utf32-wstring, with optional codepage for input string ; utf32 big endian
		PRIVATE FUNCTION StrToUtf32(BYREF st1 AS STRING, BYREF codePage AS STRING = "")BYREF AS WSTRING
			if st1 = "" then return wstr("")
			'convert to dwstr according the specified codepage	and dereference pointer buffer
			return Dw_Wstr(st1, codePage)
		END FUNCTION

		'function to convert utf32-wstring to string, with optional codepage for output string ; utf32 big endian
		PRIVATE FUNCTION Utf32ToStr(BYREF wst1 AS WSTRING, BYVAL codePage AS string = "") AS STRING
			if wst1 = "" THEN return ""
			return Dw_Str(wst1, codePage)'convert to string using codepage
		END FUNCTION

		'function to convert string to String, with codepage for input string and optional codepage for output string
		PRIVATE FUNCTION StrToStr(BYREF st1 AS STRING, BYVAL codeIn AS string, BYVAL codeOut AS string = "") AS STRING
			if st1 = "" then return ""
			if ucase(codeIn) = ucase(codeOut) then return st1
			if instr(ucase(codeIn),"UTF-8") > 0 and instr(ucase(codeOut),"UTF-8") > 0 THEN return st1
			if instr(ucase(codeIn),"UTF-8") > 0 and codeOut = "" THEN return st1
			if instr(ucase(codeOut),"UTF-8") > 0 and codeIn = "" THEN return st1
			If ucase( mid(codeIn,instr(codeIn,"."))) = ucase( mid(codeOut,instr(codeOut,"."))) THEN return st1
			return Dw_Str(Dw_Wstr(st1, codeIn), codeOut)'convert to dwstr using codeIn and convert back to string using codeOut
		END FUNCTION
	#ELSE

		'function to convert string to utf8-string, with optional codepage for input string
		PRIVATE FUNCTION StrToUtf8(BYREF st1 AS STRING, BYVAL codePage AS LONG = 0) AS STRING
			dim len1 as long = len(st1)
			if len1 = 0 then return ""

			if codePage = 0 THEN   'use system codepage
				dim as string dest = string(len1 * 5, 0)
				dim as long i1
				CharToUTF(1, strptr(st1), len1, strptr(dest), Cast(Integer Ptr, @i1))
				return dest
			elseif codePage = CP_UTF8 THEN 'already utf8 coded
				return st1
			else	'convert to dwstr according the specified codepage, convert back to string utf8 coded
				return Dw_Str(Dw_Wstr(st1, codePage), CP_UTF8)
			END IF
		END FUNCTION

		'function to convert utf8-string to string, with optional codepage for output string
		PRIVATE FUNCTION Utf8ToStr(BYREF st1 AS STRING, BYVAL codePage AS LONG = 0) AS STRING
			if st1 = "" or codepage = CP_UTF8 THEN return st1
			return Dw_Str(dw_wstr(st1, CP_UTF8), codePage)'convert to dwstr according CP_UTF8, convert back to string using codepage
		END FUNCTION

		'function to convert utf8-string to wstring
		PRIVATE FUNCTION Utf8ToWstr(BYREF st1 AS STRING)BYREF AS WSTRING
			return dw_wstr(st1, CP_UTF8)'convert to dwstr according CP_UTF8,
		END FUNCTION

		'function to convert wstring to utf8-string
		PRIVATE FUNCTION WstrToUtf8(BYREF wst1 AS WSTRING)AS STRING
			return dw_str(wst1, CP_UTF8)'convert to dwstr according CP_UTF8,
		END FUNCTION

		'function to convert string to utf16-wstring, with optional codepage for input string ; utf16 big endian
		PRIVATE FUNCTION StrToUtf16(BYREF st1 AS STRING, BYVAL codePage AS LONG = 0)BYREF AS WSTRING
			if st1 = "" then return wstr("")
			'convert to dwstr according the specified codepage	and dereference pointer buffer
			return Dw_Wstr(st1, codePage)
		END FUNCTION

		'function to convert utf16-wstring to string, with optional codepage for output string ; utf16 big endian
		PRIVATE FUNCTION Utf16ToStr(BYREF wst1 AS WSTRING, BYVAL codePage AS LONG = 0) AS STRING
			if wst1 = "" THEN return ""
			return Dw_Str(wst1, codePage)'convert to string using codepage
		END FUNCTION

		'function to convert string to String, with codepage for input string and optional codepage for output string
		PRIVATE FUNCTION StrToStr(BYREF st1 AS STRING, BYVAL codeIn AS LONG, BYVAL codeOut AS LONG = 0) AS STRING
			if st1 = "" then return ""
			if codeIn = codeOut then return st1
			return Dw_Str(Dw_Wstr(st1, codeIn), codeOut)'convert to dwstr using codeIn and convert back to string using codeOut
		END FUNCTION

	#ENDIF   'IFDEF __FB_LINUX__


#ENDIF	'IFNDEF __FB_DOS__
Post Reply