| ... | @@ -2963,6 +2963,47 @@ fn outputUnicodeEscape( | ... | @@ -2963,6 +2963,47 @@ fn outputUnicodeEscape( |
| 2963 | } | 2963 | } |
| 2964 | } | 2964 | } |
| 2965 | | 2965 | |
| | 2966 | fn outputJsonString(value: []const u8, options: StringifyOptions, out_stream: anytype) !void { |
| | 2967 | try out_stream.writeByte('\"'); |
| | 2968 | var i: usize = 0; |
| | 2969 | while (i < value.len) : (i += 1) { |
| | 2970 | switch (value[i]) { |
| | 2971 | // normal ascii character |
| | 2972 | 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => |c| try out_stream.writeByte(c), |
| | 2973 | // only 2 characters that *must* be escaped |
| | 2974 | '\\' => try out_stream.writeAll("\\\\"), |
| | 2975 | '\"' => try out_stream.writeAll("\\\""), |
| | 2976 | // solidus is optional to escape |
| | 2977 | '/' => { |
| | 2978 | if (options.string.String.escape_solidus) { |
| | 2979 | try out_stream.writeAll("\\/"); |
| | 2980 | } else { |
| | 2981 | try out_stream.writeByte('/'); |
| | 2982 | } |
| | 2983 | }, |
| | 2984 | // control characters with short escapes |
| | 2985 | // TODO: option to switch between unicode and 'short' forms? |
| | 2986 | 0x8 => try out_stream.writeAll("\\b"), |
| | 2987 | 0xC => try out_stream.writeAll("\\f"), |
| | 2988 | '\n' => try out_stream.writeAll("\\n"), |
| | 2989 | '\r' => try out_stream.writeAll("\\r"), |
| | 2990 | '\t' => try out_stream.writeAll("\\t"), |
| | 2991 | else => { |
| | 2992 | const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable; |
| | 2993 | // control characters (only things left with 1 byte length) should always be printed as unicode escapes |
| | 2994 | if (ulen == 1 or options.string.String.escape_unicode) { |
| | 2995 | const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable; |
| | 2996 | try outputUnicodeEscape(codepoint, out_stream); |
| | 2997 | } else { |
| | 2998 | try out_stream.writeAll(value[i .. i + ulen]); |
| | 2999 | } |
| | 3000 | i += ulen - 1; |
| | 3001 | }, |
| | 3002 | } |
| | 3003 | } |
| | 3004 | try out_stream.writeByte('\"'); |
| | 3005 | } |
| | 3006 | |
| 2966 | pub fn stringify( | 3007 | pub fn stringify( |
| 2967 | value: anytype, | 3008 | value: anytype, |
| 2968 | options: StringifyOptions, | 3009 | options: StringifyOptions, |
| ... | @@ -3048,7 +3089,7 @@ pub fn stringify( | ... | @@ -3048,7 +3089,7 @@ pub fn stringify( |
| 3048 | try out_stream.writeByte('\n'); | 3089 | try out_stream.writeByte('\n'); |
| 3049 | try child_whitespace.outputIndent(out_stream); | 3090 | try child_whitespace.outputIndent(out_stream); |
| 3050 | } | 3091 | } |
| 3051 | try stringify(Field.name, options, out_stream); | 3092 | try outputJsonString(Field.name, options, out_stream); |
| 3052 | try out_stream.writeByte(':'); | 3093 | try out_stream.writeByte(':'); |
| 3053 | if (child_options.whitespace) |child_whitespace| { | 3094 | if (child_options.whitespace) |child_whitespace| { |
| 3054 | if (child_whitespace.separator) { | 3095 | if (child_whitespace.separator) { |
| ... | @@ -3082,44 +3123,7 @@ pub fn stringify( | ... | @@ -3082,44 +3123,7 @@ pub fn stringify( |
| 3082 | // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) | 3123 | // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) |
| 3083 | .Slice => { | 3124 | .Slice => { |
| 3084 | if (ptr_info.child == u8 and options.string == .String and std.unicode.utf8ValidateSlice(value)) { | 3125 | if (ptr_info.child == u8 and options.string == .String and std.unicode.utf8ValidateSlice(value)) { |
| 3085 | try out_stream.writeByte('\"'); | 3126 | try outputJsonString(value, options, out_stream); |
| 3086 | var i: usize = 0; | | |
| 3087 | while (i < value.len) : (i += 1) { | | |
| 3088 | switch (value[i]) { | | |
| 3089 | // normal ascii character | | |
| 3090 | 0x20...0x21, 0x23...0x2E, 0x30...0x5B, 0x5D...0x7F => |c| try out_stream.writeByte(c), | | |
| 3091 | // only 2 characters that *must* be escaped | | |
| 3092 | '\\' => try out_stream.writeAll("\\\\"), | | |
| 3093 | '\"' => try out_stream.writeAll("\\\""), | | |
| 3094 | // solidus is optional to escape | | |
| 3095 | '/' => { | | |
| 3096 | if (options.string.String.escape_solidus) { | | |
| 3097 | try out_stream.writeAll("\\/"); | | |
| 3098 | } else { | | |
| 3099 | try out_stream.writeByte('/'); | | |
| 3100 | } | | |
| 3101 | }, | | |
| 3102 | // control characters with short escapes | | |
| 3103 | // TODO: option to switch between unicode and 'short' forms? | | |
| 3104 | 0x8 => try out_stream.writeAll("\\b"), | | |
| 3105 | 0xC => try out_stream.writeAll("\\f"), | | |
| 3106 | '\n' => try out_stream.writeAll("\\n"), | | |
| 3107 | '\r' => try out_stream.writeAll("\\r"), | | |
| 3108 | '\t' => try out_stream.writeAll("\\t"), | | |
| 3109 | else => { | | |
| 3110 | const ulen = std.unicode.utf8ByteSequenceLength(value[i]) catch unreachable; | | |
| 3111 | // control characters (only things left with 1 byte length) should always be printed as unicode escapes | | |
| 3112 | if (ulen == 1 or options.string.String.escape_unicode) { | | |
| 3113 | const codepoint = std.unicode.utf8Decode(value[i .. i + ulen]) catch unreachable; | | |
| 3114 | try outputUnicodeEscape(codepoint, out_stream); | | |
| 3115 | } else { | | |
| 3116 | try out_stream.writeAll(value[i .. i + ulen]); | | |
| 3117 | } | | |
| 3118 | i += ulen - 1; | | |
| 3119 | }, | | |
| 3120 | } | | |
| 3121 | } | | |
| 3122 | try out_stream.writeByte('\"'); | | |
| 3123 | return; | 3127 | return; |
| 3124 | } | 3128 | } |
| 3125 | | 3129 | |