Implement storage of URLs in a hash map

This commit is contained in:
Kovid Goyal 2020-08-26 13:25:37 +05:30
parent e99d93ca30
commit 33beecddda
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
10 changed files with 1428 additions and 31 deletions

View File

@ -41,7 +41,8 @@ void log_error(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
typedef unsigned long long id_type;
typedef uint32_t char_type;
typedef uint32_t color_type;
typedef uint32_t hyperlink_id_type;
typedef uint16_t hyperlink_id_type;
#define HYPERLINK_MAX_NUMBER UINT16_MAX
typedef uint16_t combining_type;
typedef uint32_t pixel;
typedef unsigned int index_type;
@ -249,6 +250,7 @@ typedef struct {
typedef struct {int x;} *SPRITE_MAP_HANDLE;
#define FONTS_DATA_HEAD SPRITE_MAP_HANDLE sprite_map; double logical_dpi_x, logical_dpi_y, font_sz_in_pts; unsigned int cell_width, cell_height;
typedef struct {FONTS_DATA_HEAD} *FONTS_DATA_HANDLE;
typedef struct {int x;} *HYPERLINK_POOL_HANDLE;
#define PARSER_BUF_SZ (8 * 1024)
#define READ_BUF_SZ (1024*1024)

View File

@ -155,6 +155,11 @@ historybuf_init_line(HistoryBuf *self, index_type lnum, Line *l) {
init_line(self, index_of(self, lnum), l);
}
CPUCell*
historybuf_cpu_cells(HistoryBuf *self, index_type lnum) {
return cpu_lineptr(self, index_of(self, lnum));
}
void
historybuf_mark_line_clean(HistoryBuf *self, index_type y) {
line_attrs_type *p = attrptr(self, index_of(self, y));

View File

@ -5,24 +5,122 @@
* Distributed under terms of the GPL3 license.
*/
#define _POSIX_C_SOURCE 200809L
#include "hyperlink.h"
#include "uthash.h"
#include <string.h>
#include "screen.h"
bool
parse_osc_8(char *buf, char **id, char **url) {
char *boundary = strstr(buf, ";");
if (boundary == NULL) return false;
*boundary = 0;
if (*(boundary + 1)) *url = boundary + 1;
char *save, *token = strtok_r(buf, ":", &save);
while (token != NULL) {
size_t len = strlen(token);
if (len > 3 && token[0] == 'i' && token[1] == 'd' && token[2] == '=' && token[3]) {
*id = token + 3;
break;
}
token = strtok_r(NULL, ":", &save);
}
return true;
#define MAX_KEY_LEN 2048
#undef uthash_fatal
#define uthash_fatal(msg) fatal(msg)
typedef struct {
const char *key;
hyperlink_id_type id;
UT_hash_handle hh;
} HyperLinkEntry;
typedef struct {
HyperLinkEntry *hyperlinks;
unsigned int max_link_id, num_of_adds_since_garbage_collection;
} HyperLinkPool;
static void
free_hyperlink_entry(HyperLinkEntry *s) {
free((void*)s->key);
free(s);
}
static void
clear_pool(HyperLinkPool *pool) {
if (pool->hyperlinks) {
HyperLinkEntry *tmp, *s;
HASH_ITER(hh, pool->hyperlinks, s, tmp) {
HASH_DEL(pool->hyperlinks, s);
free_hyperlink_entry(s);
}
pool->max_link_id = 0;
}
}
HYPERLINK_POOL_HANDLE
alloc_hyperlink_pool(void) {
return calloc(1, sizeof(HyperLinkPool));
}
void
clear_hyperlink_pool(HYPERLINK_POOL_HANDLE h) {
if (h) {
HyperLinkPool *pool = (HyperLinkPool*)h;
clear_pool(pool);
}
}
void
free_hyperlink_pool(HYPERLINK_POOL_HANDLE h) {
if (h) {
HyperLinkPool *pool = (HyperLinkPool*)h;
clear_pool(pool);
free(pool);
}
}
static void
garbage_collect_pool(Screen *screen) {
HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;
pool->num_of_adds_since_garbage_collection = 0;
if (!pool->max_link_id) return;
hyperlink_id_type *map = calloc(HYPERLINK_MAX_NUMBER + 4, sizeof(hyperlink_id_type));
if (!map) fatal("Out of memory");
hyperlink_id_type num = remap_hyperlink_ids(screen, map);
if (num) {
HyperLinkEntry *s, *tmp;
pool->max_link_id = 0;
HASH_ITER(hh, pool->hyperlinks, s, tmp) {
if (map[s->id]) {
s->id = map[s->id];
pool->max_link_id = MAX(pool->max_link_id, s->id);
} else {
HASH_DEL(pool->hyperlinks, s);
free_hyperlink_entry(s);
}
}
} else clear_pool(pool);
free(map);
}
hyperlink_id_type
get_id_for_hyperlink(Screen *screen, const char *id, const char *url) {
if (!url) return 0;
HyperLinkPool *pool = (HyperLinkPool*)screen->hyperlink_pool;
static char key[MAX_KEY_LEN] = {0};
size_t keylen = snprintf(key, MAX_KEY_LEN-1, "%s:%s", id ? id : "", url);
HyperLinkEntry *s = NULL;
if (pool->hyperlinks) {
HASH_FIND_STR(pool->hyperlinks, key, s);
if (s) return s->id;
}
hyperlink_id_type new_id = 0;
if (pool->num_of_adds_since_garbage_collection >= 256) garbage_collect_pool(screen);
if (pool->max_link_id >= HYPERLINK_MAX_NUMBER) {
log_error("Too many hyperlinks, discarding oldest, this means some hyperlinks might be incorrect");
new_id = pool->hyperlinks->id;
HyperLinkEntry *s = pool->hyperlinks;
HASH_DEL(pool->hyperlinks, s);
free_hyperlink_entry(s);
}
s = malloc(sizeof(HyperLinkEntry));
if (!s) fatal("Out of memory");
s->key = malloc(keylen + 1);
if (!s->key) fatal("Out of memory");
memcpy((void*)s->key, key, keylen + 1);
s->id = new_id ? new_id : ++pool->max_link_id;
HASH_ADD_KEYPTR(hh, pool->hyperlinks, s->key, keylen, s);
pool->num_of_adds_since_garbage_collection++;
return s->id;
}

15
kitty/hyperlink.h Normal file
View File

@ -0,0 +1,15 @@
/*
* Copyright (C) 2020 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
#include "screen.h"
HYPERLINK_POOL_HANDLE alloc_hyperlink_pool(void);
void free_hyperlink_pool(HYPERLINK_POOL_HANDLE);
void clear_hyperlink_pool(HYPERLINK_POOL_HANDLE);
hyperlink_id_type get_id_for_hyperlink(Screen*, const char*, const char*);
hyperlink_id_type remap_hyperlink_ids(Screen *self, hyperlink_id_type *map);

View File

@ -420,6 +420,7 @@ set_text(Line* self, PyObject *args) {
for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) {
self->cpu_cells[i].ch = (PyUnicode_READ(kind, buf, offset));
self->cpu_cells[i].hyperlink_id = 0;
self->gpu_cells[i].attrs = attrs;
self->gpu_cells[i].fg = fg;
self->gpu_cells[i].bg = bg;
@ -454,15 +455,10 @@ cursor_from(Line* self, PyObject *args) {
void
line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch) {
attrs_type width = ch ? 1 : 0;
#define PREFIX \
for (index_type i = at; i < MIN(self->xnum, at + num); i++) { \
self->cpu_cells[i].ch = ch; memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx)); \
self->gpu_cells[i].attrs = (self->gpu_cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \
}
if (CHAR_IS_BLANK(ch)) {
PREFIX
} else {
PREFIX
for (index_type i = at; i < MIN(self->xnum, at + num); i++) {
self->cpu_cells[i].ch = ch; memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx));
self->cpu_cells[i].hyperlink_id = 0;
self->gpu_cells[i].attrs = (self->gpu_cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width;
}
}
@ -486,6 +482,7 @@ line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num,
for (index_type i = at; i < self->xnum && i < at + num; i++) {
if (clear_char) {
self->cpu_cells[i].ch = BLANK_CHAR;
self->cpu_cells[i].hyperlink_id = 0;
memset(self->cpu_cells[i].cc_idx, 0, sizeof(self->cpu_cells[i].cc_idx));
self->gpu_cells[i].attrs = attrs;
clear_sprite_position(self->gpu_cells[i]);
@ -517,6 +514,7 @@ void line_right_shift(Line *self, unsigned int at, unsigned int num) {
char_type w = (self->gpu_cells[self->xnum - 1].attrs) & WIDTH_MASK;
if (w != 1) {
self->cpu_cells[self->xnum - 1].ch = BLANK_CHAR;
self->cpu_cells[self->xnum - 1].hyperlink_id = 0;
self->gpu_cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0;
clear_sprite_position(self->gpu_cells[self->xnum - 1]);
}

View File

@ -28,7 +28,7 @@ static inline void
clear_chars_in_line(CPUCell *cpu_cells, GPUCell *gpu_cells, index_type xnum, char_type ch) {
// Clear only the char part of each cell, the rest must have been cleared by a memset or similar
if (ch) {
for (index_type i = 0; i < xnum; i++) { cpu_cells[i].ch = ch; gpu_cells[i].attrs = 1; }
for (index_type i = 0; i < xnum; i++) { cpu_cells[i].ch = ch; cpu_cells[i].hyperlink_id = 0; gpu_cells[i].attrs = 1; }
}
}
@ -61,6 +61,7 @@ left_shift_line(Line *line, index_type at, index_type num) {
}
if (at < line->xnum && ((line->gpu_cells[at].attrs) & WIDTH_MASK) != 1) {
line->cpu_cells[at].ch = BLANK_CHAR;
line->cpu_cells[at].hyperlink_id = 0;
line->gpu_cells[at].attrs = BLANK_CHAR ? 1 : 0;
clear_sprite_position(line->gpu_cells[at]);
}
@ -102,6 +103,7 @@ void linebuf_refresh_sprite_positions(LineBuf *self);
void historybuf_add_line(HistoryBuf *self, const Line *line);
void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other);
void historybuf_init_line(HistoryBuf *self, index_type num, Line *l);
CPUCell* historybuf_cpu_cells(HistoryBuf *self, index_type num);
void historybuf_mark_line_clean(HistoryBuf *self, index_type y);
void historybuf_mark_line_dirty(HistoryBuf *self, index_type y);
void historybuf_refresh_sprite_positions(HistoryBuf *self);

View File

@ -4,6 +4,9 @@
* Distributed under terms of the GPL3 license.
*/
// Need _POSIX_C_SOURCE for strtok_r
#define _POSIX_C_SOURCE 200809L
#include "data-types.h"
#include "control-codes.h"
#include "screen.h"
@ -306,6 +309,24 @@ handle_esc_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_cal
// OSC mode {{{
static inline bool
parse_osc_8(char *buf, char **id, char **url) {
char *boundary = strstr(buf, ";");
if (boundary == NULL) return false;
*boundary = 0;
if (*(boundary + 1)) *url = boundary + 1;
char *save, *token = strtok_r(buf, ":", &save);
while (token != NULL) {
size_t len = strlen(token);
if (len > 3 && token[0] == 'i' && token[1] == 'd' && token[2] == '=' && token[3]) {
*id = token + 3;
break;
}
token = strtok_r(NULL, ":", &save);
}
return true;
}
static inline void
dispatch_hyperlink(Screen *screen, size_t pos, size_t size, PyObject DUMP_UNUSED *dump_callback) {
// since the spec says only ASCII printable chars are allowed in OSC 8, we

View File

@ -13,7 +13,7 @@
#include "state.h"
#include "fonts.h"
#include "lineops.h"
#include "screen.h"
#include "hyperlink.h"
#include <structmember.h>
#include <limits.h>
#include <sys/types.h>
@ -129,6 +129,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
init_tabstops(self->main_tabstops, self->columns);
init_tabstops(self->alt_tabstops, self->columns);
if (!init_overlay_line(self, self->columns)) { Py_CLEAR(self); return NULL; }
self->hyperlink_pool = alloc_hyperlink_pool();
if (!self->hyperlink_pool) { Py_CLEAR(self); return PyErr_NoMemory(); }
}
return (PyObject*) self;
}
@ -142,6 +144,7 @@ screen_reset(Screen *self) {
if (self->overlay_line.is_active) deactivate_overlay_line(self);
linebuf_clear(self->linebuf, BLANK_CHAR);
historybuf_clear(self->historybuf);
clear_hyperlink_pool(self->hyperlink_pool);
grman_clear(self->grman, false, self->cell_size);
self->modes = empty_modes;
self->active_hyperlink_id = 0;
@ -289,6 +292,7 @@ dealloc(Screen* self) {
PyMem_Free(self->overlay_line.gpu_cells);
PyMem_Free(self->main_tabstops);
free(self->pending_mode.buf);
free_hyperlink_pool(self->hyperlink_pool);
Py_TYPE(self)->tp_free((PyObject*)self);
} // }}}
@ -368,8 +372,33 @@ set_active_hyperlink(Screen *self, char *id, char *url) {
self->active_hyperlink_id = 0;
return;
}
self->active_hyperlink_id = get_id_for_hyperlink(self, id, url);
}
hyperlink_id_type
remap_hyperlink_ids(Screen *self, hyperlink_id_type *map) {
#define PROCESS_CELL(cell) { hid = (cell).hyperlink_id; if (hid) { if (!map[hid]) map[hid] = ++num; (cell).hyperlink_id = map[hid]; }}
hyperlink_id_type num = 0, hid;
if (self->historybuf->count) {
for (index_type y = self->historybuf->count; y-- > 0;) {
CPUCell *cells = historybuf_cpu_cells(self->historybuf, y);
for (index_type x = 0; x < self->historybuf->xnum; x++) {
PROCESS_CELL(cells[x]);
}
}
}
LineBuf *second = self->linebuf, *first = second == self->main_linebuf ? self->alt_linebuf : self->main_linebuf;
for (index_type i = 0; i < self->lines * self->columns; i++) {
PROCESS_CELL(first->cpu_cell_buf[i]);
}
for (index_type i = 0; i < self->lines * self->columns; i++) {
PROCESS_CELL(second->cpu_cell_buf[i]);
}
return num;
#undef PROCESS_CELL
}
static inline bool is_flag_pair(char_type a, char_type b) {
return is_flag_codepoint(a) && is_flag_codepoint(b);
}

View File

@ -124,6 +124,7 @@ typedef struct {
bool has_focus;
bool has_activity_since_last_focus;
hyperlink_id_type active_hyperlink_id;
HYPERLINK_POOL_HANDLE hyperlink_pool;
} Screen;
@ -207,7 +208,6 @@ bool screen_history_scroll(Screen *self, int amt, bool upwards);
Line* screen_visual_line(Screen *self, index_type y);
unsigned long screen_current_char_width(Screen *self);
void screen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y);
bool parse_osc_8(char *buf, char **id, char **url);
void set_active_hyperlink(Screen*, char*, char*);
void screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload);
bool screen_open_url(Screen*);

1227
kitty/uthash.h Normal file

File diff suppressed because it is too large Load Diff