Go源码学习:bytes包 - 1.2 - bytes.go -(3)

bytes包官方文档

Go源码学习-索引目录

上一篇:
Go源码学习:bytes包 - 1.2 - bytes.go -(2)

21、asciiSpace:ASCII空白字符映射

// asciiSpace 是一个长度为 256 的数组,用于表示 ASCII 码中的空白字符。
// '\t': 水平制表符,'\n': 换行符,'\v': 垂直制表符,'\f': 换页符,'\r': 回车符,' ': 空格。
var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}

解释:

  • asciiSpace 是一个长度为 256 的数组,用于表示 ASCII 码中的空白字符。
  • 每个元素的索引对应一个 ASCII 码,而元素的值表示该字符是否为空白字符,1 表示是,0 表示不是。

作用:

  • 该数组提供了一种快速的方式来检查一个字符是否是ASCII空白字符。

22、Fields:切分 UTF-8 编码的字符串为字段

// Fields 将 s 视为 UTF-8 编码的码点序列。
// 它根据 unicode.IsSpace 定义的一个或多个连续的空白字符来切分切片 s,
// 返回 s 的子切片组成的切片,如果 s 只包含空白字符,则返回一个空切片。
func Fields(s []byte) [][]byte {
    // 首先计算字段数。
    // 如果 s 是 ASCII,则这是一个准确的计数,否则这是一个近似值。
    n := 0
    wasSpace := 1
    // setBits 用于跟踪 s 的字节中哪些位被设置。
    setBits := uint8(0)
    for i := 0; i < len(s); i++ {
        r := s[i]
        setBits |= r
        isSpace := int(asciiSpace[r])
        n += wasSpace & ^isSpace
        wasSpace = isSpace
    }

    if setBits >= utf8.RuneSelf {
        // 输入切片中有一些不是 ASCII 的字符。
        return FieldsFunc(s, unicode.IsSpace)
    }

    // ASCII 快速路径
    a := make([][]byte, n)
    na := 0
    fieldStart := 0
    i := 0
    // 在输入的开头跳过空格。
    for i < len(s) && asciiSpace[s[i]] != 0 {
        i++
    }
    fieldStart = i
    for i < len(s) {
        if asciiSpace[s[i]] == 0 {
            i++
            continue
        }
        a[na] = s[fieldStart:i:i]
        na++
        i++
        // 在字段之间跳过空格。
        for i < len(s) && asciiSpace[s[i]] != 0 {
            i++
        }
        fieldStart = i
    }
    if fieldStart < len(s) { // 最后一个字段可能在文件结束。
        a[na] = s[fieldStart:len(s):len(s)]
    }
    return a
}

解释:

  • Fields 方法将切片 s 视为 UTF-8 编码的码点序列,根据空白字符的定义切分切片,并返回由 s 的子切片组成的切片,如果 s 只包含空白字符,则返回一个空切片。
  • 该方法采用一种快速路径,如果输入 s 是 ASCII,直接通过预先计算空白字符个数来分配内存,避免了调用 FieldsFunc 方法。
  • 通过遍历切片中的字符,计算字段数,然后根据计算结果进行切分。

作用:

  • Fields 方法用于将字符串切分为字段,其中字段之间由一个或多个连续的空白字符分隔。
  • 该方法对于处理 ASCII 字符串具有高效的路径,提高了性能。

23、FieldsFunc:根据自定义函数切分字符串为字段

// FieldsFunc 将 s 视为 UTF-8 编码的码点序列。
// 它根据满足 f(c) 的码点 c 运行的连续码点来切分切片 s,
// 返回 s 的子切片组成的切片。如果 s 中的所有码点都满足 f(c),或者 len(s) == 0,则返回一个空切片。
//
// FieldsFunc 不保证调用 f(c) 的顺序,并假设对于给定的 c,f 总是返回相同的值。
func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
    // span 用于记录 s 的形式为 s[start:end] 的子切片。
    // 开始索引包含在内,结束索引不包含在内。
    type span struct {
        start int
        end   int
    }
    spans := make([]span, 0, 32)

    // 查找字段的起始和结束索引。
    // 将这个过程分开进行(而不是立即对字符串 s 进行切片并收集结果子字符串)会更高效,可能是由于缓存效应。
    start := -1 // 如果 >= 0,则 start 为有效的 span 开始索引
    for i := 0; i < len(s); {
        size := 1
        r := rune(s[i])
        if r >= utf8.RuneSelf {
            r, size = utf8.DecodeRune(s[i:])
        }
        if f(r) {
            if start >= 0 {
                spans = append(spans, span{start, i})
                start = -1
            }
        } else {
            if start < 0 {
                start = i
            }
        }
        i += size
    }

    // 最后一个字段可能在文件结束。
    if start >= 0 {
        spans = append(spans, span{start, len(s)})
    }

    // 从记录的字段索引创建子切片。
    a := make([][]byte, len(spans))
    for i, span := range spans {
        a[i] = s[span.start:span.end:span.end]
    }

    return a
}

解释:

  • FieldsFunc 方法将切片 s 视为 UTF-8 编码的码点序列。根据满足自定义函数 f(c) 的码点 c 运行的连续码点来切分切片 s,并返回由 s 的子切片组成的切片。如果 s 中的所有码点都满足 f(c),或者 len(s) == 0,则返回一个空切片。
  • 该方法通过创建 span 结构体记录字段的起始和结束索引,然后根据记录的字段索引创建子切片。

作用:

  • FieldsFunc 方法用于根据自定义函数切分字符串为字段,通过自定义函数 f(c) 来确定切分的规则,适用于更灵活的字符串处理需求。

24、Join:连接切片元素为新的字节切片

// Join 将切片 s 的元素连接起来,创建一个新的字节切片。分隔符 sep 被放置在结果切片中的元素之间。
func Join(s [][]byte, sep []byte) []byte {
    if len(s) == 0 {
        return []byte{}
    }
    if len(s) == 1 {
        // 只返回副本。
        return append([]byte(nil), s[0]...)
    }

    var n int
    if len(sep) > 0 {
        if len(sep) >= maxInt/(len(s)-1) {
            panic("bytes: Join output length overflow")
        }
        n += len(sep) * (len(s) - 1)
    }
    for _, v := range s {
        if len(v) > maxInt-n {
            panic("bytes: Join output length overflow")
        }
        n += len(v)
    }

    b := bytealg.MakeNoZero(n)
    bp := copy(b, s[0])
    for _, v := range s[1:] {
        bp += copy(b[bp:], sep)
        bp += copy(b[bp:], v)
    }
    return b
}

解释:

  • Join 方法连接切片 s 的元素,使用分隔符 sep 在结果切片中的元素之间放置。
  • 如果切片 s 为空,直接返回一个空字节切片。
  • 如果切片 s 只有一个元素,直接返回该元素的副本。
  • 计算连接后的结果切片的总长度 n,包括分隔符的长度。
  • 确保结果切片的长度不会溢出,如果溢出则抛出 panic。
  • 创建一个新的字节切片 b,长度为 n
  • 使用循环将切片 s 的元素拷贝到结果切片中,同时在元素之间插入分隔符。

作用:

  • Join 方法用于连接字节切片的元素,形成一个新的字节切片。
  • 可以指定分隔符,在连接元素之间插入分隔符。
  • 适用于将多个字节切片合并为一个字符串的场景。

25、HasPrefix:判断字节切片是否以指定前缀开头

// HasPrefix 报告字节切片 s 是否以前缀开头。
func HasPrefix(s, prefix []byte) bool {
    return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix)
}

解释:

  • HasPrefix 函数用于判断字节切片 s 是否以指定前缀 prefix 开头。
  • 返回值为布尔型,表示是否满足条件。
  • 条件判断:字节切片 s 的长度必须大于等于前缀 prefix 的长度,并且通过 Equal 函数比较切片的前缀与指定前缀是否相等。

作用:

  • 用于检查字节切片是否以指定前缀开头。

26、HasSuffix:判断字节切片是否以指定后缀结尾

// HasSuffix 报告字节切片 s 是否以后缀结尾。
func HasSuffix(s, suffix []byte) bool {
    return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix)
}

解释:

  • HasSuffix 函数用于判断字节切片 s 是否以指定后缀 suffix 结尾。
  • 返回值为布尔型,表示是否满足条件。
  • 条件判断:字节切片 s 的长度必须大于等于后缀 suffix 的长度,并且通过 Equal 函数比较切片的后缀与指定后缀是否相等。

作用:

  • 用于检查字节切片是否以指定后缀结尾。

27、Map:对字节切片进行字符映射

// Map 返回字节切片 s 的副本,其中的每个字符根据映射函数进行修改。
// 如果映射函数返回负值,该字符将从字节切片中删除且不替换。
// 字节切片 s 和输出都被解释为UTF-8编码的码点。
func Map(mapping func(r rune) rune, s []byte) []byte {
    // 在最坏的情况下,切片在映射时可能会增长,使情况变得复杂。
    // 但这种情况非常罕见,我们假设这是可以接受的。
    // 它也可能会缩小,但那自然而然地发生。
    b := make([]byte, 0, len(s))
    for i := 0; i < len(s); {
        wid := 1
        r := rune(s[i])
        if r >= utf8.RuneSelf {
            r, wid = utf8.DecodeRune(s[i:])
        }
        r = mapping(r)
        if r >= 0 {
            b = utf8.AppendRune(b, r)
        }
        i += wid
    }
    return b
}

解释:

  • Map 函数返回字节切片 s 的副本,其中的每个字符根据映射函数进行修改。
  • 映射函数 mapping 接受一个 rune 类型的参数,并返回一个 rune 类型的结果。
  • 如果映射函数返回负值,表示该字符将从字节切片中删除且不替换。
  • 字节切片 s 和输出都被解释为 UTF-8 编码的码点。
  • 在循环中,遍历字节切片 s 中的每个字符,根据映射函数进行处理,并将结果追加到新的字节切片 b 中。

作用:

  • 用于对字节切片中的字符进行映射操作,可以根据映射函数修改字符或删除特定字符。

28、Repeat:重复生成字节切片

// Repeat 返回一个新的字节切片,其中包含 b 的 count 个副本。
//
// 如果 count 为负数或者 (len(b) * count) 溢出,则引发 panic。
func Repeat(b []byte, count int) []byte {
    if count == 0 {
        return []byte{}
    }

    // 由于我们无法在溢出时返回错误,
    // 如果重复将导致溢出,我们应该引发 panic。
    // 参见 golang.org/issue/16237。
    if count < 0 {
        panic("bytes: negative Repeat count")
    }
    if len(b) >= maxInt/count {
        panic("bytes: Repeat output length overflow")
    }
    n := len(b) * count

    if len(b) == 0 {
        return []byte{}
    }

    // 超过一定的块大小时,使用更大的块作为写入的源是得不偿失的,
    // 因为当源太大时,我们基本上只是在折腾 CPU D-cache。
    // 因此,如果结果长度大于经验性找到的限制(8KB),我们停止在达到限制后增长源字符串,
    // 并继续重用相同的源字符串 - 这应该总是驻留在 L1 缓存中 - 直到完成结果的构建。
    // 在结果长度较大(大约超过 L2 缓存大小)的情况下,这会产生显着的速度提升(最多+100%)。
    const chunkLimit = 8 * 1024
    chunkMax := n
    if chunkMax > chunkLimit {
        chunkMax = chunkLimit / len(b) * len(b)
        if chunkMax == 0 {
            chunkMax = len(b)
        }
    }
    nb := bytealg.MakeNoZero(n)
    bp := copy(nb, b)
    for bp < n {
        chunk := bp
        if chunk > chunkMax {
            chunk = chunkMax
        }
        bp += copy(nb[bp:], nb[:chunk])
    }
    return nb
}

解释:

  • Repeat 函数返回一个新的字节切片,其中包含字节切片 bcount 个副本。
  • 如果 count 为零,直接返回空字节切片 []byte{}
  • 如果 count 为负数,引发 panic,表示重复次数为负数。
  • 如果 (len(b) * count) 溢出,引发 panic,表示输出长度溢出。
  • 计算新字节切片的长度 nlen(b) * count
  • 如果字节切片 b 本身为空,直接返回空字节切片 []byte{}
  • 通过循环复制源字节切片 b 到新建的字节切片 nb 中,直到达到重复次数。

作用:

  • 用于生成包含指定次数副本的字节切片。
  • 注意:如果重复次数为负数或者输出长度溢出,会引发 panic。

29、ToUpper:将字节切片中的字母转换为大写

// ToUpper 返回字节切片 s 的一个副本,其中所有 Unicode 字母都映射为它们的大写形式。
func ToUpper(s []byte) []byte {
    isASCII, hasLower := true, false
    for i := 0; i < len(s); i++ {
        c := s[i]
        if c >= utf8.RuneSelf {
            isASCII = false
            break
        }
        hasLower = hasLower || ('a' <= c && c <= 'z')
    }

    if isASCII { // 优化仅包含 ASCII 字节的情况。
        if !hasLower {
            // 只需返回副本。
            return append([]byte(""), s...)
        }
        b := bytealg.MakeNoZero(len(s))
        for i := 0; i < len(s); i++ {
            c := s[i]
            if 'a' <= c && c <= 'z' {
                c -= 'a' - 'A'
            }
            b[i] = c
        }
        return b
    }
    return Map(unicode.ToUpper, s)
}

解释:

  • ToUpper 函数返回一个字节切片 s 的副本,其中所有 Unicode 字母都映射为它们的大写形式。
  • 首先,通过遍历判断是否为纯 ASCII 字符串(只包含单字节字符),以及是否包含小写字母。
  • 如果是纯 ASCII,且不包含小写字母,直接返回字节切片的副本。
  • 如果不是纯 ASCII 或包含小写字母,则通过循环将小写字母转换为大写形式,得到新的字节切片。
  • 如果是纯 ASCII 但包含小写字母,调用 Map 函数使用 Unicode 大写映射进行转换。

作用:

  • 将字节切片中的字母转换为大写形式。
  • 优化了只包含 ASCII 字符的情况,避免调用 Map 函数。

30、ToLower:将字节切片中的字母转换为小写

// ToLower 返回字节切片 s 的一个副本,其中所有 Unicode 字母都映射为它们的小写形式。
func ToLower(s []byte) []byte {
    isASCII, hasUpper := true, false
    for i := 0; i < len(s); i++ {
        c := s[i]
        if c >= utf8.RuneSelf {
            isASCII = false
            break
        }
        hasUpper = hasUpper || ('A' <= c && c <= 'Z')
    }

    if isASCII { // 优化仅包含 ASCII 字节的情况。
        if !hasUpper {
            return append([]byte(""), s...)
        }
        b := bytealg.MakeNoZero(len(s))
        for i := 0; i < len(s); i++ {
            c := s[i]
            if 'A' <= c && c <= 'Z' {
                c += 'a' - 'A'
            }
            b[i] = c
        }
        return b
    }
    return Map(unicode.ToLower, s)
}

解释:

  • ToLower 函数返回一个字节切片 s 的副本,其中所有 Unicode 字母都映射为它们的小写形式。
  • 首先,通过遍历判断是否为纯 ASCII 字符串(只包含单字节字符),以及是否包含大写字母。
  • 如果是纯 ASCII,且不包含大写字母,直接返回字节切片的副本。
  • 如果不是纯 ASCII 或包含大写字母,则通过循环将大写字母转换为小写形式,得到新的字节切片。
  • 如果是纯 ASCII 但包含大写字母,调用 Map 函数使用 Unicode 小写映射进行转换。

作用:

  • 将字节切片中的字母转换为小写形式。
  • 优化了只包含 ASCII 字符的情况,避免调用 Map 函数。
  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风不归Alkaid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值