bracket-lib icon indicating copy to clipboard operation
bracket-lib copied to clipboard

thread 'main' panicked at 'index out of bounds when using crossterm as the backend

Open aimerib opened this issue 3 years ago • 7 comments

I've been unable to use crossterm as my backend for bracket-lib, getting a compile error even when trying one of the examples.

I initially thought that this issue could be related to #256 but that issue seems specifically about how to get bracket to use crossterm instead of a opengl window, so I'm opening an issue for my specific error.

This is my minimal setup that seems to be able to reproduce my issue:

rustup toolchain: stable-x86_64-unknown-linux-gnu (default) rustc version: rustc 1.57.0 (f1edd0429 2021-11-29) cargo version: cargo 1.57.0 (b2e52d7ca 2021-10-21) Linux kernel: Linux 5.13.0-27-generic x86_64 Ubuntu version: Ubuntu 21.10 Iimpish Idri $TERM: xterm Shell: zsh

Cargo.toml

[package]
name = "bracket-test"
version = "0.1.0"
edition = "2021"
resolver = "2"

[dependencies]
bracket-terminal = { git = "https://github.com/amethyst/bracket-lib.git", version = "0.8.1", default-features = false, features = ["cross_term"] }
legion = { version = "0.4.0", default-features = false, features = ["wasm-bindgen"] }
lazy_static = "1.4.0"

sample main.rs to reproduce:

use bracket_terminal::prelude::*;

bracket_terminal::add_wasm_support!();

struct State {}

impl GameState for State {
    fn tick(&mut self, ctx: &mut BTerm) {
        ctx.print(1, 1, "Hello Bracket World");
    }
}

fn main() -> BError {
    let context = BTermBuilder::simple80x50()
        .with_title("Hello Minimal Bracket World")
        .build()?;

    let gs: State = State {};

    main_loop(context, gs)
}

Note: The above code is a copy paste from the hello_minimal.rs

The error I get is this:

cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target/debug/bracket-test`
thread 'main' panicked at 'index out of bounds: the len is 8930 but the index is 9310', /home/user/.cargo/git/checkouts/bracket-lib-bcfc6f411b633b6a/5e35a05/bracket-terminal/src/hal/crossterm_be/main_loop.rs:233:25
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

and the full backtrace:

Finished dev [unoptimized + debuginfo] target(s) in 0.05s                                                                                                                                                                                                   Running `target/debug/bracket-test`
thread 'main' panicked at 'index out of bounds: the len is 1840 but the index is 3920',
  /home/user/.cargo/git/checkouts/bracket-lib-bcfc6f411b633b6a/5e35a05/bracket-terminal/src/hal/crossterm_be/main_loop.rs:233:25                                                   
stack backtrace:                                                                                                 
  0: rust_begin_unwind at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/std/src/panicking.rs:517:5
  1: core::panicking::panic_fmt at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14
  2: core::panicking::panic_bounds_check at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:76:5
  3: <usize as core::slice::index::SliceIndex<[T]>>::index_mut at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/slice/index.rs:190:14
  4: core::slice::index::<impl core::ops::index::IndexMut<I> for [T]>::index_mut at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/slice/index.rs:26:9
  5: <alloc::vec::Vec<T,A> as core::ops::index::IndexMut<I>>::index_mut at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/alloc/src/vec/mod.rs:2508:9
  6: bracket_terminal::hal::crossterm_be::main_loop::full_redraw at /home/aimeri/.cargo/git/checkouts/bracket-lib-bcfc6f411b633b6a/5e35a05/bracket-terminal/src/hal/crossterm_be/main_loop.rs:233:25
  7: bracket_terminal::hal::crossterm_be::main_loop::main_loop sterm_be/main_loop.rs:140:34                     
  8: bracket_terminal::bterm::main_loop at /home/aimeri/.cargo/git/checkouts/bracket-lib-bcfc6f411b633b6a/5e35a05/bracket-terminal/src/bterm.rs:1083:5
  9: bracket_test::main at ./src/main.rs:43:5          
  10: core::ops::function::FnOnce::call_once at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

so it seems like on line 233 of main_loop.rs for crossterm_be this line is called:

buffer[buf_idx].glyph = to_char(t.glyph as u8);

which means that buffer.len() == 8930 and buf_idx == 9310. seems like buf_idx is calculated by this line

let mut buf_idx = (st.height as u16 - (y as u16 + 1)) as usize * width as usize;

on my machine the values from width and height are as follow when executing my application:

st.height == 50
st.width == 80
width == 190
height == 47

that means on first iteration of the loops in partial_redraw the buf_idx ends up being calculate as (50 - (0 + 1)) * 190 = 9310 but buffer is 190 * 47 = 8930 as per

let mut buffer = vec![OutputBuffer::default(); height as usize * width as usize];
# same as  vec![OutputBuffer::default(); 8930];

I'm glad to help troubleshoot this in any way I can, so feel free to ask for any additional information I can provide to help troubleshoot. I'm not afraid to jump in to the code in the library to get some debug outputs as well, so if that's helpful in anyway let me know.

aimerib avatar Feb 02 '22 17:02 aimerib

just another bit of info, the exact same error happens if I try to run the example hello_minimal with

cargo run --no-default-features --features cross_term --example hello_minimal

when running the example directly from the cloned repository

aimerib avatar Feb 02 '22 18:02 aimerib

That's kinda odd; I tried it on my windows setup, and the crossterm back-end resized my terminal to match the requested size. So the actual width & height match the requested dimensions. It looks like I'm happily assuming that the resize worked - resizing the window after it's running crashes it.

I'll see what I can come up with.

thebracket avatar Feb 03 '22 14:02 thebracket

The fix I just pushed takes care of the problem on my end. My testing options are a bit limited right now (I'm work-from-home with a newborn baby) - so I can't currently get to my Linux & Mac test boxes.

thebracket avatar Feb 03 '22 14:02 thebracket

Thank you so much for the fast reply, and congratulations on your new child! I find myself in a very similar situation, working from home with a 5mo baby, so I understand the struggle and appreciate the effort.

Alas, this didn't fix it for me. Now the buffer size and the buffer_idx always have the same size, which after lines 239 and 317 will always be one higher than the length of the buffer vector, therefore causing the next run of the loop to crash with an out of bounds again.

Since you mentioned this is working for you (on Windows I assume? wsl2 or cgywin?) I will do some more digging on my end to see if I can figure out why the discrepancies. It will take me some time as I have to setup a vm for this. I will also test it on a Mac to see if the behavior is any different. I will also try on a simple ubuntu vm with a fresh install to rule out any weirdness with whatever I might have installed in my system.

Just some extra behavioral information I thought was interesting: When I subtracted 1 from the initial buf_idx (to account for the fact that it is now created with the same size as the buffer itself) and reduce the buffer_idx at the end of the loop instead of increase, bracket-terminal compiles just fine, but the resulting console would only cover 2/5 of my screen (on the left side?). I don't think the above is related to the root issue, but it was nice to see I could at least get something on the screen.

Just for my own edification, why do we want the buf_id to be the same size as the buffer, instead of starting from 0 and increasing it with each iteration of the for loops until it reaches the size of the buffer?

aimerib avatar Feb 03 '22 20:02 aimerib

I should be able to take another look tomorrow. The backing buffer index is calculated with the terminal size, while the size of a console may not be exactly the same (sometimes you request a size and the window manager helpfully hands you another one, or the application is assuming a fixed size and we've ruined its day by resizing). So we calculate the index based on the console's coordinates - which might not be the same. I'm beginning to question my logic; I should be able to give this a good once-over tomorrow.

thebracket avatar Feb 04 '22 00:02 thebracket

@thebracket any ideas on this? I'm also getting this same error trying to use crossterm

MichaelMackus avatar Jan 14 '23 23:01 MichaelMackus

Strangely, I did just try the backend = "cross_term" on one of the examples in the git master (and in v0.8.7) and it works fine. Maybe this is only failing if you don't use bracket_terminal directly, or from an outside project? I tried copying the example over to my project's main.rs file (after changing the import line) and it failed there on bracket-lib v0.8.7

MichaelMackus avatar Jan 14 '23 23:01 MichaelMackus