Skip to content

Commit

Permalink
Add YYJSON_WRITE_NEWLINE_AT_END flag for JSON writer: #147
Browse files Browse the repository at this point in the history
  • Loading branch information
ibireme committed Dec 11, 2023
1 parent d044a64 commit dd712db
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 5 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
All notable changes to this project will be documented in this file.


## Unreleased
#### Added
- Add `YYJSON_WRITE_NEWLINE_AT_END` flag for JSON writer: #147


## 0.8.0 (2023-09-13)
#### Added
- Add `YYJSON_SUBTYPE_NOESC` subtype to mark strings that do not need to be escaped.
Expand Down
4 changes: 4 additions & 0 deletions doc/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,10 @@ Invalid characters within string values will be copied byte by byte. If `YYJSON_

This flag does not affect the performance of correctly encoded string.

**YYJSON_WRITE_NEWLINE_AT_END**<br/>
Adds a newline character `\n` at the end of the JSON.
This can be helpful for text editors or NDJSON.


---------------
# Accessing JSON Document
Expand Down
35 changes: 30 additions & 5 deletions src/yyjson.c
Original file line number Diff line number Diff line change
Expand Up @@ -8309,21 +8309,23 @@ static_inline u8 *yyjson_write_single(yyjson_val *val,
bool cpy = (enc_table == enc_table_cpy);
bool esc = has_write_flag(ESCAPE_UNICODE) != 0;
bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0;
bool newline = has_write_flag(NEWLINE_AT_END) != 0;
const usize end_len = 2; /* '\n' and '\0' */

switch (unsafe_yyjson_get_type(val)) {
case YYJSON_TYPE_RAW:
str_len = unsafe_yyjson_get_len(val);
str_ptr = (const u8 *)unsafe_yyjson_get_str(val);
check_str_len(str_len);
incr_len(str_len + 1);
incr_len(str_len + end_len);
cur = write_raw(cur, str_ptr, str_len);
break;

case YYJSON_TYPE_STR:
str_len = unsafe_yyjson_get_len(val);
str_ptr = (const u8 *)unsafe_yyjson_get_str(val);
check_str_len(str_len);
incr_len(str_len * 6 + 4);
incr_len(str_len * 6 + 2 + end_len);
if (likely(cpy) && unsafe_yyjson_get_subtype(val)) {
cur = write_string_noesc(cur, str_ptr, str_len);
} else {
Expand All @@ -8333,7 +8335,7 @@ static_inline u8 *yyjson_write_single(yyjson_val *val,
break;

case YYJSON_TYPE_NUM:
incr_len(32);
incr_len(32 + end_len);
cur = write_number(cur, val, flg);
if (unlikely(!cur)) goto fail_num;
break;
Expand All @@ -8349,13 +8351,13 @@ static_inline u8 *yyjson_write_single(yyjson_val *val,
break;

case YYJSON_TYPE_ARR:
incr_len(4);
incr_len(2 + end_len);
byte_copy_2(cur, "[]");
cur += 2;
break;

case YYJSON_TYPE_OBJ:
incr_len(4);
incr_len(2 + end_len);
byte_copy_2(cur, "{}");
cur += 2;
break;
Expand All @@ -8364,6 +8366,7 @@ static_inline u8 *yyjson_write_single(yyjson_val *val,
goto fail_type;
}

if (newline) *cur++ = '\n';
*cur = '\0';
*dat_len = (usize)(cur - hdr);
memset(err, 0, sizeof(yyjson_write_err));
Expand Down Expand Up @@ -8436,6 +8439,7 @@ static_inline u8 *yyjson_write_minify(const yyjson_val *root,
bool cpy = (enc_table == enc_table_cpy);
bool esc = has_write_flag(ESCAPE_UNICODE) != 0;
bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0;
bool newline = has_write_flag(NEWLINE_AT_END) != 0;

alc_len = root->uni.ofs / sizeof(yyjson_val);
alc_len = alc_len * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64;
Expand Down Expand Up @@ -8542,6 +8546,11 @@ static_inline u8 *yyjson_write_minify(const yyjson_val *root,
}

doc_end:
if (newline) {
incr_len(2);
*(cur - 1) = '\n';
cur++;
}
*--cur = '\0';
*dat_len = (usize)(cur - hdr);
memset(err, 0, sizeof(yyjson_write_err));
Expand Down Expand Up @@ -8615,6 +8624,7 @@ static_inline u8 *yyjson_write_pretty(const yyjson_val *root,
bool esc = has_write_flag(ESCAPE_UNICODE) != 0;
bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0;
usize spaces = has_write_flag(PRETTY_TWO_SPACES) ? 2 : 4;
bool newline = has_write_flag(NEWLINE_AT_END) != 0;

alc_len = root->uni.ofs / sizeof(yyjson_val);
alc_len = alc_len * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64;
Expand Down Expand Up @@ -8745,6 +8755,10 @@ static_inline u8 *yyjson_write_pretty(const yyjson_val *root,
}

doc_end:
if (newline) {
incr_len(2);
*cur++ = '\n';
}
*cur = '\0';
*dat_len = (usize)(cur - hdr);
memset(err, 0, sizeof(yyjson_write_err));
Expand Down Expand Up @@ -8977,6 +8991,7 @@ static_inline u8 *yyjson_mut_write_minify(const yyjson_mut_val *root,
bool cpy = (enc_table == enc_table_cpy);
bool esc = has_write_flag(ESCAPE_UNICODE) != 0;
bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0;
bool newline = has_write_flag(NEWLINE_AT_END) != 0;

alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64;
alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx));
Expand Down Expand Up @@ -9087,6 +9102,11 @@ static_inline u8 *yyjson_mut_write_minify(const yyjson_mut_val *root,
}

doc_end:
if (newline) {
incr_len(2);
*(cur - 1) = '\n';
cur++;
}
*--cur = '\0';
*dat_len = (usize)(cur - hdr);
err->code = YYJSON_WRITE_SUCCESS;
Expand Down Expand Up @@ -9162,6 +9182,7 @@ static_inline u8 *yyjson_mut_write_pretty(const yyjson_mut_val *root,
bool esc = has_write_flag(ESCAPE_UNICODE) != 0;
bool inv = has_write_flag(ALLOW_INVALID_UNICODE) != 0;
usize spaces = has_write_flag(PRETTY_TWO_SPACES) ? 2 : 4;
bool newline = has_write_flag(NEWLINE_AT_END) != 0;

alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64;
alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx));
Expand Down Expand Up @@ -9296,6 +9317,10 @@ static_inline u8 *yyjson_mut_write_pretty(const yyjson_mut_val *root,
}

doc_end:
if (newline) {
incr_len(2);
*cur++ = '\n';
}
*cur = '\0';
*dat_len = (usize)(cur - hdr);
err->code = YYJSON_WRITE_SUCCESS;
Expand Down
4 changes: 4 additions & 0 deletions src/yyjson.h
Original file line number Diff line number Diff line change
Expand Up @@ -1096,6 +1096,10 @@ static const yyjson_write_flag YYJSON_WRITE_ALLOW_INVALID_UNICODE = 1 << 5;
This flag will override `YYJSON_WRITE_PRETTY` flag. */
static const yyjson_write_flag YYJSON_WRITE_PRETTY_TWO_SPACES = 1 << 6;

/** Adds a newline character `\n` at the end of the JSON.
This can be helpful for text editors or NDJSON. */
static const yyjson_write_flag YYJSON_WRITE_NEWLINE_AT_END = 1 << 7;



/** Result code for JSON writer */
Expand Down
67 changes: 67 additions & 0 deletions test/test_json_writer.c
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,73 @@ yy_test_case(test_json_writer) {
yyjson_doc_free(doc);
yyjson_mut_doc_free(mdoc);
}


// test newline at end
{
size_t len;
const char *str;
yyjson_doc *doc;
yyjson_mut_doc *mdoc;
char *ret;

// single value
str = "123";
doc = yyjson_read(str, strlen(str), 0);
ret = yyjson_write(doc, YYJSON_WRITE_NEWLINE_AT_END, &len);
yy_assert(strlen(ret) == len && len == strlen(str) + 1);
yy_assert(memcmp(str, ret, strlen(str)) == 0);
yy_assert(ret[strlen(str)] == '\n');
free(ret);

mdoc = yyjson_doc_mut_copy(doc, NULL);
ret = yyjson_mut_write(mdoc, YYJSON_WRITE_NEWLINE_AT_END, &len);
yy_assert(strlen(ret) == len && len == strlen(str) + 1);
yy_assert(memcmp(str, ret, strlen(str)) == 0);
yy_assert(ret[strlen(str)] == '\n');
free(ret);

yyjson_doc_free(doc);
yyjson_mut_doc_free(mdoc);

// multiple values
str = "[123]";
doc = yyjson_read(str, strlen(str), 0);
ret = yyjson_write(doc, YYJSON_WRITE_NEWLINE_AT_END, &len);
yy_assert(strlen(ret) == len && len == strlen(str) + 1);
yy_assert(memcmp(str, ret, strlen(str)) == 0);
yy_assert(ret[strlen(str)] == '\n');
free(ret);

mdoc = yyjson_doc_mut_copy(doc, NULL);
ret = yyjson_mut_write(mdoc, YYJSON_WRITE_NEWLINE_AT_END, &len);
yy_assert(strlen(ret) == len && len == strlen(str) + 1);
yy_assert(memcmp(str, ret, strlen(str)) == 0);
yy_assert(ret[strlen(str)] == '\n');
free(ret);

yyjson_doc_free(doc);
yyjson_mut_doc_free(mdoc);

// multiple values, pretty
str = "[\n 123\n]";
doc = yyjson_read(str, strlen(str), 0);
ret = yyjson_write(doc, YYJSON_WRITE_PRETTY | YYJSON_WRITE_NEWLINE_AT_END, &len);
yy_assert(strlen(ret) == len && len == strlen(str) + 1);
yy_assert(memcmp(str, ret, strlen(str)) == 0);
yy_assert(ret[strlen(str)] == '\n');
free(ret);

mdoc = yyjson_doc_mut_copy(doc, NULL);
ret = yyjson_mut_write(mdoc, YYJSON_WRITE_PRETTY | YYJSON_WRITE_NEWLINE_AT_END, &len);
yy_assert(strlen(ret) == len && len == strlen(str) + 1);
yy_assert(memcmp(str, ret, strlen(str)) == 0);
yy_assert(ret[strlen(str)] == '\n');
free(ret);

yyjson_doc_free(doc);
yyjson_mut_doc_free(mdoc);
}
#endif

// test build JSON on stack
Expand Down

0 comments on commit dd712db

Please sign in to comment.