887 return &cx_json_value_nothing; |
888 return &cx_json_value_nothing; |
888 } |
889 } |
889 return value->value.array.array[index]; |
890 return value->value.array.array[index]; |
890 } |
891 } |
891 |
892 |
892 static void *cx_json_iter_current(const void *it) { |
|
893 const CxIterator *iter = it; |
|
894 return *(CxJsonValue**)iter->elem_handle; |
|
895 } |
|
896 |
|
897 static bool cx_json_iter_valid(const void *it) { |
|
898 const CxIterator *iter = it; |
|
899 return iter->index < iter->elem_count; |
|
900 } |
|
901 |
|
902 static void cx_json_iter_next(void *it) { |
|
903 CxIterator *iter = it; |
|
904 iter->index++; |
|
905 iter->elem_handle = (char *) iter->elem_handle + sizeof(void *); |
|
906 } |
|
907 |
|
908 CxIterator cxJsonArrIter(const CxJsonValue *value) { |
893 CxIterator cxJsonArrIter(const CxJsonValue *value) { |
909 CxIterator iter; |
894 return cxIteratorPtr( |
910 |
895 value->value.array.array, |
911 iter.index = 0; |
896 value->value.array.array_size |
912 iter.elem_count = value->value.array.array_size; |
897 ); |
913 iter.src_handle.m = value->value.array.array; |
898 } |
914 iter.elem_handle = iter.src_handle.m; |
899 |
915 iter.elem_size = sizeof(CxJsonValue*); |
900 CxIterator cxJsonObjIter(const CxJsonValue *value) { |
916 iter.base.valid = cx_json_iter_valid; |
901 return cxIterator( |
917 iter.base.current = cx_json_iter_current; |
902 value->value.object.values, |
918 iter.base.next = cx_json_iter_next; |
903 sizeof(CxJsonObjValue), |
919 iter.base.remove = false; |
904 value->value.object.values_size |
920 iter.base.mutating = false; |
905 ); |
921 |
|
922 return iter; |
|
923 } |
906 } |
924 |
907 |
925 CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name) { |
908 CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name) { |
926 CxJsonObjValue *member = json_find_objvalue(value, name); |
909 CxJsonObjValue *member = json_find_objvalue(value, name); |
927 if (member == NULL) { |
910 if (member == NULL) { |
928 return &cx_json_value_nothing; |
911 return &cx_json_value_nothing; |
929 } else { |
912 } else { |
930 return member->value; |
913 return member->value; |
931 } |
914 } |
932 } |
915 } |
|
916 |
|
917 static const CxJsonWriter cx_json_writer_default = { |
|
918 false, |
|
919 true, |
|
920 255, |
|
921 false, |
|
922 0, |
|
923 false, |
|
924 0 |
|
925 }; |
|
926 |
|
927 // TODO: add default for pretty printing and add functions to create default structs |
|
928 |
|
929 |
|
930 int cx_json_write_rec( |
|
931 void *target, |
|
932 const CxJsonValue *value, |
|
933 cx_write_func wfunc, |
|
934 const CxJsonWriter *settings, |
|
935 unsigned int depth |
|
936 ) { |
|
937 // TODO: implement indentation |
|
938 |
|
939 // keep track of written items |
|
940 size_t actual = 0, expected = 0; |
|
941 |
|
942 // small buffer for number to string conversions |
|
943 char numbuf[32]; |
|
944 |
|
945 // recursively write the values |
|
946 switch (value->type) { |
|
947 case CX_JSON_OBJECT: { |
|
948 const char *begin_obj = "{\n"; |
|
949 if (settings->pretty) { |
|
950 actual += wfunc(begin_obj, 1, 2, target); |
|
951 expected += 2; |
|
952 } else { |
|
953 actual += wfunc(begin_obj, 1, 1, target); |
|
954 expected++; |
|
955 } |
|
956 CxIterator iter = cxJsonObjIter(value); |
|
957 cx_foreach(CxJsonObjValue*, member, iter) { |
|
958 // the name |
|
959 actual += wfunc("\"", 1, 1, target); |
|
960 // TODO: escape the string |
|
961 actual += wfunc(member->name.ptr, 1, |
|
962 member->name.length, target); |
|
963 actual += wfunc("\"", 1, 1, target); |
|
964 const char *obj_name_sep = ": "; |
|
965 if (settings->pretty) { |
|
966 actual += wfunc(obj_name_sep, 1, 2, target); |
|
967 expected += 4 + member->name.length; |
|
968 } else { |
|
969 actual += wfunc(obj_name_sep, 1, 1, target); |
|
970 expected += 3 + member->name.length; |
|
971 } |
|
972 |
|
973 // the value |
|
974 if (0 == cx_json_write_rec( |
|
975 target, member->value, |
|
976 wfunc, settings, depth + 1) |
|
977 ) { |
|
978 actual++; // count the nested values as one item |
|
979 } |
|
980 expected++; |
|
981 |
|
982 // end of object-value |
|
983 if (iter.index < iter.elem_count - 1) { |
|
984 const char *obj_value_sep = ",\n"; |
|
985 if (settings->pretty) { |
|
986 actual += wfunc(obj_value_sep, 1, 2, target); |
|
987 expected += 2; |
|
988 } else { |
|
989 actual += wfunc(obj_value_sep, 1, 1, target); |
|
990 expected++; |
|
991 } |
|
992 } else { |
|
993 if (settings->pretty) { |
|
994 actual += wfunc("\n", 1, 1, target); |
|
995 expected ++; |
|
996 } |
|
997 } |
|
998 } |
|
999 actual += wfunc("}", 1, 1, target); |
|
1000 expected++; |
|
1001 break; |
|
1002 } |
|
1003 case CX_JSON_ARRAY: { |
|
1004 // TODO: implement array wrapping |
|
1005 actual += wfunc("[", 1, 1, target); |
|
1006 expected++; |
|
1007 CxIterator iter = cxJsonArrIter(value); |
|
1008 cx_foreach(CxJsonValue*, element, iter) { |
|
1009 // TODO: pretty printing obj elements vs. primitives |
|
1010 if (0 == cx_json_write_rec( |
|
1011 target, element, |
|
1012 wfunc, settings, depth + 1) |
|
1013 ) { |
|
1014 actual++; // count the nested values as one item |
|
1015 } |
|
1016 expected++; |
|
1017 |
|
1018 if (iter.index < iter.elem_count - 1) { |
|
1019 const char *arr_value_sep = ", "; |
|
1020 if (settings->pretty) { |
|
1021 actual += wfunc(arr_value_sep, 1, 2, target); |
|
1022 expected += 2; |
|
1023 } else { |
|
1024 actual += wfunc(arr_value_sep, 1, 1, target); |
|
1025 expected++; |
|
1026 } |
|
1027 } |
|
1028 } |
|
1029 actual += wfunc("]", 1, 1, target); |
|
1030 expected++; |
|
1031 break; |
|
1032 } |
|
1033 case CX_JSON_STRING: { |
|
1034 actual += wfunc("\"", 1, 1, target); |
|
1035 // TODO: escape the string |
|
1036 actual += wfunc(value->value.string.ptr, 1, |
|
1037 value->value.string.length, target); |
|
1038 actual += wfunc("\"", 1, 1, target); |
|
1039 expected += 2 + value->value.string.length; |
|
1040 break; |
|
1041 } |
|
1042 case CX_JSON_NUMBER: { |
|
1043 // TODO: locale bullshit |
|
1044 // TODO: formatting settings |
|
1045 snprintf(numbuf, 32, "%g", value->value.number); |
|
1046 size_t len = strlen(numbuf); |
|
1047 actual += wfunc(numbuf, 1, len, target); |
|
1048 expected += len; |
|
1049 break; |
|
1050 } |
|
1051 case CX_JSON_INTEGER: { |
|
1052 snprintf(numbuf, 32, "%" PRIi64, value->value.integer); |
|
1053 size_t len = strlen(numbuf); |
|
1054 actual += wfunc(numbuf, 1, len, target); |
|
1055 expected += len; |
|
1056 break; |
|
1057 } |
|
1058 case CX_JSON_LITERAL: { |
|
1059 if (value->value.literal == CX_JSON_TRUE) { |
|
1060 actual += wfunc("true", 1, 4, target); |
|
1061 expected += 4; |
|
1062 } else if (value->value.literal == CX_JSON_FALSE) { |
|
1063 actual += wfunc("false", 1, 5, target); |
|
1064 expected += 5; |
|
1065 } else { |
|
1066 actual += wfunc("null", 1, 4, target); |
|
1067 expected += 4; |
|
1068 } |
|
1069 break; |
|
1070 } |
|
1071 case CX_JSON_NOTHING: { |
|
1072 // deliberately supported as an empty string! |
|
1073 // users might want to just write the result |
|
1074 // of a get operation without testing the value |
|
1075 // and therefore this should not blow up |
|
1076 break; |
|
1077 } |
|
1078 default: assert(false); // LCOV_EXCL_LINE |
|
1079 } |
|
1080 |
|
1081 return expected != actual; |
|
1082 } |
|
1083 |
|
1084 int cxJsonWrite( |
|
1085 void *target, |
|
1086 const CxJsonValue *value, |
|
1087 cx_write_func wfunc, |
|
1088 const CxJsonWriter *settings |
|
1089 ) { |
|
1090 if (settings == NULL) { |
|
1091 settings = &cx_json_writer_default; |
|
1092 } |
|
1093 assert(target != NULL); |
|
1094 assert(value != NULL); |
|
1095 assert(wfunc != NULL); |
|
1096 |
|
1097 return cx_json_write_rec(target, value, wfunc, settings, 0); |
|
1098 } |