Support \u0000 - add size_t string lengths to API, load and dump \u000, etc.

Also:
  Steal strings during parsing for half the mallocs!
  Change all input-caused assertions to errors.  No crashes please, we're programmers.
This commit is contained in:
Chip Salzenberg 2012-04-03 18:00:29 -07:00
parent e4d6a9f6f4
commit 9c259c07aa
15 changed files with 283 additions and 122 deletions

View File

@ -65,24 +65,25 @@ static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t
return 0; return 0;
} }
static int dump_string(const char *str, json_dump_callback_t dump, void *data, size_t flags) static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data, size_t flags)
{ {
const char *pos, *end; const char *pos, *end, *lim;
int32_t codepoint; int32_t codepoint;
if(dump("\"", 1, data)) if(dump("\"", 1, data))
return -1; return -1;
end = pos = str; end = pos = str;
lim = str + len;
while(1) while(1)
{ {
const char *text; const char *text;
char seq[13]; char seq[13];
int length; int length;
while(*end) while(end < lim)
{ {
end = utf8_iterate(pos, &codepoint); end = utf8_iterate(pos, lim - pos, &codepoint);
if(!end) if(!end)
return -1; return -1;
@ -215,7 +216,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
} }
case JSON_STRING: case JSON_STRING:
return dump_string(json_string_value(json), dump, data, flags); return dump_string(json_string_value(json), json_string_length(json), dump, data, flags);
case JSON_ARRAY: case JSON_ARRAY:
{ {
@ -336,7 +337,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
value = json_object_get(json, key); value = json_object_get(json, key);
assert(value); assert(value);
dump_string(key, dump, data, flags); dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) || if(dump(separator, separator_length, data) ||
do_dump(value, flags, depth + 1, dump, data)) do_dump(value, flags, depth + 1, dump, data))
{ {
@ -372,8 +373,9 @@ static int do_dump(const json_t *json, size_t flags, int depth,
while(iter) while(iter)
{ {
void *next = json_object_iter_next((json_t *)json, iter); void *next = json_object_iter_next((json_t *)json, iter);
const char *key = json_object_iter_key(iter);
dump_string(json_object_iter_key(iter), dump, data, flags); dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) || if(dump(separator, separator_length, data) ||
do_dump(json_object_iter_value(iter), flags, depth + 1, do_dump(json_object_iter_value(iter), flags, depth + 1,
dump, data)) dump, data))

View File

@ -67,23 +67,25 @@ typedef long json_int_t;
#endif #endif
#define json_typeof(json) ((json)->type) #define json_typeof(json) ((json)->type)
#define json_is_object(json) (json && json_typeof(json) == JSON_OBJECT) #define json_is_object(json) ((json) && json_typeof(json) == JSON_OBJECT)
#define json_is_array(json) (json && json_typeof(json) == JSON_ARRAY) #define json_is_array(json) ((json) && json_typeof(json) == JSON_ARRAY)
#define json_is_string(json) (json && json_typeof(json) == JSON_STRING) #define json_is_string(json) ((json) && json_typeof(json) == JSON_STRING)
#define json_is_integer(json) (json && json_typeof(json) == JSON_INTEGER) #define json_is_integer(json) ((json) && json_typeof(json) == JSON_INTEGER)
#define json_is_real(json) (json && json_typeof(json) == JSON_REAL) #define json_is_real(json) ((json) && json_typeof(json) == JSON_REAL)
#define json_is_number(json) (json_is_integer(json) || json_is_real(json)) #define json_is_number(json) (json_is_integer(json) || json_is_real(json))
#define json_is_true(json) (json && json_typeof(json) == JSON_TRUE) #define json_is_true(json) ((json) && json_typeof(json) == JSON_TRUE)
#define json_is_false(json) (json && json_typeof(json) == JSON_FALSE) #define json_is_false(json) ((json) && json_typeof(json) == JSON_FALSE)
#define json_is_boolean(json) (json_is_true(json) || json_is_false(json)) #define json_is_boolean(json) (json_is_true(json) || json_is_false(json))
#define json_is_null(json) (json && json_typeof(json) == JSON_NULL) #define json_is_null(json) ((json) && json_typeof(json) == JSON_NULL)
/* construction, destruction, reference counting */ /* construction, destruction, reference counting */
json_t *json_object(void); json_t *json_object(void);
json_t *json_array(void); json_t *json_array(void);
json_t *json_string(const char *value); json_t *json_string(const char *value);
json_t *json_stringn(const char *value, size_t len);
json_t *json_string_nocheck(const char *value); json_t *json_string_nocheck(const char *value);
json_t *json_stringn_nocheck(const char *value, size_t len);
json_t *json_integer(json_int_t value); json_t *json_integer(json_int_t value);
json_t *json_real(double value); json_t *json_real(double value);
json_t *json_true(void); json_t *json_true(void);
@ -199,16 +201,18 @@ int json_array_insert(json_t *array, size_t ind, json_t *value)
} }
const char *json_string_value(const json_t *string); const char *json_string_value(const json_t *string);
size_t json_string_length(const json_t *string);
json_int_t json_integer_value(const json_t *integer); json_int_t json_integer_value(const json_t *integer);
double json_real_value(const json_t *real); double json_real_value(const json_t *real);
double json_number_value(const json_t *json); double json_number_value(const json_t *json);
int json_string_set(json_t *string, const char *value); int json_string_set(json_t *string, const char *value);
int json_string_setn(json_t *string, const char *value, size_t len);
int json_string_set_nocheck(json_t *string, const char *value); int json_string_set_nocheck(json_t *string, const char *value);
int json_string_setn_nocheck(json_t *string, const char *value, size_t len);
int json_integer_set(json_t *integer, json_int_t value); int json_integer_set(json_t *integer, json_int_t value);
int json_real_set(json_t *real, double value); int json_real_set(json_t *real, double value);
/* pack, unpack */ /* pack, unpack */
json_t *json_pack(const char *fmt, ...); json_t *json_pack(const char *fmt, ...);

View File

@ -49,6 +49,7 @@ typedef struct {
typedef struct { typedef struct {
json_t json; json_t json;
char *value; char *value;
size_t length;
} json_string_t; } json_string_t;
typedef struct { typedef struct {
@ -67,6 +68,10 @@ typedef struct {
#define json_to_real(json_) container_of(json_, json_real_t, json) #define json_to_real(json_) container_of(json_, json_real_t, json)
#define json_to_integer(json_) container_of(json_, json_integer_t, json) #define json_to_integer(json_) container_of(json_, json_integer_t, json)
/* Create a string by taking ownership of an existing buffer */
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len);
/* Error message formatting */
void jsonp_error_init(json_error_t *error, const char *source); void jsonp_error_init(json_error_t *error, const char *source);
void jsonp_error_set_source(json_error_t *error, const char *source); void jsonp_error_set_source(json_error_t *error, const char *source);
void jsonp_error_set(json_error_t *error, int line, int column, void jsonp_error_set(json_error_t *error, int line, int column,
@ -83,6 +88,7 @@ void* jsonp_malloc(size_t size);
void jsonp_free(void *ptr); void jsonp_free(void *ptr);
char *jsonp_strndup(const char *str, size_t length); char *jsonp_strndup(const char *str, size_t length);
char *jsonp_strdup(const char *str); char *jsonp_strdup(const char *str);
char *jsonp_strndup(const char *str, size_t len);
/* Windows compatibility */ /* Windows compatibility */
#ifdef _WIN32 #ifdef _WIN32

View File

@ -63,7 +63,10 @@ typedef struct {
strbuffer_t saved_text; strbuffer_t saved_text;
int token; int token;
union { union {
char *string; struct {
char *val;
size_t len;
} string;
json_int_t integer; json_int_t integer;
double real; double real;
} value; } value;
@ -279,6 +282,13 @@ static void lex_save_cached(lex_t *lex)
} }
} }
static void lex_free_string(lex_t *lex)
{
jsonp_free(lex->value.string.val);
lex->value.string.val = NULL;
lex->value.string.len = 0;
}
/* assumes that str points to 'u' plus at least 4 valid hex digits */ /* assumes that str points to 'u' plus at least 4 valid hex digits */
static int32_t decode_unicode_escape(const char *str) static int32_t decode_unicode_escape(const char *str)
{ {
@ -297,7 +307,7 @@ static int32_t decode_unicode_escape(const char *str)
else if(l_isupper(c)) else if(l_isupper(c))
value += c - 'A' + 10; value += c - 'A' + 10;
else else
assert(0); return -1;
} }
return value; return value;
@ -310,7 +320,7 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
char *t; char *t;
int i; int i;
lex->value.string = NULL; lex->value.string.val = NULL;
lex->token = TOKEN_INVALID; lex->token = TOKEN_INVALID;
c = lex_get_save(lex, error); c = lex_get_save(lex, error);
@ -365,14 +375,12 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
- two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair - two \uXXXX escapes (length 12) forming an UTF-16 surrogate pair
are converted to 4 bytes are converted to 4 bytes
*/ */
lex->value.string = jsonp_malloc(lex->saved_text.length + 1); t = jsonp_malloc(lex->saved_text.length + 1);
if(!lex->value.string) { if(!t) {
/* this is not very nice, since TOKEN_INVALID is returned */ /* this is not very nice, since TOKEN_INVALID is returned */
goto out; goto out;
} }
lex->value.string.val = t;
/* the target */
t = lex->value.string;
/* + 1 to skip the " */ /* + 1 to skip the " */
p = strbuffer_value(&lex->saved_text) + 1; p = strbuffer_value(&lex->saved_text) + 1;
@ -381,17 +389,24 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
if(*p == '\\') { if(*p == '\\') {
p++; p++;
if(*p == 'u') { if(*p == 'u') {
char buffer[4]; size_t length;
int length;
int32_t value; int32_t value;
value = decode_unicode_escape(p); value = decode_unicode_escape(p);
if(value < 0) {
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
goto out;
}
p += 5; p += 5;
if(0xD800 <= value && value <= 0xDBFF) { if(0xD800 <= value && value <= 0xDBFF) {
/* surrogate pair */ /* surrogate pair */
if(*p == '\\' && *(p + 1) == 'u') { if(*p == '\\' && *(p + 1) == 'u') {
int32_t value2 = decode_unicode_escape(++p); int32_t value2 = decode_unicode_escape(++p);
if(value2 < 0) {
error_set(error, lex, "invalid Unicode escape '%.6s'", p - 1);
goto out;
}
p += 5; p += 5;
if(0xDC00 <= value2 && value2 <= 0xDFFF) { if(0xDC00 <= value2 && value2 <= 0xDFFF) {
@ -420,16 +435,9 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
error_set(error, lex, "invalid Unicode '\\u%04X'", value); error_set(error, lex, "invalid Unicode '\\u%04X'", value);
goto out; goto out;
} }
else if(value == 0)
{
error_set(error, lex, "\\u0000 is not allowed");
goto out;
}
if(utf8_encode(value, buffer, &length)) if(utf8_encode(value, t, &length))
assert(0); assert(0);
memcpy(t, buffer, length);
t += length; t += length;
} }
else { else {
@ -451,11 +459,12 @@ static void lex_scan_string(lex_t *lex, json_error_t *error)
*(t++) = *(p++); *(t++) = *(p++);
} }
*t = '\0'; *t = '\0';
lex->value.string.len = t - lex->value.string.val;
lex->token = TOKEN_STRING; lex->token = TOKEN_STRING;
return; return;
out: out:
jsonp_free(lex->value.string); lex_free_string(lex);
} }
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ #ifndef JANSSON_USING_CMAKE /* disabled if using cmake */
@ -571,10 +580,8 @@ static int lex_scan(lex_t *lex, json_error_t *error)
strbuffer_clear(&lex->saved_text); strbuffer_clear(&lex->saved_text);
if(lex->token == TOKEN_STRING) { if(lex->token == TOKEN_STRING)
jsonp_free(lex->value.string); lex_free_string(lex);
lex->value.string = NULL;
}
c = lex_get(lex, error); c = lex_get(lex, error);
while(c == ' ' || c == '\t' || c == '\n' || c == '\r') while(c == ' ' || c == '\t' || c == '\n' || c == '\r')
@ -635,13 +642,14 @@ out:
return lex->token; return lex->token;
} }
static char *lex_steal_string(lex_t *lex) static char *lex_steal_string(lex_t *lex, size_t *out_len)
{ {
char *result = NULL; char *result = NULL;
if(lex->token == TOKEN_STRING) if(lex->token == TOKEN_STRING) {
{ result = lex->value.string.val;
result = lex->value.string; *out_len = lex->value.string.len;
lex->value.string = NULL; lex->value.string.val = NULL;
lex->value.string.len = 0;
} }
return result; return result;
} }
@ -659,7 +667,7 @@ static int lex_init(lex_t *lex, get_func get, void *data)
static void lex_close(lex_t *lex) static void lex_close(lex_t *lex)
{ {
if(lex->token == TOKEN_STRING) if(lex->token == TOKEN_STRING)
jsonp_free(lex->value.string); lex_free_string(lex);
strbuffer_close(&lex->saved_text); strbuffer_close(&lex->saved_text);
} }
@ -680,6 +688,7 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
while(1) { while(1) {
char *key; char *key;
size_t len;
json_t *value; json_t *value;
if(lex->token != TOKEN_STRING) { if(lex->token != TOKEN_STRING) {
@ -687,9 +696,14 @@ static json_t *parse_object(lex_t *lex, size_t flags, json_error_t *error)
goto error; goto error;
} }
key = lex_steal_string(lex); key = lex_steal_string(lex, &len);
if(!key) if(!key)
return NULL; return NULL;
if (memchr(key, len, '\0')) {
jsonp_free(key);
error_set(error, lex, "nul char in object key not supported");
goto error;
}
if(flags & JSON_REJECT_DUPLICATES) { if(flags & JSON_REJECT_DUPLICATES) {
if(json_object_get(object, key)) { if(json_object_get(object, key)) {
@ -788,7 +802,11 @@ static json_t *parse_value(lex_t *lex, size_t flags, json_error_t *error)
switch(lex->token) { switch(lex->token) {
case TOKEN_STRING: { case TOKEN_STRING: {
json = json_string_nocheck(lex->value.string); json = jsonp_stringn_nocheck_own(lex->value.string.val, lex->value.string.len);
if(json) {
lex->value.string.val = NULL;
lex->value.string.len = 0;
}
break; break;
} }

View File

@ -12,6 +12,10 @@
#include "jansson.h" #include "jansson.h"
#include "jansson_private.h" #include "jansson_private.h"
/* C89 allows these to be macros */
#undef malloc
#undef free
/* memory function pointers */ /* memory function pointers */
static json_malloc_t do_malloc = malloc; static json_malloc_t do_malloc = malloc;
static json_free_t do_free = free; static json_free_t do_free = free;
@ -34,18 +38,19 @@ void jsonp_free(void *ptr)
char *jsonp_strdup(const char *str) char *jsonp_strdup(const char *str)
{ {
char *new_str; return jsonp_strndup(str, strlen(str));
size_t len; }
len = strlen(str); char *jsonp_strndup(const char *str, size_t len)
if(len == (size_t)-1) {
return NULL; char *new_str;
new_str = jsonp_malloc(len + 1); new_str = jsonp_malloc(len + 1);
if(!new_str) if(!new_str)
return NULL; return NULL;
memcpy(new_str, str, len + 1); memcpy(new_str, str, len);
new_str[len] = '\0';
return new_str; return new_str;
} }

View File

@ -125,19 +125,18 @@ static json_t *pack(scanner_t *s, va_list *ap);
/* ours will be set to 1 if jsonp_free() must be called for the result /* ours will be set to 1 if jsonp_free() must be called for the result
afterwards */ afterwards */
static char *read_string(scanner_t *s, va_list *ap, static char *read_string(scanner_t *s, va_list *ap,
const char *purpose, int *ours) const char *purpose, size_t *out_len, int *ours)
{ {
char t; char t;
strbuffer_t strbuff; strbuffer_t strbuff;
const char *str; const char *str;
size_t length; size_t length;
char *result;
next_token(s); next_token(s);
t = token(s); t = token(s);
prev_token(s); prev_token(s);
if(t != '#' && t != '+') { if(t != '#' && t != '%' && t != '+') {
/* Optimize the simple case */ /* Optimize the simple case */
str = va_arg(*ap, const char *); str = va_arg(*ap, const char *);
@ -146,11 +145,14 @@ static char *read_string(scanner_t *s, va_list *ap,
return NULL; return NULL;
} }
if(!utf8_check_string(str, -1)) { length = strlen(str);
if(!utf8_check_string(str, length)) {
set_error(s, "<args>", "Invalid UTF-8 %s", purpose); set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
return NULL; return NULL;
} }
*out_len = length;
*ours = 0; *ours = 0;
return (char *)str; return (char *)str;
} }
@ -170,6 +172,9 @@ static char *read_string(scanner_t *s, va_list *ap,
if(token(s) == '#') { if(token(s) == '#') {
length = va_arg(*ap, int); length = va_arg(*ap, int);
} }
else if(token(s) == '%') {
length = va_arg(*ap, size_t);
}
else { else {
prev_token(s); prev_token(s);
length = strlen(str); length = strlen(str);
@ -188,15 +193,15 @@ static char *read_string(scanner_t *s, va_list *ap,
} }
} }
result = strbuffer_steal_value(&strbuff); if(!utf8_check_string(strbuff.value, strbuff.length)) {
if(!utf8_check_string(result, -1)) {
set_error(s, "<args>", "Invalid UTF-8 %s", purpose); set_error(s, "<args>", "Invalid UTF-8 %s", purpose);
strbuffer_close(&strbuff);
return NULL; return NULL;
} }
*out_len = strbuff.length;
*ours = 1; *ours = 1;
return result; return strbuffer_steal_value(&strbuff);
} }
static json_t *pack_object(scanner_t *s, va_list *ap) static json_t *pack_object(scanner_t *s, va_list *ap)
@ -206,6 +211,7 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
while(token(s) != '}') { while(token(s) != '}') {
char *key; char *key;
size_t len;
int ours; int ours;
json_t *value; json_t *value;
@ -219,15 +225,19 @@ static json_t *pack_object(scanner_t *s, va_list *ap)
goto error; goto error;
} }
key = read_string(s, ap, "object key", &ours); key = read_string(s, ap, "object key", &len, &ours);
if(!key) if(!key)
goto error; goto error;
next_token(s); next_token(s);
value = pack(s, ap); value = pack(s, ap);
if(!value) if(!value) {
if(ours)
jsonp_free(key);
goto error; goto error;
}
if(json_object_set_new_nocheck(object, key, value)) { if(json_object_set_new_nocheck(object, key, value)) {
if(ours) if(ours)
@ -290,20 +300,20 @@ static json_t *pack(scanner_t *s, va_list *ap)
case '[': case '[':
return pack_array(s, ap); return pack_array(s, ap);
case 's': { /* string */ case 's': /* string */
{
char *str; char *str;
size_t len;
int ours; int ours;
json_t *result;
str = read_string(s, ap, "string", &ours); str = read_string(s, ap, "string", &len, &ours);
if(!str) if(!str)
return NULL; return NULL;
result = json_string_nocheck(str); if (ours)
if(ours) return jsonp_stringn_nocheck_own(str, len);
jsonp_free(str); else
return json_stringn_nocheck(str, len);
return result;
} }
case 'n': /* null */ case 'n': /* null */
@ -523,16 +533,32 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap)
} }
if(!(s->flags & JSON_VALIDATE_ONLY)) { if(!(s->flags & JSON_VALIDATE_ONLY)) {
const char **target; const char **str_target;
size_t *len_target = NULL;
target = va_arg(*ap, const char **); str_target = va_arg(*ap, const char **);
if(!target) { if(!str_target) {
set_error(s, "<args>", "NULL string argument"); set_error(s, "<args>", "NULL string argument");
return -1; return -1;
} }
if(root) next_token(s);
*target = json_string_value(root);
if(token(s) == '%') {
len_target = va_arg(*ap, size_t *);
if(!len_target) {
set_error(s, "<args>", "NULL string length argument");
return -1;
}
}
else
prev_token(s);
if(root) {
*str_target = json_string_value(root);
if(len_target)
*len_target = json_string_length(root);
}
} }
return 0; return 0;

View File

@ -8,7 +8,7 @@
#include <string.h> #include <string.h>
#include "utf.h" #include "utf.h"
int utf8_encode(int32_t codepoint, char *buffer, int *size) int utf8_encode(int32_t codepoint, char *buffer, size_t *size)
{ {
if(codepoint < 0) if(codepoint < 0)
return -1; return -1;
@ -44,7 +44,7 @@ int utf8_encode(int32_t codepoint, char *buffer, int *size)
return 0; return 0;
} }
int utf8_check_first(char byte) size_t utf8_check_first(char byte)
{ {
unsigned char u = (unsigned char)byte; unsigned char u = (unsigned char)byte;
@ -80,9 +80,9 @@ int utf8_check_first(char byte)
} }
} }
int utf8_check_full(const char *buffer, int size, int32_t *codepoint) size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint)
{ {
int i; size_t i;
int32_t value = 0; int32_t value = 0;
unsigned char u = (unsigned char)buffer[0]; unsigned char u = (unsigned char)buffer[0];
@ -136,12 +136,12 @@ int utf8_check_full(const char *buffer, int size, int32_t *codepoint)
return 1; return 1;
} }
const char *utf8_iterate(const char *buffer, int32_t *codepoint) const char *utf8_iterate(const char *buffer, size_t bufsize, int32_t *codepoint)
{ {
int count; size_t count;
int32_t value; int32_t value;
if(!*buffer) if(!bufsize)
return buffer; return buffer;
count = utf8_check_first(buffer[0]); count = utf8_check_first(buffer[0]);
@ -152,7 +152,7 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
value = (unsigned char)buffer[0]; value = (unsigned char)buffer[0];
else else
{ {
if(!utf8_check_full(buffer, count, &value)) if(count > bufsize || !utf8_check_full(buffer, count, &value))
return NULL; return NULL;
} }
@ -162,16 +162,13 @@ const char *utf8_iterate(const char *buffer, int32_t *codepoint)
return buffer + count; return buffer + count;
} }
int utf8_check_string(const char *string, int length) int utf8_check_string(const char *string, size_t length)
{ {
int i; size_t i;
if(length == -1)
length = strlen(string);
for(i = 0; i < length; i++) for(i = 0; i < length; i++)
{ {
int count = utf8_check_first(string[i]); size_t count = utf8_check_first(string[i]);
if(count == 0) if(count == 0)
return 0; return 0;
else if(count > 1) else if(count > 1)

View File

@ -28,12 +28,12 @@ typedef int int32_t;
#endif /* HAVE_CONFIG_H */ #endif /* HAVE_CONFIG_H */
int utf8_encode(int codepoint, char *buffer, int *size); int utf8_encode(int codepoint, char *buffer, size_t *size);
int utf8_check_first(char byte); size_t utf8_check_first(char byte);
int utf8_check_full(const char *buffer, int size, int32_t *codepoint); size_t utf8_check_full(const char *buffer, size_t size, int32_t *codepoint);
const char *utf8_iterate(const char *buffer, int32_t *codepoint); const char *utf8_iterate(const char *buffer, size_t size, int32_t *codepoint);
int utf8_check_string(const char *string, int length); int utf8_check_string(const char *string, size_t length);
#endif #endif

View File

@ -108,7 +108,7 @@ int json_object_set_new_nocheck(json_t *json, const char *key, json_t *value)
int json_object_set_new(json_t *json, const char *key, json_t *value) int json_object_set_new(json_t *json, const char *key, json_t *value)
{ {
if(!key || !utf8_check_string(key, -1)) if(!key || !utf8_check_string(key, strlen(key)))
{ {
json_decref(value); json_decref(value);
return -1; return -1;
@ -620,33 +620,68 @@ static json_t *json_array_deep_copy(const json_t *array)
/*** string ***/ /*** string ***/
json_t *json_string_nocheck(const char *value) static json_t *string_create(const char *value, size_t len, int own)
{ {
char *v;
json_string_t *string; json_string_t *string;
if(!value) if(!value)
return NULL; return NULL;
string = jsonp_malloc(sizeof(json_string_t)); if(own)
if(!string) v = (char *)value;
return NULL; else {
json_init(&string->json, JSON_STRING); v = jsonp_strndup(value, len);
if(!v)
string->value = jsonp_strdup(value);
if(!string->value) {
jsonp_free(string);
return NULL; return NULL;
} }
string = jsonp_malloc(sizeof(json_string_t));
if(!string) {
if(!own)
jsonp_free(v);
return NULL;
}
json_init(&string->json, JSON_STRING);
string->value = v;
string->length = len;
return &string->json; return &string->json;
} }
json_t *json_string(const char *value) json_t *json_string_nocheck(const char *value)
{ {
if(!value || !utf8_check_string(value, -1)) if(!value)
return NULL; return NULL;
return json_string_nocheck(value); return string_create(value, strlen(value), 0);
}
json_t *json_stringn_nocheck(const char *value, size_t len)
{
return string_create(value, len, 0);
}
/* this is private; "steal" is not a public API concept */
json_t *jsonp_stringn_nocheck_own(const char *value, size_t len)
{
return string_create(value, len, 1);
}
json_t *json_string(const char *value)
{
if(!value)
return NULL;
return json_stringn(value, strlen(value));
}
json_t *json_stringn(const char *value, size_t len)
{
if(!value || !utf8_check_string(value, len))
return NULL;
return json_stringn_nocheck(value, len);
} }
const char *json_string_value(const json_t *json) const char *json_string_value(const json_t *json)
@ -657,7 +692,23 @@ const char *json_string_value(const json_t *json)
return json_to_string(json)->value; return json_to_string(json)->value;
} }
size_t json_string_length(const json_t *json)
{
if(!json_is_string(json))
return 0;
return json_to_string(json)->length;
}
int json_string_set_nocheck(json_t *json, const char *value) int json_string_set_nocheck(json_t *json, const char *value)
{
if(!value)
return -1;
return json_string_setn_nocheck(json, value, strlen(value));
}
int json_string_setn_nocheck(json_t *json, const char *value, size_t len)
{ {
char *dup; char *dup;
json_string_t *string; json_string_t *string;
@ -665,7 +716,7 @@ int json_string_set_nocheck(json_t *json, const char *value)
if(!json_is_string(json) || !value) if(!json_is_string(json) || !value)
return -1; return -1;
dup = jsonp_strdup(value); dup = jsonp_strndup(value, len);
if(!dup) if(!dup)
return -1; return -1;
@ -678,10 +729,18 @@ int json_string_set_nocheck(json_t *json, const char *value)
int json_string_set(json_t *json, const char *value) int json_string_set(json_t *json, const char *value)
{ {
if(!value || !utf8_check_string(value, -1)) if(!value)
return -1; return -1;
return json_string_set_nocheck(json, value); return json_string_setn(json, value, strlen(value));
}
int json_string_setn(json_t *json, const char *value, size_t len)
{
if(!value || !utf8_check_string(value, len))
return -1;
return json_string_setn_nocheck(json, value, len);
} }
static void json_delete_string(json_string_t *string) static void json_delete_string(json_string_t *string)
@ -692,12 +751,25 @@ static void json_delete_string(json_string_t *string)
static int json_string_equal(json_t *string1, json_t *string2) static int json_string_equal(json_t *string1, json_t *string2)
{ {
return strcmp(json_string_value(string1), json_string_value(string2)) == 0; json_string_t *s1, *s2;
if(!json_is_string(string1) || !json_is_string(string2))
return 0;
s1 = json_to_string(string1);
s2 = json_to_string(string2);
return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length);
} }
static json_t *json_string_copy(const json_t *string) static json_t *json_string_copy(const json_t *string)
{ {
return json_string_nocheck(json_string_value(string)); json_string_t *s;
if(!json_is_string(string))
return NULL;
s = json_to_string(string);
return json_stringn_nocheck(s->value, s->length);
} }

View File

@ -83,7 +83,7 @@ static void run_tests()
fail("json_pack string refcount failed"); fail("json_pack string refcount failed");
json_decref(value); json_decref(value);
/* string and length */ /* string and length (int) */
value = json_pack("s#", "test asdf", 4); value = json_pack("s#", "test asdf", 4);
if(!json_is_string(value) || strcmp("test", json_string_value(value))) if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length failed"); fail("json_pack string and length failed");
@ -91,14 +91,30 @@ static void run_tests()
fail("json_pack string and length refcount failed"); fail("json_pack string and length refcount failed");
json_decref(value); json_decref(value);
/* string and length, non-NUL terminated string */ /* string and length (size_t) */
value = json_pack("s#", buffer, 4); value = json_pack("s%", "test asdf", (size_t)4);
if(!json_is_string(value) || strcmp("test", json_string_value(value))) if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length failed"); fail("json_pack string and length failed");
if(value->refcount != (size_t)1) if(value->refcount != (size_t)1)
fail("json_pack string and length refcount failed"); fail("json_pack string and length refcount failed");
json_decref(value); json_decref(value);
/* string and length (int), non-NUL terminated string */
value = json_pack("s#", buffer, 4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length (int) failed");
if(value->refcount != (size_t)1)
fail("json_pack string and length (int) refcount failed");
json_decref(value);
/* string and length (size_t), non-NUL terminated string */
value = json_pack("s%", buffer, (size_t)4);
if(!json_is_string(value) || strcmp("test", json_string_value(value)))
fail("json_pack string and length (size_t) failed");
if(value->refcount != (size_t)1)
fail("json_pack string and length (size_t) refcount failed");
json_decref(value);
/* string concatenation */ /* string concatenation */
value = json_pack("s++", "te", "st", "ing"); value = json_pack("s++", "te", "st", "ing");
if(!json_is_string(value) || strcmp("testing", json_string_value(value))) if(!json_is_string(value) || strcmp("testing", json_string_value(value)))
@ -107,12 +123,20 @@ static void run_tests()
fail("json_pack string concatenation refcount failed"); fail("json_pack string concatenation refcount failed");
json_decref(value); json_decref(value);
/* string concatenation and length */ /* string concatenation and length (int) */
value = json_pack("s#+#+", "test", 1, "test", 2, "test"); value = json_pack("s#+#+", "test", 1, "test", 2, "test");
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value))) if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
fail("json_pack string concatenation and length failed"); fail("json_pack string concatenation and length (int) failed");
if(value->refcount != (size_t)1) if(value->refcount != (size_t)1)
fail("json_pack string concatenation and length refcount failed"); fail("json_pack string concatenation and length (int) refcount failed");
json_decref(value);
/* string concatenation and length (size_t) */
value = json_pack("s%+%+", "test", (size_t)1, "test", (size_t)2, "test");
if(!json_is_string(value) || strcmp("ttetest", json_string_value(value)))
fail("json_pack string concatenation and length (size_t) failed");
if(value->refcount != (size_t)1)
fail("json_pack string concatenation and length (size_t) refcount failed");
json_decref(value); json_decref(value);
/* empty object */ /* empty object */

View File

@ -17,6 +17,7 @@ static void run_tests()
int i1, i2, i3; int i1, i2, i3;
json_int_t I1; json_int_t I1;
int rv; int rv;
size_t z;
double f; double f;
char *s; char *s;
@ -81,6 +82,13 @@ static void run_tests()
fail("json_unpack string failed"); fail("json_unpack string failed");
json_decref(j); json_decref(j);
/* string with length (size_t) */
j = json_string("foo");
rv = json_unpack(j, "s%", &s, &z);
if(rv || strcmp(s, "foo") || z != 3)
fail("json_unpack string with length (size_t) failed");
json_decref(j);
/* empty object */ /* empty object */
j = json_object(); j = json_object();
if(json_unpack(j, "{}")) if(json_unpack(j, "{}"))

View File

@ -1,2 +0,0 @@
1 33 33
\u0000 is not allowed

View File

@ -1 +0,0 @@
["\u0000 (null byte not allowed)"]

View File

@ -0,0 +1 @@
["null char \u0000 in string"]

View File

@ -0,0 +1 @@
["null char \u0000 in string"]