Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ext/bubbletea/extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ extern const rb_data_type_t program_type;

typedef struct {
unsigned long long handle;
char pending_buf[256]; /* buffered bytes not yet consumed by poll_event */
int pending_len; /* number of valid bytes in pending_buf */
} bubbletea_program_t;

#define GET_PROGRAM(self, program) \
Expand Down
27 changes: 23 additions & 4 deletions ext/bubbletea/program.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "extension.h"
#include <string.h>

static void program_free(void *pointer) {
bubbletea_program_t *program = (bubbletea_program_t *)pointer;
Expand Down Expand Up @@ -27,6 +28,7 @@ const rb_data_type_t program_type = {
static VALUE program_alloc(VALUE klass) {
bubbletea_program_t *program = ALLOC(bubbletea_program_t);
program->handle = tea_new_program();
program->pending_len = 0;
return TypedData_Wrap_Struct(klass, &program_type, program);
}

Expand Down Expand Up @@ -159,20 +161,37 @@ static VALUE program_poll_event(VALUE self, VALUE timeout_ms) {
GET_PROGRAM(self, program);

char buffer[256];
int bytes_read = tea_input_read_raw(program->handle, buffer, sizeof(buffer), NUM2INT(timeout_ms));
int bytes_avail;

if (bytes_read <= 0) {
return Qnil; // Timeout or error
if (program->pending_len > 0) {
/* Consume buffered bytes from a previous read first */
memcpy(buffer, program->pending_buf, program->pending_len);
bytes_avail = program->pending_len;
program->pending_len = 0;
} else {
/* No buffered data — read from stdin */
bytes_avail = tea_input_read_raw(program->handle, buffer, sizeof(buffer), NUM2INT(timeout_ms));
if (bytes_avail <= 0) {
return Qnil; /* Timeout or error */
}
}

int consumed;
char *json = tea_parse_input_with_consumed(buffer, bytes_read, &consumed);
char *json = tea_parse_input_with_consumed(buffer, bytes_avail, &consumed);

if (json == NULL || json[0] == '\0') {
tea_free(json);
/* If parse failed but there are remaining bytes, discard them to avoid loops */
return Qnil;
}

/* Save any unconsumed bytes for the next poll_event call */
int remaining = bytes_avail - consumed;
if (remaining > 0 && remaining <= (int)sizeof(program->pending_buf)) {
memcpy(program->pending_buf, buffer + consumed, remaining);
program->pending_len = remaining;
}

VALUE rb_json = rb_utf8_str_new_cstr(json);
tea_free(json);

Expand Down