Big initial commit: States, simulation, presentation, menus, some art.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -36,6 +36,7 @@
|
|||||||
*.i*86
|
*.i*86
|
||||||
*.x86_64
|
*.x86_64
|
||||||
*.hex
|
*.hex
|
||||||
|
build
|
||||||
|
|
||||||
# Debug files
|
# Debug files
|
||||||
*.dSYM/
|
*.dSYM/
|
||||||
|
|||||||
60
Makefile
Normal file
60
Makefile
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
CC := gcc
|
||||||
|
SRC_DIR := src
|
||||||
|
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
|
||||||
|
ifeq ($(UNAME_S), Linux)
|
||||||
|
PLATFORM := linux
|
||||||
|
else ifeq ($(OS), Windows_NT)
|
||||||
|
PLATFORM := windows
|
||||||
|
else
|
||||||
|
PLATFORM := unknown
|
||||||
|
endif
|
||||||
|
|
||||||
|
BUILD_DIR := build/$(PLATFORM)
|
||||||
|
CFLAGS := -g -Isrc -Wextra -MMD -MP -Wall -pedantic
|
||||||
|
LDFLAGS := -flto -Wl,--gc-sections
|
||||||
|
|
||||||
|
LIB_DIR := $(abspath libs/$(PLATFORM))
|
||||||
|
CFLAGS += -I$(LIB_DIR)
|
||||||
|
LDFLAGS += -L$(LIB_DIR)
|
||||||
|
|
||||||
|
ifeq ($(PLATFORM), linux)
|
||||||
|
LIBS += -lraylib -lm -lraygui
|
||||||
|
endif
|
||||||
|
|
||||||
|
SRC := $(shell find -L $(SRC_DIR) -type f -name '*.c')
|
||||||
|
|
||||||
|
OBJ := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRC))
|
||||||
|
DEP := $(OBJ:.o=.d)
|
||||||
|
|
||||||
|
ifeq ($(PLATFORM), windows)
|
||||||
|
BIN := $(BUILD_DIR)/main.exe
|
||||||
|
endif
|
||||||
|
ifeq ($(PLATFORM), linux)
|
||||||
|
BIN := $(BUILD_DIR)/main
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
all: $(BIN)
|
||||||
|
|
||||||
|
run: $(BIN)
|
||||||
|
@cd $(BUILD_DIR) && ./$(notdir $(BIN))
|
||||||
|
|
||||||
|
$(BIN): $(OBJ)
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
@cp -r $(SRC_DIR)/assets $(BUILD_DIR)
|
||||||
|
@cp -r libs $(BUILD_DIR)
|
||||||
|
@$(CC) $(LDFLAGS) $(OBJ) -o $@ $(LIBS)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||||
|
@echo "Compiling $<..."
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
|
-include $(DEP)
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
731
src/MultiplayerApi.c
Normal file
731
src/MultiplayerApi.c
Normal file
@@ -0,0 +1,731 @@
|
|||||||
|
#include "MultiplayerApi.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
typedef struct ListenerNode {
|
||||||
|
int id;
|
||||||
|
MultiplayerListener cb;
|
||||||
|
void *user_data;
|
||||||
|
struct ListenerNode *next;
|
||||||
|
} ListenerNode;
|
||||||
|
|
||||||
|
typedef struct ListenerSnapshot {
|
||||||
|
MultiplayerListener cb;
|
||||||
|
void *user_data;
|
||||||
|
} ListenerSnapshot;
|
||||||
|
|
||||||
|
struct MultiplayerApi {
|
||||||
|
char *server_host;
|
||||||
|
uint16_t server_port;
|
||||||
|
int sockfd;
|
||||||
|
char *session_id;
|
||||||
|
|
||||||
|
pthread_t recv_thread;
|
||||||
|
int recv_thread_started;
|
||||||
|
int running;
|
||||||
|
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
ListenerNode *listeners;
|
||||||
|
int next_listener_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int connect_to_server(const char *host, uint16_t port);
|
||||||
|
static int ensure_connected(MultiplayerApi *api);
|
||||||
|
static int send_all(int fd, const char *buf, size_t len);
|
||||||
|
static int send_json_line(MultiplayerApi *api, json_t *obj); /* tar över ägarskap */
|
||||||
|
static int read_line(int fd, char **out_line);
|
||||||
|
static void *recv_thread_main(void *arg);
|
||||||
|
static void process_line(MultiplayerApi *api, const char *line);
|
||||||
|
static int start_recv_thread(MultiplayerApi *api);
|
||||||
|
|
||||||
|
MultiplayerApi *mp_api_create(const char *server_host, uint16_t server_port) {
|
||||||
|
MultiplayerApi *api = (MultiplayerApi *)calloc(1, sizeof(MultiplayerApi));
|
||||||
|
if (!api) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_host) {
|
||||||
|
api->server_host = strdup(server_host);
|
||||||
|
} else {
|
||||||
|
api->server_host = strdup("127.0.0.1");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!api->server_host) {
|
||||||
|
free(api);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
api->server_port = server_port;
|
||||||
|
api->sockfd = -1;
|
||||||
|
api->session_id = NULL;
|
||||||
|
api->recv_thread_started = 0;
|
||||||
|
api->running = 0;
|
||||||
|
api->listeners = NULL;
|
||||||
|
api->next_listener_id = 1;
|
||||||
|
|
||||||
|
if (pthread_mutex_init(&api->lock, NULL) != 0) {
|
||||||
|
free(api->server_host);
|
||||||
|
free(api);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mp_api_destroy(MultiplayerApi *api) {
|
||||||
|
if (!api) return;
|
||||||
|
|
||||||
|
if (api->recv_thread_started && api->sockfd >= 0) {
|
||||||
|
shutdown(api->sockfd, SHUT_RDWR);
|
||||||
|
pthread_join(api->recv_thread, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (api->sockfd >= 0) {
|
||||||
|
close(api->sockfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&api->lock);
|
||||||
|
ListenerNode *node = api->listeners;
|
||||||
|
api->listeners = NULL;
|
||||||
|
pthread_mutex_unlock(&api->lock);
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
ListenerNode *next = node->next;
|
||||||
|
free(node);
|
||||||
|
node = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (api->session_id) {
|
||||||
|
free(api->session_id);
|
||||||
|
}
|
||||||
|
if (api->server_host) {
|
||||||
|
free(api->server_host);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&api->lock);
|
||||||
|
free(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mp_api_host(MultiplayerApi *api,
|
||||||
|
char **out_session,
|
||||||
|
char **out_clientId,
|
||||||
|
json_t **out_data) {
|
||||||
|
if (!api) return MP_API_ERR_ARGUMENT;
|
||||||
|
if (api->session_id) return MP_API_ERR_STATE;
|
||||||
|
|
||||||
|
int rc = ensure_connected(api);
|
||||||
|
if (rc != MP_API_OK) return rc;
|
||||||
|
|
||||||
|
json_t *root = json_object();
|
||||||
|
if (!root) return MP_API_ERR_IO;
|
||||||
|
|
||||||
|
json_object_set_new(root, "session", json_null());
|
||||||
|
json_object_set_new(root, "cmd", json_string("host"));
|
||||||
|
json_object_set_new(root, "data", json_object());
|
||||||
|
|
||||||
|
rc = send_json_line(api, root);
|
||||||
|
if (rc != MP_API_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
rc = read_line(api->sockfd, &line);
|
||||||
|
if (rc != MP_API_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_error_t jerr;
|
||||||
|
json_t *resp = json_loads(line, 0, &jerr);
|
||||||
|
free(line);
|
||||||
|
if (!resp || !json_is_object(resp)) {
|
||||||
|
if (resp) json_decref(resp);
|
||||||
|
return MP_API_ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *cmd_val = json_object_get(resp, "cmd");
|
||||||
|
if (!json_is_string(cmd_val) || strcmp(json_string_value(cmd_val), "host") != 0) {
|
||||||
|
json_decref(resp);
|
||||||
|
return MP_API_ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *sess_val = json_object_get(resp, "session");
|
||||||
|
if (!json_is_string(sess_val)) {
|
||||||
|
json_decref(resp);
|
||||||
|
return MP_API_ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
const char *session = json_string_value(sess_val);
|
||||||
|
|
||||||
|
json_t *cid_val = json_object_get(resp, "clientId");
|
||||||
|
const char *clientId = json_is_string(cid_val) ? json_string_value(cid_val) : NULL;
|
||||||
|
|
||||||
|
json_t *data_val = json_object_get(resp, "data");
|
||||||
|
json_t *data_obj = NULL;
|
||||||
|
if (json_is_object(data_val)) {
|
||||||
|
data_obj = data_val;
|
||||||
|
json_incref(data_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
api->session_id = strdup(session);
|
||||||
|
if (!api->session_id) {
|
||||||
|
if (data_obj) json_decref(data_obj);
|
||||||
|
json_decref(resp);
|
||||||
|
return MP_API_ERR_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_session) {
|
||||||
|
*out_session = strdup(session);
|
||||||
|
}
|
||||||
|
if (out_clientId && clientId) {
|
||||||
|
*out_clientId = strdup(clientId);
|
||||||
|
}
|
||||||
|
if (out_data) {
|
||||||
|
*out_data = data_obj;
|
||||||
|
} else if (data_obj) {
|
||||||
|
json_decref(data_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_decref(resp);
|
||||||
|
|
||||||
|
rc = start_recv_thread(api);
|
||||||
|
if (rc != MP_API_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MP_API_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mp_api_list(MultiplayerApi *api, json_t **out_list)
|
||||||
|
{
|
||||||
|
if (!api || !out_list) return MP_API_ERR_ARGUMENT;
|
||||||
|
|
||||||
|
int rc = ensure_connected(api);
|
||||||
|
if (rc != MP_API_OK) return rc;
|
||||||
|
|
||||||
|
json_t *root = json_object();
|
||||||
|
if (!root) return MP_API_ERR_IO;
|
||||||
|
|
||||||
|
json_object_set_new(root, "cmd", json_string("list"));
|
||||||
|
|
||||||
|
rc = send_json_line(api, root);
|
||||||
|
if (rc != MP_API_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
rc = read_line(api->sockfd, &line);
|
||||||
|
if (rc != MP_API_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Received line: %s\n", line); // Debug print
|
||||||
|
|
||||||
|
json_error_t jerr;
|
||||||
|
json_t *resp = json_loads(line, 0, &jerr);
|
||||||
|
free(line);
|
||||||
|
if (!resp || !json_is_object(resp)) {
|
||||||
|
if (resp) json_decref(resp);
|
||||||
|
return MP_API_ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *cmd_val = json_object_get(resp, "cmd");
|
||||||
|
if (!json_is_string(cmd_val) || strcmp(json_string_value(cmd_val), "list") != 0) {
|
||||||
|
json_decref(resp);
|
||||||
|
return MP_API_ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *list_val = json_object_get(resp, "data");
|
||||||
|
if (!json_is_object(list_val)) {
|
||||||
|
json_decref(resp);
|
||||||
|
return MP_API_ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *list_obj = json_object_get(list_val, "list");
|
||||||
|
if (!json_is_array(list_obj)) {
|
||||||
|
json_decref(resp);
|
||||||
|
return MP_API_ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_list = list_obj;
|
||||||
|
json_incref(*out_list);
|
||||||
|
|
||||||
|
json_decref(resp);
|
||||||
|
return MP_API_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mp_api_join(MultiplayerApi *api,
|
||||||
|
const char *sessionId,
|
||||||
|
json_t *data,
|
||||||
|
char **out_session,
|
||||||
|
char **out_clientId,
|
||||||
|
json_t **out_data) {
|
||||||
|
if (!api || !sessionId) return MP_API_ERR_ARGUMENT;
|
||||||
|
if (api->session_id) return MP_API_ERR_STATE;
|
||||||
|
|
||||||
|
int rc = ensure_connected(api);
|
||||||
|
if (rc != MP_API_OK) return rc;
|
||||||
|
|
||||||
|
json_t *root = json_object();
|
||||||
|
if (!root) return MP_API_ERR_IO;
|
||||||
|
|
||||||
|
json_object_set_new(root, "session", json_string(sessionId));
|
||||||
|
json_object_set_new(root, "cmd", json_string("join"));
|
||||||
|
|
||||||
|
json_t *data_copy;
|
||||||
|
if (data && json_is_object(data)) {
|
||||||
|
data_copy = json_deep_copy(data);
|
||||||
|
} else {
|
||||||
|
data_copy = json_object();
|
||||||
|
}
|
||||||
|
json_object_set_new(root, "data", data_copy);
|
||||||
|
|
||||||
|
rc = send_json_line(api, root);
|
||||||
|
if (rc != MP_API_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
rc = read_line(api->sockfd, &line);
|
||||||
|
if (rc != MP_API_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_error_t jerr;
|
||||||
|
json_t *resp = json_loads(line, 0, &jerr);
|
||||||
|
free(line);
|
||||||
|
if (!resp || !json_is_object(resp)) {
|
||||||
|
if (resp) json_decref(resp);
|
||||||
|
return MP_API_ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *cmd_val = json_object_get(resp, "cmd");
|
||||||
|
if (!json_is_string(cmd_val) || strcmp(json_string_value(cmd_val), "join") != 0) {
|
||||||
|
json_decref(resp);
|
||||||
|
return MP_API_ERR_PROTOCOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *sess_val = json_object_get(resp, "session");
|
||||||
|
const char *session = NULL;
|
||||||
|
if (json_is_string(sess_val)) {
|
||||||
|
session = json_string_value(sess_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *cid_val = json_object_get(resp, "clientId");
|
||||||
|
const char *clientId = json_is_string(cid_val) ? json_string_value(cid_val) : NULL;
|
||||||
|
|
||||||
|
json_t *data_val = json_object_get(resp, "data");
|
||||||
|
json_t *data_obj = NULL;
|
||||||
|
if (json_is_object(data_val)) {
|
||||||
|
data_obj = data_val;
|
||||||
|
json_incref(data_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
int joinAccepted = 1;
|
||||||
|
if (data_obj) {
|
||||||
|
json_t *status_val = json_object_get(data_obj, "status");
|
||||||
|
if (json_is_string(status_val) &&
|
||||||
|
strcmp(json_string_value(status_val), "error") == 0) {
|
||||||
|
joinAccepted = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (joinAccepted && session) {
|
||||||
|
api->session_id = strdup(session);
|
||||||
|
if (!api->session_id) {
|
||||||
|
if (data_obj) json_decref(data_obj);
|
||||||
|
json_decref(resp);
|
||||||
|
return MP_API_ERR_IO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_session && session) {
|
||||||
|
*out_session = strdup(session);
|
||||||
|
}
|
||||||
|
if (out_clientId && clientId) {
|
||||||
|
*out_clientId = strdup(clientId);
|
||||||
|
}
|
||||||
|
if (out_data) {
|
||||||
|
*out_data = data_obj;
|
||||||
|
} else if (data_obj) {
|
||||||
|
json_decref(data_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_decref(resp);
|
||||||
|
|
||||||
|
if (joinAccepted && api->session_id) {
|
||||||
|
rc = start_recv_thread(api);
|
||||||
|
if (rc != MP_API_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return joinAccepted ? MP_API_OK : MP_API_ERR_REJECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mp_api_game(MultiplayerApi *api, json_t *data) {
|
||||||
|
if (!api || !data) return MP_API_ERR_ARGUMENT;
|
||||||
|
if (api->sockfd < 0 || !api->session_id) return MP_API_ERR_STATE;
|
||||||
|
|
||||||
|
json_t *root = json_object();
|
||||||
|
if (!root) return MP_API_ERR_IO;
|
||||||
|
|
||||||
|
json_object_set_new(root, "session", json_string(api->session_id));
|
||||||
|
json_object_set_new(root, "cmd", json_string("game"));
|
||||||
|
|
||||||
|
json_t *data_copy;
|
||||||
|
if (json_is_object(data)) {
|
||||||
|
data_copy = json_deep_copy(data);
|
||||||
|
} else {
|
||||||
|
data_copy = json_object();
|
||||||
|
}
|
||||||
|
json_object_set_new(root, "data", data_copy);
|
||||||
|
|
||||||
|
return send_json_line(api, root);
|
||||||
|
}
|
||||||
|
|
||||||
|
int mp_api_listen(MultiplayerApi *api,
|
||||||
|
MultiplayerListener cb,
|
||||||
|
void *user_data) {
|
||||||
|
if (!api || !cb) return -1;
|
||||||
|
|
||||||
|
ListenerNode *node = (ListenerNode *)malloc(sizeof(ListenerNode));
|
||||||
|
if (!node) return -1;
|
||||||
|
|
||||||
|
node->cb = cb;
|
||||||
|
node->user_data = user_data;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&api->lock);
|
||||||
|
node->id = api->next_listener_id++;
|
||||||
|
node->next = api->listeners;
|
||||||
|
api->listeners = node;
|
||||||
|
pthread_mutex_unlock(&api->lock);
|
||||||
|
|
||||||
|
return node->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mp_api_unlisten(MultiplayerApi *api, int listener_id) {
|
||||||
|
if (!api || listener_id <= 0) return;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&api->lock);
|
||||||
|
ListenerNode *prev = NULL;
|
||||||
|
ListenerNode *cur = api->listeners;
|
||||||
|
while (cur) {
|
||||||
|
if (cur->id == listener_id) {
|
||||||
|
if (prev) {
|
||||||
|
prev->next = cur->next;
|
||||||
|
} else {
|
||||||
|
api->listeners = cur->next;
|
||||||
|
}
|
||||||
|
free(cur);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
prev = cur;
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&api->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Interna hjälpfunktioner --- */
|
||||||
|
|
||||||
|
static int connect_to_server(const char *host, uint16_t port) {
|
||||||
|
if (!host) host = "127.0.0.1";
|
||||||
|
|
||||||
|
char port_str[16];
|
||||||
|
snprintf(port_str, sizeof(port_str), "%u", (unsigned int)port);
|
||||||
|
|
||||||
|
struct addrinfo hints;
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = AF_UNSPEC; /* IPv4 eller IPv6 */
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
struct addrinfo *res = NULL;
|
||||||
|
int err = getaddrinfo(host, port_str, &hints, &res);
|
||||||
|
if (err != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = -1;
|
||||||
|
for (struct addrinfo *rp = res; rp != NULL; rp = rp->ai_next) {
|
||||||
|
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||||
|
if (fd == -1) continue;
|
||||||
|
if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ensure_connected(MultiplayerApi *api) {
|
||||||
|
if (!api) return MP_API_ERR_ARGUMENT;
|
||||||
|
if (api->sockfd >= 0) return MP_API_OK;
|
||||||
|
|
||||||
|
int fd = connect_to_server(api->server_host, api->server_port);
|
||||||
|
if (fd < 0) {
|
||||||
|
return MP_API_ERR_CONNECT;
|
||||||
|
}
|
||||||
|
api->sockfd = fd;
|
||||||
|
return MP_API_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_all(int fd, const char *buf, size_t len) {
|
||||||
|
size_t sent = 0;
|
||||||
|
while (sent < len) {
|
||||||
|
ssize_t n = send(fd, buf + sent, len - sent, 0);
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sent += (size_t)n;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_json_line(MultiplayerApi *api, json_t *obj) {
|
||||||
|
if (!api || api->sockfd < 0 || !obj) return MP_API_ERR_ARGUMENT;
|
||||||
|
|
||||||
|
char *text = json_dumps(obj, JSON_COMPACT);
|
||||||
|
if (!text) {
|
||||||
|
json_decref(obj);
|
||||||
|
return MP_API_ERR_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(text);
|
||||||
|
int fd = api->sockfd;
|
||||||
|
|
||||||
|
int rc = 0;
|
||||||
|
if (send_all(fd, text, len) != 0 || send_all(fd, "\n", 1) != 0) {
|
||||||
|
rc = MP_API_ERR_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(text);
|
||||||
|
json_decref(obj);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_line(int fd, char **out_line) {
|
||||||
|
if (!out_line) return MP_API_ERR_ARGUMENT;
|
||||||
|
|
||||||
|
size_t cap = 256;
|
||||||
|
size_t len = 0;
|
||||||
|
char *buf = (char *)malloc(cap);
|
||||||
|
if (!buf) return MP_API_ERR_IO;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
char c;
|
||||||
|
ssize_t n = recv(fd, &c, 1, 0);
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
free(buf);
|
||||||
|
return MP_API_ERR_IO;
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
|
free(buf);
|
||||||
|
return MP_API_ERR_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '\n') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len + 1 >= cap) {
|
||||||
|
cap *= 2;
|
||||||
|
char *tmp = (char *)realloc(buf, cap);
|
||||||
|
if (!tmp) {
|
||||||
|
free(buf);
|
||||||
|
return MP_API_ERR_IO;
|
||||||
|
}
|
||||||
|
buf = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[len++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[len] = '\0';
|
||||||
|
*out_line = buf;
|
||||||
|
return MP_API_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void process_line(MultiplayerApi *api, const char *line) {
|
||||||
|
if (!api || !line || !*line) return;
|
||||||
|
|
||||||
|
json_error_t jerr;
|
||||||
|
json_t *root = json_loads(line, 0, &jerr);
|
||||||
|
if (!root || !json_is_object(root)) {
|
||||||
|
if (root) json_decref(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *cmd_val = json_object_get(root, "cmd");
|
||||||
|
if (!json_is_string(cmd_val)) {
|
||||||
|
json_decref(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cmd = json_string_value(cmd_val);
|
||||||
|
if (!cmd) {
|
||||||
|
json_decref(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(cmd, "joined") != 0 &&
|
||||||
|
strcmp(cmd, "leaved") != 0 &&
|
||||||
|
strcmp(cmd, "game") != 0) {
|
||||||
|
json_decref(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_int_t msgId = 0;
|
||||||
|
json_t *mid_val = json_object_get(root, "messageId");
|
||||||
|
if (json_is_integer(mid_val)) {
|
||||||
|
msgId = json_integer_value(mid_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *clientId = NULL;
|
||||||
|
json_t *cid_val = json_object_get(root, "clientId");
|
||||||
|
if (json_is_string(cid_val)) {
|
||||||
|
clientId = json_string_value(cid_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *data_val = json_object_get(root, "data");
|
||||||
|
json_t *data_obj;
|
||||||
|
if (json_is_object(data_val)) {
|
||||||
|
data_obj = data_val;
|
||||||
|
json_incref(data_obj);
|
||||||
|
} else {
|
||||||
|
data_obj = json_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&api->lock);
|
||||||
|
int count = 0;
|
||||||
|
ListenerNode *node = api->listeners;
|
||||||
|
while (node) {
|
||||||
|
if (node->cb) count++;
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
pthread_mutex_unlock(&api->lock);
|
||||||
|
json_decref(data_obj);
|
||||||
|
json_decref(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListenerSnapshot *snapshot = (ListenerSnapshot *)malloc(sizeof(ListenerSnapshot) * count);
|
||||||
|
if (!snapshot) {
|
||||||
|
pthread_mutex_unlock(&api->lock);
|
||||||
|
json_decref(data_obj);
|
||||||
|
json_decref(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
node = api->listeners;
|
||||||
|
while (node) {
|
||||||
|
if (node->cb) {
|
||||||
|
snapshot[idx].cb = node->cb;
|
||||||
|
snapshot[idx].user_data = node->user_data;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
node = node->next;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&api->lock);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
snapshot[i].cb(cmd, (int64_t)msgId, clientId, data_obj, snapshot[i].user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(snapshot);
|
||||||
|
json_decref(data_obj);
|
||||||
|
json_decref(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *recv_thread_main(void *arg) {
|
||||||
|
MultiplayerApi *api = (MultiplayerApi *)arg;
|
||||||
|
char buffer[1024];
|
||||||
|
char *acc = NULL;
|
||||||
|
size_t acc_len = 0;
|
||||||
|
size_t acc_cap = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
ssize_t n = recv(api->sockfd, buffer, sizeof(buffer), 0);
|
||||||
|
if (n <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ssize_t i = 0; i < n; ++i) {
|
||||||
|
char ch = buffer[i];
|
||||||
|
if (ch == '\n') {
|
||||||
|
if (acc_len > 0) {
|
||||||
|
char *line = (char *)malloc(acc_len + 1);
|
||||||
|
if (!line) {
|
||||||
|
acc_len = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
memcpy(line, acc, acc_len);
|
||||||
|
line[acc_len] = '\0';
|
||||||
|
|
||||||
|
acc_len = 0;
|
||||||
|
process_line(api, line);
|
||||||
|
free(line);
|
||||||
|
} else {
|
||||||
|
acc_len = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (acc_len + 1 >= acc_cap) {
|
||||||
|
size_t new_cap = acc_cap == 0 ? 256 : acc_cap * 2;
|
||||||
|
char *tmp = (char *)realloc(acc, new_cap);
|
||||||
|
if (!tmp) {
|
||||||
|
free(acc);
|
||||||
|
acc = NULL;
|
||||||
|
acc_len = 0;
|
||||||
|
acc_cap = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
acc = tmp;
|
||||||
|
acc_cap = new_cap;
|
||||||
|
}
|
||||||
|
acc[acc_len++] = ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acc) {
|
||||||
|
free(acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int start_recv_thread(MultiplayerApi *api) {
|
||||||
|
if (!api) return MP_API_ERR_ARGUMENT;
|
||||||
|
if (api->recv_thread_started) {
|
||||||
|
return MP_API_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
api->running = 1;
|
||||||
|
int rc = pthread_create(&api->recv_thread, NULL, recv_thread_main, api);
|
||||||
|
if (rc != 0) {
|
||||||
|
api->running = 0;
|
||||||
|
return MP_API_ERR_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
api->recv_thread_started = 1;
|
||||||
|
return MP_API_OK;
|
||||||
|
}
|
||||||
88
src/MultiplayerApi.h
Normal file
88
src/MultiplayerApi.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#ifndef MULTIPLAYER_API_H
|
||||||
|
#define MULTIPLAYER_API_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "jansson/jansson.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct MultiplayerApi MultiplayerApi;
|
||||||
|
|
||||||
|
/* Callback‑typ för inkommande events från servern. */
|
||||||
|
typedef void (*MultiplayerListener)(
|
||||||
|
const char *event, /* "joined", "leaved", "game" */
|
||||||
|
int64_t messageId, /* sekventiellt meddelande‑ID (från host) */
|
||||||
|
const char *clientId, /* avsändarens klient‑ID (eller NULL) */
|
||||||
|
json_t *data, /* JSON‑objekt med godtycklig speldata */
|
||||||
|
void *user_data /* godtycklig pekare som skickas vidare */
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Returkoder */
|
||||||
|
enum {
|
||||||
|
MP_API_OK = 0,
|
||||||
|
MP_API_ERR_ARGUMENT = 1,
|
||||||
|
MP_API_ERR_STATE = 2,
|
||||||
|
MP_API_ERR_CONNECT = 3,
|
||||||
|
MP_API_ERR_PROTOCOL = 4,
|
||||||
|
MP_API_ERR_IO = 5,
|
||||||
|
MP_API_ERR_REJECTED = 6 /* t.ex. ogiltigt sessions‑ID vid join */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Skapar en ny API‑instans. Returnerar NULL vid fel. */
|
||||||
|
MultiplayerApi *mp_api_create(const char *server_host, uint16_t server_port);
|
||||||
|
|
||||||
|
/* Stänger ner anslutning, stoppar mottagartråd och frigör minne. */
|
||||||
|
void mp_api_destroy(MultiplayerApi *api);
|
||||||
|
|
||||||
|
/* Hostar en ny session. Blockerar tills svar erhållits eller fel uppstår.
|
||||||
|
out_session / out_clientId pekar på nyallokerade strängar (malloc) som
|
||||||
|
anroparen ansvarar för att free:a. out_data (om ej NULL) får ett json_t*
|
||||||
|
med extra data från servern (anroparen ska json_decref när klart). */
|
||||||
|
int mp_api_host(MultiplayerApi *api,
|
||||||
|
char **out_session,
|
||||||
|
char **out_clientId,
|
||||||
|
json_t **out_data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hämtar en lista över tillgängliga publika sessioner.
|
||||||
|
Returnerar MP_API_OK vid framgång, annan felkod vid fel.
|
||||||
|
Anroparen ansvarar för att json_decref:a out_list när klar. */
|
||||||
|
int mp_api_list(MultiplayerApi *api,
|
||||||
|
json_t **out_list);
|
||||||
|
|
||||||
|
/* Går med i befintlig session.
|
||||||
|
sessionId: sessionskod (t.ex. "ABC123").
|
||||||
|
data: valfri JSON‑payload med spelarinformation (kan vara NULL).
|
||||||
|
out_* fungerar som i mp_api_host.
|
||||||
|
|
||||||
|
Returnerar:
|
||||||
|
- MP_API_OK vid lyckad join
|
||||||
|
- MP_API_ERR_REJECTED om servern svarar med status:error (t.ex. ogiltigt ID)
|
||||||
|
- annan felkod vid nätverks/protokoll‑fel.
|
||||||
|
*/
|
||||||
|
int mp_api_join(MultiplayerApi *api,
|
||||||
|
const char *sessionId,
|
||||||
|
json_t *data,
|
||||||
|
char **out_session,
|
||||||
|
char **out_clientId,
|
||||||
|
json_t **out_data);
|
||||||
|
|
||||||
|
/* Skickar ett "game"‑meddelande med godtycklig JSON‑data till sessionen. */
|
||||||
|
int mp_api_game(MultiplayerApi *api, json_t *data);
|
||||||
|
|
||||||
|
/* Registrerar en lyssnare för inkommande events.
|
||||||
|
Returnerar ett positivt listener‑ID, eller −1 vid fel. */
|
||||||
|
int mp_api_listen(MultiplayerApi *api,
|
||||||
|
MultiplayerListener cb,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
/* Avregistrerar lyssnare. Listener‑ID är värdet från mp_api_listen. */
|
||||||
|
void mp_api_unlisten(MultiplayerApi *api, int listener_id);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* MULTIPLAYER_API_H */
|
||||||
BIN
src/assets/sprites/spr_floor_grass.png
Normal file
BIN
src/assets/sprites/spr_floor_grass.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 643 B |
BIN
src/assets/sprites/spr_food_apple.png
Normal file
BIN
src/assets/sprites/spr_food_apple.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 653 B |
BIN
src/assets/sprites/spr_snake_body.png
Normal file
BIN
src/assets/sprites/spr_snake_body.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 592 B |
BIN
src/assets/sprites/spr_snake_head.png
Normal file
BIN
src/assets/sprites/spr_snake_head.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 605 B |
516
src/jansson/dump.c
Normal file
516
src/jansson/dump.c
Normal file
@@ -0,0 +1,516 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "jansson_private.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "jansson.h"
|
||||||
|
#include "strbuffer.h"
|
||||||
|
#include "utf.h"
|
||||||
|
|
||||||
|
#define MAX_INTEGER_STR_LENGTH 100
|
||||||
|
#define MAX_REAL_STR_LENGTH 100
|
||||||
|
|
||||||
|
#define FLAGS_TO_INDENT(f) ((f) & 0x1F)
|
||||||
|
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
|
||||||
|
|
||||||
|
struct buffer {
|
||||||
|
const size_t size;
|
||||||
|
size_t used;
|
||||||
|
char *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int dump_to_strbuffer(const char *buffer, size_t size, void *data)
|
||||||
|
{
|
||||||
|
return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dump_to_buffer(const char *buffer, size_t size, void *data)
|
||||||
|
{
|
||||||
|
struct buffer *buf = (struct buffer *)data;
|
||||||
|
|
||||||
|
if(buf->used + size <= buf->size)
|
||||||
|
memcpy(&buf->data[buf->used], buffer, size);
|
||||||
|
|
||||||
|
buf->used += size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dump_to_file(const char *buffer, size_t size, void *data)
|
||||||
|
{
|
||||||
|
FILE *dest = (FILE *)data;
|
||||||
|
if(fwrite(buffer, size, 1, dest) != 1)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dump_to_fd(const char *buffer, size_t size, void *data)
|
||||||
|
{
|
||||||
|
(void)buffer;
|
||||||
|
(void)size;
|
||||||
|
(void)data;
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
int *dest = (int *)data;
|
||||||
|
if(write(*dest, buffer, size) == (ssize_t)size)
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 32 spaces (the maximum indentation size) */
|
||||||
|
#ifdef JSON_USE_TAB_INDENT
|
||||||
|
static const char JSON_ws[] = " ";
|
||||||
|
#else
|
||||||
|
static const char JSON_ws[] = " ";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump, void *data)
|
||||||
|
{
|
||||||
|
if(FLAGS_TO_INDENT(flags) > 0)
|
||||||
|
{
|
||||||
|
unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
|
||||||
|
|
||||||
|
if(dump("\n", 1, data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while(n_spaces > 0)
|
||||||
|
{
|
||||||
|
int cur_n = n_spaces < sizeof JSON_ws - 1 ? n_spaces : sizeof JSON_ws - 1;
|
||||||
|
|
||||||
|
if(dump(JSON_ws, cur_n, data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
n_spaces -= cur_n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(space && !(flags & JSON_COMPACT))
|
||||||
|
{
|
||||||
|
return dump(" ", 1, data);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
|
||||||
|
{
|
||||||
|
const char *pos, *end, *lim;
|
||||||
|
int32_t codepoint = 0;
|
||||||
|
|
||||||
|
if(dump("\"", 1, data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
end = pos = str;
|
||||||
|
lim = str + len;
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
const char *text;
|
||||||
|
char seq[13];
|
||||||
|
int length;
|
||||||
|
|
||||||
|
while(end < lim)
|
||||||
|
{
|
||||||
|
end = utf8_iterate(pos, lim - pos, &codepoint);
|
||||||
|
if(!end)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* mandatory escape or control char */
|
||||||
|
if(codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* slash */
|
||||||
|
if((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* non-ASCII */
|
||||||
|
if((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pos = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(pos != str) {
|
||||||
|
if(dump(str, pos - str, data))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(end == pos)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* handle \, /, ", and control codes */
|
||||||
|
length = 2;
|
||||||
|
switch(codepoint)
|
||||||
|
{
|
||||||
|
case '\\': text = "\\\\"; break;
|
||||||
|
case '\"': text = "\\\""; break;
|
||||||
|
case '\b': text = "\\b"; break;
|
||||||
|
case '\f': text = "\\f"; break;
|
||||||
|
case '\n': text = "\\n"; break;
|
||||||
|
case '\r': text = "\\r"; break;
|
||||||
|
case '\t': text = "\\t"; break;
|
||||||
|
case '/': text = "\\/"; break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
/* codepoint is in BMP */
|
||||||
|
if(codepoint < 0x10000)
|
||||||
|
{
|
||||||
|
snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
|
||||||
|
length = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* not in BMP -> construct a UTF-16 surrogate pair */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int32_t first, last;
|
||||||
|
|
||||||
|
codepoint -= 0x10000;
|
||||||
|
first = 0xD800 | ((codepoint & 0xffc00) >> 10);
|
||||||
|
last = 0xDC00 | (codepoint & 0x003ff);
|
||||||
|
|
||||||
|
snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first, (unsigned int)last);
|
||||||
|
length = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
text = seq;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dump(text, length, data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
str = pos = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dump("\"", 1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_keys(const void *key1, const void *key2)
|
||||||
|
{
|
||||||
|
return strcmp(*(const char **)key1, *(const char **)key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
|
||||||
|
{
|
||||||
|
snprintf(key, key_size, "%p", json);
|
||||||
|
if (hashtable_get(parents, key))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return hashtable_set(parents, key, json_null());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_dump(const json_t *json, size_t flags, int depth,
|
||||||
|
hashtable_t *parents, json_dump_callback_t dump, void *data)
|
||||||
|
{
|
||||||
|
int embed = flags & JSON_EMBED;
|
||||||
|
|
||||||
|
flags &= ~JSON_EMBED;
|
||||||
|
|
||||||
|
if(!json)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
switch(json_typeof(json)) {
|
||||||
|
case JSON_NULL:
|
||||||
|
return dump("null", 4, data);
|
||||||
|
|
||||||
|
case JSON_TRUE:
|
||||||
|
return dump("true", 4, data);
|
||||||
|
|
||||||
|
case JSON_FALSE:
|
||||||
|
return dump("false", 5, data);
|
||||||
|
|
||||||
|
case JSON_INTEGER:
|
||||||
|
{
|
||||||
|
char buffer[MAX_INTEGER_STR_LENGTH];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
/*size = snprintf(buffer, MAX_INTEGER_STR_LENGTH,
|
||||||
|
"%" JSON_INTEGER_FORMAT,
|
||||||
|
json_integer_value(json));
|
||||||
|
*/
|
||||||
|
|
||||||
|
size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%" JSON_INTEGER_FORMAT, json_integer_value(json));
|
||||||
|
|
||||||
|
if(size < 0 || size >= MAX_INTEGER_STR_LENGTH)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return dump(buffer, size, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case JSON_REAL:
|
||||||
|
{
|
||||||
|
char buffer[MAX_REAL_STR_LENGTH];
|
||||||
|
int size;
|
||||||
|
double value = json_real_value(json);
|
||||||
|
|
||||||
|
size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
|
||||||
|
FLAGS_TO_PRECISION(flags));
|
||||||
|
if(size < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return dump(buffer, size, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case JSON_STRING:
|
||||||
|
return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
|
||||||
|
|
||||||
|
case JSON_ARRAY:
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
size_t i;
|
||||||
|
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
|
||||||
|
char key[2 + (sizeof(json) * 2) + 1];
|
||||||
|
|
||||||
|
/* detect circular references */
|
||||||
|
if (loop_check(parents, json, key, sizeof(key)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
n = json_array_size(json);
|
||||||
|
|
||||||
|
if(!embed && dump("[", 1, data))
|
||||||
|
return -1;
|
||||||
|
if(n == 0) {
|
||||||
|
hashtable_del(parents, key);
|
||||||
|
return embed ? 0 : dump("]", 1, data);
|
||||||
|
}
|
||||||
|
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for(i = 0; i < n; ++i) {
|
||||||
|
if(do_dump(json_array_get(json, i), flags, depth + 1,
|
||||||
|
parents, dump, data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(i < n - 1)
|
||||||
|
{
|
||||||
|
if(dump(",", 1, data) ||
|
||||||
|
dump_indent(flags, depth + 1, 1, dump, data))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(dump_indent(flags, depth, 0, dump, data))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashtable_del(parents, key);
|
||||||
|
return embed ? 0 : dump("]", 1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
case JSON_OBJECT:
|
||||||
|
{
|
||||||
|
void *iter;
|
||||||
|
const char *separator;
|
||||||
|
int separator_length;
|
||||||
|
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
|
||||||
|
char loop_key[2 + (sizeof(json) * 2) + 1];
|
||||||
|
|
||||||
|
if(flags & JSON_COMPACT) {
|
||||||
|
separator = ":";
|
||||||
|
separator_length = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
separator = ": ";
|
||||||
|
separator_length = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* detect circular references */
|
||||||
|
if (loop_check(parents, json, loop_key, sizeof(loop_key)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
iter = json_object_iter((json_t *)json);
|
||||||
|
|
||||||
|
if(!embed && dump("{", 1, data))
|
||||||
|
return -1;
|
||||||
|
if(!iter) {
|
||||||
|
hashtable_del(parents, loop_key);
|
||||||
|
return embed ? 0 : dump("}", 1, data);
|
||||||
|
}
|
||||||
|
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(flags & JSON_SORT_KEYS)
|
||||||
|
{
|
||||||
|
const char **keys;
|
||||||
|
size_t size, i;
|
||||||
|
|
||||||
|
size = json_object_size(json);
|
||||||
|
keys = (const char**)jsonp_malloc(size * sizeof(const char *));
|
||||||
|
if(!keys)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while(iter)
|
||||||
|
{
|
||||||
|
keys[i] = json_object_iter_key(iter);
|
||||||
|
iter = json_object_iter_next((json_t *)json, iter);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
assert(i == size);
|
||||||
|
|
||||||
|
qsort(keys, size, sizeof(const char *), compare_keys);
|
||||||
|
|
||||||
|
for(i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
const char *key;
|
||||||
|
json_t *value;
|
||||||
|
|
||||||
|
key = keys[i];
|
||||||
|
value = json_object_get(json, key);
|
||||||
|
assert(value);
|
||||||
|
|
||||||
|
dump_string(key, strlen(key), dump, data, flags);
|
||||||
|
if(dump(separator, separator_length, data) ||
|
||||||
|
do_dump(value, flags, depth + 1, parents, dump, data))
|
||||||
|
{
|
||||||
|
jsonp_free(keys);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i < size - 1)
|
||||||
|
{
|
||||||
|
if(dump(",", 1, data) ||
|
||||||
|
dump_indent(flags, depth + 1, 1, dump, data))
|
||||||
|
{
|
||||||
|
jsonp_free(keys);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(dump_indent(flags, depth, 0, dump, data))
|
||||||
|
{
|
||||||
|
jsonp_free(keys);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonp_free(keys);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Don't sort keys */
|
||||||
|
|
||||||
|
while(iter)
|
||||||
|
{
|
||||||
|
void *next = json_object_iter_next((json_t *)json, iter);
|
||||||
|
const char *key = json_object_iter_key(iter);
|
||||||
|
|
||||||
|
dump_string(key, strlen(key), dump, data, flags);
|
||||||
|
if(dump(separator, separator_length, data) ||
|
||||||
|
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||||
|
parents, dump, data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(next)
|
||||||
|
{
|
||||||
|
if(dump(",", 1, data) ||
|
||||||
|
dump_indent(flags, depth + 1, 1, dump, data))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(dump_indent(flags, depth, 0, dump, data))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashtable_del(parents, loop_key);
|
||||||
|
return embed ? 0 : dump("}", 1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* not reached */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *json_dumps(const json_t *json, size_t flags)
|
||||||
|
{
|
||||||
|
strbuffer_t strbuff;
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
if(strbuffer_init(&strbuff))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if(json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
|
||||||
|
result = NULL;
|
||||||
|
else
|
||||||
|
result = jsonp_strdup(strbuffer_value(&strbuff));
|
||||||
|
|
||||||
|
strbuffer_close(&strbuff);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
|
||||||
|
{
|
||||||
|
struct buffer buf = { size, 0, buffer };
|
||||||
|
|
||||||
|
if(json_dump_callback(json, dump_to_buffer, (void *)&buf, flags))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return buf.used;
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_dumpf(const json_t *json, FILE *output, size_t flags)
|
||||||
|
{
|
||||||
|
return json_dump_callback(json, dump_to_file, (void *)output, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_dumpfd(const json_t *json, int output, size_t flags)
|
||||||
|
{
|
||||||
|
return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_dump_file(const json_t *json, const char *path, size_t flags)
|
||||||
|
{
|
||||||
|
int result;
|
||||||
|
|
||||||
|
FILE *output = fopen(path, "w");
|
||||||
|
if(!output)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
result = json_dumpf(json, output, flags);
|
||||||
|
if(fclose(output) != 0)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
hashtable_t parents_set;
|
||||||
|
|
||||||
|
if(!(flags & JSON_ENCODE_ANY)) {
|
||||||
|
if(!json_is_array(json) && !json_is_object(json))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hashtable_init(&parents_set))
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
res = do_dump(json, flags, 0, &parents_set, callback, data);
|
||||||
|
|
||||||
|
hashtable_close(&parents_set);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
71
src/jansson/error.c
Normal file
71
src/jansson/error.c
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include "jansson_private.h"
|
||||||
|
|
||||||
|
void jsonp_error_init(json_error_t *error, const char *source)
|
||||||
|
{
|
||||||
|
if(error)
|
||||||
|
{
|
||||||
|
error->text[0] = '\0';
|
||||||
|
error->line = -1;
|
||||||
|
error->column = -1;
|
||||||
|
error->position = 0;
|
||||||
|
error->code = 0;
|
||||||
|
if(source)
|
||||||
|
jsonp_error_set_source(error, source);
|
||||||
|
else
|
||||||
|
error->source[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jsonp_error_set_source(json_error_t *error, const char *source)
|
||||||
|
{
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
if(!error || !source)
|
||||||
|
return;
|
||||||
|
|
||||||
|
length = strlen(source);
|
||||||
|
if(length < JSON_ERROR_SOURCE_LENGTH)
|
||||||
|
{
|
||||||
|
//strncpy(error->source, source, length + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t extra = length - JSON_ERROR_SOURCE_LENGTH + 4;
|
||||||
|
memcpy(error->source, "...", 3);
|
||||||
|
strncpy(error->source + 3, source + extra, length - extra + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jsonp_error_set(json_error_t *error, int line, int column,
|
||||||
|
size_t position, enum json_error_code code,
|
||||||
|
const char *msg, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, msg);
|
||||||
|
jsonp_error_vset(error, line, column, position, code, msg, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||||
|
size_t position, enum json_error_code code,
|
||||||
|
const char *msg, va_list ap)
|
||||||
|
{
|
||||||
|
if(!error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(error->text[0] != '\0') {
|
||||||
|
/* error already set */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
error->line = line;
|
||||||
|
error->column = column;
|
||||||
|
error->position = (int)position;
|
||||||
|
|
||||||
|
vsnprintf(error->text, JSON_ERROR_TEXT_LENGTH - 1, msg, ap);
|
||||||
|
error->text[JSON_ERROR_TEXT_LENGTH - 2] = '\0';
|
||||||
|
error->text[JSON_ERROR_TEXT_LENGTH - 1] = code;
|
||||||
|
error->code = code;
|
||||||
|
}
|
||||||
352
src/jansson/hashtable.c
Normal file
352
src/jansson/hashtable.c
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "jansson_config.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "jansson_config.h" /* for JSON_INLINE */
|
||||||
|
#include "jansson_private.h" /* for container_of() */
|
||||||
|
#include "hashtable.h"
|
||||||
|
|
||||||
|
#ifndef INITIAL_HASHTABLE_ORDER
|
||||||
|
#define INITIAL_HASHTABLE_ORDER 3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct hashtable_list list_t;
|
||||||
|
typedef struct hashtable_pair pair_t;
|
||||||
|
typedef struct hashtable_bucket bucket_t;
|
||||||
|
|
||||||
|
extern volatile uint32_t hashtable_seed;
|
||||||
|
|
||||||
|
/* Implementation of the hash function */
|
||||||
|
#include "lookup3.h"
|
||||||
|
|
||||||
|
#define list_to_pair(list_) container_of(list_, pair_t, list)
|
||||||
|
#define ordered_list_to_pair(list_) container_of(list_, pair_t, ordered_list)
|
||||||
|
#define hash_str(key) ((size_t)hashlittle((key), strlen(key), hashtable_seed))
|
||||||
|
|
||||||
|
JSON_INLINE void list_init(list_t *list)
|
||||||
|
{
|
||||||
|
list->next = list;
|
||||||
|
list->prev = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE void list_insert(list_t *list, list_t *node)
|
||||||
|
{
|
||||||
|
node->next = list;
|
||||||
|
node->prev = list->prev;
|
||||||
|
list->prev->next = node;
|
||||||
|
list->prev = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE void list_remove(list_t *list)
|
||||||
|
{
|
||||||
|
list->prev->next = list->next;
|
||||||
|
list->next->prev = list->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE int bucket_is_empty(hashtable_t *hashtable, bucket_t *bucket)
|
||||||
|
{
|
||||||
|
return bucket->first == &hashtable->list && bucket->first == bucket->last;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void insert_to_bucket(hashtable_t *hashtable, bucket_t *bucket,
|
||||||
|
list_t *list)
|
||||||
|
{
|
||||||
|
if(bucket_is_empty(hashtable, bucket))
|
||||||
|
{
|
||||||
|
list_insert(&hashtable->list, list);
|
||||||
|
bucket->first = bucket->last = list;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list_insert(bucket->first, list);
|
||||||
|
bucket->first = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static pair_t *hashtable_find_pair(hashtable_t *hashtable, bucket_t *bucket,
|
||||||
|
const char *key, size_t hash)
|
||||||
|
{
|
||||||
|
list_t *list;
|
||||||
|
pair_t *pair;
|
||||||
|
|
||||||
|
if(bucket_is_empty(hashtable, bucket))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
list = bucket->first;
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
pair = list_to_pair(list);
|
||||||
|
if(pair->hash == hash && strcmp(pair->key, key) == 0)
|
||||||
|
return pair;
|
||||||
|
|
||||||
|
if(list == bucket->last)
|
||||||
|
break;
|
||||||
|
|
||||||
|
list = list->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns 0 on success, -1 if key was not found */
|
||||||
|
static int hashtable_do_del(hashtable_t *hashtable,
|
||||||
|
const char *key, size_t hash)
|
||||||
|
{
|
||||||
|
pair_t *pair;
|
||||||
|
bucket_t *bucket;
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
index = hash & hashmask(hashtable->order);
|
||||||
|
bucket = &hashtable->buckets[index];
|
||||||
|
|
||||||
|
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||||
|
if(!pair)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(&pair->list == bucket->first && &pair->list == bucket->last)
|
||||||
|
bucket->first = bucket->last = &hashtable->list;
|
||||||
|
|
||||||
|
else if(&pair->list == bucket->first)
|
||||||
|
bucket->first = pair->list.next;
|
||||||
|
|
||||||
|
else if(&pair->list == bucket->last)
|
||||||
|
bucket->last = pair->list.prev;
|
||||||
|
|
||||||
|
list_remove(&pair->list);
|
||||||
|
list_remove(&pair->ordered_list);
|
||||||
|
json_decref(pair->value);
|
||||||
|
|
||||||
|
jsonp_free(pair);
|
||||||
|
hashtable->size--;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hashtable_do_clear(hashtable_t *hashtable)
|
||||||
|
{
|
||||||
|
list_t *list, *next;
|
||||||
|
pair_t *pair;
|
||||||
|
|
||||||
|
for(list = hashtable->list.next; list != &hashtable->list; list = next)
|
||||||
|
{
|
||||||
|
next = list->next;
|
||||||
|
pair = list_to_pair(list);
|
||||||
|
json_decref(pair->value);
|
||||||
|
jsonp_free(pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hashtable_do_rehash(hashtable_t *hashtable)
|
||||||
|
{
|
||||||
|
list_t *list, *next;
|
||||||
|
pair_t *pair;
|
||||||
|
size_t i, index, new_size, new_order;
|
||||||
|
struct hashtable_bucket* new_buckets;
|
||||||
|
|
||||||
|
new_order = hashtable->order + 1;
|
||||||
|
new_size = hashsize(new_order);
|
||||||
|
|
||||||
|
new_buckets = (struct hashtable_bucket*)jsonp_malloc(new_size * sizeof(bucket_t));
|
||||||
|
if(!new_buckets)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
jsonp_free(hashtable->buckets);
|
||||||
|
hashtable->buckets = new_buckets;
|
||||||
|
hashtable->order = new_order;
|
||||||
|
|
||||||
|
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||||
|
{
|
||||||
|
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||||
|
&hashtable->list;
|
||||||
|
}
|
||||||
|
|
||||||
|
list = hashtable->list.next;
|
||||||
|
list_init(&hashtable->list);
|
||||||
|
|
||||||
|
for(; list != &hashtable->list; list = next) {
|
||||||
|
next = list->next;
|
||||||
|
pair = list_to_pair(list);
|
||||||
|
index = pair->hash % new_size;
|
||||||
|
insert_to_bucket(hashtable, &hashtable->buckets[index], &pair->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hashtable_init(hashtable_t *hashtable)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
hashtable->size = 0;
|
||||||
|
hashtable->order = INITIAL_HASHTABLE_ORDER;
|
||||||
|
hashtable->buckets = (struct hashtable_bucket*)jsonp_malloc(hashsize(hashtable->order) * sizeof(bucket_t));
|
||||||
|
if(!hashtable->buckets)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
list_init(&hashtable->list);
|
||||||
|
list_init(&hashtable->ordered_list);
|
||||||
|
|
||||||
|
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||||
|
{
|
||||||
|
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||||
|
&hashtable->list;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashtable_close(hashtable_t *hashtable)
|
||||||
|
{
|
||||||
|
hashtable_do_clear(hashtable);
|
||||||
|
jsonp_free(hashtable->buckets);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value)
|
||||||
|
{
|
||||||
|
pair_t* pair;
|
||||||
|
bucket_t* bucket;
|
||||||
|
size_t hash, index;
|
||||||
|
|
||||||
|
/* rehash if the load ratio exceeds 1 */
|
||||||
|
if(hashtable->size >= hashsize(hashtable->order))
|
||||||
|
if(hashtable_do_rehash(hashtable))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
hash = hash_str(key);
|
||||||
|
index = hash & hashmask(hashtable->order);
|
||||||
|
bucket = &hashtable->buckets[index];
|
||||||
|
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||||
|
|
||||||
|
if(pair)
|
||||||
|
{
|
||||||
|
json_decref(pair->value);
|
||||||
|
pair->value = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* offsetof(...) returns the size of pair_t without the last,
|
||||||
|
flexible member. This way, the correct amount is
|
||||||
|
allocated. */
|
||||||
|
|
||||||
|
size_t len = strlen(key);
|
||||||
|
if(len >= (size_t)-1 - offsetof(pair_t, key)) {
|
||||||
|
/* Avoid an overflow if the key is very long */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pair = (pair_t*)jsonp_malloc(offsetof(pair_t, key) + len + 1);
|
||||||
|
if(!pair)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pair->hash = hash;
|
||||||
|
strncpy(pair->key, key, len + 1);
|
||||||
|
pair->value = value;
|
||||||
|
list_init(&pair->list);
|
||||||
|
list_init(&pair->ordered_list);
|
||||||
|
|
||||||
|
insert_to_bucket(hashtable, bucket, &pair->list);
|
||||||
|
list_insert(&hashtable->ordered_list, &pair->ordered_list);
|
||||||
|
|
||||||
|
hashtable->size++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashtable_get(hashtable_t *hashtable, const char *key)
|
||||||
|
{
|
||||||
|
pair_t *pair;
|
||||||
|
size_t hash;
|
||||||
|
bucket_t *bucket;
|
||||||
|
|
||||||
|
hash = hash_str(key);
|
||||||
|
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||||
|
|
||||||
|
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||||
|
if(!pair)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return pair->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hashtable_del(hashtable_t *hashtable, const char *key)
|
||||||
|
{
|
||||||
|
size_t hash = hash_str(key);
|
||||||
|
return hashtable_do_del(hashtable, key, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashtable_clear(hashtable_t *hashtable)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
hashtable_do_clear(hashtable);
|
||||||
|
|
||||||
|
for(i = 0; i < hashsize(hashtable->order); i++)
|
||||||
|
{
|
||||||
|
hashtable->buckets[i].first = hashtable->buckets[i].last =
|
||||||
|
&hashtable->list;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_init(&hashtable->list);
|
||||||
|
list_init(&hashtable->ordered_list);
|
||||||
|
hashtable->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashtable_iter(hashtable_t *hashtable)
|
||||||
|
{
|
||||||
|
return hashtable_iter_next(hashtable, &hashtable->ordered_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashtable_iter_at(hashtable_t *hashtable, const char *key)
|
||||||
|
{
|
||||||
|
pair_t *pair;
|
||||||
|
size_t hash;
|
||||||
|
bucket_t *bucket;
|
||||||
|
|
||||||
|
hash = hash_str(key);
|
||||||
|
bucket = &hashtable->buckets[hash & hashmask(hashtable->order)];
|
||||||
|
|
||||||
|
pair = hashtable_find_pair(hashtable, bucket, key, hash);
|
||||||
|
if(!pair)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &pair->ordered_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashtable_iter_next(hashtable_t *hashtable, void *iter)
|
||||||
|
{
|
||||||
|
list_t *list = (list_t *)iter;
|
||||||
|
if(list->next == &hashtable->ordered_list)
|
||||||
|
return NULL;
|
||||||
|
return list->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashtable_iter_key(void *iter)
|
||||||
|
{
|
||||||
|
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||||
|
return pair->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *hashtable_iter_value(void *iter)
|
||||||
|
{
|
||||||
|
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||||
|
return pair->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hashtable_iter_set(void *iter, json_t *value)
|
||||||
|
{
|
||||||
|
pair_t *pair = ordered_list_to_pair((list_t *)iter);
|
||||||
|
|
||||||
|
json_decref(pair->value);
|
||||||
|
pair->value = value;
|
||||||
|
}
|
||||||
176
src/jansson/hashtable.h
Normal file
176
src/jansson/hashtable.h
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HASHTABLE_H
|
||||||
|
#define HASHTABLE_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "jansson.h"
|
||||||
|
|
||||||
|
struct hashtable_list {
|
||||||
|
struct hashtable_list *prev;
|
||||||
|
struct hashtable_list *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* "pair" may be a bit confusing a name, but think of it as a
|
||||||
|
key-value pair. In this case, it just encodes some extra data,
|
||||||
|
too */
|
||||||
|
struct hashtable_pair {
|
||||||
|
struct hashtable_list list;
|
||||||
|
struct hashtable_list ordered_list;
|
||||||
|
size_t hash;
|
||||||
|
json_t *value;
|
||||||
|
char key[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hashtable_bucket {
|
||||||
|
struct hashtable_list *first;
|
||||||
|
struct hashtable_list *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct hashtable {
|
||||||
|
size_t size;
|
||||||
|
struct hashtable_bucket *buckets;
|
||||||
|
size_t order; /* hashtable has pow(2, order) buckets */
|
||||||
|
struct hashtable_list list;
|
||||||
|
struct hashtable_list ordered_list;
|
||||||
|
} hashtable_t;
|
||||||
|
|
||||||
|
|
||||||
|
#define hashtable_key_to_iter(key_) \
|
||||||
|
(&(container_of(key_, struct hashtable_pair, key)->ordered_list))
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_init - Initialize a hashtable object
|
||||||
|
*
|
||||||
|
* @hashtable: The (statically allocated) hashtable object
|
||||||
|
*
|
||||||
|
* Initializes a statically allocated hashtable object. The object
|
||||||
|
* should be cleared with hashtable_close when it's no longer used.
|
||||||
|
*
|
||||||
|
* Returns 0 on success, -1 on error (out of memory).
|
||||||
|
*/
|
||||||
|
int hashtable_init(hashtable_t *hashtable) JSON_ATTRS(warn_unused_result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_close - Release all resources used by a hashtable object
|
||||||
|
*
|
||||||
|
* @hashtable: The hashtable
|
||||||
|
*
|
||||||
|
* Destroys a statically allocated hashtable object.
|
||||||
|
*/
|
||||||
|
void hashtable_close(hashtable_t *hashtable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_set - Add/modify value in hashtable
|
||||||
|
*
|
||||||
|
* @hashtable: The hashtable object
|
||||||
|
* @key: The key
|
||||||
|
* @serial: For addition order of keys
|
||||||
|
* @value: The value
|
||||||
|
*
|
||||||
|
* If a value with the given key already exists, its value is replaced
|
||||||
|
* with the new value. Value is "stealed" in the sense that hashtable
|
||||||
|
* doesn't increment its refcount but decreases the refcount when the
|
||||||
|
* value is no longer needed.
|
||||||
|
*
|
||||||
|
* Returns 0 on success, -1 on failure (out of memory).
|
||||||
|
*/
|
||||||
|
int hashtable_set(hashtable_t *hashtable, const char *key, json_t *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_get - Get a value associated with a key
|
||||||
|
*
|
||||||
|
* @hashtable: The hashtable object
|
||||||
|
* @key: The key
|
||||||
|
*
|
||||||
|
* Returns value if it is found, or NULL otherwise.
|
||||||
|
*/
|
||||||
|
void *hashtable_get(hashtable_t *hashtable, const char *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_del - Remove a value from the hashtable
|
||||||
|
*
|
||||||
|
* @hashtable: The hashtable object
|
||||||
|
* @key: The key
|
||||||
|
*
|
||||||
|
* Returns 0 on success, or -1 if the key was not found.
|
||||||
|
*/
|
||||||
|
int hashtable_del(hashtable_t *hashtable, const char *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_clear - Clear hashtable
|
||||||
|
*
|
||||||
|
* @hashtable: The hashtable object
|
||||||
|
*
|
||||||
|
* Removes all items from the hashtable.
|
||||||
|
*/
|
||||||
|
void hashtable_clear(hashtable_t *hashtable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_iter - Iterate over hashtable
|
||||||
|
*
|
||||||
|
* @hashtable: The hashtable object
|
||||||
|
*
|
||||||
|
* Returns an opaque iterator to the first element in the hashtable.
|
||||||
|
* The iterator should be passed to hashtable_iter_* functions.
|
||||||
|
* The hashtable items are not iterated over in any particular order.
|
||||||
|
*
|
||||||
|
* There's no need to free the iterator in any way. The iterator is
|
||||||
|
* valid as long as the item that is referenced by the iterator is not
|
||||||
|
* deleted. Other values may be added or deleted. In particular,
|
||||||
|
* hashtable_iter_next() may be called on an iterator, and after that
|
||||||
|
* the key/value pair pointed by the old iterator may be deleted.
|
||||||
|
*/
|
||||||
|
void *hashtable_iter(hashtable_t *hashtable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_iter_at - Return an iterator at a specific key
|
||||||
|
*
|
||||||
|
* @hashtable: The hashtable object
|
||||||
|
* @key: The key that the iterator should point to
|
||||||
|
*
|
||||||
|
* Like hashtable_iter() but returns an iterator pointing to a
|
||||||
|
* specific key.
|
||||||
|
*/
|
||||||
|
void *hashtable_iter_at(hashtable_t *hashtable, const char *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_iter_next - Advance an iterator
|
||||||
|
*
|
||||||
|
* @hashtable: The hashtable object
|
||||||
|
* @iter: The iterator
|
||||||
|
*
|
||||||
|
* Returns a new iterator pointing to the next element in the
|
||||||
|
* hashtable or NULL if the whole hastable has been iterated over.
|
||||||
|
*/
|
||||||
|
void *hashtable_iter_next(hashtable_t *hashtable, void *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_iter_key - Retrieve the key pointed by an iterator
|
||||||
|
*
|
||||||
|
* @iter: The iterator
|
||||||
|
*/
|
||||||
|
void *hashtable_iter_key(void *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_iter_value - Retrieve the value pointed by an iterator
|
||||||
|
*
|
||||||
|
* @iter: The iterator
|
||||||
|
*/
|
||||||
|
void *hashtable_iter_value(void *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hashtable_iter_set - Set the value pointed by an iterator
|
||||||
|
*
|
||||||
|
* @iter: The iterator
|
||||||
|
* @value: The value to set
|
||||||
|
*/
|
||||||
|
void hashtable_iter_set(void *iter, json_t *value);
|
||||||
|
|
||||||
|
#endif
|
||||||
275
src/jansson/hashtable_seed.c
Normal file
275
src/jansson/hashtable_seed.c
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
/* Generate sizeof(uint32_t) bytes of as random data as possible to seed
|
||||||
|
the hash function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "jansson_config.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_STDINT_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FCNTL_H
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SCHED_H
|
||||||
|
#include <sched.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_TIME_H
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
/* For GetModuleHandle(), GetProcAddress() and GetCurrentProcessId() */
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "jansson.h"
|
||||||
|
|
||||||
|
|
||||||
|
inline uint32_t buf_to_uint32(char *data) {
|
||||||
|
size_t i;
|
||||||
|
uint32_t result = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(uint32_t); i++)
|
||||||
|
result = (result << 8) | (unsigned char)data[i];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* /dev/urandom */
|
||||||
|
#if !defined(_WIN32) && defined(USE_URANDOM)
|
||||||
|
int seed_from_urandom(uint32_t *seed) {
|
||||||
|
/* Use unbuffered I/O if we have open(), close() and read(). Otherwise
|
||||||
|
fall back to fopen() */
|
||||||
|
|
||||||
|
char data[sizeof(uint32_t)];
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
#if defined(HAVE_OPEN) && defined(HAVE_CLOSE) && defined(HAVE_READ)
|
||||||
|
int urandom;
|
||||||
|
urandom = open("/dev/urandom", O_RDONLY);
|
||||||
|
if (urandom == -1)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
ok = read(urandom, data, sizeof(uint32_t)) == sizeof(uint32_t);
|
||||||
|
close(urandom);
|
||||||
|
#else
|
||||||
|
FILE *urandom;
|
||||||
|
|
||||||
|
urandom = fopen("/dev/urandom", "rb");
|
||||||
|
if (!urandom)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
ok = fread(data, 1, sizeof(uint32_t), urandom) == sizeof(uint32_t);
|
||||||
|
fclose(urandom);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*seed = buf_to_uint32(data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Windows Crypto API */
|
||||||
|
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
|
||||||
|
#include <wincrypt.h>
|
||||||
|
|
||||||
|
typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv, LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags);
|
||||||
|
typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer);
|
||||||
|
typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv, DWORD dwFlags);
|
||||||
|
|
||||||
|
int seed_from_windows_cryptoapi(uint32_t *seed)
|
||||||
|
{
|
||||||
|
HINSTANCE hAdvAPI32 = NULL;
|
||||||
|
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;
|
||||||
|
CRYPTGENRANDOM pCryptGenRandom = NULL;
|
||||||
|
CRYPTRELEASECONTEXT pCryptReleaseContext = NULL;
|
||||||
|
HCRYPTPROV hCryptProv = 0;
|
||||||
|
BYTE data[sizeof(uint32_t)];
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
hAdvAPI32 = GetModuleHandle(TEXT("advapi32.dll"));
|
||||||
|
if(hAdvAPI32 == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32, "CryptAcquireContextA");
|
||||||
|
if (!pCryptAcquireContext)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32, "CryptGenRandom");
|
||||||
|
if (!pCryptGenRandom)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
pCryptReleaseContext = (CRYPTRELEASECONTEXT)GetProcAddress(hAdvAPI32, "CryptReleaseContext");
|
||||||
|
if (!pCryptReleaseContext)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!pCryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
ok = pCryptGenRandom(hCryptProv, sizeof(uint32_t), data);
|
||||||
|
pCryptReleaseContext(hCryptProv, 0);
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
*seed = buf_to_uint32((char *)data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* gettimeofday() and getpid() */
|
||||||
|
int seed_from_timestamp_and_pid(uint32_t *seed) {
|
||||||
|
#ifdef HAVE_GETTIMEOFDAY
|
||||||
|
/* XOR of seconds and microseconds */
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
*seed = (uint32_t)tv.tv_sec ^ (uint32_t)tv.tv_usec;
|
||||||
|
#else
|
||||||
|
/* Seconds only */
|
||||||
|
*seed = (uint32_t)time(NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* XOR with PID for more randomness */
|
||||||
|
#if defined(_WIN32)
|
||||||
|
*seed ^= (uint32_t)GetCurrentProcessId();
|
||||||
|
#elif defined(HAVE_GETPID)
|
||||||
|
*seed ^= (uint32_t)getpid();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t generate_seed() {
|
||||||
|
uint32_t seed = 0;
|
||||||
|
int done = 0;
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && defined(USE_URANDOM)
|
||||||
|
if (seed_from_urandom(&seed) == 0)
|
||||||
|
done = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(USE_WINDOWS_CRYPTOAPI)
|
||||||
|
if (seed_from_windows_cryptoapi(&seed) == 0)
|
||||||
|
done = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!done) {
|
||||||
|
/* Fall back to timestamp and PID if no better randomness is
|
||||||
|
available */
|
||||||
|
seed_from_timestamp_and_pid(&seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure the seed is never zero */
|
||||||
|
if (seed == 0)
|
||||||
|
seed = 1;
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
volatile uint32_t hashtable_seed = 0;
|
||||||
|
|
||||||
|
#if defined(HAVE_ATOMIC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
|
||||||
|
volatile char seed_initialized = 0;
|
||||||
|
|
||||||
|
void json_object_seed(size_t seed) {
|
||||||
|
uint32_t new_seed = (uint32_t)seed;
|
||||||
|
|
||||||
|
if (hashtable_seed == 0) {
|
||||||
|
if (__atomic_test_and_set(&seed_initialized, __ATOMIC_RELAXED) == 0) {
|
||||||
|
/* Do the seeding ourselves */
|
||||||
|
if (new_seed == 0)
|
||||||
|
new_seed = generate_seed();
|
||||||
|
|
||||||
|
__atomic_store_n(&hashtable_seed, new_seed, __ATOMIC_RELEASE);
|
||||||
|
} else {
|
||||||
|
/* Wait for another thread to do the seeding */
|
||||||
|
do {
|
||||||
|
#ifdef HAVE_SCHED_YIELD
|
||||||
|
sched_yield();
|
||||||
|
#endif
|
||||||
|
} while(__atomic_load_n(&hashtable_seed, __ATOMIC_ACQUIRE) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif defined(HAVE_SYNC_BUILTINS) && (defined(HAVE_SCHED_YIELD) || !defined(_WIN32))
|
||||||
|
void json_object_seed(size_t seed) {
|
||||||
|
uint32_t new_seed = (uint32_t)seed;
|
||||||
|
|
||||||
|
if (hashtable_seed == 0) {
|
||||||
|
if (new_seed == 0) {
|
||||||
|
/* Explicit synchronization fences are not supported by the
|
||||||
|
__sync builtins, so every thread getting here has to
|
||||||
|
generate the seed value.
|
||||||
|
*/
|
||||||
|
new_seed = generate_seed();
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (__sync_bool_compare_and_swap(&hashtable_seed, 0, new_seed)) {
|
||||||
|
/* We were the first to seed */
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* Wait for another thread to do the seeding */
|
||||||
|
#ifdef HAVE_SCHED_YIELD
|
||||||
|
sched_yield();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} while(hashtable_seed == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
long seed_initialized = 0;
|
||||||
|
void json_object_seed(size_t seed) {
|
||||||
|
uint32_t new_seed = (uint32_t)seed;
|
||||||
|
|
||||||
|
if (hashtable_seed == 0) {
|
||||||
|
if (InterlockedIncrement(&seed_initialized) == 1) {
|
||||||
|
/* Do the seeding ourselves */
|
||||||
|
if (new_seed == 0)
|
||||||
|
new_seed = generate_seed();
|
||||||
|
|
||||||
|
hashtable_seed = new_seed;
|
||||||
|
} else {
|
||||||
|
/* Wait for another thread to do the seeding */
|
||||||
|
do {
|
||||||
|
SwitchToThread();
|
||||||
|
} while (hashtable_seed == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Fall back to a thread-unsafe version */
|
||||||
|
void json_object_seed(size_t seed) {
|
||||||
|
uint32_t new_seed = (uint32_t)seed;
|
||||||
|
|
||||||
|
if (hashtable_seed == 0) {
|
||||||
|
if (new_seed == 0)
|
||||||
|
new_seed = generate_seed();
|
||||||
|
|
||||||
|
hashtable_seed = new_seed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
BIN
src/jansson/jansson-readthedocs-io-en-latest.pdf
Normal file
BIN
src/jansson/jansson-readthedocs-io-en-latest.pdf
Normal file
Binary file not shown.
412
src/jansson/jansson.h
Normal file
412
src/jansson/jansson.h
Normal file
@@ -0,0 +1,412 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JSON_H
|
||||||
|
#define JSON_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h> /* for size_t */
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "jansson_config.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* version */
|
||||||
|
|
||||||
|
#define JSON_MAJOR_VERSION 2
|
||||||
|
#define JSON_MINOR_VERSION 12
|
||||||
|
#define JSON_MICRO_VERSION 0
|
||||||
|
|
||||||
|
/* Micro version is omitted if it's 0 */
|
||||||
|
#define JSON_VERSION "2.12"
|
||||||
|
|
||||||
|
/* Version as a 3-byte hex number, e.g. 0x010201 == 1.2.1. Use this
|
||||||
|
for numeric comparisons, e.g. #if JSON_VERSION_HEX >= ... */
|
||||||
|
#define JSON_VERSION_HEX ((JSON_MAJOR_VERSION << 16) | \
|
||||||
|
(JSON_MINOR_VERSION << 8) | \
|
||||||
|
(JSON_MICRO_VERSION << 0))
|
||||||
|
|
||||||
|
/* If __atomic or __sync builtins are available the library is thread
|
||||||
|
* safe for all read-only functions plus reference counting. */
|
||||||
|
#if JSON_HAVE_ATOMIC_BUILTINS || JSON_HAVE_SYNC_BUILTINS
|
||||||
|
#define JSON_THREAD_SAFE_REFCOUNT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define JSON_ATTRS(...) __attribute__((__VA_ARGS__))
|
||||||
|
#else
|
||||||
|
#define JSON_ATTRS(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* types */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
JSON_OBJECT,
|
||||||
|
JSON_ARRAY,
|
||||||
|
JSON_STRING,
|
||||||
|
JSON_INTEGER,
|
||||||
|
JSON_REAL,
|
||||||
|
JSON_TRUE,
|
||||||
|
JSON_FALSE,
|
||||||
|
JSON_NULL
|
||||||
|
} json_type;
|
||||||
|
|
||||||
|
typedef struct json_t {
|
||||||
|
json_type type;
|
||||||
|
volatile size_t refcount;
|
||||||
|
} json_t;
|
||||||
|
|
||||||
|
#ifndef JSON_USING_CMAKE /* disabled if using cmake */
|
||||||
|
|
||||||
|
#if JSON_INTEGER_IS_LONG_LONG
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define JSON_INTEGER_FORMAT "I64d"
|
||||||
|
#else
|
||||||
|
#define JSON_INTEGER_FORMAT "lld"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef long long json_int_t;
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define JSON_INTEGER_FORMAT "ld"
|
||||||
|
typedef long json_int_t;
|
||||||
|
|
||||||
|
#endif /* JSON_INTEGER_IS_LONG_LONG */
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define json_typeof(json) ((json)->type)
|
||||||
|
#define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT)
|
||||||
|
#define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY)
|
||||||
|
#define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING)
|
||||||
|
#define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER)
|
||||||
|
#define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL)
|
||||||
|
#define json_is_number(json) (json_is_integer(json) || json_is_real(json))
|
||||||
|
#define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE)
|
||||||
|
#define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE)
|
||||||
|
#define json_boolean_value json_is_true
|
||||||
|
#define json_is_boolean(json) (json_is_true(json) || json_is_false(json))
|
||||||
|
#define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
|
||||||
|
|
||||||
|
/* construction, destruction, reference counting */
|
||||||
|
|
||||||
|
json_t *json_object(void);
|
||||||
|
json_t *json_array(void);
|
||||||
|
json_t *json_string(const char *value);
|
||||||
|
json_t *json_stringn(const char *value, size_t len);
|
||||||
|
json_t *json_string_nocheck(const char *value);
|
||||||
|
json_t *json_stringn_nocheck(const char *value, size_t len);
|
||||||
|
json_t *json_integer(json_int_t value);
|
||||||
|
json_t *json_real(double value);
|
||||||
|
json_t *json_true(void);
|
||||||
|
json_t *json_false(void);
|
||||||
|
#define json_boolean(val) ((val > 0) ? json_true() : json_false())
|
||||||
|
json_t *json_null(void);
|
||||||
|
|
||||||
|
/* do not call JSON_INTERNAL_INCREF or JSON_INTERNAL_DECREF directly */
|
||||||
|
#if JSON_HAVE_ATOMIC_BUILTINS
|
||||||
|
#define JSON_INTERNAL_INCREF(json) __atomic_add_fetch(&json->refcount, 1, __ATOMIC_ACQUIRE)
|
||||||
|
#define JSON_INTERNAL_DECREF(json) __atomic_sub_fetch(&json->refcount, 1, __ATOMIC_RELEASE)
|
||||||
|
#elif JSON_HAVE_SYNC_BUILTINS
|
||||||
|
#define JSON_INTERNAL_INCREF(json) __sync_add_and_fetch(&json->refcount, 1)
|
||||||
|
#define JSON_INTERNAL_DECREF(json) __sync_sub_and_fetch(&json->refcount, 1)
|
||||||
|
#else
|
||||||
|
#define JSON_INTERNAL_INCREF(json) (++json->refcount)
|
||||||
|
#define JSON_INTERNAL_DECREF(json) (--json->refcount)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
json_t *json_incref(json_t *json)
|
||||||
|
{
|
||||||
|
if(json && json->refcount != (size_t)-1)
|
||||||
|
JSON_INTERNAL_INCREF(json);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do not call json_delete directly */
|
||||||
|
void json_delete(json_t *json);
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
void json_decref(json_t *json)
|
||||||
|
{
|
||||||
|
if(json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0)
|
||||||
|
json_delete(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
JSON_INLINE
|
||||||
|
void json_decrefp(json_t **json)
|
||||||
|
{
|
||||||
|
if(json) {
|
||||||
|
json_decref(*json);
|
||||||
|
*json = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
void json_decrefp_def(json_t **json)
|
||||||
|
{
|
||||||
|
json_decrefp(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define json_auto_t json_t __attribute__((cleanup(json_decrefp)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* error reporting */
|
||||||
|
|
||||||
|
#define JSON_ERROR_TEXT_LENGTH 160
|
||||||
|
#define JSON_ERROR_SOURCE_LENGTH 80
|
||||||
|
|
||||||
|
typedef struct json_error_t {
|
||||||
|
int line;
|
||||||
|
int column;
|
||||||
|
int position;
|
||||||
|
char source[JSON_ERROR_SOURCE_LENGTH];
|
||||||
|
char text[JSON_ERROR_TEXT_LENGTH];
|
||||||
|
int code;
|
||||||
|
} json_error_t;
|
||||||
|
|
||||||
|
enum json_error_code {
|
||||||
|
json_error_unknown,
|
||||||
|
json_error_out_of_memory,
|
||||||
|
json_error_stack_overflow,
|
||||||
|
json_error_cannot_open_file,
|
||||||
|
json_error_invalid_argument,
|
||||||
|
json_error_invalid_utf8,
|
||||||
|
json_error_premature_end_of_input,
|
||||||
|
json_error_end_of_input_expected,
|
||||||
|
json_error_invalid_syntax,
|
||||||
|
json_error_invalid_format,
|
||||||
|
json_error_wrong_type,
|
||||||
|
json_error_null_character,
|
||||||
|
json_error_null_value,
|
||||||
|
json_error_null_byte_in_key,
|
||||||
|
json_error_duplicate_key,
|
||||||
|
json_error_numeric_overflow,
|
||||||
|
json_error_item_not_found,
|
||||||
|
json_error_index_out_of_range
|
||||||
|
};
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
enum json_error_code json_error_code(const json_error_t *e)
|
||||||
|
{
|
||||||
|
return (enum json_error_code)e->text[JSON_ERROR_TEXT_LENGTH - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
enum json_error_code json_error_code_def(const json_error_t *e)
|
||||||
|
{
|
||||||
|
return json_error_code(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* getters, setters, manipulation */
|
||||||
|
|
||||||
|
void json_object_seed(size_t seed);
|
||||||
|
size_t json_object_size(const json_t *object);
|
||||||
|
json_t *json_object_get(const json_t *object, const char *key) JSON_ATTRS(warn_unused_result);
|
||||||
|
int json_object_set_new(json_t *object, const char *key, json_t *value);
|
||||||
|
int json_object_set_new_nocheck(json_t *object, const char *key, json_t *value);
|
||||||
|
int json_object_del(json_t *object, const char *key);
|
||||||
|
int json_object_clear(json_t *object);
|
||||||
|
int json_object_update(json_t *object, json_t *other);
|
||||||
|
int json_object_update_existing(json_t *object, json_t *other);
|
||||||
|
int json_object_update_missing(json_t *object, json_t *other);
|
||||||
|
void *json_object_iter(json_t *object);
|
||||||
|
void *json_object_iter_at(json_t *object, const char *key);
|
||||||
|
void *json_object_key_to_iter(const char *key);
|
||||||
|
void *json_object_iter_next(json_t *object, void *iter);
|
||||||
|
const char *json_object_iter_key(void *iter);
|
||||||
|
json_t *json_object_iter_value(void *iter);
|
||||||
|
int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
|
||||||
|
|
||||||
|
#define json_object_foreach(object, key, value) \
|
||||||
|
for(key = json_object_iter_key(json_object_iter(object)); \
|
||||||
|
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
|
||||||
|
key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
|
||||||
|
|
||||||
|
#define json_object_foreach_safe(object, n, key, value) \
|
||||||
|
for(key = json_object_iter_key(json_object_iter(object)), \
|
||||||
|
n = json_object_iter_next(object, json_object_key_to_iter(key)); \
|
||||||
|
key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
|
||||||
|
key = json_object_iter_key(n), \
|
||||||
|
n = json_object_iter_next(object, json_object_key_to_iter(key)))
|
||||||
|
|
||||||
|
#define json_array_foreach(array, index, value) \
|
||||||
|
for(index = 0; \
|
||||||
|
index < json_array_size(array) && (value = json_array_get(array, index)); \
|
||||||
|
index++)
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_object_set(json_t *object, const char *key, json_t *value)
|
||||||
|
{
|
||||||
|
return json_object_set_new(object, key, json_incref(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_object_set_def(json_t *object, const char *key, json_t *value)
|
||||||
|
{
|
||||||
|
return json_object_set(object, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_object_set_nocheck(json_t *object, const char *key, json_t *value)
|
||||||
|
{
|
||||||
|
return json_object_set_new_nocheck(object, key, json_incref(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_object_iter_set(json_t *object, void *iter, json_t *value)
|
||||||
|
{
|
||||||
|
return json_object_iter_set_new(object, iter, json_incref(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_object_iter_set_def(json_t *object, void *iter, json_t *value)
|
||||||
|
{
|
||||||
|
return json_object_iter_set(object, iter, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t json_array_size(const json_t *array);
|
||||||
|
json_t *json_array_get(const json_t *array, size_t index) JSON_ATTRS(warn_unused_result);
|
||||||
|
int json_array_set_new(json_t *array, size_t index, json_t *value);
|
||||||
|
int json_array_append_new(json_t *array, json_t *value);
|
||||||
|
int json_array_insert_new(json_t *array, size_t index, json_t *value);
|
||||||
|
int json_array_remove(json_t *array, size_t index);
|
||||||
|
int json_array_clear(json_t *array);
|
||||||
|
int json_array_extend(json_t *array, json_t *other);
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_array_set(json_t *array, size_t ind, json_t *value)
|
||||||
|
{
|
||||||
|
return json_array_set_new(array, ind, json_incref(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_array_set_def(json_t *array, size_t ind, json_t *value)
|
||||||
|
{
|
||||||
|
return json_array_set(array, ind, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_array_append(json_t *array, json_t *value)
|
||||||
|
{
|
||||||
|
return json_array_append_new(array, json_incref(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_array_insert(json_t *array, size_t ind, json_t *value)
|
||||||
|
{
|
||||||
|
return json_array_insert_new(array, ind, json_incref(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON_INLINE
|
||||||
|
int json_array_insert_def(json_t *array, size_t ind, json_t *value)
|
||||||
|
{
|
||||||
|
return json_array_insert(array, ind, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *json_string_value(const json_t *string);
|
||||||
|
size_t json_string_length(const json_t *string);
|
||||||
|
json_int_t json_integer_value(const json_t *integer);
|
||||||
|
double json_real_value(const json_t *real);
|
||||||
|
double json_number_value(const json_t *json);
|
||||||
|
|
||||||
|
int json_string_set(json_t *string, const char *value);
|
||||||
|
int json_string_setn(json_t *string, const char *value, size_t len);
|
||||||
|
int json_string_set_nocheck(json_t *string, const char *value);
|
||||||
|
int json_string_setn_nocheck(json_t *string, const char *value, size_t len);
|
||||||
|
int json_integer_set(json_t *integer, json_int_t value);
|
||||||
|
int json_real_set(json_t *real, double value);
|
||||||
|
|
||||||
|
/* pack, unpack */
|
||||||
|
|
||||||
|
json_t *json_pack(const char *fmt, ...) JSON_ATTRS(warn_unused_result);
|
||||||
|
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...) JSON_ATTRS(warn_unused_result);
|
||||||
|
json_t *json_vpack_ex(json_error_t *error, size_t flags, const char *fmt, va_list ap) JSON_ATTRS(warn_unused_result);
|
||||||
|
|
||||||
|
#define JSON_VALIDATE_ONLY 0x1
|
||||||
|
#define JSON_STRICT 0x2
|
||||||
|
|
||||||
|
int json_unpack(json_t *root, const char *fmt, ...);
|
||||||
|
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...);
|
||||||
|
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, va_list ap);
|
||||||
|
|
||||||
|
/* sprintf */
|
||||||
|
|
||||||
|
json_t *json_sprintf(const char *fmt, ...) JSON_ATTRS(warn_unused_result, format(printf, 1, 2));
|
||||||
|
json_t *json_vsprintf(const char *fmt, va_list ap) JSON_ATTRS(warn_unused_result, format(printf, 1, 0));
|
||||||
|
|
||||||
|
|
||||||
|
/* equality */
|
||||||
|
|
||||||
|
int json_equal(const json_t *value1, const json_t *value2);
|
||||||
|
|
||||||
|
|
||||||
|
/* copying */
|
||||||
|
|
||||||
|
json_t *json_copy(json_t *value) JSON_ATTRS(warn_unused_result);
|
||||||
|
json_t *json_deep_copy(const json_t *value) JSON_ATTRS(warn_unused_result);
|
||||||
|
|
||||||
|
|
||||||
|
/* decoding */
|
||||||
|
|
||||||
|
#define JSON_REJECT_DUPLICATES 0x1
|
||||||
|
#define JSON_DISABLE_EOF_CHECK 0x2
|
||||||
|
#define JSON_DECODE_ANY 0x4
|
||||||
|
#define JSON_DECODE_INT_AS_REAL 0x8
|
||||||
|
#define JSON_ALLOW_NUL 0x10
|
||||||
|
|
||||||
|
typedef size_t (*json_load_callback_t)(void *buffer, size_t buflen, void *data);
|
||||||
|
|
||||||
|
json_t *json_loads(const char *input, size_t flags, json_error_t *error) JSON_ATTRS(warn_unused_result);
|
||||||
|
json_t *json_loadb(const char *buffer, size_t buflen, size_t flags, json_error_t *error) JSON_ATTRS(warn_unused_result);
|
||||||
|
json_t *json_loadf(FILE *input, size_t flags, json_error_t *error) JSON_ATTRS(warn_unused_result);
|
||||||
|
json_t *json_loadfd(int input, size_t flags, json_error_t *error) JSON_ATTRS(warn_unused_result);
|
||||||
|
json_t *json_load_file(const char *path, size_t flags, json_error_t *error) JSON_ATTRS(warn_unused_result);
|
||||||
|
json_t *json_load_callback(json_load_callback_t callback, void *data, size_t flags, json_error_t *error) JSON_ATTRS(warn_unused_result);
|
||||||
|
|
||||||
|
|
||||||
|
/* encoding */
|
||||||
|
|
||||||
|
#define JSON_MAX_INDENT 0x1F
|
||||||
|
#define JSON_INDENT(n) ((n) & JSON_MAX_INDENT)
|
||||||
|
#define JSON_COMPACT 0x20
|
||||||
|
#define JSON_ENSURE_ASCII 0x40
|
||||||
|
#define JSON_SORT_KEYS 0x80
|
||||||
|
#define JSON_PRESERVE_ORDER 0x100
|
||||||
|
#define JSON_ENCODE_ANY 0x200
|
||||||
|
#define JSON_ESCAPE_SLASH 0x400
|
||||||
|
#define JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11)
|
||||||
|
#define JSON_EMBED 0x10000
|
||||||
|
|
||||||
|
typedef int (*json_dump_callback_t)(const char *buffer, size_t size, void *data);
|
||||||
|
|
||||||
|
char *json_dumps(const json_t *json, size_t flags) JSON_ATTRS(warn_unused_result);
|
||||||
|
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags);
|
||||||
|
int json_dumpf(const json_t *json, FILE *output, size_t flags);
|
||||||
|
int json_dumpfd(const json_t *json, int output, size_t flags);
|
||||||
|
int json_dump_file(const json_t *json, const char *path, size_t flags);
|
||||||
|
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags);
|
||||||
|
|
||||||
|
/* custom memory allocation */
|
||||||
|
|
||||||
|
typedef void *(*json_malloc_t)(size_t);
|
||||||
|
typedef void (*json_free_t)(void *);
|
||||||
|
|
||||||
|
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn);
|
||||||
|
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
82
src/jansson/jansson_config.h
Normal file
82
src/jansson/jansson_config.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This file specifies a part of the site-specific configuration for
|
||||||
|
* Jansson, namely those things that affect the public API in
|
||||||
|
* jansson.h.
|
||||||
|
*
|
||||||
|
* The configure script copies this file to jansson_config.h and
|
||||||
|
* replaces @var@ substitutions by values that fit your system. If you
|
||||||
|
* cannot run the configure script, you can do the value substitution
|
||||||
|
* by hand.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JSON_CONFIG_H
|
||||||
|
#define JSON_CONFIG_H
|
||||||
|
|
||||||
|
#define HAVE_STDINT_H
|
||||||
|
|
||||||
|
#define JSON_USE_TAB_INDENT
|
||||||
|
|
||||||
|
/* If your compiler supports the inline keyword in C, JSON_INLINE is
|
||||||
|
defined to `inline', otherwise empty. In C++, the inline is always
|
||||||
|
supported. */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define JSON_INLINE inline
|
||||||
|
#else
|
||||||
|
#define JSON_INLINE static inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If your compiler supports the `long long` type and the strtoll()
|
||||||
|
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
|
||||||
|
otherwise to 0. */
|
||||||
|
|
||||||
|
//#if defined WSYSTEM_CORETYPE_ILON
|
||||||
|
#define JSON_INTEGER_IS_LONG_LONG 1
|
||||||
|
//#else
|
||||||
|
//#define JSON_INTEGER_IS_LONG_LONG 0
|
||||||
|
//#endif
|
||||||
|
|
||||||
|
/* If locale.h and localeconv() are available, define to 1,
|
||||||
|
otherwise to 0. */
|
||||||
|
#define JSON_HAVE_LOCALECONV 0
|
||||||
|
|
||||||
|
/* If __atomic builtins are available they will be used to manage
|
||||||
|
reference counts of json_t. */
|
||||||
|
#define JSON_HAVE_ATOMIC_BUILTINS 0
|
||||||
|
|
||||||
|
/* If __atomic builtins are not available we try using __sync builtins
|
||||||
|
to manage reference counts of json_t. */
|
||||||
|
#define JSON_HAVE_SYNC_BUILTINS 0
|
||||||
|
|
||||||
|
/* Maximum recursion depth for parsing JSON input.
|
||||||
|
This limits the depth of e.g. array-within-array constructions. */
|
||||||
|
#define JSON_PARSER_MAX_DEPTH 2048
|
||||||
|
|
||||||
|
/*
|
||||||
|
static inline void* Jansson_config_malloc(size_t size)
|
||||||
|
{
|
||||||
|
return (void*)malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void Jansson_config_free(void* ptr)
|
||||||
|
{
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void* Jansson_config_malloc_def(size_t size)
|
||||||
|
{
|
||||||
|
return Jansson_config_malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void Jansson_config_free_def(void* ptr)
|
||||||
|
{
|
||||||
|
Jansson_config_free(ptr);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif
|
||||||
51
src/jansson/jansson_config.h.in
Normal file
51
src/jansson/jansson_config.h.in
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2010-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This file specifies a part of the site-specific configuration for
|
||||||
|
* Jansson, namely those things that affect the public API in
|
||||||
|
* jansson.h.
|
||||||
|
*
|
||||||
|
* The configure script copies this file to jansson_config.h and
|
||||||
|
* replaces @var@ substitutions by values that fit your system. If you
|
||||||
|
* cannot run the configure script, you can do the value substitution
|
||||||
|
* by hand.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JSON_CONFIG_H
|
||||||
|
#define JSON_CONFIG_H
|
||||||
|
|
||||||
|
/* If your compiler supports the inline keyword in C, JSON_INLINE is
|
||||||
|
defined to `inline', otherwise empty. In C++, the inline is always
|
||||||
|
supported. */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define JSON_INLINE inline
|
||||||
|
#else
|
||||||
|
#define JSON_INLINE @json_inline@
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If your compiler supports the `long long` type and the strtoll()
|
||||||
|
library function, JSON_INTEGER_IS_LONG_LONG is defined to 1,
|
||||||
|
otherwise to 0. */
|
||||||
|
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@
|
||||||
|
|
||||||
|
/* If locale.h and localeconv() are available, define to 1,
|
||||||
|
otherwise to 0. */
|
||||||
|
#define JSON_HAVE_LOCALECONV @json_have_localeconv@
|
||||||
|
|
||||||
|
/* If __atomic builtins are available they will be used to manage
|
||||||
|
reference counts of json_t. */
|
||||||
|
#define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@
|
||||||
|
|
||||||
|
/* If __atomic builtins are not available we try using __sync builtins
|
||||||
|
to manage reference counts of json_t. */
|
||||||
|
#define JSON_HAVE_SYNC_BUILTINS @json_have_sync_builtins@
|
||||||
|
|
||||||
|
/* Maximum recursion depth for parsing JSON input.
|
||||||
|
This limits the depth of e.g. array-within-array constructions. */
|
||||||
|
#define JSON_PARSER_MAX_DEPTH 2048
|
||||||
|
|
||||||
|
#endif
|
||||||
109
src/jansson/jansson_private.h
Normal file
109
src/jansson/jansson_private.h
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef JSON_PRIVATE_H
|
||||||
|
#define JSON_PRIVATE_H
|
||||||
|
|
||||||
|
#include "jansson_config.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "jansson.h"
|
||||||
|
#include "hashtable.h"
|
||||||
|
#include "strbuffer.h"
|
||||||
|
|
||||||
|
#define container_of(ptr_, type_, member_) \
|
||||||
|
((type_ *)((char *)ptr_ - offsetof(type_, member_)))
|
||||||
|
|
||||||
|
/* On some platforms, max() may already be defined */
|
||||||
|
#ifndef max
|
||||||
|
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* va_copy is a C99 feature. In C89 implementations, it's sometimes
|
||||||
|
available as __va_copy. If not, memcpy() should do the trick. */
|
||||||
|
#ifndef va_copy
|
||||||
|
#ifdef __va_copy
|
||||||
|
#define va_copy __va_copy
|
||||||
|
#else
|
||||||
|
#define va_copy(a, b) memcpy(&(a), &(b), sizeof(va_list))
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
json_t json;
|
||||||
|
hashtable_t hashtable;
|
||||||
|
} json_object_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
json_t json;
|
||||||
|
size_t size;
|
||||||
|
size_t entries;
|
||||||
|
json_t **table;
|
||||||
|
} json_array_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
json_t json;
|
||||||
|
char *value;
|
||||||
|
size_t length;
|
||||||
|
} json_string_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
json_t json;
|
||||||
|
double value;
|
||||||
|
} json_real_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
json_t json;
|
||||||
|
json_int_t value;
|
||||||
|
} json_integer_t;
|
||||||
|
|
||||||
|
#define json_to_object(json_) container_of(json_, json_object_t, json)
|
||||||
|
#define json_to_array(json_) container_of(json_, json_array_t, json)
|
||||||
|
#define json_to_string(json_) container_of(json_, json_string_t, json)
|
||||||
|
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||||
|
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||||
|
|
||||||
|
/* Create a string by taking ownership of an existing buffer */
|
||||||
|
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
|
||||||
|
|
||||||
|
/* Error message formatting */
|
||||||
|
void jsonp_error_init(json_error_t *error, const char *source);
|
||||||
|
void jsonp_error_set_source(json_error_t *error, const char *source);
|
||||||
|
void jsonp_error_set(json_error_t *error, int line, int column,
|
||||||
|
size_t position, enum json_error_code code,
|
||||||
|
const char *msg, ...);
|
||||||
|
void jsonp_error_vset(json_error_t *error, int line, int column,
|
||||||
|
size_t position, enum json_error_code code,
|
||||||
|
const char *msg, va_list ap);
|
||||||
|
|
||||||
|
/* Locale independent string<->double conversions */
|
||||||
|
int jsonp_strtod(strbuffer_t *strbuffer, double *out);
|
||||||
|
int jsonp_dtostr(char *buffer, size_t size, double value, int prec);
|
||||||
|
|
||||||
|
/* Wrappers for custom memory functions */
|
||||||
|
void* jsonp_malloc(size_t size) JSON_ATTRS(warn_unused_result);
|
||||||
|
void jsonp_free(void *ptr);
|
||||||
|
char *jsonp_strndup(const char *str, size_t length) JSON_ATTRS(warn_unused_result);
|
||||||
|
char *jsonp_strdup(const char *str) JSON_ATTRS(warn_unused_result);
|
||||||
|
char *jsonp_strndup(const char *str, size_t len) JSON_ATTRS(warn_unused_result);
|
||||||
|
|
||||||
|
|
||||||
|
/* Windows compatibility */
|
||||||
|
#if defined(_WIN32) || defined(WIN32)
|
||||||
|
# if defined(_MSC_VER) /* MS compiller */
|
||||||
|
# if (_MSC_VER < 1900) && !defined(snprintf) /* snprintf not defined yet & not introduced */
|
||||||
|
# define snprintf _snprintf
|
||||||
|
# endif
|
||||||
|
# if (_MSC_VER < 1500) && !defined(vsnprintf) /* vsnprintf not defined yet & not introduced */
|
||||||
|
# define vsnprintf(b,c,f,a) _vsnprintf(b,c,f,a)
|
||||||
|
# endif
|
||||||
|
# else /* Other Windows compiller, old definition */
|
||||||
|
# define snprintf _snprintf
|
||||||
|
# define vsnprintf _vsnprintf
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
158
src/jansson/jansson_private_config.h
Normal file
158
src/jansson/jansson_private_config.h
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/* jansson_config.h.in. Generated from configure.ac by autoheader. */
|
||||||
|
|
||||||
|
/* Define to 1 if gcc's __atomic builtins are available */
|
||||||
|
#undef HAVE_ATOMIC_BUILTINS
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `close' function. */
|
||||||
|
#undef HAVE_CLOSE
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
#undef HAVE_DLFCN_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <endian.h> header file. */
|
||||||
|
#undef HAVE_ENDIAN_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||||
|
#undef HAVE_FCNTL_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `getpid' function. */
|
||||||
|
#undef HAVE_GETPID
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `gettimeofday' function. */
|
||||||
|
#undef HAVE_GETTIMEOFDAY
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#undef HAVE_INTTYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `localeconv' function. */
|
||||||
|
#undef HAVE_LOCALECONV
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <locale.h> header file. */
|
||||||
|
#undef HAVE_LOCALE_H
|
||||||
|
|
||||||
|
/* Define to 1 if the system has the type 'long long int'. */
|
||||||
|
#undef HAVE_LONG_LONG_INT
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <memory.h> header file. */
|
||||||
|
#undef HAVE_MEMORY_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `open' function. */
|
||||||
|
#undef HAVE_OPEN
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `read' function. */
|
||||||
|
#undef HAVE_READ
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sched.h> header file. */
|
||||||
|
#undef HAVE_SCHED_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `sched_yield' function. */
|
||||||
|
#undef HAVE_SCHED_YIELD
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#undef HAVE_STDINT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#undef HAVE_STDLIB_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#undef HAVE_STRINGS_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#undef HAVE_STRING_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strtoll' function. */
|
||||||
|
#undef HAVE_STRTOLL
|
||||||
|
|
||||||
|
/* Define to 1 if gcc's __sync builtins are available */
|
||||||
|
#undef HAVE_SYNC_BUILTINS
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/param.h> header file. */
|
||||||
|
#undef HAVE_SYS_PARAM_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#undef HAVE_SYS_STAT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||||
|
#undef HAVE_SYS_TIME_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#undef HAVE_SYS_TYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#undef HAVE_UNISTD_H
|
||||||
|
|
||||||
|
/* Define to 1 if the system has the type 'unsigned long long int'. */
|
||||||
|
#undef HAVE_UNSIGNED_LONG_LONG_INT
|
||||||
|
|
||||||
|
/* Number of buckets new object hashtables contain is 2 raised to this power.
|
||||||
|
E.g. 3 -> 2^3 = 8. */
|
||||||
|
#undef INITIAL_HASHTABLE_ORDER
|
||||||
|
|
||||||
|
/* Define to the sub-directory where libtool stores uninstalled libraries. */
|
||||||
|
#undef LT_OBJDIR
|
||||||
|
|
||||||
|
/* Name of package */
|
||||||
|
#undef PACKAGE
|
||||||
|
|
||||||
|
/* Define to the address where bug reports for this package should be sent. */
|
||||||
|
#undef PACKAGE_BUGREPORT
|
||||||
|
|
||||||
|
/* Define to the full name of this package. */
|
||||||
|
#undef PACKAGE_NAME
|
||||||
|
|
||||||
|
/* Define to the full name and version of this package. */
|
||||||
|
#undef PACKAGE_STRING
|
||||||
|
|
||||||
|
/* Define to the one symbol short name of this package. */
|
||||||
|
#undef PACKAGE_TARNAME
|
||||||
|
|
||||||
|
/* Define to the home page for this package. */
|
||||||
|
#undef PACKAGE_URL
|
||||||
|
|
||||||
|
/* Define to the version of this package. */
|
||||||
|
#undef PACKAGE_VERSION
|
||||||
|
|
||||||
|
/* Define to 1 if you have the ANSI C header files. */
|
||||||
|
#undef STDC_HEADERS
|
||||||
|
|
||||||
|
/* Define to 1 if /dev/urandom should be used for seeding the hash function */
|
||||||
|
#undef USE_URANDOM
|
||||||
|
|
||||||
|
/* Define to 1 if CryptGenRandom should be used for seeding the hash function
|
||||||
|
*/
|
||||||
|
#undef USE_WINDOWS_CRYPTOAPI
|
||||||
|
|
||||||
|
/* Version number of package */
|
||||||
|
#undef VERSION
|
||||||
|
|
||||||
|
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
|
||||||
|
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||||
|
#define below would cause a syntax error. */
|
||||||
|
#undef _UINT32_T
|
||||||
|
|
||||||
|
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
|
||||||
|
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
|
||||||
|
#define below would cause a syntax error. */
|
||||||
|
#undef _UINT8_T
|
||||||
|
|
||||||
|
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||||
|
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#undef inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define to the type of a signed integer type of width exactly 32 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef int32_t
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 16 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef uint16_t
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 32 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef uint32_t
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 8 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef uint8_t
|
||||||
1162
src/jansson/load.c
Normal file
1162
src/jansson/load.c
Normal file
File diff suppressed because it is too large
Load Diff
71
src/jansson/lookup3.h
Normal file
71
src/jansson/lookup3.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define hashsize(n) ((size_t)1 << (n))
|
||||||
|
#define hashmask(n) (hashsize(n) - 1)
|
||||||
|
|
||||||
|
static inline uint32_t rot(uint32_t x, uint32_t k) {
|
||||||
|
return (x << k) | (x >> (32 - k));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MIX(a,b,c) do { \
|
||||||
|
a -= c; a ^= rot(c, 4); c += b; \
|
||||||
|
b -= a; b ^= rot(a, 6); a += c; \
|
||||||
|
c -= b; c ^= rot(b, 8); b += a; \
|
||||||
|
a -= c; a ^= rot(c,16); c += b; \
|
||||||
|
b -= a; b ^= rot(a,19); a += c; \
|
||||||
|
c -= b; c ^= rot(b, 4); b += a; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define FINAL(a,b,c) do { \
|
||||||
|
c ^= b; c -= rot(b,14); \
|
||||||
|
a ^= c; a -= rot(c,11); \
|
||||||
|
b ^= a; b -= rot(a,25); \
|
||||||
|
c ^= b; c -= rot(b,16); \
|
||||||
|
a ^= c; a -= rot(c, 4); \
|
||||||
|
b ^= a; b -= rot(a,14); \
|
||||||
|
c ^= b; c -= rot(b,24); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* ASan-safe Jenkins hashlittle: never reads past 'length' bytes */
|
||||||
|
static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
|
||||||
|
{
|
||||||
|
const uint8_t *p = (const uint8_t *)key;
|
||||||
|
uint32_t a, b, c;
|
||||||
|
|
||||||
|
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
||||||
|
c = initval;
|
||||||
|
|
||||||
|
/* Handle most of the key in 12-byte chunks */
|
||||||
|
while (length >= 12) {
|
||||||
|
uint32_t x;
|
||||||
|
/* use memcpy so the compiler won’t issue over-reads or require alignment */
|
||||||
|
memcpy(&x, p + 0, 4); a += x;
|
||||||
|
memcpy(&x, p + 4, 4); b += x;
|
||||||
|
memcpy(&x, p + 8, 4); c += x;
|
||||||
|
|
||||||
|
MIX(a, b, c);
|
||||||
|
p += 12;
|
||||||
|
length -= 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle the last 0..11 bytes */
|
||||||
|
switch (length) {
|
||||||
|
case 11: c += (uint32_t)p[10] << 16; /* fallthrough */
|
||||||
|
case 10: c += (uint32_t)p[9] << 8; /* fallthrough */
|
||||||
|
case 9: c += (uint32_t)p[8]; /* fallthrough */
|
||||||
|
case 8: b += (uint32_t)p[7] << 24; /* fallthrough */
|
||||||
|
case 7: b += (uint32_t)p[6] << 16; /* fallthrough */
|
||||||
|
case 6: b += (uint32_t)p[5] << 8; /* fallthrough */
|
||||||
|
case 5: b += (uint32_t)p[4]; /* fallthrough */
|
||||||
|
case 4: a += (uint32_t)p[3] << 24; /* fallthrough */
|
||||||
|
case 3: a += (uint32_t)p[2] << 16; /* fallthrough */
|
||||||
|
case 2: a += (uint32_t)p[1] << 8; /* fallthrough */
|
||||||
|
case 1: a += (uint32_t)p[0]; /* fallthrough */
|
||||||
|
break;
|
||||||
|
case 0: /* nothing left */ break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FINAL(a, b, c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
383
src/jansson/lookup3.h.copy
Normal file
383
src/jansson/lookup3.h.copy
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
/*
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
lookup3.c, by Bob Jenkins, May 2006, Public Domain.
|
||||||
|
|
||||||
|
These are functions for producing 32-bit hashes for hash table lookup.
|
||||||
|
hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
|
||||||
|
are externally useful functions. Routines to test the hash are included
|
||||||
|
if SELF_TEST is defined. You can use this free for any purpose. It's in
|
||||||
|
the public domain. It has no warranty.
|
||||||
|
|
||||||
|
You probably want to use hashlittle(). hashlittle() and hashbig()
|
||||||
|
hash byte arrays. hashlittle() is is faster than hashbig() on
|
||||||
|
little-endian machines. Intel and AMD are little-endian machines.
|
||||||
|
On second thought, you probably want hashlittle2(), which is identical to
|
||||||
|
hashlittle() except it returns two 32-bit hashes for the price of one.
|
||||||
|
You could implement hashbig2() if you wanted but I haven't bothered here.
|
||||||
|
|
||||||
|
If you want to find a hash of, say, exactly 7 integers, do
|
||||||
|
a = i1; b = i2; c = i3;
|
||||||
|
mix(a,b,c);
|
||||||
|
a += i4; b += i5; c += i6;
|
||||||
|
mix(a,b,c);
|
||||||
|
a += i7;
|
||||||
|
final(a,b,c);
|
||||||
|
then use c as the hash value. If you have a variable length array of
|
||||||
|
4-byte integers to hash, use hashword(). If you have a byte array (like
|
||||||
|
a character string), use hashlittle(). If you have several byte arrays, or
|
||||||
|
a mix of things, see the comments above hashlittle().
|
||||||
|
|
||||||
|
Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
|
||||||
|
then mix those integers. This is fast (you can do a lot more thorough
|
||||||
|
mixing with 12*3 instructions on 3 integers than you can with 3 instructions
|
||||||
|
on 1 byte), but shoehorning those bytes into integers efficiently is messy.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef LOOKUUP3_H
|
||||||
|
#define LOOKUUP3_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "jansson_config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_STDINT_H
|
||||||
|
#include <stdint.h> /* defines uint32_t etc */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_PARAM_H
|
||||||
|
#include <sys/param.h> /* attempt to define endianness */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_ENDIAN_H
|
||||||
|
# include <endian.h> /* attempt to define endianness */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* My best guess at if you are big-endian or little-endian. This may
|
||||||
|
* need adjustment.
|
||||||
|
*/
|
||||||
|
#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
|
||||||
|
__BYTE_ORDER == __LITTLE_ENDIAN) || \
|
||||||
|
(defined(i386) || defined(__i386__) || defined(__i486__) || \
|
||||||
|
defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
|
||||||
|
# define HASH_LITTLE_ENDIAN 1
|
||||||
|
# define HASH_BIG_ENDIAN 0
|
||||||
|
#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
|
||||||
|
__BYTE_ORDER == __BIG_ENDIAN) || \
|
||||||
|
(defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
|
||||||
|
# define HASH_LITTLE_ENDIAN 0
|
||||||
|
# define HASH_BIG_ENDIAN 1
|
||||||
|
#else
|
||||||
|
# define HASH_LITTLE_ENDIAN 0
|
||||||
|
# define HASH_BIG_ENDIAN 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define hashsize(n) ((uint32_t)1<<(n))
|
||||||
|
#define hashmask(n) (hashsize(n)-1)
|
||||||
|
#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
|
||||||
|
|
||||||
|
/*
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
mix -- mix 3 32-bit values reversibly.
|
||||||
|
|
||||||
|
This is reversible, so any information in (a,b,c) before mix() is
|
||||||
|
still in (a,b,c) after mix().
|
||||||
|
|
||||||
|
If four pairs of (a,b,c) inputs are run through mix(), or through
|
||||||
|
mix() in reverse, there are at least 32 bits of the output that
|
||||||
|
are sometimes the same for one pair and different for another pair.
|
||||||
|
This was tested for:
|
||||||
|
* pairs that differed by one bit, by two bits, in any combination
|
||||||
|
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||||
|
(a,b,c).
|
||||||
|
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||||
|
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||||
|
is commonly produced by subtraction) look like a single 1-bit
|
||||||
|
difference.
|
||||||
|
* the base values were pseudorandom, all zero but one bit set, or
|
||||||
|
all zero plus a counter that starts at zero.
|
||||||
|
|
||||||
|
Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
|
||||||
|
satisfy this are
|
||||||
|
4 6 8 16 19 4
|
||||||
|
9 15 3 18 27 15
|
||||||
|
14 9 3 7 17 3
|
||||||
|
Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
|
||||||
|
for "differ" defined as + with a one-bit base and a two-bit delta. I
|
||||||
|
used http://burtleburtle.net/bob/hash/avalanche.html to choose
|
||||||
|
the operations, constants, and arrangements of the variables.
|
||||||
|
|
||||||
|
This does not achieve avalanche. There are input bits of (a,b,c)
|
||||||
|
that fail to affect some output bits of (a,b,c), especially of a. The
|
||||||
|
most thoroughly mixed value is c, but it doesn't really even achieve
|
||||||
|
avalanche in c.
|
||||||
|
|
||||||
|
This allows some parallelism. Read-after-writes are good at doubling
|
||||||
|
the number of bits affected, so the goal of mixing pulls in the opposite
|
||||||
|
direction as the goal of parallelism. I did what I could. Rotates
|
||||||
|
seem to cost as much as shifts on every machine I could lay my hands
|
||||||
|
on, and rotates are much kinder to the top and bottom bits, so I used
|
||||||
|
rotates.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#define mix(a,b,c) \
|
||||||
|
{ \
|
||||||
|
a -= c; a ^= rot(c, 4); c += b; \
|
||||||
|
b -= a; b ^= rot(a, 6); a += c; \
|
||||||
|
c -= b; c ^= rot(b, 8); b += a; \
|
||||||
|
a -= c; a ^= rot(c,16); c += b; \
|
||||||
|
b -= a; b ^= rot(a,19); a += c; \
|
||||||
|
c -= b; c ^= rot(b, 4); b += a; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
final -- final mixing of 3 32-bit values (a,b,c) into c
|
||||||
|
|
||||||
|
Pairs of (a,b,c) values differing in only a few bits will usually
|
||||||
|
produce values of c that look totally different. This was tested for
|
||||||
|
* pairs that differed by one bit, by two bits, in any combination
|
||||||
|
of top bits of (a,b,c), or in any combination of bottom bits of
|
||||||
|
(a,b,c).
|
||||||
|
* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
|
||||||
|
the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
|
||||||
|
is commonly produced by subtraction) look like a single 1-bit
|
||||||
|
difference.
|
||||||
|
* the base values were pseudorandom, all zero but one bit set, or
|
||||||
|
all zero plus a counter that starts at zero.
|
||||||
|
|
||||||
|
These constants passed:
|
||||||
|
14 11 25 16 4 14 24
|
||||||
|
12 14 25 16 4 14 24
|
||||||
|
and these came close:
|
||||||
|
4 8 15 26 3 22 24
|
||||||
|
10 8 15 26 3 22 24
|
||||||
|
11 8 15 26 3 22 24
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#define final(a,b,c) \
|
||||||
|
{ \
|
||||||
|
c ^= b; c -= rot(b,14); \
|
||||||
|
a ^= c; a -= rot(c,11); \
|
||||||
|
b ^= a; b -= rot(a,25); \
|
||||||
|
c ^= b; c -= rot(b,16); \
|
||||||
|
a ^= c; a -= rot(c,4); \
|
||||||
|
b ^= a; b -= rot(a,14); \
|
||||||
|
c ^= b; c -= rot(b,24); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
hashlittle() -- hash a variable-length key into a 32-bit value
|
||||||
|
k : the key (the unaligned variable-length array of bytes)
|
||||||
|
length : the length of the key, counting by bytes
|
||||||
|
initval : can be any 4-byte value
|
||||||
|
Returns a 32-bit value. Every bit of the key affects every bit of
|
||||||
|
the return value. Two keys differing by one or two bits will have
|
||||||
|
totally different hash values.
|
||||||
|
|
||||||
|
The best hash table sizes are powers of 2. There is no need to do
|
||||||
|
mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||||
|
use a bitmask. For example, if you need only 10 bits, do
|
||||||
|
h = (h & hashmask(10));
|
||||||
|
In which case, the hash table should have hashsize(10) elements.
|
||||||
|
|
||||||
|
If you are hashing n strings (uint8_t **)k, do it like this:
|
||||||
|
for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
|
||||||
|
|
||||||
|
By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
|
||||||
|
code any way you wish, private, educational, or commercial. It's free.
|
||||||
|
|
||||||
|
Use for hash table lookup, or anything where one collision in 2^^32 is
|
||||||
|
acceptable. Do NOT use for cryptographic purposes.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint32_t hashlittle(const void *key, size_t length, uint32_t initval)
|
||||||
|
{
|
||||||
|
uint32_t a,b,c; /* internal state */
|
||||||
|
union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
|
||||||
|
|
||||||
|
/* Set up the internal state */
|
||||||
|
a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
|
||||||
|
|
||||||
|
u.ptr = key;
|
||||||
|
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
|
||||||
|
const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
|
||||||
|
|
||||||
|
/* Detect Valgrind or AddressSanitizer */
|
||||||
|
#ifdef VALGRIND
|
||||||
|
# define NO_MASKING_TRICK 1
|
||||||
|
#else
|
||||||
|
# if defined(__has_feature) /* Clang */
|
||||||
|
# if __has_feature(address_sanitizer) /* is ASAN enabled? */
|
||||||
|
# define NO_MASKING_TRICK 1
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# if defined(__SANITIZE_ADDRESS__) /* GCC 4.8.x, is ASAN enabled? */
|
||||||
|
# define NO_MASKING_TRICK 1
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef NO_MASKING_TRICK
|
||||||
|
const uint8_t *k8;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
|
||||||
|
while (length > 12)
|
||||||
|
{
|
||||||
|
a += k[0];
|
||||||
|
b += k[1];
|
||||||
|
c += k[2];
|
||||||
|
mix(a,b,c);
|
||||||
|
length -= 12;
|
||||||
|
k += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------- handle the last (probably partial) block */
|
||||||
|
/*
|
||||||
|
* "k[2]&0xffffff" actually reads beyond the end of the string, but
|
||||||
|
* then masks off the part it's not allowed to read. Because the
|
||||||
|
* string is aligned, the masked-off tail is in the same word as the
|
||||||
|
* rest of the string. Every machine with memory protection I've seen
|
||||||
|
* does it on word boundaries, so is OK with this. But VALGRIND will
|
||||||
|
* still catch it and complain. The masking trick does make the hash
|
||||||
|
* noticably faster for short strings (like English words).
|
||||||
|
*/
|
||||||
|
#ifndef NO_MASKING_TRICK
|
||||||
|
|
||||||
|
switch(length)
|
||||||
|
{
|
||||||
|
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 8 : b+=k[1]; a+=k[0]; break;
|
||||||
|
case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
|
||||||
|
case 6 : b+=k[1]&0xffff; a+=k[0]; break;
|
||||||
|
case 5 : b+=k[1]&0xff; a+=k[0]; break;
|
||||||
|
case 4 : a+=k[0]; break;
|
||||||
|
case 3 : a+=k[0]&0xffffff; break;
|
||||||
|
case 2 : a+=k[0]&0xffff; break;
|
||||||
|
case 1 : a+=k[0]&0xff; break;
|
||||||
|
case 0 : return c; /* zero length strings require no mixing */
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* make valgrind happy */
|
||||||
|
|
||||||
|
k8 = (const uint8_t *)k;
|
||||||
|
switch(length)
|
||||||
|
{
|
||||||
|
case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
|
||||||
|
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||||
|
case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
|
||||||
|
case 9 : c+=k8[8]; /* fall through */
|
||||||
|
case 8 : b+=k[1]; a+=k[0]; break;
|
||||||
|
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||||
|
case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
|
||||||
|
case 5 : b+=k8[4]; /* fall through */
|
||||||
|
case 4 : a+=k[0]; break;
|
||||||
|
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||||
|
case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
|
||||||
|
case 1 : a+=k8[0]; break;
|
||||||
|
case 0 : return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !valgrind */
|
||||||
|
|
||||||
|
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
|
||||||
|
const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
|
||||||
|
const uint8_t *k8;
|
||||||
|
|
||||||
|
/*--------------- all but last block: aligned reads and different mixing */
|
||||||
|
while (length > 12)
|
||||||
|
{
|
||||||
|
a += k[0] + (((uint32_t)k[1])<<16);
|
||||||
|
b += k[2] + (((uint32_t)k[3])<<16);
|
||||||
|
c += k[4] + (((uint32_t)k[5])<<16);
|
||||||
|
mix(a,b,c);
|
||||||
|
length -= 12;
|
||||||
|
k += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*----------------------------- handle the last (probably partial) block */
|
||||||
|
k8 = (const uint8_t *)k;
|
||||||
|
switch(length)
|
||||||
|
{
|
||||||
|
case 12: c+=k[4]+(((uint32_t)k[5])<<16);
|
||||||
|
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||||
|
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
|
||||||
|
case 10: c+=k[4];
|
||||||
|
b+=k[2]+(((uint32_t)k[3])<<16);
|
||||||
|
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 9 : c+=k8[8]; /* fall through */
|
||||||
|
case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
|
||||||
|
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
|
||||||
|
case 6 : b+=k[2];
|
||||||
|
a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 5 : b+=k8[4]; /* fall through */
|
||||||
|
case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
|
||||||
|
break;
|
||||||
|
case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
|
||||||
|
case 2 : a+=k[0];
|
||||||
|
break;
|
||||||
|
case 1 : a+=k8[0];
|
||||||
|
break;
|
||||||
|
case 0 : return c; /* zero length requires no mixing */
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { /* need to read the key one byte at a time */
|
||||||
|
const uint8_t *k = (const uint8_t *)key;
|
||||||
|
|
||||||
|
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
|
||||||
|
while (length > 12)
|
||||||
|
{
|
||||||
|
a += k[0];
|
||||||
|
a += ((uint32_t)k[1])<<8;
|
||||||
|
a += ((uint32_t)k[2])<<16;
|
||||||
|
a += ((uint32_t)k[3])<<24;
|
||||||
|
b += k[4];
|
||||||
|
b += ((uint32_t)k[5])<<8;
|
||||||
|
b += ((uint32_t)k[6])<<16;
|
||||||
|
b += ((uint32_t)k[7])<<24;
|
||||||
|
c += k[8];
|
||||||
|
c += ((uint32_t)k[9])<<8;
|
||||||
|
c += ((uint32_t)k[10])<<16;
|
||||||
|
c += ((uint32_t)k[11])<<24;
|
||||||
|
mix(a,b,c);
|
||||||
|
length -= 12;
|
||||||
|
k += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*-------------------------------- last block: affect all 32 bits of (c) */
|
||||||
|
switch(length) /* all the case statements fall through */
|
||||||
|
{
|
||||||
|
case 12: c+=((uint32_t)k[11])<<24; /* fall through */
|
||||||
|
case 11: c+=((uint32_t)k[10])<<16; /* fall through */
|
||||||
|
case 10: c+=((uint32_t)k[9])<<8; /* fall through */
|
||||||
|
case 9 : c+=k[8]; /* fall through */
|
||||||
|
case 8 : b+=((uint32_t)k[7])<<24; /* fall through */
|
||||||
|
case 7 : b+=((uint32_t)k[6])<<16; /* fall through */
|
||||||
|
case 6 : b+=((uint32_t)k[5])<<8; /* fall through */
|
||||||
|
case 5 : b+=k[4]; /* fall through */
|
||||||
|
case 4 : a+=((uint32_t)k[3])<<24; /* fall through */
|
||||||
|
case 3 : a+=((uint32_t)k[2])<<16; /* fall through */
|
||||||
|
case 2 : a+=((uint32_t)k[1])<<8; /* fall through */
|
||||||
|
case 1 : a+=k[0];
|
||||||
|
break;
|
||||||
|
case 0 : return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final(a,b,c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //LOOKUUP3_H
|
||||||
69
src/jansson/memory.c
Normal file
69
src/jansson/memory.c
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
* Copyright (c) 2011-2012 Basile Starynkevitch <basile@starynkevitch.net>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "jansson.h"
|
||||||
|
#include "jansson_private.h"
|
||||||
|
|
||||||
|
/* C89 allows these to be macros */
|
||||||
|
#undef malloc
|
||||||
|
#undef free
|
||||||
|
|
||||||
|
/* memory function pointers */
|
||||||
|
static json_malloc_t do_malloc = malloc;
|
||||||
|
static json_free_t do_free = free;
|
||||||
|
|
||||||
|
void *jsonp_malloc(size_t size)
|
||||||
|
{
|
||||||
|
if(!size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return (*do_malloc)(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jsonp_free(void *ptr)
|
||||||
|
{
|
||||||
|
if(!ptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
(*do_free)(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *jsonp_strdup(const char *str)
|
||||||
|
{
|
||||||
|
return jsonp_strndup(str, strlen(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *jsonp_strndup(const char *str, size_t len)
|
||||||
|
{
|
||||||
|
char *new_str;
|
||||||
|
|
||||||
|
new_str = (char*)jsonp_malloc(len + 1);
|
||||||
|
if(!new_str)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memcpy(new_str, str, len);
|
||||||
|
new_str[len] = '\0';
|
||||||
|
return new_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_set_alloc_funcs(json_malloc_t malloc_fn, json_free_t free_fn)
|
||||||
|
{
|
||||||
|
do_malloc = malloc_fn;
|
||||||
|
do_free = free_fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void json_get_alloc_funcs(json_malloc_t *malloc_fn, json_free_t *free_fn)
|
||||||
|
{
|
||||||
|
if (malloc_fn)
|
||||||
|
*malloc_fn = do_malloc;
|
||||||
|
if (free_fn)
|
||||||
|
*free_fn = do_free;
|
||||||
|
}
|
||||||
952
src/jansson/pack_unpack.c
Normal file
952
src/jansson/pack_unpack.c
Normal file
@@ -0,0 +1,952 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
* Copyright (c) 2011-2012 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "jansson.h"
|
||||||
|
#include "jansson_private.h"
|
||||||
|
#include "utf.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int line;
|
||||||
|
int column;
|
||||||
|
size_t pos;
|
||||||
|
char token;
|
||||||
|
} token_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *start;
|
||||||
|
const char *fmt;
|
||||||
|
token_t prev_token;
|
||||||
|
token_t token;
|
||||||
|
token_t next_token;
|
||||||
|
json_error_t *error;
|
||||||
|
size_t flags;
|
||||||
|
int line;
|
||||||
|
int column;
|
||||||
|
size_t pos;
|
||||||
|
int has_error;
|
||||||
|
} scanner_t;
|
||||||
|
|
||||||
|
#define token(scanner) ((scanner)->token.token)
|
||||||
|
|
||||||
|
static const char * const type_names[] = {
|
||||||
|
"object",
|
||||||
|
"array",
|
||||||
|
"string",
|
||||||
|
"integer",
|
||||||
|
"real",
|
||||||
|
"true",
|
||||||
|
"false",
|
||||||
|
"null"
|
||||||
|
};
|
||||||
|
|
||||||
|
#define type_name(x) type_names[json_typeof(x)]
|
||||||
|
|
||||||
|
static const char unpack_value_starters[] = "{[siIbfFOon";
|
||||||
|
|
||||||
|
static void scanner_init(scanner_t *s, json_error_t *error,
|
||||||
|
size_t flags, const char *fmt)
|
||||||
|
{
|
||||||
|
s->error = error;
|
||||||
|
s->flags = flags;
|
||||||
|
s->fmt = s->start = fmt;
|
||||||
|
memset(&s->prev_token, 0, sizeof(token_t));
|
||||||
|
memset(&s->token, 0, sizeof(token_t));
|
||||||
|
memset(&s->next_token, 0, sizeof(token_t));
|
||||||
|
s->line = 1;
|
||||||
|
s->column = 0;
|
||||||
|
s->pos = 0;
|
||||||
|
s->has_error = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void next_token(scanner_t *s)
|
||||||
|
{
|
||||||
|
const char *t;
|
||||||
|
s->prev_token = s->token;
|
||||||
|
|
||||||
|
if(s->next_token.line) {
|
||||||
|
s->token = s->next_token;
|
||||||
|
s->next_token.line = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!token(s) && !*s->fmt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
t = s->fmt;
|
||||||
|
s->column++;
|
||||||
|
s->pos++;
|
||||||
|
|
||||||
|
/* skip space and ignored chars */
|
||||||
|
while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
|
||||||
|
if(*t == '\n') {
|
||||||
|
s->line++;
|
||||||
|
s->column = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s->column++;
|
||||||
|
|
||||||
|
s->pos++;
|
||||||
|
t++;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->token.token = *t;
|
||||||
|
s->token.line = s->line;
|
||||||
|
s->token.column = s->column;
|
||||||
|
s->token.pos = s->pos;
|
||||||
|
|
||||||
|
if (*t) t++;
|
||||||
|
s->fmt = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prev_token(scanner_t *s)
|
||||||
|
{
|
||||||
|
s->next_token = s->token;
|
||||||
|
s->token = s->prev_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_error(scanner_t *s, const char *source, enum json_error_code code,
|
||||||
|
const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
|
||||||
|
jsonp_error_vset(s->error, s->token.line, s->token.column, s->token.pos,
|
||||||
|
code, fmt, ap);
|
||||||
|
|
||||||
|
jsonp_error_set_source(s->error, source);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_t *pack(scanner_t *s, va_list *ap);
|
||||||
|
|
||||||
|
|
||||||
|
/* ours will be set to 1 if jsonp_free() must be called for the result
|
||||||
|
afterwards */
|
||||||
|
static char *read_string(scanner_t *s, va_list *ap,
|
||||||
|
const char *purpose, size_t *out_len, int *ours, int optional)
|
||||||
|
{
|
||||||
|
char t;
|
||||||
|
strbuffer_t strbuff;
|
||||||
|
const char *str;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
t = token(s);
|
||||||
|
prev_token(s);
|
||||||
|
|
||||||
|
*ours = 0;
|
||||||
|
if(t != '#' && t != '%' && t != '+') {
|
||||||
|
/* Optimize the simple case */
|
||||||
|
str = va_arg(*ap, const char *);
|
||||||
|
|
||||||
|
if(!str) {
|
||||||
|
if (!optional) {
|
||||||
|
set_error(s, "<args>", json_error_null_value, "NULL %s", purpose);
|
||||||
|
s->has_error = 1;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
length = strlen(str);
|
||||||
|
|
||||||
|
if(!utf8_check_string(str, length)) {
|
||||||
|
set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
|
||||||
|
s->has_error = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_len = length;
|
||||||
|
return (char *)str;
|
||||||
|
} else if (optional) {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Cannot use '%c' on optional strings", t);
|
||||||
|
s->has_error = 1;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strbuffer_init(&strbuff)) {
|
||||||
|
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
|
||||||
|
s->has_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
str = va_arg(*ap, const char *);
|
||||||
|
if(!str) {
|
||||||
|
set_error(s, "<args>", json_error_null_value, "NULL %s", purpose);
|
||||||
|
s->has_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
|
||||||
|
if(token(s) == '#') {
|
||||||
|
length = va_arg(*ap, int);
|
||||||
|
}
|
||||||
|
else if(token(s) == '%') {
|
||||||
|
length = va_arg(*ap, size_t);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
prev_token(s);
|
||||||
|
length = s->has_error ? 0 : strlen(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!s->has_error && strbuffer_append_bytes(&strbuff, str, length) == -1) {
|
||||||
|
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
|
||||||
|
s->has_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
if(token(s) != '+') {
|
||||||
|
prev_token(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s->has_error) {
|
||||||
|
strbuffer_close(&strbuff);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!utf8_check_string(strbuff.value, strbuff.length)) {
|
||||||
|
set_error(s, "<args>", json_error_invalid_utf8, "Invalid UTF-8 %s", purpose);
|
||||||
|
strbuffer_close(&strbuff);
|
||||||
|
s->has_error = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_len = strbuff.length;
|
||||||
|
*ours = 1;
|
||||||
|
return strbuffer_steal_value(&strbuff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_t *pack_object(scanner_t *s, va_list *ap)
|
||||||
|
{
|
||||||
|
json_t *object = json_object();
|
||||||
|
next_token(s);
|
||||||
|
|
||||||
|
while(token(s) != '}') {
|
||||||
|
char *key;
|
||||||
|
size_t len;
|
||||||
|
int ours;
|
||||||
|
json_t *value;
|
||||||
|
char valueOptional;
|
||||||
|
|
||||||
|
if(!token(s)) {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(token(s) != 's') {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = read_string(s, ap, "object key", &len, &ours, 0);
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
valueOptional = token(s);
|
||||||
|
prev_token(s);
|
||||||
|
|
||||||
|
value = pack(s, ap);
|
||||||
|
if(!value) {
|
||||||
|
if(ours)
|
||||||
|
jsonp_free(key);
|
||||||
|
|
||||||
|
if(valueOptional != '*') {
|
||||||
|
set_error(s, "<args>", json_error_null_value, "NULL object value");
|
||||||
|
s->has_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s->has_error)
|
||||||
|
json_decref(value);
|
||||||
|
|
||||||
|
if(!s->has_error && json_object_set_new_nocheck(object, key, value)) {
|
||||||
|
set_error(s, "<internal>", json_error_out_of_memory, "Unable to add key \"%s\"", key);
|
||||||
|
s->has_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ours)
|
||||||
|
jsonp_free(key);
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!s->has_error)
|
||||||
|
return object;
|
||||||
|
|
||||||
|
error:
|
||||||
|
json_decref(object);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_t *pack_array(scanner_t *s, va_list *ap)
|
||||||
|
{
|
||||||
|
json_t *array = json_array();
|
||||||
|
next_token(s);
|
||||||
|
|
||||||
|
while(token(s) != ']') {
|
||||||
|
json_t *value;
|
||||||
|
char valueOptional;
|
||||||
|
|
||||||
|
if(!token(s)) {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
|
||||||
|
/* Format string errors are unrecoverable. */
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
valueOptional = token(s);
|
||||||
|
prev_token(s);
|
||||||
|
|
||||||
|
value = pack(s, ap);
|
||||||
|
if(!value) {
|
||||||
|
if(valueOptional != '*') {
|
||||||
|
s->has_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(s->has_error)
|
||||||
|
json_decref(value);
|
||||||
|
|
||||||
|
if(!s->has_error && json_array_append_new(array, value)) {
|
||||||
|
set_error(s, "<internal>", json_error_out_of_memory, "Unable to append to array");
|
||||||
|
s->has_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!s->has_error)
|
||||||
|
return array;
|
||||||
|
|
||||||
|
error:
|
||||||
|
json_decref(array);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_t *pack_string(scanner_t *s, va_list *ap)
|
||||||
|
{
|
||||||
|
char *str;
|
||||||
|
char t;
|
||||||
|
size_t len;
|
||||||
|
int ours;
|
||||||
|
int optional;
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
t = token(s);
|
||||||
|
optional = t == '?' || t == '*';
|
||||||
|
if (!optional)
|
||||||
|
prev_token(s);
|
||||||
|
|
||||||
|
str = read_string(s, ap, "string", &len, &ours, optional);
|
||||||
|
|
||||||
|
if (!str)
|
||||||
|
return t == '?' && !s->has_error ? json_null() : NULL;
|
||||||
|
|
||||||
|
if (s->has_error) {
|
||||||
|
/* It's impossible to reach this point if ours != 0, do not free str. */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ours)
|
||||||
|
return jsonp_stringn_nocheck_own(str, len);
|
||||||
|
|
||||||
|
return json_stringn_nocheck(str, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_t *pack_object_inter(scanner_t *s, va_list *ap, int need_incref)
|
||||||
|
{
|
||||||
|
json_t *json;
|
||||||
|
char ntoken;
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
ntoken = token(s);
|
||||||
|
|
||||||
|
if (ntoken != '?' && ntoken != '*')
|
||||||
|
prev_token(s);
|
||||||
|
|
||||||
|
json = va_arg(*ap, json_t *);
|
||||||
|
|
||||||
|
if (json)
|
||||||
|
return need_incref ? json_incref(json) : json;
|
||||||
|
|
||||||
|
switch (ntoken) {
|
||||||
|
case '?':
|
||||||
|
return json_null();
|
||||||
|
case '*':
|
||||||
|
return NULL;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_error(s, "<args>", json_error_null_value, "NULL object");
|
||||||
|
s->has_error = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_t *pack_integer(scanner_t *s, json_int_t value)
|
||||||
|
{
|
||||||
|
json_t *json = json_integer(value);
|
||||||
|
|
||||||
|
if (!json) {
|
||||||
|
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
|
||||||
|
s->has_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_t *pack_real(scanner_t *s, double value)
|
||||||
|
{
|
||||||
|
/* Allocate without setting value so we can identify OOM error. */
|
||||||
|
json_t *json = json_real(0.0);
|
||||||
|
|
||||||
|
if (!json) {
|
||||||
|
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
|
||||||
|
s->has_error = 1;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json_real_set(json, value)) {
|
||||||
|
json_decref(json);
|
||||||
|
|
||||||
|
set_error(s, "<args>", json_error_numeric_overflow, "Invalid floating point value");
|
||||||
|
s->has_error = 1;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
static json_t *pack(scanner_t *s, va_list *ap)
|
||||||
|
{
|
||||||
|
switch(token(s)) {
|
||||||
|
case '{':
|
||||||
|
return pack_object(s, ap);
|
||||||
|
|
||||||
|
case '[':
|
||||||
|
return pack_array(s, ap);
|
||||||
|
|
||||||
|
case 's': /* string */
|
||||||
|
return pack_string(s, ap);
|
||||||
|
|
||||||
|
case 'n': /* null */
|
||||||
|
return json_null();
|
||||||
|
|
||||||
|
case 'b': /* boolean */
|
||||||
|
return va_arg(*ap, int) ? json_true() : json_false();
|
||||||
|
|
||||||
|
case 'i': /* integer from int */
|
||||||
|
return pack_integer(s, va_arg(*ap, int));
|
||||||
|
|
||||||
|
case 'I': /* integer from json_int_t */
|
||||||
|
return pack_integer(s, va_arg(*ap, json_int_t));
|
||||||
|
|
||||||
|
case 'f': /* real */
|
||||||
|
return pack_real(s, va_arg(*ap, double));
|
||||||
|
|
||||||
|
case 'O': /* a json_t object; increments refcount */
|
||||||
|
return pack_object_inter(s, ap, 1);
|
||||||
|
|
||||||
|
case 'o': /* a json_t object; doesn't increment refcount */
|
||||||
|
return pack_object_inter(s, ap, 0);
|
||||||
|
|
||||||
|
default:
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
|
||||||
|
token(s));
|
||||||
|
s->has_error = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unpack(scanner_t *s, json_t *root, va_list *ap);
|
||||||
|
|
||||||
|
static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
int strict = 0;
|
||||||
|
int gotopt = 0;
|
||||||
|
|
||||||
|
/* Use a set (emulated by a hashtable) to check that all object
|
||||||
|
keys are accessed. Checking that the correct number of keys
|
||||||
|
were accessed is not enough, as the same key can be unpacked
|
||||||
|
multiple times.
|
||||||
|
*/
|
||||||
|
hashtable_t key_set;
|
||||||
|
|
||||||
|
if(hashtable_init(&key_set)) {
|
||||||
|
set_error(s, "<internal>", json_error_out_of_memory, "Out of memory");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(root && !json_is_object(root)) {
|
||||||
|
set_error(s, "<validation>", json_error_wrong_type, "Expected object, got %s",
|
||||||
|
type_name(root));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
next_token(s);
|
||||||
|
|
||||||
|
while(token(s) != '}') {
|
||||||
|
const char *key;
|
||||||
|
json_t *value;
|
||||||
|
int opt = 0;
|
||||||
|
|
||||||
|
if(strict != 0) {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Expected '}' after '%c', got '%c'",
|
||||||
|
(strict == 1 ? '!' : '*'), token(s));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!token(s)) {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(token(s) == '!' || token(s) == '*') {
|
||||||
|
strict = (token(s) == '!' ? 1 : -1);
|
||||||
|
next_token(s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(token(s) != 's') {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Expected format 's', got '%c'", token(s));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = va_arg(*ap, const char *);
|
||||||
|
if(!key) {
|
||||||
|
set_error(s, "<args>", json_error_null_value, "NULL object key");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
|
||||||
|
if(token(s) == '?') {
|
||||||
|
opt = gotopt = 1;
|
||||||
|
next_token(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!root) {
|
||||||
|
/* skipping */
|
||||||
|
value = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value = json_object_get(root, key);
|
||||||
|
if(!value && !opt) {
|
||||||
|
set_error(s, "<validation>", json_error_item_not_found, "Object item not found: %s", key);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(unpack(s, value, ap))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
hashtable_set(&key_set, key, json_null());
|
||||||
|
next_token(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strict == 0 && (s->flags & JSON_STRICT))
|
||||||
|
strict = 1;
|
||||||
|
|
||||||
|
if(root && strict == 1) {
|
||||||
|
/* We need to check that all non optional items have been parsed */
|
||||||
|
const char *key;
|
||||||
|
/* keys_res is 1 for uninitialized, 0 for success, -1 for error. */
|
||||||
|
int keys_res = 1;
|
||||||
|
strbuffer_t unrecognized_keys;
|
||||||
|
json_t *value;
|
||||||
|
long unpacked = 0;
|
||||||
|
|
||||||
|
if (gotopt || json_object_size(root) != key_set.size) {
|
||||||
|
json_object_foreach(root, key, value) {
|
||||||
|
if(!hashtable_get(&key_set, key)) {
|
||||||
|
unpacked++;
|
||||||
|
|
||||||
|
/* Save unrecognized keys for the error message */
|
||||||
|
if (keys_res == 1) {
|
||||||
|
keys_res = strbuffer_init(&unrecognized_keys);
|
||||||
|
} else if (!keys_res) {
|
||||||
|
keys_res = strbuffer_append_bytes(&unrecognized_keys, ", ", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keys_res)
|
||||||
|
keys_res = strbuffer_append_bytes(&unrecognized_keys, key, strlen(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unpacked) {
|
||||||
|
set_error(s, "<validation>", json_error_end_of_input_expected,
|
||||||
|
"%li object item(s) left unpacked: %s",
|
||||||
|
unpacked,
|
||||||
|
keys_res ? "<unknown>" : strbuffer_value(&unrecognized_keys));
|
||||||
|
strbuffer_close(&unrecognized_keys);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
hashtable_close(&key_set);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
int strict = 0;
|
||||||
|
|
||||||
|
if(root && !json_is_array(root)) {
|
||||||
|
set_error(s, "<validation>", json_error_wrong_type, "Expected array, got %s", type_name(root));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
next_token(s);
|
||||||
|
|
||||||
|
while(token(s) != ']') {
|
||||||
|
json_t *value;
|
||||||
|
|
||||||
|
if(strict != 0) {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Expected ']' after '%c', got '%c'",
|
||||||
|
(strict == 1 ? '!' : '*'),
|
||||||
|
token(s));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!token(s)) {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Unexpected end of format string");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(token(s) == '!' || token(s) == '*') {
|
||||||
|
strict = (token(s) == '!' ? 1 : -1);
|
||||||
|
next_token(s);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!strchr(unpack_value_starters, token(s))) {
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
|
||||||
|
token(s));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!root) {
|
||||||
|
/* skipping */
|
||||||
|
value = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
value = json_array_get(root, i);
|
||||||
|
if(!value) {
|
||||||
|
set_error(s, "<validation>", json_error_index_out_of_range, "Array index %lu out of range",
|
||||||
|
(unsigned long)i);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(unpack(s, value, ap))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strict == 0 && (s->flags & JSON_STRICT))
|
||||||
|
strict = 1;
|
||||||
|
|
||||||
|
if(root && strict == 1 && i != json_array_size(root)) {
|
||||||
|
long diff = (long)json_array_size(root) - (long)i;
|
||||||
|
set_error(s, "<validation>", json_error_end_of_input_expected, "%li array item(s) left unpacked", diff);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unpack(scanner_t *s, json_t *root, va_list *ap)
|
||||||
|
{
|
||||||
|
switch(token(s))
|
||||||
|
{
|
||||||
|
case '{':
|
||||||
|
return unpack_object(s, root, ap);
|
||||||
|
|
||||||
|
case '[':
|
||||||
|
return unpack_array(s, root, ap);
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
if(root && !json_is_string(root)) {
|
||||||
|
set_error(s, "<validation>", json_error_wrong_type, "Expected string, got %s",
|
||||||
|
type_name(root));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||||
|
const char **str_target;
|
||||||
|
size_t *len_target = NULL;
|
||||||
|
|
||||||
|
str_target = va_arg(*ap, const char **);
|
||||||
|
if(!str_target) {
|
||||||
|
set_error(s, "<args>", json_error_null_value, "NULL string argument");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_token(s);
|
||||||
|
|
||||||
|
if(token(s) == '%') {
|
||||||
|
len_target = va_arg(*ap, size_t *);
|
||||||
|
if(!len_target) {
|
||||||
|
set_error(s, "<args>", json_error_null_value, "NULL string length argument");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
prev_token(s);
|
||||||
|
|
||||||
|
if(root) {
|
||||||
|
*str_target = json_string_value(root);
|
||||||
|
if(len_target)
|
||||||
|
*len_target = json_string_length(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
if(root && !json_is_integer(root)) {
|
||||||
|
set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
|
||||||
|
type_name(root));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||||
|
int *target = va_arg(*ap, int*);
|
||||||
|
if(root)
|
||||||
|
*target = (int)json_integer_value(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 'I':
|
||||||
|
if(root && !json_is_integer(root)) {
|
||||||
|
set_error(s, "<validation>", json_error_wrong_type, "Expected integer, got %s",
|
||||||
|
type_name(root));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||||
|
json_int_t *target = va_arg(*ap, json_int_t*);
|
||||||
|
if(root)
|
||||||
|
*target = json_integer_value(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 'b':
|
||||||
|
if(root && !json_is_boolean(root)) {
|
||||||
|
set_error(s, "<validation>", json_error_wrong_type, "Expected true or false, got %s",
|
||||||
|
type_name(root));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||||
|
int *target = va_arg(*ap, int*);
|
||||||
|
if(root)
|
||||||
|
*target = json_is_true(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
if(root && !json_is_real(root)) {
|
||||||
|
set_error(s, "<validation>", json_error_wrong_type, "Expected real, got %s",
|
||||||
|
type_name(root));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||||
|
double *target = va_arg(*ap, double*);
|
||||||
|
if(root)
|
||||||
|
*target = json_real_value(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 'F':
|
||||||
|
if(root && !json_is_number(root)) {
|
||||||
|
set_error(s, "<validation>", json_error_wrong_type, "Expected real or integer, got %s",
|
||||||
|
type_name(root));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||||
|
double *target = va_arg(*ap, double*);
|
||||||
|
if(root)
|
||||||
|
*target = json_number_value(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 'O':
|
||||||
|
if(root && !(s->flags & JSON_VALIDATE_ONLY))
|
||||||
|
json_incref(root);
|
||||||
|
/* Fall through */
|
||||||
|
|
||||||
|
case 'o':
|
||||||
|
if(!(s->flags & JSON_VALIDATE_ONLY)) {
|
||||||
|
json_t **target = va_arg(*ap, json_t**);
|
||||||
|
if(root)
|
||||||
|
*target = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
/* Never assign, just validate */
|
||||||
|
if(root && !json_is_null(root)) {
|
||||||
|
set_error(s, "<validation>", json_error_wrong_type, "Expected null, got %s",
|
||||||
|
type_name(root));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
default:
|
||||||
|
set_error(s, "<format>", json_error_invalid_format, "Unexpected format character '%c'",
|
||||||
|
token(s));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *json_vpack_ex(json_error_t *error, size_t flags,
|
||||||
|
const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
scanner_t s;
|
||||||
|
va_list ap_copy;
|
||||||
|
json_t *value;
|
||||||
|
|
||||||
|
if(!fmt || !*fmt) {
|
||||||
|
jsonp_error_init(error, "<format>");
|
||||||
|
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
jsonp_error_init(error, NULL);
|
||||||
|
|
||||||
|
scanner_init(&s, error, flags, fmt);
|
||||||
|
next_token(&s);
|
||||||
|
|
||||||
|
va_copy(ap_copy, ap);
|
||||||
|
value = pack(&s, &ap_copy);
|
||||||
|
va_end(ap_copy);
|
||||||
|
|
||||||
|
/* This will cover all situations where s.has_error is true */
|
||||||
|
if(!value)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
next_token(&s);
|
||||||
|
if(token(&s)) {
|
||||||
|
json_decref(value);
|
||||||
|
set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
json_t *value;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
value = json_vpack_ex(error, flags, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *json_pack(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
json_t *value;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
value = json_vpack_ex(NULL, 0, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
|
||||||
|
const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
scanner_t s;
|
||||||
|
va_list ap_copy;
|
||||||
|
|
||||||
|
if(!root) {
|
||||||
|
jsonp_error_init(error, "<root>");
|
||||||
|
jsonp_error_set(error, -1, -1, 0, json_error_null_value, "NULL root value");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!fmt || !*fmt) {
|
||||||
|
jsonp_error_init(error, "<format>");
|
||||||
|
jsonp_error_set(error, -1, -1, 0, json_error_invalid_argument, "NULL or empty format string");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
jsonp_error_init(error, NULL);
|
||||||
|
|
||||||
|
scanner_init(&s, error, flags, fmt);
|
||||||
|
next_token(&s);
|
||||||
|
|
||||||
|
va_copy(ap_copy, ap);
|
||||||
|
if(unpack(&s, root, &ap_copy)) {
|
||||||
|
va_end(ap_copy);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
va_end(ap_copy);
|
||||||
|
|
||||||
|
next_token(&s);
|
||||||
|
if(token(&s)) {
|
||||||
|
set_error(&s, "<format>", json_error_invalid_format, "Garbage after format string");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = json_vunpack_ex(root, error, flags, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_unpack(json_t *root, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = json_vunpack_ex(root, NULL, 0, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
111
src/jansson/strbuffer.c
Normal file
111
src/jansson/strbuffer.c
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "jansson_private.h"
|
||||||
|
#include "strbuffer.h"
|
||||||
|
|
||||||
|
#define STRBUFFER_MIN_SIZE 16
|
||||||
|
#define STRBUFFER_FACTOR 2
|
||||||
|
#define STRBUFFER_SIZE_MAX ((size_t)-1)
|
||||||
|
|
||||||
|
int strbuffer_init(strbuffer_t *strbuff)
|
||||||
|
{
|
||||||
|
strbuff->size = STRBUFFER_MIN_SIZE;
|
||||||
|
strbuff->length = 0;
|
||||||
|
|
||||||
|
strbuff->value = (char*)jsonp_malloc(strbuff->size);
|
||||||
|
if(!strbuff->value)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* initialize to empty */
|
||||||
|
strbuff->value[0] = '\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuffer_close(strbuffer_t *strbuff)
|
||||||
|
{
|
||||||
|
if(strbuff->value)
|
||||||
|
jsonp_free(strbuff->value);
|
||||||
|
|
||||||
|
strbuff->size = 0;
|
||||||
|
strbuff->length = 0;
|
||||||
|
strbuff->value = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuffer_clear(strbuffer_t *strbuff)
|
||||||
|
{
|
||||||
|
strbuff->length = 0;
|
||||||
|
strbuff->value[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *strbuffer_value(const strbuffer_t *strbuff)
|
||||||
|
{
|
||||||
|
return strbuff->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strbuffer_steal_value(strbuffer_t *strbuff)
|
||||||
|
{
|
||||||
|
char *result = strbuff->value;
|
||||||
|
strbuff->value = NULL;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strbuffer_append_byte(strbuffer_t *strbuff, char byte)
|
||||||
|
{
|
||||||
|
return strbuffer_append_bytes(strbuff, &byte, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size)
|
||||||
|
{
|
||||||
|
if(size >= strbuff->size - strbuff->length)
|
||||||
|
{
|
||||||
|
size_t new_size;
|
||||||
|
char *new_value;
|
||||||
|
|
||||||
|
/* avoid integer overflow */
|
||||||
|
if (strbuff->size > STRBUFFER_SIZE_MAX / STRBUFFER_FACTOR
|
||||||
|
|| size > STRBUFFER_SIZE_MAX - 1
|
||||||
|
|| strbuff->length > STRBUFFER_SIZE_MAX - 1 - size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
new_size = max(strbuff->size * STRBUFFER_FACTOR,
|
||||||
|
strbuff->length + size + 1);
|
||||||
|
|
||||||
|
new_value = (char*)jsonp_malloc(new_size);
|
||||||
|
if(!new_value)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memcpy(new_value, strbuff->value, strbuff->length);
|
||||||
|
|
||||||
|
jsonp_free(strbuff->value);
|
||||||
|
strbuff->value = new_value;
|
||||||
|
strbuff->size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(strbuff->value + strbuff->length, data, size);
|
||||||
|
strbuff->length += size;
|
||||||
|
strbuff->value[strbuff->length] = '\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char strbuffer_pop(strbuffer_t *strbuff)
|
||||||
|
{
|
||||||
|
if(strbuff->length > 0) {
|
||||||
|
char c = strbuff->value[--strbuff->length];
|
||||||
|
strbuff->value[strbuff->length] = '\0';
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return '\0';
|
||||||
|
}
|
||||||
34
src/jansson/strbuffer.h
Normal file
34
src/jansson/strbuffer.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STRBUFFER_H
|
||||||
|
#define STRBUFFER_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *value;
|
||||||
|
size_t length; /* bytes used */
|
||||||
|
size_t size; /* bytes allocated */
|
||||||
|
} strbuffer_t;
|
||||||
|
|
||||||
|
int strbuffer_init(strbuffer_t *strbuff) JSON_ATTRS(warn_unused_result);
|
||||||
|
void strbuffer_close(strbuffer_t *strbuff);
|
||||||
|
|
||||||
|
void strbuffer_clear(strbuffer_t *strbuff);
|
||||||
|
|
||||||
|
const char *strbuffer_value(const strbuffer_t *strbuff);
|
||||||
|
|
||||||
|
/* Steal the value and close the strbuffer */
|
||||||
|
char *strbuffer_steal_value(strbuffer_t *strbuff);
|
||||||
|
|
||||||
|
int strbuffer_append_byte(strbuffer_t *strbuff, char byte);
|
||||||
|
int strbuffer_append_bytes(strbuffer_t *strbuff, const char *data, size_t size);
|
||||||
|
|
||||||
|
char strbuffer_pop(strbuffer_t *strbuff);
|
||||||
|
|
||||||
|
#endif
|
||||||
143
src/jansson/strconv.c
Normal file
143
src/jansson/strconv.c
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#undef __NO_ISOCEXT /* ensure stdlib.h will declare prototypes for mingw own 'strtod' replacement, called '__strtod' */
|
||||||
|
#endif
|
||||||
|
#include "jansson_private.h"
|
||||||
|
#include "strbuffer.h"
|
||||||
|
|
||||||
|
/* need jansson_config.h to get the correct snprintf */
|
||||||
|
#include "jansson_config.h"
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#define strtod __strtod
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JSON_HAVE_LOCALECONV
|
||||||
|
#include <locale.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
- This code assumes that the decimal separator is exactly one
|
||||||
|
character.
|
||||||
|
|
||||||
|
- If setlocale() is called by another thread between the call to
|
||||||
|
localeconv() and the call to sprintf() or strtod(), the result may
|
||||||
|
be wrong. setlocale() is not thread-safe and should not be used
|
||||||
|
this way. Multi-threaded programs should use uselocale() instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void to_locale(strbuffer_t *strbuffer)
|
||||||
|
{
|
||||||
|
const char *point;
|
||||||
|
char *pos;
|
||||||
|
|
||||||
|
point = localeconv()->decimal_point;
|
||||||
|
if(*point == '.') {
|
||||||
|
/* No conversion needed */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = strchr(strbuffer->value, '.');
|
||||||
|
if(pos)
|
||||||
|
*pos = *point;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void from_locale(char *buffer)
|
||||||
|
{
|
||||||
|
const char *point;
|
||||||
|
char *pos;
|
||||||
|
|
||||||
|
point = localeconv()->decimal_point;
|
||||||
|
if(*point == '.') {
|
||||||
|
/* No conversion needed */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = strchr(buffer, *point);
|
||||||
|
if(pos)
|
||||||
|
*pos = '.';
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int jsonp_strtod(strbuffer_t *strbuffer, double *out)
|
||||||
|
{
|
||||||
|
double value;
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
#if JSON_HAVE_LOCALECONV
|
||||||
|
to_locale(strbuffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
value = strtod(strbuffer->value, &end);
|
||||||
|
assert(end == strbuffer->value + strbuffer->length);
|
||||||
|
|
||||||
|
if((value == HUGE_VAL || value == -HUGE_VAL) && errno == ERANGE) {
|
||||||
|
/* Overflow */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = value;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int jsonp_dtostr(char *buffer, size_t size, double value, int precision)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
char *start, *end;
|
||||||
|
size_t length;
|
||||||
|
|
||||||
|
if (precision == 0)
|
||||||
|
precision = 17;
|
||||||
|
|
||||||
|
ret = snprintf(buffer, size, "%.*g", precision, value);
|
||||||
|
if(ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
length = (size_t)ret;
|
||||||
|
if(length >= size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
#if JSON_HAVE_LOCALECONV
|
||||||
|
from_locale(buffer);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Make sure there's a dot or 'e' in the output. Otherwise
|
||||||
|
a real is converted to an integer when decoding */
|
||||||
|
if(strchr(buffer, '.') == NULL &&
|
||||||
|
strchr(buffer, 'e') == NULL)
|
||||||
|
{
|
||||||
|
if(length + 3 >= size) {
|
||||||
|
/* No space to append ".0" */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
buffer[length] = '.';
|
||||||
|
buffer[length + 1] = '0';
|
||||||
|
buffer[length + 2] = '\0';
|
||||||
|
length += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove leading '+' from positive exponent. Also remove leading
|
||||||
|
zeros from exponents (added by some printf() implementations) */
|
||||||
|
start = strchr(buffer, 'e');
|
||||||
|
if(start) {
|
||||||
|
start++;
|
||||||
|
end = start + 1;
|
||||||
|
|
||||||
|
if(*start == '-')
|
||||||
|
start++;
|
||||||
|
|
||||||
|
while(*end == '0')
|
||||||
|
end++;
|
||||||
|
|
||||||
|
if(end != start) {
|
||||||
|
memmove(start, end, length - (size_t)(end - buffer));
|
||||||
|
length -= (size_t)(end - start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)length;
|
||||||
|
}
|
||||||
187
src/jansson/utf.c
Normal file
187
src/jansson/utf.c
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "utf.h"
|
||||||
|
|
||||||
|
int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
|
||||||
|
{
|
||||||
|
if(codepoint < 0)
|
||||||
|
return -1;
|
||||||
|
else if(codepoint < 0x80)
|
||||||
|
{
|
||||||
|
buffer[0] = (char)codepoint;
|
||||||
|
*size = 1;
|
||||||
|
}
|
||||||
|
else if(codepoint < 0x800)
|
||||||
|
{
|
||||||
|
buffer[0] = 0xC0 + ((codepoint & 0x7C0) >> 6);
|
||||||
|
buffer[1] = 0x80 + ((codepoint & 0x03F));
|
||||||
|
*size = 2;
|
||||||
|
}
|
||||||
|
else if(codepoint < 0x10000)
|
||||||
|
{
|
||||||
|
buffer[0] = 0xE0 + ((codepoint & 0xF000) >> 12);
|
||||||
|
buffer[1] = 0x80 + ((codepoint & 0x0FC0) >> 6);
|
||||||
|
buffer[2] = 0x80 + ((codepoint & 0x003F));
|
||||||
|
*size = 3;
|
||||||
|
}
|
||||||
|
else if(codepoint <= 0x10FFFF)
|
||||||
|
{
|
||||||
|
buffer[0] = 0xF0 + ((codepoint & 0x1C0000) >> 18);
|
||||||
|
buffer[1] = 0x80 + ((codepoint & 0x03F000) >> 12);
|
||||||
|
buffer[2] = 0x80 + ((codepoint & 0x000FC0) >> 6);
|
||||||
|
buffer[3] = 0x80 + ((codepoint & 0x00003F));
|
||||||
|
*size = 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t utf8_check_first(char byte)
|
||||||
|
{
|
||||||
|
unsigned char u = (unsigned char)byte;
|
||||||
|
|
||||||
|
if(u < 0x80)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if(0x80 <= u && u <= 0xBF) {
|
||||||
|
/* second, third or fourth byte of a multi-byte
|
||||||
|
sequence, i.e. a "continuation byte" */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if(u == 0xC0 || u == 0xC1) {
|
||||||
|
/* overlong encoding of an ASCII byte */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if(0xC2 <= u && u <= 0xDF) {
|
||||||
|
/* 2-byte sequence */
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(0xE0 <= u && u <= 0xEF) {
|
||||||
|
/* 3-byte sequence */
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
else if(0xF0 <= u && u <= 0xF4) {
|
||||||
|
/* 4-byte sequence */
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
else { /* u >= 0xF5 */
|
||||||
|
/* Restricted (start of 4-, 5- or 6-byte sequence) or invalid
|
||||||
|
UTF-8 */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int32_t value = 0;
|
||||||
|
unsigned char u = (unsigned char)buffer[0];
|
||||||
|
|
||||||
|
if(size == 2)
|
||||||
|
{
|
||||||
|
value = u & 0x1F;
|
||||||
|
}
|
||||||
|
else if(size == 3)
|
||||||
|
{
|
||||||
|
value = u & 0xF;
|
||||||
|
}
|
||||||
|
else if(size == 4)
|
||||||
|
{
|
||||||
|
value = u & 0x7;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for(i = 1; i < size; i++)
|
||||||
|
{
|
||||||
|
u = (unsigned char)buffer[i];
|
||||||
|
|
||||||
|
if(u < 0x80 || u > 0xBF) {
|
||||||
|
/* not a continuation byte */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = (value << 6) + (u & 0x3F);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(value > 0x10FFFF) {
|
||||||
|
/* not in Unicode range */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(0xD800 <= value && value <= 0xDFFF) {
|
||||||
|
/* invalid code point (UTF-16 surrogate halves) */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if((size == 2 && value < 0x80) ||
|
||||||
|
(size == 3 && value < 0x800) ||
|
||||||
|
(size == 4 && value < 0x10000)) {
|
||||||
|
/* overlong encoding */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(codepoint)
|
||||||
|
*codepoint = value;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
|
||||||
|
{
|
||||||
|
size_t count;
|
||||||
|
int32_t value;
|
||||||
|
|
||||||
|
if(!bufsize)
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
count = utf8_check_first(buffer[0]);
|
||||||
|
if(count <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if(count == 1)
|
||||||
|
value = (unsigned char)buffer[0];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(count > bufsize || !utf8_check_full(buffer, count, &value))
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(codepoint)
|
||||||
|
*codepoint = value;
|
||||||
|
|
||||||
|
return buffer + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int utf8_check_string(const char *string, size_t length)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for(i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
size_t count = utf8_check_first(string[i]);
|
||||||
|
if(count == 0)
|
||||||
|
return 0;
|
||||||
|
else if(count > 1)
|
||||||
|
{
|
||||||
|
if(count > length - i)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(!utf8_check_full(&string[i], count, NULL))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
i += count - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
25
src/jansson/utf.h
Normal file
25
src/jansson/utf.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef UTF_H
|
||||||
|
#define UTF_H
|
||||||
|
|
||||||
|
#include "jansson_config.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_STDINT_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int utf8_encode(int32_t codepoint, char *buffer, size_t *size);
|
||||||
|
|
||||||
|
size_t utf8_check_first(char byte);
|
||||||
|
size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint);
|
||||||
|
const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint);
|
||||||
|
|
||||||
|
int utf8_check_string(const char *string, size_t length);
|
||||||
|
|
||||||
|
#endif
|
||||||
1076
src/jansson/value.c
Normal file
1076
src/jansson/value.c
Normal file
File diff suppressed because it is too large
Load Diff
53
src/main.c
Normal file
53
src/main.c
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "presentation/states/state_ingame.h"
|
||||||
|
#include "presentation/states/state_main_menu.h"
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
InitWindow(1280, 720, "snejk");
|
||||||
|
SetTargetFPS(60);
|
||||||
|
SetExitKey(KEY_NULL);
|
||||||
|
|
||||||
|
bool should_quit_game = false;
|
||||||
|
|
||||||
|
Presentation_State_Ingame_Context presentation_state_ingame_ctx = (Presentation_State_Ingame_Context) {
|
||||||
|
};
|
||||||
|
Presentation_State_Main_Menu_Context presentation_state_main_menu_ctx = {
|
||||||
|
.should_quit_game = &should_quit_game,
|
||||||
|
};
|
||||||
|
|
||||||
|
presentation_state_ingame_init(&presentation_state_ingame_ctx);
|
||||||
|
presentation_state_main_menu_init(&presentation_state_main_menu_ctx);
|
||||||
|
|
||||||
|
presentation_state_machine_go_to(&presentation_state_main_menu);
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
if(WindowShouldClose() || should_quit_game) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Presentation_State *state = presentation_state_machine.current;
|
||||||
|
|
||||||
|
// Tick.
|
||||||
|
if(state != NULL && state->tick != NULL) {
|
||||||
|
state->tick(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render.
|
||||||
|
BeginDrawing();
|
||||||
|
{
|
||||||
|
if(state != NULL && state->render != NULL) {
|
||||||
|
state->render(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EndDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(presentation_state_machine_go_to(NULL));
|
||||||
|
CloseWindow();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
206
src/presentation/states/state_ingame.c
Normal file
206
src/presentation/states/state_ingame.c
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#include "state_ingame.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "raygui.h"
|
||||||
|
|
||||||
|
#include "session/game_session.h"
|
||||||
|
|
||||||
|
#define GRID_CELL_SIZE 8
|
||||||
|
#define ENTITY_PRESENTATION_Y_OFFSET -2
|
||||||
|
|
||||||
|
static void state_enter(Presentation_State *state) {
|
||||||
|
Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context;
|
||||||
|
(void)ctx;
|
||||||
|
|
||||||
|
printf("Entered ingame.\n");
|
||||||
|
|
||||||
|
printf("Setting up session ...\n");
|
||||||
|
|
||||||
|
assert(g_current_session != NULL);
|
||||||
|
Game_Session_Settings *settings = &g_current_session->settings;
|
||||||
|
printf(
|
||||||
|
"Singleplayer? %i.\n"
|
||||||
|
"Settings:\n"
|
||||||
|
"- Seed: %u\n"
|
||||||
|
"- Level width: %u, height: %u\n"
|
||||||
|
"- Max players: %u\n"
|
||||||
|
,
|
||||||
|
g_current_session->is_singleplayer,
|
||||||
|
settings->seed,
|
||||||
|
settings->level_width, settings->level_height,
|
||||||
|
settings->max_players
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
printf("Setting up context ...\n");
|
||||||
|
ctx->main_camera = (Camera2D) {
|
||||||
|
.offset = (Vector2) { 0, 0 },
|
||||||
|
.target = (Vector2) { 0, 0 },
|
||||||
|
.rotation = 0.0f,
|
||||||
|
.zoom = 3.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: SS - Maybe put the textures in an array and index them using an enum?
|
||||||
|
// These should probably be loaded at start-up instead of here.
|
||||||
|
ctx->texture_grass = LoadTexture("assets/sprites/spr_floor_grass.png");
|
||||||
|
assert(IsTextureValid(ctx->texture_grass));
|
||||||
|
ctx->texture_apple = LoadTexture("assets/sprites/spr_food_apple.png");
|
||||||
|
assert(IsTextureValid(ctx->texture_apple));
|
||||||
|
ctx->texture_snake_head = LoadTexture("assets/sprites/spr_snake_head.png");
|
||||||
|
assert(IsTextureValid(ctx->texture_snake_head));
|
||||||
|
ctx->texture_snake_body = LoadTexture("assets/sprites/spr_snake_body.png");
|
||||||
|
assert(IsTextureValid(ctx->texture_snake_body));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void state_tick(Presentation_State *state) {
|
||||||
|
Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context;
|
||||||
|
(void)ctx;
|
||||||
|
|
||||||
|
{ // TEMP: SS
|
||||||
|
if(IsKeyPressed(KEY_ESCAPE)) {
|
||||||
|
presentation_state_machine_go_to(&presentation_state_main_menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void state_render(Presentation_State *state) {
|
||||||
|
Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context;
|
||||||
|
(void)ctx;
|
||||||
|
|
||||||
|
ClearBackground((Color) { 240, 236, 226, 255 });
|
||||||
|
|
||||||
|
Game_World *world = g_current_session->simulation_world.game_world;
|
||||||
|
Grid *grid = &world->grid;
|
||||||
|
|
||||||
|
uint32_t grid_total_size = grid->width * grid->height;
|
||||||
|
|
||||||
|
BeginMode2D(ctx->main_camera); {
|
||||||
|
// Render the level.
|
||||||
|
for(uint32_t i = 0; i < grid_total_size; i++) {
|
||||||
|
uint32_t x = i % grid->width;
|
||||||
|
uint32_t y = i / grid->width;
|
||||||
|
|
||||||
|
uint32_t pres_x = x * GRID_CELL_SIZE;
|
||||||
|
uint32_t pres_y = y * GRID_CELL_SIZE;
|
||||||
|
|
||||||
|
uint32_t random_floor_index = i; // TODO: SS - Get a deterministic random value based on seed.
|
||||||
|
|
||||||
|
DrawTextureRec(
|
||||||
|
ctx->texture_grass,
|
||||||
|
(Rectangle) { GRID_CELL_SIZE * random_floor_index, 0, GRID_CELL_SIZE, GRID_CELL_SIZE },
|
||||||
|
(Vector2) { pres_x, pres_y },
|
||||||
|
WHITE
|
||||||
|
);
|
||||||
|
DrawRectangleLines(pres_x, pres_y, GRID_CELL_SIZE, GRID_CELL_SIZE, (Color) { 0, 0, 0, 8 });
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < grid_total_size; i++) {
|
||||||
|
uint32_t x = i % grid->width;
|
||||||
|
uint32_t y = i / grid->width;
|
||||||
|
|
||||||
|
uint32_t pres_x = x * GRID_CELL_SIZE;
|
||||||
|
uint32_t pres_y = y * GRID_CELL_SIZE;
|
||||||
|
|
||||||
|
Grid_Cell *cell = &grid->cells[i];
|
||||||
|
Entity *entity = &cell->entity;
|
||||||
|
switch(entity->type) {
|
||||||
|
case Entity_Type_None: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Entity_Type_Snake_Head:
|
||||||
|
case Entity_Type_Snake_Body: {
|
||||||
|
Color tint = (Color) { 255, 135, 102, 255 }; // TODO: SS - Get tint based on player ID.
|
||||||
|
|
||||||
|
Vector2 origin = (Vector2) { GRID_CELL_SIZE/2, GRID_CELL_SIZE/2 };
|
||||||
|
|
||||||
|
float sin_frequency = 0.0f;
|
||||||
|
float sin_amplitude = 0.0f;
|
||||||
|
|
||||||
|
Texture2D *texture = NULL;
|
||||||
|
if(entity->type == Entity_Type_Snake_Head) {
|
||||||
|
texture = &ctx->texture_snake_head;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
texture = &ctx->texture_snake_body;
|
||||||
|
// TODO: SS - If it's a body, check what index it is and use that as an y-offset to make it look cool, like a wave.
|
||||||
|
sin_frequency = 4.0f;
|
||||||
|
sin_amplitude = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawTexturePro(
|
||||||
|
*texture,
|
||||||
|
(Rectangle) { // Source.
|
||||||
|
0, 0, GRID_CELL_SIZE, GRID_CELL_SIZE
|
||||||
|
},
|
||||||
|
(Rectangle) { // Destination.
|
||||||
|
pres_x + origin.x,
|
||||||
|
pres_y + origin.y + ENTITY_PRESENTATION_Y_OFFSET - (sin(GetTime() * sin_frequency)) * sin_amplitude,
|
||||||
|
GRID_CELL_SIZE,
|
||||||
|
GRID_CELL_SIZE
|
||||||
|
},
|
||||||
|
origin, // Origin.
|
||||||
|
0, // Rotation.
|
||||||
|
tint // Tint.
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Entity_Type_Food: {
|
||||||
|
uint32_t flash = ((sin(GetTime() * 8) + 1)/2) * 32;
|
||||||
|
Color tint = (Color) { 255-flash, 255-flash, 255-flash, 255 };
|
||||||
|
|
||||||
|
Vector2 origin = (Vector2) { GRID_CELL_SIZE/2, GRID_CELL_SIZE/2 };
|
||||||
|
|
||||||
|
DrawTexturePro(
|
||||||
|
ctx->texture_apple,
|
||||||
|
(Rectangle) { // Source.
|
||||||
|
0, 0, GRID_CELL_SIZE, GRID_CELL_SIZE
|
||||||
|
},
|
||||||
|
(Rectangle) { // Destination.
|
||||||
|
pres_x + origin.x,
|
||||||
|
pres_y + origin.y + ENTITY_PRESENTATION_Y_OFFSET - ((sin(GetTime() * 12) + 1)/2) * 1,
|
||||||
|
GRID_CELL_SIZE,
|
||||||
|
GRID_CELL_SIZE
|
||||||
|
},
|
||||||
|
origin, // Origin.
|
||||||
|
0, // Rotation.
|
||||||
|
tint // Tint.
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EndMode2D();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void state_exit(Presentation_State *state) {
|
||||||
|
Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context;
|
||||||
|
(void)ctx;
|
||||||
|
printf("Exiting ingame\n");
|
||||||
|
|
||||||
|
UnloadTexture(ctx->texture_grass);
|
||||||
|
UnloadTexture(ctx->texture_apple);
|
||||||
|
UnloadTexture(ctx->texture_snake_head);
|
||||||
|
UnloadTexture(ctx->texture_snake_body);
|
||||||
|
|
||||||
|
game_session_destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
Presentation_State presentation_state_ingame;
|
||||||
|
|
||||||
|
void presentation_state_ingame_init(Presentation_State_Ingame_Context *ctx) {
|
||||||
|
presentation_state_ingame = (Presentation_State) {
|
||||||
|
.name = "Ingame",
|
||||||
|
.context = (void *)ctx,
|
||||||
|
.enter = state_enter,
|
||||||
|
.tick = state_tick,
|
||||||
|
.render = state_render,
|
||||||
|
.exit = state_exit
|
||||||
|
};
|
||||||
|
}
|
||||||
21
src/presentation/states/state_ingame.h
Normal file
21
src/presentation/states/state_ingame.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef PRES_STATE_INGAME_H
|
||||||
|
#define PRES_STATE_INGAME_H
|
||||||
|
|
||||||
|
#include "states.h"
|
||||||
|
#include "simulation/simulation_world.h"
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Camera2D main_camera;
|
||||||
|
|
||||||
|
// Textures.
|
||||||
|
Texture2D texture_grass;
|
||||||
|
Texture2D texture_apple;
|
||||||
|
Texture2D texture_snake_head;
|
||||||
|
Texture2D texture_snake_body;
|
||||||
|
} Presentation_State_Ingame_Context;
|
||||||
|
|
||||||
|
void presentation_state_ingame_init(Presentation_State_Ingame_Context *ctx);
|
||||||
|
|
||||||
|
#endif
|
||||||
48
src/presentation/states/state_machine.c
Normal file
48
src/presentation/states/state_machine.c
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#include "states.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
Presentation_State_Machine presentation_state_machine = (Presentation_State_Machine) {
|
||||||
|
.current = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
Presentation_State presentation_state_create(
|
||||||
|
const char *name,
|
||||||
|
void *context,
|
||||||
|
Presentation_State_Enter_Callback enter,
|
||||||
|
Presentation_State_Tick_Callback tick,
|
||||||
|
Presentation_State_Render_Callback render,
|
||||||
|
Presentation_State_Exit_Callback exit
|
||||||
|
) {
|
||||||
|
return (Presentation_State) {
|
||||||
|
.name = name,
|
||||||
|
.context = context,
|
||||||
|
.enter = enter,
|
||||||
|
.tick = tick,
|
||||||
|
.render = render,
|
||||||
|
.exit = exit,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool presentation_state_machine_go_to(Presentation_State *target) {
|
||||||
|
if(presentation_state_machine.current == target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(presentation_state_machine.current != NULL) {
|
||||||
|
assert(presentation_state_machine.current->exit != NULL);
|
||||||
|
presentation_state_machine.current->exit(presentation_state_machine.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
presentation_state_machine.current = target;
|
||||||
|
|
||||||
|
if(presentation_state_machine.current == NULL) {
|
||||||
|
return true; // Return true here even though it's NULL. This is nice for when we want to exit the program and the current state.
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(presentation_state_machine.current->enter != NULL);
|
||||||
|
presentation_state_machine.current->enter(presentation_state_machine.current);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
40
src/presentation/states/state_machine.h
Normal file
40
src/presentation/states/state_machine.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#ifndef PRES_STATE_MACHINE_H
|
||||||
|
#define PRES_STATE_MACHINE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct Presentation_State Presentation_State;
|
||||||
|
|
||||||
|
typedef void (*Presentation_State_Enter_Callback)(Presentation_State *state);
|
||||||
|
typedef void (*Presentation_State_Tick_Callback)(Presentation_State *state);
|
||||||
|
typedef void (*Presentation_State_Render_Callback)(Presentation_State *state);
|
||||||
|
typedef void (*Presentation_State_Exit_Callback)(Presentation_State *state);
|
||||||
|
|
||||||
|
struct Presentation_State {
|
||||||
|
const char *name;
|
||||||
|
void *context;
|
||||||
|
|
||||||
|
Presentation_State_Enter_Callback enter;
|
||||||
|
Presentation_State_Tick_Callback tick;
|
||||||
|
Presentation_State_Render_Callback render;
|
||||||
|
Presentation_State_Exit_Callback exit;
|
||||||
|
};
|
||||||
|
|
||||||
|
Presentation_State presentation_state_create(
|
||||||
|
const char *name,
|
||||||
|
void *context,
|
||||||
|
Presentation_State_Enter_Callback enter,
|
||||||
|
Presentation_State_Tick_Callback tick,
|
||||||
|
Presentation_State_Render_Callback render,
|
||||||
|
Presentation_State_Exit_Callback exit
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Presentation_State *current;
|
||||||
|
} Presentation_State_Machine;
|
||||||
|
|
||||||
|
extern Presentation_State_Machine presentation_state_machine;
|
||||||
|
|
||||||
|
bool presentation_state_machine_go_to(Presentation_State *target);
|
||||||
|
|
||||||
|
#endif
|
||||||
127
src/presentation/states/state_main_menu.c
Normal file
127
src/presentation/states/state_main_menu.c
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#include "state_main_menu.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "raylib.h"
|
||||||
|
#include "raygui.h"
|
||||||
|
|
||||||
|
static void state_enter(Presentation_State *state) {
|
||||||
|
Presentation_State_Main_Menu_Context *ctx = (Presentation_State_Main_Menu_Context *)state->context;
|
||||||
|
(void)ctx;
|
||||||
|
|
||||||
|
printf("Entered main menu\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void state_tick(Presentation_State *state) {
|
||||||
|
(void)state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void state_render(Presentation_State *state) {
|
||||||
|
Presentation_State_Main_Menu_Context *ctx = (Presentation_State_Main_Menu_Context *)state->context;
|
||||||
|
|
||||||
|
ClearBackground((Color) {
|
||||||
|
230, 204, 138, 255
|
||||||
|
});
|
||||||
|
|
||||||
|
#define BUTTON_HEIGHT 32
|
||||||
|
|
||||||
|
switch(ctx->mode) {
|
||||||
|
case Main_Menu_Mode_Home: {
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 0), 128, BUTTON_HEIGHT }, "#191#Singleplayer")) {
|
||||||
|
ctx->mode = Main_Menu_Mode_Singleplayer;
|
||||||
|
}
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "#191#Multiplayer")) {
|
||||||
|
ctx->mode = Main_Menu_Mode_Multiplayer;
|
||||||
|
}
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 2), 128, BUTTON_HEIGHT }, "#191#Quit")) {
|
||||||
|
*(ctx->should_quit_game) = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Main_Menu_Mode_Singleplayer: {
|
||||||
|
ctx->is_singleplayer = true;
|
||||||
|
ctx->mode = Main_Menu_Mode_Game_Setup;
|
||||||
|
game_session_init_default_settings(ctx->is_singleplayer, &ctx->session_settings);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Main_Menu_Mode_Multiplayer: {
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 0), 128, BUTTON_HEIGHT }, "#191#Host")) {
|
||||||
|
ctx->is_singleplayer = false;
|
||||||
|
ctx->mode = Main_Menu_Mode_Game_Setup;
|
||||||
|
game_session_init_default_settings(ctx->is_singleplayer, &ctx->session_settings);
|
||||||
|
}
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "#191#Join")) {
|
||||||
|
ctx->mode = Main_Menu_Mode_Multiplayer_Join;
|
||||||
|
}
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 2), 128, BUTTON_HEIGHT }, "#191#Back to Main Menu")) {
|
||||||
|
ctx->mode = Main_Menu_Mode_Home;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Main_Menu_Mode_Game_Setup: {
|
||||||
|
// TODO: SS - Add options for the game here so players can customize..
|
||||||
|
// - public or locked session (unlock with password?)
|
||||||
|
// - max-players
|
||||||
|
// - game-speed (tickrate)
|
||||||
|
// - match-time
|
||||||
|
// - map
|
||||||
|
// - seed
|
||||||
|
// - obstacles
|
||||||
|
// - power-ups
|
||||||
|
// etc..
|
||||||
|
// Modify 'ctx->session_settings'.
|
||||||
|
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 0), 128, BUTTON_HEIGHT }, "#191#Play")) {
|
||||||
|
// Set up the ingame-context and transition to the ingame-state.
|
||||||
|
// NOTE: SS - We might need to wait before transitioning when playing multiplayer.
|
||||||
|
game_session_create(
|
||||||
|
ctx->is_singleplayer,
|
||||||
|
!ctx->is_singleplayer,
|
||||||
|
ctx->session_settings
|
||||||
|
);
|
||||||
|
|
||||||
|
presentation_state_machine_go_to(&presentation_state_ingame);
|
||||||
|
}
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "#191#Cancel")) {
|
||||||
|
ctx->mode = ctx->is_singleplayer ? Main_Menu_Mode_Home : Main_Menu_Mode_Multiplayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Main_Menu_Mode_Multiplayer_Join: {
|
||||||
|
// TODO: SS - Add text-input here so the player can specify what session to try connecting to.
|
||||||
|
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 1), 128, BUTTON_HEIGHT }, "#191#Connect")) {
|
||||||
|
printf("TODO: SS - Connect to session id.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GuiButton((Rectangle){ 64, 64 + (BUTTON_HEIGHT * 2), 128, BUTTON_HEIGHT }, "#191#Cancel")) {
|
||||||
|
ctx->mode = Main_Menu_Mode_Multiplayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void state_exit(Presentation_State *state) {
|
||||||
|
(void)state;
|
||||||
|
printf("Exited main menu\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
Presentation_State presentation_state_main_menu;
|
||||||
|
|
||||||
|
void presentation_state_main_menu_init(Presentation_State_Main_Menu_Context *ctx) {
|
||||||
|
presentation_state_main_menu = (Presentation_State) {
|
||||||
|
.name = "Main Menu",
|
||||||
|
.context = (void *)ctx,
|
||||||
|
.enter = state_enter,
|
||||||
|
.tick = state_tick,
|
||||||
|
.render = state_render,
|
||||||
|
.exit = state_exit
|
||||||
|
};
|
||||||
|
}
|
||||||
27
src/presentation/states/state_main_menu.h
Normal file
27
src/presentation/states/state_main_menu.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef PRES_STATE_MAIN_MENU_H
|
||||||
|
#define PRES_STATE_MAIN_MENU_H
|
||||||
|
|
||||||
|
#include "states.h"
|
||||||
|
#include "session/game_session.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
Main_Menu_Mode_Home,
|
||||||
|
Main_Menu_Mode_Singleplayer,
|
||||||
|
Main_Menu_Mode_Multiplayer,
|
||||||
|
|
||||||
|
Main_Menu_Mode_Game_Setup,
|
||||||
|
Main_Menu_Mode_Multiplayer_Join,
|
||||||
|
} Main_Menu_Mode;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Main_Menu_Mode mode;
|
||||||
|
|
||||||
|
bool is_singleplayer;
|
||||||
|
Game_Session_Settings session_settings;
|
||||||
|
|
||||||
|
bool *should_quit_game;
|
||||||
|
} Presentation_State_Main_Menu_Context;
|
||||||
|
|
||||||
|
void presentation_state_main_menu_init(Presentation_State_Main_Menu_Context *ctx);
|
||||||
|
|
||||||
|
#endif
|
||||||
10
src/presentation/states/states.h
Normal file
10
src/presentation/states/states.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef PRES_STATES_H
|
||||||
|
#define PRES_STATES_H
|
||||||
|
|
||||||
|
#include "state_machine.h"
|
||||||
|
|
||||||
|
extern Presentation_State presentation_state_main_menu;
|
||||||
|
extern Presentation_State presentation_state_ingame;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
5987
src/raygui.h
Normal file
5987
src/raygui.h
Normal file
File diff suppressed because it is too large
Load Diff
1727
src/raylib.h
Normal file
1727
src/raylib.h
Normal file
File diff suppressed because it is too large
Load Diff
46
src/session/game_session.c
Normal file
46
src/session/game_session.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "game_session.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
Game_Session *g_current_session = NULL;
|
||||||
|
|
||||||
|
void game_session_create(bool is_singleplayer, bool is_host, Game_Session_Settings settings) {
|
||||||
|
assert(g_current_session == NULL);
|
||||||
|
|
||||||
|
Game_Session *session = (Game_Session *)calloc(1, sizeof(Game_Session));
|
||||||
|
session->is_singleplayer = is_singleplayer;
|
||||||
|
session->is_host = is_host;
|
||||||
|
session->settings = settings;
|
||||||
|
|
||||||
|
session->simulation_world = simulation_create_world(
|
||||||
|
session->settings.seed,
|
||||||
|
session->settings.max_players,
|
||||||
|
session->settings.level_width,
|
||||||
|
session->settings.level_height
|
||||||
|
);
|
||||||
|
|
||||||
|
g_current_session = session;
|
||||||
|
|
||||||
|
printf("New Game_Session created.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_session_destroy() {
|
||||||
|
if(g_current_session == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
simulation_destroy_world(&g_current_session->simulation_world);
|
||||||
|
|
||||||
|
free(g_current_session);
|
||||||
|
g_current_session = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_session_init_default_settings(bool is_singleplayer, Game_Session_Settings *out_settings) {
|
||||||
|
out_settings->seed = 1337; // TODO: SS - Randomize.
|
||||||
|
out_settings->level_width = 48;
|
||||||
|
out_settings->level_height = 48;
|
||||||
|
out_settings->max_players = is_singleplayer ? 1 : 8;
|
||||||
|
}
|
||||||
38
src/session/game_session.h
Normal file
38
src/session/game_session.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef GAME_SESSION_H
|
||||||
|
#define GAME_SESSION_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "simulation/simulation_world.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t seed;
|
||||||
|
|
||||||
|
uint16_t level_width;
|
||||||
|
uint16_t level_height;
|
||||||
|
|
||||||
|
uint8_t max_players;
|
||||||
|
|
||||||
|
// ..
|
||||||
|
} Game_Session_Settings;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool is_singleplayer;
|
||||||
|
bool is_host;
|
||||||
|
|
||||||
|
Game_Session_Settings settings;
|
||||||
|
|
||||||
|
// TODO: SS - Game-state (counting down, active, over)
|
||||||
|
// TODO: SS - Local(?) input-queue.
|
||||||
|
Simulation_World simulation_world;
|
||||||
|
} Game_Session;
|
||||||
|
|
||||||
|
extern Game_Session *g_current_session;
|
||||||
|
|
||||||
|
void game_session_create(bool is_singleplayer, bool is_host, Game_Session_Settings settings);
|
||||||
|
void game_session_destroy();
|
||||||
|
|
||||||
|
void game_session_init_default_settings(bool is_singleplayer, Game_Session_Settings *out_settings);
|
||||||
|
|
||||||
|
#endif
|
||||||
17
src/shared/entity.h
Normal file
17
src/shared/entity.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef ENTITY_H
|
||||||
|
#define ENTITY_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
Entity_Type_None,
|
||||||
|
Entity_Type_Snake_Head,
|
||||||
|
Entity_Type_Snake_Body,
|
||||||
|
Entity_Type_Food,
|
||||||
|
} Entity_Type;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Entity_Type type;
|
||||||
|
} Entity;
|
||||||
|
|
||||||
|
#endif
|
||||||
66
src/shared/game_world.c
Normal file
66
src/shared/game_world.c
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#include "game_world.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define MIN_LEVEL_WIDTH 8 // TODO: SS - Move these out of here.
|
||||||
|
#define MIN_LEVEL_HEIGHT 8
|
||||||
|
|
||||||
|
Game_World *game_world_create(uint32_t seed, uint16_t level_width, uint16_t level_height) {
|
||||||
|
assert(level_width >= MIN_LEVEL_WIDTH); // We should probably have failed earlier.
|
||||||
|
assert(level_height >= MIN_LEVEL_HEIGHT);
|
||||||
|
|
||||||
|
Game_World *w = (Game_World *)calloc(1, sizeof(Game_World));
|
||||||
|
assert(w != NULL);
|
||||||
|
w->seed = seed;
|
||||||
|
|
||||||
|
w->max_entities = level_width * level_height;
|
||||||
|
w->entities = (Entity *)calloc(w->max_entities, sizeof(Entity));
|
||||||
|
|
||||||
|
grid_initialize(&w->grid, level_width, level_height);
|
||||||
|
|
||||||
|
|
||||||
|
{ // TODO: SS - Create the level.
|
||||||
|
{
|
||||||
|
Grid_Cell *cell = grid_get_cell(&w->grid, 5, 3);
|
||||||
|
assert(cell != NULL);
|
||||||
|
cell->entity.type = Entity_Type_Food;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Grid_Cell *cell = grid_get_cell(&w->grid, 11, 4);
|
||||||
|
assert(cell != NULL);
|
||||||
|
cell->entity.type = Entity_Type_Food;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Grid_Cell *cell = grid_get_cell(&w->grid, 15, 9);
|
||||||
|
assert(cell != NULL);
|
||||||
|
cell->entity.type = Entity_Type_Food;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Grid_Cell *cell = grid_get_cell(&w->grid, 24, 15);
|
||||||
|
assert(cell != NULL);
|
||||||
|
cell->entity.type = Entity_Type_Snake_Head;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Grid_Cell *cell = grid_get_cell(&w->grid, 23, 15);
|
||||||
|
assert(cell != NULL);
|
||||||
|
cell->entity.type = Entity_Type_Snake_Body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game_world_destroy(Game_World *world) {
|
||||||
|
assert(world != NULL);
|
||||||
|
|
||||||
|
world->seed = 0;
|
||||||
|
world->max_entities = 0;
|
||||||
|
free(world->entities);
|
||||||
|
grid_dispose(&world->grid);
|
||||||
|
free(world);
|
||||||
|
}
|
||||||
20
src/shared/game_world.h
Normal file
20
src/shared/game_world.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef WORLD_H
|
||||||
|
#define WORLD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "entity.h"
|
||||||
|
#include "grid.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t seed;
|
||||||
|
|
||||||
|
Entity *entities;
|
||||||
|
uint16_t max_entities;
|
||||||
|
|
||||||
|
Grid grid;
|
||||||
|
} Game_World;
|
||||||
|
|
||||||
|
Game_World *game_world_create(uint32_t seed, uint16_t level_width, uint16_t level_height);
|
||||||
|
void game_world_destroy(Game_World *world);
|
||||||
|
|
||||||
|
#endif
|
||||||
63
src/shared/grid.c
Normal file
63
src/shared/grid.c
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#include "grid.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define GRID_MIN_WIDTH 8
|
||||||
|
#define GRID_MIN_HEIGHT 8
|
||||||
|
|
||||||
|
bool grid_initialize(Grid *grid, uint16_t width, uint16_t height) {
|
||||||
|
assert(grid != NULL);
|
||||||
|
|
||||||
|
if(width < GRID_MIN_WIDTH || height < GRID_MIN_HEIGHT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
grid->width = width;
|
||||||
|
grid->height = height;
|
||||||
|
grid->cells = calloc(width * height, sizeof(Grid_Cell));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grid_dispose(Grid *grid) {
|
||||||
|
assert(grid != NULL);
|
||||||
|
|
||||||
|
free(grid->cells);
|
||||||
|
memset(grid, 0, sizeof(Grid));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool to_grid_index(Grid *grid, uint16_t x, uint16_t y, uint16_t *out_grid_index) {
|
||||||
|
assert(grid != NULL);
|
||||||
|
assert(out_grid_index != NULL);
|
||||||
|
|
||||||
|
if (!grid || !out_grid_index) return false;
|
||||||
|
if (x >= grid->width || y >= grid->height) return false;
|
||||||
|
|
||||||
|
*out_grid_index = y * grid->width + x;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool from_grid_index(Grid *grid, uint16_t grid_index, uint16_t *out_x, uint16_t *out_y) {
|
||||||
|
assert(grid != NULL);
|
||||||
|
assert(out_x != NULL);
|
||||||
|
assert(out_y != NULL);
|
||||||
|
|
||||||
|
if (!grid || !out_x || !out_y) return false;
|
||||||
|
if (grid_index >= grid->width * grid->height) return false;
|
||||||
|
|
||||||
|
*out_x = grid_index % grid->width;
|
||||||
|
*out_y = grid_index / grid->width;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Grid_Cell *grid_get_cell(Grid *grid, uint16_t x, uint16_t y) {
|
||||||
|
assert(grid != NULL);
|
||||||
|
if(x >= grid->width || y >= grid->height) return NULL;
|
||||||
|
|
||||||
|
uint16_t grid_index = 0;
|
||||||
|
if(!to_grid_index(grid, x, y, &grid_index)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &grid->cells[grid_index];
|
||||||
|
}
|
||||||
25
src/shared/grid.h
Normal file
25
src/shared/grid.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifndef GRID_H
|
||||||
|
#define GRID_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "entity.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Entity entity;
|
||||||
|
} Grid_Cell;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
|
||||||
|
Grid_Cell *cells;
|
||||||
|
} Grid;
|
||||||
|
|
||||||
|
bool grid_initialize(Grid *grid, uint16_t width, uint16_t height);
|
||||||
|
void grid_dispose(Grid *grid);
|
||||||
|
|
||||||
|
Grid_Cell *grid_get_cell(Grid *grid, uint16_t x, uint16_t y);
|
||||||
|
|
||||||
|
#endif
|
||||||
21
src/simulation/simulation_world.c
Normal file
21
src/simulation/simulation_world.c
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include "simulation_world.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
Simulation_World simulation_create_world(uint32_t seed, uint8_t max_players, uint16_t width, uint16_t height) {
|
||||||
|
Simulation_World w;
|
||||||
|
memset(&w, 0, sizeof(Simulation_World));
|
||||||
|
|
||||||
|
w.game_world = game_world_create(seed, width, height);
|
||||||
|
assert(w.game_world != NULL);
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
void simulation_destroy_world(Simulation_World *simulation_world) {
|
||||||
|
assert(simulation_world != NULL);
|
||||||
|
|
||||||
|
game_world_destroy(simulation_world->game_world);
|
||||||
|
simulation_world->game_world = NULL;
|
||||||
|
}
|
||||||
15
src/simulation/simulation_world.h
Normal file
15
src/simulation/simulation_world.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef SIMULATION_WORLD_H
|
||||||
|
#define SIMULATION_WORLD_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "shared/game_world.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Game_World *game_world;
|
||||||
|
} Simulation_World;
|
||||||
|
|
||||||
|
Simulation_World simulation_create_world(uint32_t seed, uint8_t max_players, uint16_t width, uint16_t height);
|
||||||
|
void simulation_destroy_world(Simulation_World *simulation_world);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user