/ src / Ryujinx.Common / Utilities / MessagePackObjectFormatter.cs
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  }