StringCompareTo:字符串相比较

java中stringCompareTo的用法如下:

  1. 如果两个string相等返回0
  2. 如果在lengthMin(s1, s2)的范围中,重叠的部分相等,则返回length的差
  3. 如果在比较的过程中发现有不相等,则返回char的差值,其中如果为十六进制的数,比较的范围为16位,具体情况如图


    compareto

在intrinsic的实现中,函数调用到了kQuickStringCompareTo,该函数的申明在art/runtime/arch/riscv64/entrypoints_init_riscv64.cc中,具体的实现在同目录下的qucik_entrypoints_riscv64.S中,经过runtest测试过的riscv已经实现。

首先该函数时通过mips64移植过来的,在对于mips的理解上有一个比较重要的概念:如果涉及到跳转指令如beqz等,如果没有在跳转指令后面加上c,比如beqzc,那么一律默认都是延时跳转的。如:


mips中的延时跳转

如果a0=a1是成立的,那么会先进行move的指令,然后再进行跳转到 .Lstring_compareto_length_diff中,这是一个与riscv差距比较大的汇编指令,如果不熟悉的话很容易觉得它的代码写错了。但是如果在跳转指令后加上字母'c',在这种情况下是不需要延时跳转的。接下来我们看compareTo的实现,像其他的函数如Equal等其实思路差不多。

ENTRY art_quick_string_compareto
/* a0 holds address of "this" */
/* a1 holds address of "anotherString" */
    mv      a2, zero
    mv      a3, zero
    beq     a0, a1, .Lstring_compareto_length_diff  # this and anotherString are the same object
# a0,a1为入参,分别代表要进行比较的this string 和another string,a2,a3寄存器专门用来存放记录this string 和another string的length长度,初始化为0。
# 此时将两个参数进行比较,如果地址相等,说明他两存放的是一个东西,没什么好比较的,跳转到比较长度,但是由于此时a2, a3都是0,即返回的是0

#if (STRING_COMPRESSION_FEATURE)
    lw      a4, MIRROR_STRING_COUNT_OFFSET(a0)      # 'count' field of this
    lw      a5, MIRROR_STRING_COUNT_OFFSET(a1)      # 'count' field of anotherString
    sraiw   a2, a4, 1                               # this.length()
    sraiw   a3, a5, 1                               # anotherString.length()
# 这里涉及到一个我不怎么懂,且网上还没什么资料的知识,还是通过不断测试发现的规律。
# 即string_compression的概念,什么叫做字符串压缩?我正常输入abc这种肯定不是压缩过的指令吧?实际上abc这种才是压缩的……
# 为什么呢?因为这种string系列最后也会转化成为机器码,最常见的就是ascii码,当然最常用的还是涵盖了更多的UTF-16
# 也就是说,这些在我们眼中看起来的字符串,其实就是压缩过的string,那么什么才是未压缩过的指令呢?
# 就是回到开头我写的例子中的s4, s5,已经通过十六进制来表示string
# 我不懂的就是这个MIRROR_STRING_COUNT_OFFSET函数时如何计算出一个带flag的值,如果0位为0就是被压缩过的
# 如果第0位为1,就是没有被压缩过的,我只是通过大量的实验证明了这个情况……
# 而且这个分支肯定会被走到,无论你输入的是压缩过的还是没有被压缩过的,因为if的的情况只是表明,你是否支持这个功能,显然我们的强大的art是支持的
# 计算出一个带flag的数之后给到一个寄存器中,分别是a4, a5,将他们的值右移一位就得到了他们的真实length
#else
    lw      a2, MIRROR_STRING_COUNT_OFFSET(a0)      # this.length()
    lw      a3, MIRROR_STRING_COUNT_OFFSET(a1)      # anotherString.length()
#endif

    ble     a2, a3, 1f
    mv      t2, a3
    j       2f
1:
    mv      t2, a2
    # t2 now holds min(this.length(),anotherString.length())
2:
    beqz    t2, .Lstring_compareto_length_diff      # if t2==0
                                                    # return (this.length() - anotherString.length())
# t2寄存器保存的是两个string的最小值,为什么要计算这个最小值呢?因为compareto函数的功能是如果重叠部分相同,则返回length差。也就是说,我一个个比,比的最大长度是a2, a3中的最小值

#if (STRING_COMPRESSION_FEATURE)
    # Differ cases:
    andi    a6, a4, 0x1
    andi    a7, a5, 0x1
    beqz    a6, .Lstring_compareto_this_is_compressed
    beqz    a7, .Lstring_compareto_that_is_compressed
    j       .Lstring_compareto_both_not_compressed
# 这里我用了一个投机取巧的方法,通过runtest测试证明是对的,但是我不知道这样做是不是严谨,因为在mips中的指令是:
#  dext   $a6, $a4, 0, 1
# 大概的意思是将第0位上的size位1的左移到最高位,然后再右移到第0位,那么按我的理解,不就是要第0位的值是不是1么?
# 那我直接与1相与不就行了,显然我也不知道mips这么大动干戈的目的是什么。而且我用riscv的方法尝试了一下还老报错……
# 意思很明显了,就是如果第0位如果是1,就是没有被压缩过的,但如果是0,就是被压缩过的,其中还分为一个压缩,另一个没有被压缩的情况
# 在不同分支中,比较的思路都是差不多的,唯一的区别是怎么比?
# 如果是被压缩的,那么一下子取得是‘lb’,即一个字节,是8位,但如果是没有被压缩过的,一下子取得是‘lh’,half Word,是16位。
# 取出来,比较,如果相等,那么将之前得到的minLength(s1, s2) - 1, 继续比,循环。
#  dext   $a6, $a4, 0, 1如果在比较的过程中发现有不一样的情况,则返回char的差值,但是如果t2等于0了,说明重叠部分相等,只要返回length的差值就可以了

.Lstring_compareto_this_is_compressed:
    beqz    a7, .Lstring_compareto_both_compressed
    /* If (this->IsCompressed() && that->IsCompressed() == false) */
.Lstring_compareto_loop_comparison_this_compressed:
    lbu     t0, MIRROR_STRING_VALUE_OFFSET(a0)
    lhu     t1, MIRROR_STRING_VALUE_OFFSET(a1)
    bne     t0, t1, .Lstring_compareto_char_diff
    addi    a0, a0, 1       # point at this.charAt(i++) - compressed
    addi    a1, a1, 2       # point at anotherString.charAt(i++) - uncompressed
    addi    t2, t2, -1      # new value of min(this.length(),anotherString.length())-i
    bnez    t2, .Lstring_compareto_loop_comparison_this_compressed
    subw    a0, a2, a3      # return (this.length() - anotherString.length())
    ret

.Lstring_compareto_that_is_compressed:
    lhu     t0, MIRROR_STRING_VALUE_OFFSET(a0)
    lbu     t1, MIRROR_STRING_VALUE_OFFSET(a1)
    bne     t0, t1, .Lstring_compareto_char_diff
    addi    a0, a0, 2      # point at this.charAt(i++) - uncompressed
    addi    a1, a1, 1      # point at anotherString.charAt(i++) - compressed
    addi    t2, t2, -1     # new value of min(this.length(),anotherString.length())-i
    bnez    t2, .Lstring_compareto_that_is_compressed
    subw    a0, a2, a3     # return (this.length() - anotherString.length())
    ret

.Lstring_compareto_both_compressed:
    lbu     t0, MIRROR_STRING_VALUE_OFFSET(a0)
    lbu     t1, MIRROR_STRING_VALUE_OFFSET(a1)
    bne     t0, t1, .Lstring_compareto_char_diff
    addi    a0, a0, 1      # point at this.charAt(i++) - compressed
    addi    a1, a1, 1      # point at anotherString.charAt(i++) - compressed
    addi    t2, t2, -1     # new value of min(this.length(),anotherString.length())-i
    bnez    t2, .Lstring_compareto_both_compressed
    subw    a0, a2, a3     # return (this.length() - anotherString.length())
    ret
#endif

.Lstring_compareto_both_not_compressed:
    lhu     t0, MIRROR_STRING_VALUE_OFFSET(a0)    # while this.charAt(i) == anotherString.charAt(i)
    lhu     t1, MIRROR_STRING_VALUE_OFFSET(a1)
    bne     t0, t1, .Lstring_compareto_char_diff
    addi    a0, a0, 2
    addi    a1, a1, 2
    addi    t2, t2, -1
    bnez    t2, .Lstring_compareto_both_not_compressed

.Lstring_compareto_length_diff:
    subw   a0, a2, a3        # return (this.length() - anotherString.length())
    ret

.Lstring_compareto_char_diff:
    subw   a0, t0, t1        # return (this.charAt(i) - anotherString.charAt(i))
    ret
END art_quick_string_compareto
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容