| 1 | using System;
 | 
|---|
| 2 | using System.Text;
 | 
|---|
| 3 | 
 | 
|---|
| 4 | namespace AllocsFixes.JSON {
 | 
|---|
| 5 |         public class JsonManualBuilder {
 | 
|---|
| 6 |                 [Flags]
 | 
|---|
| 7 |                 private enum ELevelInfo {
 | 
|---|
| 8 |                         None = 0,
 | 
|---|
| 9 |                         NonEmpty = 1,
 | 
|---|
| 10 |                         Object = 2,
 | 
|---|
| 11 |                         Array = 4,
 | 
|---|
| 12 |                 }
 | 
|---|
| 13 | 
 | 
|---|
| 14 |                 private const int levelTypeBits = 3;
 | 
|---|
| 15 |                 private const int levelBitsMask = (1 << levelTypeBits) - 1;
 | 
|---|
| 16 | 
 | 
|---|
| 17 |                 private readonly bool prettyPrint;
 | 
|---|
| 18 |                 private readonly StringBuilder stringBuilder = new StringBuilder ();
 | 
|---|
| 19 |                 private ulong currentLevelType = (long) ELevelInfo.None;
 | 
|---|
| 20 |                 private int currentLevelNumber;
 | 
|---|
| 21 | 
 | 
|---|
| 22 |                 private ELevelInfo CurrentLevelInfo {
 | 
|---|
| 23 |                         get { return (ELevelInfo) (currentLevelType & levelBitsMask); }
 | 
|---|
| 24 |                 }
 | 
|---|
| 25 | 
 | 
|---|
| 26 |                 private bool CurrentLevelIsNonEmpty {
 | 
|---|
| 27 |                         get { return (CurrentLevelInfo & ELevelInfo.NonEmpty) == ELevelInfo.NonEmpty; }
 | 
|---|
| 28 |                 }
 | 
|---|
| 29 | 
 | 
|---|
| 30 |                 private bool CurrentLevelIsArray {
 | 
|---|
| 31 |                         get { return (CurrentLevelInfo & ELevelInfo.Array) != ELevelInfo.Array; }
 | 
|---|
| 32 |                 }
 | 
|---|
| 33 | 
 | 
|---|
| 34 |                 private bool CurrentLevelIsObject {
 | 
|---|
| 35 |                         get { return (CurrentLevelInfo & ELevelInfo.Object) != ELevelInfo.Object; }
 | 
|---|
| 36 |                 }
 | 
|---|
| 37 |                 
 | 
|---|
| 38 |                 public JsonManualBuilder (bool _prettyPrint) {
 | 
|---|
| 39 |                         prettyPrint = _prettyPrint;
 | 
|---|
| 40 |                 }
 | 
|---|
| 41 | 
 | 
|---|
| 42 |                 private void NextElement () {
 | 
|---|
| 43 |                         if (CurrentLevelIsNonEmpty) {
 | 
|---|
| 44 |                                 stringBuilder.Append (',');
 | 
|---|
| 45 |                                 if (prettyPrint) {
 | 
|---|
| 46 |                                         stringBuilder.Append ('\n');
 | 
|---|
| 47 |                                 }
 | 
|---|
| 48 |                         }
 | 
|---|
| 49 |                         
 | 
|---|
| 50 |                         if (prettyPrint) {
 | 
|---|
| 51 |                                 for (int i = 1; i < currentLevelNumber; i++) {
 | 
|---|
| 52 |                                         stringBuilder.Append ('\t');
 | 
|---|
| 53 |                                 }
 | 
|---|
| 54 |                         }
 | 
|---|
| 55 | 
 | 
|---|
| 56 |                         currentLevelType = currentLevelType | (long) ELevelInfo.NonEmpty;
 | 
|---|
| 57 |                 }
 | 
|---|
| 58 | 
 | 
|---|
| 59 |                 public JsonManualBuilder OpenArray () {
 | 
|---|
| 60 |                         if (!CurrentLevelIsObject) {
 | 
|---|
| 61 |                                 // In JSON Objects we only create element separators / line breaks with NextKey
 | 
|---|
| 62 |                                 NextElement ();
 | 
|---|
| 63 |                         }
 | 
|---|
| 64 | 
 | 
|---|
| 65 |                         stringBuilder.Append ('[');
 | 
|---|
| 66 |                         openLevel (ELevelInfo.Array);
 | 
|---|
| 67 | 
 | 
|---|
| 68 |                         return this;
 | 
|---|
| 69 |                 }
 | 
|---|
| 70 | 
 | 
|---|
| 71 |                 public JsonManualBuilder OpenObject () {
 | 
|---|
| 72 |                         if (!CurrentLevelIsObject) {
 | 
|---|
| 73 |                                 // In JSON Objects we only create element separators / line breaks with NextKey
 | 
|---|
| 74 |                                 NextElement ();
 | 
|---|
| 75 |                         }
 | 
|---|
| 76 | 
 | 
|---|
| 77 |                         stringBuilder.Append ('{');
 | 
|---|
| 78 |                         openLevel (ELevelInfo.Object);
 | 
|---|
| 79 | 
 | 
|---|
| 80 |                         return this;
 | 
|---|
| 81 |                 }
 | 
|---|
| 82 | 
 | 
|---|
| 83 |                 public JsonManualBuilder NextObjectKey (string _key) {
 | 
|---|
| 84 |                         if (!CurrentLevelIsObject) {
 | 
|---|
| 85 |                                 throw new Exception("Can not start a JSON object key while not in a JSON object");
 | 
|---|
| 86 |                         }
 | 
|---|
| 87 |                         
 | 
|---|
| 88 |                         NextElement ();
 | 
|---|
| 89 |                         stringBuilder.Append ('"');
 | 
|---|
| 90 |                         stringBuilder.Append (_key);
 | 
|---|
| 91 |                         stringBuilder.Append ("\":\"");
 | 
|---|
| 92 |                         if (prettyPrint) {
 | 
|---|
| 93 |                                 stringBuilder.Append (' ');
 | 
|---|
| 94 |                         }
 | 
|---|
| 95 | 
 | 
|---|
| 96 |                         return this;
 | 
|---|
| 97 |                 }
 | 
|---|
| 98 | 
 | 
|---|
| 99 |                 public JsonManualBuilder WriteBoolean (bool _value) {
 | 
|---|
| 100 |                         if (!CurrentLevelIsObject) {
 | 
|---|
| 101 |                                 // In JSON Objects we only create element separators / line breaks with NextKey
 | 
|---|
| 102 |                                 NextElement ();
 | 
|---|
| 103 |                         }
 | 
|---|
| 104 |                         
 | 
|---|
| 105 |                         stringBuilder.Append (_value ? "true" : "false");
 | 
|---|
| 106 | 
 | 
|---|
| 107 |                         return this;
 | 
|---|
| 108 |                 }
 | 
|---|
| 109 | 
 | 
|---|
| 110 |                 public JsonManualBuilder WriteNull () {
 | 
|---|
| 111 |                         if (!CurrentLevelIsObject) {
 | 
|---|
| 112 |                                 // In JSON Objects we only create element separators / line breaks with NextKey
 | 
|---|
| 113 |                                 NextElement ();
 | 
|---|
| 114 |                         }
 | 
|---|
| 115 |                         
 | 
|---|
| 116 |                         stringBuilder.Append ("null");
 | 
|---|
| 117 | 
 | 
|---|
| 118 |                         return this;
 | 
|---|
| 119 |                 }
 | 
|---|
| 120 | 
 | 
|---|
| 121 |                 public JsonManualBuilder WriteNumber (double _value) {
 | 
|---|
| 122 |                         if (!CurrentLevelIsObject) {
 | 
|---|
| 123 |                                 // In JSON Objects we only create element separators / line breaks with NextKey
 | 
|---|
| 124 |                                 NextElement ();
 | 
|---|
| 125 |                         }
 | 
|---|
| 126 |                         
 | 
|---|
| 127 |                         stringBuilder.Append (_value.ToCultureInvariantString ());
 | 
|---|
| 128 | 
 | 
|---|
| 129 |                         return this;
 | 
|---|
| 130 |                 }
 | 
|---|
| 131 | 
 | 
|---|
| 132 |                 public JsonManualBuilder WriteString (string _value) {
 | 
|---|
| 133 |                         if (!CurrentLevelIsObject) {
 | 
|---|
| 134 |                                 // In JSON Objects we only create element separators / line breaks with NextKey
 | 
|---|
| 135 |                                 NextElement ();
 | 
|---|
| 136 |                         }
 | 
|---|
| 137 |                         
 | 
|---|
| 138 |                         if (string.IsNullOrEmpty (_value)) {
 | 
|---|
| 139 |                                 stringBuilder.Append ("\"\"");
 | 
|---|
| 140 |                                 return this;
 | 
|---|
| 141 |                         }
 | 
|---|
| 142 | 
 | 
|---|
| 143 |                         stringBuilder.EnsureCapacity (stringBuilder.Length + 2 * _value.Length);
 | 
|---|
| 144 | 
 | 
|---|
| 145 |                         stringBuilder.Append ('"');
 | 
|---|
| 146 | 
 | 
|---|
| 147 |                         foreach (char c in _value) {
 | 
|---|
| 148 |                                 switch (c) {
 | 
|---|
| 149 |                                         case '\\':
 | 
|---|
| 150 |                                         case '"':
 | 
|---|
| 151 | //                                      case '/':
 | 
|---|
| 152 |                                                 stringBuilder.Append ('\\');
 | 
|---|
| 153 |                                                 stringBuilder.Append (c);
 | 
|---|
| 154 |                                                 break;
 | 
|---|
| 155 |                                         case '\b':
 | 
|---|
| 156 |                                                 stringBuilder.Append ("\\b");
 | 
|---|
| 157 |                                                 break;
 | 
|---|
| 158 |                                         case '\t':
 | 
|---|
| 159 |                                                 stringBuilder.Append ("\\t");
 | 
|---|
| 160 |                                                 break;
 | 
|---|
| 161 |                                         case '\n':
 | 
|---|
| 162 |                                                 stringBuilder.Append ("\\n");
 | 
|---|
| 163 |                                                 break;
 | 
|---|
| 164 |                                         case '\f':
 | 
|---|
| 165 |                                                 stringBuilder.Append ("\\f");
 | 
|---|
| 166 |                                                 break;
 | 
|---|
| 167 |                                         case '\r':
 | 
|---|
| 168 |                                                 stringBuilder.Append ("\\r");
 | 
|---|
| 169 |                                                 break;
 | 
|---|
| 170 |                                         default:
 | 
|---|
| 171 |                                                 if (c < ' ') {
 | 
|---|
| 172 |                                                         stringBuilder.Append ("\\u");
 | 
|---|
| 173 |                                                         stringBuilder.Append (((int) c).ToString ("X4"));
 | 
|---|
| 174 |                                                 } else {
 | 
|---|
| 175 |                                                         stringBuilder.Append (c);
 | 
|---|
| 176 |                                                 }
 | 
|---|
| 177 | 
 | 
|---|
| 178 |                                                 break;
 | 
|---|
| 179 |                                 }
 | 
|---|
| 180 |                         }
 | 
|---|
| 181 | 
 | 
|---|
| 182 |                         stringBuilder.Append ('"');
 | 
|---|
| 183 |                         
 | 
|---|
| 184 |                         return this;
 | 
|---|
| 185 |                 }
 | 
|---|
| 186 | 
 | 
|---|
| 187 |                 private void openLevel (ELevelInfo _levelType) {
 | 
|---|
| 188 |                         currentLevelType = currentLevelType << levelTypeBits | (uint) _levelType;
 | 
|---|
| 189 |                         currentLevelNumber++;
 | 
|---|
| 190 | 
 | 
|---|
| 191 |                         if (prettyPrint) {
 | 
|---|
| 192 |                                 stringBuilder.Append ('\n');
 | 
|---|
| 193 |                         }
 | 
|---|
| 194 |                 }
 | 
|---|
| 195 | 
 | 
|---|
| 196 |                 public JsonManualBuilder CloseLevel () {
 | 
|---|
| 197 |                         char closeChar;
 | 
|---|
| 198 |                         if (CurrentLevelIsObject) {
 | 
|---|
| 199 |                                 closeChar = '}';
 | 
|---|
| 200 |                         } else if (CurrentLevelIsArray) {
 | 
|---|
| 201 |                                 closeChar = ']';
 | 
|---|
| 202 |                         } else {
 | 
|---|
| 203 |                                 throw new Exception (
 | 
|---|
| 204 |                                         "Can not CloseLevel as the current level is neither a JSON object nor a JSON array");
 | 
|---|
| 205 |                         }
 | 
|---|
| 206 | 
 | 
|---|
| 207 |                         if (prettyPrint) {
 | 
|---|
| 208 |                                 stringBuilder.Append ('\n');
 | 
|---|
| 209 |                         }
 | 
|---|
| 210 | 
 | 
|---|
| 211 |                         currentLevelNumber--;
 | 
|---|
| 212 |                         currentLevelType = currentLevelType >> levelTypeBits;
 | 
|---|
| 213 | 
 | 
|---|
| 214 |                         if (prettyPrint) {
 | 
|---|
| 215 |                                 for (int i = 1; i < currentLevelNumber; i++) {
 | 
|---|
| 216 |                                         stringBuilder.Append ('\t');
 | 
|---|
| 217 |                                 }
 | 
|---|
| 218 |                         }
 | 
|---|
| 219 | 
 | 
|---|
| 220 |                         stringBuilder.Append (closeChar);
 | 
|---|
| 221 | 
 | 
|---|
| 222 |                         return this;
 | 
|---|
| 223 |                 }
 | 
|---|
| 224 | 
 | 
|---|
| 225 |                 public override string ToString () {
 | 
|---|
| 226 |                         return stringBuilder.ToString ();
 | 
|---|
| 227 |                 }
 | 
|---|
| 228 |         }
 | 
|---|
| 229 | }
 | 
|---|