{$asmmode intel}
function UTF8Len_aligned16(p16: pchar; BlockCount: PtrInt):PtrInt;assembler;
const
ZEROMASK :array[0..15] of byte=($00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00);
ONEMASK :array[0..15] of byte=($01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01);
ONEMASKx80:array[0..15] of byte=($80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80,$80);
ONEMASKxFF:array[0..15] of byte=($FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF);
Label
loop;
asm
push ecx
push edi
push ebx
{ tmp counter }
MOV ecx, 0
{ masks }
MOVDQA xmm0, ZEROMASK
MOVDQA xmm1, ONEMASK
MOVDQA xmm2, ONEMASKx80
MOVDQA xmm3, ONEMASKxFF
Loop:
{ get 16 bytes }
MOVDQA xmm4, [p16]
{ Invert 16 bytes }
MOVDQA xmm5, xmm4
ANDNPD xmm5, xmm3 { xmm5 = not xmm4}
{ Shift the inverted bytes 6 bits to the right }
PSRLQ xmm5, 6
{ Keep msb of each non-inverted byte }
PAND xmm4, ONEMASKx80
{ Shift them to right 7 bits }
PSRLQ xmm4, 7 { Shift Right Logical QWord }
{ A one in the 1st bit means: NOT the first byte of a codepoint }
PAND xmm5, xmm4
{ Count them ;-) }
PSADBW xmm5, xmm0
MOVD edi, xmm5
PEXTRW ebx, xmm5, 4
ADD ecx, ebx
ADD ecx, edi
{ Next 16 bytes }
ADD p16, 16
DEC edx
JNZ Loop
{ Result }
Mov eax, ecx
pop ebx
pop edi
pop ecx
end;
function UTF8Len_SIMD(p: PChar; ByteCount: PtrInt): PtrInt;
var
pn8:pint8 absolute p;
n8:int8;
ix:PtrInt absolute p;
i, cnt, BlockCount:PtrInt;
begin
Result := 0;
{ Handle any initial misaligned bytes. }
cnt := (not (ix-1)) and $F;
if cnt>ByteCount then
cnt := ByteCount;
for i := 1 to cnt do
begin
n8 := pn8^;
{ Is this byte NOT the first byte of a character? }
Result += (n8 shr 7) and ((not n8) shr 6);
inc(pn8);
end;
cnt := ByteCount - cnt;
if cnt=0 then
exit(ByteCount - Result);
{ Handle blocks of 16 bytes }
BlockCount := cnt div $10;
if BlockCount>0 then
Result += UTF8Len_aligned16(pchar(pn8), BlockCount);
{ Take care of any left-over bytes. }
inc(pn8, BlockCount shl 4);
for i := 1 to cnt and $F do
begin
n8 := pn8^;
{ Is this byte NOT the first byte of a character? }
Result += (n8 shr 7) and ((not n8) shr 6);
inc(pn8);
end;
Result := (ByteCount - Result);
end;
function UTF8Len_SIMD(const s: string): PtrInt;
begin
Result := UTF8Len_SIMD(@s[1], Length(s));
end;