mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
vt: decouple row iterator allocation from population
Change row_iterator_new to only allocate with undefined fields, matching the pattern used by row_cells_new. The iterator is now populated via the render state get API with a new .row_iterator data kind, which slices the row data and resets y to null. This separates the lifetime of the opaque handle from the render state it iterates, letting callers allocate once and re-populate from different states without reallocating.
This commit is contained in:
@@ -119,6 +119,13 @@ typedef enum {
|
||||
|
||||
/** Current dirty state (GhosttyRenderStateDirty). */
|
||||
GHOSTTY_RENDER_STATE_DATA_DIRTY = 3,
|
||||
|
||||
/** Populate a pre-allocated GhosttyRenderStateRowIterator with row data
|
||||
* from the render state (GhosttyRenderStateRowIterator). Row data is
|
||||
* only valid as long as the underlying render state is not updated.
|
||||
* It is unsafe to use row data after updating the render state.
|
||||
* */
|
||||
GHOSTTY_RENDER_STATE_DATA_ROW_ITERATOR = 4,
|
||||
} GhosttyRenderStateData;
|
||||
|
||||
/**
|
||||
@@ -147,7 +154,9 @@ typedef enum {
|
||||
GHOSTTY_RENDER_STATE_ROW_DATA_RAW = 2,
|
||||
|
||||
/** Populate a pre-allocated GhosttyRenderStateRowCells with cell data for
|
||||
* the current row (GhosttyRenderStateRowCells). */
|
||||
* the current row (GhosttyRenderStateRowCells). Cell data is only
|
||||
* valid as long as the underlying render state is not updated.
|
||||
* It is unsafe to use cell data after updating the render state. */
|
||||
GHOSTTY_RENDER_STATE_ROW_DATA_CELLS = 3,
|
||||
} GhosttyRenderStateRowData;
|
||||
|
||||
@@ -298,21 +307,21 @@ GhosttyResult ghostty_render_state_colors_get(GhosttyRenderState state,
|
||||
GhosttyRenderStateColors* out_colors);
|
||||
|
||||
/**
|
||||
* Create a row iterator for a render state.
|
||||
* Create a new row iterator instance.
|
||||
*
|
||||
* The iterator borrows from `state`; `state` must outlive the iterator.
|
||||
* All fields except the allocator are left undefined until populated
|
||||
* via ghostty_render_state_get() with
|
||||
* GHOSTTY_RENDER_STATE_DATA_ROW_ITERATOR.
|
||||
*
|
||||
* @param allocator Pointer to allocator, or NULL to use the default allocator
|
||||
* @param state The render state handle to iterate (NULL returns GHOSTTY_INVALID_VALUE)
|
||||
* @param[out] out_iterator On success, receives the created iterator handle
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_INVALID_VALUE if `state` is
|
||||
* NULL, GHOSTTY_OUT_OF_MEMORY on allocation failure
|
||||
* @return GHOSTTY_SUCCESS on success, GHOSTTY_OUT_OF_MEMORY on allocation
|
||||
* failure
|
||||
*
|
||||
* @ingroup render
|
||||
*/
|
||||
GhosttyResult ghostty_render_state_row_iterator_new(
|
||||
const GhosttyAllocator* allocator,
|
||||
GhosttyRenderState state,
|
||||
GhosttyRenderStateRowIterator* out_iterator);
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,6 +58,7 @@ pub const Data = enum(c_int) {
|
||||
cols = 1,
|
||||
rows = 2,
|
||||
dirty = 3,
|
||||
row_iterator = 4,
|
||||
|
||||
/// Output type expected for querying the data of the given kind.
|
||||
pub fn OutType(comptime self: Data) type {
|
||||
@@ -65,6 +66,7 @@ pub const Data = enum(c_int) {
|
||||
.invalid => void,
|
||||
.cols, .rows => size.CellCountInt,
|
||||
.dirty => Dirty,
|
||||
.row_iterator => RowIterator,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -163,6 +165,17 @@ fn getTyped(
|
||||
.cols => out.* = state.state.cols,
|
||||
.rows => out.* = state.state.rows,
|
||||
.dirty => out.* = state.state.dirty,
|
||||
.row_iterator => {
|
||||
const it = out.* orelse return .invalid_value;
|
||||
const row_data = state.state.row_data.slice();
|
||||
it.* = .{
|
||||
.alloc = it.alloc,
|
||||
.y = null,
|
||||
.raws = row_data.items(.raw),
|
||||
.cells = row_data.items(.cells),
|
||||
.dirty = row_data.items(.dirty),
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
return .success;
|
||||
@@ -266,43 +279,22 @@ pub fn colors_get(
|
||||
|
||||
pub fn row_iterator_new(
|
||||
alloc_: ?*const CAllocator,
|
||||
state_: RenderState,
|
||||
out_iterator_: ?*RowIterator,
|
||||
result: *RowIterator,
|
||||
) callconv(.c) Result {
|
||||
const state = state_ orelse return .invalid_value;
|
||||
const out_iterator = out_iterator_ orelse return .invalid_value;
|
||||
const alloc = lib_alloc.default(alloc_);
|
||||
|
||||
out_iterator.* = row_iterator_new_(
|
||||
alloc,
|
||||
state,
|
||||
) catch |err| {
|
||||
out_iterator.* = null;
|
||||
switch (err) {
|
||||
error.OutOfMemory => return .out_of_memory,
|
||||
}
|
||||
const ptr = alloc.create(RowIteratorWrapper) catch {
|
||||
result.* = null;
|
||||
return .out_of_memory;
|
||||
};
|
||||
|
||||
return .success;
|
||||
}
|
||||
|
||||
fn row_iterator_new_(
|
||||
alloc: Allocator,
|
||||
state: *RenderStateWrapper,
|
||||
) !*RowIteratorWrapper {
|
||||
const it = try alloc.create(RowIteratorWrapper);
|
||||
errdefer alloc.destroy(it);
|
||||
|
||||
const row_data = state.state.row_data.slice();
|
||||
it.* = .{
|
||||
ptr.* = .{
|
||||
.alloc = alloc,
|
||||
.y = null,
|
||||
.raws = row_data.items(.raw),
|
||||
.cells = row_data.items(.cells),
|
||||
.dirty = row_data.items(.dirty),
|
||||
.y = undefined,
|
||||
.raws = undefined,
|
||||
.cells = undefined,
|
||||
.dirty = undefined,
|
||||
};
|
||||
|
||||
return it;
|
||||
result.* = ptr;
|
||||
return .success;
|
||||
}
|
||||
|
||||
pub fn row_iterator_free(iterator_: RowIterator) callconv(.c) void {
|
||||
@@ -559,25 +551,15 @@ test "render: set null value" {
|
||||
try testing.expectEqual(Result.invalid_value, set(state, .dirty, null));
|
||||
}
|
||||
|
||||
test "render: row iterator new invalid value" {
|
||||
var state: RenderState = null;
|
||||
try testing.expectEqual(Result.success, new(
|
||||
&lib_alloc.test_allocator,
|
||||
&state,
|
||||
));
|
||||
defer free(state);
|
||||
|
||||
test "render: row iterator get invalid value" {
|
||||
var iterator: RowIterator = null;
|
||||
try testing.expectEqual(Result.invalid_value, row_iterator_new(
|
||||
try testing.expectEqual(Result.success, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
null,
|
||||
&iterator,
|
||||
));
|
||||
try testing.expectEqual(Result.invalid_value, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
null,
|
||||
));
|
||||
defer row_iterator_free(iterator);
|
||||
|
||||
try testing.expectEqual(Result.invalid_value, get(null, .row_iterator, @ptrCast(&iterator)));
|
||||
}
|
||||
|
||||
test "render: row iterator new/free" {
|
||||
@@ -605,12 +587,14 @@ test "render: row iterator new/free" {
|
||||
var iterator: RowIterator = null;
|
||||
try testing.expectEqual(Result.success, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
&iterator,
|
||||
));
|
||||
defer row_iterator_free(iterator);
|
||||
|
||||
try testing.expect(iterator != null);
|
||||
|
||||
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator)));
|
||||
|
||||
const iterator_ptr = iterator.?;
|
||||
const row_data = state.?.state.row_data.slice();
|
||||
|
||||
@@ -658,11 +642,11 @@ test "render: row get invalid data" {
|
||||
var iterator: RowIterator = null;
|
||||
try testing.expectEqual(Result.success, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
&iterator,
|
||||
));
|
||||
defer row_iterator_free(iterator);
|
||||
|
||||
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator)));
|
||||
try testing.expect(row_iterator_next(iterator));
|
||||
try testing.expectEqual(Result.invalid_value, row_get(iterator, .invalid, null));
|
||||
}
|
||||
@@ -697,11 +681,11 @@ test "render: row set before iteration" {
|
||||
var iterator: RowIterator = null;
|
||||
try testing.expectEqual(Result.success, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
&iterator,
|
||||
));
|
||||
defer row_iterator_free(iterator);
|
||||
|
||||
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator)));
|
||||
const dirty = false;
|
||||
try testing.expectEqual(Result.invalid_value, row_set(iterator, .dirty, @ptrCast(&dirty)));
|
||||
}
|
||||
@@ -731,11 +715,11 @@ test "render: row get before iteration" {
|
||||
var iterator: RowIterator = null;
|
||||
try testing.expectEqual(Result.success, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
&iterator,
|
||||
));
|
||||
defer row_iterator_free(iterator);
|
||||
|
||||
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator)));
|
||||
var dirty: bool = undefined;
|
||||
try testing.expectEqual(Result.invalid_value, row_get(iterator, .dirty, @ptrCast(&dirty)));
|
||||
}
|
||||
@@ -770,11 +754,11 @@ test "render: row get/set dirty" {
|
||||
var it: RowIterator = null;
|
||||
try testing.expectEqual(Result.success, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
&it,
|
||||
));
|
||||
defer row_iterator_free(it);
|
||||
|
||||
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it)));
|
||||
try testing.expect(row_iterator_next(it));
|
||||
var dirty: bool = undefined;
|
||||
try testing.expectEqual(Result.success, row_get(it, .dirty, @ptrCast(&dirty)));
|
||||
@@ -788,11 +772,11 @@ test "render: row get/set dirty" {
|
||||
var it2: RowIterator = null;
|
||||
try testing.expectEqual(Result.success, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
&it2,
|
||||
));
|
||||
defer row_iterator_free(it2);
|
||||
|
||||
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&it2)));
|
||||
try testing.expect(row_iterator_next(it2));
|
||||
try testing.expectEqual(Result.success, row_get(it2, .dirty, @ptrCast(&dirty)));
|
||||
try testing.expect(!dirty);
|
||||
@@ -823,11 +807,12 @@ test "render: row iterator next" {
|
||||
var iterator: RowIterator = null;
|
||||
try testing.expectEqual(Result.success, row_iterator_new(
|
||||
&lib_alloc.test_allocator,
|
||||
state,
|
||||
&iterator,
|
||||
));
|
||||
defer row_iterator_free(iterator);
|
||||
|
||||
try testing.expectEqual(Result.success, get(state, .row_iterator, @ptrCast(&iterator)));
|
||||
|
||||
const rows = state.?.state.rows;
|
||||
if (rows == 0) {
|
||||
try testing.expect(!row_iterator_next(iterator));
|
||||
|
||||
Reference in New Issue
Block a user