I added a couple more and wrapped them inside a module.
Case insensitive functionality only ignores case for A-Z and a-z .
Since it's quite a bit of ASM code, testing is appreciated to detect any bugs

The UCountString method is not very useful for very short strings that occur a lot like a single space character.
In a case like that it is slow because it's not one continues operation but a new call to a find procedure every time a match occurs.
I added a simple UCountChar procedure to quickly count how often a single character occurs.
Here's the UCS2 module code
Code: Select all
DeclareModule UCS2
; v 1.01 24/08/2014
; Case insensitive functionality only ignores case for A-Z and a-z
; Procedure.i UCountChar(*UCS2String, CharCode.u)
; Procedure.i UCountString(*UCS2String, *UCS2StringToFind, Mode = #PB_String_CaseSensitive)
; Procedure.i UFind(*UCS2String, *UCS2StringToFind)
; Procedure.i UFindI(*UCS2String, *UCS2StringToFind)
; Procedure.i UFindString(*UCS2String, *UCS2StringToFind, StartPosition = -1, Mode = #PB_String_CaseSensitive)
; Procedure.i ULen(*UCS2String, MaxLen.i = -1)
; Procedure.s UMid(*UCS2String, StartPos.i, Length.i = -1)
Declare.i UCountChar(*UCS2String, CharCode.u)
Declare.i UCountString(*UCS2String, *UCS2StringToFind, Mode = #PB_String_CaseSensitive)
Declare.i UFind(*UCS2String, *UCS2StringToFind)
Declare.i UFindI(*UCS2String, *UCS2StringToFind)
Declare.i UFindString(*UCS2String, *UCS2StringToFind, StartPosition = -1, Mode = #PB_String_CaseSensitive)
Declare.i ULen(*UCS2String, MaxLen.i = -1)
Declare.s UMid(*UCS2String, StartPos.i, Length.i = -1)
EndDeclareModule
Module UCS2
DisableDebugger ; Required because of the use of (r/e)bp register !
EnableASM
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
Macro rax : eax : EndMacro
Macro rbx : ebx : EndMacro
Macro rcx : ecx : EndMacro
Macro rdx : edx : EndMacro
Macro rsi : esi : EndMacro
Macro rdi : edi : EndMacro
Macro rbp : ebp : EndMacro
Macro rsp : esp : EndMacro
CompilerEndIf
Macro MOV_XR(xmm_reg, reg)
!movdqa xmm_reg, [reg]
EndMacro
Macro PUSH_XMM(xmm_reg)
sub rsp, 16
!movdqu [rsp], xmm_reg
EndMacro
Macro POP_XMM(xmm_reg)
!movdqu xmm_reg, [rsp]
add rsp, 16
EndMacro
Macro ULen_(u)
CompilerIf u
!movdqa xmm3, xmm0
!pslldq xmm0, 1
!inc cl
CompilerEndIf
!pcmpeqw xmm0, xmm1
!pmovmskb eax, xmm0
!shr eax, cl
!shl eax, cl
lea rdx, [rdx + 16]
!test eax, eax
!jnz ucs2.l_len_e
!ucs2.l_len_loop#u#i:
MOV_XR(xmm0, rdx)
CompilerIf u
!movdqa xmm2, xmm0
!pslldq xmm0, 1
!psrldq xmm3, 15
!por xmm0, xmm3
!movdqa xmm3, xmm2
CompilerEndIf
!pcmpeqw xmm0, xmm1
!pmovmskb eax, xmm0
cmp rdx, rbx
lea rdx, [rdx + 16]
!ja ucs2.l_len_e
!test eax, eax
!jz ucs2.l_len_loop#u#i
!jmp ucs2.l_len_e
EndMacro
Procedure.i ULen(*UCS2String, MaxLen.i = -1)
mov rdx, *UCS2String
test rdx, rdx
!jz ucs2.l_len_ae
mov rax, MaxLen
push rbx
lea rbx, [rdx + rax * 2]
sar rax, 63
Or rbx, rax
mov rcx, rdx
And rdx, -16
MOV_XR(xmm0, rdx)
!pxor xmm1, xmm1
sub rcx, rdx
!test ecx, 1
!jnz ucs2.l_len_unaligned
ULen_(0)
!ucs2.l_len_unaligned:
ULen_(1)
; exit procedure
!ucs2.l_len_e:
bsf ecx, eax
lea rax, [rcx + rdx - 16]
cmp rax, rbx
cmova rax, rbx
pop rbx
sub rax, *UCS2String
shr rax, 1
ProcedureReturn
!ucs2.l_len_ae:
ProcedureReturn 0
EndProcedure
Macro UFind_(u, i)
MOV_XR(xmm2, rdx) ; get 8 characters
CompilerIf u
!movdqa xmm5, xmm2
!pslldq xmm2, 1
!inc cl
CompilerEndIf
!pxor xmm4, xmm4
!pcmpeqw xmm4, xmm2 ; compare with 00000000
CompilerIf i
!pcmpeqw xmm6, xmm6
!psrlw xmm6, 15
!psllw xmm6, 5
!por xmm0, xmm6
!por xmm1, xmm6
!por xmm2, xmm6
CompilerEndIf
!movdqa xmm3, xmm0
!pcmpeqw xmm3, xmm2 ; compare with AAAAAAAA
!psllw xmm3, 8
!por xmm3, xmm4 ; combine results
!pmovmskb eax, xmm3 ; move mask
!shr eax, cl
!shl eax, cl
!jmp ucs2.l_find_loop0#u#i
!ucs2.l_find_loop#u#i:
add rdx, 16
MOV_XR(xmm2, rdx) ; get next 8 characters
CompilerIf u
!movdqa xmm4, xmm2
!pslldq xmm2, 1
!psrldq xmm5, 15
!por xmm2, xmm5
!movdqa xmm5, xmm4
CompilerEndIf
!pxor xmm4, xmm4
!pcmpeqw xmm4, xmm2 ; compare with 00000000
CompilerIf i
!por xmm2, xmm6
CompilerEndIf
!movdqa xmm3, xmm0
!pcmpeqw xmm3, xmm2 ; compare with AAAAAAAA
!psllw xmm3, 8
!por xmm3, xmm4 ; combine results
!pmovmskb eax, xmm3 ; move mask
!ucs2.l_find_loop0#u#i:
!test eax, eax ; check if any A or 0
!jz ucs2.l_find_loop#u#i ; loop if not
!pcmpeqw xmm2, xmm1 ; compare with BBBBBBBB
!pmovmskb ebx, xmm2 ; move mask
!lea eax, [eax * 4]
!or ebx, ebp
!and eax, ebx
!jz ucs2.l_find_loop#u#i ; loop if nothing found
!ucs2.l_bitscan#u#i:
!bsf ebx, eax ; scan for first bit set
!jz ucs2.l_find_loop#u#i
!test ebx, 1 ; bit set on an even place ?
!jz ucs2.l_find_nf#i ; if so, exit with not found
!btr eax, ebx ; clear bit
; full check
CompilerIf u
lea rbx, [rdx + rbx - 2] ; rbx = source
CompilerElse
lea rbx, [rdx + rbx - 1] ; rbx = source
CompilerEndIf
CompilerIf i
mov rsi, -2 ; rsi = index
CompilerElse
XOr rsi, rsi ; rsi = index
CompilerEndIf
!ucs2.l_fullcheck#u#i:
lea rsi, [rsi + 2]
mov cx, [rdi + rsi]
!test cx, cx
!jz ucs2.l_find_f#i ; *** exit found ***
XOr cx, [rbx + rsi - 2]
!jz ucs2.l_fullcheck#u#i
CompilerIf i
!test cx, 0xffdf
!jnz ucs2.l_bitscan#u#i
Or cx, [rbx + rsi - 2]
!sub cx, 0x61
!cmp cx, 26
!jb ucs2.l_fullcheck#u#i
CompilerEndIf
!jmp ucs2.l_bitscan#u#i
EndMacro
Procedure.i UFind(*UCS2String, *UCS2StringToFind)
mov rdx, *UCS2String
mov rax, *UCS2StringToFind
test rdx, rdx
!jz ucs2.l_find_ae
test rax, rax
!jz ucs2.l_find_ae
mov cx, [rax]
!test cx, cx
!jz ucs2.l_find_ae
push rbp
push rbx
push rdi
push rsi
!rol ecx, 16
mov rdi, rax
mov cx, [rdi + 2]
!movd xmm1, ecx
!test cx, cx
!mov eax, 0x3ffff
!punpcklwd xmm1, xmm1
!mov ebp, 0x35555
!pshufd xmm0, xmm1, 01010101b ; xmm0 = AAAAAAAA
cmovz ebp, eax
!pshufd xmm1, xmm1, 00000000b ; xmm1 = BBBBBBBB
mov rcx, rdx
And rdx, -16
sub rcx, rdx
!test ecx, 1
!jnz ucs2.l_find_unaligned
UFind_(0, 0)
!ucs2.l_find_unaligned:
UFind_(1, 0)
!ucs2.l_find_f0: ; found
mov rax, rbx
pop rsi
pop rdi
pop rbx
pop rbp
sub rax, *UCS2String
shr rax, 1
ProcedureReturn
!ucs2.l_find_nf0: ; exit point when not found
pop rsi
pop rdi
pop rbx
pop rbp
!ucs2.l_find_ae: ; exit point for argument error
ProcedureReturn 0
EndProcedure
Procedure.i UFindI(*UCS2String, *UCS2StringToFind)
mov rdx, *UCS2String
mov rax, *UCS2StringToFind
test rdx, rdx
!jz ucs2.l_find_iae
test rax, rax
!jz ucs2.l_find_iae
mov cx, [rax]
!test cx, cx
!jz ucs2.l_find_iae
push rbp
push rbx
push rdi
push rsi
CompilerIf #PB_Compiler_OS = #PB_OS_Windows And #PB_Compiler_Processor = #PB_Processor_x64
PUSH_XMM(xmm6) ; on Windows x64, XMM6 is considered non-volatile
CompilerEndIf
!rol ecx, 16
mov rdi, rax
mov cx, [rdi + 2]
!movd xmm1, ecx
!test cx, cx
!mov eax, 0x3ffff
!punpcklwd xmm1, xmm1
!mov ebp, 0x35555
!pshufd xmm0, xmm1, 01010101b ; xmm0 = AAAAAAAA
cmovz ebp, eax
!pshufd xmm1, xmm1, 00000000b ; xmm1 = BBBBBBBB
mov rcx, rdx
And rdx, -16
sub rcx, rdx
!test ecx, 1
!jnz ucs2.l_find_iunaligned
UFind_(0, 1)
!ucs2.l_find_iunaligned:
UFind_(1, 1)
!ucs2.l_find_f1: ; found
mov rax, rbx
CompilerIf #PB_Compiler_OS = #PB_OS_Windows And #PB_Compiler_Processor = #PB_Processor_x64
POP_XMM(xmm6)
CompilerEndIf
pop rsi
pop rdi
pop rbx
pop rbp
sub rax, *UCS2String
shr rax, 1
ProcedureReturn
!ucs2.l_find_nf1: ; exit point when not found
CompilerIf #PB_Compiler_OS = #PB_OS_Windows And #PB_Compiler_Processor = #PB_Processor_x64
POP_XMM(xmm6)
CompilerEndIf
pop rsi
pop rdi
pop rbx
pop rbp
!ucs2.l_find_iae: ; exit point for argument error
ProcedureReturn 0
EndProcedure
Procedure.i UCountChar(*UCS2String, CharCode.u)
mov rdx, *UCS2String
test rdx, rdx
!jz ucs2.l_cc_ae
mov cx, CharCode
push rbx
XOr rax, rax
!jmp ucs2.l_cc_loop_entry
!ucs2.l_cc_loop:
sub bx, cx
sub bx, 1
adc rax, 0
add rdx, 2
!ucs2.l_cc_loop_entry:
mov bx, [rdx]
test bx, bx
!jnz ucs2.l_cc_loop
pop rbx
ProcedureReturn
!ucs2.l_cc_ae:
ProcedureReturn 0
EndProcedure
EnableDebugger
Procedure.s UMid(*UCS2String, StartPos.i, Length.i = -1)
If StartPos > 0
ProcedureReturn PeekS(*UCS2String + ULen(*UCS2String, StartPos - 1) << 1, Length)
Else
ProcedureReturn PeekS(*UCS2String + ULen(*UCS2String, 0) << 1, Length)
EndIf
EndProcedure
Procedure.i UFindString(*UCS2String, *UCS2StringToFind, StartPosition = -1, Mode = #PB_String_CaseSensitive)
Protected.i Position
If StartPosition > 0
StartPosition - 1
*UCS2String + ULen(*UCS2String, StartPosition) << 1
Else
StartPosition = 0
EndIf
If Mode = #PB_String_NoCase
Position = UFindI(*UCS2String, *UCS2StringToFind)
Else
Position = UFind(*UCS2String, *UCS2StringToFind)
EndIf
If Position
Position + StartPosition
EndIf
ProcedureReturn Position
EndProcedure
Procedure.i UCountString(*UCS2String, *UCS2StringToFind, Mode = #PB_String_CaseSensitive)
Protected.i Count, Offset, FSize = ULen(*UCS2StringToFind)
If Mode = #PB_String_NoCase
Offset = UFindI(*UCS2String, *UCS2StringToFind)
While Offset
*UCS2String + (Offset + FSize - 1) << 1
Offset = UFindI(*UCS2String, *UCS2StringToFind)
Count + 1
Wend
Else
Offset = UFind(*UCS2String, *UCS2StringToFind)
While Offset
*UCS2String + (Offset + FSize - 1) << 1
Offset = UFind(*UCS2String, *UCS2StringToFind)
Count + 1
Wend
EndIf
ProcedureReturn Count
EndProcedure
EndModule