Skip to content

Commit c91623d

Browse files
committed
Fix staircasing in layout
Instead of placing a new fiber below all overlapping fibers, place it in the first large-enough gap.
1 parent 05371a8 commit c91623d

File tree

3 files changed

+68
-6
lines changed

3 files changed

+68
-6
lines changed

lib/layout.ml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,12 @@ let rec layout_item ~duration (i : item) =
145145
let itv = Itv.create intervals in
146146
let height = ref i.height in
147147
intervals |> List.to_seq |> Seq.drop n_ccs |> Seq.iter (fun (interval : _ Itv.interval) ->
148-
let y = ref 1 in
149-
let adjust other =
150-
y := max !y (other.y + other.height);
151-
in
148+
let space = Space.create 1 in
149+
let adjust other = Space.mark_range space other.y (other.y + other.height) in
152150
Itv.iter_overlaps adjust interval.start interval.stop itv;
153151
let f = interval.Itv.value in
154152
layout_item ~duration f;
155-
f.y <- !y;
153+
f.y <- Space.first_free space f.height;
156154
height := max !height (f.y + f.height);
157155
);
158156
i.height <- !height

lib/space.ml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
(* Tracks which of a set of rows have free space.
2+
For large sets it might be more efficient to use an integer tree,
3+
but layouts that large are fairly unusable anyway. *)
4+
5+
type t = {
6+
start : int;
7+
mutable buf : bytes;
8+
}
9+
10+
let create start = { start; buf = Bytes.empty }
11+
12+
let mark t row =
13+
let i = row - t.start in
14+
if i >= 0 then (
15+
let byte = i lsr 3 in
16+
if byte >= Bytes.length t.buf then (
17+
let old_buf = t.buf in
18+
let old_len = Bytes.length old_buf in
19+
let new_len = max (byte + 1) (old_len * 2) in
20+
let new_buf = Bytes.extend old_buf 0 (new_len - old_len) in
21+
Bytes.fill new_buf old_len (new_len - old_len) (Char.chr 0);
22+
t.buf <- new_buf
23+
);
24+
let buf = t.buf in
25+
let v = Bytes.get_uint8 buf byte lor (1 lsl (i land 0x7)) in
26+
Bytes.set_uint8 buf byte v
27+
)
28+
29+
let mark_range t a b =
30+
let a = max a t.start in
31+
for i = a to b - 1 do
32+
mark t i
33+
done
34+
35+
let rec find_free_bit v start =
36+
if v land 1 = 0 then start
37+
else find_free_bit (v lsr 1) (start + 1)
38+
39+
let (.%[]) t row =
40+
let i = row - t.start in
41+
let buf = t.buf in
42+
let byte = i lsr 3 in
43+
if byte >= Bytes.length buf then false
44+
else (Bytes.get_uint8 buf byte land (1 lsl (i land 7))) <> 0
45+
46+
let first_free t len =
47+
assert (len >= 0);
48+
let rec check i need =
49+
if need = 0 then i - len
50+
else if t.%[i] then check (i + 1) len
51+
else check (i + 1) (need - 1)
52+
in
53+
check t.start len

test/test.ml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module Itv = Eio_trace.Itv
2+
module Space = Eio_trace.Space
23

34
let span = Crowbar.(map [uint8; uint8]) (fun start len -> (float start, float (start + len)))
45

@@ -24,5 +25,15 @@ let test_ivt spans (start, stop) =
2425
)
2526
)
2627

28+
let test_space start used height =
29+
let s = Space.create start in
30+
List.iter (Space.mark s) used;
31+
let free = Space.first_free s height in
32+
for i = free to free + height - 1 do
33+
if List.mem i used then
34+
Crowbar.failf "Row %d is used, but was returned as free (%d+%d)!" i free height
35+
done
36+
2737
let () =
28-
Crowbar.(add_test ~name:"ivt" [list span; span] test_ivt)
38+
Crowbar.(add_test ~name:"ivt" [list span; span] test_ivt);
39+
Crowbar.(add_test ~name:"space" [int8; list int8; uint8] test_space)

0 commit comments

Comments
 (0)