/* Converts string to a JSON string in FBuffer buffer, where only the * characters required by the JSON standard are JSON escaped. The remaining * characters (should be UTF8) are just passed through and appended to the * result. */ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string) { const char *ptr = RSTRING_PTR(string), *p; int len = RSTRING_LEN(string), start = 0, end = 0; const char *escape = NULL; int escape_len; unsigned char c; char buf[6] = { '\\', 'u' }; for (start = 0, end = 0; end < len;) { p = ptr + end; c = (unsigned char) *p; if (c < 0x20) { switch (c) { case '\n': escape = "\\n"; escape_len = 2; break; case '\r': escape = "\\r"; escape_len = 2; break; case '\t': escape = "\\t"; escape_len = 2; break; case '\f': escape = "\\f"; escape_len = 2; break; case '\b': escape = "\\b"; escape_len = 2; break; default: unicode_escape(buf, (UTF16) *p); escape = buf; escape_len = 6; break; } } else { switch (c) { case '\\': escape = "\\\\"; escape_len = 2; break; case '"': escape = "\\\""; escape_len = 2; break; default: end++; continue; break; } } fbuffer_append(buffer, ptr + start, end - start); fbuffer_append(buffer, escape, escape_len); start = ++end; escape = NULL; } fbuffer_append(buffer, ptr + start, end - start); }
/* Converts string to a JSON string in FBuffer buffer, where only the * characters required by the JSON standard are JSON escaped. The remaining * characters (should be UTF8) are just passed through and appended to the * result. */ static void convert_UTF8_to_JSON(FBuffer *buffer, VALUE string) { const char *ptr = RSTRING_PTR(string), *p; unsigned long len = RSTRING_LEN(string), start = 0, end = 0; const char *escape = NULL; int escape_len; unsigned char c; char buf[6] = { '\\', 'u' }; for (start = 0, end = 0; end < len;) { p = ptr + end; c = (unsigned char) *p; if (c < 0x20) { switch (c) { case '\n': escape = "\\n"; escape_len = 2; break; case '\r': escape = "\\r"; escape_len = 2; break; case '\t': escape = "\\t"; escape_len = 2; break; case '\f': escape = "\\f"; escape_len = 2; break; case '\b': escape = "\\b"; escape_len = 2; break; default: unicode_escape(buf, (UTF16) *p); escape = buf; escape_len = 6; break; } } else { switch (c) { case '\\': escape = "\\\\"; escape_len = 2; break; case '"': escape = "\\\""; escape_len = 2; break; default: { unsigned short clen = trailingBytesForUTF8[c] + 1; if (end + clen > len) { rb_raise(rb_path2class("JSON::GeneratorError"), "partial character in source, but hit end"); } if (!isLegalUTF8((UTF8 *) p, clen)) { rb_raise(rb_path2class("JSON::GeneratorError"), "source sequence is illegal/malformed utf-8"); } end += clen; } continue; break; } } fbuffer_append(buffer, ptr + start, end - start); fbuffer_append(buffer, escape, escape_len); start = ++end; escape = NULL; } fbuffer_append(buffer, ptr + start, end - start); }
/* Escapes the UTF16 character and stores the result in the buffer buf, then * the buffer buf іs appended to the FBuffer buffer. */ static void unicode_escape_to_buffer(FBuffer *buffer, char buf[6], UTF16 character) { unicode_escape(buf, character); fbuffer_append(buffer, buf, 6); }
void JSON_convert_UTF8_to_JSON(VALUE buffer, VALUE string, ConversionFlags flags) { char buf[7]; const UTF8* source = (UTF8 *) RSTRING_PTR(string); const UTF8* sourceEnd = source + RSTRING_LEN(string); while (source < sourceEnd) { UTF32 ch = 0; unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; if (source + extraBytesToRead >= sourceEnd) { rb_raise(rb_path2class("JSON::GeneratorError"), "partial character in source, but hit end"); } if (!isLegalUTF8(source, extraBytesToRead+1)) { rb_raise(rb_path2class("JSON::GeneratorError"), "source sequence is illegal/malformed"); } /* * The cases all fall through. See "Note A" below. */ switch (extraBytesToRead) { case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ case 3: ch += *source++; ch <<= 6; case 2: ch += *source++; ch <<= 6; case 1: ch += *source++; ch <<= 6; case 0: ch += *source++; } ch -= offsetsFromUTF8[extraBytesToRead]; if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ /* UTF-16 surrogate values are illegal in UTF-32 */ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { if (flags == strictConversion) { source -= (extraBytesToRead+1); /* return to the illegal value itself */ rb_raise(rb_path2class("JSON::GeneratorError"), "source sequence is illegal/malformed"); } else { unicode_escape(buffer, UNI_REPLACEMENT_CHAR); } } else { /* normal case */ if (ch == '"') { rb_str_buf_cat2(buffer, "\\\""); } else if (ch == '\\') { rb_str_buf_cat2(buffer, "\\\\"); } else if (ch == '/') { rb_str_buf_cat2(buffer, "\\/"); } else if (ch >= 0x20 && ch <= 0x7f) { rb_str_buf_cat(buffer, (char *) source - 1, 1); } else if (ch == '\n') { rb_str_buf_cat2(buffer, "\\n"); } else if (ch == '\r') { rb_str_buf_cat2(buffer, "\\r"); } else if (ch == '\t') { rb_str_buf_cat2(buffer, "\\t"); } else if (ch == '\f') { rb_str_buf_cat2(buffer, "\\f"); } else if (ch == '\b') { rb_str_buf_cat2(buffer, "\\b"); } else if (ch < 0x20) { unicode_escape(buffer, (UTF16) ch); } else { unicode_escape(buffer, (UTF16) ch); } } } else if (ch > UNI_MAX_UTF16) { if (flags == strictConversion) { source -= (extraBytesToRead+1); /* return to the start */ rb_raise(rb_path2class("JSON::GeneratorError"), "source sequence is illegal/malformed"); } else { unicode_escape(buffer, UNI_REPLACEMENT_CHAR); } } else { /* target is a character in range 0xFFFF - 0x10FFFF. */ ch -= halfBase; unicode_escape(buffer, (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START)); unicode_escape(buffer, (UTF16)((ch & halfMask) + UNI_SUR_LOW_START)); } } }