MessagePackObjectFormatter.cs
1 using MsgPack; 2 using System; 3 using System.Text; 4 5 namespace Ryujinx.Common.Utilities 6 { 7 public static class MessagePackObjectFormatter 8 { 9 public static string ToString(this MessagePackObject obj, bool pretty) 10 { 11 if (pretty) 12 { 13 return Format(obj); 14 } 15 16 return obj.ToString(); 17 } 18 19 public static string Format(MessagePackObject obj) 20 { 21 var builder = new IndentedStringBuilder(); 22 23 FormatMsgPackObj(obj, builder); 24 25 return builder.ToString(); 26 } 27 28 private static void FormatMsgPackObj(MessagePackObject obj, IndentedStringBuilder builder) 29 { 30 if (obj.IsMap || obj.IsDictionary) 31 { 32 FormatMsgPackMap(obj, builder); 33 } 34 else if (obj.IsArray || obj.IsList) 35 { 36 FormatMsgPackArray(obj, builder); 37 } 38 else if (obj.IsNil) 39 { 40 builder.Append("null"); 41 } 42 else 43 { 44 var literal = obj.ToObject(); 45 46 if (literal is String) 47 { 48 builder.AppendQuotedString(obj.AsStringUtf16()); 49 } 50 else if (literal is byte[] byteArray) 51 { 52 FormatByteArray(byteArray, builder); 53 } 54 else if (literal is MessagePackExtendedTypeObject extObject) 55 { 56 builder.Append('{'); 57 58 // Indent 59 builder.IncreaseIndent() 60 .AppendLine(); 61 62 // Print TypeCode field 63 builder.AppendQuotedString("TypeCode") 64 .Append(": ") 65 .Append(extObject.TypeCode) 66 .AppendLine(","); 67 68 // Print Value field 69 builder.AppendQuotedString("Value") 70 .Append(": "); 71 72 FormatByteArrayAsString(extObject.GetBody(), builder, true); 73 74 // Unindent 75 builder.DecreaseIndent() 76 .AppendLine(); 77 78 builder.Append('}'); 79 } 80 else 81 { 82 builder.Append(literal); 83 } 84 } 85 } 86 87 private static void FormatByteArray(byte[] arr, IndentedStringBuilder builder) 88 { 89 builder.Append("[ "); 90 91 foreach (var b in arr) 92 { 93 builder.Append("0x"); 94 builder.Append(ToHexChar(b >> 4)); 95 builder.Append(ToHexChar(b & 0xF)); 96 builder.Append(", "); 97 } 98 99 // Remove trailing comma 100 builder.Remove(builder.Length - 2, 2); 101 102 builder.Append(" ]"); 103 } 104 105 private static void FormatByteArrayAsString(byte[] arr, IndentedStringBuilder builder, bool withPrefix) 106 { 107 builder.Append('"'); 108 109 if (withPrefix) 110 { 111 builder.Append("0x"); 112 } 113 114 foreach (var b in arr) 115 { 116 builder.Append(ToHexChar(b >> 4)); 117 builder.Append(ToHexChar(b & 0xF)); 118 } 119 120 builder.Append('"'); 121 } 122 123 private static void FormatMsgPackMap(MessagePackObject obj, IndentedStringBuilder builder) 124 { 125 var map = obj.AsDictionary(); 126 127 builder.Append('{'); 128 129 // Indent 130 builder.IncreaseIndent() 131 .AppendLine(); 132 133 foreach (var item in map) 134 { 135 FormatMsgPackObj(item.Key, builder); 136 137 builder.Append(": "); 138 139 FormatMsgPackObj(item.Value, builder); 140 141 builder.AppendLine(","); 142 } 143 144 // Remove the trailing new line and comma 145 builder.TrimLastLine() 146 .Remove(builder.Length - 1, 1); 147 148 // Unindent 149 builder.DecreaseIndent() 150 .AppendLine(); 151 152 builder.Append('}'); 153 } 154 155 private static void FormatMsgPackArray(MessagePackObject obj, IndentedStringBuilder builder) 156 { 157 var arr = obj.AsList(); 158 159 builder.Append("[ "); 160 161 foreach (var item in arr) 162 { 163 FormatMsgPackObj(item, builder); 164 165 builder.Append(", "); 166 } 167 168 // Remove trailing comma 169 builder.Remove(builder.Length - 2, 2); 170 171 builder.Append(" ]"); 172 } 173 174 private static char ToHexChar(int b) 175 { 176 if (b < 10) 177 { 178 return unchecked((char)('0' + b)); 179 } 180 181 return unchecked((char)('A' + (b - 10))); 182 } 183 184 internal class IndentedStringBuilder 185 { 186 const string DefaultIndent = " "; 187 188 private int _indentCount; 189 private int _newLineIndex; 190 private readonly StringBuilder _builder; 191 192 public string IndentString { get; set; } = DefaultIndent; 193 194 public IndentedStringBuilder(StringBuilder builder) 195 { 196 _builder = builder; 197 } 198 199 public IndentedStringBuilder() 200 : this(new StringBuilder()) 201 { } 202 203 public IndentedStringBuilder(string str) 204 : this(new StringBuilder(str)) 205 { } 206 207 public IndentedStringBuilder(int length) 208 : this(new StringBuilder(length)) 209 { } 210 211 public int Length { get => _builder.Length; } 212 213 public IndentedStringBuilder IncreaseIndent() 214 { 215 _indentCount++; 216 217 return this; 218 } 219 220 public IndentedStringBuilder DecreaseIndent() 221 { 222 _indentCount--; 223 224 return this; 225 } 226 227 public IndentedStringBuilder Append(char value) 228 { 229 _builder.Append(value); 230 231 return this; 232 } 233 234 public IndentedStringBuilder Append(string value) 235 { 236 _builder.Append(value); 237 238 return this; 239 } 240 241 public IndentedStringBuilder Append(object value) 242 { 243 this.Append(value.ToString()); 244 245 return this; 246 } 247 248 public IndentedStringBuilder AppendQuotedString(string value) 249 { 250 _builder.Append('"'); 251 _builder.Append(value); 252 _builder.Append('"'); 253 254 return this; 255 } 256 257 public IndentedStringBuilder AppendLine() 258 { 259 _newLineIndex = _builder.Length; 260 261 _builder.AppendLine(); 262 263 for (int i = 0; i < _indentCount; i++) 264 _builder.Append(IndentString); 265 266 return this; 267 } 268 269 public IndentedStringBuilder AppendLine(string value) 270 { 271 _builder.Append(value); 272 273 this.AppendLine(); 274 275 return this; 276 } 277 278 public IndentedStringBuilder TrimLastLine() 279 { 280 _builder.Remove(_newLineIndex, _builder.Length - _newLineIndex); 281 282 return this; 283 } 284 285 public IndentedStringBuilder Remove(int startIndex, int length) 286 { 287 _builder.Remove(startIndex, length); 288 289 return this; 290 } 291 292 public override string ToString() 293 { 294 return _builder.ToString(); 295 } 296 } 297 } 298 }