Как безопасно использовать дополненную структуру в качестве ключа hashmap - PullRequest
1 голос
/ 07 апреля 2020

Я использую libuv для написания UDP-сервера. Чтобы отличить клиентов друг от друга, мне нужно взглянуть на IP-адрес источника и порт источника. Это обеспечивается в обратном вызове on_read как const struct sockaddr*. Мне нужно использовать эту информацию как ключ для поиска контекста пользователя.

В идеале я бы использовал hashmap и использовал бы эту структуру в качестве ключа. Однако неясно, если libuv ноль инициализирует эту структуру, и поэтому в заполнении могут быть случайные данные, что делает его непригодным в качестве необработанного ключа hashmap (memcmp в структуре).

Предполагая, что libuv не обнуляет сначала заполнение, какой самый эффективный способ создать ключ из этой информации? Я думаю, что могу просто использовать присваивание или memcpy, чтобы скопировать два поля, которые я хочу, в чистую структуру, но мне придется делать это для каждого пакета.

Я знаю, что в общей схеме вещей это не огромные накладные расходы, но я пропустил более элегантное или эффективное решение?

Редактировать: я обновил заголовок, чтобы отразить это, хотя моя задача с libuv прямо сейчас, это на самом деле это не просто проблема libuv Speci c, поскольку такая структура может происходить из разных мест. Когда вам передают структуру откуда-то и вам нужно использовать это (или его содержимое) в качестве ключа, каков правильный / безопасный способ сделать это?

1 Ответ

1 голос
/ 08 апреля 2020

РЕДАКТИРОВАТЬ: Добавление ответа «generi c», возвращение неверного ответа TCP libuv.

Если вы не хотите копировать структуру (в данном случае она очень мала, но в виде generi c проблема) простое решение состоит из sh член за членом.

Давайте предположим, что вам нужно извлечь много разреженных полей из большой структуры. Например, если значение ha sh сводится только к сумме:

#define KEY_INITIAL_STATUS 0

void hash(char *status, const char *buf, size_t len) {
    size_t i;
    for (i=0; i<len; ++i)
        status += buf[i];
}

void receive_buf(struct addr_t addr, ...) {
    char key = KEY_INITIAL_STATUS;
    hash(&key, addr.field1, addr.field1_len);
    hash(&key, addr.field2, addr.field2_len);

    void *value = hashtable_search(hashtable, key, ...);

    // Do things with the value
}

Большинство хешей можно рассчитать таким образом, а затем оптимизировать (нет необходимости в байтах).

Требуется эталонный тест, чтобы проверить, лучше ли это сделать или скопировать все в нулевую структуру.


Я вижу, что обратный вызов чтения libuv использует эту сигнатуру:

void read_cb(uv_stream_t * stream, ssize_t nread, const uv_buf_t *buf)

Данные клиента связаны с соединением / потоком, и libuv уже сделал этот поиск для вас. Библиотека ожидает, что вы как-то передадите данные.

Если я поищу здесь do c:

http://docs.libuv.org/en/v1.x/stream.html

«См. Также: uv_handle_t члены также применяются.»

Поэтому, если я проверю uv_handle_t членов в http://docs.libuv.org/en/v1.x/handle.html#c .uv_handle_t :

void * uv_handle_t.data

Пространство для пользовательских произвольных данных. libuv не использует это поле.

Таким образом, вы должны сохранять и использовать информацию о своем клиенте здесь, вам не нужно выполнять один поиск.

В других библиотеках обычно встречается возвращать данные этого типа либо в структуре соединения, в качестве параметра обратного вызова on_read (или аналогичного) в виде указателя void *, либо даже выделять больше памяти в структуре library_stream_t, например malloc(sizeof(uv_stream_t) + sizeof(my_opaque_data).

...