@@ -38,12 +38,15 @@ public static void PrepUtils()
38
38
int subticks = 0 ;
39
39
Program . SlowTickEvent += ( ) =>
40
40
{
41
- GC . Collect ( GC . MaxGeneration , GCCollectionMode . Optimized , false , false ) ;
42
41
if ( subticks ++ > 20 )
43
42
{
44
43
subticks = 0 ;
45
44
GC . Collect ( GC . MaxGeneration , GCCollectionMode . Forced , false , true ) ;
46
45
}
46
+ else
47
+ {
48
+ QuickGC ( ) ;
49
+ }
47
50
} ;
48
51
new Thread ( TickLoop ) . Start ( ) ;
49
52
}
@@ -253,7 +256,7 @@ public static CancellationTokenSource TimedCancel(TimeSpan time)
253
256
public static async Task SendJson ( this WebSocket socket , JObject obj , TimeSpan maxDuration )
254
257
{
255
258
using CancellationTokenSource cancel = TimedCancel ( maxDuration ) ;
256
- await socket . SendAsync ( obj . ToString ( Formatting . None ) . EncodeUTF8 ( ) , WebSocketMessageType . Text , true , cancel . Token ) ;
259
+ await socket . SendAsync ( JsonToByteArray ( obj ) , WebSocketMessageType . Text , true , cancel . Token ) ;
257
260
}
258
261
259
262
/// <summary>Equivalent to <see cref="Task.WhenAny(IEnumerable{Task})"/> but doesn't break on an empty list.</summary>
@@ -378,6 +381,52 @@ public static JObject SortByKey<TSortable>(this JObject obj, Func<string, TSorta
378
381
return JObject . FromObject ( obj . Properties ( ) . OrderBy ( p => sort ( p . Name ) ) . ToDictionary ( p => p . Name , p => p . Value ) ) ;
379
382
}
380
383
384
+ /// <summary>(Experimental) aggressively simply low-mem ToString for JSON data. Dense, spaceless, unformatted.</summary>
385
+ public static void ToStringFast ( this JToken jval , StringBuilder builder )
386
+ {
387
+ if ( jval is JObject jobj )
388
+ {
389
+ builder . Append ( '{' ) ;
390
+ if ( jobj . Count > 0 )
391
+ {
392
+ foreach ( ( string key , JToken val ) in jobj )
393
+ {
394
+ builder . Append ( '"' ) . Append ( EscapeJsonString ( key ) ) . Append ( "\" :" ) ;
395
+ val . ToStringFast ( builder ) ;
396
+ builder . Append ( ',' ) ;
397
+ }
398
+ builder . Length -- ;
399
+ }
400
+ builder . Append ( '}' ) ;
401
+ }
402
+ else if ( jval is JArray jarr )
403
+ {
404
+ builder . Append ( '[' ) ;
405
+ if ( jarr . Count > 0 )
406
+ {
407
+ foreach ( JToken val in jarr )
408
+ {
409
+ val . ToStringFast ( builder ) ;
410
+ builder . Append ( ',' ) ;
411
+ }
412
+ builder . Length -- ;
413
+ }
414
+ builder . Append ( ']' ) ;
415
+ }
416
+ else
417
+ {
418
+ builder . Append ( jval . ToString ( Formatting . None ) ) ;
419
+ }
420
+ }
421
+
422
+ /// <summary>Converts a <see cref="JObject"/> to a UTF-8 string byte array.</summary>
423
+ public static byte [ ] JsonToByteArray ( JObject jdata )
424
+ {
425
+ StringBuilder builder = new ( 1024 ) ;
426
+ jdata . ToStringFast ( builder ) ;
427
+ return builder . ToString ( ) . EncodeUTF8 ( ) ;
428
+ }
429
+
381
430
/// <summary>Gives a clean standard 4-space serialize of this <see cref="JObject"/>.</summary>
382
431
public static string SerializeClean ( this JObject jobj )
383
432
{
@@ -391,7 +440,6 @@ public static string SerializeClean(this JObject jobj)
391
440
serializer . Serialize ( jw , jobj ) ;
392
441
jw . Flush ( ) ;
393
442
return sw . ToString ( ) + Environment . NewLine ;
394
-
395
443
}
396
444
397
445
public static async Task YieldJsonOutput ( this HttpContext context , WebSocket socket , int status , JObject obj )
@@ -403,7 +451,7 @@ public static async Task YieldJsonOutput(this HttpContext context, WebSocket soc
403
451
await socket . CloseAsync ( WebSocketCloseStatus . NormalClosure , null , cancel . Token ) ;
404
452
return ;
405
453
}
406
- byte [ ] resp = obj . ToString ( Formatting . None ) . EncodeUTF8 ( ) ;
454
+ byte [ ] resp = JsonToByteArray ( obj ) ;
407
455
context . Response . ContentType = "application/json" ;
408
456
context . Response . StatusCode = status ;
409
457
context . Response . ContentLength = resp . Length ;
@@ -419,7 +467,7 @@ public static JObject ErrorObj(string message, string error_id)
419
467
420
468
public static ByteArrayContent JSONContent ( JObject jobj )
421
469
{
422
- ByteArrayContent content = new ( jobj . ToString ( Formatting . None ) . EncodeUTF8 ( ) ) ;
470
+ ByteArrayContent content = new ( JsonToByteArray ( jobj ) ) ;
423
471
content . Headers . ContentType = new MediaTypeHeaderValue ( "application/json" ) ;
424
472
return content ;
425
473
}
@@ -971,6 +1019,12 @@ public static string GetLocalIPAddress()
971
1019
return null ;
972
1020
}
973
1021
1022
+ /// <summary>Encourage the Garbage Collector to clean up memory.</summary>
1023
+ public static void QuickGC ( )
1024
+ {
1025
+ GC . Collect ( GC . MaxGeneration , GCCollectionMode . Optimized , false , false ) ;
1026
+ }
1027
+
974
1028
/// <summary>Cause an immediate aggressive RAM cleanup.</summary>
975
1029
public static void CleanRAM ( )
976
1030
{
0 commit comments