diff --git a/examples/c_contract/.gitignore b/examples/c_contract/.gitignore index 7aa06b98..eceb15dd 100644 --- a/examples/c_contract/.gitignore +++ b/examples/c_contract/.gitignore @@ -1,2 +1 @@ -rnd_contract -echo_contract \ No newline at end of file +rnd_contract \ No newline at end of file diff --git a/examples/c_contract/echo_contract.c b/examples/c_contract/echo_contract.c deleted file mode 100644 index 8cc2430c..00000000 --- a/examples/c_contract/echo_contract.c +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include -#include "hotpocket_contract.h" - -// gcc echo_contract.c -o echo_contract - -void store_timestamp(const uint64_t timestamp); -void process_user_message(const struct hp_user *user, const void *buf, const uint32_t len); - -int main(int argc, char **argv) -{ - if (hp_init_contract() == -1) - return 1; - - const struct hp_contract_context *ctx = hp_get_context(); - - // We store the execution timestamp as an example state file change. - if (!ctx->readonly) - store_timestamp(ctx->timestamp); - - // Read and process all user inputs from the mmap. - const void *input_mmap = hp_init_user_input_mmap(); - - // Iterate through all users. - for (size_t u = 0; u < ctx->users.count; u++) - { - const struct hp_user *user = &ctx->users.list[u]; - - // Iterate through all inputs from this user. - for (size_t i = 0; i < user->inputs.count; i++) - { - const struct hp_user_input input = user->inputs.list[i]; - - // Instead of mmap, we can also read the inputs from 'ctx->users.in_fd' using file I/O. - // However, using mmap is recommended because user inputs already reside in memory. - const uint8_t *buf = (uint8_t *)input_mmap + input.offset; - - process_user_message(user, buf, input.size); - } - } - - // NPL message send example: - // hp_write_npl_msg("Hello!", 6); - - // NPL message receive example: - // // Allocate buffers for received message. - // char sender[HP_KEY_SIZE]; - // char *msg = malloc(HP_NPL_MSG_MAX_SIZE); - // // Wait for 200ms for incoming message. We will receive our own message as well if we are part of unl. - // const int len = hp_read_npl_msg(msg, sender, 200); - // if (len > 0) - // printf("Received %.*s from %.*s", len, msg, HP_KEY_SIZE, sender); - // free(msg); - - // Peers update example: - // const char *add_peers[2] = {"localhost:4444", "localhost:3333"}; - // hp_update_peers(add_peers, 2, NULL, 0); - - // Config update example: - // struct hp_config *config = hp_get_config(); - // hp_set_config_string(&config->version, "2.0", 4); - // config->round_limits.user_input_bytes = 1024; - // struct hp_public_key new_unl[2] = {{"ed726f9f536904b125bdca10bbdd1e66591b274799b92ac8bcfc75bf45d7da4c0f"}, - // {"ed3e63992d62804ea0c182e5b22fe43c4b652fbbf068ec7520f3020f4c3771416a"}}; - // hp_set_config_unl(config, new_unl, 2); - // hp_update_config(config); - // hp_free_config(config); - - hp_deinit_user_input_mmap(); - hp_deinit_contract(); - return 0; -} - -void store_timestamp(const uint64_t timestamp) -{ - int fd = open("exects.txt", O_RDWR | O_CREAT | O_APPEND, 0644); - if (fd > 0) - { - char tsbuf[20]; - memset(tsbuf, 0, 20); - sprintf(tsbuf, "%lu\n", timestamp); - struct iovec vec[2] = {{(void *)"ts:", 3}, {(void *)tsbuf, strlen(tsbuf)}}; - writev(fd, vec, 2); - close(fd); - } -} - -void process_user_message(const struct hp_user *user, const void *buf, const uint32_t len) -{ - if (strncmp((const char *)buf, "ts", 2) == 0) - { - int fd = open("exects.txt", O_RDONLY); - if (fd > 0) - { - struct stat st; - if (fstat(fd, &st) != -1) - { - char tsbuf[st.st_size]; - if (read(fd, tsbuf, st.st_size) > 0) - { - for (int i = 0; i < st.st_size; i++) - { - if (tsbuf[i] == '\n' || tsbuf[i] == 0) - tsbuf[i] = ' '; - } - hp_write_user_msg(user, tsbuf, st.st_size - 1); - } - } - close(fd); - } - } - else - { - struct iovec vec[2] = {{(void *)"Echoing: ", 9}, {(void *)buf, len}}; - hp_writev_user_msg(user, vec, 2); - } -} diff --git a/examples/c_contract/hotpocket_contract.h b/examples/c_contract/hotpocket_contract.h deleted file mode 100644 index a0f29c97..00000000 --- a/examples/c_contract/hotpocket_contract.h +++ /dev/null @@ -1,1141 +0,0 @@ -#ifndef __HOTPOCKET_CONTRACT_LIB_C__ -#define __HOTPOCKET_CONTRACT_LIB_C__ - -// Hot Pocket contract library version 0.5.0 - -#include -#include -#include -#include -#include -#include -#include -#include -#include "json.h" -#include - -// Private constants. -#define __HP_MMAP_BLOCK_SIZE 4096 -#define __HP_MMAP_BLOCK_ALIGN(x) (((x) + ((off_t)(__HP_MMAP_BLOCK_SIZE)-1)) & ~((off_t)(__HP_MMAP_BLOCK_SIZE)-1)) -#define __HP_STREAM_MSG_HEADER_SIZE 4 -#define __HP_SEQPKT_MAX_SIZE 131072 // 128KB to support SEQ_PACKET sockets. -const char *__HP_PATCH_FILE_PATH = "../patch.cfg"; - -// Public constants. -#define HP_NPL_MSG_MAX_SIZE __HP_SEQPKT_MAX_SIZE -#define HP_PUBLIC_KEY_SIZE 66 // Hex public_key size. (64 char key + 2 chars for key type prefix) -#define HP_PRIVATE_KEY_SIZE 130 // Hex public_key size. (128 char key + 2 chars for key type prefix) -#define HP_HASH_SIZE 64 // Hex hash size. -#define HP_CONTRACT_ID_SIZE 36 // Contract Id UUIDv4 string length. -const char *HP_POST_EXEC_SCRIPT_NAME = "post_exec.sh"; - -#define __HP_ASSIGN_STRING(dest, elem) \ - { \ - if (elem->value->type == json_type_string) \ - { \ - const struct json_string_s *value = (struct json_string_s *)elem->value->payload; \ - memcpy(dest, value->string, sizeof(dest)); \ - } \ - } - -#define __HP_ASSIGN_CHAR_PTR(dest, elem) \ - { \ - if (elem->value->type == json_type_string) \ - { \ - const struct json_string_s *value = (struct json_string_s *)elem->value->payload; \ - dest = (char *)malloc(value->string_size + 1); \ - memcpy(dest, value->string, value->string_size + 1); \ - } \ - } - -#define __HP_ASSIGN_UINT64(dest, elem) \ - { \ - if (elem->value->type == json_type_number) \ - { \ - const struct json_number_s *value = (struct json_number_s *)elem->value->payload; \ - dest = strtoull(value->number, NULL, 0); \ - } \ - } - -#define __HP_ASSIGN_INT(dest, elem) \ - { \ - if (elem->value->type == json_type_number) \ - { \ - const struct json_number_s *value = (struct json_number_s *)elem->value->payload; \ - dest = atoi(value->number); \ - } \ - } - -#define __HP_ASSIGN_BOOL(dest, elem) \ - { \ - if (elem->value->type == json_type_true) \ - dest = true; \ - else if (elem->value->type == json_type_false) \ - dest = false; \ - } - -#define __HP_FROM_BE(buf, pos) \ - ((uint8_t)buf[pos + 0] << 24 | (uint8_t)buf[pos + 1] << 16 | (uint8_t)buf[pos + 2] << 8 | (uint8_t)buf[pos + 3]) - -#define __HP_TO_BE(num, buf, pos) \ - { \ - buf[pos] = num >> 24; \ - buf[1 + pos] = num >> 16; \ - buf[2 + pos] = num >> 8; \ - buf[3 + pos] = num; \ - } - -#define __HP_FREE(ptr) \ - { \ - free(ptr); \ - ptr = NULL; \ - } - -#define __HP_UPDATE_CONFIG_ERROR(msg) \ - { \ - fprintf(stderr, "%s\n", msg); \ - return -1; \ - } - -enum MODE -{ - PUBLIC, - PRIVATE -}; - -struct hp_user_input -{ - off_t offset; - uint32_t size; -}; - -struct hp_user_inputs_collection -{ - struct hp_user_input *list; - size_t count; -}; - -struct hp_public_key -{ - char data[HP_PUBLIC_KEY_SIZE + 1]; // +1 for null char. -}; - -struct hp_private_key -{ - char data[HP_PRIVATE_KEY_SIZE + 1]; // +1 for null char. -}; - -// Represents a user that is connected to HP cluster. -struct hp_user -{ - struct hp_public_key public_key; - int outfd; - struct hp_user_inputs_collection inputs; -}; - -// Represents a node that's part of unl. -struct hp_unl_node -{ - struct hp_public_key public_key; - uint64_t active_on; -}; - -struct hp_users_collection -{ - struct hp_user *list; - size_t count; - int in_fd; -}; - -struct hp_public_key_collection -{ - struct hp_public_key *list; - size_t count; -}; - -struct hp_unl_collection -{ - struct hp_unl_node *list; - size_t count; - int npl_fd; -}; - -struct hp_round_limits_config -{ - size_t user_input_bytes; - size_t user_output_bytes; - size_t npl_output_bytes; - size_t proc_cpu_seconds; - size_t proc_mem_bytes; - size_t proc_ofd_count; -}; - -struct consensus_config -{ - enum MODE mode; - uint32_t roundtime; - uint32_t stage_slice; - uint16_t threshold; -}; - -struct npl_config -{ - enum MODE mode; -}; - -struct hp_config -{ - char *version; - struct hp_public_key_collection unl; - char *bin_path; - char *bin_args; - char *environment; - uint32_t roundtime; - uint32_t stage_slice; - struct consensus_config consensus; - struct npl_config npl; - uint16_t max_input_ledger_offset; - struct hp_round_limits_config round_limits; -}; - -struct hp_contract_context -{ - bool readonly; - uint64_t timestamp; - char contract_id[HP_CONTRACT_ID_SIZE + 1]; // +1 for null char. - struct hp_public_key public_key; - struct hp_private_key private_key; - uint64_t lcl_seq_no; // lcl sequence no. - char lcl_hash[HP_HASH_SIZE + 1]; // +1 for null char. - struct hp_users_collection users; - struct hp_unl_collection unl; -}; - -struct __hp_contract -{ - struct hp_contract_context *cctx; - int control_fd; - void *user_inmap; - size_t user_inmap_size; -}; - -int hp_init_contract(); -int hp_deinit_contract(); -const struct hp_contract_context *hp_get_context(); -const void *hp_init_user_input_mmap(); -void hp_deinit_user_input_mmap(); -int hp_write_user_msg(const struct hp_user *user, const void *buf, const uint32_t len); -int hp_writev_user_msg(const struct hp_user *user, const struct iovec *bufs, const int buf_count); -int hp_write_npl_msg(const void *buf, const uint32_t len); -int hp_writev_npl_msg(const struct iovec *bufs, const int buf_count); -int hp_read_npl_msg(void *msg_buf, char *public_key_buf, const int timeout); -struct hp_config *hp_get_config(); -int hp_update_config(const struct hp_config *config); -int hp_update_peers(const char *add_peers[], const size_t add_peers_count, const char *remove_peers[], const size_t remove_peers_count); -void hp_set_config_string(char **config_str, const char *value, const size_t value_size); -void hp_set_config_unl(struct hp_config *config, const struct hp_public_key *new_unl, const size_t new_unl_count); -void hp_free_config(struct hp_config *config); - -void __hp_parse_args_json(const struct json_object_s *object); -int __hp_write_control_msg(const void *buf, const uint32_t len); -void __hp_populate_patch_from_json_object(struct hp_config *config, const struct json_object_s *object); -int __hp_write_to_patch_file(const int fd, const struct hp_config *config); -struct hp_config *__hp_read_from_patch_file(const int fd); -size_t __hp_get_json_string_array_encoded_len(const char *elems[], const size_t count); -int __hp_encode_json_string_array(char *buf, const char *elems[], const size_t count); - -static struct __hp_contract __hpc = {}; - -int hp_init_contract() -{ - if (__hpc.cctx) - return -1; // Already initialized. - - // Check whether we are running from terminal and produce warning. - if (isatty(STDIN_FILENO) == 1) - { - fprintf(stderr, "Error: Hot Pocket smart contracts must be executed via Hot Pocket.\n"); - return -1; - } - - char buf[4096]; - const ssize_t len = read(STDIN_FILENO, buf, sizeof(buf)); - if (len == -1) - { - perror("Error when reading stdin."); - return -1; - } - - struct json_value_s *root = json_parse(buf, len); - - if (root && root->type == json_type_object) - { - struct json_object_s *object = (struct json_object_s *)root->payload; - if (object->length > 0) - { - // Create and populate hotpocket context. - __hpc.cctx = (struct hp_contract_context *)malloc(sizeof(struct hp_contract_context)); - __hp_parse_args_json(object); - __HP_FREE(root); - - return 0; - } - } - __HP_FREE(root); - return -1; -} - -int hp_deinit_contract() -{ - struct hp_contract_context *cctx = __hpc.cctx; - - if (!cctx) - return -1; // Not initialized. - - // Cleanup user input mmap (if mapped). - hp_deinit_user_input_mmap(); - - // Cleanup user and npl fd. - close(cctx->users.in_fd); - for (size_t i = 0; i < cctx->users.count; i++) - close(cctx->users.list[i].outfd); - close(cctx->unl.npl_fd); - - // Cleanup user list allocation. - if (cctx->users.list) - { - for (size_t i = 0; i < cctx->users.count; i++) - __HP_FREE(cctx->users.list[i].inputs.list); - - __HP_FREE(cctx->users.list); - } - // Cleanup unl list allocation. - __HP_FREE(cctx->unl.list); - // Cleanup contract context. - __HP_FREE(cctx); - - // Send termination control message. - const int ret = __hp_write_control_msg("{\"type\":\"contract_end\"}", 23); - - close(__hpc.control_fd); - return ret; -} - -const struct hp_contract_context *hp_get_context() -{ - return __hpc.cctx; -} - -const void *hp_init_user_input_mmap() -{ - if (__hpc.user_inmap) - return __hpc.user_inmap; - - struct hp_contract_context *cctx = __hpc.cctx; - struct stat st; - if (fstat(cctx->users.in_fd, &st) == -1) - { - perror("Error in user input fd stat"); - return NULL; - } - - if (st.st_size == 0) - return NULL; - - const size_t mmap_size = __HP_MMAP_BLOCK_ALIGN(st.st_size); - void *mmap_ptr = mmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, cctx->users.in_fd, 0); - if (mmap_ptr == MAP_FAILED) - { - perror("Error in user input fd mmap"); - return NULL; - } - - __hpc.user_inmap = mmap_ptr; - __hpc.user_inmap_size = mmap_size; - return __hpc.user_inmap; -} - -void hp_deinit_user_input_mmap() -{ - if (__hpc.user_inmap) - munmap(__hpc.user_inmap, __hpc.user_inmap_size); - __hpc.user_inmap = NULL; - __hpc.user_inmap_size = 0; -} - -int hp_write_user_msg(const struct hp_user *user, const void *buf, const uint32_t len) -{ - const struct iovec vec = {(void *)buf, len}; - return hp_writev_user_msg(user, &vec, 1); -} - -int hp_writev_user_msg(const struct hp_user *user, const struct iovec *bufs, const int buf_count) -{ - const int total_buf_count = buf_count + 1; - struct iovec all_bufs[total_buf_count]; // We need to prepend the length header buf to indicate user message length. - - uint32_t msg_len = 0; - for (int i = 0; i < buf_count; i++) - { - all_bufs[i + 1].iov_base = bufs[i].iov_base; - all_bufs[i + 1].iov_len = bufs[i].iov_len; - msg_len += bufs[i].iov_len; - } - - uint8_t header_buf[__HP_STREAM_MSG_HEADER_SIZE]; - __HP_TO_BE(msg_len, header_buf, 0); - - all_bufs[0].iov_base = header_buf; - all_bufs[0].iov_len = __HP_STREAM_MSG_HEADER_SIZE; - - return writev(user->outfd, all_bufs, total_buf_count); -} - -int hp_write_npl_msg(const void *buf, const uint32_t len) -{ - if (len > HP_NPL_MSG_MAX_SIZE) - { - fprintf(stderr, "NPL message exceeds max length %d.\n", HP_NPL_MSG_MAX_SIZE); - return -1; - } - - return write(__hpc.cctx->unl.npl_fd, buf, len); -} - -int hp_writev_npl_msg(const struct iovec *bufs, const int buf_count) -{ - uint32_t len = 0; - for (int i = 0; i < buf_count; i++) - len += bufs[i].iov_len; - - if (len > HP_NPL_MSG_MAX_SIZE) - { - fprintf(stderr, "NPL message exceeds max length %d.\n", HP_NPL_MSG_MAX_SIZE); - return -1; - } - - return writev(__hpc.cctx->unl.npl_fd, bufs, buf_count); -} - -/** - * Reads a NPL message while waiting for 'timeout' milliseconds. - * @param msg_buf The buffer to place the incoming message. Must be of at least 'HP_NPL_MSG_MAX_SIZE' length. - * @param public_key_buf The buffer to place the sender public_key (hex). Must be of at least 'HP_PUBLIC_KEY_SIZE' length. - * @param timeout Maximum milliseoncds to wait until a message arrives. If 0, returns immediately. - * If -1, waits forever until message arrives. - * @return Message length on success. 0 if no message arrived within timeout. -1 on error. - */ -int hp_read_npl_msg(void *msg_buf, char *public_key_buf, const int timeout) -{ - struct pollfd pfd = {__hpc.cctx->unl.npl_fd, POLLIN, 0}; - - // NPL messages consist of alternating SEQ packets of public_key and data. - // So we need to wait for both public_key and data packets to form a complete NPL message. - - // Wait for the public_key. - if (poll(&pfd, 1, timeout) == -1) - { - perror("NPL channel public_key poll error"); - return -1; - } - else if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) - { - fprintf(stderr, "NPL channel public_key poll returned error: %d\n", pfd.revents); - return -1; - } - else if (pfd.revents & POLLIN) - { - // Read public_key. - if (read(pfd.fd, public_key_buf, HP_PUBLIC_KEY_SIZE) == -1) - { - perror("Error reading public_key from NPL channel"); - return -1; - } - - // Wait for data. (data should be available immediately because we have received the public_key) - pfd.revents = 0; - if (poll(&pfd, 1, 100) == -1) - { - perror("NPL channel data poll error"); - return -1; - } - else if (pfd.revents & (POLLHUP | POLLERR | POLLNVAL)) - { - fprintf(stderr, "NPL channel data poll returned error: %d\n", pfd.revents); - return -1; - } - else if (pfd.revents & POLLIN) - { - // Read data. - const int readres = read(pfd.fd, msg_buf, HP_NPL_MSG_MAX_SIZE); - if (readres == -1) - { - perror("Error reading public_key from NPL channel"); - return -1; - } - return readres; - } - } - - return 0; -} - -/** - * Get the existing config file values. - * @return returns a pointer to a config structure, returns NULL on error. - */ -struct hp_config *hp_get_config() -{ - const int fd = open(__HP_PATCH_FILE_PATH, O_RDONLY); - if (fd == -1) - { - fprintf(stderr, "Error opening patch.cfg file.\n"); - return NULL; - } - - struct hp_config *config = __hp_read_from_patch_file(fd); - if (config == NULL) - fprintf(stderr, "Error reading patch.cfg file.\n"); - - close(fd); - return config; -} - -/** - * Update the params of the existing config file. - * @param config Pointer to the updated config struct. - */ -int hp_update_config(const struct hp_config *config) -{ - struct hp_contract_context *cctx = __hpc.cctx; - - if (cctx->readonly) - { - fprintf(stderr, "Config update not allowed in readonly mode.\n"); - return -1; - } - - // Validate fields. - - if (!config->version || strlen(config->version) == 0) - __HP_UPDATE_CONFIG_ERROR("Version cannot be empty."); - - if (config->unl.count) - { - for (size_t i = 0; i < config->unl.count; i++) - { - const char *public_key = config->unl.list[i].data; - const size_t public_key_len = strlen(public_key); - if (public_key_len == 0) - __HP_UPDATE_CONFIG_ERROR("Unl public_key cannot be empty."); - - if (public_key_len != HP_PUBLIC_KEY_SIZE) - __HP_UPDATE_CONFIG_ERROR("Unl public_key invalid. Invalid length."); - - if (public_key[0] != 'e' || public_key[1] != 'd') - __HP_UPDATE_CONFIG_ERROR("Unl public_key invalid. Invalid format."); - - // Checking the validity of hexadecimal portion. (without 'ed'). - for (size_t j = 2; j < HP_PUBLIC_KEY_SIZE; j++) - { - const char current_char = public_key[j]; - if ((current_char < 'A' || current_char > 'F') && (current_char < 'a' || current_char > 'f') && (current_char < '0' || current_char > '9')) - __HP_UPDATE_CONFIG_ERROR("Unl public_key invalid. Invalid character."); - } - } - } - - if (!config->bin_path || strlen(config->bin_path) == 0) - __HP_UPDATE_CONFIG_ERROR("Binary path cannot be empty."); - - if (config->consensus.roundtime <= 0 || config->consensus.roundtime > 3600000) - __HP_UPDATE_CONFIG_ERROR("Round time must be between 1 and 3600000ms inclusive."); - - if (config->consensus.stage_slice <= 0 || config->consensus.stage_slice > 33) - __HP_UPDATE_CONFIG_ERROR("Stage slice must be between 1 and 33 percent inclusive"); - - if (config->consensus.threshold <= 0 || config->consensus.threshold > 100) - __HP_UPDATE_CONFIG_ERROR("Threshold must be between 1 and 100 percent inclusive"); - - if (config->max_input_ledger_offset < 0) - __HP_UPDATE_CONFIG_ERROR("Invalid max input ledger offset."); - - if (config->consensus.mode != PUBLIC && config->consensus.mode != PRIVATE) - __HP_UPDATE_CONFIG_ERROR("Invalid consensus mode. Valid values: public|private"); - - if (config->npl.mode != PRIVATE && config->npl.mode != PUBLIC) - __HP_UPDATE_CONFIG_ERROR("Invalid npl mode. Valid values: public|private"); - - if (config->round_limits.user_input_bytes < 0 || config->round_limits.user_output_bytes < 0 || config->round_limits.npl_output_bytes < 0 || - config->round_limits.proc_cpu_seconds < 0 || config->round_limits.proc_mem_bytes < 0 || config->round_limits.proc_ofd_count < 0) - __HP_UPDATE_CONFIG_ERROR("Invalid round limits."); - - const int fd = open(__HP_PATCH_FILE_PATH, O_RDWR); - if (fd == -1) - __HP_UPDATE_CONFIG_ERROR("Error opening patch.cfg file."); - - if (__hp_write_to_patch_file(fd, config) == -1) - { - close(fd); - __HP_UPDATE_CONFIG_ERROR("Error writing updated config to patch.cfg file."); - } - - close(fd); - return 0; -} - -/** - * Assigns the given string value to the specified config string field. - * @param config_str Pointer to the string field to populate the new value to. - * @param value New string value. - * @param value_size String length of the new value. - */ -void hp_set_config_string(char **config_str, const char *value, const size_t value_size) -{ - *config_str = (char *)realloc(*config_str, value_size); - strncpy(*config_str, value, value_size); -} - -/** - * Populates the config unl list with the specified values. - * @param config The config struct to populate the unl to. - * @param new_unl Pointer to array of unl public_keys. - * @param new_unl_count No. of entries in the new unl public_key array. - */ -void hp_set_config_unl(struct hp_config *config, const struct hp_public_key *new_unl, const size_t new_unl_count) -{ - const size_t mem_size = sizeof(struct hp_public_key) * new_unl_count; - config->unl.list = (struct hp_public_key *)realloc(config->unl.list, mem_size); - memcpy(config->unl.list, new_unl, mem_size); - config->unl.count = new_unl_count; -} - -/** - * Frees the memory allocated for the config structure. - * @param config Pointer to the config to be freed. - */ -void hp_free_config(struct hp_config *config) -{ - __HP_FREE(config->version); - __HP_FREE(config->unl.list); - __HP_FREE(config->bin_path); - __HP_FREE(config->bin_args); - __HP_FREE(config->environment); - __HP_FREE(config); -} - -/** - * Updates the known-peers this node must attempt connections to. - * @param add_peers Array of strings containing peers to be added. Each string must be in the format of ":". - * @param add_peers_count No. of peers to be added. - * @param remove_peers Array of strings containing peers to be removed. Each string must be in the format of ":". - * @param remove_peers_count No. of peers to be removed. - */ -int hp_update_peers(const char *add_peers[], const size_t add_peers_count, const char *remove_peers[], const size_t remove_peers_count) -{ - const size_t add_json_len = __hp_get_json_string_array_encoded_len(add_peers, add_peers_count); - char add_json[add_json_len]; - if (__hp_encode_json_string_array(add_json, add_peers, add_peers_count) == -1) - { - fprintf(stderr, "Error when encoding peer update changeset 'add'.\n"); - return -1; - } - - const size_t remove_json_len = __hp_get_json_string_array_encoded_len(remove_peers, remove_peers_count); - char remove_json[remove_json_len]; - if (__hp_encode_json_string_array(remove_json, remove_peers, remove_peers_count) == -1) - { - fprintf(stderr, "Error when encoding peer update changeset 'remove'.\n"); - return -1; - } - - const size_t msg_len = 47 + (add_json_len - 1) + (remove_json_len - 1); - char msg[msg_len]; - sprintf(msg, "{\"type\":\"peer_changeset\",\"add\":[%s],\"remove\":[%s]}", add_json, remove_json); - - if (__hp_write_control_msg(msg, msg_len - 1) == -1) - return -1; - - return 0; -} - -/** - * Returns the null-terminated string length required to encode as a json string array without enclosing brackets. - * @param elems Array of strings. - * @param count No. of strings. - */ -size_t __hp_get_json_string_array_encoded_len(const char *elems[], const size_t count) -{ - size_t len = 1; // +1 for null terminator. - for (size_t i = 0; i < count; i++) - { - len += (strlen(elems[i]) + 2); // Quoted string. - if (i < count - 1) - len += 1; // Comma - } - - return len; -} - -/** - * Formats a string array in JSON notation without enclosing brackets. - * @param buf Buffer to populate the encoded output. - * @param elems Array of strings. - * @param count No. of strings. - */ -int __hp_encode_json_string_array(char *buf, const char *elems[], const size_t count) -{ - size_t pos = 0; - for (size_t i = 0; i < count; i++) - { - const char *elem = elems[i]; - buf[pos++] = '\"'; - strcpy((buf + pos), elem); - pos += strlen(elem); - buf[pos++] = '\"'; - - if (i < count - 1) - buf[pos++] = ','; - } - buf[pos] = '\0'; - return 0; -} - -/** - * Read the values from the existing patch file. - * @param fd File discriptor of the patch.cfg file. - * @return returns a pointer to a patch_config structure, returns NULL on error. - */ -struct hp_config *__hp_read_from_patch_file(const int fd) -{ - char buf[4096]; - const ssize_t len = read(fd, buf, sizeof(buf)); - if (len == -1) - return NULL; - - struct json_value_s *root = json_parse(buf, len); - if (root && root->type == json_type_object) - { - struct json_object_s *object = (struct json_object_s *)root->payload; - // Create struct to populate json values. - struct hp_config *config; - // Allocate memory for the patch_config struct. - config = (struct hp_config *)malloc(sizeof(struct hp_config)); - // malloc and populate values to the struct. - __hp_populate_patch_from_json_object(config, object); - __HP_FREE(root); - return config; - } - - __HP_FREE(root); - return NULL; -} - -/** - * Write values of the given patch config struct to the file discriptor given. - * @param fd File discriptor of the patch.cfg file. - * @param config Patch config structure. - */ -int __hp_write_to_patch_file(const int fd, const struct hp_config *config) -{ - struct iovec iov_vec[6]; - // {version: + newline + 4 spaces => 21; - const size_t version_len = 21 + strlen(config->version); - char version_buf[version_len]; - sprintf(version_buf, "{\n \"version\": \"%s\",\n", config->version); - iov_vec[0].iov_base = version_buf; - iov_vec[0].iov_len = version_len; - - const size_t unl_buf_size = 20 + (69 * config->unl.count - (config->unl.count ? 1 : 0)) + (9 * config->unl.count); - char unl_buf[unl_buf_size]; - - memcpy(unl_buf, " \"unl\": [", 12); - size_t pos = 12; - for (size_t i = 0; i < config->unl.count; i++) - { - if (i > 0) - unl_buf[pos++] = ','; - - memcpy(unl_buf + pos, "\n ", 9); - pos += 9; - unl_buf[pos++] = '"'; - memcpy(unl_buf + pos, config->unl.list[i].data, HP_PUBLIC_KEY_SIZE); - pos += HP_PUBLIC_KEY_SIZE; - unl_buf[pos++] = '"'; - } - - memcpy(unl_buf + pos, "\n ],\n", 8); - iov_vec[1].iov_base = unl_buf; - iov_vec[1].iov_len = unl_buf_size; - - // Top-level field values. - - const char *json_string = " \"bin_path\": \"%s\",\n \"bin_args\": \"%s\",\n \"environment\": \"%s\",\n" - " \"max_input_ledger_offset\": %s,\n"; - - char max_input_ledger_offset_str[16]; - sprintf(max_input_ledger_offset_str, "%d", config->max_input_ledger_offset); - - const size_t json_string_len = 96 + strlen(config->bin_path) + strlen(config->bin_args) + strlen(config->environment) + strlen(max_input_ledger_offset_str); - char json_buf[json_string_len]; - sprintf(json_buf, json_string, config->bin_path, config->bin_args, config->environment, max_input_ledger_offset_str); - iov_vec[2].iov_base = json_buf; - iov_vec[2].iov_len = json_string_len; - - // Consensus fields - - const char *consensus_json = " \"consensus\": {\n" - " \"mode\": %s,\n \"roundtime\": %s,\n \"stage_slice\": %s,\n" - " \"threshold\": %s\n },\n"; - - char consensus_mode_str[10], roundtime_str[16], stage_slice_str[16], threshold_str[6]; - - sprintf(consensus_mode_str, "\"%s\"", config->consensus.mode == PUBLIC ? "public" : "private"); - sprintf(roundtime_str, "%d", config->consensus.roundtime); - sprintf(stage_slice_str, "%d", config->consensus.stage_slice); - sprintf(threshold_str, "%d", config->consensus.threshold); - - const size_t consensus_json_len = 114 + strlen(consensus_mode_str) + strlen(roundtime_str) + strlen(stage_slice_str) + strlen(threshold_str); - char consensus_buf[consensus_json_len]; - sprintf(consensus_buf, consensus_json, consensus_mode_str, roundtime_str, stage_slice_str, threshold_str); - iov_vec[3].iov_base = consensus_buf; - iov_vec[3].iov_len = consensus_json_len; - - // npl field values - - const char *npl_json = " \"npl\": {\n" - " \"mode\": %s\n },\n"; - - char npl_mode_str[10]; - sprintf(npl_mode_str, "\"%s\"", config->npl.mode == PUBLIC ? "public" : "private"); - const size_t npl_json_len = 37 + strlen(npl_mode_str); - char npl_buf[npl_json_len]; - sprintf(npl_buf, npl_json, npl_mode_str); - iov_vec[4].iov_base = npl_buf; - iov_vec[4].iov_len = npl_json_len; - - // Round limits field values. - - const char *round_limits_json = " \"round_limits\": {\n" - " \"user_input_bytes\": %s,\n \"user_output_bytes\": %s,\n \"npl_output_bytes\": %s,\n" - " \"proc_cpu_seconds\": %s,\n \"proc_mem_bytes\": %s,\n \"proc_ofd_count\": %s\n }\n}"; - - char user_input_bytes_str[20], user_output_bytes_str[20], npl_output_bytes_str[20], - proc_cpu_seconds_str[20], proc_mem_bytes_str[20], proc_ofd_count_str[20]; - - sprintf(user_input_bytes_str, "%" PRIu64, config->round_limits.user_input_bytes); - sprintf(user_output_bytes_str, "%" PRIu64, config->round_limits.user_output_bytes); - sprintf(npl_output_bytes_str, "%" PRIu64, config->round_limits.npl_output_bytes); - - sprintf(proc_cpu_seconds_str, "%" PRIu64, config->round_limits.proc_cpu_seconds); - sprintf(proc_mem_bytes_str, "%" PRIu64, config->round_limits.proc_mem_bytes); - sprintf(proc_ofd_count_str, "%" PRIu64, config->round_limits.proc_ofd_count); - - const size_t round_limits_json_len = 205 + strlen(user_input_bytes_str) + strlen(user_output_bytes_str) + strlen(npl_output_bytes_str) + - strlen(proc_cpu_seconds_str) + strlen(proc_mem_bytes_str) + strlen(proc_ofd_count_str); - char round_limits_buf[round_limits_json_len]; - sprintf(round_limits_buf, round_limits_json, - user_input_bytes_str, user_output_bytes_str, npl_output_bytes_str, - proc_cpu_seconds_str, proc_mem_bytes_str, proc_ofd_count_str); - iov_vec[5].iov_base = round_limits_buf; - iov_vec[5].iov_len = round_limits_json_len; - - if (ftruncate(fd, 0) == -1 || // Clear any previous content in the file. - pwritev(fd, iov_vec, 6, 0) == -1) // Start writing from begining. - return -1; - - return 0; -} - -/** - * Populate the given patch struct file from the json_object obtained from the existing patch.cfg file. - * @param config Pointer to the patch config sturct to be populated. - * @param object Pointer to the json object. - */ -void __hp_populate_patch_from_json_object(struct hp_config *config, const struct json_object_s *object) -{ - const struct json_object_element_s *elem = object->start; - do - { - const struct json_string_s *k = elem->name; - - if (strcmp(k->string, "version") == 0) - { - __HP_ASSIGN_CHAR_PTR(config->version, elem); - } - else if (strcmp(k->string, "unl") == 0) - { - if (elem->value->type == json_type_array) - { - const struct json_array_s *unl_array = (struct json_array_s *)elem->value->payload; - const size_t unl_count = unl_array->length; - - config->unl.count = unl_count; - config->unl.list = unl_count ? (struct hp_public_key *)malloc(sizeof(struct hp_public_key) * unl_count) : NULL; - - if (unl_count > 0) - { - struct json_array_element_s *unl_elem = unl_array->start; - for (size_t i = 0; i < unl_count; i++) - { - __HP_ASSIGN_STRING(config->unl.list[i].data, unl_elem); - unl_elem = unl_elem->next; - } - } - } - } - else if (strcmp(k->string, "bin_path") == 0) - { - __HP_ASSIGN_CHAR_PTR(config->bin_path, elem); - } - else if (strcmp(k->string, "bin_args") == 0) - { - __HP_ASSIGN_CHAR_PTR(config->bin_args, elem); - } - else if (strcmp(k->string, "environment") == 0) - { - __HP_ASSIGN_CHAR_PTR(config->environment, elem); - } - else if (strcmp(k->string, "max_input_ledger_offset") == 0) - { - const struct json_number_s *value = (struct json_number_s *)elem->value->payload; - config->max_input_ledger_offset = strtoul(value->number, NULL, 0); - } - else if (strcmp(k->string, "consensus") == 0) - { - struct json_object_s *object = (struct json_object_s *)elem->value->payload; - struct json_object_element_s *sub_ele = object->start; - do - { - if (strcmp(sub_ele->name->string, "roundtime") == 0) - { - __HP_ASSIGN_UINT64(config->consensus.roundtime, sub_ele); - } - else if (strcmp(sub_ele->name->string, "stage_slice") == 0) - { - __HP_ASSIGN_UINT64(config->consensus.stage_slice, sub_ele); - } - else if (strcmp(sub_ele->name->string, "threshold") == 0) - { - __HP_ASSIGN_UINT64(config->consensus.threshold, sub_ele); - } - else if (strcmp(sub_ele->name->string, "mode") == 0) - { - if (sub_ele->value->type == json_type_string) - { - const struct json_string_s *value = (struct json_string_s *)sub_ele->value->payload; - config->consensus.mode = (strcmp(value->string, "public") == 0) ? PUBLIC : PRIVATE; - } - } - sub_ele = sub_ele->next; - } while (sub_ele); - } - else if (strcmp(k->string, "npl") == 0) - { - struct json_object_s *object = (struct json_object_s *)elem->value->payload; - struct json_object_element_s *sub_ele = object->start; - do - { - if (strcmp(sub_ele->name->string, "mode") == 0) - { - if (sub_ele->value->type == json_type_string) - { - const struct json_string_s *value = (struct json_string_s *)sub_ele->value->payload; - config->npl.mode = (strcmp(value->string, "public") == 0) ? PUBLIC : PRIVATE; - } - } - sub_ele = sub_ele->next; - } while (sub_ele); - } - else if (strcmp(k->string, "round_limits") == 0) - { - struct json_object_s *object = (struct json_object_s *)elem->value->payload; - struct json_object_element_s *sub_ele = object->start; - do - { - if (strcmp(sub_ele->name->string, "user_input_bytes") == 0) - { - __HP_ASSIGN_UINT64(config->round_limits.user_input_bytes, sub_ele); - } - else if (strcmp(sub_ele->name->string, "user_output_bytes") == 0) - { - __HP_ASSIGN_UINT64(config->round_limits.user_output_bytes, sub_ele); - } - else if (strcmp(sub_ele->name->string, "npl_output_bytes") == 0) - { - __HP_ASSIGN_UINT64(config->round_limits.npl_output_bytes, sub_ele); - } - else if (strcmp(sub_ele->name->string, "proc_cpu_seconds") == 0) - { - __HP_ASSIGN_UINT64(config->round_limits.proc_cpu_seconds, sub_ele); - } - else if (strcmp(sub_ele->name->string, "proc_mem_bytes") == 0) - { - __HP_ASSIGN_UINT64(config->round_limits.proc_mem_bytes, sub_ele); - } - else if (strcmp(sub_ele->name->string, "proc_ofd_count") == 0) - { - __HP_ASSIGN_UINT64(config->round_limits.proc_ofd_count, sub_ele); - } - sub_ele = sub_ele->next; - } while (sub_ele); - } - - elem = elem->next; - } while (elem); -} - -void __hp_parse_args_json(const struct json_object_s *object) -{ - const struct json_object_element_s *elem = object->start; - struct hp_contract_context *cctx = __hpc.cctx; - - do - { - const struct json_string_s *k = elem->name; - - if (strcmp(k->string, "contract_id") == 0) - { - __HP_ASSIGN_STRING(cctx->contract_id, elem); - } - else if (strcmp(k->string, "public_key") == 0) - { - __HP_ASSIGN_STRING(cctx->public_key.data, elem); - } - else if (strcmp(k->string, "private_key") == 0) - { - __HP_ASSIGN_STRING(cctx->private_key.data, elem); - } - else if (strcmp(k->string, "timestamp") == 0) - { - __HP_ASSIGN_UINT64(cctx->timestamp, elem); - } - else if (strcmp(k->string, "readonly") == 0) - { - __HP_ASSIGN_BOOL(cctx->readonly, elem); - } - else if (strcmp(k->string, "lcl_seq_no") == 0) - { - __HP_ASSIGN_UINT64(cctx->lcl_seq_no, elem); - } - else if (strcmp(k->string, "lcl_hash") == 0) - { - __HP_ASSIGN_STRING(cctx->lcl_hash, elem); - } - else if (strcmp(k->string, "user_in_fd") == 0) - { - __HP_ASSIGN_INT(cctx->users.in_fd, elem); - } - else if (strcmp(k->string, "users") == 0) - { - if (elem->value->type == json_type_object) - { - const struct json_object_s *user_object = (struct json_object_s *)elem->value->payload; - const size_t user_count = user_object->length; - - cctx->users.count = user_count; - cctx->users.list = user_count ? (struct hp_user *)malloc(sizeof(struct hp_user) * user_count) : NULL; - - if (user_count > 0) - { - struct json_object_element_s *user_elem = user_object->start; - for (size_t i = 0; i < user_count; i++) - { - struct hp_user *user = &cctx->users.list[i]; - memcpy(user->public_key.data, user_elem->name->string, HP_PUBLIC_KEY_SIZE); - - if (user_elem->value->type == json_type_array) - { - const struct json_array_s *arr = (struct json_array_s *)user_elem->value->payload; - struct json_array_element_s *arr_elem = arr->start; - - // First element is the output fd. - __HP_ASSIGN_INT(user->outfd, arr_elem); - arr_elem = arr_elem->next; - - // Subsequent elements are tupels of [offset, size] of input messages for this user. - user->inputs.count = arr->length - 1; - user->inputs.list = user->inputs.count ? (struct hp_user_input *)malloc(user->inputs.count * sizeof(struct hp_user_input)) : NULL; - for (size_t i = 0; i < user->inputs.count; i++) - { - if (arr_elem->value->type == json_type_array) - { - const struct json_array_s *input_info = (struct json_array_s *)arr_elem->value->payload; - if (input_info->length == 2) - { - __HP_ASSIGN_UINT64(user->inputs.list[i].offset, input_info->start); - __HP_ASSIGN_UINT64(user->inputs.list[i].size, input_info->start->next); - } - } - arr_elem = arr_elem->next; - } - } - user_elem = user_elem->next; - } - } - } - } - else if (strcmp(k->string, "npl_fd") == 0) - { - __HP_ASSIGN_INT(cctx->unl.npl_fd, elem); - } - else if (strcmp(k->string, "unl") == 0) - { - // unl is an object with public_keys as keys. Each key contains an object with that node statistics. - if (elem->value->type == json_type_object) - { - const struct json_object_s *unl_obj = (struct json_object_s *)elem->value->payload; - const size_t unl_count = unl_obj->length; - - cctx->unl.count = unl_count; - cctx->unl.list = unl_count ? (struct hp_unl_node *)malloc(sizeof(struct hp_unl_node) * unl_count) : NULL; - - if (unl_count > 0) - { - struct json_object_element_s *unl_elem = unl_obj->start; - for (size_t i = 0; i < unl_count; i++) - { - // Each element(key) is named by the public_key. - strncpy(cctx->unl.list[i].public_key.data, unl_elem->name->string, unl_elem->name->string_size); - - if (unl_elem->value->type == json_type_object) - { - const struct json_object_s *stat_obj = (struct json_object_s *)unl_elem->value->payload; - struct json_object_element_s *stat_elem = stat_obj->start; - do - { - const struct json_string_s *k = stat_elem->name; - if (strcmp(k->string, "active_on") == 0) - { - __HP_ASSIGN_UINT64(cctx->unl.list[i].active_on, stat_elem); - } - stat_elem = stat_elem->next; - } while (stat_elem); - } - - unl_elem = unl_elem->next; - } - } - } - } - else if (strcmp(k->string, "control_fd") == 0) - { - __HP_ASSIGN_INT(__hpc.control_fd, elem); - } - - elem = elem->next; - } while (elem); -} - -int __hp_write_control_msg(const void *buf, const uint32_t len) -{ - if (len > __HP_SEQPKT_MAX_SIZE) - { - fprintf(stderr, "Control message exceeds max length %d.\n", __HP_SEQPKT_MAX_SIZE); - return -1; - } - - return write(__hpc.control_fd, buf, len); -} - -#endif \ No newline at end of file diff --git a/examples/c_contract/json.h b/examples/c_contract/json.h deleted file mode 100644 index 8daa5374..00000000 --- a/examples/c_contract/json.h +++ /dev/null @@ -1,3080 +0,0 @@ -/* - The latest version of this library is available on GitHub; - https://github.com/sheredom/json.h. -*/ - -/* - This is free and unencumbered software released into the public domain. - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this software, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. - In jurisdictions that recognize copyright laws, the author or authors - of this software dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - For more information, please refer to . -*/ - -#ifndef SHEREDOM_JSON_H_INCLUDED -#define SHEREDOM_JSON_H_INCLUDED - -#if defined(_MSC_VER) -#pragma warning(push) - -/* disable 'bytes padding added after construct' warning */ -#pragma warning(disable : 4820) -#endif - -#include - -#if defined(__clang__) || defined(__GNUC__) -#define json_weak __attribute__((weak)) -#elif defined(_MSC_VER) -#define json_weak __inline -#else -#error Non clang, non gcc, non MSVC compiler found! -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -struct json_value_s; -struct json_parse_result_s; - -enum json_parse_flags_e { - json_parse_flags_default = 0, - - /* allow trailing commas in objects and arrays. For example, both [true,] and - {"a" : null,} would be allowed with this option on. */ - json_parse_flags_allow_trailing_comma = 0x1, - - /* allow unquoted keys for objects. For example, {a : null} would be allowed - with this option on. */ - json_parse_flags_allow_unquoted_keys = 0x2, - - /* allow a global unbracketed object. For example, a : null, b : true, c : {} - would be allowed with this option on. */ - json_parse_flags_allow_global_object = 0x4, - - /* allow objects to use '=' instead of ':' between key/value pairs. For - example, a = null, b : true would be allowed with this option on. */ - json_parse_flags_allow_equals_in_object = 0x8, - - /* allow that objects don't have to have comma separators between key/value - pairs. */ - json_parse_flags_allow_no_commas = 0x10, - - /* allow c-style comments (either variants) to be ignored in the input JSON - file. */ - json_parse_flags_allow_c_style_comments = 0x20, - - /* deprecated flag, unused. */ - json_parse_flags_deprecated = 0x40, - - /* record location information for each value. */ - json_parse_flags_allow_location_information = 0x80, - - /* allow strings to be 'single quoted'. */ - json_parse_flags_allow_single_quoted_strings = 0x100, - - /* allow numbers to be hexadecimal. */ - json_parse_flags_allow_hexadecimal_numbers = 0x200, - - /* allow numbers like +123 to be parsed. */ - json_parse_flags_allow_leading_plus_sign = 0x400, - - /* allow numbers like .0123 or 123. to be parsed. */ - json_parse_flags_allow_leading_or_trailing_decimal_point = 0x800, - - /* allow Infinity, -Infinity, NaN, -NaN. */ - json_parse_flags_allow_inf_and_nan = 0x1000, - - /* allow multi line string values. */ - json_parse_flags_allow_multi_line_strings = 0x2000, - - /* allow simplified JSON to be parsed. Simplified JSON is an enabling of a set - of other parsing options. */ - json_parse_flags_allow_simplified_json = - (json_parse_flags_allow_trailing_comma | - json_parse_flags_allow_unquoted_keys | - json_parse_flags_allow_global_object | - json_parse_flags_allow_equals_in_object | - json_parse_flags_allow_no_commas), - - /* allow JSON5 to be parsed. JSON5 is an enabling of a set of other parsing - options. */ - json_parse_flags_allow_json5 = - (json_parse_flags_allow_trailing_comma | - json_parse_flags_allow_unquoted_keys | - json_parse_flags_allow_c_style_comments | - json_parse_flags_allow_single_quoted_strings | - json_parse_flags_allow_hexadecimal_numbers | - json_parse_flags_allow_leading_plus_sign | - json_parse_flags_allow_leading_or_trailing_decimal_point | - json_parse_flags_allow_inf_and_nan | - json_parse_flags_allow_multi_line_strings) -}; - -/* Parse a JSON text file, returning a pointer to the root of the JSON - * structure. json_parse performs 1 call to malloc for the entire encoding. - * Returns 0 if an error occurred (malformed JSON input, or malloc failed). */ -json_weak struct json_value_s *json_parse(const void *src, size_t src_size); - -/* Parse a JSON text file, returning a pointer to the root of the JSON - * structure. json_parse performs 1 call to malloc for the entire encoding. - * Returns 0 if an error occurred (malformed JSON input, or malloc failed). If - * an error occurred, the result struct (if not NULL) will explain the type of - * error, and the location in the input it occurred. */ -json_weak struct json_value_s * -json_parse_ex(const void *src, size_t src_size, size_t flags_bitset, - void *(*alloc_func_ptr)(void *, size_t), void *user_data, - struct json_parse_result_s *result); - -/* Write out a minified JSON utf-8 string. This string is an encoding of the - * minimal string characters required to still encode the same data. - * json_write_minified performs 1 call to malloc for the entire encoding. Return - * 0 if an error occurred (malformed JSON input, or malloc failed). The out_size - * parameter is optional as the utf-8 string is null terminated. */ -json_weak void *json_write_minified(const struct json_value_s *value, - size_t *out_size); - -/* Write out a pretty JSON utf-8 string. This string is encoded such that the - * resultant JSON is pretty in that it is easily human readable. The indent and - * newline parameters allow a user to specify what kind of indentation and - * newline they want (two spaces / three spaces / tabs? \r, \n, \r\n ?). Both - * indent and newline can be NULL, indent defaults to two spaces (" "), and - * newline defaults to linux newlines ('\n' as the newline character). - * json_write_pretty performs 1 call to malloc for the entire encoding. Return 0 - * if an error occurred (malformed JSON input, or malloc failed). The out_size - * parameter is optional as the utf-8 string is null terminated. */ -json_weak void *json_write_pretty(const struct json_value_s *value, - const char *indent, const char *newline, - size_t *out_size); - -/* Reinterpret a JSON value as a string. Returns null is the value was not a - * string. */ -json_weak struct json_string_s * -json_value_as_string(struct json_value_s *const value); - -/* Reinterpret a JSON value as a number. Returns null is the value was not a - * number. */ -json_weak struct json_number_s * -json_value_as_number(struct json_value_s *const value); - -/* Reinterpret a JSON value as an object. Returns null is the value was not an - * object. */ -json_weak struct json_object_s * -json_value_as_object(struct json_value_s *const value); - -/* Reinterpret a JSON value as an array. Returns null is the value was not an - * array. */ -json_weak struct json_array_s * -json_value_as_array(struct json_value_s *const value); - -/* Whether the value is true. */ -json_weak int json_value_is_true(const struct json_value_s *const value); - -/* Whether the value is false. */ -json_weak int json_value_is_false(const struct json_value_s *const value); - -/* Whether the value is null. */ -json_weak int json_value_is_null(const struct json_value_s *const value); - -/* The various types JSON values can be. Used to identify what a value is. */ -enum json_type_e { - json_type_string, - json_type_number, - json_type_object, - json_type_array, - json_type_true, - json_type_false, - json_type_null -}; - -/* A JSON string value. */ -struct json_string_s { - /* utf-8 string */ - const char *string; - /* The size (in bytes) of the string */ - size_t string_size; -}; - -/* A JSON string value (extended). */ -struct json_string_ex_s { - /* The JSON string this extends. */ - struct json_string_s string; - - /* The character offset for the value in the JSON input. */ - size_t offset; - - /* The line number for the value in the JSON input. */ - size_t line_no; - - /* The row number for the value in the JSON input, in bytes. */ - size_t row_no; -}; - -/* A JSON number value. */ -struct json_number_s { - /* ASCII string containing representation of the number. */ - const char *number; - /* the size (in bytes) of the number. */ - size_t number_size; -}; - -/* an element of a JSON object. */ -struct json_object_element_s { - /* the name of this element. */ - struct json_string_s *name; - /* the value of this element. */ - struct json_value_s *value; - /* the next object element (can be NULL if the last element in the object). */ - struct json_object_element_s *next; -}; - -/* a JSON object value. */ -struct json_object_s { - /* a linked list of the elements in the object. */ - struct json_object_element_s *start; - /* the number of elements in the object. */ - size_t length; -}; - -/* an element of a JSON array. */ -struct json_array_element_s { - /* the value of this element. */ - struct json_value_s *value; - /* the next array element (can be NULL if the last element in the array). */ - struct json_array_element_s *next; -}; - -/* a JSON array value. */ -struct json_array_s { - /* a linked list of the elements in the array. */ - struct json_array_element_s *start; - /* the number of elements in the array. */ - size_t length; -}; - -/* a JSON value. */ -struct json_value_s { - /* a pointer to either a json_string_s, json_number_s, json_object_s, or. */ - /* json_array_s. Should be cast to the appropriate struct type based on what. - */ - /* the type of this value is. */ - void *payload; - /* must be one of json_type_e. If type is json_type_true, json_type_false, or. - */ - /* json_type_null, payload will be NULL. */ - size_t type; -}; - -/* a JSON value (extended). */ -struct json_value_ex_s { - /* the JSON value this extends. */ - struct json_value_s value; - - /* the character offset for the value in the JSON input. */ - size_t offset; - - /* the line number for the value in the JSON input. */ - size_t line_no; - - /* the row number for the value in the JSON input, in bytes. */ - size_t row_no; -}; - -/* a parsing error code. */ -enum json_parse_error_e { - /* no error occurred (huzzah!). */ - json_parse_error_none = 0, - - /* expected either a comma or a closing '}' or ']' to close an object or. */ - /* array! */ - json_parse_error_expected_comma_or_closing_bracket, - - /* colon separating name/value pair was missing! */ - json_parse_error_expected_colon, - - /* expected string to begin with '"'! */ - json_parse_error_expected_opening_quote, - - /* invalid escaped sequence in string! */ - json_parse_error_invalid_string_escape_sequence, - - /* invalid number format! */ - json_parse_error_invalid_number_format, - - /* invalid value! */ - json_parse_error_invalid_value, - - /* reached end of buffer before object/array was complete! */ - json_parse_error_premature_end_of_buffer, - - /* string was malformed! */ - json_parse_error_invalid_string, - - /* a call to malloc, or a user provider allocator, failed. */ - json_parse_error_allocator_failed, - - /* the JSON input had unexpected trailing characters that weren't part of the. - */ - /* JSON value. */ - json_parse_error_unexpected_trailing_characters, - - /* catch-all error for everything else that exploded (real bad chi!). */ - json_parse_error_unknown -}; - -/* error report from json_parse_ex(). */ -struct json_parse_result_s { - /* the error code (one of json_parse_error_e). */ - size_t error; - - /* the character offset for the error in the JSON input. */ - size_t error_offset; - - /* the line number for the error in the JSON input. */ - size_t error_line_no; - - /* the row number for the error, in bytes. */ - size_t error_row_no; -}; - -#ifdef __cplusplus -} /* extern "C". */ -#endif - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -#include - -#if defined(_MSC_VER) -#define json_strtoumax _strtoui64 -#define json_uintmax_t unsigned __int64 -#else -#include -#define json_strtoumax strtoumax -#define json_uintmax_t uintmax_t -#endif - -#if defined(__cplusplus) && (__cplusplus >= 201103L) -#define json_null nullptr -#else -#define json_null 0 -#endif - -#if defined(__clang__) -#pragma clang diagnostic push - -/* we do one big allocation via malloc, then cast aligned slices of this for. */ -/* our structures - we don't have a way to tell the compiler we know what we. */ -/* are doing, so disable the warning instead! */ -#pragma clang diagnostic ignored "-Wcast-align" - -/* We use C style casts everywhere. */ -#pragma clang diagnostic ignored "-Wold-style-cast" - -/* We need long long for strtoull. */ -#pragma clang diagnostic ignored "-Wc++11-long-long" - -/* Who cares if nullptr doesn't work with C++98, we don't use it there! */ -#pragma clang diagnostic ignored "-Wc++98-compat" -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#elif defined(_MSC_VER) -#pragma warning(push) - -/* disable 'function selected for inline expansion' warning. */ -#pragma warning(disable : 4711) - -/* disable '#pragma warning: there is no warning number' warning. */ -#pragma warning(disable : 4619) - -/* disable 'warning number not a valid compiler warning' warning. */ -#pragma warning(disable : 4616) - -/* disable 'Compiler will insert Spectre mitigation for memory load if - * /Qspectre. */ -/* switch specified' warning. */ -#pragma warning(disable : 5045) -#endif - -struct json_parse_state_s { - const char *src; - size_t size; - size_t offset; - size_t flags_bitset; - char *data; - char *dom; - size_t dom_size; - size_t data_size; - size_t line_no; /* line counter for error reporting. */ - size_t line_offset; /* (offset-line_offset) is the character number (in - bytes). */ - size_t error; -}; - -json_weak int json_hexadecimal_digit(const char c); -int json_hexadecimal_digit(const char c) { - if ('0' <= c && c <= '9') { - return c - '0'; - } - if ('a' <= c && c <= 'f') { - return c - 'a' + 10; - } - if ('A' <= c && c <= 'F') { - return c - 'A' + 10; - } - return -1; -} - -json_weak int json_hexadecimal_value(const char * c, const unsigned long size, unsigned long * result); - int json_hexadecimal_value(const char * c, const unsigned long size, unsigned long * result) { - const char * p; - int digit; - - if (size > sizeof(unsigned long) * 2) { - return 0; - } - - *result = 0; - for (p = c; (unsigned long)(p - c) < size; ++p) { - *result <<= 4; - digit = json_hexadecimal_digit(*p); - if (digit < 0 || digit > 15) { - return 0; - } - *result |= (unsigned char)digit; - } - return 1; -} - -json_weak int json_skip_whitespace(struct json_parse_state_s *state); - int json_skip_whitespace(struct json_parse_state_s *state) { - size_t offset = state->offset; - const size_t size = state->size; - const char *const src = state->src; - - /* the only valid whitespace according to ECMA-404 is ' ', '\n', '\r' and - * '\t'. */ - switch (src[offset]) { - default: - return 0; - case ' ': - case '\r': - case '\t': - case '\n': - break; - } - - do { - switch (src[offset]) { - default: - /* Update offset. */ - state->offset = offset; - return 1; - case ' ': - case '\r': - case '\t': - break; - case '\n': - state->line_no++; - state->line_offset = offset; - break; - } - - offset++; - } while (offset < size); - - /* Update offset. */ - state->offset = offset; - return 1; -} - -json_weak int json_skip_c_style_comments(struct json_parse_state_s *state); - int json_skip_c_style_comments(struct json_parse_state_s *state) { - /* do we have a comment?. */ - if ('/' == state->src[state->offset]) { - /* skip '/'. */ - state->offset++; - - if ('/' == state->src[state->offset]) { - /* we had a comment of the form //. */ - - /* skip second '/'. */ - state->offset++; - - while (state->offset < state->size) { - switch (state->src[state->offset]) { - default: - /* skip the character in the comment. */ - state->offset++; - break; - case '\n': - /* if we have a newline, our comment has ended! Skip the newline. */ - state->offset++; - - /* we entered a newline, so move our line info forward. */ - state->line_no++; - state->line_offset = state->offset; - return 1; - } - } - - /* we reached the end of the JSON file! */ - return 1; - } else if ('*' == state->src[state->offset]) { - /* we had a comment in the C-style long form. */ - - /* skip '*'. */ - state->offset++; - - while (state->offset + 1 < state->size) { - if (('*' == state->src[state->offset]) && - ('/' == state->src[state->offset + 1])) { - /* we reached the end of our comment! */ - state->offset += 2; - return 1; - } else if ('\n' == state->src[state->offset]) { - /* we entered a newline, so move our line info forward. */ - state->line_no++; - state->line_offset = state->offset; - } - - /* skip character within comment. */ - state->offset++; - } - - /* Comment wasn't ended correctly which is a failure. */ - return 1; - } - } - - /* we didn't have any comment, which is ok too! */ - return 0; -} - -json_weak int json_skip_all_skippables(struct json_parse_state_s *state); - int json_skip_all_skippables(struct json_parse_state_s *state) { - /* skip all whitespace and other skippables until there are none left. note - * that the previous version suffered from read past errors should. the - * stream end on json_skip_c_style_comments eg. '{"a" ' with comments flag. - */ - - int did_consume = 0; - const size_t size = state->size; - - if (json_parse_flags_allow_c_style_comments & state->flags_bitset) { - do { - if (state->offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - did_consume = json_skip_whitespace(state); - - /* This should really be checked on access, not in front of every call. - */ - if (state->offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - did_consume |= json_skip_c_style_comments(state); - } while (0 != did_consume); - } else { - do { - if (state->offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - did_consume = json_skip_whitespace(state); - } while (0 != did_consume); - } - - if (state->offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - return 0; -} - -json_weak int json_get_value_size(struct json_parse_state_s *state, - int is_global_object); - -json_weak int json_get_string_size(struct json_parse_state_s *state, - size_t is_key); -int json_get_string_size(struct json_parse_state_s *state, - size_t is_key) { - size_t offset = state->offset; - const size_t size = state->size; - size_t data_size = 0; - const char *const src = state->src; - const int is_single_quote = '\'' == src[offset]; - const char quote_to_use = is_single_quote ? '\'' : '"'; - const size_t flags_bitset = state->flags_bitset; - unsigned long codepoint; - unsigned long high_surrogate = 0; - - if ((json_parse_flags_allow_location_information & flags_bitset) != 0 && - is_key != 0) { - state->dom_size += sizeof(struct json_string_ex_s); - } else { - state->dom_size += sizeof(struct json_string_s); - } - - if ('"' != src[offset]) { - /* if we are allowed single quoted strings check for that too. */ - if (!((json_parse_flags_allow_single_quoted_strings & flags_bitset) && - is_single_quote)) { - state->error = json_parse_error_expected_opening_quote; - state->offset = offset; - return 1; - } - } - - /* skip leading '"' or '\''. */ - offset++; - - while ((offset < size) && (quote_to_use != src[offset])) { - /* add space for the character. */ - data_size++; - - if ('\\' == src[offset]) { - /* skip reverse solidus character. */ - offset++; - - if (offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - state->offset = offset; - return 1; - } - - switch (src[offset]) { - default: - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - case '"': - case '\\': - case '/': - case 'b': - case 'f': - case 'n': - case 'r': - case 't': - /* all valid characters! */ - offset++; - break; - case 'u': - if (!(offset + 5 < size)) { - /* invalid escaped unicode sequence! */ - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - - codepoint = 0; - if (!json_hexadecimal_value(&src[offset + 1], 4, &codepoint)) { - /* escaped unicode sequences must contain 4 hexadecimal digits! */ - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - - /* Valid sequence! - * see: https://en.wikipedia.org/wiki/UTF-8#Invalid_code_points. - * 1 7 U + 0000 U + 007F 0xxxxxxx. - * 2 11 U + 0080 U + 07FF 110xxxxx - * 10xxxxxx. - * 3 16 U + 0800 U + FFFF 1110xxxx - * 10xxxxxx 10xxxxxx. - * 4 21 U + 10000 U + 10FFFF 11110xxx - * 10xxxxxx 10xxxxxx 10xxxxxx. - * Note: the high and low surrogate halves used by UTF-16 (U+D800 - * through U+DFFF) and code points not encodable by UTF-16 (those after - * U+10FFFF) are not legal Unicode values, and their UTF-8 encoding must - * be treated as an invalid byte sequence. */ - - if (high_surrogate != 0) { - /* we previously read the high half of the \uxxxx\uxxxx pair, so now - * we expect the low half. */ - if (codepoint >= 0xdc00 && - codepoint <= 0xdfff) { /* low surrogate range. */ - data_size += 3; - high_surrogate = 0; - } else { - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - } else if (codepoint <= 0x7f) { - data_size += 0; - } else if (codepoint <= 0x7ff) { - data_size += 1; - } else if (codepoint >= 0xd800 && - codepoint <= 0xdbff) { /* high surrogate range. */ - /* The codepoint is the first half of a "utf-16 surrogate pair". so we - * need the other half for it to be valid: \uHHHH\uLLLL. */ - if (offset + 11 > size || '\\' != src[offset + 5] || - 'u' != src[offset + 6]) { - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - high_surrogate = codepoint; - } else if (codepoint >= 0xd800 && - codepoint <= 0xdfff) { /* low surrogate range. */ - /* we did not read the other half before. */ - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } else { - data_size += 2; - } - /* escaped codepoints after 0xffff are supported in json through utf-16 - * surrogate pairs: \uD83D\uDD25 for U+1F525. */ - - offset += 5; - break; - } - } else if (('\r' == src[offset]) || ('\n' == src[offset])) { - if (!(json_parse_flags_allow_multi_line_strings & flags_bitset)) { - /* invalid escaped unicode sequence! */ - state->error = json_parse_error_invalid_string_escape_sequence; - state->offset = offset; - return 1; - } - - offset++; - } else { - /* skip character (valid part of sequence). */ - offset++; - } - } - - /* If the offset is equal to the size, we had a non-terminated string! */ - if (offset == size) { - state->error = json_parse_error_premature_end_of_buffer; - state->offset = offset - 1; - return 1; - } - - /* skip trailing '"' or '\''. */ - offset++; - - /* add enough space to store the string. */ - state->data_size += data_size; - - /* one more byte for null terminator ending the string! */ - state->data_size++; - - /* update offset. */ - state->offset = offset; - - return 0; -} - -json_weak int is_valid_unquoted_key_char(const char c); - int is_valid_unquoted_key_char(const char c) { - return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || - ('A' <= c && c <= 'Z') || ('_' == c)); -} - -json_weak int json_get_key_size(struct json_parse_state_s *state); - int json_get_key_size(struct json_parse_state_s *state) { - const size_t flags_bitset = state->flags_bitset; - - if (json_parse_flags_allow_unquoted_keys & flags_bitset) { - size_t offset = state->offset; - const size_t size = state->size; - const char *const src = state->src; - size_t data_size = state->data_size; - - /* if we are allowing unquoted keys, first grok for a quote... */ - if ('"' == src[offset]) { - /* ... if we got a comma, just parse the key as a string as normal. */ - return json_get_string_size(state, 1); - } else if ((json_parse_flags_allow_single_quoted_strings & flags_bitset) && - ('\'' == src[offset])) { - /* ... if we got a comma, just parse the key as a string as normal. */ - return json_get_string_size(state, 1); - } else { - while ((offset < size) && is_valid_unquoted_key_char(src[offset])) { - offset++; - data_size++; - } - - /* one more byte for null terminator ending the string! */ - data_size++; - - if (json_parse_flags_allow_location_information & flags_bitset) { - state->dom_size += sizeof(struct json_string_ex_s); - } else { - state->dom_size += sizeof(struct json_string_s); - } - - /* update offset. */ - state->offset = offset; - - /* update data_size. */ - state->data_size = data_size; - - return 0; - } - } else { - /* we are only allowed to have quoted keys, so just parse a string! */ - return json_get_string_size(state, 1); - } -} - -json_weak int json_get_object_size(struct json_parse_state_s *state, - int is_global_object); - int json_get_object_size(struct json_parse_state_s *state, - int is_global_object) { - const size_t flags_bitset = state->flags_bitset; - const char *const src = state->src; - const size_t size = state->size; - size_t elements = 0; - int allow_comma = 0; - - if (is_global_object) { - /* if we found an opening '{' of an object, we actually have a normal JSON - * object at the root of the DOM... */ - if (!json_skip_all_skippables(state) && '{' == state->src[state->offset]) { - /* . and we don't actually have a global object after all! */ - is_global_object = 0; - } - } - - if (!is_global_object) { - if ('{' != src[state->offset]) { - state->error = json_parse_error_unknown; - return 1; - } - - /* skip leading '{'. */ - state->offset++; - } - - state->dom_size += sizeof(struct json_object_s); - - while (state->offset < size) { - if (!is_global_object) { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - if ('}' == src[state->offset]) { - /* skip trailing '}'. */ - state->offset++; - - /* finished the object! */ - break; - } - } else { - /* we don't require brackets, so that means the object ends when the input - * stream ends! */ - if (json_skip_all_skippables(state)) { - break; - } - } - - /* if we parsed at least once element previously, grok for a comma. */ - if (allow_comma) { - if (',' == src[state->offset]) { - /* skip comma. */ - state->offset++; - allow_comma = 0; - } else if (json_parse_flags_allow_no_commas & flags_bitset) { - /* we don't require a comma, and we didn't find one, which is ok! */ - allow_comma = 0; - } else { - /* otherwise we are required to have a comma, and we found none. */ - state->error = json_parse_error_expected_comma_or_closing_bracket; - return 1; - } - - if (json_parse_flags_allow_trailing_comma & flags_bitset) { - continue; - } else { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - } - } - - if (json_get_key_size(state)) { - /* key parsing failed! */ - state->error = json_parse_error_invalid_string; - return 1; - } - - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - if (json_parse_flags_allow_equals_in_object & flags_bitset) { - const char current = src[state->offset]; - if ((':' != current) && ('=' != current)) { - state->error = json_parse_error_expected_colon; - return 1; - } - } else { - if (':' != src[state->offset]) { - state->error = json_parse_error_expected_colon; - return 1; - } - } - - /* skip colon. */ - state->offset++; - - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - if (json_get_value_size(state, /* is_global_object = */ 0)) { - /* value parsing failed! */ - return 1; - } - - /* successfully parsed a name/value pair! */ - elements++; - allow_comma = 1; - } - - state->dom_size += sizeof(struct json_object_element_s) * elements; - - return 0; -} - -json_weak int json_get_array_size(struct json_parse_state_s *state); - int json_get_array_size(struct json_parse_state_s *state) { - const size_t flags_bitset = state->flags_bitset; - size_t elements = 0; - int allow_comma = 0; - const char *const src = state->src; - const size_t size = state->size; - - if ('[' != src[state->offset]) { - /* expected array to begin with leading '['. */ - state->error = json_parse_error_unknown; - return 1; - } - - /* skip leading '['. */ - state->offset++; - - state->dom_size += sizeof(struct json_array_s); - - while (state->offset < size) { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - if (']' == src[state->offset]) { - /* skip trailing ']'. */ - state->offset++; - - state->dom_size += sizeof(struct json_array_element_s) * elements; - - /* finished the object! */ - return 0; - } - - /* if we parsed at least once element previously, grok for a comma. */ - if (allow_comma) { - if (',' == src[state->offset]) { - /* skip comma. */ - state->offset++; - allow_comma = 0; - } else if (!(json_parse_flags_allow_no_commas & flags_bitset)) { - state->error = json_parse_error_expected_comma_or_closing_bracket; - return 1; - } - - if (json_parse_flags_allow_trailing_comma & flags_bitset) { - allow_comma = 0; - continue; - } else { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - } - } - - if (json_get_value_size(state, /* is_global_object = */ 0)) { - /* value parsing failed! */ - return 1; - } - - /* successfully parsed an array element! */ - elements++; - allow_comma = 1; - } - - /* we consumed the entire input before finding the closing ']' of the array! - */ - state->error = json_parse_error_premature_end_of_buffer; - return 1; -} - -json_weak int json_get_number_size(struct json_parse_state_s *state); - int json_get_number_size(struct json_parse_state_s *state) { - const size_t flags_bitset = state->flags_bitset; - size_t offset = state->offset; - const size_t size = state->size; - int had_leading_digits = 0; - const char *const src = state->src; - - state->dom_size += sizeof(struct json_number_s); - - if ((json_parse_flags_allow_hexadecimal_numbers & flags_bitset) && - (offset + 1 < size) && ('0' == src[offset]) && - (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) { - /* skip the leading 0x that identifies a hexadecimal number. */ - offset += 2; - - /* consume hexadecimal digits. */ - while ((offset < size) && (('0' <= src[offset] && src[offset] <= '9') || - ('a' <= src[offset] && src[offset] <= 'f') || - ('A' <= src[offset] && src[offset] <= 'F'))) { - offset++; - } - } else { - int found_sign = 0; - int inf_or_nan = 0; - - if ((offset < size) && - (('-' == src[offset]) || - ((json_parse_flags_allow_leading_plus_sign & flags_bitset) && - ('+' == src[offset])))) { - /* skip valid leading '-' or '+'. */ - offset++; - - found_sign = 1; - } - - if (json_parse_flags_allow_inf_and_nan & flags_bitset) { - const char inf[9] = "Infinity"; - const size_t inf_strlen = sizeof(inf) - 1; - const char nan[4] = "NaN"; - const size_t nan_strlen = sizeof(nan) - 1; - - if (offset + inf_strlen < size) { - int found = 1; - size_t i; - for (i = 0; i < inf_strlen; i++) { - if (inf[i] != src[offset + i]) { - found = 0; - break; - } - } - - if (found) { - /* We found our special 'Infinity' keyword! */ - offset += inf_strlen; - - inf_or_nan = 1; - } - } - - if (offset + nan_strlen < size) { - int found = 1; - size_t i; - for (i = 0; i < nan_strlen; i++) { - if (nan[i] != src[offset + i]) { - found = 0; - break; - } - } - - if (found) { - /* We found our special 'NaN' keyword! */ - offset += nan_strlen; - - inf_or_nan = 1; - } - } - } - - if (found_sign && !inf_or_nan && (offset < size) && - !('0' <= src[offset] && src[offset] <= '9')) { - /* check if we are allowing leading '.'. */ - if (!(json_parse_flags_allow_leading_or_trailing_decimal_point & - flags_bitset) || - ('.' != src[offset])) { - /* a leading '-' must be immediately followed by any digit! */ - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - } - } - - if ((offset < size) && ('0' == src[offset])) { - /* skip valid '0'. */ - offset++; - - /* we need to record whether we had any leading digits for checks later. - */ - had_leading_digits = 1; - - if ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { - /* a leading '0' must not be immediately followed by any digit! */ - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - } - } - - /* the main digits of our number next. */ - while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { - offset++; - - /* we need to record whether we had any leading digits for checks later. - */ - had_leading_digits = 1; - } - - if ((offset < size) && ('.' == src[offset])) { - offset++; - - if (!('0' <= src[offset] && src[offset] <= '9')) { - if (!(json_parse_flags_allow_leading_or_trailing_decimal_point & - flags_bitset) || - !had_leading_digits) { - /* a decimal point must be followed by at least one digit. */ - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - } - } - - /* a decimal point can be followed by more digits of course! */ - while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { - offset++; - } - } - - if ((offset < size) && ('e' == src[offset] || 'E' == src[offset])) { - /* our number has an exponent! Wkip 'e' or 'E'. */ - offset++; - - if ((offset < size) && ('-' == src[offset] || '+' == src[offset])) { - /* skip optional '-' or '+'. */ - offset++; - } - - /* consume exponent digits. */ - while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) { - offset++; - } - } - } - - if (offset < size) { - switch (src[offset]) { - case ' ': - case '\t': - case '\r': - case '\n': - case '}': - case ',': - case ']': - /* all of the above are ok. */ - break; - case '=': - if (json_parse_flags_allow_equals_in_object & flags_bitset) { - break; - } - - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - default: - state->error = json_parse_error_invalid_number_format; - state->offset = offset; - return 1; - } - } - - state->data_size += offset - state->offset; - - /* one more byte for null terminator ending the number string! */ - state->data_size++; - - /* update offset. */ - state->offset = offset; - - return 0; -} - -json_weak int json_get_value_size(struct json_parse_state_s *state, - int is_global_object); - int json_get_value_size(struct json_parse_state_s *state, - int is_global_object) { - const size_t flags_bitset = state->flags_bitset; - const char *const src = state->src; - size_t offset; - const size_t size = state->size; - - if (json_parse_flags_allow_location_information & flags_bitset) { - state->dom_size += sizeof(struct json_value_ex_s); - } else { - state->dom_size += sizeof(struct json_value_s); - } - - if (is_global_object) { - return json_get_object_size(state, /* is_global_object = */ 1); - } else { - if (json_skip_all_skippables(state)) { - state->error = json_parse_error_premature_end_of_buffer; - return 1; - } - - /* can cache offset now. */ - offset = state->offset; - - switch (src[offset]) { - case '"': - return json_get_string_size(state, 0); - case '\'': - if (json_parse_flags_allow_single_quoted_strings & flags_bitset) { - return json_get_string_size(state, 0); - } else { - /* invalid value! */ - state->error = json_parse_error_invalid_value; - return 1; - } - case '{': - return json_get_object_size(state, /* is_global_object = */ 0); - case '[': - return json_get_array_size(state); - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return json_get_number_size(state); - case '+': - if (json_parse_flags_allow_leading_plus_sign & flags_bitset) { - return json_get_number_size(state); - } else { - /* invalid value! */ - state->error = json_parse_error_invalid_number_format; - return 1; - } - case '.': - if (json_parse_flags_allow_leading_or_trailing_decimal_point & - flags_bitset) { - return json_get_number_size(state); - } else { - /* invalid value! */ - state->error = json_parse_error_invalid_number_format; - return 1; - } - default: - if ((offset + 4) <= size && 't' == src[offset + 0] && - 'r' == src[offset + 1] && 'u' == src[offset + 2] && - 'e' == src[offset + 3]) { - state->offset += 4; - return 0; - } else if ((offset + 5) <= size && 'f' == src[offset + 0] && - 'a' == src[offset + 1] && 'l' == src[offset + 2] && - 's' == src[offset + 3] && 'e' == src[offset + 4]) { - state->offset += 5; - return 0; - } else if ((offset + 4) <= size && 'n' == state->src[offset + 0] && - 'u' == state->src[offset + 1] && - 'l' == state->src[offset + 2] && - 'l' == state->src[offset + 3]) { - state->offset += 4; - return 0; - } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && - (offset + 3) <= size && 'N' == src[offset + 0] && - 'a' == src[offset + 1] && 'N' == src[offset + 2]) { - return json_get_number_size(state); - } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && - (offset + 8) <= size && 'I' == src[offset + 0] && - 'n' == src[offset + 1] && 'f' == src[offset + 2] && - 'i' == src[offset + 3] && 'n' == src[offset + 4] && - 'i' == src[offset + 5] && 't' == src[offset + 6] && - 'y' == src[offset + 7]) { - return json_get_number_size(state); - } - - /* invalid value! */ - state->error = json_parse_error_invalid_value; - return 1; - } - } -} - -json_weak void json_parse_value(struct json_parse_state_s *state, - int is_global_object, struct json_value_s *value); - -json_weak void json_parse_string(struct json_parse_state_s *state, - struct json_string_s *string); - void json_parse_string(struct json_parse_state_s *state, - struct json_string_s *string) { - size_t offset = state->offset; - size_t bytes_written = 0; - const char *const src = state->src; - const char quote_to_use = '\'' == src[offset] ? '\'' : '"'; - char *data = state->data; - unsigned long high_surrogate = 0; - unsigned long codepoint; - - string->string = data; - - /* skip leading '"' or '\''. */ - offset++; - - while (quote_to_use != src[offset]) { - if ('\\' == src[offset]) { - /* skip the reverse solidus. */ - offset++; - - switch (src[offset++]) { - default: - return; /* we cannot ever reach here. */ - case 'u': { - codepoint = 0; - if (!json_hexadecimal_value(&src[offset], 4, &codepoint)) { - return; /* this shouldn't happen as the value was already validated. - */ - } - - offset += 4; - - if (codepoint <= 0x7fu) { - data[bytes_written++] = (char)codepoint; /* 0xxxxxxx. */ - } else if (codepoint <= 0x7ffu) { - data[bytes_written++] = - (char)(0xc0u | (codepoint >> 6)); /* 110xxxxx. */ - data[bytes_written++] = - (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */ - } else if (codepoint >= 0xd800 && - codepoint <= 0xdbff) { /* high surrogate. */ - high_surrogate = codepoint; - continue; /* we need the low half to form a complete codepoint. */ - } else if (codepoint >= 0xdc00 && - codepoint <= 0xdfff) { /* low surrogate. */ - /* combine with the previously read half to obtain the complete - * codepoint. */ - const unsigned long surrogate_offset = 0x10000u - (0xD800u << 10) - 0xDC00u; - codepoint = (high_surrogate << 10) + codepoint + surrogate_offset; - high_surrogate = 0; - data[bytes_written++] = - (char)(0xF0u | (codepoint >> 18)); /* 11110xxx. */ - data[bytes_written++] = - (char)(0x80u | ((codepoint >> 12) & 0x3fu)); /* 10xxxxxx. */ - data[bytes_written++] = - (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */ - data[bytes_written++] = - (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */ - } else { - /* we assume the value was validated and thus is within the valid - * range. */ - data[bytes_written++] = - (char)(0xe0u | (codepoint >> 12)); /* 1110xxxx. */ - data[bytes_written++] = - (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */ - data[bytes_written++] = - (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */ - } - } break; - case '"': - data[bytes_written++] = '"'; - break; - case '\\': - data[bytes_written++] = '\\'; - break; - case '/': - data[bytes_written++] = '/'; - break; - case 'b': - data[bytes_written++] = '\b'; - break; - case 'f': - data[bytes_written++] = '\f'; - break; - case 'n': - data[bytes_written++] = '\n'; - break; - case 'r': - data[bytes_written++] = '\r'; - break; - case 't': - data[bytes_written++] = '\t'; - break; - case '\r': - data[bytes_written++] = '\r'; - - /* check if we have a "\r\n" sequence. */ - if ('\n' == src[offset]) { - data[bytes_written++] = '\n'; - offset++; - } - - break; - case '\n': - data[bytes_written++] = '\n'; - break; - } - } else { - /* copy the character. */ - data[bytes_written++] = src[offset++]; - } - } - - /* skip trailing '"' or '\''. */ - offset++; - - /* record the size of the string. */ - string->string_size = bytes_written; - - /* add null terminator to string. */ - data[bytes_written++] = '\0'; - - /* move data along. */ - state->data += bytes_written; - - /* update offset. */ - state->offset = offset; -} - -json_weak void json_parse_key(struct json_parse_state_s *state, - struct json_string_s *string); - void json_parse_key(struct json_parse_state_s *state, - struct json_string_s *string) { - if (json_parse_flags_allow_unquoted_keys & state->flags_bitset) { - const char *const src = state->src; - char *const data = state->data; - size_t offset = state->offset; - - /* if we are allowing unquoted keys, check for quoted anyway... */ - if (('"' == src[offset]) || ('\'' == src[offset])) { - /* ... if we got a quote, just parse the key as a string as normal. */ - json_parse_string(state, string); - } else { - size_t size = 0; - - string->string = state->data; - - while (is_valid_unquoted_key_char(src[offset])) { - data[size++] = src[offset++]; - } - - /* add null terminator to string. */ - data[size] = '\0'; - - /* record the size of the string. */ - string->string_size = size++; - - /* move data along. */ - state->data += size; - - /* update offset. */ - state->offset = offset; - } - } else { - /* we are only allowed to have quoted keys, so just parse a string! */ - json_parse_string(state, string); - } -} - -json_weak void json_parse_object(struct json_parse_state_s *state, - int is_global_object, - struct json_object_s *object); - void json_parse_object(struct json_parse_state_s *state, - int is_global_object, - struct json_object_s *object) { - const size_t flags_bitset = state->flags_bitset; - const size_t size = state->size; - const char *const src = state->src; - size_t elements = 0; - int allow_comma = 0; - struct json_object_element_s *previous = json_null; - - if (is_global_object) { - /* if we skipped some whitespace, and then found an opening '{' of an. */ - /* object, we actually have a normal JSON object at the root of the DOM... - */ - if ('{' == src[state->offset]) { - /* . and we don't actually have a global object after all! */ - is_global_object = 0; - } - } - - if (!is_global_object) { - /* skip leading '{'. */ - state->offset++; - } - - (void)json_skip_all_skippables(state); - - /* reset elements. */ - elements = 0; - - while (state->offset < size) { - struct json_object_element_s *element = json_null; - struct json_string_s *string = json_null; - struct json_value_s *value = json_null; - - if (!is_global_object) { - (void)json_skip_all_skippables(state); - - if ('}' == src[state->offset]) { - /* skip trailing '}'. */ - state->offset++; - - /* finished the object! */ - break; - } - } else { - if (json_skip_all_skippables(state)) { - /* global object ends when the file ends! */ - break; - } - } - - /* if we parsed at least one element previously, grok for a comma. */ - if (allow_comma) { - if (',' == src[state->offset]) { - /* skip comma. */ - state->offset++; - allow_comma = 0; - continue; - } - } - - element = (struct json_object_element_s *)state->dom; - - state->dom += sizeof(struct json_object_element_s); - - if (json_null == previous) { - /* this is our first element, so record it in our object. */ - object->start = element; - } else { - previous->next = element; - } - - previous = element; - - if (json_parse_flags_allow_location_information & flags_bitset) { - struct json_string_ex_s *string_ex = - (struct json_string_ex_s *)state->dom; - state->dom += sizeof(struct json_string_ex_s); - - string_ex->offset = state->offset; - string_ex->line_no = state->line_no; - string_ex->row_no = state->offset - state->line_offset; - - string = &(string_ex->string); - } else { - string = (struct json_string_s *)state->dom; - state->dom += sizeof(struct json_string_s); - } - - element->name = string; - - (void)json_parse_key(state, string); - - (void)json_skip_all_skippables(state); - - /* skip colon or equals. */ - state->offset++; - - (void)json_skip_all_skippables(state); - - if (json_parse_flags_allow_location_information & flags_bitset) { - struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom; - state->dom += sizeof(struct json_value_ex_s); - - value_ex->offset = state->offset; - value_ex->line_no = state->line_no; - value_ex->row_no = state->offset - state->line_offset; - - value = &(value_ex->value); - } else { - value = (struct json_value_s *)state->dom; - state->dom += sizeof(struct json_value_s); - } - - element->value = value; - - json_parse_value(state, /* is_global_object = */ 0, value); - - /* successfully parsed a name/value pair! */ - elements++; - allow_comma = 1; - } - - /* if we had at least one element, end the linked list. */ - if (previous) { - previous->next = json_null; - } - - if (0 == elements) { - object->start = json_null; - } - - object->length = elements; -} - -json_weak void json_parse_array(struct json_parse_state_s *state, - struct json_array_s *array); - void json_parse_array(struct json_parse_state_s *state, - struct json_array_s *array) { - const char *const src = state->src; - const size_t size = state->size; - size_t elements = 0; - int allow_comma = 0; - struct json_array_element_s *previous = json_null; - - /* skip leading '['. */ - state->offset++; - - (void)json_skip_all_skippables(state); - - /* reset elements. */ - elements = 0; - - do { - struct json_array_element_s *element = json_null; - struct json_value_s *value = json_null; - - (void)json_skip_all_skippables(state); - - if (']' == src[state->offset]) { - /* skip trailing ']'. */ - state->offset++; - - /* finished the array! */ - break; - } - - /* if we parsed at least one element previously, grok for a comma. */ - if (allow_comma) { - if (',' == src[state->offset]) { - /* skip comma. */ - state->offset++; - allow_comma = 0; - continue; - } - } - - element = (struct json_array_element_s *)state->dom; - - state->dom += sizeof(struct json_array_element_s); - - if (json_null == previous) { - /* this is our first element, so record it in our array. */ - array->start = element; - } else { - previous->next = element; - } - - previous = element; - - if (json_parse_flags_allow_location_information & state->flags_bitset) { - struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom; - state->dom += sizeof(struct json_value_ex_s); - - value_ex->offset = state->offset; - value_ex->line_no = state->line_no; - value_ex->row_no = state->offset - state->line_offset; - - value = &(value_ex->value); - } else { - value = (struct json_value_s *)state->dom; - state->dom += sizeof(struct json_value_s); - } - - element->value = value; - - json_parse_value(state, /* is_global_object = */ 0, value); - - /* successfully parsed an array element! */ - elements++; - allow_comma = 1; - } while (state->offset < size); - - /* end the linked list. */ - if (previous) { - previous->next = json_null; - } - - if (0 == elements) { - array->start = json_null; - } - - array->length = elements; -} - -json_weak void json_parse_number(struct json_parse_state_s *state, - struct json_number_s *number); - void json_parse_number(struct json_parse_state_s *state, - struct json_number_s *number) { - const size_t flags_bitset = state->flags_bitset; - size_t offset = state->offset; - const size_t size = state->size; - size_t bytes_written = 0; - const char *const src = state->src; - char *data = state->data; - - number->number = data; - - if (json_parse_flags_allow_hexadecimal_numbers & flags_bitset) { - if (('0' == src[offset]) && - (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) { - /* consume hexadecimal digits. */ - while ((offset < size) && - (('0' <= src[offset] && src[offset] <= '9') || - ('a' <= src[offset] && src[offset] <= 'f') || - ('A' <= src[offset] && src[offset] <= 'F') || - ('x' == src[offset]) || ('X' == src[offset]))) { - data[bytes_written++] = src[offset++]; - } - } - } - - while (offset < size) { - int end = 0; - - switch (src[offset]) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '.': - case 'e': - case 'E': - case '+': - case '-': - data[bytes_written++] = src[offset++]; - break; - default: - end = 1; - break; - } - - if (0 != end) { - break; - } - } - - if (json_parse_flags_allow_inf_and_nan & flags_bitset) { - const size_t inf_strlen = 8; /* = strlen("Infinity");. */ - const size_t nan_strlen = 3; /* = strlen("NaN");. */ - - if (offset + inf_strlen < size) { - if ('I' == src[offset]) { - size_t i; - /* We found our special 'Infinity' keyword! */ - for (i = 0; i < inf_strlen; i++) { - data[bytes_written++] = src[offset++]; - } - } - } - - if (offset + nan_strlen < size) { - if ('N' == src[offset]) { - size_t i; - /* We found our special 'NaN' keyword! */ - for (i = 0; i < nan_strlen; i++) { - data[bytes_written++] = src[offset++]; - } - } - } - } - - /* record the size of the number. */ - number->number_size = bytes_written; - /* add null terminator to number string. */ - data[bytes_written++] = '\0'; - /* move data along. */ - state->data += bytes_written; - /* update offset. */ - state->offset = offset; -} - -json_weak void json_parse_value(struct json_parse_state_s *state, - int is_global_object, struct json_value_s *value); - void json_parse_value(struct json_parse_state_s *state, - int is_global_object, struct json_value_s *value) { - const size_t flags_bitset = state->flags_bitset; - const char *const src = state->src; - const size_t size = state->size; - size_t offset; - - (void)json_skip_all_skippables(state); - - /* cache offset now. */ - offset = state->offset; - - if (is_global_object) { - value->type = json_type_object; - value->payload = state->dom; - state->dom += sizeof(struct json_object_s); - json_parse_object(state, /* is_global_object = */ 1, - (struct json_object_s *)value->payload); - } else { - switch (src[offset]) { - case '"': - case '\'': - value->type = json_type_string; - value->payload = state->dom; - state->dom += sizeof(struct json_string_s); - json_parse_string(state, (struct json_string_s *)value->payload); - break; - case '{': - value->type = json_type_object; - value->payload = state->dom; - state->dom += sizeof(struct json_object_s); - json_parse_object(state, /* is_global_object = */ 0, - (struct json_object_s *)value->payload); - break; - case '[': - value->type = json_type_array; - value->payload = state->dom; - state->dom += sizeof(struct json_array_s); - json_parse_array(state, (struct json_array_s *)value->payload); - break; - case '-': - case '+': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '.': - value->type = json_type_number; - value->payload = state->dom; - state->dom += sizeof(struct json_number_s); - json_parse_number(state, (struct json_number_s *)value->payload); - break; - default: - if ((offset + 4) <= size && 't' == src[offset + 0] && - 'r' == src[offset + 1] && 'u' == src[offset + 2] && - 'e' == src[offset + 3]) { - value->type = json_type_true; - value->payload = json_null; - state->offset += 4; - } else if ((offset + 5) <= size && 'f' == src[offset + 0] && - 'a' == src[offset + 1] && 'l' == src[offset + 2] && - 's' == src[offset + 3] && 'e' == src[offset + 4]) { - value->type = json_type_false; - value->payload = json_null; - state->offset += 5; - } else if ((offset + 4) <= size && 'n' == src[offset + 0] && - 'u' == src[offset + 1] && 'l' == src[offset + 2] && - 'l' == src[offset + 3]) { - value->type = json_type_null; - value->payload = json_null; - state->offset += 4; - } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && - (offset + 3) <= size && 'N' == src[offset + 0] && - 'a' == src[offset + 1] && 'N' == src[offset + 2]) { - value->type = json_type_number; - value->payload = state->dom; - state->dom += sizeof(struct json_number_s); - json_parse_number(state, (struct json_number_s *)value->payload); - } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) && - (offset + 8) <= size && 'I' == src[offset + 0] && - 'n' == src[offset + 1] && 'f' == src[offset + 2] && - 'i' == src[offset + 3] && 'n' == src[offset + 4] && - 'i' == src[offset + 5] && 't' == src[offset + 6] && - 'y' == src[offset + 7]) { - value->type = json_type_number; - value->payload = state->dom; - state->dom += sizeof(struct json_number_s); - json_parse_number(state, (struct json_number_s *)value->payload); - } - break; - } - } -} - -struct json_value_s * -json_parse_ex(const void *src, size_t src_size, size_t flags_bitset, - void *(*alloc_func_ptr)(void *user_data, size_t size), - void *user_data, struct json_parse_result_s *result) { - struct json_parse_state_s state; - void *allocation; - struct json_value_s *value; - size_t total_size; - int input_error; - - if (result) { - result->error = json_parse_error_none; - result->error_offset = 0; - result->error_line_no = 0; - result->error_row_no = 0; - } - - if (json_null == src) { - /* invalid src pointer was null! */ - return json_null; - } - - state.src = (const char *)src; - state.size = src_size; - state.offset = 0; - state.line_no = 1; - state.line_offset = 0; - state.error = json_parse_error_none; - state.dom_size = 0; - state.data_size = 0; - state.flags_bitset = flags_bitset; - - input_error = json_get_value_size( - &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset)); - - if (0 == input_error) { - json_skip_all_skippables(&state); - - if (state.offset != state.size) { - /* our parsing didn't have an error, but there are characters remaining in - * the input that weren't part of the JSON! */ - - state.error = json_parse_error_unexpected_trailing_characters; - input_error = 1; - } - } - - if (input_error) { - /* parsing value's size failed (most likely an invalid JSON DOM!). */ - if (result) { - result->error = state.error; - result->error_offset = state.offset; - result->error_line_no = state.line_no; - result->error_row_no = state.offset - state.line_offset; - } - return json_null; - } - - /* our total allocation is the combination of the dom and data sizes (we. */ - /* first encode the structure of the JSON, and then the data referenced by. */ - /* the JSON values). */ - total_size = state.dom_size + state.data_size; - - if (json_null == alloc_func_ptr) { - allocation = malloc(total_size); - } else { - allocation = alloc_func_ptr(user_data, total_size); - } - - if (json_null == allocation) { - /* malloc failed! */ - if (result) { - result->error = json_parse_error_allocator_failed; - result->error_offset = 0; - result->error_line_no = 0; - result->error_row_no = 0; - } - - return json_null; - } - - /* reset offset so we can reuse it. */ - state.offset = 0; - - /* reset the line information so we can reuse it. */ - state.line_no = 1; - state.line_offset = 0; - - state.dom = (char *)allocation; - state.data = state.dom + state.dom_size; - - if (json_parse_flags_allow_location_information & state.flags_bitset) { - struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state.dom; - state.dom += sizeof(struct json_value_ex_s); - - value_ex->offset = state.offset; - value_ex->line_no = state.line_no; - value_ex->row_no = state.offset - state.line_offset; - - value = &(value_ex->value); - } else { - value = (struct json_value_s *)state.dom; - state.dom += sizeof(struct json_value_s); - } - - json_parse_value( - &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset), - value); - - return (struct json_value_s *)allocation; -} - -struct json_value_s *json_parse(const void *src, size_t src_size) { - return json_parse_ex(src, src_size, json_parse_flags_default, json_null, - json_null, json_null); -} - -struct json_string_s *json_value_as_string(struct json_value_s *const value) { - if (value->type != json_type_string) { - return json_null; - } - - return (struct json_string_s *)value->payload; -} - -struct json_number_s *json_value_as_number(struct json_value_s *const value) { - if (value->type != json_type_number) { - return json_null; - } - - return (struct json_number_s *)value->payload; -} - -struct json_object_s *json_value_as_object(struct json_value_s *const value) { - if (value->type != json_type_object) { - return json_null; - } - - return (struct json_object_s *)value->payload; -} - -struct json_array_s *json_value_as_array(struct json_value_s *const value) { - if (value->type != json_type_array) { - return json_null; - } - - return (struct json_array_s *)value->payload; -} - -int json_value_is_true(const struct json_value_s *const value) { - return value->type == json_type_true; -} - -int json_value_is_false(const struct json_value_s *const value) { - return value->type == json_type_false; -} - -int json_value_is_null(const struct json_value_s *const value) { - return value->type == json_type_null; -} - -json_weak int json_write_minified_get_value_size(const struct json_value_s *value, - size_t *size); - -json_weak int json_write_get_number_size(const struct json_number_s *number, - size_t *size); - int json_write_get_number_size(const struct json_number_s *number, - size_t *size) { - json_uintmax_t parsed_number; - size_t i; - - if (number->number_size >= 2) { - switch (number->number[1]) { - default: - break; - case 'x': - case 'X': - /* the number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal - * so we have to do extra work to convert it to a non-hexadecimal for JSON - * output. */ - parsed_number = json_strtoumax(number->number, json_null, 0); - - i = 0; - - while (0 != parsed_number) { - parsed_number /= 10; - i++; - } - - *size += i; - return 0; - } - } - - /* check to see if the number has leading/trailing decimal point. */ - i = 0; - - /* skip any leading '+' or '-'. */ - if ((i < number->number_size) && - (('+' == number->number[i]) || ('-' == number->number[i]))) { - i++; - } - - /* check if we have infinity. */ - if ((i < number->number_size) && ('I' == number->number[i])) { - const char *inf = "Infinity"; - size_t k; - - for (k = i; k < number->number_size; k++) { - const char c = *inf++; - - /* Check if we found the Infinity string! */ - if ('\0' == c) { - break; - } else if (c != number->number[k]) { - break; - } - } - - if ('\0' == *inf) { - /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */ - *size += 22; - - /* if we had a leading '-' we need to record it in the JSON output. */ - if ('-' == number->number[0]) { - *size += 1; - } - } - - return 0; - } - - /* check if we have nan. */ - if ((i < number->number_size) && ('N' == number->number[i])) { - const char *nan = "NaN"; - size_t k; - - for (k = i; k < number->number_size; k++) { - const char c = *nan++; - - /* Check if we found the NaN string! */ - if ('\0' == c) { - break; - } else if (c != number->number[k]) { - break; - } - } - - if ('\0' == *nan) { - /* NaN becomes 1 because JSON can't support it. */ - *size += 1; - - return 0; - } - } - - /* if we had a leading decimal point. */ - if ((i < number->number_size) && ('.' == number->number[i])) { - /* 1 + because we had a leading decimal point. */ - *size += 1; - goto cleanup; - } - - for (; i < number->number_size; i++) { - const char c = number->number[i]; - if (!('0' <= c && c <= '9')) { - break; - } - } - - /* if we had a trailing decimal point. */ - if ((i + 1 == number->number_size) && ('.' == number->number[i])) { - /* 1 + because we had a trailing decimal point. */ - *size += 1; - goto cleanup; - } - -cleanup: - *size += number->number_size; /* the actual string of the number. */ - - /* if we had a leading '+' we don't record it in the JSON output. */ - if ('+' == number->number[0]) { - *size -= 1; - } - - return 0; -} - -json_weak int json_write_get_string_size(const struct json_string_s *string, - size_t *size); - int json_write_get_string_size(const struct json_string_s *string, - size_t *size) { - size_t i; - for (i = 0; i < string->string_size; i++) { - switch (string->string[i]) { - case '"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - *size += 2; - break; - default: - *size += 1; - break; - } - } - - *size += 2; /* need to encode the surrounding '"' characters. */ - - return 0; -} - -json_weak int json_write_minified_get_array_size(const struct json_array_s *array, - size_t *size); - int json_write_minified_get_array_size(const struct json_array_s *array, - size_t *size) { - struct json_array_element_s *element; - - *size += 2; /* '[' and ']'. */ - - if (1 < array->length) { - *size += array->length - 1; /* ','s seperate each element. */ - } - - for (element = array->start; json_null != element; element = element->next) { - if (json_write_minified_get_value_size(element->value, size)) { - /* value was malformed! */ - return 1; - } - } - - return 0; -} - -json_weak int -json_write_minified_get_object_size(const struct json_object_s *object, - size_t *size); - int -json_write_minified_get_object_size(const struct json_object_s *object, - size_t *size) { - struct json_object_element_s *element; - - *size += 2; /* '{' and '}'. */ - - *size += object->length; /* ':'s seperate each name/value pair. */ - - if (1 < object->length) { - *size += object->length - 1; /* ','s seperate each element. */ - } - - for (element = object->start; json_null != element; element = element->next) { - if (json_write_get_string_size(element->name, size)) { - /* string was malformed! */ - return 1; - } - - if (json_write_minified_get_value_size(element->value, size)) { - /* value was malformed! */ - return 1; - } - } - - return 0; -} - -json_weak int json_write_minified_get_value_size(const struct json_value_s *value, - size_t *size); - int json_write_minified_get_value_size(const struct json_value_s *value, - size_t *size) { - switch (value->type) { - default: - /* unknown value type found! */ - return 1; - case json_type_number: - return json_write_get_number_size((struct json_number_s *)value->payload, - size); - case json_type_string: - return json_write_get_string_size((struct json_string_s *)value->payload, - size); - case json_type_array: - return json_write_minified_get_array_size( - (struct json_array_s *)value->payload, size); - case json_type_object: - return json_write_minified_get_object_size( - (struct json_object_s *)value->payload, size); - case json_type_true: - *size += 4; /* the string "true". */ - return 0; - case json_type_false: - *size += 5; /* the string "false". */ - return 0; - case json_type_null: - *size += 4; /* the string "null". */ - return 0; - } -} - -json_weak char *json_write_minified_value(const struct json_value_s *value, - char *data); - -json_weak char *json_write_number(const struct json_number_s *number, char *data); - char *json_write_number(const struct json_number_s *number, char *data) { - json_uintmax_t parsed_number, backup; - size_t i; - - if (number->number_size >= 2) { - switch (number->number[1]) { - default: - break; - case 'x': - case 'X': - /* The number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal - * so we have to do extra work to convert it to a non-hexadecimal for JSON - * output. */ - parsed_number = json_strtoumax(number->number, json_null, 0); - - /* We need a copy of parsed number twice, so take a backup of it. */ - backup = parsed_number; - - i = 0; - - while (0 != parsed_number) { - parsed_number /= 10; - i++; - } - - /* Restore parsed_number to its original value stored in the backup. */ - parsed_number = backup; - - /* Now use backup to take a copy of i, or the length of the string. */ - backup = i; - - do { - *(data + i - 1) = '0' + (char)(parsed_number % 10); - parsed_number /= 10; - i--; - } while (0 != parsed_number); - - data += backup; - - return data; - } - } - - /* check to see if the number has leading/trailing decimal point. */ - i = 0; - - /* skip any leading '-'. */ - if ((i < number->number_size) && - (('+' == number->number[i]) || ('-' == number->number[i]))) { - i++; - } - - /* check if we have infinity. */ - if ((i < number->number_size) && ('I' == number->number[i])) { - const char *inf = "Infinity"; - size_t k; - - for (k = i; k < number->number_size; k++) { - const char c = *inf++; - - /* Check if we found the Infinity string! */ - if ('\0' == c) { - break; - } else if (c != number->number[k]) { - break; - } - } - - if ('\0' == *inf++) { - const char *dbl_max; - - /* if we had a leading '-' we need to record it in the JSON output. */ - if ('-' == number->number[0]) { - *data++ = '-'; - } - - /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */ - for (dbl_max = "1.7976931348623158e308"; '\0' != *dbl_max; dbl_max++) { - *data++ = *dbl_max; - } - - return data; - } - } - - /* check if we have nan. */ - if ((i < number->number_size) && ('N' == number->number[i])) { - const char *nan = "NaN"; - size_t k; - - for (k = i; k < number->number_size; k++) { - const char c = *nan++; - - /* Check if we found the NaN string! */ - if ('\0' == c) { - break; - } else if (c != number->number[k]) { - break; - } - } - - if ('\0' == *nan++) { - /* NaN becomes 0 because JSON can't support it. */ - *data++ = '0'; - return data; - } - } - - /* if we had a leading decimal point. */ - if ((i < number->number_size) && ('.' == number->number[i])) { - i = 0; - - /* skip any leading '+'. */ - if ('+' == number->number[i]) { - i++; - } - - /* output the leading '-' if we had one. */ - if ('-' == number->number[i]) { - *data++ = '-'; - i++; - } - - /* insert a '0' to fix the leading decimal point for JSON output. */ - *data++ = '0'; - - /* and output the rest of the number as normal. */ - for (; i < number->number_size; i++) { - *data++ = number->number[i]; - } - - return data; - } - - for (; i < number->number_size; i++) { - const char c = number->number[i]; - if (!('0' <= c && c <= '9')) { - break; - } - } - - /* if we had a trailing decimal point. */ - if ((i + 1 == number->number_size) && ('.' == number->number[i])) { - i = 0; - - /* skip any leading '+'. */ - if ('+' == number->number[i]) { - i++; - } - - /* output the leading '-' if we had one. */ - if ('-' == number->number[i]) { - *data++ = '-'; - i++; - } - - /* and output the rest of the number as normal. */ - for (; i < number->number_size; i++) { - *data++ = number->number[i]; - } - - /* insert a '0' to fix the trailing decimal point for JSON output. */ - *data++ = '0'; - - return data; - } - - i = 0; - - /* skip any leading '+'. */ - if ('+' == number->number[i]) { - i++; - } - - for (; i < number->number_size; i++) { - *data++ = number->number[i]; - } - - return data; -} - -json_weak char *json_write_string(const struct json_string_s *string, char *data); - char *json_write_string(const struct json_string_s *string, char *data) { - size_t i; - - *data++ = '"'; /* open the string. */ - - for (i = 0; i < string->string_size; i++) { - switch (string->string[i]) { - case '"': - *data++ = '\\'; /* escape the control character. */ - *data++ = '"'; - break; - case '\\': - *data++ = '\\'; /* escape the control character. */ - *data++ = '\\'; - break; - case '\b': - *data++ = '\\'; /* escape the control character. */ - *data++ = 'b'; - break; - case '\f': - *data++ = '\\'; /* escape the control character. */ - *data++ = 'f'; - break; - case '\n': - *data++ = '\\'; /* escape the control character. */ - *data++ = 'n'; - break; - case '\r': - *data++ = '\\'; /* escape the control character. */ - *data++ = 'r'; - break; - case '\t': - *data++ = '\\'; /* escape the control character. */ - *data++ = 't'; - break; - default: - *data++ = string->string[i]; - break; - } - } - - *data++ = '"'; /* close the string. */ - - return data; -} - -json_weak char *json_write_minified_array(const struct json_array_s *array, - char *data); -char *json_write_minified_array(const struct json_array_s *array, - char *data) { - struct json_array_element_s *element = json_null; - - *data++ = '['; /* open the array. */ - - for (element = array->start; json_null != element; element = element->next) { - if (element != array->start) { - *data++ = ','; /* ','s seperate each element. */ - } - - data = json_write_minified_value(element->value, data); - - if (json_null == data) { - /* value was malformed! */ - return json_null; - } - } - - *data++ = ']'; /* close the array. */ - - return data; -} - -json_weak char *json_write_minified_object(const struct json_object_s *object, - char *data); - char *json_write_minified_object(const struct json_object_s *object, - char *data) { - struct json_object_element_s *element = json_null; - - *data++ = '{'; /* open the object. */ - - for (element = object->start; json_null != element; - element = element->next) { - if (element != object->start) { - *data++ = ','; /* ','s seperate each element. */ - } - - data = json_write_string(element->name, data); - - if (json_null == data) { - /* string was malformed! */ - return json_null; - } - - *data++ = ':'; /* ':'s seperate each name/value pair. */ - - data = json_write_minified_value(element->value, data); - - if (json_null == data) { - /* value was malformed! */ - return json_null; - } - } - - *data++ = '}'; /* close the object. */ - - return data; -} - -json_weak char *json_write_minified_value(const struct json_value_s *value, - char *data); - char *json_write_minified_value(const struct json_value_s *value, - char *data) { - switch (value->type) { - default: - /* unknown value type found! */ - return json_null; - case json_type_number: - return json_write_number((struct json_number_s *)value->payload, data); - case json_type_string: - return json_write_string((struct json_string_s *)value->payload, data); - case json_type_array: - return json_write_minified_array((struct json_array_s *)value->payload, - data); - case json_type_object: - return json_write_minified_object((struct json_object_s *)value->payload, - data); - case json_type_true: - data[0] = 't'; - data[1] = 'r'; - data[2] = 'u'; - data[3] = 'e'; - return data + 4; - case json_type_false: - data[0] = 'f'; - data[1] = 'a'; - data[2] = 'l'; - data[3] = 's'; - data[4] = 'e'; - return data + 5; - case json_type_null: - data[0] = 'n'; - data[1] = 'u'; - data[2] = 'l'; - data[3] = 'l'; - return data + 4; - } -} - -void *json_write_minified(const struct json_value_s *value, size_t *out_size) { - size_t size = 0; - char *data = json_null; - char *data_end = json_null; - - if (json_null == value) { - return json_null; - } - - if (json_write_minified_get_value_size(value, &size)) { - /* value was malformed! */ - return json_null; - } - - size += 1; /* for the '\0' null terminating character. */ - - data = (char *)malloc(size); - - if (json_null == data) { - /* malloc failed! */ - return json_null; - } - - data_end = json_write_minified_value(value, data); - - if (json_null == data_end) { - /* bad chi occurred! */ - free(data); - return json_null; - } - - /* null terminated the string. */ - *data_end = '\0'; - - if (json_null != out_size) { - *out_size = size; - } - - return data; -} - -json_weak int json_write_pretty_get_value_size(const struct json_value_s *value, - size_t depth, size_t indent_size, - size_t newline_size, size_t *size); - -json_weak int json_write_pretty_get_array_size(const struct json_array_s *array, - size_t depth, size_t indent_size, - size_t newline_size, size_t *size); - int json_write_pretty_get_array_size(const struct json_array_s *array, - size_t depth, size_t indent_size, - size_t newline_size, size_t *size) { - struct json_array_element_s *element; - - *size += 1; /* '['. */ - - if (0 < array->length) { - /* if we have any elements we need to add a newline after our '['. */ - *size += newline_size; - - *size += array->length - 1; /* ','s seperate each element. */ - - for (element = array->start; json_null != element; - element = element->next) { - /* each element gets an indent. */ - *size += (depth + 1) * indent_size; - - if (json_write_pretty_get_value_size(element->value, depth + 1, - indent_size, newline_size, size)) { - /* value was malformed! */ - return 1; - } - - /* each element gets a newline too. */ - *size += newline_size; - } - - /* since we wrote out some elements, need to add a newline and indentation. - */ - /* to the trailing ']'. */ - *size += depth * indent_size; - } - - *size += 1; /* ']'. */ - - return 0; -} - -json_weak int json_write_pretty_get_object_size(const struct json_object_s *object, - size_t depth, size_t indent_size, - size_t newline_size, - size_t *size); - int json_write_pretty_get_object_size(const struct json_object_s *object, - size_t depth, size_t indent_size, - size_t newline_size, - size_t *size) { - struct json_object_element_s *element; - - *size += 1; /* '{'. */ - - if (0 < object->length) { - *size += newline_size; /* need a newline next. */ - - *size += object->length - 1; /* ','s seperate each element. */ - - for (element = object->start; json_null != element; - element = element->next) { - /* each element gets an indent and newline. */ - *size += (depth + 1) * indent_size; - *size += newline_size; - - if (json_write_get_string_size(element->name, size)) { - /* string was malformed! */ - return 1; - } - - *size += 3; /* seperate each name/value pair with " : ". */ - - if (json_write_pretty_get_value_size(element->value, depth + 1, - indent_size, newline_size, size)) { - /* value was malformed! */ - return 1; - } - } - - *size += depth * indent_size; - } - - *size += 1; /* '}'. */ - - return 0; -} - -json_weak int json_write_pretty_get_value_size(const struct json_value_s *value, - size_t depth, size_t indent_size, - size_t newline_size, size_t *size); - int json_write_pretty_get_value_size(const struct json_value_s *value, - size_t depth, size_t indent_size, - size_t newline_size, size_t *size) { - switch (value->type) { - default: - /* unknown value type found! */ - return 1; - case json_type_number: - return json_write_get_number_size((struct json_number_s *)value->payload, - size); - case json_type_string: - return json_write_get_string_size((struct json_string_s *)value->payload, - size); - case json_type_array: - return json_write_pretty_get_array_size( - (struct json_array_s *)value->payload, depth, indent_size, newline_size, - size); - case json_type_object: - return json_write_pretty_get_object_size( - (struct json_object_s *)value->payload, depth, indent_size, - newline_size, size); - case json_type_true: - *size += 4; /* the string "true". */ - return 0; - case json_type_false: - *size += 5; /* the string "false". */ - return 0; - case json_type_null: - *size += 4; /* the string "null". */ - return 0; - } -} - -json_weak char *json_write_pretty_value(const struct json_value_s *value, - size_t depth, const char *indent, - const char *newline, char *data); - -json_weak char *json_write_pretty_array(const struct json_array_s *array, - size_t depth, const char *indent, - const char *newline, char *data); - char *json_write_pretty_array(const struct json_array_s *array, - size_t depth, const char *indent, - const char *newline, char *data) { - size_t k, m; - struct json_array_element_s *element; - - *data++ = '['; /* open the array. */ - - if (0 < array->length) { - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - - for (element = array->start; json_null != element; - element = element->next) { - if (element != array->start) { - *data++ = ','; /* ','s seperate each element. */ - - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - } - - for (k = 0; k < depth + 1; k++) { - for (m = 0; '\0' != indent[m]; m++) { - *data++ = indent[m]; - } - } - - data = json_write_pretty_value(element->value, depth + 1, indent, newline, - data); - - if (json_null == data) { - /* value was malformed! */ - return json_null; - } - } - - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - - for (k = 0; k < depth; k++) { - for (m = 0; '\0' != indent[m]; m++) { - *data++ = indent[m]; - } - } - } - - *data++ = ']'; /* close the array. */ - - return data; -} - -json_weak char *json_write_pretty_object(const struct json_object_s *object, - size_t depth, const char *indent, - const char *newline, char *data); - char *json_write_pretty_object(const struct json_object_s *object, - size_t depth, const char *indent, - const char *newline, char *data) { - size_t k, m; - struct json_object_element_s *element; - - *data++ = '{'; /* open the object. */ - - if (0 < object->length) { - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - - for (element = object->start; json_null != element; - element = element->next) { - if (element != object->start) { - *data++ = ','; /* ','s seperate each element. */ - - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - } - - for (k = 0; k < depth + 1; k++) { - for (m = 0; '\0' != indent[m]; m++) { - *data++ = indent[m]; - } - } - - data = json_write_string(element->name, data); - - if (json_null == data) { - /* string was malformed! */ - return json_null; - } - - /* " : "s seperate each name/value pair. */ - *data++ = ' '; - *data++ = ':'; - *data++ = ' '; - - data = json_write_pretty_value(element->value, depth + 1, indent, newline, - data); - - if (json_null == data) { - /* value was malformed! */ - return json_null; - } - } - - for (k = 0; '\0' != newline[k]; k++) { - *data++ = newline[k]; - } - - for (k = 0; k < depth; k++) { - for (m = 0; '\0' != indent[m]; m++) { - *data++ = indent[m]; - } - } - } - - *data++ = '}'; /* close the object. */ - - return data; -} - -json_weak char *json_write_pretty_value(const struct json_value_s *value, - size_t depth, const char *indent, - const char *newline, char *data); - char *json_write_pretty_value(const struct json_value_s *value, - size_t depth, const char *indent, - const char *newline, char *data) { - switch (value->type) { - default: - /* unknown value type found! */ - return json_null; - case json_type_number: - return json_write_number((struct json_number_s *)value->payload, data); - case json_type_string: - return json_write_string((struct json_string_s *)value->payload, data); - case json_type_array: - return json_write_pretty_array((struct json_array_s *)value->payload, depth, - indent, newline, data); - case json_type_object: - return json_write_pretty_object((struct json_object_s *)value->payload, - depth, indent, newline, data); - case json_type_true: - data[0] = 't'; - data[1] = 'r'; - data[2] = 'u'; - data[3] = 'e'; - return data + 4; - case json_type_false: - data[0] = 'f'; - data[1] = 'a'; - data[2] = 'l'; - data[3] = 's'; - data[4] = 'e'; - return data + 5; - case json_type_null: - data[0] = 'n'; - data[1] = 'u'; - data[2] = 'l'; - data[3] = 'l'; - return data + 4; - } -} - -void *json_write_pretty(const struct json_value_s *value, const char *indent, - const char *newline, size_t *out_size) { - size_t size = 0; - size_t indent_size = 0; - size_t newline_size = 0; - char *data = json_null; - char *data_end = json_null; - - if (json_null == value) { - return json_null; - } - - if (json_null == indent) { - indent = " "; /* default to two spaces. */ - } - - if (json_null == newline) { - newline = "\n"; /* default to linux newlines. */ - } - - while ('\0' != indent[indent_size]) { - ++indent_size; /* skip non-null terminating characters. */ - } - - while ('\0' != newline[newline_size]) { - ++newline_size; /* skip non-null terminating characters. */ - } - - if (json_write_pretty_get_value_size(value, 0, indent_size, newline_size, - &size)) { - /* value was malformed! */ - return json_null; - } - - size += 1; /* for the '\0' null terminating character. */ - - data = (char *)malloc(size); - - if (json_null == data) { - /* malloc failed! */ - return json_null; - } - - data_end = json_write_pretty_value(value, 0, indent, newline, data); - - if (json_null == data_end) { - /* bad chi occurred! */ - free(data); - return json_null; - } - - /* null terminated the string. */ - *data_end = '\0'; - - if (json_null != out_size) { - *out_size = size; - } - - return data; -} - -#if defined(__clang__) -#pragma clang diagnostic pop -#elif defined(_MSC_VER) -#pragma warning(pop) -#endif - -#endif /* SHEREDOM_JSON_H_INCLUDED. */ \ No newline at end of file