美文网首页
wcslen 源码 实现 学习

wcslen 源码 实现 学习

作者: onedam | 来源:发表于2022-05-05 15:10 被阅读0次

    vscode 连接 wsl 的c调试 good
    2022年5月2日 学了下 strlen 和 wcslen
    musl的实现可读性好。 glibc 优化到了汇编

    #include <wchar.h>
    
    size_t wcslen(const wchar_t *s)
    {
        const wchar_t *a;
        for (a=s; *s; s++);
        return s-a;
    }
    //删掉了一些宏,方便理解。
    size_t strlen(const char *s)
    {
        const char *a = s; 
        for (; *s; s++);
        return s-a;
    }
    

    ulibc的

    //strlen.c 
    #include "_string.h"
    #ifdef WANT_WIDE
    # define Wstrlen wcslen
    #else
    # define Wstrlen strlen
    #endif
    
    size_t Wstrlen(const Wchar *s)
    {
        register const Wchar *p;
    
        for (p=s ; *p ; p++);
    
        return p - s;
    }
    libc_hidden_weak(Wstrlen)
    //ulibc更加简洁。 wcslen和strlen 根据宏来一个方法
    //wcslen.c
    #define WANT_WIDE
    #include "strlen.c"
    
    // http://c.biancheng.net/c/assert/ 
    // 在操作指针时,一定要保证在指针有效内存空间内操作,不然 Segmentation fault  段错误。 被操作系统kill.
    // c编译器内置宏 https://zhuanlan.zhihu.com/p/409044316
    // ANSI:即 char,可用字符串处理函数:strcat( ),strcpy( ), strlen( )等以str打头的函数。
    // UNICODE:wchar_t是Unicode字符的数据类型,它实际定义在里:
    int main(void)
    {
        // assert(NULL);
        printf("文件: %s,函数: %s %s %s \n",__FILE__ , __func__,__ASSERT_FUNCTION,__DATE__); //几个c编译器内置变量
        char destination[25];
        char *blank = " ", *c = "C++", *Borland = "Borland";
    
        strcpy(destination, Borland);
        strcat(destination, blank);
        strcat(destination, c);
    
        printf("is: %s\n", strchr(destination, 'a'));
        printf("is2: %s\n", my_strchr(destination, 'a+lskjdsafsfsfa'));
        printf("%s\n", destination);
    
    // 字符串分割 
        char str[80] = "This is - www.runoob.com - website";
        // const char s[2] = "-";
        char* s = "-";
        char *token;
        /* 获取第一个子字符串 */
        token = strtok(str, s);
        /* 继续获取其他的子字符串 */
        while (token != NULL)
        {
            printf("%s\n", token);
            token = strtok(NULL, s);
        }
    //调用 strcat 必须保证目标地址有足够的已分配的内存用于存储结果。通常需要用 malloc 提前分配内存 或 定义一个足够大的静态 char[] 。
        // char* one1="hao",one2=" ca";
        char one1[100]="hao";
        // char *one1=malloc(100);
        char* one2=" ca";
        char* one3="ab2行者";  //wcslen本程序是7 . 这里会有各种奇怪长度。因为wcslen会4个字节计算一个字符直到遇到0
        char* one4=L"ab2行者";
        char* one5=L"ab2行者6789十了ab"; //超过10行则调用 xmmm寄存器 wsclen.s 优化的汇编...
        // hexdump -C str.c  查看assicc码 22 是双引号"
        printf("strcat: %s \n",strcat(one1,one2));
        // printf("%d \n",strlen("wanglaowu"));
        // printf("%d \n",strlen("行者"));  // strlen 计算的是字节 不是字符。
        // printf("%d \n",strlen(L"行者")); 
        // printf("%d \n",wcslen(L"行者")); //wsl x64下 调用的是__wcslen_sse2 用汇编实现的 
        printf("one3 strlen: %d \n",strlen(one3));
        printf("one3 wcslen: %d \n",wcslen(one3));
        printf("one4 strlen: %d \n",strlen(one4));
        printf("one4 wcslen: %d \n",wcslen(one4));
        printf("one5 wcslen: %d \n",wcslen(one5));
        printf("%d \n",wcslen(L"a冯哥2"));
        return 0;
    }
    // vscode 调试控制台 下 gdb 查看内存 
    // -exec x /10xw 0x5555555560b8
    
    image.png

    首先 strlen 和 wcslen 都是遇到内存为 0 的时候停止计算
    strlen 计算 L 开头的utf-16(网上一般叫宽字符,实际上是utf-16编码的) 还是按字节来数的。 strlen 计算L开头的字符串就是 1 或 2 。因为4个字节为一组, 例如 a 就是 62 00 00 00 行 就是 4c 88 00 str 遇到0就停止。
    不是L开头的字符串,是文件的编码,现在一般都是utf-8.


    d72f2e27f53c9fb0f05361feeecdd8e.png
    image.png

    printf("z9: %d \n",strlen("xa冯哥2")); // 9 good
    上面程序 4!!后面加上这行输出。则变为 了 6. 遇0则止 ....

    wcslen.s 汇编代码 10个字符 40个字节内的 前面几行优化
    /home/feng/glibc_source/sysdeps/i386/i686/multiarch/wcslen-sse2.S

    #if IS_IN (libc)
    # include <sysdep.h>
    # define STR    4
    
        .text
    ENTRY (__wcslen_sse2)
        mov STR(%esp), %edx
    
        cmp $0, (%edx)
        jz  L(exit_tail0)
        cmp $0, 4(%edx)
        jz  L(exit_tail1)
        cmp $0, 8(%edx)
        jz  L(exit_tail2)
        cmp $0, 12(%edx)
        jz  L(exit_tail3)
        cmp $0, 16(%edx)
        jz  L(exit_tail4)
        cmp $0, 20(%edx)
        jz  L(exit_tail5)
        cmp $0, 24(%edx)
        jz  L(exit_tail6)
        cmp $0, 28(%edx)
        jz  L(exit_tail7)
    
        pxor    %xmm0, %xmm0
    
        lea 32(%edx), %eax
        lea 16(%edx), %ecx
        and $-16, %eax
    
        pcmpeqd (%eax), %xmm0
        pmovmskb %xmm0, %edx
        pxor    %xmm1, %xmm1
        test    %edx, %edx
        lea 16(%eax), %eax
        jnz L(exit)
    
        pcmpeqd (%eax), %xmm1
        pmovmskb %xmm1, %edx
        pxor    %xmm2, %xmm2
        test    %edx, %edx
        lea 16(%eax), %eax
        jnz L(exit)
    
        pcmpeqd (%eax), %xmm2
        pmovmskb %xmm2, %edx
        pxor    %xmm3, %xmm3
        test    %edx, %edx
        lea 16(%eax), %eax
        jnz L(exit)
    
        pcmpeqd (%eax), %xmm3
        pmovmskb %xmm3, %edx
        test    %edx, %edx
        lea 16(%eax), %eax
        jnz L(exit)
    
        and $-0x40, %eax
    
        .p2align 4
    L(aligned_64_loop):
        movaps  (%eax), %xmm0
        movaps  16(%eax), %xmm1
        movaps  32(%eax), %xmm2
        movaps  48(%eax), %xmm6
    
        pminub  %xmm1, %xmm0
        pminub  %xmm6, %xmm2
        pminub  %xmm0, %xmm2
        pcmpeqd %xmm3, %xmm2
        pmovmskb %xmm2, %edx
        test    %edx, %edx
        lea 64(%eax), %eax
        jz  L(aligned_64_loop)
    
        pcmpeqd -64(%eax), %xmm3
        pmovmskb %xmm3, %edx
        test    %edx, %edx
        lea 48(%ecx), %ecx
        jnz L(exit)
    
        pcmpeqd %xmm1, %xmm3
        pmovmskb %xmm3, %edx
        test    %edx, %edx
        lea -16(%ecx), %ecx
        jnz L(exit)
    
        pcmpeqd -32(%eax), %xmm3
        pmovmskb %xmm3, %edx
        test    %edx, %edx
        lea -16(%ecx), %ecx
        jnz L(exit)
    
        pcmpeqd %xmm6, %xmm3
        pmovmskb %xmm3, %edx
        test    %edx, %edx
        lea -16(%ecx), %ecx
        jnz L(exit)
    
        jmp L(aligned_64_loop)
    
        .p2align 4
    L(exit):
        sub %ecx, %eax
        shr $2, %eax
        test    %dl, %dl
        jz  L(exit_high)
    
        mov %dl, %cl
        and $15, %cl
        jz  L(exit_1)
        ret
    
        .p2align 4
    L(exit_high):
        mov %dh, %ch
        and $15, %ch
        jz  L(exit_3)
        add $2, %eax
        ret
    
        .p2align 4
    L(exit_1):
        add $1, %eax
        ret
    
        .p2align 4
    L(exit_3):
        add $3, %eax
        ret
    
        .p2align 4
    L(exit_tail0):
        xor %eax, %eax
        ret
    
        .p2align 4
    L(exit_tail1):
        mov $1, %eax
        ret
    
        .p2align 4
    L(exit_tail2):
        mov $2, %eax
        ret
    
        .p2align 4
    L(exit_tail3):
        mov $3, %eax
        ret
    
        .p2align 4
    L(exit_tail4):
        mov $4, %eax
        ret
    
        .p2align 4
    L(exit_tail5):
        mov $5, %eax
        ret
    
        .p2align 4
    L(exit_tail6):
        mov $6, %eax
        ret
    
        .p2align 4
    L(exit_tail7):
        mov $7, %eax
        ret
    
    END (__wcslen_sse2)
    #endif
    

    还有个差不多的
    /home/feng/glibc_source/sysdeps/x86_64/wcslen.S
    都是glibc的代码 .我在 wsl x64 win10 下调试 遇到的是上面的 sse 优化过的汇编。

    相关文章

      网友评论

          本文标题:wcslen 源码 实现 学习

          本文链接:https://www.haomeiwen.com/subject/llxbyrtx.html