feat(integrate utable): deps/utable

This commit is contained in:
yangwei
2024-11-25 19:22:19 +08:00
parent cce1155ae3
commit 1199e9d83f
35 changed files with 38191 additions and 232 deletions

17
deps/utable/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,17 @@
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lm")
add_library(utable SHARED utable.c utable_ipfix_exporter.c)
target_link_libraries(utable base64 nmx_pool cjson-static yyjson mpack)
add_executable(ipfix_exporter_example
utable_ipfix_exporter.c
ipfix_exporter_example.cpp
)
target_link_libraries(
ipfix_exporter_example
utable
pthread
)
add_subdirectory(test)

197
deps/utable/ipfix_exporter_example.cpp vendored Normal file
View File

@@ -0,0 +1,197 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "utable.h"
#include "cjson/cJSON.h"
#define THREAD_MAX 8
#define TEMPLATE_MAX 13
struct ipfix_template_id_list
{
const char *template_name;
int template_id;
};
struct ipfix_template_id_list template_id_list[TEMPLATE_MAX] = {
{"BASE", 0},
{"SSL", 0},
{"HTTP", 0},
{"MAIL", 0},
{"DNS", 0},
{"DTLS", 0},
{"QUIC", 0},
{"FTP", 0},
{"SIP", 0},
{"RTP", 0},
{"SSH", 0},
{"RDP", 0},
{"Stratum", 0}};
int g_udp_sock_fd = 0;
const char *ipfix_schema_json_path = NULL;
struct ipfix_exporter_schema *g_ipfix_schema = NULL;
static int ipfix_exporter_get_socket_fd(char *collector_ip, uint16_t collector_port)
{
int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_fd <= 0)
{
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(collector_port);
addr.sin_addr.s_addr = inet_addr(collector_ip);
if (connect(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
printf("connect error, illegal collector ip or port\n");
printf("expample: ./ipfix_exporter_example 127.0.0.1 4397");
close(sock_fd);
return -1;
}
return sock_fd;
}
void *ipfix_template_send_thread_loop(void *arg)
{
int interval_s = (*(int *)arg);
while (1)
{
size_t blob_len = 0;
char *blob = NULL;
for (int i = 0; i < THREAD_MAX; i++)
{
blob = (char *)utable_ipfix_template_flow_get0(g_ipfix_schema, i, &blob_len);
send(g_udp_sock_fd, blob, blob_len, 0);
blob = NULL;
}
sleep(interval_s);
}
return NULL;
}
extern "C" int load_file_to_memory(const char *file_name, unsigned char **pp_out, size_t *out_sz);
void ipfix_exporter_test_utable_init(struct utable *table, int index, const char *file_name)
{
size_t json_size = 0;
unsigned char *json_str = NULL;
load_file_to_memory(file_name, &json_str, &json_size);
if (json_str == NULL || json_size == 0)
{
return;
}
cJSON *root = NULL;
root = cJSON_Parse((const char *)json_str);
cJSON *template_item = cJSON_GetArrayItem(cJSON_GetObjectItem(root, "templates"), index);
cJSON *template_key_array = cJSON_GetObjectItem(template_item, "elements");
for (int i = 0; i < cJSON_GetArraySize(template_key_array); i++)
{
char *template_key = cJSON_GetArrayItem(template_key_array, i)->valuestring;
cJSON *elements_array = cJSON_GetObjectItem(root, template_key);
for (int j = 0; j < cJSON_GetArraySize(elements_array); j++)
{
cJSON *element = cJSON_GetArrayItem(elements_array, j);
char *element_key = cJSON_GetObjectItem(element, "element_name")->valuestring;
if (strcmp(cJSON_GetObjectItem(element, "element_type")->valuestring, "string") == 0)
{
char temp[128] = {0};
snprintf(temp, 128, "%s_%s_%d", element_key, "string", cJSON_GetObjectItem(element, "element_id")->valueint);
utable_add_cstring(table, element_key, temp, strlen(temp));
}
else if (strcmp(cJSON_GetObjectItem(element, "element_type")->valuestring, "unsigned64") == 0 ||
strcmp(cJSON_GetObjectItem(element, "element_type")->valuestring, "unsigned32") == 0 ||
strcmp(cJSON_GetObjectItem(element, "element_type")->valuestring, "unsigned16") == 0 ||
strcmp(cJSON_GetObjectItem(element, "element_type")->valuestring, "unsigned8") == 0)
{
utable_add_integer(table, element_key, cJSON_GetObjectItem(element, "element_id")->valueint);
}
}
}
free(json_str);
cJSON_Delete(root);
}
void *ipfix_worker_thread_data_flow_send(void *arg)
{
uint16_t worker_id = (*(uint16_t *)arg);
while (1)
{
for (int i = 0; i < TEMPLATE_MAX; i++)
{
struct utable *table = utable_new();
ipfix_exporter_test_utable_init(table, i, ipfix_schema_json_path);
utable_delete_item(table, "decoded_as");
utable_add_cstring(table, "decoded_as", template_id_list[i].template_name, strlen(template_id_list[i].template_name));
size_t blob_len = 0;
char *blob = NULL;
utable_ipfix_data_flow_exporter(table, g_ipfix_schema, template_id_list[i].template_id, worker_id, &blob, &blob_len);
send(g_udp_sock_fd, blob, blob_len, 0);
free(blob);
blob = NULL;
utable_free(table);
}
sleep(5);
}
return NULL;
}
// ./ipfix_exporter_example ipfix_schema.json 127.0.0.1 4397
extern "C" int main(int argc, char *argv[])
{
if (argc != 4)
{
printf("expample: ./ipfix_exporter_example ipfix_schema.json 127.0.0.1 4397\n");
return -1;
}
ipfix_schema_json_path = argv[1];
g_ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, 1, THREAD_MAX);
if (g_ipfix_schema == NULL)
{
printf("ipfix_exporter_schema_init error, illegal ipfix_schema_json_path: %s\n", ipfix_schema_json_path);
return -1;
}
for (int i = 0; i < TEMPLATE_MAX; i++)
{
template_id_list[i].template_id = utable_ipfix_template_get(g_ipfix_schema, template_id_list[i].template_name);
}
g_udp_sock_fd = ipfix_exporter_get_socket_fd(argv[2], atoi(argv[3]));
int interval_s = 100;
pthread_t template_thread_id;
pthread_create(&template_thread_id, NULL, ipfix_template_send_thread_loop, (void *)&interval_s);
uint16_t worker_id[THREAD_MAX];
pthread_t pid[THREAD_MAX];
for (int i = 0; i < THREAD_MAX; i++)
{
worker_id[i] = i;
pthread_create(&pid[i], NULL, ipfix_worker_thread_data_flow_send, (void *)&worker_id[i]);
}
sleep(1000);
utable_ipfix_exporter_schema_free(g_ipfix_schema);
pthread_join(template_thread_id, NULL);
for (int i = 0; i < THREAD_MAX; i++)
{
pthread_join(pid[i], NULL);
}
close(g_udp_sock_fd);
return 0;
}

31
deps/utable/test/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,31 @@
include_directories(${CMAKE_SOURCE_DIR}/deps)
add_executable(gtest_utable
unit_test_utable.cpp
)
target_link_libraries(
gtest_utable
utable
gtest
)
add_executable(gtest_ipfix_exporter
unit_test_ipfix_exporter.cpp
)
target_link_libraries(
gtest_ipfix_exporter
utable
gtest
)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/conf DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
include(GoogleTest)
gtest_discover_tests(gtest_utable)
gtest_discover_tests(gtest_ipfix_exporter
TEST_LIST gtest_ipfix_exporter_tests
)
set_tests_properties(${gtest_ipfix_exporter_tests} PROPERTIES WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/)

File diff suppressed because one or more lines are too long

1222
deps/utable/test/conf/ipfix_schema.json vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
{
"flags_identify_info": [1,2,3],
"security_rule_list": ["aaa","bbb", "ccc"],
"tcp_rtt_ms": 61,
"ssl_ja3s_hash": "cd5a8d2e276eabf0839bf1a25acc479e",
"ssl_version": "v3",
"ssl_cn": "*.google.com",
"ssl_cert_issuer": "CN=GTS CA 1C3;O=Google Trust Services LLC;C=US;;;;",
"ssl_cert_subject": "CN=*.google.com;;;;;;",
"ssl_san": "*.google.com;*.appengine.google.com;*.bdn.dev;*.cloud.google.com;*.crowdsource.google.com;*.datacompute.google.com;*.google.ca;*.google.cl;*.google.co.in;*.google.co.jp;*.google.co.uk;*.google.com.ar;*.google.com.au;*.google.com.br;*.google.com.co;*.google.com.mx;*.google.com.tr;*.google.com.vn;*.google.de;*.google.es;*.google.fr;*.google.hu;*.google.it;*.google.nl;*.google.pl;*.google.pt;*.googleadapis.com;*.googleapis.cn;*.googlevideo.com;*.gstatic.cn;*.gstatic-cn.com;*.gstaticcnapps.cn;googlecnapps.cn;*.googlecnapps.cn;googleapps-cn.com;*.googleapps-cn.com;gkecnapps.cn;*.gkecnapps.cn;googledownloads.cn;*.googledownloads.cn;recaptcha.net.cn;*.recaptcha.net.cn;widevine.cn;*.widevine.cn;ampproject.org.cn;*.ampproject.org.cn;ampproject.net.cn;*.ampproject.net.cn;google-analytics-cn.com;*.google-analytics-cn.com;googleadservices-cn.com;*.googleadservices-cn.com;googlevads-cn.com;*.googlevads-cn.com;googleapis-cn.com;*.googleapis-cn.com;googleoptimize-cn.com;*.googleoptimize-cn.com;doubleclick-cn.net;*.doubleclick-cn.net;*.fls.doubleclick-cn.net;*.g.doubleclick-cn.net;doubleclick.cn;*.doubleclick.cn;*.fls.doubleclick.cn;*.g.doubleclick.cn;dartsearch-cn.net;*.dartsearch-cn.net;googletraveladservices-cn.com;*.googletraveladservices-cn.com;googletagservices-cn.com;*.googletagservices-cn.com;googletagmanager-cn.com;*.googletagmanager-cn.com;googlesyndication-cn.com;*.googlesyndication-cn.com;*.safeframe.googlesyndication-cn.com;app-measurement-cn.com;*.app-measurement-cn.com;gvt1-cn.com;*.gvt1-cn.com;gvt2-cn.com;*.gvt2-cn.com;2mdn-cn.net;*.2mdn-cn.net;googleflights-cn.net;*.googleflights-cn.net;admob-cn.com;*.admob-cn.com;*.gstatic.com;*.metric.gstatic.com;*.gvt1.com;*.gcpcdn.gvt1.com;*.gvt2.com;*.gcp.gvt2.com;*.url.google.com;*.youtube-nocookie.com;*.ytimg.com;android.com;*.android.com;*.flash.android.com;g.cn;*.g.cn;g.co;*.g.co;goo.gl;www.goo.gl;google-analytics.com;*.google-analytics.com;google.com;googlecommerce.com;*.googlecommerce.com;ggpht.cn",
"in_src_mac": "d4:c1:c8:8f:ac:f0",
"in_dest_mac": "d4:c1:c8:98:c7:60",
"tcp_client_isn": 2664633009,
"tcp_server_isn": 2183931785,
"address_type": 4,
"client_ip": "196.190.248.0",
"server_ip": "142.250.185.34",
"client_port": 16986,
"server_port": 443,
"in_link_id": 0,
"out_link_id": 0,
"start_timestamp_ms": 1702610615910,
"end_timestamp_ms": 1702610615996,
"duration_ms": 86,
"sent_pkts": 0,
"sent_bytes": 0,
"received_pkts": 10,
"received_bytes": 7246,
"tcp_c2s_ip_fragments": 0,
"tcp_s2c_ip_fragments": 0,
"tcp_c2s_rtx_pkts": 0,
"tcp_c2s_rtx_bytes": 0,
"tcp_s2c_rtx_pkts": 0,
"tcp_s2c_rtx_bytes": 0,
"tcp_c2s_o3_pkts": 0,
"tcp_s2c_o3_pkts": 0,
"tcp_c2s_lost_bytes": 0,
"tcp_s2c_lost_bytes": 0,
"flags": 0,
"decoded_as": "SSL",
"decoded_path": "ETHERNET.IPv4.UDP.VXLAN.ETHERNET.IPv4.TCP",
"t_vsys_id": 1,
"vsys_id": 1,
"session_id": 11849422307955,
"tcp_handshake_latency_ms": 37,
"server_os_desc": "Android",
"device_tag": "{\"tags\":[{\"tag\":\"device_id\",\"value\":\"device_1\"}]}",
"sled_ip": "127.0.0.1",
"dup_traffic_flag": 0
}

View File

@@ -0,0 +1,802 @@
#include <stdio.h>
#include <sys/time.h>
#include <gtest/gtest.h>
#include <netinet/in.h>
#include "utable/utable.h"
#include "cjson/cJSON.h"
#include "uthash/utarray.h"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#define IPFIX_DEFUALT_VERSION 10
#define GEEDGE_NETWORKS_PEN_NUMBER 54450
#define IPFIX_DEFUALT_PEN_NUMBER GEEDGE_NETWORKS_PEN_NUMBER
#define IPFIX_NONE_PEN_NUMBER -1
#define IPFIX_TEMPLATE_SET_ID 2
#define IPFIX_BUFF_MAX_SIZE 2048
#define IPFIX_ELEMENT_MAX_LEN 32
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
enum ipfix_type
{
IPFIX_UNSIGNED8 = 0,
IPFIX_UNSIGNED16,
IPFIX_UNSIGNED32,
IPFIX_UNSIGNED64,
IPFIX_VARIABLE_STRING,
IPFIX_UNKNOWN
};
struct ipfix_element
{
enum ipfix_type type;
uint16_t element_id;
uint16_t value_length; // if type is variable_string, value_length is 0
int PEN_number;
char name[IPFIX_ELEMENT_MAX_LEN];
};
struct ipfix_template
{
uint16_t template_id;
char *name;
UT_array *elements_array; // utarray for current template elements
};
struct ipfix_worker_context
{
int source_id;
int sequence;
size_t template_blob_length;
char *template_blob;
};
struct ipfix_exporter_schema
{
uint16_t n_worker;
uint16_t version;
int domain_id;
int PEN_number;
UT_array *templates_array; // utarray for templates
struct ipfix_worker_context *worker_context_array; // utarray for worker_context
};
struct ipfix_message_head
{
uint16_t version;
uint16_t length;
int exporttime;
int ipfix_message_sequence;
int domain_id;
};
#define IPFIX_MESSAGE_HEAD_LEN sizeof(struct ipfix_message_head)
#define THREAD_MAX 4
#define TEMPLATE_MAX 13
struct gtest_ipfix_template_info
{
const char *template_name;
int template_id;
int n_elements;
};
struct gtest_ipfix_template_info gtest_ipfix_template_info[TEMPLATE_MAX] = {
{"BASE", 257, 87},
{"SSL", 258, 98},
{"HTTP", 259, 108},
{"MAIL", 260, 100},
{"DNS", 261, 106},
{"DTLS", 262, 97},
{"QUIC", 263, 90},
{"FTP", 264, 91},
{"SIP", 265, 102},
{"RTP", 266, 91},
{"SSH", 267, 98},
{"RDP", 268, 102},
{"Stratum", 269, 91}};
#define IPFIX_ELEMENT_HEAD_LEN sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) // element_id + element_length + PEN_number
const char *ipfix_schema_json_path = "./conf/ipfix_schema.json";
int g_domain_id = 1;
TEST(utable_ipfix_exporter_test, ipfix_schema_new_error_path)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new((const char *)"", g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema == NULL);
}
int test_template_blob_len_calculate(struct ipfix_exporter_schema *ipfix_schema)
{
int template_blob_length = 0;
template_blob_length += IPFIX_MESSAGE_HEAD_LEN; // message head
template_blob_length += sizeof(uint16_t) + sizeof(uint16_t); // flow set id + flow set length
for (unsigned int i = 0; i < TEMPLATE_MAX; i++)
{
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, i);
template_blob_length += sizeof(uint16_t) + sizeof(uint16_t) + utarray_len(template_item->elements_array) * (IPFIX_ELEMENT_HEAD_LEN); // sizeof(template_id) + sizeof(field_count) + n_elements * sizeof(element_id + element_length + PEN_number)
}
return template_blob_length;
}
TEST(utable_ipfix_exporter_test, ipfix_schema_new_check_schema)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
EXPECT_EQ(ipfix_schema->domain_id, 1);
EXPECT_EQ(ipfix_schema->n_worker, THREAD_MAX);
EXPECT_EQ(utarray_len(ipfix_schema->templates_array), TEMPLATE_MAX);
EXPECT_EQ(ipfix_schema->PEN_number, GEEDGE_NETWORKS_PEN_NUMBER);
EXPECT_EQ(ipfix_schema->version, IPFIX_DEFUALT_VERSION);
for (unsigned int i = 0; i < TEMPLATE_MAX; i++)
{
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, i);
ASSERT_TRUE(template_item != NULL);
EXPECT_EQ(template_item->template_id, gtest_ipfix_template_info[i].template_id);
EXPECT_EQ(gtest_ipfix_template_info[i].n_elements, utarray_len(template_item->elements_array));
EXPECT_STREQ(template_item->name, gtest_ipfix_template_info[i].template_name);
}
ASSERT_TRUE(ipfix_schema->worker_context_array != NULL);
for (int i = 0; i < THREAD_MAX; i++)
{
EXPECT_EQ(ipfix_schema->worker_context_array[i].source_id, (g_domain_id << 16) + i);
EXPECT_EQ(ipfix_schema->worker_context_array[i].sequence, 0);
ASSERT_TRUE(ipfix_schema->worker_context_array[i].template_blob != NULL);
EXPECT_EQ(ipfix_schema->worker_context_array[i].template_blob_length, test_template_blob_len_calculate(ipfix_schema));
}
utable_ipfix_exporter_schema_free(ipfix_schema);
}
TEST(utable_ipfix_exporter_test, ipfix_template_flow_get0_all_elements_check_blob)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
for (int i = 0; i < THREAD_MAX; i++)
{
size_t blob_len = 0;
const char *blob = utable_ipfix_template_flow_get0(ipfix_schema, i, &blob_len);
ASSERT_TRUE(blob != NULL);
EXPECT_EQ(blob_len, test_template_blob_len_calculate(ipfix_schema));
// check header
struct ipfix_message_head *header = (struct ipfix_message_head *)blob;
struct ipfix_message_head header_value = {};
header_value.version = htons(header->version);
header_value.length = htons(header->length);
header_value.domain_id = htonl(header->domain_id);
header_value.ipfix_message_sequence = htonl(header->ipfix_message_sequence);
EXPECT_EQ(header_value.version, IPFIX_DEFUALT_VERSION);
EXPECT_EQ(header_value.length, test_template_blob_len_calculate(ipfix_schema));
EXPECT_EQ(header_value.domain_id, (g_domain_id << 16) + i);
EXPECT_EQ(header_value.ipfix_message_sequence, 0);
size_t offset = 0;
offset += IPFIX_MESSAGE_HEAD_LEN;
uint16_t flow_set_id = ntohs(*(uint16_t *)(blob + offset));
EXPECT_EQ(flow_set_id, IPFIX_TEMPLATE_SET_ID); // template set id
offset += 2;
uint16_t flow_set_length = ntohs(*(uint16_t *)(blob + offset));
EXPECT_EQ(flow_set_length, blob_len - IPFIX_MESSAGE_HEAD_LEN); // template set length
offset += 2;
for (unsigned int j = 0; j < TEMPLATE_MAX; j++)
{
uint16_t cur_template_blob_len = sizeof(uint16_t) + sizeof(uint16_t) + gtest_ipfix_template_info[j].n_elements * IPFIX_ELEMENT_HEAD_LEN;
ASSERT_LE(offset + cur_template_blob_len, blob_len);
uint16_t template_id = ntohs(*(uint16_t *)(blob + offset));
EXPECT_EQ(template_id, gtest_ipfix_template_info[j].template_id); // template id
offset += 2;
uint16_t field_count = ntohs(*(uint16_t *)(blob + offset));
EXPECT_EQ(field_count, gtest_ipfix_template_info[j].n_elements); // field count
offset += 2;
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, j);
for (unsigned int p = 0; p < (unsigned int)gtest_ipfix_template_info[j].n_elements; p++)
{
struct ipfix_element *element = (struct ipfix_element *)utarray_eltptr(template_item->elements_array, p);
uint16_t PEN_number_flag = ntohs(*(uint16_t *)(blob + offset)) & 0x8000;
EXPECT_EQ(PEN_number_flag, 0x8000); // PEN number flag
uint16_t element_id = ntohs(*(uint16_t *)(blob + offset)) & 0x7fff;
EXPECT_EQ(element_id, element->element_id); // element id
offset += 2;
if (element->type == IPFIX_VARIABLE_STRING)
{
uint16_t element_length = ntohs(*(uint16_t *)(blob + offset));
EXPECT_EQ(element_length, 65535); // element length
}
else
{
uint16_t element_length = ntohs(*(uint16_t *)(blob + offset));
EXPECT_EQ(element_length, element->value_length); // element length
}
offset += 2;
uint32_t PEN_number = ntohl(*(uint32_t *)(blob + offset));
EXPECT_EQ(PEN_number, element->PEN_number); // PEN number
offset += 4;
}
}
}
utable_ipfix_exporter_schema_free(ipfix_schema);
}
TEST(utable_ipfix_exporter_test, ipfix_template_flow_get0_all_elements_check_sequence)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
for (int i = 0; i < THREAD_MAX; i++)
{
size_t blob_len = 0;
const char *blob = utable_ipfix_template_flow_get0(ipfix_schema, i, &blob_len);
ASSERT_TRUE(blob != NULL);
// check header
struct ipfix_message_head *header = (struct ipfix_message_head *)blob;
struct ipfix_message_head header_value = {};
header_value.version = htons(header->version);
header_value.length = htons(header->length);
header_value.domain_id = htonl(header->domain_id);
header_value.ipfix_message_sequence = htonl(header->ipfix_message_sequence);
EXPECT_EQ(header_value.version, IPFIX_DEFUALT_VERSION);
EXPECT_EQ(header_value.length, test_template_blob_len_calculate(ipfix_schema));
EXPECT_EQ(header_value.domain_id, (g_domain_id << 16) + i);
EXPECT_EQ(header_value.ipfix_message_sequence, 0);
memset(&header_value, 0, sizeof(struct ipfix_message_head));
header = NULL;
blob = NULL;
// check sequence, utable_ipfix_template_flow_get0 will not increase sequence
blob = utable_ipfix_template_flow_get0(ipfix_schema, i, &blob_len);
ASSERT_TRUE(blob != NULL);
// check header
header = (struct ipfix_message_head *)blob;
header_value.ipfix_message_sequence = htonl(header->ipfix_message_sequence);
EXPECT_EQ(header_value.ipfix_message_sequence, 0);
}
utable_ipfix_exporter_schema_free(ipfix_schema);
}
TEST(utable_ipfix_exporter_test, ipfix_template_get_not_found)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
int template_id = utable_ipfix_template_get(ipfix_schema, "test");
EXPECT_EQ(template_id, -1);
utable_ipfix_exporter_schema_free(ipfix_schema);
}
TEST(utable_ipfix_exporter_test, ipfix_template_get_check_template_id)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
int template_id = 0;
for (unsigned int i = 0; i < TEMPLATE_MAX; i++)
{
template_id = utable_ipfix_template_get(ipfix_schema, gtest_ipfix_template_info[i].template_name);
EXPECT_EQ(template_id, i);
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, i);
ASSERT_TRUE(template_item != NULL);
}
utable_ipfix_exporter_schema_free(ipfix_schema);
}
cJSON *test_ipifx_data_blob_to_cjson(struct ipfix_template *template_item, char *blob, size_t sz_blob)
{
cJSON *root = cJSON_CreateObject();
size_t offset = IPFIX_MESSAGE_HEAD_LEN + sizeof(uint16_t) + sizeof(uint16_t); // skip message head and flow set id and flow set length
size_t n_elements = utarray_len(template_item->elements_array);
for (size_t i = 0; i < n_elements; i++)
{
struct ipfix_element *p_element = (struct ipfix_element *)utarray_eltptr(template_item->elements_array, i);
switch (p_element->type)
{
case IPFIX_UNSIGNED8:
{
cJSON_AddNumberToObject(root, p_element->name, *(uint8_t *)(blob + offset));
offset += sizeof(uint8_t);
break;
}
case IPFIX_UNSIGNED16:
{
cJSON_AddNumberToObject(root, p_element->name, ntohs(*(uint16_t *)(blob + offset)));
offset += sizeof(uint16_t);
break;
}
case IPFIX_UNSIGNED32:
{
cJSON_AddNumberToObject(root, p_element->name, ntohl(*(uint32_t *)(blob + offset)));
offset += sizeof(uint32_t);
break;
}
case IPFIX_UNSIGNED64:
{
cJSON_AddNumberToObject(root, p_element->name, be64toh(*(uint64_t *)(blob + offset)));
offset += sizeof(uint64_t);
break;
}
case IPFIX_VARIABLE_STRING:
{
uint8_t sz_value = *(uint8_t *)(blob + offset);
offset += sizeof(uint8_t);
char *string_value = NULL;
if (sz_value == 0)
{
break;
}
else if (sz_value < 255)
{
string_value = (char *)malloc(sizeof(char) * (sz_value + 1));
memcpy(string_value, blob + offset, sz_value);
string_value[sz_value] = '\0';
offset += sz_value;
}
else // >= 255
{
uint16_t sz_long_string_value = ntohs(*(uint16_t *)(blob + offset));
offset += sizeof(uint16_t);
string_value = (char *)malloc(sizeof(char) * (sz_long_string_value + 1));
memcpy(string_value, blob + offset, sz_long_string_value);
string_value[sz_long_string_value] = '\0';
offset += sz_long_string_value;
}
cJSON_AddStringToObject(root, p_element->name, string_value);
free(string_value);
break;
}
default:
break;
}
}
return root;
}
// after include before, and the extra part is cJSON_Number, but valueint = 0;
void test_ipfix_compare_cjson(cJSON *before, cJSON *after)
{
// find before's element in after
cJSON *item = before->child;
while (item != NULL)
{
if (item->type == cJSON_Number)
{
cJSON *item_after = cJSON_GetObjectItem(after, item->string);
ASSERT_TRUE(item_after != NULL);
EXPECT_EQ(item->valueint, item_after->valueint);
}
if (item->type == cJSON_String)
{
cJSON *item_after = cJSON_GetObjectItem(after, item->string);
ASSERT_TRUE(item_after != NULL);
EXPECT_STREQ(item->valuestring, item_after->valuestring);
}
item = item->next;
}
// find after's element in before
item = after->child;
while (item != NULL)
{
if (item->type == cJSON_Number) // if after's element is cJSON_Number, before's element may be empty or cJSON_Number
{
cJSON *item_before = cJSON_GetObjectItem(before, item->string);
if (item_before == NULL) // if before's element is empty, after's element valueint must be 0
{
EXPECT_EQ(item->valueint, 0);
}
else
{
EXPECT_EQ(item->valueint, item_before->valueint); // if before's element is cJSON_Number, after's element valueint must be equal to before's element valueint
}
}
if (item->type == cJSON_String)
{
cJSON *item_before = cJSON_GetObjectItem(before, item->string);
ASSERT_TRUE(item_before != NULL);
EXPECT_STREQ(item->valuestring, item_before->valuestring);
}
item = item->next;
}
}
int test_empty_data_blob_len_calculate(struct ipfix_exporter_schema *ipfix_schema, int template_id)
{
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, (unsigned int)template_id);
int data_blob_length = 0;
data_blob_length += IPFIX_MESSAGE_HEAD_LEN; // message head
data_blob_length += sizeof(uint16_t) + sizeof(uint16_t); // flow set id + flow set length
for (unsigned int i = 0; i < utarray_len(template_item->elements_array); i++)
{
struct ipfix_element *element = (struct ipfix_element *)utarray_eltptr(template_item->elements_array, i);
if (element->type == IPFIX_VARIABLE_STRING)
{
data_blob_length += 1;
}
else
{
data_blob_length += element->value_length; // element length
}
}
return data_blob_length;
}
TEST(utable_ipfix_exporter_test, ipfix_data_flow_empty_utable_check_blob)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
for (unsigned int i = 0; i < TEMPLATE_MAX; i++)
{
cJSON *before = cJSON_CreateObject();
struct utable *table = utable_new();
int template_id = utable_ipfix_template_get(ipfix_schema, gtest_ipfix_template_info[i].template_name);
ASSERT_EQ(template_id, i);
for (unsigned int j = 0; j < THREAD_MAX; j++)
{
size_t blob_len = 0;
char *blob = NULL;
utable_ipfix_data_flow_exporter(table, ipfix_schema, template_id, j, &blob, &blob_len);
ASSERT_TRUE(blob != NULL);
struct ipfix_message_head *header = (struct ipfix_message_head *)blob;
struct ipfix_message_head header_value = {};
header_value.version = htons(header->version);
header_value.length = htons(header->length);
header_value.domain_id = htonl(header->domain_id);
header_value.ipfix_message_sequence = htonl(header->ipfix_message_sequence);
ASSERT_EQ(header_value.version, IPFIX_DEFUALT_VERSION);
ASSERT_EQ(header_value.length, blob_len);
ASSERT_EQ(header_value.length, test_empty_data_blob_len_calculate(ipfix_schema, i));
ASSERT_EQ(header_value.domain_id, (g_domain_id << 16) + j);
ASSERT_EQ(header_value.ipfix_message_sequence, i);
cJSON *after = test_ipifx_data_blob_to_cjson((struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, i), blob, blob_len);
test_ipfix_compare_cjson(before, after);
cJSON_Delete(after);
free(blob);
blob = NULL;
}
cJSON_Delete(before);
utable_free(table);
}
utable_ipfix_exporter_schema_free(ipfix_schema);
}
TEST(utable_ipfix_exporter_test, ipfix_data_flow_utable_value_type_integer)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
cJSON *before = cJSON_CreateObject();
struct utable *table = utable_new();
utable_add_integer(table, "session_id", 123456789);
cJSON_AddNumberToObject(before, "session_id", 123456789);
unsigned int template_id = utable_ipfix_template_get(ipfix_schema, "HTTP");
char *blob = NULL;
size_t sz_blob = 0;
utable_ipfix_data_flow_exporter(table, ipfix_schema, template_id, 0, &blob, &sz_blob);
ASSERT_EQ(sz_blob, test_empty_data_blob_len_calculate(ipfix_schema, template_id)); // integer value length has been calculated in test_empty_data_blob_len_calculate
cJSON *after = test_ipifx_data_blob_to_cjson((struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, template_id), blob, sz_blob);
test_ipfix_compare_cjson(before, after);
cJSON_Delete(before);
cJSON_Delete(after);
utable_free(table);
free(blob);
utable_ipfix_exporter_schema_free(ipfix_schema);
}
TEST(utable_ipfix_exporter_test, ipfix_data_flow_utable_value_type_cstring)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
cJSON *before = cJSON_CreateObject();
struct utable *table = utable_new();
utable_add_integer(table, "session_id", 123456789);
cJSON_AddNumberToObject(before, "session_id", 123456789);
utable_add_cstring(table, "http_url", "http://www.baidu.com", strlen("http://www.baidu.com"));
cJSON_AddStringToObject(before, "http_url", "http://www.baidu.com");
unsigned int template_id = utable_ipfix_template_get(ipfix_schema, "HTTP");
char *blob = NULL;
size_t sz_blob = 0;
utable_ipfix_data_flow_exporter(table, ipfix_schema, template_id, 0, &blob, &sz_blob);
ASSERT_EQ(sz_blob, test_empty_data_blob_len_calculate(ipfix_schema, template_id) + strlen("http://www.baidu.com")); // variable_string length need to be calculated
cJSON *after = test_ipifx_data_blob_to_cjson((struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, template_id), blob, sz_blob);
test_ipfix_compare_cjson(before, after);
cJSON_Delete(before);
cJSON_Delete(after);
utable_free(table);
free(blob);
utable_ipfix_exporter_schema_free(ipfix_schema);
}
char http_url_test[] = "storeapi.app-vtion.com/storeApi/systemApp/checkAppStatus.json?store=com.tcl.appmarket2&mac=3C:59:1E:DC:F8:F4&accessKeyId=1472540256617";
TEST(utable_ipfix_exporter_test, ipfix_data_flow_variable_string_6000) // 6000 bytes > 2 * IPFIX_BUFF_MAX_SIZE, test variable_string length > 255 and realloc
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
char *http_url = NULL;
struct utable *table = utable_new();
cJSON *before = cJSON_CreateObject();
utable_add_cstring(table, "decoded_as", "HTTP", strlen("HTTP"));
cJSON_AddStringToObject(before, "decoded_as", "HTTP");
http_url = (char *)malloc(sizeof(char) * 7000);
int offset = 0;
while (offset < 6000)
{
memcpy(http_url + offset, http_url_test, sizeof(http_url_test));
offset += sizeof(http_url_test);
}
utable_add_cstring(table, "http_url", http_url, offset);
cJSON_AddStringToObject(before, "http_url", http_url);
unsigned int template_id = utable_ipfix_template_get(ipfix_schema, "HTTP");
char *blob = NULL;
size_t sz_blob = 0;
utable_ipfix_data_flow_exporter(table, ipfix_schema, template_id, 0, &blob, &sz_blob);
ASSERT_EQ(sz_blob, test_empty_data_blob_len_calculate(ipfix_schema, template_id) + offset + strlen("HTTP") + 2); // variable_string length need to be calculated, if length > 255, length + 2
cJSON *after = test_ipifx_data_blob_to_cjson((struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, template_id), blob, sz_blob);
test_ipfix_compare_cjson(before, after);
cJSON_Delete(before);
cJSON_Delete(after);
utable_free(table);
free(blob);
free(http_url);
utable_ipfix_exporter_schema_free(ipfix_schema);
}
TEST(utable_ipfix_exporter_test, ipfix_data_flow_utable_value_type_integer_array)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
cJSON *before = cJSON_CreateObject();
struct utable *table = utable_new();
utable_add_integer(table, "session_id", 123456789);
cJSON_AddNumberToObject(before, "session_id", 123456789);
int64_t monitor_rule_list[8] = {1, 2, 3, 4, 5, 6, 7, 8};
utable_add_integer_array(table, "monitor_rule_list", monitor_rule_list, 8);
cJSON_AddStringToObject(before, "monitor_rule_list", "[1,2,3,4,5,6,7,8]");
unsigned int template_id = utable_ipfix_template_get(ipfix_schema, "HTTP");
char *blob = NULL;
size_t sz_blob = 0;
utable_ipfix_data_flow_exporter(table, ipfix_schema, template_id, 0, &blob, &sz_blob);
ASSERT_EQ(sz_blob, test_empty_data_blob_len_calculate(ipfix_schema, template_id) + strlen("[1,2,3,4,5,6,7,8]")); // variable_string length need to be calculated
cJSON *after = test_ipifx_data_blob_to_cjson((struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, template_id), blob, sz_blob);
test_ipfix_compare_cjson(before, after);
cJSON_Delete(before);
cJSON_Delete(after);
utable_free(table);
free(blob);
utable_ipfix_exporter_schema_free(ipfix_schema);
}
extern "C" int load_file_to_memory(const char *file_name, unsigned char **pp_out, size_t *out_sz);
cJSON *ipfix_init_utable_from_log_json(struct utable *table, const char *test_json_path, size_t *sz_blob)
{
size_t json_size = 0;
unsigned char *json_str = NULL;
load_file_to_memory(test_json_path, &json_str, &json_size);
if (json_str == NULL || json_size == 0)
{
return NULL;
}
cJSON *root = NULL;
root = cJSON_Parse((const char *)json_str);
cJSON *item = root->child;
while (item != NULL)
{
switch (item->type)
{
case cJSON_Number:
{
utable_add_integer(table, item->string, item->valueint);
break;
}
case cJSON_String:
{
int sz_value = strlen(item->valuestring);
utable_add_cstring(table, item->string, item->valuestring, sz_value);
if (sz_value < 255)
{
*sz_blob += strlen(item->valuestring);
}
else
{
*sz_blob += (strlen(item->valuestring) + 2);
}
break;
}
case cJSON_Array:
{
cJSON *array_item = cJSON_GetArrayItem(item, 0);
size_t array_sz = cJSON_GetArraySize(item);
if (array_sz > 0)
{
if (array_item->type == cJSON_Number)
{
int *array = (int *)malloc(sizeof(int) * array_sz);
int64_t *long_array = (int64_t *)malloc(sizeof(int64_t) * array_sz);
for (size_t i = 0; i < array_sz; i++)
{
array_item = cJSON_GetArrayItem(item, i);
if (array_item->type == cJSON_Number)
{
array[i] = array_item->valueint;
long_array[i] = array_item->valueint;
}
}
utable_add_integer_array(table, item->string, long_array, array_sz);
cJSON *json_array = cJSON_CreateIntArray((const int *)array, array_sz);
cJSON_Delete(item->child);
item->type = cJSON_String;
item->child = NULL;
item->valuestring = cJSON_PrintUnformatted(json_array);
cJSON_Delete(json_array);
free(array);
free(long_array);
}
if (array_item->type == cJSON_String)
{
char **array = (char **)malloc(sizeof(char *) * array_sz);
size_t *sz_value = (size_t *)malloc(sizeof(size_t) * array_sz);
for (size_t i = 0; i < array_sz; i++)
{
array_item = cJSON_GetArrayItem(item, i);
if (array_item->type == cJSON_String)
{
sz_value[i] = strlen(array_item->valuestring);
array[i] = strdup(array_item->valuestring);
}
}
utable_add_cstring_array(table, item->string, (const char **)array, sz_value, array_sz);
cJSON *json_array = cJSON_CreateStringArray((const char **)array, array_sz);
item->valuestring = cJSON_PrintUnformatted(json_array);
item->type = cJSON_String;
cJSON_Delete(item->child);
item->child = NULL;
cJSON_Delete(json_array);
for (size_t i = 0; i < array_sz; i++)
{
free(array[i]);
}
free(array);
}
int sz_value = strlen(item->valuestring);
if (sz_value < 255)
{
*sz_blob += sz_value;
}
else
{
*sz_blob += (sz_value + 2);
}
}
break;
}
default:
break;
}
item = item->next;
}
free(json_str);
return root;
}
const char *ssl_test_json_path = "./conf/ipfix_ssl_test.json";
TEST(utable_ipfix_exporter_test, ipfix_data_flow_ssl_log_test)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
struct utable *table = utable_new();
unsigned int template_id = utable_ipfix_template_get(ipfix_schema, "SSL");
size_t predict_sz_blob = test_empty_data_blob_len_calculate(ipfix_schema, template_id);
cJSON *before_json_root = ipfix_init_utable_from_log_json(table, ssl_test_json_path, &predict_sz_blob);
char *blob = NULL;
size_t sz_blob = 0;
utable_ipfix_data_flow_exporter(table, ipfix_schema, template_id, 0, &blob, &sz_blob);
ASSERT_EQ(sz_blob, predict_sz_blob);
cJSON *after_json_root = test_ipifx_data_blob_to_cjson((struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, template_id), blob, sz_blob);
char *before_json_str = cJSON_Print(before_json_root);
char *after_json_str = cJSON_Print(after_json_root);
printf("before_json: %s\n", before_json_str);
printf("after_json: %s\n", after_json_str);
free(before_json_str);
free(after_json_str);
test_ipfix_compare_cjson(before_json_root, after_json_root);
cJSON_Delete(before_json_root);
cJSON_Delete(after_json_root);
free(blob);
utable_free(table);
utable_ipfix_exporter_schema_free(ipfix_schema);
}
const char *http_test_json_path = "./conf/ipfix_http_test.json";
TEST(utable_ipfix_exporter_test, ipfix_data_flow_http_log_test)
{
struct ipfix_exporter_schema *ipfix_schema = utable_ipfix_exporter_schema_new(ipfix_schema_json_path, g_domain_id, THREAD_MAX);
ASSERT_TRUE(ipfix_schema != NULL);
struct utable *table = utable_new();
unsigned int template_id = utable_ipfix_template_get(ipfix_schema, "HTTP");
size_t predict_sz_blob = test_empty_data_blob_len_calculate(ipfix_schema, template_id);
cJSON *before_json_root = ipfix_init_utable_from_log_json(table, http_test_json_path, &predict_sz_blob);
char *blob = NULL;
size_t sz_blob = 0;
utable_ipfix_data_flow_exporter(table, ipfix_schema, template_id, 0, &blob, &sz_blob);
ASSERT_EQ(sz_blob, predict_sz_blob);
cJSON *after_json_root = test_ipifx_data_blob_to_cjson((struct ipfix_template *)utarray_eltptr(ipfix_schema->templates_array, template_id), blob, sz_blob);
test_ipfix_compare_cjson(before_json_root, after_json_root);
cJSON_Delete(before_json_root);
cJSON_Delete(after_json_root);
free(blob);
utable_free(table);
utable_ipfix_exporter_schema_free(ipfix_schema);
}
int main(int argc, char *argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

773
deps/utable/test/unit_test_utable.cpp vendored Normal file
View File

@@ -0,0 +1,773 @@
#include <gtest/gtest.h>
#include "base64/b64.h"
#include "utable/utable.h"
#include "cjson/cJSON.h"
#include "mpack/mpack.h"
void test_utable_assert_arr(const struct utable *table, const char *key, int64_t expected_arr[], size_t n_expected_arr)
{
int64_t *arr = NULL;
size_t n_arr = 0;
EXPECT_EQ(utable_get_value_type(table, key), utable_value_type_integer_array);
utable_get0_integer_value_array(table, key, &arr, &n_arr);
EXPECT_EQ(n_arr, n_expected_arr);
for (size_t i = 0; i < n_arr; i++) {
EXPECT_EQ(expected_arr[i], arr[i]);
}
}
void test_utable_assert_str(const struct utable *table, const char *key, const char *expected_str)
{
char *str = NULL;
size_t str_len = 0;
EXPECT_EQ(utable_get_value_type(table, key), utable_value_type_cstring);
utable_get0_cstring_value(table, key, &str, &str_len);
EXPECT_EQ(str_len, strlen(expected_str));
EXPECT_EQ(strncmp(str, expected_str, str_len), 0);
}
void test_utable_assert_str_array(const struct utable *table, const char *key, const char **expected_str_array, size_t n_expected_str)
{
char **str_array = NULL;
size_t *str_len = NULL;
size_t n_str = 0;
EXPECT_EQ(utable_get_value_type(table, key), utable_value_type_cstring_array);
utable_get0_cstring_value_array(table, key, &str_array, &str_len, &n_str);
EXPECT_EQ(n_str, n_expected_str);
for (size_t i = 0; i < n_str; i++) {
EXPECT_EQ(str_len[i], strlen(expected_str_array[i]));
EXPECT_EQ(strncmp(str_array[i], expected_str_array[i], str_len[i]), 0);
}
}
void test_utable_assert_blob(const struct utable *table, const char *key, const char *expected_blob, size_t expected_blob_len)
{
char *blob = NULL;
size_t blob_len = 0;
EXPECT_EQ(utable_get_value_type(table, key), utable_value_type_blob);
utable_get0_blob_value(table, key, &blob, &blob_len);
EXPECT_EQ(blob_len, expected_blob_len);
EXPECT_EQ(memcmp(blob, expected_blob, blob_len), 0);
}
TEST(utable_test, new_del_when_empty)
{
struct utable *table = utable_new();
ASSERT_TRUE(table != NULL);
utable_free(table);
}
TEST(utable_test, add_1_int_and_query)
{
struct utable *table = utable_new();
int64_t int_array[] = { 1 };
utable_add_integer_array(table, "key", int_array, 1);
test_utable_assert_arr(table, "key", int_array, 1);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key")+sizeof(int64_t);
B.n_integer_array=1;
B.n_integer_array_size=sizeof(int64_t);
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
}
TEST(utable_test, add_2_int_and_query)
{
struct utable *table = utable_new();
int64_t int_array[] = { 1, 2 };
utable_add_integer_array(table, "key", int_array, 2);
test_utable_assert_arr(table, "key", int_array, 2);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key")+sizeof(int64_t)*2;
B.n_integer_array=1;
B.n_integer_array_size=sizeof(int64_t)*2;
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
}
TEST(utable_test, add_string_and_query)
{
struct utable *table = utable_new();
const char *str = "hello world";
utable_add_cstring(table, "key", str, strlen(str));
test_utable_assert_str(table, "key", str);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key")+strlen(str);
B.n_cstring=1;
B.n_cstring_size=strlen(str);
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
}
TEST(utable_test, add_string_array_and_query)
{
struct utable *table = utable_new();
const char *str[] = {"hello world", "foo bar"};
size_t n_str=sizeof(str)/sizeof(str[0]);
size_t str_sz[] = {strlen(str[0]), strlen(str[1])};
utable_add_cstring_array(table, "key", str, str_sz, n_str);
test_utable_assert_str_array(table, "key", str, n_str);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key")+str_sz[0]+str_sz[1];
B.n_cstring_array=1;
B.n_cstring_array_size=str_sz[0]+str_sz[1];
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
}
TEST(utable_test, add_blob_and_query)
{
struct utable *table = utable_new();
const char *blob = "hello world";
size_t blob_len = strlen(blob);
utable_add_blob(table, "key", blob, blob_len);
test_utable_assert_blob(table, "key", blob, blob_len);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key")+blob_len;
B.n_blob=1;
B.n_blob_size=blob_len;
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
}
void test_utable_check_if_value_in_json(cJSON *tag_obj, const char *key, const std::string &value)
{
cJSON *tag_val = cJSON_GetObjectItem(tag_obj, key);
EXPECT_NE(tag_val, nullptr);
EXPECT_STREQ(tag_val->valuestring, value.c_str());
}
void test_utable_check_if_value_in_json(cJSON *tag_obj, const char *key, const char *value[], size_t n_value)
{
cJSON *tag_val = cJSON_GetObjectItem(tag_obj, key);
EXPECT_NE(tag_val, nullptr);
EXPECT_EQ(tag_val->type, cJSON_Array);
int arr_size = cJSON_GetArraySize(tag_val);
EXPECT_EQ(arr_size, n_value);
for (int i = 0; i < arr_size; i++) {
cJSON *arr_item = cJSON_GetArrayItem(tag_val, i);
EXPECT_STREQ(arr_item->valuestring, value[i]);
}
}
void test_utable_check_if_value_in_json(cJSON *tag_obj, const char *key, int64_t value[], size_t n_value)
{
cJSON *tag_val = cJSON_GetObjectItem(tag_obj, key);
EXPECT_NE(tag_val, nullptr);
EXPECT_EQ(tag_val->type, cJSON_Array);
int arr_size = cJSON_GetArraySize(tag_val);
EXPECT_EQ(arr_size, n_value);
for (int i = 0; i < arr_size; i++) {
cJSON *arr_item = cJSON_GetArrayItem(tag_val, i);
EXPECT_EQ(arr_item->valueint, value[i]);
}
}
void test_utable_check_if_value_in_json(cJSON *tag_obj, const char *key, const char *value, size_t blob_len)
{
cJSON *tag_val = cJSON_GetObjectItem(tag_obj, key);
EXPECT_NE(tag_val, nullptr);
// the tag_val is encoded in base64
size_t dec_size = 0;
unsigned char *dec = b64_decode_ex(tag_val->valuestring, strlen(tag_val->valuestring), &dec_size);
EXPECT_EQ(dec_size, blob_len);
EXPECT_EQ(memcmp(dec, value, blob_len), 0);
free(dec);
}
TEST(utable_test, export_json)
{
struct utable *table = utable_new();
int64_t int_array[] = { 1, 2 };
const char *str = "hello world";
const char *blob = "bin hello world";
const char *str_array[] = {"hello world", "foo bar"};
size_t n_str=sizeof(str_array)/sizeof(str_array[0]);
size_t str_sz[] = {strlen(str_array[0]), strlen(str_array[1])};
utable_add_integer_array(table, "key1", int_array, 2);
utable_add_cstring(table, "key2", str, strlen(str));
utable_add_blob(table, "key3", blob, strlen(blob));
utable_add_cstring_array(table, "key4", str_array, str_sz, n_str);
char *json = NULL;
size_t json_len = 0;
EXPECT_EQ(utable_json_export(table, &json, &json_len), 0);
EXPECT_NE(nullptr, json);
cJSON *root = cJSON_Parse(json);
test_utable_check_if_value_in_json(root, "key1", int_array, 2);
std::string expected_str(str);
test_utable_check_if_value_in_json(root, "key2", expected_str);
test_utable_check_if_value_in_json(root, "key3", blob, strlen(blob));
test_utable_check_if_value_in_json(root, "key4", str_array, n_str);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=4;
B.n_item_size=strlen("key1")+sizeof(int64_t)*2+strlen("key2")+strlen(str)+strlen("key3")+strlen(blob)+strlen("key4")+str_sz[0]+str_sz[1];
B.n_blob=1;
B.n_blob_size=strlen(blob);
B.n_cstring=1;
B.n_cstring_size=strlen(str);
B.n_integer_array=1;
B.n_integer_array_size=sizeof(int64_t)*2;
B.n_integer=0;
B.n_integer_size=0;
B.n_cstring_array=1;
B.n_cstring_array_size=str_sz[0]+str_sz[1];
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
free(json);
cJSON_Delete(root);
}
TEST(utable_test, export_invalid_unicode_string_to_json)
{
struct utable *table = utable_new();
const char *str = "googleads.g.doublec\x86rck.net";
utable_add_cstring(table, "key2", str, strlen(str));
char *json = NULL;
size_t json_len = 0;
EXPECT_EQ(utable_json_export(table, &json, &json_len), 0);
EXPECT_NE(nullptr, json);
cJSON *root = cJSON_Parse(json);
std::string expected_str(str);
test_utable_check_if_value_in_json(root, "key2", expected_str);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key2")+strlen(str);
B.n_cstring=1;
B.n_cstring_size=strlen(str);
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
free(json);
cJSON_Delete(root);
}
char *test_get_cstring_from_mpack_node(mpack_node_t node)
{
size_t len = mpack_node_strlen(node);
char *str = (char *)malloc(len + 1);
memcpy(str, mpack_node_str(node), len);
str[len] = '\0';
return str;
}
void test_utable_check_if_value_in_mpack(mpack_node_t item, const char *key, const std::string &value)
{
mpack_node_t key_node = mpack_node_map_cstr(item, "key");
mpack_node_t type_node = mpack_node_map_cstr(item, "type");
mpack_node_t value_node = mpack_node_map_cstr(item, "value");
char *key_tmp = test_get_cstring_from_mpack_node(key_node);
char *type_tmp = test_get_cstring_from_mpack_node(type_node);
char *value_tmp = test_get_cstring_from_mpack_node(value_node);
EXPECT_STREQ(key_tmp, key);
EXPECT_STREQ(type_tmp, "c_str");
EXPECT_STREQ(value_tmp, value.c_str());
free(key_tmp);
free(type_tmp);
free(value_tmp);
}
void test_utable_check_if_value_in_mpack(mpack_node_t item, const char *key, int64_t value[], size_t n_value)
{
mpack_node_t key_node = mpack_node_map_cstr(item, "key");
mpack_node_t type_node = mpack_node_map_cstr(item, "type");
mpack_node_t value_node = mpack_node_map_cstr(item, "value");
char *key_tmp = test_get_cstring_from_mpack_node(key_node);
char *type_tmp = test_get_cstring_from_mpack_node(type_node);
EXPECT_STREQ(key_tmp, key);
EXPECT_STREQ(type_tmp, "i_arr");
EXPECT_EQ(mpack_node_array_length(value_node), n_value);
for (size_t i = 0; i < n_value; i++) {
mpack_node_t tmp_v_node = mpack_node_array_at(value_node, i);
EXPECT_EQ(mpack_node_i64(tmp_v_node), value[i]);
}
free(key_tmp);
free(type_tmp);
}
void test_utable_check_if_value_in_mpack(mpack_node_t item, const char *key, const char *value, size_t blob_len)
{
mpack_node_t key_node = mpack_node_map_cstr(item, "key");
mpack_node_t type_node = mpack_node_map_cstr(item, "type");
mpack_node_t value_node = mpack_node_map_cstr(item, "value");
char *key_tmp = test_get_cstring_from_mpack_node(key_node);
char *type_tmp = test_get_cstring_from_mpack_node(type_node);
EXPECT_STREQ(key_tmp, key);
EXPECT_STREQ(type_tmp, "blob");
EXPECT_EQ(mpack_node_bin_size(value_node), blob_len);
EXPECT_EQ(memcmp(mpack_node_bin_data(value_node), value, blob_len), 0);
free(key_tmp);
free(type_tmp);
}
TEST(utable_test, DISABLED_export_mpack)
{
struct utable *table = utable_new();
int64_t int_array[] = { 1, 2 };
const char *str = "hello world";
const char *blob = "bin hello world";
utable_add_integer_array(table, "key1", int_array, 2);
utable_add_cstring(table, "key2", str, strlen(str));
utable_add_blob(table, "key3", blob, strlen(blob));
char *mpack = NULL;
size_t mpack_len = 0;
EXPECT_EQ(utable_msgpack_export(table, &mpack, &mpack_len), 0);
mpack_tree_t tree;
mpack_tree_init_data(&tree, mpack, mpack_len);
mpack_tree_parse(&tree);
mpack_node_t root = mpack_tree_root(&tree);
size_t n_item = mpack_node_array_length(root);
EXPECT_EQ(n_item, 3);
test_utable_check_if_value_in_mpack(mpack_node_array_at(root, 0), "key1", int_array, 2);
std::string expected_str(str);
test_utable_check_if_value_in_mpack(mpack_node_array_at(root, 1), "key2", expected_str);
test_utable_check_if_value_in_mpack(mpack_node_array_at(root, 2), "key3", blob, strlen(blob));
utable_free(table);
free(mpack);
mpack_tree_destroy(&tree);
}
TEST(utable_test, add_blob_then_del)
{
struct utable *table = utable_new();
const char *blob = "hello world";
size_t blob_len = strlen(blob);
utable_add_blob(table, "key", blob, blob_len);
test_utable_assert_blob(table, "key", blob, blob_len);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key")+strlen(blob);
B.n_blob=1;
B.n_blob_size=strlen(blob);
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
char *value;
size_t value_len;
utable_delete_item(table, "key");
EXPECT_EQ(utable_get0_blob_value(table, "key", &value, &value_len), -1);
EXPECT_EQ(utable_get0_cstring_value(table, "key", &value, &value_len), -1);
int64_t *value_array;
size_t n_value;
EXPECT_EQ(utable_get0_integer_value_array(table, "key", &value_array, &n_value), -1);
EXPECT_EQ(utable_get_value_type(table, "key"), utable_value_type_undefined);
struct utable_stat C={};
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &C, sizeof(struct utable_stat)), 0);
utable_free(table);
}
TEST(utable_test, add_string_array_then_del)
{
struct utable *table = utable_new();
const char *str[] = {"hello world", "foo bar"};
size_t n_str=sizeof(str)/sizeof(str[0]);
size_t str_sz[] = {strlen(str[0]), strlen(str[1])};
utable_add_cstring_array(table, "key", str, str_sz, n_str);
test_utable_assert_str_array(table, "key", str, n_str);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key")+str_sz[0]+str_sz[1];
B.n_cstring_array=1;
B.n_cstring_array_size=str_sz[0]+str_sz[1];
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
char *value;
size_t value_len;
utable_delete_item(table, "key");
EXPECT_EQ(utable_get0_blob_value(table, "key", &value, &value_len), -1);
EXPECT_EQ(utable_get0_cstring_value(table, "key", &value, &value_len), -1);
int64_t *value_array;
size_t n_value;
EXPECT_EQ(utable_get0_integer_value_array(table, "key", &value_array, &n_value), -1);
char **str_array = NULL;
size_t *str_len = NULL;
n_str = 0;
EXPECT_EQ(utable_get0_cstring_value_array(table, "key", &str_array, &str_len, &n_str), -1);
EXPECT_EQ(utable_get_value_type(table, "key"), utable_value_type_undefined);
struct utable_stat C={};
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &C, sizeof(struct utable_stat)), 0);
utable_free(table);
}
TEST(utable_test, query_on_wrong_key)
{
struct utable *table = utable_new();
char *value;
size_t value_len;
EXPECT_EQ(utable_get0_blob_value(table, "key", &value, &value_len), -1);
EXPECT_EQ(utable_get0_cstring_value(table, "key", &value, &value_len), -1);
int64_t *value_array;
size_t n_value;
EXPECT_EQ(utable_get0_integer_value_array(table, "key", &value_array, &n_value), -1);
EXPECT_EQ(utable_get_value_type(table, "key"), utable_value_type_undefined);
utable_free(table);
}
TEST(utable_test, add_on_dup_key)
{
struct utable *table = utable_new();
int64_t int_array[] = { 1, 2 };
const char *str = "hello world";
utable_add_integer_array(table, "key", int_array, 2);
utable_add_cstring(table, "key", str, strlen(str));
test_utable_assert_arr(table, "key", int_array, 2);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key")+sizeof(int64_t)*2;
B.n_integer_array=1;
B.n_integer_array_size=sizeof(int64_t)*2;
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
}
TEST(utable_test, iter_and_query)
{
struct utable *table = utable_new();
int64_t int_array[] = { 1, 2 };
const char *str = "hello world";
utable_add_integer_array(table, "key1", int_array, 2);
utable_add_cstring(table, "key2", str, strlen(str));
utable_add_integer_array(table, "key3", int_array, 2);
EXPECT_STREQ(utable_next_key(table), "key1");
EXPECT_STREQ(utable_next_key(table), "key2");
EXPECT_STREQ(utable_next_key(table), "key3");
EXPECT_EQ(utable_next_key(table), nullptr);
utable_reset_iter(table);
EXPECT_STREQ(utable_next_key(table), "key1");
EXPECT_STREQ(utable_next_key(table), "key2");
EXPECT_STREQ(utable_next_key(table), "key3");
EXPECT_EQ(utable_next_key(table), nullptr);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=3;
B.n_item_size=strlen("key1")+sizeof(int64_t)*2+strlen("key2")+strlen(str)+strlen("key3")+sizeof(int64_t)*2;
B.n_blob=0;
B.n_blob_size=0;
B.n_cstring=1;
B.n_cstring_size=strlen(str);
B.n_integer_array=2;
B.n_integer_array_size=sizeof(int64_t)*4;
B.n_integer=0;
B.n_integer_size=0;
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
}
TEST(utable_test, dup_empty)
{
struct utable *table = utable_new();
struct utable *dup_table = utable_duplicate(table);
EXPECT_EQ(utable_next_key(dup_table), nullptr);
utable_free(table);
utable_free(dup_table);
}
TEST(utable_test, dup_and_query)
{
struct utable *table = utable_new();
const char *str = "hello world";
utable_add_cstring(table, "key1", str, strlen(str));
int64_t int_array[] = { 1, 2 };
utable_add_integer_array(table, "key2", int_array, 2);
const char *blob = "bin hello world";
utable_add_blob(table, "key3", blob, strlen(blob));
struct utable *dup_table = utable_duplicate(table);
test_utable_assert_arr(dup_table, "key2", int_array, 2);
test_utable_assert_str(dup_table, "key1", str);
test_utable_assert_blob(dup_table, "key3", blob, strlen(blob));
struct utable_stat A={}, B={};
utable_stat(table, &A);
utable_stat(dup_table, &B);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
utable_free(dup_table);
}
TEST(utable_test, replace_8k_cstring)
{
struct utable *table = utable_new();
const char str1[] = "hello world";
char str2[8192];
memset(str2, 'B', sizeof(str2));
str2[8191] = '\0'; // make sure it's null-terminated
utable_add_cstring(table, "key1", str1, strlen(str1));
test_utable_assert_str(table, "key1", str1);
utable_delete_item(table, "key1");
utable_add_cstring(table, "key1", str2, strlen(str2));
test_utable_assert_str(table, "key1", str2);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key1")+strlen(str2);
B.n_cstring=1;
B.n_cstring_size=strlen(str2);
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
}
TEST(utable_test, replace_cstring_many_times)
{
struct utable *table = utable_new();
char str[4000] ;
memset(str, 'B', sizeof(str));
str[3999] = '\0'; // make sure it's null-terminated
utable_add_cstring(table, "key1", str, strlen(str));
test_utable_assert_str(table, "key1", str);
for(int i=0; i<100000; i++)
{
utable_delete_item(table, "key1");
utable_add_cstring(table, "key1", str, strlen(str));
}
test_utable_assert_str(table, "key1", str);
struct utable_stat A={};
struct utable_stat B={};
B.n_item=1;
B.n_item_size=strlen("key1")+strlen(str);
B.n_cstring=1;
B.n_cstring_size=strlen(str);
utable_stat(table, &A);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
printf("replace_cstring_many_times done\n");
utable_free(table);
}
TEST(utable_test, merge_empty_to_empty)
{
struct utable *table = utable_new();
struct utable *table2 = utable_new();
utable_merge(table, table2);
EXPECT_EQ(utable_next_key(table), nullptr);
struct utable_stat A={}, B={};
utable_stat(table, &A);
utable_stat(table2, &B);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
utable_free(table2);
}
TEST(utable_test, merge_all_new)
{
struct utable *table = utable_new();
struct utable *table2 = utable_new();
const char *str = "hello world";
utable_add_cstring(table, "key1", str, strlen(str));
int64_t int_array[] = { 1, 2 };
utable_add_integer_array(table, "key2", int_array, 2);
const char *blob = "bin hello world";
utable_add_blob(table2, "key on tbl2", blob, strlen(blob));
utable_merge(table2, table);
test_utable_assert_arr(table2, "key2", int_array, 2);
test_utable_assert_str(table2, "key1", str);
test_utable_assert_blob(table2, "key on tbl2", blob, strlen(blob));
utable_merge(table, table2);
struct utable_stat A={}, B={};
utable_stat(table, &A);
utable_stat(table2, &B);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
utable_free(table2);
}
TEST(utable_test, merge_all_skip)
{
struct utable *table = utable_new();
const char *str = "hello world";
utable_add_cstring(table, "key1", str, strlen(str));
int64_t int_array[] = { 1, 2 };
utable_add_integer_array(table, "key2", int_array, 2);
struct utable *table2 = utable_duplicate(table);
utable_merge(table2, table);
EXPECT_STREQ(utable_next_key(table2), "key1");
EXPECT_STREQ(utable_next_key(table2), "key2");
EXPECT_EQ(utable_next_key(table2), nullptr);
utable_free(table);
utable_free(table2);
}
TEST(utable_test, merge_some_skip_some_new)
{
struct utable *table = utable_new();
struct utable *table2 = utable_new();
const char *str = "val on tbl1";
utable_add_cstring(table, "key_share", str, strlen(str));
const char *str2 = "val on tbl2";
utable_add_cstring(table2, "key_share", str2, strlen(str2));
const char *str3 = "val on tbl1";
utable_add_cstring(table, "key1", str3, strlen(str3));
utable_merge(table2, table);
test_utable_assert_str(table2, "key_share", str2);
test_utable_assert_str(table2, "key1", str3);
utable_free(table);
utable_free(table2);
}
TEST(utable_test, serialize_and_deserialize)
{
struct utable *table = utable_new();
int64_t int_array[] = { 1, 2 };
const char *str = "hello world";
const char *blob = "bin hello world";
utable_add_integer_array(table, "key1", int_array, 2);
utable_add_cstring(table, "key2", str, strlen(str));
utable_add_blob(table, "key3", blob, strlen(blob));
char *blob_out;
size_t blob_size;
utable_serialize(table, &blob_out, &blob_size);
struct utable *table2 = utable_deserialize(blob_out, blob_size);
test_utable_assert_arr(table2, "key1", int_array, 2);
test_utable_assert_str(table2, "key2", str);
test_utable_assert_blob(table2, "key3", blob, strlen(blob));
struct utable_stat A={}, B={};
utable_stat(table, &A);
utable_stat(table2, &B);
EXPECT_EQ(memcmp(&A, &B, sizeof(struct utable_stat)), 0);
utable_free(table);
free(blob_out);
utable_free(table2);
}
TEST(utable_test, serialize_empty_and_deserialize)
{
struct utable *table = utable_new();
char *blob_out;
size_t blob_size;
utable_serialize(table, &blob_out, &blob_size);
struct utable *table2 = utable_deserialize(blob_out, blob_size);
ASSERT_TRUE(table2 != NULL);
ASSERT_EQ(utable_next_key(table2), nullptr);
utable_free(table);
free(blob_out);
utable_free(table2);
}
int main(int argc, char *argv[])
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

730
deps/utable/utable.c vendored Normal file
View File

@@ -0,0 +1,730 @@
#include "utable.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mpack/mpack.h"
#define YYJSON_HAS_STDINT_H 1
#define YYJSON_DISABLE_READER 1
#define YYJSON_HAS_STDBOOL_H 1
#include "yyjson/yyjson.h"
#include "uthash/uthash.h"
#include "base64/b64.h"
#include "nmx_pool/mempool.h"
#ifdef DEBUG_MODE
#define DEBUG_PRINT(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINT(...)
#endif
#define ALLOC(number, type) ((type *)calloc(sizeof(type), number))
#define FREE(p) {free(p);p=NULL;}
#define MEMPOOL_TYPE mem_pool_t
#define MEMPOOL_CREATE(pool_size) create_mem_pool(pool_size)
#define MEMPOOL_DESTROY(pool) destroy_mem_pool(pool)
#define MEMPOOL_ALLOC(pool, number, type) ((type *)mem_alloc(pool, sizeof(type) * number))
#define MEMPOOL_FREE(pool, p) mem_free(pool, p)
struct utable_item {
char *key;
size_t key_sz;
enum utable_value_type value_type;
size_t value_sz;
union {
struct
{
char *cstring;
size_t cstring_sz;
};
struct
{
char *blob;
size_t blob_sz;
};
struct
{
int64_t *interger_array;
size_t n_integer;
};
struct
{
char **cstring_array;
size_t *cstring_array_sz;
size_t n_cstring;
};
int64_t integer;
};
UT_hash_handle hh;
};
struct utable {
struct utable_item *items;
struct utable_item *iter;
MEMPOOL_TYPE *mempool;
struct utable_stat stat;
};
struct utable *utable_new_with_size(size_t sz)
{
struct utable *table = ALLOC(1, struct utable);
table->mempool = MEMPOOL_CREATE(sz);
return table;
}
#define UTABLE_INITIAL_MEMPOOL_SIZE 2 * 1024
inline struct utable *utable_new(void)
{
return utable_new_with_size(UTABLE_INITIAL_MEMPOOL_SIZE);
}
void utable_free(struct utable *table)
{
if(table->items) HASH_CLEAR(hh, table->items);
if(table->mempool) MEMPOOL_DESTROY(table->mempool);
FREE(table);
}
inline void utable_stat(struct utable *table, struct utable_stat *stat)
{
if(stat == NULL)
return;
memcpy(stat, &table->stat, sizeof(struct utable_stat));
}
static void utable_item_stat_add(struct utable_stat *stat, struct utable_item *item)
{
if(stat==NULL || item == NULL)
return;
stat->n_item++;
stat->n_item_size += item->key_sz;
stat->n_item_size += item->value_sz;
switch (item->value_type)
{
case utable_value_type_cstring:
stat->n_cstring++;
stat->n_cstring_size += item->value_sz;
break;
case utable_value_type_blob:
stat->n_blob++;
stat->n_blob_size += item->value_sz;
break;
case utable_value_type_integer:
stat->n_integer++;
stat->n_integer_size += item->value_sz;
break;
case utable_value_type_integer_array:
stat->n_integer_array++;
stat->n_integer_array_size += item->value_sz;
break;
case utable_value_type_cstring_array:
stat->n_cstring_array++;
stat->n_cstring_array_size += item->value_sz;
break;
default:
break;
}
}
static void utable_item_stat_sub(struct utable_stat *stat, struct utable_item *item)
{
if(stat==NULL || item == NULL)
return;
stat->n_item--;
stat->n_item_size -= item->key_sz;
stat->n_item_size -= item->value_sz;
switch (item->value_type)
{
case utable_value_type_cstring:
stat->n_cstring--;
stat->n_cstring_size -= item->cstring_sz;
break;
case utable_value_type_blob:
stat->n_blob--;
stat->n_blob_size -= item->blob_sz;
break;
case utable_value_type_integer:
stat->n_integer--;
stat->n_integer_size -= sizeof(item->integer);
break;
case utable_value_type_integer_array:
stat->n_integer_array--;
stat->n_integer_array_size -= sizeof(int64_t) * item->n_integer;
break;
case utable_value_type_cstring_array:
stat->n_cstring_array--;
stat->n_cstring_array_size -= item->value_sz;
break;
default:
break;
}
}
static char*mempool_strdup(MEMPOOL_TYPE *mempool, const char* s, size_t len)
{
char* new_str = MEMPOOL_ALLOC(mempool, len + 1, char);
memcpy(new_str, s, len + 1);
return new_str;
}
void utable_add_cstring(struct utable *table, const char *key, const char *value, size_t value_sz)
{
// check if key already exists
struct utable_item *item;
size_t key_sz = strlen(key);
HASH_FIND(hh, table->items, key, key_sz, item);
if (item) {
DEBUG_PRINT("ERR: key %s already exists\n", key);
return;
}
item = MEMPOOL_ALLOC(table->mempool ,1,struct utable_item);
item->key = mempool_strdup(table->mempool,key, key_sz);
item->key_sz = key_sz;
item->value_type = utable_value_type_cstring;
item->cstring = MEMPOOL_ALLOC(table->mempool ,value_sz + 1, char);
memcpy(item->cstring, value, value_sz);
item->cstring[value_sz] = '\0';
item->cstring_sz = value_sz;
item->value_sz = value_sz;
HASH_ADD_KEYPTR(hh, table->items, item->key, item->key_sz, item);
utable_item_stat_add(&table->stat, item);
}
void utable_add_blob(struct utable *table, const char *key, const char *blob, size_t blob_sz)
{
// check if key already exists
struct utable_item *item;
size_t key_sz = strlen(key);
HASH_FIND(hh, table->items, key, key_sz, item);
if (item) {
DEBUG_PRINT("ERR: key %s already exists\n", key);
return;
}
item = MEMPOOL_ALLOC(table->mempool ,1, struct utable_item);
item->key = mempool_strdup(table->mempool, key, key_sz);
item->key_sz = key_sz;
item->value_type = utable_value_type_blob;
item->blob = MEMPOOL_ALLOC(table->mempool ,blob_sz, char);
memcpy(item->blob, blob, blob_sz);
item->blob_sz = blob_sz;
item->value_sz = blob_sz;
HASH_ADD_KEYPTR(hh, table->items, item->key, item->key_sz, item);
utable_item_stat_add(&table->stat, item);
}
void utable_add_integer(struct utable *table, const char *key, int64_t value)
{
// check if key already exists
struct utable_item *item;
size_t key_sz = strlen(key);
HASH_FIND(hh, table->items, key, key_sz, item);
if (item) {
DEBUG_PRINT("ERR: key %s already exists\n", key);
return;
}
item = MEMPOOL_ALLOC(table->mempool ,1, struct utable_item);
item->key = mempool_strdup(table->mempool, key, key_sz);
item->key_sz = key_sz;
item->value_type = utable_value_type_integer;
item->integer = value;
item->value_sz = sizeof(int64_t);
HASH_ADD_KEYPTR(hh, table->items, item->key, item->key_sz, item);
utable_item_stat_add(&table->stat, item);
}
void utable_add_integer_array(struct utable *table, const char *key, int64_t value_array[], size_t n_value)
{
// check if key already exists
struct utable_item *item;
size_t key_sz = strlen(key);
HASH_FIND(hh, table->items, key, key_sz, item);
if (item) {
DEBUG_PRINT("ERR: key %s already exists\n", key);
return;
}
item = MEMPOOL_ALLOC(table->mempool ,1, struct utable_item);
item->key = mempool_strdup(table->mempool, key, key_sz);
item->key_sz = key_sz;
item->value_type = utable_value_type_integer_array;
item->interger_array = MEMPOOL_ALLOC(table->mempool ,n_value, int64_t);
memcpy(item->interger_array, value_array, sizeof(int64_t) * n_value);
item->n_integer = n_value;
item->value_sz = sizeof(int64_t) * n_value;
HASH_ADD_KEYPTR(hh, table->items, item->key, item->key_sz, item);
utable_item_stat_add(&table->stat, item);
}
void utable_add_cstring_array(struct utable *table, const char *key, const char* value_array[], size_t value_sz[], size_t n_value)
{
// check if key already exists
struct utable_item *item;
size_t key_sz = strlen(key);
HASH_FIND(hh, table->items, key, key_sz, item);
if (item) {
DEBUG_PRINT("ERR: key %s already exists\n", key);
return;
}
item = MEMPOOL_ALLOC(table->mempool ,1, struct utable_item);
item->key = mempool_strdup(table->mempool, key, key_sz);
item->key_sz = key_sz;
item->value_type = utable_value_type_cstring_array;
item->cstring_array = MEMPOOL_ALLOC(table->mempool ,n_value, char *);
item->cstring_array_sz = MEMPOOL_ALLOC(table->mempool ,n_value, size_t);
item->value_sz = 0;
for(size_t i =0; i < n_value; i++)
{
item->cstring_array[i] = MEMPOOL_ALLOC(table->mempool , value_sz[i]+1, char);
memcpy(item->cstring_array[i], value_array[i], value_sz[i]);
item->cstring_array[i][value_sz[i]] = '\0';
item->cstring_array_sz[i] = value_sz[i];
item->value_sz += value_sz[i];
}
item->n_cstring = n_value;
HASH_ADD_KEYPTR(hh, table->items, item->key, item->key_sz, item);
utable_item_stat_add(&table->stat, item);
}
void utable_delete_item(struct utable *table, const char *key)
{
struct utable_item *item;
HASH_FIND_STR(table->items, key, item);
if (item) {
HASH_DEL(table->items, item);
MEMPOOL_FREE(table->mempool, item->key);
switch (item->value_type) {
case utable_value_type_cstring:
MEMPOOL_FREE(table->mempool,item->cstring);
break;
case utable_value_type_blob:
MEMPOOL_FREE(table->mempool,item->blob);
break;
case utable_value_type_integer_array:
MEMPOOL_FREE(table->mempool,item->interger_array);
break;
case utable_value_type_cstring_array:
for(size_t i=0; i < item->n_cstring; i++)
{
MEMPOOL_FREE(table->mempool,item->cstring_array[i]);
}
MEMPOOL_FREE(table->mempool,item->cstring_array_sz);
break;
default:
break;
}
utable_item_stat_sub(&table->stat, item);
MEMPOOL_FREE(table->mempool,item);
}
}
void utable_reset_iter(struct utable *table)
{
table->iter = NULL;
}
const char *utable_next_key(struct utable *table)
{
if (table->iter == NULL) {
table->iter = table->items;
} else {
table->iter = (struct utable_item *)table->iter->hh.next;
}
if (table->iter == NULL) {
return NULL;
}
return table->iter->key;
}
enum utable_value_type utable_get_value_type(const struct utable *table, const char *key)
{
struct utable_item *item;
HASH_FIND_STR(table->items, key, item);
if (item) {
return item->value_type;
}
return utable_value_type_undefined;
}
int utable_get0_blob_value(const struct utable *table, const char *key, char **value, size_t *value_len)
{
struct utable_item *item;
HASH_FIND_STR(table->items, key, item);
if (item) {
if (item->value_type == utable_value_type_blob) {
*value = item->blob;
*value_len = item->blob_sz;
return 0;
}
}
return -1;
}
int utable_get0_cstring_value(const struct utable *table, const char *key, char **value, size_t *value_len)
{
struct utable_item *item;
HASH_FIND_STR(table->items, key, item);
if (item) {
if (item->value_type == utable_value_type_cstring) {
*value = item->cstring;
*value_len = item->cstring_sz;
return 0;
}
}
return -1;
}
int utable_get0_integer_value(const struct utable *table, const char *key, int64_t *value)
{
struct utable_item *item;
HASH_FIND_STR(table->items, key, item);
if (item) {
if (item->value_type == utable_value_type_integer) {
*value = item->integer;
return 0;
}
}
return -1;
}
int utable_get0_integer_value_array(const struct utable *table, const char *key, int64_t **value_array, size_t *n_value)
{
struct utable_item *item;
HASH_FIND_STR(table->items, key, item);
if (item) {
if (item->value_type == utable_value_type_integer_array) {
*value_array = item->interger_array;
*n_value = item->n_integer;
return 0;
}
}
return -1;
}
int utable_get0_cstring_value_array(const struct utable *table, const char *key, char ***value_array, size_t **value_len, size_t *n_value)
{
struct utable_item *item;
HASH_FIND_STR(table->items, key, item);
if (item) {
if (item->value_type == utable_value_type_cstring_array) {
*value_array = item->cstring_array;
*value_len = item->cstring_array_sz;
*n_value = item->n_cstring;
return 0;
}
}
return -1;
}
struct utable_item *utable_item_dup(MEMPOOL_TYPE *mempool, const struct utable_item *item)
{
struct utable_item *new_item = MEMPOOL_ALLOC(mempool, 1, struct utable_item);
size_t key_sz = item->key_sz;
new_item->key = mempool_strdup(mempool, item->key, key_sz);
new_item->key_sz = key_sz;
new_item->value_type = item->value_type;
new_item->value_sz= item->value_sz;
switch (item->value_type) {
case utable_value_type_cstring:
new_item->cstring = MEMPOOL_ALLOC(mempool,item->cstring_sz + 1, char);
memcpy(new_item->cstring, item->cstring, item->cstring_sz);
new_item->cstring[item->cstring_sz] = '\0';
new_item->cstring_sz = item->cstring_sz;
break;
case utable_value_type_blob:
new_item->blob = MEMPOOL_ALLOC(mempool,item->blob_sz, char);
memcpy(new_item->blob, item->blob, item->blob_sz);
new_item->blob_sz = item->blob_sz;
break;
case utable_value_type_integer:
new_item->integer = item->integer;
break;
case utable_value_type_integer_array:
new_item->interger_array = MEMPOOL_ALLOC(mempool,item->n_integer, int64_t);
memcpy(new_item->interger_array, item->interger_array, sizeof(int64_t) * item->n_integer);
new_item->n_integer = item->n_integer;
break;
case utable_value_type_cstring_array:
new_item->n_cstring= item->n_cstring;
new_item->cstring_array = MEMPOOL_ALLOC(mempool ,item->n_cstring, char *);
new_item->cstring_array_sz = MEMPOOL_ALLOC(mempool ,item->n_cstring, size_t);
for(size_t i =0; i < item->n_cstring; i++)
{
new_item->cstring_array[i] = MEMPOOL_ALLOC(mempool , item->cstring_array_sz[i]+1, char);
memcpy(new_item->cstring_array[i], item->cstring_array[i], item->cstring_array_sz[i]);
new_item->cstring_array[i][item->cstring_array_sz[i]] = '\0';
new_item->cstring_array_sz[i] = item->cstring_array_sz[i];
}
break;
default:
break;
}
return new_item;
}
struct utable *utable_duplicate(const struct utable *table)
{
struct utable *new_table = utable_new();
struct utable_item *item, *tmp;
HASH_ITER(hh, table->items, item, tmp) {
struct utable_item *new_item = utable_item_dup(new_table->mempool, item);
HASH_ADD_KEYPTR(hh, new_table->items, new_item->key, new_item->key_sz, new_item);
}
new_table->stat = table->stat;
return new_table;
}
int utable_merge(struct utable *dst, const struct utable *src)
{
struct utable_item *item, *tmp;
HASH_ITER(hh, src->items, item, tmp) {
struct utable_item *dst_item;
HASH_FIND(hh, dst->items, item->key, item->key_sz, dst_item);
if (dst_item) {
continue;
}
struct utable_item *new_item = utable_item_dup(dst->mempool, item);
HASH_ADD_KEYPTR(hh, dst->items, new_item->key, new_item->key_sz, new_item);
utable_item_stat_add(&dst->stat, item);
}
return 0;
}
int utable_json_export(const struct utable *table, char **blob, size_t *blob_len)
{
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *root = yyjson_mut_obj(doc);
yyjson_mut_doc_set_root(doc, root);
char *encs[HASH_COUNT(table->items)];
struct utable_item *item, *tmp;
int enc_cnt = 0;
HASH_ITER(hh, table->items, item, tmp) {
switch (item->value_type) {
case utable_value_type_cstring: {
yyjson_mut_obj_add_str(doc, root, item->key, item->cstring); // key and cstring are shallow copied
}
break;
case utable_value_type_blob: {
char *enc = b64_encode((unsigned char *)item->blob, item->blob_sz);
yyjson_mut_obj_add_str(doc, root, item->key, enc);
// do not free enc now, it is shallow copied
encs[enc_cnt++] = enc;
}
break;
case utable_value_type_integer: {
yyjson_mut_obj_add_int(doc, root, item->key, item->integer);
}
break;
case utable_value_type_integer_array: {
yyjson_mut_val *arr = yyjson_mut_arr_with_sint64(doc, item->interger_array, item->n_integer);
yyjson_mut_obj_add_val(doc, root, item->key, arr);
}
break;
case utable_value_type_cstring_array: {
yyjson_mut_val *arr = yyjson_mut_arr_with_strn(doc, (const char **)item->cstring_array, item->cstring_array_sz, item->n_cstring);
yyjson_mut_obj_add_val(doc, root, item->key, arr);
}
break;
default:
break;
}
}
char *json_str = yyjson_mut_write(doc, YYJSON_WRITE_ALLOW_INVALID_UNICODE, blob_len);
yyjson_mut_doc_free(doc);
*blob = json_str;
for (int j = 0; j < enc_cnt; j++) {
free(encs[j]);
}
return 0;
}
/*
[{
"key":<tag key string>
"type":"i_arr", "c_str", or "blob"
"value":<
}]
*/
int utable_msgpack_export(const struct utable *table, char **blob, size_t *blob_len)
{
mpack_writer_t writer;
mpack_writer_init_growable(&writer, blob, blob_len);
mpack_start_array(&writer, HASH_COUNT(table->items));
struct utable_item *item, *tmp;
HASH_ITER(hh, table->items, item, tmp) {
mpack_start_map(&writer, 3);
mpack_write_cstr(&writer, "key");
mpack_write_cstr(&writer, item->key);
mpack_write_cstr(&writer, "type");
switch (item->value_type) {
case utable_value_type_cstring:
mpack_write_cstr(&writer, "c_str");
break;
case utable_value_type_blob:
mpack_write_cstr(&writer, "blob");
break;
case utable_value_type_integer:
mpack_write_cstr(&writer, "i");
break;
case utable_value_type_integer_array:
mpack_write_cstr(&writer, "i_arr");
break;
default:
break;
}
mpack_write_cstr(&writer, "value");
switch (item->value_type) {
case utable_value_type_cstring:
mpack_write_bin(&writer, item->cstring, item->cstring_sz);
break;
case utable_value_type_blob:
mpack_write_bin(&writer, item->blob, item->blob_sz);
break;
case utable_value_type_integer:
mpack_write_i64(&writer, item->integer);
break;
case utable_value_type_integer_array:
mpack_start_array(&writer, item->n_integer);
for (size_t i = 0; i < item->n_integer; i++) {
mpack_write_i64(&writer, item->interger_array[i]);
}
mpack_finish_array(&writer);
break;
default:
break;
}
mpack_finish_map(&writer);
}
mpack_finish_array(&writer);
if (mpack_writer_destroy(&writer) != mpack_ok) {
DEBUG_PRINT("ERR mpack writer fieldtag_list_serialize destroy failed\n");
return -1;
}
return 0;
}
void utable_serialize(const struct utable *table, char **blob, size_t *blob_len) {
utable_msgpack_export(table, blob, blob_len);
}
char *get_cstring_from_mpack_node(MEMPOOL_TYPE *mempool ,mpack_node_t node)
{
size_t len = mpack_node_strlen(node);
char *str = MEMPOOL_ALLOC(mempool, len + 1, char);
memcpy(str, mpack_node_str(node), len);
str[len] = '\0';
return str;
}
static void utable_item_free(MEMPOOL_TYPE *mempool, struct utable_item *item)
{
if(item == NULL)
return;
if(item->key)MEMPOOL_FREE(mempool, item->key);
switch (item->value_type) {
case utable_value_type_cstring:
MEMPOOL_FREE(mempool,item->cstring);
break;
case utable_value_type_blob:
MEMPOOL_FREE(mempool,item->blob);
break;
case utable_value_type_integer_array:
MEMPOOL_FREE(mempool,item->interger_array);
break;
case utable_value_type_cstring_array:
for(size_t i=0; i < item->n_cstring; i++)
{
MEMPOOL_FREE(mempool,item->cstring_array[i]);
}
MEMPOOL_FREE(mempool,item->cstring_array_sz);
break;
default:
break;
}
}
struct utable *utable_deserialize(const char *blob, size_t blob_len)
{
mpack_tree_t tree;
mpack_tree_init_data(&tree, blob, blob_len);
mpack_tree_parse(&tree);
mpack_node_t root = mpack_tree_root(&tree);
if (mpack_node_type(root) != mpack_type_array) {
DEBUG_PRINT("ERR mpack root type is not array\n");
return NULL;
}
size_t n_item = mpack_node_array_length(root);
struct utable *table = utable_new();
for (size_t i = 0; i < n_item; i++) {
mpack_node_t item = mpack_node_array_at(root, i);
mpack_node_t key = mpack_node_map_cstr(item, "key");
mpack_node_t type = mpack_node_map_cstr(item, "type");
mpack_node_t value = mpack_node_map_cstr(item, "value");
char *type_str = get_cstring_from_mpack_node(table->mempool, type);
struct utable_item *new_item = MEMPOOL_ALLOC(table->mempool,1, struct utable_item);
new_item->key = get_cstring_from_mpack_node(table->mempool, key);
new_item->key_sz = strlen(new_item->key);
new_item->value_sz = 0;
if (strcmp(type_str, "c_str") == 0) {
new_item->value_type = utable_value_type_cstring;
new_item->cstring = MEMPOOL_ALLOC(table->mempool,mpack_node_bin_size(value), char);
new_item->cstring_sz = mpack_node_bin_size(value);
new_item->value_sz = new_item->cstring_sz;
memcpy(new_item->cstring, mpack_node_bin_data(value), mpack_node_bin_size(value));
} else if (strcmp(type_str, "blob") == 0) {
new_item->value_type = utable_value_type_blob;
new_item->blob = MEMPOOL_ALLOC(table->mempool,mpack_node_bin_size(value), char);
new_item->blob_sz = mpack_node_bin_size(value);
new_item->value_sz = new_item->blob_sz;
memcpy(new_item->blob, mpack_node_bin_data(value), mpack_node_bin_size(value));
} else if (strcmp(type_str, "i") == 0) {
new_item->value_type = utable_value_type_integer;
new_item->integer = mpack_node_i64(value);
} else if (strcmp(type_str, "i_arr") == 0) {
new_item->value_type = utable_value_type_integer_array;
new_item->n_integer = mpack_node_array_length(value);
new_item->interger_array = MEMPOOL_ALLOC(table->mempool,new_item->n_integer, int64_t);
for (size_t j = 0; j < new_item->n_integer; j++) {
new_item->interger_array[j] = mpack_node_i64(mpack_node_array_at(value, j));
}
new_item->value_sz = sizeof(int64_t) * new_item->n_integer;
} else {
DEBUG_PRINT("ERR unknown type %s\n", type_str);
mpack_tree_destroy(&tree);
MEMPOOL_FREE(table->mempool, type_str);
utable_item_free(table->mempool, new_item);
return NULL;
}
HASH_ADD_KEYPTR(hh, table->items, new_item->key, new_item->key_sz, new_item);
utable_item_stat_add(&table->stat, new_item);
MEMPOOL_FREE(table->mempool, type_str);
}
mpack_tree_destroy(&tree);
return table;
}

87
deps/utable/utable.h vendored Normal file
View File

@@ -0,0 +1,87 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
struct utable_stat
{
size_t n_item;
size_t n_item_size;
size_t n_blob;
size_t n_blob_size;
size_t n_cstring;
size_t n_cstring_size;
size_t n_integer;
size_t n_integer_size;
size_t n_integer_array;
size_t n_integer_array_size;
size_t n_cstring_array;
size_t n_cstring_array_size;
};
struct utable;
struct utable *utable_new(void);
struct utable *utable_new_with_size(size_t sz);
void utable_free(struct utable *table);
void utable_stat(struct utable *table, struct utable_stat *stat);
void utable_add_cstring(struct utable *table, const char *key, const char *value, size_t value_sz);
void utable_add_blob(struct utable *table, const char *key, const char *blob, size_t blob_sz);
void utable_add_integer(struct utable *table, const char *key, int64_t value);
void utable_add_integer_array(struct utable *table, const char *key, int64_t value_array[], size_t n_value);
void utable_add_cstring_array(struct utable *table, const char *key, const char* value_array[], size_t value_sz[], size_t n_value);
void utable_delete_item(struct utable *table, const char *key);
enum utable_value_type
{
utable_value_type_undefined=0,
utable_value_type_cstring,
utable_value_type_blob,
utable_value_type_integer,
utable_value_type_integer_array,
utable_value_type_cstring_array
};
void utable_reset_iter(struct utable *table);
const char *utable_next_key(struct utable *table);
enum utable_value_type utable_get_value_type(const struct utable *table, const char *key);
int utable_get0_blob_value(const struct utable *table, const char *key, char **value, size_t *value_len);
int utable_get0_cstring_value(const struct utable *table, const char *key, char **value, size_t *value_len);
int utable_get0_integer_value(const struct utable *table, const char *key, int64_t *value);
int utable_get0_integer_value_array(const struct utable *table, const char *key, int64_t **value_array, size_t *n_value);
int utable_get0_cstring_value_array(const struct utable *table, const char *key, char ***value_array, size_t **value_len, size_t *n_value);
struct utable *utable_duplicate(const struct utable *table);
int utable_merge(struct utable *dst, const struct utable *src);
void utable_serialize(const struct utable *table, char **blob, size_t *blob_len);
struct utable *utable_deserialize(const char *blob, size_t blob_len);
struct ipfix_exporter_schema;
struct ipfix_exporter_schema *utable_ipfix_exporter_schema_new(const char *ipfix_schema_json_path, uint16_t domain_id, uint16_t n_worker); // |domain_id|work_id|=source_id (16+16) domain_id 是进程唯一标识
void utable_ipfix_exporter_schema_free(struct ipfix_exporter_schema *ipfix_schema);
uint32_t utable_ipfix_template_flow_refresh_interval_s(struct ipfix_exporter_schema *ipfix_schema);
const char *utable_ipfix_template_flow_get0(struct ipfix_exporter_schema *ipfix_schema, uint16_t worker_id, size_t *blob_len);
// return template id, -1 if not found
int utable_ipfix_template_get(struct ipfix_exporter_schema *ipfix_schema, const char *template_name);
int utable_ipfix_data_flow_exporter(const struct utable *table, struct ipfix_exporter_schema *ipfix_schema, int template_id, uint16_t worker_id, char **blob, size_t *blob_len);
int utable_json_export(const struct utable *table, char **blob, size_t *blob_len);
int utable_msgpack_export(const struct utable *table, char **blob, size_t *blob_len);
#ifdef __cplusplus
}
#endif

795
deps/utable/utable_ipfix_exporter.c vendored Normal file
View File

@@ -0,0 +1,795 @@
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <stddef.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include "utable.h"
#include "cjson/cJSON.h"
#include "yyjson/yyjson.h"
#include "uthash/utarray.h"
#include "uthash/uthash.h"
#define IPFIX_DEFUALT_VERSION 10
#define GEEDGE_NETWORKS_PEN_NUMBER 54450
#define IPFIX_DEFUALT_PEN_NUMBER GEEDGE_NETWORKS_PEN_NUMBER
#define IPFIX_NONE_PEN_NUMBER -1
#define IPFIX_TEMPLATE_SET_ID 2
#define IPFIX_BUFF_MAX_SIZE 2048
#define IPFIX_ELEMENT_MAX_LEN 32
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
enum ipfix_type
{
IPFIX_UNSIGNED8 = 0,
IPFIX_UNSIGNED16,
IPFIX_UNSIGNED32,
IPFIX_UNSIGNED64,
IPFIX_VARIABLE_STRING,
IPFIX_UNKNOWN
};
struct ipfix_element
{
enum ipfix_type type;
uint16_t element_id;
uint16_t value_length; // if type is variable_string, value_length is 0
int PEN_number;
char name[IPFIX_ELEMENT_MAX_LEN];
};
struct ipfix_template
{
uint16_t template_id;
char *name;
UT_array *elements_array; // utarray for current template elements
};
struct ipfix_worker_context
{
int source_id;
int sequence;
size_t template_blob_length;
char *template_blob;
};
struct ipfix_exporter_schema
{
uint16_t n_worker;
uint16_t version;
int domain_id;
int PEN_number;
uint32_t templates_refresh_interval_s;
UT_array *templates_array; // utarray for templates
struct ipfix_worker_context *worker_context_array; // utarray for worker_context
};
struct ipfix_message_head
{
uint16_t version;
uint16_t length;
int exporttime;
int ipfix_message_sequence;
int domain_id;
};
#define IPFIX_MESSAGE_HEAD_LEN sizeof(struct ipfix_message_head)
// from maat
int load_file_to_memory(const char *file_name, unsigned char **pp_out, size_t *out_sz)
{
int ret = 0;
FILE *fp = NULL;
struct stat fstat_buf;
size_t read_size = 0;
ret = stat(file_name, &fstat_buf);
if (ret != 0)
{
return -1;
}
fp = fopen(file_name, "r");
if (fp == NULL)
{
return -1;
}
*out_sz = fstat_buf.st_size;
*pp_out = (unsigned char *)calloc(*out_sz + 1, sizeof(unsigned char));
read_size = fread(*pp_out, 1, *out_sz, fp);
if (read_size != *out_sz)
{
free(*pp_out);
*pp_out = NULL;
}
fclose(fp);
fp = NULL;
return 0;
}
int ipfix_exporter_get_type(struct ipfix_element *p_element, char *element_type)
{
if (strcmp(element_type, "string") == 0)
{
p_element->type = IPFIX_VARIABLE_STRING;
p_element->value_length = 0;
return 0;
}
else if (strcmp(element_type, "unsigned8") == 0)
{
p_element->type = IPFIX_UNSIGNED8;
p_element->value_length = sizeof(uint8_t);
return 0;
}
else if (strcmp(element_type, "unsigned16") == 0)
{
p_element->type = IPFIX_UNSIGNED16;
p_element->value_length = sizeof(uint16_t);
return 0;
}
else if (strcmp(element_type, "unsigned32") == 0)
{
p_element->type = IPFIX_UNSIGNED32;
p_element->value_length = sizeof(uint32_t);
return 0;
}
else if (strcmp(element_type, "unsigned64") == 0)
{
p_element->type = IPFIX_UNSIGNED64;
p_element->value_length = sizeof(uint64_t);
return 0;
}
else
{
return -1;
}
}
int ipfix_template_payload_init(UT_array *templates_array, char **blob, size_t *blob_len)
{
if (templates_array == NULL || utarray_len(templates_array) == 0 || blob == NULL || blob_len == NULL)
{
return -1;
}
size_t n_templates = utarray_len(templates_array);
size_t template_blob_len = 0;
for (size_t i = 0; i < n_templates; i++)
{
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(templates_array, i);
size_t n_elements = utarray_len(template_item->elements_array);
template_blob_len += (sizeof(uint16_t) + sizeof(uint16_t) + n_elements * (sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t))); // template_id + field_count + element_id + length + PEN_number
}
template_blob_len += ((sizeof(uint16_t) + sizeof(uint16_t)) + IPFIX_MESSAGE_HEAD_LEN); // set_id + set_length + ipfix_message_head
char *payload = (char *)calloc(template_blob_len, sizeof(char));
int offset = 0;
offset += IPFIX_MESSAGE_HEAD_LEN; // skip ipfix_message_head
*(uint16_t *)(payload + offset) = htons(IPFIX_TEMPLATE_SET_ID); // set_id
offset += sizeof(uint16_t); // skip set_id
*(uint16_t *)(payload + offset) = htons(template_blob_len - IPFIX_MESSAGE_HEAD_LEN); // set_length
offset += sizeof(uint16_t); // skip set_length
for (size_t i = 0; i < n_templates; i++)
{
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(templates_array, i);
size_t n_elements = utarray_len(template_item->elements_array);
*(uint16_t *)(payload + offset) = htons(template_item->template_id); // template_id
offset += sizeof(uint16_t); // skip template_id
*(uint16_t *)(payload + offset) = htons(n_elements); // field_count
offset += sizeof(uint16_t); // skip field_count
for (size_t j = 0; j < n_elements; j++)
{
struct ipfix_element *p_element = (struct ipfix_element *)utarray_eltptr(template_item->elements_array, j);
// set element_id
if (p_element->PEN_number != IPFIX_NONE_PEN_NUMBER)
{
*(uint16_t *)(payload + offset) = htons(p_element->element_id | 0x8000); // Pen_provided is yes
}
else
{
*(uint16_t *)(payload + offset) = htons(p_element->element_id); // Pen_provided is no
}
offset += sizeof(uint16_t); // skip element_id
// set element_length
if (p_element->type == IPFIX_VARIABLE_STRING)
{
*(uint16_t *)(payload + offset) = htons(65535); // if element_length is 65535, it means variable length
}
else
{
*(uint16_t *)(payload + offset) = htons(p_element->value_length); // length
}
offset += sizeof(uint16_t); // skip length
// set PEN_number
if (p_element->PEN_number != IPFIX_NONE_PEN_NUMBER)
{
*(uint32_t *)(payload + offset) = htonl(p_element->PEN_number); // PEN_number
offset += sizeof(uint32_t); // skip PEN_number
}
}
}
if (offset != (int)template_blob_len)
{
free(payload);
payload = NULL;
return -1;
}
*blob = payload;
*blob_len = offset;
return 0;
}
void ipfix_template_item_destroy(void *elt)
{
struct ipfix_template *template_item = (struct ipfix_template *)elt;
if (template_item == NULL)
{
return;
}
if (template_item->elements_array != NULL)
{
utarray_free(template_item->elements_array);
}
if (template_item->name != NULL)
{
free(template_item->name);
template_item->name = NULL;
}
return;
}
void ipfix_utarray_init(UT_array **_array, size_t sz_item, void (*dtor)(void *))
{
if (_array == NULL || *_array != NULL)
{
return;
}
UT_icd icd = {0};
icd.sz = sz_item;
icd.dtor = dtor;
utarray_new(*_array, &icd);
return;
}
UT_array *ipfix_elements_array_init(cJSON *root, const char *element_key, int PEN_number_global)
{
if (root == NULL || element_key == NULL)
{
return NULL;
}
cJSON *elements = cJSON_GetObjectItem(root, element_key);
if (elements == NULL)
{
return NULL;
}
UT_array *elements_array = NULL;
ipfix_utarray_init(&elements_array, sizeof(struct ipfix_element), NULL);
size_t n_elements = cJSON_GetArraySize(elements);
for (size_t i = 0; i < n_elements; i++)
{
struct ipfix_element element_item = {0};
cJSON *element = cJSON_GetArrayItem(elements, i);
cJSON *element_id = cJSON_GetObjectItem(element, "element_id");
if (element_id == NULL || element_id->valueint == 0) // element_id不存在、类型不为Number、值为0
{
goto error;
}
element_item.element_id = element_id->valueint;
cJSON *element_type = cJSON_GetObjectItem(element, "element_type");
if (element_type == NULL || element_type->valuestring == NULL) // element_type不存在、类型不为String、值为NULL
{
goto error;
}
if (ipfix_exporter_get_type(&element_item, element_type->valuestring) == -1) // element_type不在支持范围内
{
goto error;
}
cJSON *PEN_number = cJSON_GetObjectItem(element, "PEN_number");
if (PEN_number == NULL) // PEN_number不存在、类型不为Number
{
element_item.PEN_number = PEN_number_global;
}
else
{
element_item.PEN_number = PEN_number->valueint;
}
cJSON *element_name = cJSON_GetObjectItem(element, "element_name");
if (element_name == NULL || element_name->valuestring == NULL) // element_name不存在、类型不为String、值为NULL
{
goto error;
}
memcpy(element_item.name, element_name->valuestring, MIN(strlen(element_name->valuestring), IPFIX_ELEMENT_MAX_LEN - 1));
utarray_push_back(elements_array, &element_item);
}
return elements_array;
error:
utarray_free(elements_array);
elements_array = NULL;
return NULL;
}
void utable_ipfix_exporter_schema_free(struct ipfix_exporter_schema *instance)
{
if (instance == NULL)
{
return;
}
if (instance->templates_array)
{
utarray_free(instance->templates_array);
instance->templates_array = NULL;
}
if (instance->worker_context_array)
{
for (size_t i = 0; i < instance->n_worker; i++)
{
if (instance->worker_context_array[i].template_blob)
{
free(instance->worker_context_array[i].template_blob);
instance->worker_context_array[i].template_blob = NULL;
}
}
}
free(instance->worker_context_array);
instance->worker_context_array = NULL;
free(instance);
instance = NULL;
return;
}
struct ipfix_elements_hash
{
char *name;
UT_array *elements_array;
UT_hash_handle hh;
};
void ipfix_elements_array_hash_destroy(struct ipfix_elements_hash *elements_array_hash)
{
if (elements_array_hash == NULL)
{
return;
}
struct ipfix_elements_hash *elements_array_item = NULL;
struct ipfix_elements_hash *tmp = NULL;
HASH_ITER(hh, elements_array_hash, elements_array_item, tmp)
{
HASH_DEL(elements_array_hash, elements_array_item);
if (elements_array_item->name != NULL)
{
free(elements_array_item->name);
elements_array_item->name = NULL;
}
if (elements_array_item->elements_array != NULL)
{
utarray_free(elements_array_item->elements_array);
elements_array_item->elements_array = NULL;
}
free(elements_array_item);
elements_array_item = NULL;
}
return;
}
struct ipfix_exporter_schema *utable_ipfix_exporter_schema_new(const char *ipfix_schema_json_path, uint16_t domain_id, uint16_t n_worker)
{
if (ipfix_schema_json_path == NULL)
{
return NULL;
}
size_t json_size = 0;
unsigned char *json_str = NULL;
int ret = load_file_to_memory(ipfix_schema_json_path, &json_str, &json_size);
if (ret == -1)
{
return NULL;
}
cJSON *root = NULL;
root = cJSON_Parse((const char *)json_str);
if (root == NULL)
{
free(json_str);
json_str = NULL;
return NULL;
}
int version = 0;
version = cJSON_GetObjectItem(root, "version")->valueint;
if (version < IPFIX_DEFUALT_VERSION)
{
version = IPFIX_DEFUALT_VERSION;
}
int PEN_number = 0;
PEN_number = cJSON_GetObjectItem(root, "PEN_number")->valueint;
if (PEN_number <= 0)
{
PEN_number = IPFIX_DEFUALT_PEN_NUMBER;
}
uint32_t refresh_interval_s = 0;
refresh_interval_s = cJSON_GetObjectItem(root, "refresh_interval_s")->valueint;
if ((int)refresh_interval_s <= 0)
{
refresh_interval_s = 60;
}
size_t n_template = 0;
cJSON *templates = NULL;
templates = cJSON_GetObjectItem(root, "templates");
if (templates == NULL || cJSON_GetArraySize(templates) == 0)
{
free(json_str);
json_str = NULL;
return NULL;
}
n_template = cJSON_GetArraySize(templates);
UT_array *templates_array = NULL;
ipfix_utarray_init(&templates_array, sizeof(struct ipfix_template), ipfix_template_item_destroy);
struct ipfix_elements_hash *elements_array_hash = NULL;
for (size_t i = 0; i < n_template; i++)
{
struct ipfix_template template_item = {0};
cJSON *template_obj = cJSON_GetArrayItem(templates, i);
cJSON *template_id = cJSON_GetObjectItem(template_obj, "template_id");
if (template_id == NULL || template_id->valueint < 256 || template_id->valueint == 0xffff)
{
goto error;
}
template_item.template_id = template_id->valueint;
cJSON *template_name = cJSON_GetObjectItem(template_obj, "template_name");
if (template_name == NULL || template_name->valuestring == NULL)
{
goto error;
}
template_item.name = (char *)calloc(strlen(template_name->valuestring) + 1, sizeof(char));
memcpy(template_item.name, template_name->valuestring, strlen(template_name->valuestring));
cJSON *elements_key_array = cJSON_GetObjectItem(template_obj, "elements");
if (elements_key_array == NULL)
{
goto error;
}
size_t n_elements_key = cJSON_GetArraySize(elements_key_array);
ipfix_utarray_init(&template_item.elements_array, sizeof(struct ipfix_element), NULL);
for (size_t j = 0; j < n_elements_key; j++)
{
cJSON *element_key = cJSON_GetArrayItem(elements_key_array, j);
struct ipfix_elements_hash *elements_array_item = NULL;
HASH_FIND_STR(elements_array_hash, element_key->valuestring, elements_array_item);
if (elements_array_item == NULL)
{
elements_array_item = (struct ipfix_elements_hash *)calloc(1, sizeof(struct ipfix_elements_hash));
elements_array_item->name = (char *)calloc(strlen(element_key->valuestring) + 1, sizeof(char));
memcpy(elements_array_item->name, element_key->valuestring, strlen(element_key->valuestring));
HASH_ADD_STR(elements_array_hash, name, elements_array_item);
elements_array_item->elements_array = ipfix_elements_array_init(root, element_key->valuestring, PEN_number);
}
if (elements_array_item->elements_array == NULL)
{
goto error;
}
utarray_concat(template_item.elements_array, elements_array_item->elements_array); // merge elements_array
}
utarray_push_back(templates_array, &template_item);
}
ipfix_elements_array_hash_destroy(elements_array_hash);
size_t sz_blob = 0;
char *template_blob = NULL;
ret = ipfix_template_payload_init(templates_array, &template_blob, &sz_blob);
if (ret == -1 || sz_blob == 0 || template_blob == NULL)
{
goto error;
}
struct ipfix_worker_context *worker_context_array = (struct ipfix_worker_context *)calloc(n_worker, sizeof(struct ipfix_worker_context));
for (size_t i = 0; i < n_worker; i++)
{
worker_context_array[i].source_id = (domain_id << 16) | i;
worker_context_array[i].sequence = 0;
worker_context_array[i].template_blob_length = sz_blob;
worker_context_array[i].template_blob = (char *)calloc(sz_blob, sizeof(char));
memcpy(worker_context_array[i].template_blob, template_blob, sz_blob);
struct ipfix_message_head *p_message_head = (struct ipfix_message_head *)(worker_context_array[i].template_blob);
p_message_head->version = htons(version);
p_message_head->length = htons(sz_blob);
p_message_head->domain_id = htonl(worker_context_array[i].source_id);
}
struct ipfix_exporter_schema *_instance = (struct ipfix_exporter_schema *)calloc(1, sizeof(struct ipfix_exporter_schema));
_instance->version = version;
_instance->PEN_number = PEN_number;
_instance->templates_refresh_interval_s = refresh_interval_s;
_instance->domain_id = domain_id;
_instance->n_worker = n_worker;
_instance->templates_array = templates_array;
_instance->worker_context_array = worker_context_array;
free(template_blob);
cJSON_Delete(root);
free(json_str);
json_str = NULL;
return _instance;
error:
utarray_free(templates_array);
cJSON_Delete(root);
free(json_str);
json_str = NULL;
return NULL;
}
int utable_ipfix_template_get(struct ipfix_exporter_schema *instance, const char *template_name)
{
if (instance == NULL || template_name == NULL)
{
return -1;
}
size_t n_template = utarray_len(instance->templates_array);
for (size_t i = 0; i < n_template; i++)
{
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(instance->templates_array, i);
if (strcmp(template_item->name, template_name) == 0)
{
return i;
}
}
return -1;
}
char *ipfix_data_interger_serialize(char *buff, size_t *offset, size_t *buff_sz, int64_t value, size_t sz_value)
{
if (*offset + sz_value >= *buff_sz)
{
char *new_buff = (char *)realloc(buff, *offset + sz_value + IPFIX_BUFF_MAX_SIZE);
if (new_buff == NULL)
{
free(buff);
return NULL;
}
buff = new_buff;
*buff_sz = *offset + sz_value + IPFIX_BUFF_MAX_SIZE;
}
switch (sz_value)
{
case 1:
*(uint8_t *)(buff + *offset) = (uint8_t)value;
*offset += 1;
break;
case 2:
*(uint16_t *)(buff + *offset) = htons((uint16_t)value);
*offset += 2;
break;
case 4:
*(uint32_t *)(buff + *offset) = htonl((uint32_t)value);
*offset += 4;
break;
case 8:
*(uint64_t *)(buff + *offset) = htobe64((uint64_t)value);
*offset += 8;
break;
}
return buff;
}
char *ipfix_data_variable_serialize(char *buff, size_t *offset, size_t *buff_sz, char *value, size_t sz_value)
{
if (*offset + sz_value + 3 >= *buff_sz) // 3 means variable_string length > 255, need 3 bytes to describe length
{
char *new_buff = (char *)realloc(buff, *offset + sz_value + IPFIX_BUFF_MAX_SIZE);
if (new_buff == NULL)
{
free(buff);
return NULL;
}
buff = new_buff;
*buff_sz = *offset + sz_value + IPFIX_BUFF_MAX_SIZE;
}
if (sz_value == 0 || value == NULL) // value == NULL need 1 byte to describe length
{
*(uint8_t *)(buff + *offset) = 0;
*offset += 1;
}
else if (sz_value < 255)
{
*(uint8_t *)(buff + *offset) = (uint8_t)sz_value;
*offset += 1;
memcpy(buff + *offset, value, sz_value);
*offset += sz_value;
}
else // >= 255
{
*(uint8_t *)(buff + *offset) = 255;
*offset += 1;
*(uint16_t *)(buff + *offset) = htons(sz_value);
*offset += 2;
memcpy(buff + *offset, value, sz_value);
*offset += sz_value;
}
return buff;
}
int utable_ipfix_data_flow_exporter(const struct utable *table, struct ipfix_exporter_schema *instance, int template_id, uint16_t worker_id, char **blob, size_t *blob_len)
{
if (table == NULL || instance == NULL || worker_id >= instance->n_worker || template_id < 0 || template_id >= (int)utarray_len(instance->templates_array))
{
return -1;
}
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(instance->templates_array, (unsigned int)template_id);
if (template_item == NULL)
{
return -1;
}
size_t offset = 0;
size_t n_elements = utarray_len(template_item->elements_array);
size_t buff_sz = IPFIX_BUFF_MAX_SIZE;
char *buff = (char *)calloc(IPFIX_BUFF_MAX_SIZE, sizeof(char));
offset += IPFIX_MESSAGE_HEAD_LEN; // skip ipfix_message_head
*(uint16_t *)(buff + offset) = htons(template_item->template_id); // data_flow_set_id
offset += sizeof(uint16_t); // skip data_flow_set_id
offset += sizeof(uint16_t); // skip data_flow_set_length
for (size_t i = 0; i < n_elements; i++)
{
struct ipfix_element *p_element = (struct ipfix_element *)utarray_eltptr(template_item->elements_array, i);
enum utable_value_type value_type = utable_get_value_type(table, p_element->name);
switch (value_type)
{
case utable_value_type_cstring:
case utable_value_type_blob:
{
char *value = NULL;
size_t sz_value = 0;
utable_get0_cstring_value(table, p_element->name, &value, &sz_value);
buff = ipfix_data_variable_serialize(buff, &offset, &buff_sz, value, sz_value);
break;
}
case utable_value_type_integer:
{
int64_t value = 0;
utable_get0_integer_value(table, p_element->name, &value);
buff = ipfix_data_interger_serialize(buff, &offset, &buff_sz, value, p_element->value_length);
break;
}
case utable_value_type_integer_array:
{
size_t n_array = 0;
int64_t *value_array = NULL;
utable_get0_integer_value_array(table, p_element->name, &value_array, &n_array);
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *arr = yyjson_mut_arr_with_sint64(doc, value_array, n_array);
yyjson_mut_doc_set_root(doc, arr);
char *value_array_str = yyjson_mut_write(doc, 0, NULL);
if(value_array_str)
{
buff = ipfix_data_variable_serialize(buff, &offset, &buff_sz, value_array_str,
strlen(value_array_str));
yyjson_mut_doc_free(doc);
free(value_array_str);
}
break;
}
case utable_value_type_cstring_array:
{
size_t n_array = 0;
char **value_array = NULL;
size_t *value_sz = NULL;
utable_get0_cstring_value_array(table, p_element->name, &value_array, &value_sz, &n_array);
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *arr = yyjson_mut_arr_with_strn(doc, (const char **)value_array, value_sz,n_array);
yyjson_mut_doc_set_root(doc, arr);
char *value_array_str = yyjson_mut_write(doc, 0, NULL);
if (value_array_str)
{
buff = ipfix_data_variable_serialize(buff, &offset, &buff_sz, value_array_str,
strlen(value_array_str));
yyjson_mut_doc_free(doc);
free(value_array_str);
}
break;
}
default: // utable_value_type_undefined, ipfix append 0
{
switch (p_element->type)
{
case IPFIX_UNSIGNED8:
case IPFIX_UNSIGNED16:
case IPFIX_UNSIGNED32:
case IPFIX_UNSIGNED64:
buff = ipfix_data_interger_serialize(buff, &offset, &buff_sz, 0, p_element->value_length);
break;
case IPFIX_VARIABLE_STRING:
buff = ipfix_data_variable_serialize(buff, &offset, &buff_sz, NULL, 0);
break;
default:
{
free(buff);
buff = NULL;
return -1;
}
}
break;
}
}
}
*(uint16_t *)(buff + IPFIX_MESSAGE_HEAD_LEN + sizeof(uint16_t)) = htons(offset - IPFIX_MESSAGE_HEAD_LEN); // data_flow_set_length
struct ipfix_message_head *p_message_head = (struct ipfix_message_head *)(buff);
p_message_head->version = htons(instance->version);
p_message_head->length = htons(offset);
p_message_head->exporttime = htonl(time(NULL)); // time_t is long int, but exporttime is int
p_message_head->ipfix_message_sequence = htonl(instance->worker_context_array[worker_id].sequence++);
p_message_head->domain_id = htonl(instance->worker_context_array[worker_id].source_id);
*blob = buff;
*blob_len = offset;
return 0;
}
const char *utable_ipfix_template_flow_get0(struct ipfix_exporter_schema *instance, uint16_t worker_id, size_t *blob_len)
{
if (instance == NULL || instance->worker_context_array == NULL || worker_id >= instance->n_worker)
{
return NULL;
}
struct ipfix_message_head *p_message_head = (struct ipfix_message_head *)(instance->worker_context_array[worker_id].template_blob);
p_message_head->exporttime = htonl(time(NULL)); // time_t is long int, but exporttime is int
p_message_head->ipfix_message_sequence = htonl(instance->worker_context_array[worker_id].sequence); // template sequence is not increase
*blob_len = instance->worker_context_array[worker_id].template_blob_length;
return instance->worker_context_array[worker_id].template_blob;
}
uint32_t utable_ipfix_template_flow_refresh_interval_s(struct ipfix_exporter_schema *ipfix_schema)
{
return ipfix_schema->templates_refresh_interval_s;
}

6
deps/utable/version.map vendored Normal file
View File

@@ -0,0 +1,6 @@
{
global:
utable_*;
local:
*; /* Hide all other symbols */
};