ikskuh / SDL.zig

A shallow wrapper around SDL that provides object API and error handling
MIT License
360 stars 79 forks source link

Render Text: out of bounds panic despite 0 sentinel #167

Closed clebs closed 5 months ago

clebs commented 6 months ago

Hi there! I have just updated to the latest version after a couple of months pause working on a small game.

To render text I am using sdl.ttf.renderTextSolid() and after updating to zig 0.12 and latest SDL.zig it panics despite the slice passed being 0 terminated. It used to work a couple of months ago (I unfortunately do not know the exact commit to bisect...).

I have created a minimal example to reproduce this:

const std = @import("std");
const sdl = @import("sdl2");

pub fn main() !void {
    // Init SDL2
    try sdl.init(.{
        .video = true,
        .events = true,
        .audio = true,
    });
    defer sdl.quit();

    try sdl.ttf.init();
    defer sdl.ttf.quit();

    var window = try sdl.createWindow(
        "My Game",
        .{ .centered = {} },
        .{ .centered = {} },
        1200,
        720,
        .{ .vis = .shown },
    );
    defer window.destroy();

    var renderer = try sdl.createRenderer(window, null, .{ .accelerated = true });
    defer renderer.destroy();

    try renderer.setLogicalSize(1200, 720);
    try renderer.setColorRGB(0x05, 0x88, 0x88);

    const font = try sdl.ttf.openFont("unispace.ttf", 12); // just a sample ttf file on the root of the project

    while (true) {
        // run Game
        var b = std.mem.zeroes([8]u8);
        _ = try std.fmt.bufPrint(b[0..], "{s}", .{"Hello"});

        std.debug.print("\n{any}\n", .{b}); // this prints: { 72, 101, 108, 108, 111, 0, 0, 0 } => Hello followed by 0s

        const text = try sdl.createTextureFromSurface(renderer, try font.renderTextSolid(b[0.. :0], sdl.Color.white));

        const textInfo = try text.query();
        try renderer.copy(text, .{ .x = 580, .y = 10, .width = @as(c_int, @intCast(textInfo.width)), .height = @as(c_int, @intCast(textInfo.height)) }, null);

        renderer.present();
    }
}

Running this code I get the following output:

thread 41674548 panic: index out of bounds: index 9, len 8
/main.zig:41:91: 0x1004a3003 in main (my-game)
        const text = try sdl.createTextureFromSurface(renderer, try font.renderTextSolid(b[0.. :0], sdl.Color.white));
                                                                                         ^

It seems as if the slice continues to be iterated despite hitting the sentinel. I tried debugging this but was not able to follow out of the C ABI boundary.

With some guidance I would gladly help fix it 🙂

ikskuh commented 5 months ago

It seems as if the slice continues to be iterated despite hitting the sentinel.

The fix is this:

- b[0.. :0]
+ b[0 .. b.len-1 :0]

:0 asserts that after the end of the slice a sentinel is present, which is not given in your case.

A way better solution is this:

        var b = std.mem.zeroes([8]u8);
        const string = try std.fmt.bufPrintZ(b[0..], "{s}", .{"Hello"});

        const text = try sdl.createTextureFromSurface(renderer, try font.renderTextSolid(string, sdl.Color.white));
clebs commented 5 months ago

Hi, Thanks for the solution! If I understand correctly :0 means I need a slice that has 0s after the end, not a slice which contains 0s in its last position(s).

Therefore I always have to provide a subslice of my array, guaranteeing that after subslice.len there is a 0.

I guess then this was working by accident in previous zig builds....

ikskuh commented 5 months ago

Thanks for the solution! If I understand correctly :0 means I need a slice that has 0s after the end, not a slice which contains 0s in its last position(s).

Exactly!