总体逻辑
有点类似于二叉树排序树,但是比二插排序树要简单,最终构建出来的结果是:
父亲节点的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。
具体来看:
根据树的不同,和切分位置的不同可以分成5种情况。
左图
左图切3M和切10M的位置,由于切的位置没有有效数据,代码会分别走进①和②。
以切10M位置为例,切完以后返回的两个子树为s和right,这里的s重用成为了左子树,right是从②里面重新申请出来的。
s:
该子树这也就是我们通过juicefs info的时候看到的slice id为0的哪些部分。
right:nil
右图
最简单的是切中间情况,不需要递归切,即代码为③的位置,它会把原来的s复用成为返回的左子树,右边子树的根节点是new出来的:
s:
right:
切2M和10M的位置稍微复杂一点需要递归,由于是镜像的,以10M位置进行举例:
由于4M8M没有包含10M,而且该节点的右子树不为空,需要进行递归,继续去切右子树,10M在右子树9M11M的范围,
切完以后返回值的左右子树,s被复用成为左子树,s的right需要指向递归切④的左子树,递归切的右子树为第一层cut函数的第二个返回值,即最外层的右子树:
s:
right:
中序遍历
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排序的结果。