From de709a4a69fb8f150f6051dd22cfc18007f61837 Mon Sep 17 00:00:00 2001 From: Adriane Boyd Date: Fri, 8 Apr 2022 14:31:35 +0200 Subject: [PATCH] Port ultrajson/ultrajson#519 (Fix buffer overflows) Port fixes and unit tests from ultrajson/ultrajson#519 to fix buffer overflows (CVE-2021-45958) --- srsly/tests/ujson/334-reproducer.json | 857 ++++++++++++++++++++++++++ srsly/tests/ujson/test_ujson.py | 39 +- srsly/ujson/lib/ultrajson.h | 3 - srsly/ujson/lib/ultrajsonenc.c | 99 ++- 4 files changed, 959 insertions(+), 39 deletions(-) create mode 100644 srsly/tests/ujson/334-reproducer.json diff --git a/srsly/tests/ujson/334-reproducer.json b/srsly/tests/ujson/334-reproducer.json new file mode 100644 index 0000000..b53fcd5 --- /dev/null +++ b/srsly/tests/ujson/334-reproducer.json @@ -0,0 +1,857 @@ +{ + "ak.somestring.internal.Shadow": { + "id": 33300002, + "init_state": "(bk.action.array.Make, (bk.action.i32.Const, 0))", + "child": { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Collection": { + "id": 33300001, + "snap": "center", + "direction": "row", + "children": [ + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#2c8932" + } + } + } + }, + "children": [ + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "children": [ + { + "ls.components.Image": { + "media_id": "10156403921218138", + "preview_url": "https://scontent.xx.whoaa.net/v/t1.0-9/51099660_10156403921233138_3677795704043995136_n.jpg?_nc_cat=102&_nc_log=1&_nc_oc=AQk3Td-w9KpopLL2N1jgZ4WDMuxUyuGY3ZvY4mDSCk8W9-GjsFPi2S4gVQk0Y3A5ZaaQf7ASvQ2s_eR85kTmFvr0&_nc_ad=z-m&_nc_cid=0&_nc_zor=9&_nc_ht=scontent.xx&oh=fb16b0d60b13817a505f583cc9dad1eb&oe=5CBCDB46", + "height": 278, + "width": 156 + } + } + ], + "_style": { + "flex": { + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "flex_direction": "row", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#ffffff" + } + } + } + }, + "_style": { + "flex": { + "margin_right": "4dp", + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#ffffff" + } + } + } + }, + "_style": { + "flex": { + "margin_right": "4dp", + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "row", + "align_items": "stretch", + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#ffffff" + } + } + } + }, + "children": [ + { + "ak.somestring.Flexbox": { + "id": 33300004, + "_style": { + "flex": { + "grow": 1 + } + } + } + } + ], + "_style": { + "flex": { + "margin_right": "4dp", + "grow": 1 + } + } + } + } + ], + "_style": { + "flex": { + "height": "2dp", + "margin_left": "4dp" + } + } + } + } + ], + "_style": { + "flex": { + "position_type": "absolute", + "left": "0dp", + "top": "10dp", + "margin_top": "10dp", + "right": "0dp", + "height": "2dp", + "width": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "align_items": "flex_start", + "children": [ + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "corner_radius": "17dp" + } + }, + "children": [ + { + "ls.components.Image": { + "media_id": "10156403921218138", + "preview_url": "https://scontent.xx.whoaa.net/v/t1.0-9/51099660_10156403921233138_3677795704043995136_n.jpg?_nc_cat=102&_nc_log=1&_nc_oc=AQk3Td-w9KpopLL2N1jgZ4WDMuxUyuGY3ZvY4mDSCk8W9-GjsFPi2S4gVQk0Y3A5ZaaQf7ASvQ2s_eR85kTmFvr0&_nc_ad=z-m&_nc_cid=0&_nc_zor=9&_nc_ht=scontent.xx&oh=fb16b0d60b13817a505f583cc9dad1eb&oe=5CBCDB46", + "height": 34, + "width": 34, + "_style": { + "flex": { + "width": "34dp", + "height": "34dp" + } + } + } + } + ], + "_style": { + "flex": { + "margin_right": "12dp", + "width": "34dp", + "height": "34dp" + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "flex_start", + "children": [ + { + "ak.somestring.RichText": { + "children": [ + { + "ak.somestring.TextSpan": { + "text": "eric", + "text_size": "15sp", + "text_style": "bold", + "text_color": "#ffffff" + } + } + ], + "_style": { + "flex": { + "margin_bottom": "2dp", + "width": "100%" + } + } + } + }, + { + "ak.somestring.RichText": { + "children": [ + { + "ak.somestring.TextSpan": { + "text": "8h", + "text_size": "13sp", + "text_style": "normal", + "text_color": "#ffffff" + } + } + ], + "_style": { + "flex": { + "width": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "width": "100%", + "height": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "position_type": "absolute", + "top": "30dp", + "left": "10dp", + "height": "48dp" + } + } + } + }, + { + "ak.somestring.Flexbox": { + "children": [ + { + "ls.components.StoriesReplyBar": {} + } + ], + "_style": { + "flex": { + "width": "100%", + "height": "45dp", + "margin_top": "auto", + "margin_bottom": "auto" + } + } + } + } + ], + "_style": { + "flex": { + "position_type": "absolute", + "width": "100%", + "height": "100%", + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "children": [ + { + "ls.components.Image": { + "media_id": "10101230968216658", + "preview_url": "https://scontent.xx.whoaa.net/v/t1.0-9/50800535_10101230968226638_6755212111762161664_n.jpg?_nc_cat=101&_nc_log=1&_nc_oc=AQmKcqYvt6DI7aeGk3k_oF6RHSVZkUg7f9hnBCWilyaOGdCWO0-u9_zssC5qGvca6wqsrz3AP0y1RPLPiZj8ycCv&_nc_ad=z-m&_nc_cid=0&_nc_zor=9&_nc_ht=scontent.xx&oh=2fffbab8f0a102d196454ee0138c1850&oe=5CC15206", + "height": 278, + "width": 156 + } + } + ], + "_style": { + "flex": { + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "flex_direction": "row", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#ffffff" + } + } + } + }, + "_style": { + "flex": { + "margin_right": "4dp", + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "row", + "align_items": "stretch", + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#ffffff" + } + } + } + }, + "children": [ + { + "ak.somestring.Flexbox": { + "id": 33300005, + "_style": { + "flex": { + "grow": 1 + } + } + } + } + ], + "_style": { + "flex": { + "margin_right": "4dp", + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#cccccc" + } + } + } + }, + "_style": { + "flex": { + "margin_right": "4dp", + "grow": 1 + } + } + } + } + ], + "_style": { + "flex": { + "height": "2dp", + "margin_left": "4dp" + } + } + } + } + ], + "_style": { + "flex": { + "position_type": "absolute", + "left": "0dp", + "top": "10dp", + "margin_top": "10dp", + "right": "0dp", + "height": "2dp", + "width": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "align_items": "flex_start", + "children": [ + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "corner_radius": "17dp" + } + }, + "children": [ + { + "ls.components.Image": { + "media_id": "10101230968216658", + "preview_url": "https://scontent.xx.whoaa.net/v/t1.0-9/50800535_10101230968226638_6755212111762161664_n.jpg?_nc_cat=101&_nc_log=1&_nc_oc=AQmKcqYvt6DI7aeGk3k_oF6RHSVZkUg7f9hnBCWilyaOGdCWO0-u9_zssC5qGvca6wqsrz3AP0y1RPLPiZj8ycCv&_nc_ad=z-m&_nc_cid=0&_nc_zor=9&_nc_ht=scontent.xx&oh=2fffbab8f0a102d196454ee0138c1850&oe=5CC15206", + "height": 34, + "width": 34, + "_style": { + "flex": { + "width": "34dp", + "height": "34dp" + } + } + } + } + ], + "_style": { + "flex": { + "margin_right": "12dp", + "width": "34dp", + "height": "34dp" + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "flex_start", + "children": [ + { + "ak.somestring.RichText": { + "children": [ + { + "ak.somestring.TextSpan": { + "text": "eric", + "text_size": "15sp", + "text_style": "bold", + "text_color": "#ffffff" + } + } + ], + "_style": { + "flex": { + "margin_bottom": "2dp", + "width": "100%" + } + } + } + }, + { + "ak.somestring.RichText": { + "children": [ + { + "ak.somestring.TextSpan": { + "text": "2h", + "text_size": "13sp", + "text_style": "normal", + "text_color": "#ffffff" + } + } + ], + "_style": { + "flex": { + "width": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "width": "100%", + "height": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "position_type": "absolute", + "top": "30dp", + "left": "10dp", + "height": "48dp" + } + } + } + }, + { + "ak.somestring.Flexbox": { + "children": [ + { + "ls.components.StoriesReplyBar": {} + } + ], + "_style": { + "flex": { + "width": "100%", + "height": "45dp", + "margin_top": "auto", + "margin_bottom": "auto" + } + } + } + } + ], + "_style": { + "flex": { + "position_type": "absolute", + "width": "100%", + "height": "100%", + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "children": [ + { + "ls.components.Video": { + "media_id": "10156395664922983", + "video_url": "https://video.xx.whoaa.net/v/t42.9040-2/51636103_316525608877874_407931582842667008_n.mp4?_nc_cat=109&efg=eyJ2ZW5jb2RlX3RhZyI6InN2ZV9oZCJ9&_nc_log=1&_nc_oc=AQm6aMctRAFdMe3C66upF2JulQP4mV3Hd4THkueZex952PR389F6Ay9XHm1S40dV1x7M1I-fAW5y3iH7JlQ3MgDM&_nc_ht=video.xx&oh=e17b1f7ec67619d57a5b1cda5e076fef&oe=5C587F7D", + "preview_url": "https://scontent.xx.whoaa.net/v/t15.5256-10/s960x960/51767715_10156395667952983_4168426706077483008_n.jpg?_nc_cat=104&_nc_log=1&_nc_oc=AQnVwEZk2vG8Q3TcoR0SxdXSi8rL_GaST2aH3i9auDcDnJNTRKvuYEFfd_qKGBhmD4-bo-f8BY5j9jHyit765O7P&_nc_ad=z-m&_nc_cid=0&_nc_zor=9&_nc_ht=scontent.xx&oh=9a17e4bcf8a2a9aabc21d2ecf9f8611b&oe=5CB3D14B", + "show_media_play_button": false, + "media_height": 960, + "media_width": 540 + } + } + ], + "_style": { + "flex": { + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "flex_direction": "row", + "align_items": "stretch", + "children": [ + { + "ak.somestring.Flexbox": { + "flex_direction": "row", + "align_items": "stretch", + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#ffffff" + } + } + } + }, + "children": [ + { + "ak.somestring.Flexbox": { + "id": 33300006, + "_style": { + "flex": { + "grow": 1 + } + } + } + } + ], + "_style": { + "flex": { + "margin_right": "4dp", + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#cccccc" + } + } + } + }, + "_style": { + "flex": { + "margin_right": "4dp", + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "background": { + "ak.somestring.ColorDrawable": { + "color": "#cccccc" + } + } + } + }, + "_style": { + "flex": { + "margin_right": "4dp", + "grow": 1 + } + } + } + } + ], + "_style": { + "flex": { + "height": "2dp", + "margin_left": "4dp" + } + } + } + } + ], + "_style": { + "flex": { + "position_type": "absolute", + "left": "0dp", + "top": "10dp", + "margin_top": "10dp", + "right": "0dp", + "height": "2dp", + "width": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "grow": 1 + } + } + } + }, + { + "ak.somestring.Flexbox": { + "align_items": "flex_start", + "children": [ + { + "ak.somestring.Flexbox": { + "decoration": { + "ak.somestring.BoxDecoration": { + "corner_radius": "17dp" + } + }, + "children": [ + { + "ls.components.Image": { + "media_id": "10156395664922983", + "height": 34, + "width": 34, + "_style": { + "flex": { + "width": "34dp", + "height": "34dp" + } + } + } + } + ], + "_style": { + "flex": { + "margin_right": "12dp", + "width": "34dp", + "height": "34dp" + } + } + } + }, + { + "ak.somestring.Flexbox": { + "flex_direction": "column", + "align_items": "flex_start", + "children": [ + { + "ak.somestring.RichText": { + "children": [ + { + "ak.somestring.TextSpan": { + "text": "eric", + "text_size": "15sp", + "text_style": "bold", + "text_color": "#ffffff" + } + } + ], + "_style": { + "flex": { + "margin_bottom": "2dp", + "width": "100%" + } + } + } + }, + { + "ak.somestring.RichText": { + "children": [ + { + "ak.somestring.TextSpan": { + "text": "20h", + "text_size": "13sp", + "text_style": "normal", + "text_color": "#ffffff" + } + } + ], + "_style": { + "flex": { + "width": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "width": "100%", + "height": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "position_type": "absolute", + "top": "30dp", + "left": "10dp", + "height": "48dp" + } + } + } + }, + { + "ak.somestring.Flexbox": { + "children": [ + { + "ls.components.StoriesReplyBar": {} + } + ], + "_style": { + "flex": { + "width": "100%", + "height": "45dp", + "margin_top": "auto", + "margin_bottom": "auto" + } + } + } + } + ], + "_style": { + "flex": { + "position_type": "absolute", + "width": "100%", + "height": "100%", + "grow": 1 + } + } + } + } + ], + "_style": { + "flex": { + "width": "100%", + "height": "100%" + } + } + } + } + ], + "_style": { + "flex": { + "height": "100%" + } + } + } + } + ] + } + } + } +} diff --git a/srsly/tests/ujson/test_ujson.py b/srsly/tests/ujson/test_ujson.py index 777754a..ccbf0cd 100644 --- a/srsly/tests/ujson/test_ujson.py +++ b/srsly/tests/ujson/test_ujson.py @@ -10,16 +10,13 @@ import math import time import sys +import unittest import pytest +from io import StringIO +from pathlib import Path +from srsly import ujson -if six.PY2: - import unittest2 as unittest -else: - import unittest - -from ... import ujson - -json_unicode = json.dumps if six.PY3 else functools.partial(json.dumps, encoding="utf-8") +json_unicode = json.dumps class UltraJSONTests(unittest.TestCase): @@ -923,3 +920,29 @@ def test_decodeStringUTF8(self): heap = hp.heapu() print(heap) """ + + +@pytest.mark.parametrize("indent", list(range(65537, 65542))) +def test_dump_huge_indent(indent): + ujson.encode({"a": True}, indent=indent) + + +@pytest.mark.parametrize("first_length", list(range(2, 7))) +@pytest.mark.parametrize("second_length", list(range(10919, 10924))) +def test_dump_long_string(first_length, second_length): + ujson.dumps(["a" * first_length, "\x00" * second_length]) + + +def test_dump_indented_nested_list(): + a = _a = [] + for i in range(20): + _a.append(list(range(i))) + _a = _a[-1] + ujson.dumps(a, indent=i) + + +@pytest.mark.parametrize("indent", [0, 1, 2, 4, 5, 8, 49]) +def test_issue_334(indent): + path = Path(__file__).with_name("334-reproducer.json") + a = ujson.loads(path.read_bytes()) + ujson.dumps(a, indent=indent) diff --git a/srsly/ujson/lib/ultrajson.h b/srsly/ujson/lib/ultrajson.h index 6b1fb85..e931db4 100644 --- a/srsly/ujson/lib/ultrajson.h +++ b/srsly/ujson/lib/ultrajson.h @@ -56,9 +56,6 @@ tree doesn't have cyclic references. #include #include -// Don't output any extra whitespaces when encoding -#define JSON_NO_EXTRA_WHITESPACE - // Max decimals to encode double floating point numbers with #ifndef JSON_DOUBLE_MAX_DECIMALS #define JSON_DOUBLE_MAX_DECIMALS 15 diff --git a/srsly/ujson/lib/ultrajsonenc.c b/srsly/ujson/lib/ultrajsonenc.c index 6c1b120..ea6372b 100644 --- a/srsly/ujson/lib/ultrajsonenc.c +++ b/srsly/ujson/lib/ultrajsonenc.c @@ -41,6 +41,7 @@ Numeric decoder derived from from TCL library #include #include #include +#include #include #include @@ -114,14 +115,25 @@ FIXME: Keep track of how big these get across several encoder calls and try to m That way we won't run our head into the wall each call */ void Buffer_Realloc (JSONObjectEncoder *enc, size_t cbNeeded) { + size_t free_space = enc->end - enc->offset; + if (free_space >= cbNeeded) + { + return; + } size_t curSize = enc->end - enc->start; - size_t newSize = curSize * 2; + size_t newSize = curSize; size_t offset = enc->offset - enc->start; +#ifdef DEBUG + // In debug mode, allocate only what is requested so that any miscalculation + // shows up plainly as a crash. + newSize = (enc->offset - enc->start) + cbNeeded; +#else while (newSize < curSize + cbNeeded) { newSize *= 2; } +#endif if (enc->heap) { @@ -148,6 +160,12 @@ void Buffer_Realloc (JSONObjectEncoder *enc, size_t cbNeeded) enc->end = enc->start + newSize; } +#define Buffer_Reserve(__enc, __len) \ + if ( (size_t) ((__enc)->end - (__enc)->offset) < (size_t) (__len)) \ + { \ + Buffer_Realloc((__enc), (__len));\ + } \ + FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC Buffer_AppendShortHexUnchecked (char *outputOffset, unsigned short value) { *(outputOffset++) = g_hexChars[(value & 0xf000) >> 12]; @@ -267,6 +285,13 @@ int Buffer_EscapeStringValidated (JSOBJ obj, JSONObjectEncoder *enc, const char for (;;) { +#ifdef DEBUG + // 6 is the maximum length of a single character (cf. RESERVE_STRING). + if ((io < end) && (enc->end - of < 6)) { + fprintf(stderr, "Ran out of buffer space during Buffer_EscapeStringValidated()\n"); + abort(); + } +#endif JSUINT8 utflen = g_asciiOutputTable[(unsigned char) *io]; switch (utflen) @@ -488,15 +513,27 @@ int Buffer_EscapeStringValidated (JSOBJ obj, JSONObjectEncoder *enc, const char } } -#define Buffer_Reserve(__enc, __len) \ - if ( (size_t) ((__enc)->end - (__enc)->offset) < (size_t) (__len)) \ - { \ - Buffer_Realloc((__enc), (__len));\ - } \ - - -#define Buffer_AppendCharUnchecked(__enc, __chr) \ - *((__enc)->offset++) = __chr; \ +static FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC Buffer_AppendCharUnchecked(JSONObjectEncoder *enc, char chr) +{ +#ifdef DEBUG + if (enc->end <= enc->offset) + { + fprintf(stderr, "Overflow writing byte %d '%c'. The last few characters were:\n'''", chr, chr); + char * recent = enc->offset - 1000; + if (enc->start > recent) + { + recent = enc->start; + } + for (; recent < enc->offset; recent++) + { + fprintf(stderr, "%c", *recent); + } + fprintf(stderr, "'''\n"); + abort(); + } +#endif + *(enc->offset++) = chr; +} FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC strreverse(char* begin, char* end) { @@ -729,14 +766,6 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName) return; } - /* - This reservation must hold - - length of _name as encoded worst case + - maxLength of double to string OR maxLength of JSLONG to string - */ - - Buffer_Reserve(enc, 256 + RESERVE_STRING(cbName)); if (enc->errorMsg) { return; @@ -744,6 +773,8 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName) if (name) { + // 2 extra for the colon and optional space after it + Buffer_Reserve(enc, RESERVE_STRING(cbName) + 2); Buffer_AppendCharUnchecked(enc, '\"'); if (enc->forceASCII) @@ -764,14 +795,21 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName) Buffer_AppendCharUnchecked(enc, '\"'); Buffer_AppendCharUnchecked (enc, ':'); -#ifndef JSON_NO_EXTRA_WHITESPACE - Buffer_AppendCharUnchecked (enc, ' '); -#endif } tc.encoder_prv = enc->prv; enc->beginTypeContext(obj, &tc, enc); + /* + This reservation covers any additions on non-variable parts below, specifically: + - Opening brackets for JT_ARRAY and JT_OBJECT + - Number representation for JT_LONG, JT_ULONG, JT_INT, and JT_DOUBLE + - Constant value for JT_TRUE, JT_FALSE, JT_NULL + The length of 128 is the worst case length of the Buffer_AppendDoubleDconv addition. + The other types above all have smaller representations. + */ + Buffer_Reserve (enc, 128); + switch (tc.type) { case JT_INVALID: @@ -788,12 +826,12 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName) while (enc->iterNext(obj, &tc)) { + // The extra 2 bytes cover the comma and (optional) newline. + Buffer_Reserve (enc, enc->indent * (enc->level + 1) + 2); + if (count > 0) { Buffer_AppendCharUnchecked (enc, ','); -#ifndef JSON_NO_EXTRA_WHITESPACE - Buffer_AppendCharUnchecked (buffer, ' '); -#endif Buffer_AppendIndentNewlineUnchecked (enc); } @@ -806,8 +844,11 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName) } enc->iterEnd(obj, &tc); + // Reserve space for the indentation plus the newline. + Buffer_Reserve (enc, enc->indent * enc->level + 1); Buffer_AppendIndentNewlineUnchecked (enc); Buffer_AppendIndentUnchecked (enc, enc->level); + Buffer_Reserve (enc, 1); Buffer_AppendCharUnchecked (enc, ']'); break; } @@ -821,12 +862,12 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName) while (enc->iterNext(obj, &tc)) { + // The extra 2 bytes cover the comma and optional newline. + Buffer_Reserve (enc, enc->indent * (enc->level + 1) + 2); + if (count > 0) { Buffer_AppendCharUnchecked (enc, ','); -#ifndef JSON_NO_EXTRA_WHITESPACE - Buffer_AppendCharUnchecked (enc, ' '); -#endif Buffer_AppendIndentNewlineUnchecked (enc); } @@ -840,8 +881,10 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName) } enc->iterEnd(obj, &tc); + Buffer_Reserve (enc, enc->indent * enc->level + 1); Buffer_AppendIndentNewlineUnchecked (enc); Buffer_AppendIndentUnchecked (enc, enc->level); + Buffer_Reserve (enc, 1); Buffer_AppendCharUnchecked (enc, '}'); break; } @@ -953,7 +996,7 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName) return; } - Buffer_Reserve(enc, RESERVE_STRING(szlen)); + Buffer_Reserve(enc, szlen); if (enc->errorMsg) { enc->endTypeContext(obj, &tc);