Big initial commit: States, simulation, presentation, menus, some art.

This commit is contained in:
2025-12-10 23:12:19 +01:00
parent 0b0d920c84
commit 5a71d16d1f
49 changed files with 15852 additions and 0 deletions

731
src/MultiplayerApi.c Normal file
View 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
View 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;
/* Callbacktyp för inkommande events från servern. */
typedef void (*MultiplayerListener)(
const char *event, /* "joined", "leaved", "game" */
int64_t messageId, /* sekventiellt meddelandeID (från host) */
const char *clientId, /* avsändarens klientID (eller NULL) */
json_t *data, /* JSONobjekt 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 sessionsID vid join */
};
/* Skapar en ny APIinstans. 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 JSONpayload 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/protokollfel.
*/
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 JSONdata till sessionen. */
int mp_api_game(MultiplayerApi *api, json_t *data);
/* Registrerar en lyssnare för inkommande events.
Returnerar ett positivt listenerID, eller 1 vid fel. */
int mp_api_listen(MultiplayerApi *api,
MultiplayerListener cb,
void *user_data);
/* Avregistrerar lyssnare. ListenerID är värdet från mp_api_listen. */
void mp_api_unlisten(MultiplayerApi *api, int listener_id);
#ifdef __cplusplus
}
#endif
#endif /* MULTIPLAYER_API_H */

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 B

516
src/jansson/dump.c Normal file
View 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
View 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
View 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
View 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

View 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

Binary file not shown.

412
src/jansson/jansson.h Normal file
View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

71
src/jansson/lookup3.h Normal file
View 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 wont 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

53
src/main.c Normal file
View 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;
}

View 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
};
}

View 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

View 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;
}

View 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

View 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
};
}

View 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

View 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

File diff suppressed because it is too large Load Diff

1727
src/raylib.h Normal file

File diff suppressed because it is too large Load Diff

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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

View 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;
}

View 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