| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | #include <stdint.h>
 | 
					
						
							|  |  |  | #include <stddef.h>
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							| 
									
										
										
										
											2012-09-02 21:45:27 +01:00
										 |  |  | #include <stdio.h>
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | #include <string.h>
 | 
					
						
							| 
									
										
										
										
											2012-09-10 16:01:56 +01:00
										 |  |  | #include <stdarg.h>
 | 
					
						
							| 
									
										
										
										
											2013-12-10 09:17:30 +00:00
										 |  |  | #include <limits.h>
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  | #include "jv_alloc.h"
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | #include "jv.h"
 | 
					
						
							| 
									
										
										
										
											2013-05-15 00:37:38 +01:00
										 |  |  | #include "jv_unicode.h"
 | 
					
						
							| 
									
										
										
										
											2014-07-09 00:55:20 -04:00
										 |  |  | #include "util.h"
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Internal refcounting helpers | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-23 14:23:07 +01:00
										 |  |  | typedef struct jv_refcnt { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   int count; | 
					
						
							| 
									
										
										
										
											2013-06-23 14:23:07 +01:00
										 |  |  | } jv_refcnt; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static const jv_refcnt JV_REFCNT_INIT = {1}; | 
					
						
							| 
									
										
										
										
											2013-06-23 14:23:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static void jvp_refcnt_inc(jv_refcnt* c) { | 
					
						
							|  |  |  |   c->count++; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int jvp_refcnt_dec(jv_refcnt* c) { | 
					
						
							|  |  |  |   c->count--; | 
					
						
							|  |  |  |   return c->count == 0; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int jvp_refcnt_unshared(jv_refcnt* c) { | 
					
						
							|  |  |  |   assert(c->count > 0); | 
					
						
							|  |  |  |   return c->count == 1; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Simple values (true, false, null) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | #define KIND_MASK 0xf
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | jv_kind jv_get_kind(jv x) { | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   return x.kind_flags & KIND_MASK; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-10 16:07:03 +01:00
										 |  |  | const char* jv_kind_name(jv_kind k) { | 
					
						
							|  |  |  |   switch (k) { | 
					
						
							|  |  |  |   case JV_KIND_INVALID: return "<invalid>"; | 
					
						
							|  |  |  |   case JV_KIND_NULL:    return "null"; | 
					
						
							|  |  |  |   case JV_KIND_FALSE:   return "boolean"; | 
					
						
							|  |  |  |   case JV_KIND_TRUE:    return "boolean"; | 
					
						
							|  |  |  |   case JV_KIND_NUMBER:  return "number"; | 
					
						
							|  |  |  |   case JV_KIND_STRING:  return "string"; | 
					
						
							|  |  |  |   case JV_KIND_ARRAY:   return "array"; | 
					
						
							|  |  |  |   case JV_KIND_OBJECT:  return "object"; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-09-11 10:37:44 +01:00
										 |  |  |   assert(0 && "invalid kind"); | 
					
						
							| 
									
										
										
										
											2012-09-10 16:07:03 +01:00
										 |  |  |   return "<unknown>"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static const jv JV_NULL = {JV_KIND_NULL, 0, 0, 0, {0}}; | 
					
						
							| 
									
										
										
										
											2013-12-24 12:24:14 -06:00
										 |  |  | static const jv JV_INVALID = {JV_KIND_INVALID, 0, 0, 0, {0}}; | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static const jv JV_FALSE = {JV_KIND_FALSE, 0, 0, 0, {0}}; | 
					
						
							|  |  |  | static const jv JV_TRUE = {JV_KIND_TRUE, 0, 0, 0, {0}}; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | jv jv_true() { | 
					
						
							|  |  |  |   return JV_TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_false() { | 
					
						
							|  |  |  |   return JV_FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_null() { | 
					
						
							|  |  |  |   return JV_NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-09 18:09:05 +01:00
										 |  |  | jv jv_bool(int x) { | 
					
						
							|  |  |  |   return x ? JV_TRUE : JV_FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-10 15:04:19 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Invalid objects, with optional error messages | 
					
						
							|  |  |  |  */  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |   jv_refcnt refcnt; | 
					
						
							|  |  |  |   jv errmsg; | 
					
						
							|  |  |  | } jvp_invalid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_invalid_with_msg(jv err) { | 
					
						
							| 
									
										
										
										
											2013-12-24 12:24:14 -06:00
										 |  |  |   if (jv_get_kind(err) == JV_KIND_NULL) | 
					
						
							|  |  |  |     return JV_INVALID; | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |   jvp_invalid* i = jv_mem_alloc(sizeof(jvp_invalid)); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   i->refcnt = JV_REFCNT_INIT; | 
					
						
							| 
									
										
										
										
											2012-09-10 15:04:19 +01:00
										 |  |  |   i->errmsg = err; | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv x = {JV_KIND_INVALID, 0, 0, 0, {&i->refcnt}}; | 
					
						
							| 
									
										
										
										
											2012-09-10 15:04:19 +01:00
										 |  |  |   return x; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_invalid() { | 
					
						
							| 
									
										
										
										
											2013-12-24 12:24:14 -06:00
										 |  |  |   return JV_INVALID; | 
					
						
							| 
									
										
										
										
											2012-09-10 15:04:19 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-10 16:01:56 +01:00
										 |  |  | jv jv_invalid_get_msg(jv inv) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   assert(jv_get_kind(inv) == JV_KIND_INVALID); | 
					
						
							| 
									
										
										
										
											2013-12-24 12:24:14 -06:00
										 |  |  |   jv x; | 
					
						
							|  |  |  |   if (inv.u.ptr == 0) | 
					
						
							|  |  |  |     x = jv_null(); | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     x = jv_copy(((jvp_invalid*)inv.u.ptr)->errmsg); | 
					
						
							| 
									
										
										
										
											2012-09-10 15:04:19 +01:00
										 |  |  |   jv_free(inv); | 
					
						
							|  |  |  |   return x; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-11 14:52:10 +01:00
										 |  |  | int jv_invalid_has_msg(jv inv) { | 
					
						
							|  |  |  |   jv msg = jv_invalid_get_msg(inv); | 
					
						
							|  |  |  |   int r = jv_get_kind(msg) != JV_KIND_NULL; | 
					
						
							|  |  |  |   jv_free(msg); | 
					
						
							|  |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static void jvp_invalid_free(jv x) { | 
					
						
							|  |  |  |   assert(jv_get_kind(x) == JV_KIND_INVALID); | 
					
						
							| 
									
										
										
										
											2013-12-24 12:24:14 -06:00
										 |  |  |   if (x.u.ptr != 0 && jvp_refcnt_dec(x.u.ptr)) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     jv_free(((jvp_invalid*)x.u.ptr)->errmsg); | 
					
						
							|  |  |  |     jv_mem_free(x.u.ptr); | 
					
						
							| 
									
										
										
										
											2012-09-10 15:04:19 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Numbers | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_number(double x) { | 
					
						
							|  |  |  |   jv j; | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   j.kind_flags = JV_KIND_NUMBER; | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   j.size = 0; | 
					
						
							|  |  |  |   j.u.number = x; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return j; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | double jv_number_value(jv j) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_NUMBER); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   return j.u.number; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-10 09:17:30 +00:00
										 |  |  | int jv_is_integer(jv j){ | 
					
						
							|  |  |  |   if(jv_get_kind(j) != JV_KIND_NUMBER){ | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   double x = jv_number_value(j); | 
					
						
							|  |  |  |   if(x != x || x > INT_MAX || x < INT_MIN){ | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return x == (int)x; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Arrays (internal helpers) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define ARRAY_SIZE_ROUND_UP(n) (((n)*3)/2)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int imax(int a, int b) { | 
					
						
							|  |  |  |   if (a>b) return a; | 
					
						
							|  |  |  |   else return b; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //FIXME signed vs unsigned
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |   jv_refcnt refcnt; | 
					
						
							|  |  |  |   int length, alloc_length; | 
					
						
							|  |  |  |   jv elements[]; | 
					
						
							|  |  |  | } jvp_array; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jvp_array* jvp_array_ptr(jv a) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_ARRAY); | 
					
						
							|  |  |  |   return (jvp_array*)a.u.ptr; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static jvp_array* jvp_array_alloc(unsigned size) { | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |   jvp_array* a = jv_mem_alloc(sizeof(jvp_array) + sizeof(jv) * size); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   a->refcnt.count = 1; | 
					
						
							|  |  |  |   a->length = 0; | 
					
						
							|  |  |  |   a->alloc_length = size; | 
					
						
							|  |  |  |   return a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv jvp_array_new(unsigned size) { | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv r = {JV_KIND_ARRAY, 0, 0, 0, {&jvp_array_alloc(size)->refcnt}}; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static void jvp_array_free(jv a) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_ARRAY); | 
					
						
							|  |  |  |   if (jvp_refcnt_dec(a.u.ptr)) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     jvp_array* array = jvp_array_ptr(a); | 
					
						
							|  |  |  |     for (int i=0; i<array->length; i++) { | 
					
						
							|  |  |  |       jv_free(array->elements[i]); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |     jv_mem_free(array); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int jvp_array_length(jv a) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_ARRAY); | 
					
						
							|  |  |  |   return a.size; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int jvp_array_offset(jv a) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_ARRAY); | 
					
						
							|  |  |  |   return a.offset; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static jv* jvp_array_read(jv a, int i) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_ARRAY); | 
					
						
							| 
									
										
										
										
											2012-09-03 16:50:38 +01:00
										 |  |  |   if (i >= 0 && i < jvp_array_length(a)) { | 
					
						
							|  |  |  |     jvp_array* array = jvp_array_ptr(a); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     assert(i + jvp_array_offset(a) < array->length); | 
					
						
							|  |  |  |     return &array->elements[i + jvp_array_offset(a)]; | 
					
						
							| 
									
										
										
										
											2012-09-03 16:50:38 +01:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv* jvp_array_write(jv* a, int i) { | 
					
						
							| 
									
										
										
										
											2014-08-09 19:04:34 -05:00
										 |  |  |   assert(i >= 0); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   jvp_array* array = jvp_array_ptr(*a); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   int pos = i + jvp_array_offset(*a); | 
					
						
							|  |  |  |   if (pos < array->alloc_length && jvp_refcnt_unshared(a->u.ptr)) { | 
					
						
							|  |  |  |     // use existing array space
 | 
					
						
							|  |  |  |     for (int j = array->length; j <= pos; j++) { | 
					
						
							|  |  |  |       array->elements[j] = JV_NULL; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     array->length = imax(pos + 1, array->length); | 
					
						
							|  |  |  |     a->size = imax(i + 1, a->size); | 
					
						
							|  |  |  |     return &array->elements[pos]; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     // allocate a new array
 | 
					
						
							|  |  |  |     int new_length = imax(i + 1, jvp_array_length(*a)); | 
					
						
							|  |  |  |     jvp_array* new_array = jvp_array_alloc(ARRAY_SIZE_ROUND_UP(new_length)); | 
					
						
							|  |  |  |     int j; | 
					
						
							|  |  |  |     for (j = 0; j < jvp_array_length(*a); j++) { | 
					
						
							|  |  |  |       new_array->elements[j] =  | 
					
						
							|  |  |  |         jv_copy(array->elements[j + jvp_array_offset(*a)]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     for (; j < new_length; j++) { | 
					
						
							|  |  |  |       new_array->elements[j] = JV_NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     new_array->length = new_length; | 
					
						
							|  |  |  |     jvp_array_free(*a); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     jv new_jv = {JV_KIND_ARRAY, 0, 0, new_length, {&new_array->refcnt}}; | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     *a = new_jv; | 
					
						
							|  |  |  |     return &new_array->elements[i]; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int jvp_array_equal(jv a, jv b) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   if (jvp_array_length(a) != jvp_array_length(b))  | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  |   if (jvp_array_ptr(a) == jvp_array_ptr(b) && | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |       jvp_array_offset(a) == jvp_array_offset(b))  | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     return 1; | 
					
						
							|  |  |  |   for (int i=0; i<jvp_array_length(a); i++) { | 
					
						
							|  |  |  |     if (!jv_equal(jv_copy(*jvp_array_read(a, i)),  | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |                   jv_copy(*jvp_array_read(b, i)))) | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |       return 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-04 21:09:00 -07:00
										 |  |  | static void jvp_clamp_slice_params(int len, int *pstart, int *pend) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (*pstart < 0) *pstart = len + *pstart; | 
					
						
							|  |  |  |   if (*pend < 0) *pend = len + *pend; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (*pstart < 0) *pstart = 0; | 
					
						
							|  |  |  |   if (*pstart > len) *pstart = len; | 
					
						
							|  |  |  |   if (*pend > len) *pend = len; | 
					
						
							|  |  |  |   if (*pend < *pstart) *pend = *pstart; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv jvp_array_slice(jv a, int start, int end) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_ARRAY); | 
					
						
							| 
									
										
										
										
											2015-03-04 21:09:00 -07:00
										 |  |  |   int len = jvp_array_length(a); | 
					
						
							|  |  |  |   jvp_clamp_slice_params(len, &start, &end); | 
					
						
							|  |  |  |   assert(0 <= start && start <= end && end <= len); | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   // FIXME: maybe slice should reallocate if the slice is small enough
 | 
					
						
							| 
									
										
										
										
											2015-03-04 21:09:00 -07:00
										 |  |  |   if (start == end) { | 
					
						
							|  |  |  |     jv_free(a); | 
					
						
							|  |  |  |     return jv_array(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   // FIXME FIXME FIXME large offsets
 | 
					
						
							|  |  |  |   a.offset += start; | 
					
						
							|  |  |  |   a.size = end - start; | 
					
						
							|  |  |  |   return a; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Arrays (public interface) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_array_sized(int n) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   return jvp_array_new(n); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_array() { | 
					
						
							|  |  |  |   return jv_array_sized(16); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int jv_array_length(jv j) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_ARRAY); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   int len = jvp_array_length(j); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jv_free(j); | 
					
						
							|  |  |  |   return len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_array_get(jv j, int idx) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_ARRAY); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   jv* slot = jvp_array_read(j, idx); | 
					
						
							| 
									
										
										
										
											2012-09-03 16:50:38 +01:00
										 |  |  |   jv val; | 
					
						
							|  |  |  |   if (slot) { | 
					
						
							|  |  |  |     val = jv_copy(*slot); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     val = jv_invalid(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jv_free(j); | 
					
						
							|  |  |  |   return val; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_array_set(jv j, int idx, jv val) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_ARRAY); | 
					
						
							| 
									
										
										
										
											2014-08-09 19:04:34 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (idx < 0) | 
					
						
							|  |  |  |     idx = jvp_array_length(j) + idx; | 
					
						
							|  |  |  |   if (idx < 0) { | 
					
						
							|  |  |  |     jv_free(j); | 
					
						
							|  |  |  |     jv_free(val); | 
					
						
							|  |  |  |     return jv_invalid_with_msg(jv_string("Out of bounds negative array index")); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   // copy/free of val,j coalesced
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   jv* slot = jvp_array_write(&j, idx); | 
					
						
							| 
									
										
										
										
											2012-09-03 17:18:10 +01:00
										 |  |  |   jv_free(*slot); | 
					
						
							|  |  |  |   *slot = val; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return j; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_array_append(jv j, jv val) { | 
					
						
							|  |  |  |   // copy/free of val,j coalesced
 | 
					
						
							|  |  |  |   return jv_array_set(j, jv_array_length(jv_copy(j)), val); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_array_concat(jv a, jv b) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_ARRAY); | 
					
						
							|  |  |  |   assert(jv_get_kind(b) == JV_KIND_ARRAY); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   // FIXME: could be faster
 | 
					
						
							| 
									
										
										
										
											2012-12-31 23:27:00 +00:00
										 |  |  |   jv_array_foreach(b, i, elem) { | 
					
						
							|  |  |  |     a = jv_array_append(a, elem); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } | 
					
						
							|  |  |  |   jv_free(b); | 
					
						
							|  |  |  |   return a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_array_slice(jv a, int start, int end) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_ARRAY); | 
					
						
							|  |  |  |   // copy/free of a coalesced
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   return jvp_array_slice(a, start, end); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-24 18:29:33 -07:00
										 |  |  | int jv_array_contains(jv a, jv b) { | 
					
						
							|  |  |  |   int r = 1; | 
					
						
							| 
									
										
										
										
											2012-12-31 23:27:00 +00:00
										 |  |  |   jv_array_foreach(b, bi, belem) { | 
					
						
							| 
									
										
										
										
											2012-10-24 18:29:33 -07:00
										 |  |  |     int ri = 0; | 
					
						
							| 
									
										
										
										
											2012-12-31 23:27:00 +00:00
										 |  |  |     jv_array_foreach(a, ai, aelem) { | 
					
						
							|  |  |  |       if (jv_contains(aelem, jv_copy(belem))) { | 
					
						
							| 
									
										
										
										
											2012-10-24 18:29:33 -07:00
										 |  |  |         ri = 1; | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-12-31 23:27:00 +00:00
										 |  |  |     jv_free(belem); | 
					
						
							| 
									
										
										
										
											2012-10-24 18:29:33 -07:00
										 |  |  |     if (!ri) { | 
					
						
							|  |  |  |       r = 0; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv_free(a); | 
					
						
							|  |  |  |   jv_free(b); | 
					
						
							|  |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-08 02:01:44 -05:00
										 |  |  | jv jv_array_indexes(jv a, jv b) { | 
					
						
							|  |  |  |   jv res = jv_array(); | 
					
						
							|  |  |  |   int idx = -1; | 
					
						
							|  |  |  |   jv_array_foreach(a, ai, aelem) { | 
					
						
							|  |  |  |     jv_array_foreach(b, bi, belem) { | 
					
						
							|  |  |  |       // quieten compiler warnings about aelem not being used... by
 | 
					
						
							|  |  |  |       // using it
 | 
					
						
							|  |  |  |       if ((bi == 0 && !jv_equal(jv_copy(aelem), jv_copy(belem))) || | 
					
						
							|  |  |  |           (bi > 0 && !jv_equal(jv_array_get(jv_copy(a), ai + bi), jv_copy(belem)))) | 
					
						
							|  |  |  |         idx = -1; | 
					
						
							|  |  |  |       else if (bi == 0 && idx == -1) | 
					
						
							|  |  |  |         idx = ai; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (idx > -1) | 
					
						
							|  |  |  |       res = jv_array_append(res, jv_number(idx)); | 
					
						
							|  |  |  |     idx = -1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv_free(a); | 
					
						
							|  |  |  |   jv_free(b); | 
					
						
							|  |  |  |   return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Strings (internal helpers) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |   jv_refcnt refcnt; | 
					
						
							|  |  |  |   uint32_t hash; | 
					
						
							|  |  |  |   // high 31 bits are length, low bit is a flag 
 | 
					
						
							|  |  |  |   // indicating whether hash has been computed.
 | 
					
						
							|  |  |  |   uint32_t length_hashed; | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |   uint32_t alloc_length; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   char data[]; | 
					
						
							|  |  |  | } jvp_string; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jvp_string* jvp_string_ptr(jv a) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_STRING); | 
					
						
							|  |  |  |   return (jvp_string*)a.u.ptr; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static jvp_string* jvp_string_alloc(uint32_t size) { | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |   jvp_string* s = jv_mem_alloc(sizeof(jvp_string) + size + 1); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   s->refcnt.count = 1; | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |   s->alloc_length = size; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return s; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-22 13:34:24 +01:00
										 |  |  | /* Copy a UTF8 string, replacing all badly encoded points with U+FFFD */ | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) { | 
					
						
							| 
									
										
										
										
											2013-06-22 13:34:24 +01:00
										 |  |  |   const char* end = data + length; | 
					
						
							|  |  |  |   const char* i = data; | 
					
						
							|  |  |  |   const char* cstart; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD
 | 
					
						
							|  |  |  |   jvp_string* s = jvp_string_alloc(maxlength); | 
					
						
							|  |  |  |   char* out = s->data; | 
					
						
							|  |  |  |   int c = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while ((i = jvp_utf8_next((cstart = i), end, &c))) { | 
					
						
							|  |  |  |     if (c == -1) { | 
					
						
							|  |  |  |       c = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     out += jvp_utf8_encode(c, out); | 
					
						
							|  |  |  |     assert(out < s->data + maxlength); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   length = out - s->data; | 
					
						
							|  |  |  |   s->data[length] = 0; | 
					
						
							|  |  |  |   s->length_hashed = length << 1; | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}}; | 
					
						
							| 
									
										
										
										
											2013-06-22 13:34:24 +01:00
										 |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Assumes valid UTF8 */ | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv jvp_string_new(const char* data, uint32_t length) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jvp_string* s = jvp_string_alloc(length); | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |   s->length_hashed = length << 1; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   memcpy(s->data, data, length); | 
					
						
							|  |  |  |   s->data[length] = 0; | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}}; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv jvp_string_empty_new(uint32_t length) { | 
					
						
							| 
									
										
										
										
											2013-11-28 14:48:46 -06:00
										 |  |  |   jvp_string* s = jvp_string_alloc(length); | 
					
						
							|  |  |  |   s->length_hashed = 0; | 
					
						
							|  |  |  |   memset(s->data, 0, length); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}}; | 
					
						
							| 
									
										
										
										
											2013-11-28 14:48:46 -06:00
										 |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static void jvp_string_free(jv js) { | 
					
						
							|  |  |  |   jvp_string* s = jvp_string_ptr(js); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   if (jvp_refcnt_dec(&s->refcnt)) { | 
					
						
							|  |  |  |     jv_mem_free(s); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint32_t jvp_string_length(jvp_string* s) { | 
					
						
							|  |  |  |   return s->length_hashed >> 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  | static uint32_t jvp_string_remaining_space(jvp_string* s) { | 
					
						
							| 
									
										
										
										
											2012-09-17 22:04:32 +01:00
										 |  |  |   assert(s->alloc_length >= jvp_string_length(s)); | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |   uint32_t r = s->alloc_length - jvp_string_length(s); | 
					
						
							| 
									
										
										
										
											2012-09-10 16:16:39 +01:00
										 |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv jvp_string_append(jv string, const char* data, uint32_t len) { | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |   jvp_string* s = jvp_string_ptr(string); | 
					
						
							|  |  |  |   uint32_t currlen = jvp_string_length(s); | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   if (jvp_refcnt_unshared(string.u.ptr) && | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |       jvp_string_remaining_space(s) >= len) { | 
					
						
							|  |  |  |     // the next string fits at the end of a
 | 
					
						
							|  |  |  |     memcpy(s->data + currlen, data, len); | 
					
						
							|  |  |  |     s->data[currlen + len] = 0; | 
					
						
							|  |  |  |     s->length_hashed = (currlen + len) << 1; | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     return string; | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     // allocate a bigger buffer and copy
 | 
					
						
							|  |  |  |     uint32_t allocsz = (currlen + len) * 2; | 
					
						
							|  |  |  |     if (allocsz < 32) allocsz = 32; | 
					
						
							|  |  |  |     jvp_string* news = jvp_string_alloc(allocsz); | 
					
						
							|  |  |  |     news->length_hashed = (currlen + len) << 1; | 
					
						
							|  |  |  |     memcpy(news->data, s->data, currlen); | 
					
						
							|  |  |  |     memcpy(news->data + currlen, data, len); | 
					
						
							|  |  |  |     news->data[currlen + len] = 0; | 
					
						
							|  |  |  |     jvp_string_free(string); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     jv r = {JV_KIND_STRING, 0, 0, 0, {&news->refcnt}}; | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     return r; | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | static const uint32_t HASH_SEED = 0x432A9843; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint32_t rotl32 (uint32_t x, int8_t r){ | 
					
						
							|  |  |  |   return (x << r) | (x >> (32 - r)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static uint32_t jvp_string_hash(jv jstr) { | 
					
						
							|  |  |  |   jvp_string* str = jvp_string_ptr(jstr); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   if (str->length_hashed & 1)  | 
					
						
							|  |  |  |     return str->hash; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* The following is based on MurmurHash3.
 | 
					
						
							|  |  |  |      MurmurHash3 was written by Austin Appleby, and is placed | 
					
						
							|  |  |  |      in the public domain. */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const uint8_t* data = (const uint8_t*)str->data; | 
					
						
							|  |  |  |   int len = (int)jvp_string_length(str); | 
					
						
							|  |  |  |   const int nblocks = len / 4; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   uint32_t h1 = HASH_SEED; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const uint32_t c1 = 0xcc9e2d51; | 
					
						
							|  |  |  |   const uint32_t c2 = 0x1b873593; | 
					
						
							|  |  |  |   const uint32_t* blocks = (const uint32_t *)(data + nblocks*4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for(int i = -nblocks; i; i++) { | 
					
						
							|  |  |  |     uint32_t k1 = blocks[i]; //FIXME: endianness/alignment
 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     k1 *= c1; | 
					
						
							|  |  |  |     k1 = rotl32(k1,15); | 
					
						
							|  |  |  |     k1 *= c2; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     h1 ^= k1; | 
					
						
							|  |  |  |     h1 = rotl32(h1,13);  | 
					
						
							|  |  |  |     h1 = h1*5+0xe6546b64; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const uint8_t* tail = (const uint8_t*)(data + nblocks*4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   uint32_t k1 = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch(len & 3) { | 
					
						
							|  |  |  |   case 3: k1 ^= tail[2] << 16; | 
					
						
							|  |  |  |   case 2: k1 ^= tail[1] << 8; | 
					
						
							|  |  |  |   case 1: k1 ^= tail[0]; | 
					
						
							|  |  |  |           k1 *= c1; k1 = rotl32(k1,15); k1 *= c2; h1 ^= k1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   h1 ^= len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   h1 ^= h1 >> 16; | 
					
						
							|  |  |  |   h1 *= 0x85ebca6b; | 
					
						
							|  |  |  |   h1 ^= h1 >> 13; | 
					
						
							|  |  |  |   h1 *= 0xc2b2ae35; | 
					
						
							|  |  |  |   h1 ^= h1 >> 16; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   str->length_hashed |= 1; | 
					
						
							|  |  |  |   str->hash = h1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return h1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int jvp_string_equal(jv a, jv b) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_STRING); | 
					
						
							|  |  |  |   assert(jv_get_kind(b) == JV_KIND_STRING); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jvp_string* stra = jvp_string_ptr(a); | 
					
						
							|  |  |  |   jvp_string* strb = jvp_string_ptr(b); | 
					
						
							|  |  |  |   if (jvp_string_length(stra) != jvp_string_length(strb)) return 0; | 
					
						
							|  |  |  |   return memcmp(stra->data, strb->data, jvp_string_length(stra)) == 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Strings (public API) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-02 16:31:59 +01:00
										 |  |  | jv jv_string_sized(const char* str, int len) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   return | 
					
						
							|  |  |  |     jvp_utf8_is_valid(str, str+len) ?  | 
					
						
							| 
									
										
										
										
											2013-06-22 13:34:24 +01:00
										 |  |  |     jvp_string_new(str, len) : | 
					
						
							|  |  |  |     jvp_string_copy_replace_bad(str, len); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-28 14:48:46 -06:00
										 |  |  | jv jv_string_empty(int len) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   return jvp_string_empty_new(len); | 
					
						
							| 
									
										
										
										
											2013-11-28 14:48:46 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-02 16:31:59 +01:00
										 |  |  | jv jv_string(const char* str) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return jv_string_sized(str, strlen(str)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-15 00:37:38 +01:00
										 |  |  | int jv_string_length_bytes(jv j) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   assert(jv_get_kind(j) == JV_KIND_STRING); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   int r = jvp_string_length(jvp_string_ptr(j)); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jv_free(j); | 
					
						
							|  |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-15 00:37:38 +01:00
										 |  |  | int jv_string_length_codepoints(jv j) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_STRING); | 
					
						
							|  |  |  |   const char* i = jv_string_value(j); | 
					
						
							|  |  |  |   const char* end = i + jv_string_length_bytes(jv_copy(j)); | 
					
						
							|  |  |  |   int c = 0, len = 0; | 
					
						
							|  |  |  |   while ((i = jvp_utf8_next(i, end, &c))) len++; | 
					
						
							|  |  |  |   jv_free(j); | 
					
						
							|  |  |  |   return len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-28 22:27:23 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-02 12:16:38 -06:00
										 |  |  | jv jv_string_indexes(jv j, jv k) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_STRING); | 
					
						
							|  |  |  |   assert(jv_get_kind(k) == JV_KIND_STRING); | 
					
						
							|  |  |  |   const char *jstr = jv_string_value(j); | 
					
						
							|  |  |  |   const char *idxstr = jv_string_value(k); | 
					
						
							|  |  |  |   const char *p; | 
					
						
							|  |  |  |   int jlen = jv_string_length_bytes(jv_copy(j)); | 
					
						
							|  |  |  |   int idxlen = jv_string_length_bytes(jv_copy(k)); | 
					
						
							|  |  |  |   jv a = jv_array(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   p = jstr; | 
					
						
							| 
									
										
										
										
											2014-07-22 22:44:52 -05:00
										 |  |  |   while ((p = _jq_memmem(p, (jstr + jlen) - p, idxstr, idxlen)) != NULL) { | 
					
						
							| 
									
										
										
										
											2013-12-02 12:16:38 -06:00
										 |  |  |     a = jv_array_append(a, jv_number(p - jstr)); | 
					
						
							|  |  |  |     p += idxlen; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv_free(j); | 
					
						
							|  |  |  |   jv_free(k); | 
					
						
							|  |  |  |   return a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-28 22:27:23 -06:00
										 |  |  | jv jv_string_split(jv j, jv sep) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_STRING); | 
					
						
							|  |  |  |   assert(jv_get_kind(sep) == JV_KIND_STRING); | 
					
						
							|  |  |  |   const char *jstr = jv_string_value(j); | 
					
						
							| 
									
										
										
										
											2015-01-12 10:44:44 -06:00
										 |  |  |   const char *jend = jstr + jv_string_length_bytes(jv_copy(j)); | 
					
						
							| 
									
										
										
										
											2013-11-28 22:27:23 -06:00
										 |  |  |   const char *sepstr = jv_string_value(sep); | 
					
						
							|  |  |  |   const char *p, *s; | 
					
						
							|  |  |  |   int seplen = jv_string_length_bytes(jv_copy(sep)); | 
					
						
							|  |  |  |   jv a = jv_array(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   assert(jv_get_refcnt(a) == 1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-13 18:56:53 -06:00
										 |  |  |   if (seplen == 0) { | 
					
						
							|  |  |  |     int c; | 
					
						
							|  |  |  |     while ((jstr = jvp_utf8_next(jstr, jend, &c))) | 
					
						
							|  |  |  |       a = jv_array_append(a, jv_string_append_codepoint(jv_string(""), c)); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     for (p = jstr; p < jend; p = s + seplen) { | 
					
						
							|  |  |  |       s = _jq_memmem(p, jend - p, sepstr, seplen); | 
					
						
							|  |  |  |       if (s == NULL) | 
					
						
							|  |  |  |         s = jend; | 
					
						
							|  |  |  |       a = jv_array_append(a, jv_string_sized(p, s - p)); | 
					
						
							|  |  |  |       // Add an empty string to denote that j ends on a sep
 | 
					
						
							|  |  |  |       if (s + seplen == jend && seplen != 0) | 
					
						
							|  |  |  |         a = jv_array_append(a, jv_string("")); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-11-28 22:27:23 -06:00
										 |  |  |   } | 
					
						
							|  |  |  |   jv_free(j); | 
					
						
							|  |  |  |   jv_free(sep); | 
					
						
							|  |  |  |   return a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-28 14:48:46 -06:00
										 |  |  | jv jv_string_explode(jv j) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_STRING); | 
					
						
							|  |  |  |   const char* i = jv_string_value(j); | 
					
						
							|  |  |  |   int len = jv_string_length_bytes(jv_copy(j)); | 
					
						
							|  |  |  |   const char* end = i + len; | 
					
						
							|  |  |  |   jv a = jv_array_sized(len); | 
					
						
							|  |  |  |   int c; | 
					
						
							|  |  |  |   while ((i = jvp_utf8_next(i, end, &c))) | 
					
						
							|  |  |  |     a = jv_array_append(a, jv_number(c)); | 
					
						
							|  |  |  |   jv_free(j); | 
					
						
							|  |  |  |   return a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_string_implode(jv j) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_ARRAY); | 
					
						
							|  |  |  |   int len = jv_array_length(jv_copy(j)); | 
					
						
							|  |  |  |   jv s = jv_string_empty(len); | 
					
						
							|  |  |  |   int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   assert(len >= 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (i = 0; i < len; i++) { | 
					
						
							| 
									
										
										
										
											2013-11-28 14:53:50 -06:00
										 |  |  |     jv n = jv_array_get(jv_copy(j), i); | 
					
						
							| 
									
										
										
										
											2013-11-28 14:48:46 -06:00
										 |  |  |     assert(jv_get_kind(n) == JV_KIND_NUMBER); | 
					
						
							|  |  |  |     s = jv_string_append_codepoint(s, jv_number_value(n)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   jv_free(j); | 
					
						
							|  |  |  |   return s; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-23 14:23:07 +01:00
										 |  |  | unsigned long jv_string_hash(jv j) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   assert(jv_get_kind(j) == JV_KIND_STRING); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   uint32_t hash = jvp_string_hash(j); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jv_free(j); | 
					
						
							|  |  |  |   return hash; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const char* jv_string_value(jv j) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_STRING); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   return jvp_string_ptr(j)->data; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:50:02 -06:00
										 |  |  | jv jv_string_slice(jv j, int start, int end) { | 
					
						
							|  |  |  |   assert(jv_get_kind(j) == JV_KIND_STRING); | 
					
						
							|  |  |  |   const char *s = jv_string_value(j); | 
					
						
							|  |  |  |   int len = jv_string_length_bytes(jv_copy(j)); | 
					
						
							|  |  |  |   int i; | 
					
						
							|  |  |  |   const char *p, *e; | 
					
						
							|  |  |  |   int c; | 
					
						
							|  |  |  |   jv res; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-03-04 21:09:00 -07:00
										 |  |  |   jvp_clamp_slice_params(len, &start, &end); | 
					
						
							| 
									
										
										
										
											2013-11-29 12:50:02 -06:00
										 |  |  |   assert(0 <= start && start <= end && end <= len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Look for byte offset corresponding to start codepoints */ | 
					
						
							|  |  |  |   for (p = s, i = 0; i < start; i++) { | 
					
						
							|  |  |  |     p = jvp_utf8_next(p, s + len, &c); | 
					
						
							|  |  |  |     if (p == NULL) { | 
					
						
							|  |  |  |       jv_free(j); | 
					
						
							|  |  |  |       return jv_string_empty(16); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (c == -1) { | 
					
						
							|  |  |  |       jv_free(j); | 
					
						
							|  |  |  |       return jv_invalid_with_msg(jv_string("Invalid UTF-8 string")); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   /* Look for byte offset corresponding to end codepoints */ | 
					
						
							|  |  |  |   for (e = p; e != NULL && i < end; i++) { | 
					
						
							|  |  |  |     e = jvp_utf8_next(e, s + len, &c); | 
					
						
							|  |  |  |     if (e == NULL) { | 
					
						
							|  |  |  |       e = s + len; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (c == -1) { | 
					
						
							|  |  |  |       jv_free(j); | 
					
						
							|  |  |  |       return jv_invalid_with_msg(jv_string("Invalid UTF-8 string")); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /*
 | 
					
						
							|  |  |  |    * NOTE: Ideally we should do here what jvp_array_slice() does instead | 
					
						
							|  |  |  |    * of allocating a new string as we do!  However, we assume NUL- | 
					
						
							|  |  |  |    * terminated strings all over, and in the jv API, so for now we waste | 
					
						
							|  |  |  |    * memory like a drunken navy programmer.  There's probably nothing we | 
					
						
							|  |  |  |    * can do about it. | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   res = jv_string_sized(p, e - p); | 
					
						
							|  |  |  |   jv_free(j); | 
					
						
							|  |  |  |   return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-10 16:16:39 +01:00
										 |  |  | jv jv_string_concat(jv a, jv b) { | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   a = jvp_string_append(a, jv_string_value(b),  | 
					
						
							|  |  |  |                         jvp_string_length(jvp_string_ptr(b))); | 
					
						
							| 
									
										
										
										
											2012-09-10 16:16:39 +01:00
										 |  |  |   jv_free(b); | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |   return a; | 
					
						
							| 
									
										
										
										
											2012-09-10 16:16:39 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  | jv jv_string_append_buf(jv a, const char* buf, int len) { | 
					
						
							| 
									
										
										
										
											2013-06-22 13:34:24 +01:00
										 |  |  |   if (jvp_utf8_is_valid(buf, buf+len)) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     a = jvp_string_append(a, buf, len); | 
					
						
							| 
									
										
										
										
											2013-06-22 13:34:24 +01:00
										 |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     jv b = jvp_string_copy_replace_bad(buf, len); | 
					
						
							| 
									
										
										
										
											2013-06-22 13:34:24 +01:00
										 |  |  |     a = jv_string_concat(a, b); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  |   return a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-28 14:53:05 -06:00
										 |  |  | jv jv_string_append_codepoint(jv a, uint32_t c) { | 
					
						
							| 
									
										
										
										
											2013-11-28 14:48:46 -06:00
										 |  |  |   char buf[5]; | 
					
						
							|  |  |  |   int len = jvp_utf8_encode(c, buf); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   a = jvp_string_append(a, buf, len); | 
					
						
							| 
									
										
										
										
											2013-11-28 14:48:46 -06:00
										 |  |  |   return a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-17 19:41:41 +01:00
										 |  |  | jv jv_string_append_str(jv a, const char* str) { | 
					
						
							|  |  |  |   return jv_string_append_buf(a, str, strlen(str)); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-06-22 13:34:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-04 18:14:10 -06:00
										 |  |  | jv jv_string_vfmt(const char* fmt, va_list ap) { | 
					
						
							| 
									
										
										
										
											2012-09-10 16:01:56 +01:00
										 |  |  |   int size = 1024; | 
					
						
							|  |  |  |   while (1) { | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |     char* buf = jv_mem_alloc(size); | 
					
						
							| 
									
										
										
										
											2013-12-04 18:14:10 -06:00
										 |  |  |     va_list ap2; | 
					
						
							|  |  |  |     va_copy(ap2, ap); | 
					
						
							|  |  |  |     int n = vsnprintf(buf, size, fmt, ap2); | 
					
						
							|  |  |  |     va_end(ap2); | 
					
						
							| 
									
										
										
										
											2014-06-02 00:56:01 -05:00
										 |  |  |     /*
 | 
					
						
							|  |  |  |      * NOTE: here we support old vsnprintf()s that return -1 because the | 
					
						
							|  |  |  |      * buffer is too small. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     if (n >= 0 && n < size) { | 
					
						
							| 
									
										
										
										
											2012-09-10 16:01:56 +01:00
										 |  |  |       jv ret = jv_string_sized(buf, n); | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |       jv_mem_free(buf); | 
					
						
							| 
									
										
										
										
											2012-09-10 16:01:56 +01:00
										 |  |  |       return ret; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |       jv_mem_free(buf); | 
					
						
							| 
									
										
										
										
											2014-06-02 00:56:01 -05:00
										 |  |  |       size = (n > 0) ? /* standard */ (n * 2) : /* not standard */ (size * 2); | 
					
						
							| 
									
										
										
										
											2012-09-10 16:01:56 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2013-12-04 18:14:10 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | jv jv_string_fmt(const char* fmt, ...) { | 
					
						
							|  |  |  |   va_list args; | 
					
						
							|  |  |  |   va_start(args, fmt); | 
					
						
							|  |  |  |   jv res = jv_string_vfmt(fmt, args); | 
					
						
							|  |  |  |   va_end(args); | 
					
						
							|  |  |  |   return res; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-09-10 16:01:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Objects (internal helpers) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct object_slot { | 
					
						
							| 
									
										
										
										
											2013-12-04 22:30:39 +00:00
										 |  |  |   int next; /* next slot with same hash, for collisions */ | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   uint32_t hash; | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv string; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jv value; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct { | 
					
						
							|  |  |  |   jv_refcnt refcnt; | 
					
						
							| 
									
										
										
										
											2013-12-04 22:30:39 +00:00
										 |  |  |   int next_free; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   struct object_slot elements[]; | 
					
						
							|  |  |  | } jvp_object; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-05 23:28:11 -05:00
										 |  |  | /* warning: nontrivial justification of alignment */ | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv jvp_object_new(int size) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   // Allocates an object of (size) slots and (size*2) hash buckets.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // size must be a power of two
 | 
					
						
							|  |  |  |   assert(size > 0 && (size & (size - 1)) == 0); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |   jvp_object* obj = jv_mem_alloc(sizeof(jvp_object) +  | 
					
						
							|  |  |  |                                  sizeof(struct object_slot) * size + | 
					
						
							|  |  |  |                                  sizeof(int) * (size * 2)); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   obj->refcnt.count = 1; | 
					
						
							|  |  |  |   for (int i=0; i<size; i++) { | 
					
						
							|  |  |  |     obj->elements[i].next = i - 1; | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     obj->elements[i].string = JV_NULL; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     obj->elements[i].hash = 0; | 
					
						
							|  |  |  |     obj->elements[i].value = JV_NULL; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-12-04 22:30:39 +00:00
										 |  |  |   obj->next_free = 0; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   int* hashbuckets = (int*)(&obj->elements[size]); | 
					
						
							|  |  |  |   for (int i=0; i<size*2; i++) { | 
					
						
							|  |  |  |     hashbuckets[i] = -1; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv r = {JV_KIND_OBJECT, 0, 0, size, {&obj->refcnt}}; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jvp_object* jvp_object_ptr(jv o) { | 
					
						
							|  |  |  |   assert(jv_get_kind(o) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   return (jvp_object*)o.u.ptr; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static uint32_t jvp_object_mask(jv o) { | 
					
						
							|  |  |  |   assert(jv_get_kind(o) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   return (o.size * 2) - 1; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int jvp_object_size(jv o) { | 
					
						
							|  |  |  |   assert(jv_get_kind(o) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   return o.size; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int* jvp_object_buckets(jv o) { | 
					
						
							|  |  |  |   return (int*)(&jvp_object_ptr(o)->elements[o.size]); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static int* jvp_object_find_bucket(jv object, jv key) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return jvp_object_buckets(object) + (jvp_object_mask(object) & jvp_string_hash(key)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static struct object_slot* jvp_object_get_slot(jv object, int slot) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   assert(slot == -1 || (slot >= 0 && slot < jvp_object_size(object))); | 
					
						
							|  |  |  |   if (slot == -1) return 0; | 
					
						
							|  |  |  |   else return &jvp_object_ptr(object)->elements[slot]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static struct object_slot* jvp_object_next_slot(jv object, struct object_slot* slot) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return jvp_object_get_slot(object, slot->next); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static struct object_slot* jvp_object_find_slot(jv object, jv keystr, int* bucket) { | 
					
						
							|  |  |  |   uint32_t hash = jvp_string_hash(keystr); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   for (struct object_slot* curr = jvp_object_get_slot(object, *bucket);  | 
					
						
							|  |  |  |        curr;  | 
					
						
							|  |  |  |        curr = jvp_object_next_slot(object, curr)) { | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     if (curr->hash == hash && jvp_string_equal(keystr, curr->string)) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |       return curr; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static struct object_slot* jvp_object_add_slot(jv object, jv key, int* bucket) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jvp_object* o = jvp_object_ptr(object); | 
					
						
							| 
									
										
										
										
											2013-12-04 22:30:39 +00:00
										 |  |  |   int newslot_idx = o->next_free; | 
					
						
							|  |  |  |   if (newslot_idx == jvp_object_size(object)) return 0; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   struct object_slot* newslot = jvp_object_get_slot(object, newslot_idx); | 
					
						
							| 
									
										
										
										
											2013-12-04 22:30:39 +00:00
										 |  |  |   o->next_free++; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   newslot->next = *bucket; | 
					
						
							|  |  |  |   *bucket = newslot_idx; | 
					
						
							|  |  |  |   newslot->hash = jvp_string_hash(key); | 
					
						
							|  |  |  |   newslot->string = key; | 
					
						
							|  |  |  |   return newslot; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static jv* jvp_object_read(jv object, jv key) { | 
					
						
							|  |  |  |   assert(jv_get_kind(key) == JV_KIND_STRING); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   int* bucket = jvp_object_find_bucket(object, key); | 
					
						
							|  |  |  |   struct object_slot* slot = jvp_object_find_slot(object, key, bucket); | 
					
						
							|  |  |  |   if (slot == 0) return 0; | 
					
						
							|  |  |  |   else return &slot->value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static void jvp_object_free(jv o) { | 
					
						
							|  |  |  |   assert(jv_get_kind(o) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   if (jvp_refcnt_dec(o.u.ptr)) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     for (int i=0; i<jvp_object_size(o); i++) { | 
					
						
							|  |  |  |       struct object_slot* slot = jvp_object_get_slot(o, i); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |       if (jv_get_kind(slot->string) != JV_KIND_NULL) { | 
					
						
							|  |  |  |         jvp_string_free(slot->string); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |         jv_free(slot->value); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |     jv_mem_free(jvp_object_ptr(o)); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv jvp_object_rehash(jv object) { | 
					
						
							|  |  |  |   assert(jv_get_kind(object) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   assert(jvp_refcnt_unshared(object.u.ptr)); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   int size = jvp_object_size(object); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   jv new_object = jvp_object_new(size * 2); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   for (int i=0; i<size; i++) { | 
					
						
							|  |  |  |     struct object_slot* slot = jvp_object_get_slot(object, i); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     if (jv_get_kind(slot->string) == JV_KIND_NULL) continue; | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     int* new_bucket = jvp_object_find_bucket(new_object, slot->string); | 
					
						
							|  |  |  |     assert(!jvp_object_find_slot(new_object, slot->string, new_bucket)); | 
					
						
							|  |  |  |     struct object_slot* new_slot = jvp_object_add_slot(new_object, slot->string, new_bucket); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     assert(new_slot); | 
					
						
							|  |  |  |     new_slot->value = slot->value; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // references are transported, just drop the old table
 | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  |   jv_mem_free(jvp_object_ptr(object)); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   return new_object; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static jv jvp_object_unshare(jv object) { | 
					
						
							|  |  |  |   assert(jv_get_kind(object) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   if (jvp_refcnt_unshared(object.u.ptr)) | 
					
						
							|  |  |  |     return object; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   jv new_object = jvp_object_new(jvp_object_size(object)); | 
					
						
							|  |  |  |   jvp_object_ptr(new_object)->next_free = jvp_object_ptr(object)->next_free; | 
					
						
							|  |  |  |   for (int i=0; i<jvp_object_size(new_object); i++) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     struct object_slot* old_slot = jvp_object_get_slot(object, i); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     struct object_slot* new_slot = jvp_object_get_slot(new_object, i); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     *new_slot = *old_slot; | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     if (jv_get_kind(old_slot->string) != JV_KIND_NULL) { | 
					
						
							|  |  |  |       new_slot->string = jv_copy(old_slot->string); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |       new_slot->value = jv_copy(old_slot->value); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   int* old_buckets = jvp_object_buckets(object); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   int* new_buckets = jvp_object_buckets(new_object); | 
					
						
							|  |  |  |   memcpy(new_buckets, old_buckets, sizeof(int) * jvp_object_size(new_object)*2); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   jvp_object_free(object); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   assert(jvp_refcnt_unshared(new_object.u.ptr)); | 
					
						
							|  |  |  |   return new_object; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static jv* jvp_object_write(jv* object, jv key) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   *object = jvp_object_unshare(*object); | 
					
						
							|  |  |  |   int* bucket = jvp_object_find_bucket(*object, key); | 
					
						
							|  |  |  |   struct object_slot* slot = jvp_object_find_slot(*object, key, bucket); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   if (slot) { | 
					
						
							|  |  |  |     // already has the key
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     jvp_string_free(key); | 
					
						
							| 
									
										
										
										
											2012-09-09 18:09:05 +01:00
										 |  |  |     return &slot->value; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   slot = jvp_object_add_slot(*object, key, bucket); | 
					
						
							| 
									
										
										
										
											2012-09-09 18:09:05 +01:00
										 |  |  |   if (slot) { | 
					
						
							|  |  |  |     slot->value = jv_invalid(); | 
					
						
							|  |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     *object = jvp_object_rehash(*object); | 
					
						
							|  |  |  |     bucket = jvp_object_find_bucket(*object, key); | 
					
						
							|  |  |  |     assert(!jvp_object_find_slot(*object, key, bucket)); | 
					
						
							|  |  |  |     slot = jvp_object_add_slot(*object, key, bucket); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     assert(slot); | 
					
						
							| 
									
										
										
										
											2012-09-03 17:26:52 +01:00
										 |  |  |     slot->value = jv_invalid(); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } | 
					
						
							|  |  |  |   return &slot->value; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  | static int jvp_object_delete(jv* object, jv key) { | 
					
						
							|  |  |  |   assert(jv_get_kind(key) == JV_KIND_STRING); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   *object = jvp_object_unshare(*object); | 
					
						
							|  |  |  |   int* bucket = jvp_object_find_bucket(*object, key); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   int* prev_ptr = bucket; | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   uint32_t hash = jvp_string_hash(key); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   for (struct object_slot* curr = jvp_object_get_slot(*object, *bucket);  | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |        curr;  | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |        curr = jvp_object_next_slot(*object, curr)) { | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     if (hash == curr->hash && jvp_string_equal(key, curr->string)) { | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |       *prev_ptr = curr->next; | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |       jvp_string_free(curr->string); | 
					
						
							|  |  |  |       curr->string = JV_NULL; | 
					
						
							| 
									
										
										
										
											2013-12-04 22:30:39 +00:00
										 |  |  |       jv_free(curr->value); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |       return 1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     prev_ptr = &curr->next; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int jvp_object_length(jv object) { | 
					
						
							| 
									
										
										
										
											2012-09-02 00:24:23 +01:00
										 |  |  |   int n = 0; | 
					
						
							|  |  |  |   for (int i=0; i<jvp_object_size(object); i++) { | 
					
						
							|  |  |  |     struct object_slot* slot = jvp_object_get_slot(object, i); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     if (jv_get_kind(slot->string) != JV_KIND_NULL) n++; | 
					
						
							| 
									
										
										
										
											2012-09-02 00:24:23 +01:00
										 |  |  |   } | 
					
						
							|  |  |  |   return n; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  | static int jvp_object_equal(jv o1, jv o2) { | 
					
						
							| 
									
										
										
										
											2012-09-02 21:45:27 +01:00
										 |  |  |   int len2 = jvp_object_length(o2); | 
					
						
							|  |  |  |   int len1 = 0; | 
					
						
							|  |  |  |   for (int i=0; i<jvp_object_size(o1); i++) { | 
					
						
							|  |  |  |     struct object_slot* slot = jvp_object_get_slot(o1, i); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |     if (jv_get_kind(slot->string) == JV_KIND_NULL) continue; | 
					
						
							| 
									
										
										
										
											2012-09-02 21:45:27 +01:00
										 |  |  |     jv* slot2 = jvp_object_read(o2, slot->string); | 
					
						
							|  |  |  |     if (!slot2) return 0; | 
					
						
							|  |  |  |     // FIXME: do less refcounting here
 | 
					
						
							|  |  |  |     if (!jv_equal(jv_copy(slot->value), jv_copy(*slot2))) return 0; | 
					
						
							|  |  |  |     len1++; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return len1 == len2; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Objects (public interface) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define DEFAULT_OBJECT_SIZE 8
 | 
					
						
							|  |  |  | jv jv_object() { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   return jvp_object_new(8); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_object_get(jv object, jv key) { | 
					
						
							|  |  |  |   assert(jv_get_kind(object) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   assert(jv_get_kind(key) == JV_KIND_STRING); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv* slot = jvp_object_read(object, key); | 
					
						
							| 
									
										
										
										
											2012-09-03 16:16:14 +01:00
										 |  |  |   jv val; | 
					
						
							|  |  |  |   if (slot) { | 
					
						
							|  |  |  |     val = jv_copy(*slot); | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     val = jv_invalid(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jv_free(object); | 
					
						
							|  |  |  |   jv_free(key); | 
					
						
							|  |  |  |   return val; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_object_set(jv object, jv key, jv value) { | 
					
						
							|  |  |  |   assert(jv_get_kind(object) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   assert(jv_get_kind(key) == JV_KIND_STRING); | 
					
						
							|  |  |  |   // copy/free of object, key, value coalesced
 | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv* slot = jvp_object_write(&object, key); | 
					
						
							| 
									
										
										
										
											2012-09-01 18:24:17 +01:00
										 |  |  |   jv_free(*slot); | 
					
						
							|  |  |  |   *slot = value; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return object; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_object_delete(jv object, jv key) { | 
					
						
							|  |  |  |   assert(jv_get_kind(object) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   assert(jv_get_kind(key) == JV_KIND_STRING); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jvp_object_delete(&object, key); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   jv_free(key); | 
					
						
							|  |  |  |   return object; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-02 00:24:23 +01:00
										 |  |  | int jv_object_length(jv object) { | 
					
						
							|  |  |  |   assert(jv_get_kind(object) == JV_KIND_OBJECT); | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   int n = jvp_object_length(object); | 
					
						
							| 
									
										
										
										
											2012-09-02 00:24:23 +01:00
										 |  |  |   jv_free(object); | 
					
						
							|  |  |  |   return n; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-09 19:17:07 +01:00
										 |  |  | jv jv_object_merge(jv a, jv b) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_OBJECT); | 
					
						
							| 
									
										
										
										
											2012-12-31 23:27:00 +00:00
										 |  |  |   jv_object_foreach(b, k, v) { | 
					
						
							|  |  |  |     a = jv_object_set(a, k, v); | 
					
						
							| 
									
										
										
										
											2012-09-09 19:17:07 +01:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2012-09-10 15:08:39 +01:00
										 |  |  |   jv_free(b); | 
					
						
							| 
									
										
										
										
											2012-09-09 19:17:07 +01:00
										 |  |  |   return a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-08 03:56:05 +01:00
										 |  |  | jv jv_object_merge_recursive(jv a, jv b) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   assert(jv_get_kind(b) == JV_KIND_OBJECT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   jv_object_foreach(b, k, v) { | 
					
						
							|  |  |  |     jv elem = jv_object_get(jv_copy(a), jv_copy(k)); | 
					
						
							|  |  |  |     if (jv_is_valid(elem) && | 
					
						
							|  |  |  |         jv_get_kind(elem) == JV_KIND_OBJECT && | 
					
						
							|  |  |  |         jv_get_kind(v) == JV_KIND_OBJECT) { | 
					
						
							|  |  |  |       a = jv_object_set(a, k, jv_object_merge_recursive(elem, v)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       jv_free(elem); | 
					
						
							|  |  |  |       a = jv_object_set(a, k, v); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv_free(b); | 
					
						
							|  |  |  |   return a; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-24 11:42:25 -07:00
										 |  |  | int jv_object_contains(jv a, jv b) { | 
					
						
							|  |  |  |   assert(jv_get_kind(a) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   assert(jv_get_kind(b) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   int r = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-31 23:27:00 +00:00
										 |  |  |   jv_object_foreach(b, key, b_val) { | 
					
						
							| 
									
										
										
										
											2012-10-25 06:29:23 -07:00
										 |  |  |     jv a_val = jv_object_get(jv_copy(a), jv_copy(key)); | 
					
						
							| 
									
										
										
										
											2012-10-24 18:29:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-25 06:29:23 -07:00
										 |  |  |     r = jv_contains(a_val, b_val); | 
					
						
							|  |  |  |     jv_free(key); | 
					
						
							| 
									
										
										
										
											2012-10-24 18:29:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!r) break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-24 11:42:25 -07:00
										 |  |  |   jv_free(a); | 
					
						
							|  |  |  |   jv_free(b); | 
					
						
							|  |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Object iteration (internal helpers) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum { ITER_FINISHED = -2 }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int jv_object_iter_valid(jv object, int i) { | 
					
						
							|  |  |  |   return i != ITER_FINISHED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int jv_object_iter(jv object) { | 
					
						
							|  |  |  |   assert(jv_get_kind(object) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   return jv_object_iter_next(object, -1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int jv_object_iter_next(jv object, int iter) { | 
					
						
							|  |  |  |   assert(jv_get_kind(object) == JV_KIND_OBJECT); | 
					
						
							|  |  |  |   assert(iter != ITER_FINISHED); | 
					
						
							|  |  |  |   struct object_slot* slot; | 
					
						
							|  |  |  |   do { | 
					
						
							|  |  |  |     iter++; | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     if (iter >= jvp_object_size(object))  | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |       return ITER_FINISHED; | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     slot = jvp_object_get_slot(object, iter); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   } while (jv_get_kind(slot->string) == JV_KIND_NULL); | 
					
						
							|  |  |  |   assert(jv_get_kind(jvp_object_get_slot(object,iter)->string) | 
					
						
							|  |  |  |          == JV_KIND_STRING); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   return iter; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_object_iter_key(jv object, int iter) { | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   jv s = jvp_object_get_slot(object, iter)->string; | 
					
						
							|  |  |  |   assert(jv_get_kind(s) == JV_KIND_STRING); | 
					
						
							|  |  |  |   return jv_copy(s); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | jv jv_object_iter_value(jv object, int iter) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |   return jv_copy(jvp_object_get_slot(object, iter)->value); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Memory management | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | jv jv_copy(jv j) { | 
					
						
							|  |  |  |   if (jv_get_kind(j) == JV_KIND_ARRAY ||  | 
					
						
							|  |  |  |       jv_get_kind(j) == JV_KIND_STRING ||  | 
					
						
							| 
									
										
										
										
											2012-09-10 15:04:19 +01:00
										 |  |  |       jv_get_kind(j) == JV_KIND_OBJECT || | 
					
						
							| 
									
										
										
										
											2013-12-24 12:24:14 -06:00
										 |  |  |       (jv_get_kind(j) == JV_KIND_INVALID && j.u.ptr != 0)) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     jvp_refcnt_inc(j.u.ptr); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } | 
					
						
							|  |  |  |   return j; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void jv_free(jv j) { | 
					
						
							|  |  |  |   if (jv_get_kind(j) == JV_KIND_ARRAY) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     jvp_array_free(j); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } else if (jv_get_kind(j) == JV_KIND_STRING) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     jvp_string_free(j); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } else if (jv_get_kind(j) == JV_KIND_OBJECT) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     jvp_object_free(j); | 
					
						
							| 
									
										
										
										
											2012-09-10 15:04:19 +01:00
										 |  |  |   } else if (jv_get_kind(j) == JV_KIND_INVALID) { | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     jvp_invalid_free(j); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-02 21:45:27 +01:00
										 |  |  | int jv_get_refcnt(jv j) { | 
					
						
							|  |  |  |   switch (jv_get_kind(j)) { | 
					
						
							|  |  |  |   case JV_KIND_ARRAY: | 
					
						
							|  |  |  |   case JV_KIND_STRING: | 
					
						
							|  |  |  |   case JV_KIND_OBJECT: | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |     return j.u.ptr->count; | 
					
						
							| 
									
										
										
										
											2012-09-02 21:45:27 +01:00
										 |  |  |   default: | 
					
						
							|  |  |  |     return 1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Higher-level operations | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int jv_equal(jv a, jv b) { | 
					
						
							|  |  |  |   int r; | 
					
						
							|  |  |  |   if (jv_get_kind(a) != jv_get_kind(b)) { | 
					
						
							|  |  |  |     r = 0; | 
					
						
							| 
									
										
										
										
											2012-09-10 13:06:46 +01:00
										 |  |  |   } else if (jv_get_kind(a) == JV_KIND_NUMBER) { | 
					
						
							|  |  |  |     r = jv_number_value(a) == jv_number_value(b); | 
					
						
							| 
									
										
										
										
											2013-12-08 22:23:43 +00:00
										 |  |  |   } else if (a.kind_flags == b.kind_flags && | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |              a.size == b.size && | 
					
						
							|  |  |  |              a.u.ptr == b.u.ptr) { | 
					
						
							| 
									
										
										
										
											2012-09-10 13:06:46 +01:00
										 |  |  |     r = 1; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     switch (jv_get_kind(a)) { | 
					
						
							| 
									
										
										
										
											2012-09-10 13:06:46 +01:00
										 |  |  |     case JV_KIND_ARRAY: | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |       r = jvp_array_equal(a, b); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2012-09-10 13:06:46 +01:00
										 |  |  |     case JV_KIND_STRING: | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |       r = jvp_string_equal(a, b); | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2012-09-10 13:06:46 +01:00
										 |  |  |     case JV_KIND_OBJECT: | 
					
						
							| 
									
										
										
										
											2013-12-08 17:46:23 +00:00
										 |  |  |       r = jvp_object_equal(a, b); | 
					
						
							| 
									
										
										
										
											2012-09-02 21:45:27 +01:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2012-08-28 18:09:43 +01:00
										 |  |  |     default: | 
					
						
							|  |  |  |       r = 1; | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv_free(a); | 
					
						
							|  |  |  |   jv_free(b); | 
					
						
							|  |  |  |   return r; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-10-24 11:42:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | int jv_contains(jv a, jv b) { | 
					
						
							|  |  |  |   int r = 1; | 
					
						
							|  |  |  |   if (jv_get_kind(a) != jv_get_kind(b)) { | 
					
						
							|  |  |  |     r = 0; | 
					
						
							|  |  |  |   } else if (jv_get_kind(a) == JV_KIND_OBJECT) { | 
					
						
							|  |  |  |     r = jv_object_contains(jv_copy(a), jv_copy(b)); | 
					
						
							|  |  |  |   } else if (jv_get_kind(a) == JV_KIND_ARRAY) { | 
					
						
							| 
									
										
										
										
											2012-10-24 18:29:33 -07:00
										 |  |  |     r = jv_array_contains(jv_copy(a), jv_copy(b)); | 
					
						
							| 
									
										
										
										
											2012-10-24 11:42:25 -07:00
										 |  |  |   } else if (jv_get_kind(a) == JV_KIND_STRING) { | 
					
						
							|  |  |  |     r = strstr(jv_string_value(a), jv_string_value(b)) != 0; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     r = jv_equal(jv_copy(a), jv_copy(b)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   jv_free(a); | 
					
						
							|  |  |  |   jv_free(b); | 
					
						
							|  |  |  |   return r; | 
					
						
							| 
									
										
										
										
											2012-12-18 16:52:47 +00:00
										 |  |  | } |