buildSlice实现

总体逻辑

有点类似于二叉树排序树,但是比二插排序树要简单,最终构建出来的结果是:

父亲节点的pos大于等于所有左边孩子的pos+length。

父亲节点的pos小于等于所有右边边孩子的pos。

算法代码

func buildSlice(ss []*slice) []Slice {
	var root *slice
	for i := range ss {
		s := new(slice)
		*s = *ss[i]
		var right *slice
		s.left, right = root.cut(s.pos)_, s.right = right.cut(s.pos + s.len)②
		root = s
	}
	var pos uint32
	var chunk []Slice
	root.visit(func(s *slice) {
		if s.pos > pos {
			chunk = append(chunk, Slice{Size: s.pos - pos, Len: s.pos - pos})
			pos = s.pos
		}
		chunk = append(chunk, Slice{Id: s.id, Size: s.size, Off: s.off, Len: s.len})
		pos += s.len
	})
	return chunk
}

遍历ss,每次访问ss中的一个元素s,访问的元素将会重新作为树的根,①先切老树的左测,切的位置是s.pos,左测切完以后,返回切完以后的两个子树,由于左边不需要再切了,可以直接复制给当前元素的左边孩子s.left,①行返回的第二个元素right还需要继续切,②,切的位置是s.pos + s.len,切了也会返回两颗子树,第一个元素是覆盖的左边部分,因此需要忽略,第二个元素是右边部分,直接赋值给s.right。

func (s *slice) cut(pos uint32) (left, right *slice) {
	if s == nil {
		return nil, nil
	}
	if pos <= s.pos {
		if s.left == nil {
			s.left = newSlice(pos, 0, 0, 0, s.pos-pos)}
		left, s.left = s.left.cut(pos)
		return left, s
	} else if pos < s.pos+s.len {③
		l := pos - s.pos
		right = newSlice(pos, s.id, s.size, s.off+l, s.len-l)
		right.right = s.right
		s.len = l
		s.right = nil
		return s, right
	} else {
		if s.right == nil {
			s.right = newSlice(s.pos+s.len, 0, 0, 0, pos-s.pos-s.len)}
		s.right, right = s.right.cut(pos)return s, right
	}
}

该函数就是切的函数,一颗树以pos位置进行切,切了之后达到的效果是:返回的两个元素的两颗子树分别为左子树和右子树,左子树的所有子节点的pos’+len<=pos,右子树的pos’>pos。

具体来看:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mBgJmjsd-1684220685107)(/tfl/pictures/202304/tapd_20360132_1680595168_580.png)]

根据树的不同,和切分位置的不同可以分成5种情况。

左图

左图切3M和切10M的位置,由于切的位置没有有效数据,代码会分别走进①和②。

以切10M位置为例,切完以后返回的两个子树为s和right,这里的s重用成为了左子树,right是从②里面重新申请出来的。

s:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C5wJ5F9I-1684220685107)(/tfl/pictures/202304/tapd_20360132_1680595209_273.png)]

该子树这也就是我们通过juicefs info的时候看到的slice id为0的哪些部分。

right:nil

右图

最简单的是切中间情况,不需要递归切,即代码为③的位置,它会把原来的s复用成为返回的左子树,右边子树的根节点是new出来的:

s:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8URPfy0d-1684220685108)(/tfl/pictures/202304/tapd_20360132_1680595232_519.png)]

right:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FjnNiH8h-1684220685108)(/tfl/pictures/202304/tapd_20360132_1680595247_255.png)]

切2M和10M的位置稍微复杂一点需要递归,由于是镜像的,以10M位置进行举例:

由于4M8M没有包含10M,而且该节点的右子树不为空,需要进行递归,继续去切右子树,10M在右子树9M11M的范围,

切完以后返回值的左右子树,s被复用成为左子树,s的right需要指向递归切④的左子树,递归切的右子树为第一层cut函数的第二个返回值,即最外层的右子树:

s:

在这里插入图片描述

right:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ESyltf5-1684220685108)(/tfl/pictures/202304/tapd_20360132_1680595290_287.png)]

中序遍历

func (s *slice) visit(f func(*slice)) {
	if s == nil {
		return
	}
	s.left.visit(f)
	fmt.Printf("%+v", s)
	right := s.right
	f(s) // s could be freed
	right.visit(f)
}

由于是一种类似于二插排序树的结构,通过中序遍历,可以遍历出按照pos排序的结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值