package batteries
A community-maintained standard library extension
Install
Dune Dependency
Authors
Maintainers
Sources
v3.9.0.tar.gz
md5=ea26b5c72e6731e59d856626049cca4d
sha512=55975b62c26f6db77433a3ac31f97af609fc6789bb62ac38b267249c78fd44ff37fe81901f1cf560857b9493a6046dd37b0d1c0234c66bd59e52843aac3ce6cb
doc/src/batteries.unthreaded/batIO.ml.html
Source file batIO.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
(* * BatIO - Abstract input/output * Copyright (C) 2003 Nicolas Cannasse * 2008 David Teller (contributor) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version, * with the special exception on linking described in file LICENSE. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) include BatInnerIO external noop : unit -> unit = "%ignore" external default_close : unit -> unit = "%ignore" type ('a, 'b) printer = 'b output -> 'a -> unit type 'a f_printer = Format.formatter -> 'a -> unit let pos_in i = let p = ref 0 in (wrap_in ~read:(fun () -> let c = read i in incr p; c ) ~input:(fun s sp l -> let n = input i s sp l in p := !p + n; n ) ~close:noop ~underlying:[i] , (fun () -> !p)) let pos_out o = let p = ref 0 in (wrap_out ~write:(fun c -> write o c; incr p ) ~output:(fun s sp l -> let n = output o s sp l in p := !p + n; n ) ~close:noop ~flush:(fun () -> flush o) ~underlying:[o] , fun () -> !p) let progress_in inp f = wrap_in ~read: (fun () -> let c = read inp in f(); c) ~input:(fun s i l -> let r = input inp s i l in f(); r) ~close:ignore ~underlying:[inp] let progress_out out f = wrap_out ~write:(fun c -> write out c; f()) ~output:(fun s i l -> let r = output out s i l in f(); r) ~close:ignore ~flush:(fun () -> flush out) ~underlying:[out] (** {6 Support for enumerations} *) (*Function inlined here to avoid circular dependencies between [BatIO] and [ExtString].*) let string_enum s = let l = String.length s in let rec make i = BatEnum.make ~next:(fun () -> if !i = l then raise BatEnum.No_more_elements else let p = !i in incr i; String.unsafe_get s p ) ~count:(fun () -> l - !i) ~clone:(fun () -> make (ref !i)) in make (ref 0) let input_enum e = let pos = ref 0 in create_in ~read:(fun () -> match BatEnum.get e with | None -> raise No_more_input | Some c -> incr pos; c ) ~input:(fun s p l -> let rec loop p l = if l = 0 then 0 else match BatEnum.get e with | None -> l | Some c -> Bytes.unsafe_set s p c; loop (p + 1) (l - 1) in let k = loop p l in if k = l then raise No_more_input; l - k ) ~close:default_close let output_enum() = let b = Buffer.create default_buffer_size in create_out ~write:(fun x -> Buffer.add_char b x ) ~output:(fun s p l -> BatBytesCompat.buffer_add_subbytes b s p l; l ) ~close:(fun () -> let s = Buffer.contents b in string_enum s ) ~flush:default_close (** [apply_enum f x] applies [f] to [x] and converts exceptions [No_more_input] and [Input_closed] to [BatEnum.No_more_elements]*) let apply_enum do_close f x = try f x with | No_more_input -> raise BatEnum.No_more_elements | Input_closed -> do_close := false; raise BatEnum.No_more_elements (** [close_at_end input e] returns an enumeration which behaves as [e] and has the secondary effect of closing [input] once everything has been read.*) let close_at_end do_close (input:input) e = BatEnum.suffix_action (fun () -> if !do_close then close_in input) e let make_enum f input = let do_close = ref true in close_at_end do_close input (BatEnum.from (fun () -> apply_enum do_close f input)) let combine (a,b) = wrap_out ~write:(fun c -> write a c; write b c) ~output:(fun s i j -> let _ = output a s i j in output b s i j) ~flush:(fun () -> flush a; flush b) ~close:(fun () -> (close_out a, close_out b)) ~underlying:[cast_output a; cast_output b] let write_enum f out enum = BatEnum.iter (f out) enum (*; flush out*) (** {6 Big Endians} *) module BigEndian = struct let read_ui16 i = let ch2 = read_byte i in let ch1 = read_byte i in ch1 lor (ch2 lsl 8) let read_i16 i = let ch2 = read_byte i in let ch1 = read_byte i in let n = ch1 lor (ch2 lsl 8) in if ch2 land 128 <> 0 then n - 65536 else n let fix = lnot 0x7FFFFFFF (* -:) *) let read_i32 ch = let ch4 = read_byte ch in let ch3 = read_byte ch in let ch2 = read_byte ch in let ch1 = read_byte ch in if ch4 land 128 <> 0 then begin (* negative number *) if ch4 land 64 = 0 then raise (Overflow "read_i32"); (ch1 lor (ch2 lsl 8) lor (ch3 lsl 16) lor ((ch4 land 127) lsl 24)) lor fix (* FIX HERE *) end else begin (*positive number*) if ch4 land 64 <> 0 then raise (Overflow "read_i32"); ch1 lor (ch2 lsl 8) lor (ch3 lsl 16) lor (ch4 lsl 24) end let read_real_i32 ch = let big = Int32.shift_left (Int32.of_int (read_byte ch)) 24 in let ch3 = read_byte ch in let ch2 = read_byte ch in let ch1 = read_byte ch in let base = Int32.of_int (ch1 lor (ch2 lsl 8) lor (ch3 lsl 16)) in Int32.logor base big let read_i64 ch = let big = Int64.of_int32 (read_real_i32 ch) in let ch4 = read_byte ch in let ch3 = read_byte ch in let ch2 = read_byte ch in let ch1 = read_byte ch in let base = Int64.of_int (ch1 lor (ch2 lsl 8) lor (ch3 lsl 16)) in let small = Int64.logor base (Int64.shift_left (Int64.of_int ch4) 24) in Int64.logor (Int64.shift_left big 32) small let read_double ch = Int64.float_of_bits (read_i64 ch) let read_float ch = Int32.float_of_bits (read_real_i32 ch) let write_ui16 ch n = if n < 0 || n > 0xFFFF then raise (Overflow "write_ui16"); write_byte ch (n lsr 8); write_byte ch n let write_i16 ch n = if n < -0x8000 || n > 0x7FFF then raise (Overflow "write_i16"); if n < 0 then write_ui16 ch (65536 + n) else write_ui16 ch n let write_i32 ch n = write_byte ch (n asr 24); write_byte ch (n lsr 16); write_byte ch (n lsr 8); write_byte ch n let write_real_i32 ch n = let base = Int32.to_int n in let big = Int32.to_int (Int32.shift_right_logical n 24) in write_byte ch big; write_byte ch (base lsr 16); write_byte ch (base lsr 8); write_byte ch base let write_i64 ch n = write_real_i32 ch (Int64.to_int32 (Int64.shift_right_logical n 32)); write_real_i32 ch (Int64.to_int32 n) let write_double ch f = write_i64 ch (Int64.bits_of_float f) let write_float ch f = write_real_i32 ch (Int32.bits_of_float f) let ui16s_of input = make_enum read_ui16 input let i16s_of input = make_enum read_i16 input let i32s_of input = make_enum read_i32 input let real_i32s_of input = make_enum read_real_i32 input let i64s_of input = make_enum read_i64 input let doubles_of input = make_enum read_double input let floats_of input = make_enum read_float input end (** {6 Bits API} *) type 'a bc = { ch : 'a; mutable nbits : int; mutable bits : int; } type in_bits = input bc type out_bits = unit output bc exception Bits_error let input_bits ch = { ch = ch; nbits = 0; bits = 0; } let output_bits ch = { ch = cast_output ch; nbits = 0; bits = 0; } let rec read_bits b n = if b.nbits >= n then begin let c = b.nbits - n in let k = (b.bits asr c) land ((1 lsl n) - 1) in b.nbits <- c; k end else begin let k = read_byte b.ch in if b.nbits >= 24 then begin if n >= 31 then raise Bits_error; let c = 8 + b.nbits - n in let d = b.bits land ((1 lsl b.nbits) - 1) in let d = (d lsl (8 - c)) lor (k lsr c) in b.bits <- k; b.nbits <- c; d end else begin b.bits <- (b.bits lsl 8) lor k; b.nbits <- b.nbits + 8; read_bits b n; end end let drop_bits b = b.nbits <- 0 let rec write_bits b ~nbits x = let n = nbits in if n + b.nbits >= 32 then begin if n > 31 then raise Bits_error; let n2 = 32 - b.nbits - 1 in let n3 = n - n2 in write_bits b ~nbits:n2 (x asr n3); write_bits b ~nbits:n3 (x land ((1 lsl n3) - 1)); end else begin if n < 0 then raise Bits_error; if (x < 0 || x > (1 lsl n - 1)) && n <> 31 then raise Bits_error; b.bits <- (b.bits lsl n) lor x; b.nbits <- b.nbits + n; while b.nbits >= 8 do b.nbits <- b.nbits - 8; write_byte b.ch (b.bits asr b.nbits) done end let flush_bits b = if b.nbits > 0 then write_bits b ~nbits:(8 - b.nbits) 0 (** {6 Generic BatIO} *) class in_channel ch = object method input s pos len = input ch s pos len method close_in() = close_in ch end class out_channel ch = object method output s pos len = output ch s pos len method flush() = flush ch method close_out() = ignore(close_out ch) end class in_chars ch = object method get() = try read ch with No_more_input -> raise End_of_file method close_in() = close_in ch end class out_chars ch = object method put t = write ch t method flush() = flush ch method close_out() = ignore(close_out ch) end let from_in_channel ch = let cbuf = Bytes.create 1 in let read() = try if ch#input cbuf 0 1 = 0 then raise Sys_blocked_io; Bytes.unsafe_get cbuf 0 with End_of_file -> raise No_more_input in let input s p l = ch#input s p l in create_in ~read ~input ~close:ch#close_in let from_out_channel ch = let cbuf = Bytes.create 1 in let write c = Bytes.unsafe_set cbuf 0 c; if ch#output cbuf 0 1 = 0 then raise Sys_blocked_io; in let output s p l = ch#output s p l in create_out ~write ~output ~flush:ch#flush ~close:ch#close_out let from_in_chars ch = let input s p l = let i = ref 0 in try while !i < l do Bytes.unsafe_set s (p + !i) (ch#get()); incr i done; l with End_of_file when !i > 0 -> !i in create_in ~read:ch#get ~input ~close:ch#close_in let from_out_chars ch = let output s p l = for i = p to p + l - 1 do ch#put (Bytes.unsafe_get s i) done; l in create_out ~write:ch#put ~output ~flush:ch#flush ~close:ch#close_out (** {6 Enumerations} *) let bytes_of input = make_enum read_byte input let signed_bytes_of input = make_enum read_signed_byte input let ui16s_of input = make_enum read_ui16 input let i16s_of input = make_enum read_i16 input let i32s_of input = make_enum read_i32 input let real_i32s_of input = make_enum read_real_i32 input let i64s_of input = make_enum read_i64 input let doubles_of input = make_enum read_double input let floats_of input = make_enum read_float input let strings_of input = make_enum read_string input let lines_of input = make_enum read_line input let chunks_of n input = make_enum (fun ic -> nread ic n) input (** The number of chars to read at once *) let buffer_size = 1024 (*Arbitrary size.*) (* make a bunch of char enums by reading buffer_size at a time and concat them all into into one big char enum *) let chars_of input = let do_close = ref true in close_at_end do_close input (BatEnum.concat (BatEnum.from (fun () -> apply_enum do_close (fun source -> string_enum (nread source buffer_size)) input))) let bits_of input = let do_close = ref true in close_at_end do_close input.ch (BatEnum.from (fun () -> apply_enum do_close read_bits input 1)) (** Buffered lines_of, for performance. Ideas taken from ocaml stdlib *) let lines_of2 ic = let buf = Bytes.create buffer_size in let read_pos = ref 0 in (* next byte to read *) let end_pos = ref 0 in (* place to write new data *) let find_eol () = let rec find_loop pos = if pos >= !end_pos then !read_pos - pos else if Bytes.get buf pos = '\n' then 1 + pos - !read_pos (* TODO: HANDLE CRLF *) else find_loop (pos+1) in find_loop !read_pos in let join_strings total_len accu = let rec loop buf pos = function | [] -> () | h::t -> let len = Bytes.length h in Bytes.blit h 0 buf (pos-len) len; loop buf (pos-len) t in let buf = Bytes.create total_len in loop buf total_len accu; Bytes.unsafe_to_string buf in let input_buf s o l = Bytes.blit buf !read_pos s o l; read_pos := !read_pos + l; if !end_pos = !read_pos then try if !end_pos >= buffer_size then begin read_pos := 0; end_pos := input ic buf 0 buffer_size; end else begin let len_read = input ic buf 0 (buffer_size - !end_pos) in end_pos := !end_pos + len_read; end with No_more_input -> end_pos := !read_pos; in let get_line () = let rec get_pieces accu len = let n = find_eol () in if n = 0 then match accu with (* EOF *) | [] -> close_in ic; raise BatEnum.No_more_elements | _ -> join_strings len accu else if n > 0 then (* newline found *) let res = Bytes.create (n-1) in input_buf res 0 (n-1); input_buf (Bytes.of_string " ") 0 1; (* throw away EOL *) match accu with | [] -> Bytes.unsafe_to_string res | _ -> let len = len + n-1 in join_strings len (res :: accu) else (* n < 0 ; no newline found *) let piece = Bytes.create (-n) in input_buf piece 0 (-n); get_pieces (piece::accu) (len-n) in get_pieces [] 0 in (* prime the buffer *) end_pos := input ic buf 0 buffer_size; BatEnum.from get_line let write_bitss ~nbits output enum = write_enum (write_bits ~nbits) output enum (** {6 Utilities} *) let is_newline = function '\010' | '\013' -> true | _ -> false let tab_out ?(tab=' ') n out = let spaces = String.make n tab in wrap_out ~write: (fun c -> write out c; if is_newline c then nwrite out spaces; ) ~output:(fun s p l -> (*Replace each newline within the segment with newline^spaces*) let length = Bytes.length s in let buffer = Buffer.create length in for i = p to min (length - 1) l do let c = Bytes.unsafe_get s i in Buffer.add_char buffer c; if is_newline c then Buffer.add_string buffer spaces done; let s' = BatBytesCompat.buffer_to_bytes buffer in really_output out s' 0 (Bytes.length s')) ~flush:noop ~close:noop ~underlying:[out] (* let lmargin n (p:_ output -> 'a -> unit) out x = p (tab_out n (cast_output out)) x *) let comb (a,b) = create_out ~write:(fun c -> write a c; write b c) ~output:(fun s i j -> let _ = output a s i j in output b s i j) ~flush:(fun () -> flush a; flush b) ~close:(fun () -> ignore (close_out a); close_out b) (*let repeat n out = wrap_out ~underlying:[out] ~write:(fun c -> for i = 1 to n do write out c) ~output:(fun s p l -> for i = 1 to n do output out s p l) ~close:(fun () -> flush out)*) (*let copy input output = write_chunks output (chunks_of default_buffer_size input)*) (*let copy input output = write_chars output (chars_of input)*) let copy ?(buffer=4096) inp out = let n = buffer in let buf = Bytes.create n in try while true do let len = input inp buf 0 n in if len = 0 then raise No_more_input else ignore (really_output out buf 0 len) done with No_more_input -> () (*let fast_chunks_of n inp = let buffer = String.create n in make_enum (fun inp -> input inp buffer 0 n) input*) (* (** {6 Test} *) let in_channel_of_input i = let (cin, cout) = Unix.pipe () in let latest_pos_in = ref 0 in let rec aux () = let new_pos_in = pos_in cin in if new_pos_in > !latest_pos_in then (*Something has been read, we can write a little bit more*) let size = new_pos_in - !latest_pos_in in let buf = String.create size in input i buf (* UnixLabels.select ~read:? ~write:*) (* let (fin, fout) = Unix.socketpair Unix.PF_UNIX Unix.SOCK_STREAM in let cin = open_in fin and cout = open_out fout in let rec aux () = let c = read i in Pervasives.output_char cout c; aux ()*) *) (** {6 Thread-safety} *) let lock_factory = ref (fun () -> BatConcurrent.nolock) let synchronize_in ?(lock = !lock_factory ()) inp = wrap_in ~read:(BatConcurrent.sync lock (fun () -> read inp)) ~input:(BatConcurrent.sync lock (fun s p l -> input inp s p l)) ~close:noop ~underlying:[inp] let synchronize_out ?(lock = !lock_factory ()) out = wrap_out ~write: (BatConcurrent.sync lock (fun c -> write out c)) ~output:(fun s p -> BatConcurrent.sync lock (fun l -> output out s p l)) ~flush: (BatConcurrent.sync lock (fun () -> flush out)) ~close: noop ~underlying:[out] (** {6 Things that require temporary files} *) (** [to_input_channel inp] converts [inp] to an [in_channel]. In the simplest case, [inp] maps to a file descriptor, which makes it possible to just reopen the same file descriptor as an [in_channel]. There is no flushing with which to screw up and this shouldn't interfere with [pos_in] et al. because [inp] maps {e directly} to a file descriptor, not through higher-level abstract streams. Otherwise, read everything, write it to a temporary file and read it back as an [in_channel]. Yes, this is prohibitively expensive. *) let to_input_channel inp = try let descr = try BatUnix.descr_of_input inp with Invalid_argument _ -> raise Exit in (*Simple case*) Unix.in_channel_of_descr descr with Exit -> (*Bad, bad case*) (* FIXME: this 'pipe' is never deleted *) let (name, cout) = Filename.open_temp_file ~mode:[Open_binary] "ocaml" "pipe" in let out = output_channel cout in copy inp out; close_out out; open_in_bin name (*(** Copy everything to a temporary file *) let out_channel_of_output out = let (name, cout) = Filename.open_temp_file "ocaml" "tmp" in create_out cout*) let to_string print_x x = BatPrintf.sprintf2 "%a" print_x x let to_f_printer printer = fun fmt t -> Format.pp_print_string fmt (to_string printer t) module Incubator = struct module Array = struct let pp ?(flush = false) ?(first = "[|") ?(last = "|]") ?(sep = "; ") ?(indent = String.length first) pp f a = let open Format in pp_open_box f indent; pp_print_cut f (); pp_print_string f first; pp_print_cut f (); for i = 0 to Array.length a - 2 do pp_open_box f indent; pp f a.(i); pp_print_string f sep; pp_close_box f (); pp_print_cut f (); done; if Array.length a > 0 then ( (* Print the last element without a trailing separator *) pp_open_box f indent; pp f a.(Array.length a - 1); pp_close_box f (); ); pp_print_cut f (); pp_print_string f last; pp_close_box f (); if flush then pp_print_flush f () end module Enum = struct let pp ?(flush = false) ?(first = "") ?(last = "") ?(sep = " ") ?(indent = String.length first) pp f e = let open Format in pp_open_box f indent; pp_print_cut f (); pp_print_string f first; pp_print_cut f (); match BatEnum.get e with | None -> pp_print_string f last; pp_close_box f (); if flush then pp_print_flush f () | Some x -> pp_open_box f indent; pp f x; let rec aux () = match BatEnum.get e with | None -> pp_close_box f (); pp_print_cut f (); pp_print_string f last; pp_close_box f (); if flush then pp_print_flush f () | Some x -> pp_print_string f sep; pp_close_box f (); pp_print_cut f (); pp_open_box f indent; pp f x; aux () in aux () end module List = struct let pp ?(flush = false) ?(first = "[") ?(last = "]") ?(sep = "; ") ?(indent = String.length first) pp f l = let open Format in pp_open_box f indent; pp_print_cut f (); pp_print_string f first; pp_print_cut f (); match l with | [] -> pp_print_string f last; pp_close_box f (); if flush then pp_print_flush f () | hd :: tl -> pp_open_box f indent; pp f hd; let rec aux rem = match rem with | [] -> pp_close_box f (); pp_print_cut f (); pp_print_string f last; pp_close_box f (); if flush then pp_print_flush f () | hd :: tl -> pp_print_string f sep; pp_close_box f (); pp_print_cut f (); pp_open_box f indent; pp f hd; aux tl in aux tl end end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>