com.amanotes.gdk 0.2.69 → 0.2.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/Editor/AmaGDKEditor.cs +2 -1
  3. package/Editor/Utils/AmaGDKEditor.ExtractVersion.cs +6 -0
  4. package/Extra/AmaGDKInstaller.unitypackage +0 -0
  5. package/Extra/CheckDiskSpace.unitypackage +0 -0
  6. package/Extra/Consent.unitypackage +0 -0
  7. package/Extra/ForceUpdate.unitypackage +0 -0
  8. package/Extra/LegacyGDKUpdateHelper.unitypackage +0 -0
  9. package/Extra/PostProcessor.unitypackage +0 -0
  10. package/Packages/AmaGDKConfig.unitypackage +0 -0
  11. package/Packages/AmaGDKExample.unitypackage +0 -0
  12. package/Packages/AmaGDKTest.unitypackage +0 -0
  13. package/Packages/AppsFlyerAdapter.PurchaseConnector.unitypackage +0 -0
  14. package/Packages/AppsFlyerAdapter.unitypackage +0 -0
  15. package/Packages/FirebaseAnalyticsAdapter.unitypackage +0 -0
  16. package/Packages/FirebaseRemoteConfigAdapter.unitypackage +0 -0
  17. package/Packages/IronSourceAdapter.unitypackage +0 -0
  18. package/Packages/MaxAdNetworkAdapter.unitypackage +0 -0
  19. package/Packages/RevenueCatAdapter.unitypackage +0 -0
  20. package/Packages/SqliteAnalyticsAdapter.unitypackage +0 -0
  21. package/Runtime/Ad/AdLogic.cs +6 -3
  22. package/Runtime/AmaGDK.Analytics.cs +45 -38
  23. package/Runtime/AmaGDK.Device.cs +4 -4
  24. package/Runtime/AmaGDK.RemoteConfig.cs +3 -1
  25. package/Runtime/AmaGDK.cs +4 -2
  26. package/Runtime/AnalyticQualityAsset.cs +16 -9
  27. package/Runtime/AudioToolkit/Plugins/AudioDeviceDetector.cs +13 -12
  28. package/Runtime/Core/GDKPool.cs +83 -17
  29. package/Runtime/Internal/AmaGDK.Internal.cs +2 -5
  30. package/Runtime/Internal/AmaGDK.Utils.cs +32 -81
  31. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.70 - 2024-08-02]
4
+ - [Fix] onAdResult call multiple times
5
+ - [Dev] Show SQLite analytics in GDK Inspector
6
+ - [Dev] Reduce GC by reuse StringBuilders
7
+
3
8
  ## [0.2.69 - 2024-07-26]
4
9
  - [Fix] disable Max adapter
5
10
  - [Dev] Update OnAdResult callback
@@ -52,7 +52,8 @@ namespace Amanotes.Editor
52
52
  APPSFLYER,
53
53
  IRONSOURCE,
54
54
  MAX,
55
- REVENUECAT
55
+ REVENUECAT,
56
+ SQLITE_ANALYTICS,
56
57
  };
57
58
 
58
59
  public void OnEnable()
@@ -185,6 +185,7 @@ namespace Amanotes.Editor
185
185
  case IRONSOURCE: return IronSource();
186
186
  case MAX: return Max();
187
187
  case REVENUECAT: return RevenueCat();
188
+ case SQLITE_ANALYTICS: return SqliteAnalytics();
188
189
  default:
189
190
  Debug.LogWarning($"Unsupported sdkID <{sdkId}>");
190
191
  break;
@@ -329,6 +330,11 @@ namespace Amanotes.Editor
329
330
  return version?.Trim();
330
331
  }
331
332
 
333
+ internal static string SqliteAnalytics()
334
+ {
335
+ return "1.0.0";
336
+ }
337
+
332
338
  internal static string RevenueCat()
333
339
  {
334
340
  string[] files = Directory.GetFiles("Assets", "PurchasesWrapper.java", SearchOption.AllDirectories);
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -320,9 +320,7 @@ namespace Amanotes.Core.Internal
320
320
 
321
321
  _showState = isSuccess ? ShowAdsState.ShowSuccess : ShowAdsState.ShowFail;
322
322
 
323
- // Extra callback
324
- context.showFinishAt = Time.realtimeSinceStartup;
325
- context.onAdResult?.Invoke(isSuccess);
323
+
326
324
 
327
325
  var localData = Ads.localData;
328
326
  var stat = _isInterstitial ? localData.interstitial : localData.reward;
@@ -341,8 +339,13 @@ namespace Amanotes.Core.Internal
341
339
  TriggerOnAfterAdShow();
342
340
 
343
341
  Log($"[Ad] {adType} ShowAdRoutineCompleted: {isSuccess}\n{JsonUtility.ToJson(Ads.context)}");
342
+
343
+ // Extra callback
344
+ context.showFinishAt = Time.realtimeSinceStartup;
345
+ var resultCallback = context.onAdResult;
344
346
  Ads.context = null;
345
347
  _showState = ShowAdsState.None;
348
+ resultCallback?.Invoke(isSuccess);
346
349
  }
347
350
 
348
351
  private void TriggerBeforeAdShow()
@@ -68,8 +68,9 @@ namespace Amanotes.Core
68
68
  doNotIncreaseCounter = false;
69
69
  isLoggedImmediately = false;
70
70
  overrideConfig = false;
71
- ignoreAdapterIds = null;
72
- allowAdapterIds = null;
71
+
72
+ GDKPool.ReturnAndNullize(ref ignoreAdapterIds);
73
+ GDKPool.ReturnAndNullize(ref allowAdapterIds);
73
74
  _detail = null;
74
75
  }
75
76
 
@@ -307,9 +308,8 @@ namespace Amanotes.Core
307
308
  internal static readonly List<AnalyticsAdapter> listAdapters = new List<AnalyticsAdapter>();
308
309
  internal static readonly AnalyticsData localData = new AnalyticsData().LoadJsonFromFile().BuildCache();
309
310
  internal static SessionStat sessionStat;
310
- internal static readonly GDKPool<StringBuilder> sbPool = new GDKPool<StringBuilder>(3);
311
- internal static readonly GDKPool<EventParams> epPool = new GDKPool<EventParams>(1);
312
-
311
+ internal static readonly GDKPool.Pool<EventParams> epPool = new GDKPool.Pool<EventParams>(1);
312
+
313
313
  internal static readonly Dictionary<string, string> reservedPrefixes = new Dictionary<string, string>()
314
314
  {
315
315
  { "firebase_", "frb_" },
@@ -515,32 +515,31 @@ namespace Amanotes.Core
515
515
  {
516
516
  Profiler.BeginSample("AmaGDK.Analytics.NormalizeEventName()");
517
517
  {
518
- var sb = sbPool.Get();
519
- sb.Clear();
520
- NormalizeKeyString(ref eventName, sb, dryRun);
518
+ var warningLog = GDKPool.GetStringBuilder(8);
519
+ NormalizeKeyString(ref eventName, warningLog, dryRun);
521
520
  if (reservedEvents.Contains(eventName))
522
- sb.AppendLine($" - EventName <{eventName}> is reserved and should not be used");
523
- if (sb.Length > 0 && dryRun) LogWarningOnce($"NormalizeEventName <{eventName}> warnings:\n{sb}\n", $"NormalizeEventName<{eventName}>");
524
- sbPool.Return(sb);
521
+ {
522
+ warningLog.AppendLine($" - EventName <{eventName}> is reserved and should not be used");
523
+ }
524
+ if (warningLog.Length > 0 && dryRun) LogWarningOnce($"NormalizeEventName <{eventName}> warnings:\n{warningLog}\n", $"NormalizeEventName<{eventName}>");
525
+ GDKPool.Return(warningLog);
525
526
  }
526
527
  Profiler.EndSample();
527
528
  }
528
529
 
529
530
  internal static void NormalizeKeyString(ref string key, StringBuilder warningLog, bool dryRun)
530
531
  {
532
+ if (string.IsNullOrWhiteSpace(key))
533
+ {
534
+ LogWarning($" - Name should not be empty");
535
+ return;
536
+ }
537
+
531
538
  Profiler.BeginSample("AmaGDK.Analytics.NormalizeKeyString()");
532
539
  {
533
- if (string.IsNullOrWhiteSpace(key))
534
- {
535
- LogWarning($" - Name should not be empty");
536
- Profiler.EndSample();
537
- return;
538
- }
539
-
540
- var tempLog = Config.common.logLevel == LogLevel.None ? null : sbPool.Get();
541
- tempLog?.Clear();
542
- var tempKey = sbPool.Get();
543
- tempKey.Clear();
540
+ var willLog = Config.common.logLevel != LogLevel.None;
541
+ var tempLog = willLog ? GDKPool.GetStringBuilder() : null;
542
+ var tempKey = GDKPool.GetStringBuilder();
544
543
 
545
544
  var hasChanged = false;
546
545
  var trimKey = key.Trim();
@@ -563,16 +562,16 @@ namespace Amanotes.Core
563
562
  }
564
563
  }
565
564
 
566
- if (hasChanged)
565
+ if (hasChanged && willLog)
567
566
  {
568
- tempLog?.Append(" - Name may only contain alphanumeric characters and underscores");
567
+ tempLog.Append(" - Name may only contain alphanumeric characters and underscores");
569
568
  }
570
569
 
571
570
  if (tempKey.Length > 0 && !IsAlphabet(tempKey[0]))
572
571
  {
573
572
  hasChanged = true;
574
573
  tempKey.Insert(0, 'E');
575
- tempLog?.Append($" - Name must start with a letter: <{key}>");
574
+ if (willLog) tempLog.Append($" - Name must start with a letter: <{key}>");
576
575
  }
577
576
 
578
577
  foreach (var prefix in reservedPrefixes)
@@ -581,13 +580,13 @@ namespace Amanotes.Core
581
580
  hasChanged = true;
582
581
  tempKey.Remove(0, prefix.Key.Length);
583
582
  tempKey.Insert(0, prefix.Value);
584
- tempLog?.AppendLine($" - <{key}> starts with reserved prefix <{prefix}>");
583
+ if (willLog) tempLog.AppendLine($" - <{key}> starts with reserved prefix <{prefix}>");
585
584
  }
586
585
 
587
586
  if (trimKey.Length > 40)
588
587
  {
589
588
  hasChanged = true;
590
- tempLog?.AppendLine($" - Name is too long ({key.Length} > 40): <{key}> ");
589
+ if (willLog) tempLog.AppendLine($" - Name is too long ({key.Length} > 40): <{key}> ");
591
590
  tempKey.Length = 40;
592
591
  }
593
592
 
@@ -598,8 +597,8 @@ namespace Amanotes.Core
598
597
  if (hasChanged) key = tempKey.ToString();
599
598
  }
600
599
 
601
- if (tempLog != null) sbPool.Return(tempLog);
602
- sbPool.Return(tempKey);
600
+ GDKPool.Return(tempLog);
601
+ GDKPool.Return(tempKey);
603
602
  }
604
603
  Profiler.EndSample();
605
604
  }
@@ -702,9 +701,17 @@ namespace Amanotes.Core
702
701
  adapters = adapterIDs;
703
702
  }
704
703
 
705
- public string ToTsv()
704
+ public void AppendAsTSV(StringBuilder sb)
706
705
  {
707
- return $"{eventNameToSend}\t{string.Join(",", adapters)}\t{parameters}";
706
+ sb.Append(eventNameToSend);
707
+ sb.Append("\t");
708
+ for (var i = 0; i < adapters.Count; i++)
709
+ {
710
+ sb.Append(adapters[i]);
711
+ if (i < adapters.Count - 1) sb.Append(",");
712
+ }
713
+ sb.Append("\t");
714
+ sb.Append(parameters);
708
715
  }
709
716
  }
710
717
 
@@ -856,8 +863,8 @@ namespace Amanotes.Core
856
863
  {
857
864
  private const string TSV_HEADER_ROW = "EventName\tAdapters\tParameters";
858
865
  private readonly string fileName = nameof(SessionStat) + DateTime.Now.ToString("_yyMMdd_HHmmss") + ".tsv";
859
- private StringBuilder contents = new StringBuilder();
860
866
  private bool isFirstSave = true;
867
+ private StringBuilder contents = new StringBuilder(8192);
861
868
  private readonly ConcurrentQueue<EventStat> lastFrameEventStats = new ConcurrentQueue<EventStat>();
862
869
  internal readonly ConcurrentQueue<EventStat> savedEventStatsInLastFrame = new ConcurrentQueue<EventStat>();
863
870
 
@@ -878,10 +885,10 @@ namespace Amanotes.Core
878
885
  }
879
886
  while (lastFrameEventStats.TryDequeue(out var stat))
880
887
  {
881
- contents.AppendLine(stat.ToTsv());
888
+ stat.AppendAsTSV(contents);
882
889
  savedEventStatsInLastFrame.Enqueue(stat);
883
890
  }
884
-
891
+
885
892
  GDKFileUtils.SaveAppend(fileName, contents.ToString());
886
893
  }
887
894
 
@@ -972,7 +979,7 @@ namespace Amanotes.Core
972
979
  }
973
980
 
974
981
  eventData.overrideConfig = true;
975
- eventData.ignoreAdapterIds ??= new HashSet<string>();
982
+ eventData.ignoreAdapterIds ??= GDKPool.GetHashSet();
976
983
 
977
984
  foreach (string mId in analyticIds)
978
985
  {
@@ -998,7 +1005,7 @@ namespace Amanotes.Core
998
1005
  }
999
1006
 
1000
1007
  eventData.overrideConfig = true;
1001
- eventData.allowAdapterIds ??= new HashSet<string>();
1008
+ eventData.allowAdapterIds ??= GDKPool.GetHashSet();
1002
1009
 
1003
1010
  foreach (string mId in analyticIds)
1004
1011
  {
@@ -1080,12 +1087,12 @@ namespace Amanotes.Core
1080
1087
  {
1081
1088
  string eventName = (ap as EventParams)?.eventName;
1082
1089
 
1083
- var sb = sbPool.Get();
1090
+ var sb = GDKPool.GetStringBuilder();
1084
1091
  sb.Clear();
1085
1092
  NormalizeKeyString(ref key, sb, AmaGDK.Config.analytics.normalizeEventName);
1086
1093
 
1087
1094
  if (sb.Length > 0) LogWarningOnce($"Warning for event <{eventName}>, param <{key}>:\n{sb}", $"NormalizeParamKey <{eventName}>");
1088
- sbPool.Return(sb);
1095
+ GDKPool.Return(sb);
1089
1096
 
1090
1097
  if (string.IsNullOrWhiteSpace(key))
1091
1098
  {
@@ -67,16 +67,16 @@ namespace Amanotes.Core.Internal
67
67
 
68
68
  if (AmaGDK.Config.common.logLevel == LogLevel.Verbose)
69
69
  {
70
- var stringBuilder = new StringBuilder();
70
+ var stringBuilder = GDKPool.GetStringBuilder();
71
71
  stringBuilder.Append("Device info:");
72
72
  stringBuilder.AppendLine("DeviceSpec: " + result);
73
73
  stringBuilder.AppendLine("systemMemorySize: " + SystemInfo.systemMemorySize);
74
74
  stringBuilder.AppendLine("processorCount: " + SystemInfo.processorCount);
75
75
  stringBuilder.AppendLine("processorFrequency: " + SystemInfo.processorFrequency);
76
- stringBuilder.AppendLine("graphicsMemorySize: " + SystemInfo.graphicsMemorySize);
77
-
78
- Logging.Log(stringBuilder.ToString());
76
+ stringBuilder.AppendLine("graphicsMemorySize: " + SystemInfo.graphicsMemorySize);
77
+ Logging.Log(GDKPool.ReturnString(stringBuilder));
79
78
  }
79
+
80
80
  return result;
81
81
  }
82
82
 
@@ -214,7 +214,7 @@ namespace Amanotes.Core
214
214
  Profiler.BeginSample("AmaGDK.RemoteConfig.SaveCache()");
215
215
 
216
216
  _fetchState = FetchState.SaveCache;
217
- StringBuilder sb = new StringBuilder();
217
+ StringBuilder sb = GDKPool.GetStringBuilder(8);
218
218
 
219
219
  //Add embedded remote config version to force load from embedded config after app update
220
220
  sb.AppendLine($"{VERSION_PREFIX}{EmbedVersion}");
@@ -226,6 +226,8 @@ namespace Amanotes.Core
226
226
 
227
227
  string filePath = GDKFileUtils.GetPath(fileName);
228
228
  GDKFileUtils.Save(filePath, sb.ToString());
229
+ GDKPool.Return(sb);
230
+
229
231
  Log($"[RemoteConfig] Saved remote config to {filePath}");
230
232
  Profiler.EndSample();
231
233
  NextState();
package/Runtime/AmaGDK.cs CHANGED
@@ -17,7 +17,7 @@ namespace Amanotes.Core
17
17
  {
18
18
  public partial class AmaGDK : MonoBehaviour
19
19
  {
20
- public const string VERSION = "0.2.69";
20
+ public const string VERSION = "0.2.70";
21
21
 
22
22
  internal static Status _status = Status.None;
23
23
  private static ConfigAsset _config = null;
@@ -176,7 +176,7 @@ namespace Amanotes.Core
176
176
  }
177
177
 
178
178
  var startTime = Time.realtimeSinceStartup;
179
- var sb = new StringBuilder();
179
+ var sb = GDKPool.GetStringBuilder();
180
180
 
181
181
  InitModules();
182
182
 
@@ -211,6 +211,7 @@ namespace Amanotes.Core
211
211
 
212
212
  sb.Append($"\nMapping UserId: {User.UserId}\n");
213
213
  Debug.Log($"AmaGDK v{VERSION} | {READY} in {initDuration:#0.00}s\n\n{sb}");
214
+ GDKPool.Return(sb);
214
215
  }
215
216
 
216
217
  private void ConfigGeoLocation()
@@ -354,6 +355,7 @@ namespace Amanotes.Core
354
355
  public const string IRONSOURCE = "IronSource";
355
356
  public const string MAX = "Max";
356
357
  public const string REVENUECAT = "RevenueCat";
358
+ public const string SQLITE_ANALYTICS = "SqliteAnalytics";
357
359
  }
358
360
  }
359
361
 
@@ -1,3 +1,4 @@
1
+ using Amanotes.Core.Internal;
1
2
  using System;
2
3
  using System.Collections.Generic;
3
4
  using System.Linq;
@@ -82,14 +83,15 @@ namespace Amanotes.Core
82
83
 
83
84
  internal void ValidateParams(string eventName, Dictionary<string, object> eventParams)
84
85
  {
85
- StringBuilder logBuilder = new StringBuilder();
86
+ var logBuilder = GDKPool.GetStringBuilder();
87
+
86
88
  logBuilder.AppendLine($"Analytics quality violation found for event <{eventName}>");
87
- bool isValid = true;
88
- HashSet<string> checkedParameters = new HashSet<string>();
89
+ var isValid = true;
90
+ HashSet<string> checkedParameters = GDKPool.GetHashSet();
89
91
 
90
92
  foreach (var kvp in DicParam)
91
93
  {
92
- if (!eventParams.ContainsKey(kvp.Key))
94
+ if (!eventParams.TryGetValue(kvp.Key, out object paramValue))
93
95
  {
94
96
  if (!kvp.Value.isOptional)
95
97
  {
@@ -99,8 +101,6 @@ namespace Amanotes.Core
99
101
  continue;
100
102
  }
101
103
 
102
- object paramValue = eventParams[kvp.Key];
103
-
104
104
  if (!ValidateParamType(paramValue, kvp.Value.type))
105
105
  {
106
106
  isValid = false;
@@ -117,8 +117,11 @@ namespace Amanotes.Core
117
117
  isValid = false;
118
118
  logBuilder.AppendLine($"[+] {parameter.Key}");
119
119
  }
120
-
120
+
121
121
  if (!isValid) LogWarningOnce(logBuilder.ToString());
122
+
123
+ GDKPool.Return(logBuilder);
124
+ GDKPool.Return(checkedParameters);
122
125
  }
123
126
 
124
127
  private bool ValidateParamType(object paramValue, ParamType paramType)
@@ -179,7 +182,7 @@ namespace Amanotes.Core
179
182
  [ContextMenu("Validate Unique EventNames")]
180
183
  private void ValidateUniqueEventNames()
181
184
  {
182
- HashSet<string> eventNames = new HashSet<string>();
185
+ HashSet<string> eventNames = GDKPool.GetHashSet();
183
186
 
184
187
  lstEvent = lstEvent.OrderBy(evt => evt.eventName).ToList();
185
188
 
@@ -189,7 +192,7 @@ namespace Amanotes.Core
189
192
 
190
193
  if (!eventNames.Add(eventQuality.eventName)) Debug.LogWarning($"Duplicate event name found: {eventQuality.eventName}. Event names must be unique.");
191
194
 
192
- HashSet<string> paramNames = new HashSet<string>();
195
+ HashSet<string> paramNames = GDKPool.GetHashSet();
193
196
 
194
197
  eventQuality.lstParam = eventQuality.lstParam.OrderBy(param => param.paramName).ToList();
195
198
 
@@ -199,7 +202,11 @@ namespace Amanotes.Core
199
202
 
200
203
  if (!paramNames.Add(param.paramName)) Debug.LogWarning($"Duplicate parameter name found in event {eventQuality.eventName}: {param.paramName}. Parameter names must be unique within an event.");
201
204
  }
205
+
206
+ GDKPool.Return(paramNames);
202
207
  }
208
+
209
+ GDKPool.Return(eventNames);
203
210
  EditorUtility.SetDirty(this);
204
211
  }
205
212
  #endif
@@ -86,15 +86,13 @@ namespace Amanotes.Core
86
86
 
87
87
  private void OnAudioDeviceAdded(string param)
88
88
  {
89
- if (string.IsNullOrEmpty(param))
90
- return;
89
+ if (string.IsNullOrEmpty(param)) return;
91
90
  onChanged?.Invoke(param);
92
91
  }
93
92
 
94
93
  private void OnAudioDeviceRemoved(string param)
95
94
  {
96
- if (string.IsNullOrEmpty(param))
97
- return;
95
+ if (string.IsNullOrEmpty(param)) return;
98
96
  onChanged?.Invoke(param);
99
97
  }
100
98
 
@@ -170,15 +168,18 @@ namespace Amanotes.Core
170
168
  }
171
169
 
172
170
  StringBuilder sb = null;
173
-
174
- for (int i = 0; i < str.Length; i++)
171
+ for (var i = 0; i < str.Length; i++)
175
172
  {
176
173
  char ch = str[i];
177
174
 
178
175
  if (char.IsSurrogate(ch))
179
176
  {
180
- sb ??= new StringBuilder(str, 0, i, str.Length);
181
-
177
+ if (sb == null)
178
+ {
179
+ sb = GDKPool.GetStringBuilder();
180
+ sb.Append(str, 0, i);
181
+ }
182
+
182
183
  sb.Append(replacementCharacter);
183
184
 
184
185
  // If there is a high+low surrogate, skip the low surrogate
@@ -187,13 +188,13 @@ namespace Amanotes.Core
187
188
  i++;
188
189
  }
189
190
  }
190
- else if (sb != null)
191
+ else
191
192
  {
192
- sb.Append(ch);
193
+ sb?.Append(ch);
193
194
  }
194
195
  }
195
-
196
- return sb == null ? str : sb.ToString();
196
+
197
+ return sb == null ? str : GDKPool.ReturnString(sb);
197
198
  }
198
199
  }
199
200
 
@@ -1,4 +1,7 @@
1
+ using System;
1
2
  using System.Collections.Concurrent;
3
+ using System.Collections.Generic;
4
+ using System.Text;
2
5
 
3
6
  namespace Amanotes.Core.Internal
4
7
  {
@@ -6,32 +9,95 @@ namespace Amanotes.Core.Internal
6
9
  {
7
10
  void Reset();
8
11
  }
9
-
10
- public class GDKPool<T> where T : new()
12
+
13
+ public static class GDKPool
11
14
  {
12
- private readonly ConcurrentQueue<T> _pool;
15
+ internal class Pool<T> where T : new()
16
+ {
17
+ private readonly ConcurrentQueue<T> _pool;
18
+ public Pool(int initialCapacity = 4)
19
+ {
20
+ _pool = new ConcurrentQueue<T>();
21
+ for (var i = 0; i < initialCapacity; i++)
22
+ {
23
+ _pool.Enqueue(new T());
24
+ }
25
+ }
26
+
27
+ public Pool(int initialCapacity, Func<T> newT)
28
+ {
29
+ _pool = new ConcurrentQueue<T>();
30
+ for (var i = 0; i < initialCapacity; i++)
31
+ {
32
+ _pool.Enqueue(newT());
33
+ }
34
+ }
35
+
36
+ public T Get()
37
+ {
38
+ if (!_pool.TryDequeue(out var obj)) return new T();
39
+ if (obj is IPoolItem poolItem) poolItem.Reset();
40
+ return obj;
41
+ }
42
+
43
+ public void Return(T obj)
44
+ {
45
+ _pool.Enqueue(obj);
46
+ }
13
47
 
14
- public GDKPool(int initialCapacity)
48
+ public int Count => _pool.Count;
49
+ }
50
+ private static readonly Pool<StringBuilder> sbPool1KB = new Pool<StringBuilder>(8, () => new StringBuilder(1024));
51
+ private static readonly Pool<StringBuilder> sbPool8KB = new Pool<StringBuilder>(8, () => new StringBuilder(8*1024));
52
+ private static readonly Pool<HashSet<string>> hashsetPool = new Pool<HashSet<string>>(4);
53
+
54
+ public static StringBuilder GetStringBuilder(int capacityInKb = 1)
55
+ {
56
+ StringBuilder result = capacityInKb <= 1 ? sbPool1KB.Get() : sbPool8KB.Get();
57
+ result.Clear();
58
+ return result;
59
+ }
60
+
61
+ public static HashSet<string> GetHashSet()
62
+ {
63
+ HashSet<string> result = hashsetPool.Get();
64
+ result.Clear();
65
+ return result;
66
+ }
67
+
68
+ public static string ReturnString(StringBuilder sb)
15
69
  {
16
- _pool = new ConcurrentQueue<T>();
17
- for (int i = 0; i < initialCapacity; i++)
70
+ if (sb == null) return null;
71
+ var result = sb.ToString();
72
+ Return(sb);
73
+ return result;
74
+ }
75
+
76
+ public static void Return(StringBuilder sb)
77
+ {
78
+ if (sb == null) return;
79
+ int capacity = sb.Capacity;
80
+
81
+ if (capacity == 1024)
18
82
  {
19
- _pool.Enqueue(new T());
83
+ sbPool1KB.Return(sb);
84
+ return;
20
85
  }
86
+
87
+ sbPool8KB.Return(sb);
21
88
  }
22
-
23
- public T Get()
89
+
90
+ public static void Return(HashSet<string> hashset)
24
91
  {
25
- if (!_pool.TryDequeue(out var obj)) return new T();
26
- if (obj is IPoolItem poolItem) poolItem.Reset();
27
- return obj;
92
+ if (hashset == null) return;
93
+ hashsetPool.Return(hashset);
28
94
  }
29
-
30
- public void Return(T obj)
95
+
96
+ public static void ReturnAndNullize(ref HashSet<string> hashset)
31
97
  {
32
- _pool.Enqueue(obj);
98
+ if (hashset == null) return;
99
+ hashsetPool.Return(hashset);
100
+ hashset = null;
33
101
  }
34
-
35
- public int Count => _pool.Count;
36
102
  }
37
103
  }
@@ -68,16 +68,13 @@ namespace Amanotes.Core.Internal
68
68
  {
69
69
  public static Action<string> logNonfatalErrorHook = null;
70
70
  private static readonly HashSet<string> nonFatalDict = new HashSet<string>();
71
-
72
- public static readonly StringBuilder tmpWarningSB = new StringBuilder();
73
71
  public static readonly HashSet<string> logWarningDict = new HashSet<string>();
74
72
 
75
73
  public static void LogWarningOnce(string message, string key = null)
76
74
  {
77
75
  if (Config.common.logLevel < LogLevel.Warning) return;
78
76
  if (string.IsNullOrEmpty(key)) key = message;
79
- if (logWarningDict.Contains(key)) return;
80
- logWarningDict.Add(key);
77
+ if (!logWarningDict.Add(key)) return;
81
78
  Debug.LogWarning($"AmaGDK {message}");
82
79
  }
83
80
  public static void LogWarning(string message)
@@ -133,7 +130,7 @@ namespace Amanotes.Core.Internal
133
130
  }
134
131
 
135
132
  AmaGDK[] result = Resources.FindObjectsOfTypeAll<AmaGDK>();
136
- for (int i = 0; i < result.Length; i++)
133
+ for (var i = 0; i < result.Length; i++)
137
134
  {
138
135
  if (result[i] == null) continue;
139
136
 
@@ -1,6 +1,5 @@
1
1
  using System;
2
2
  using System.Collections;
3
- using System.Collections.Concurrent;
4
3
  using System.Collections.Generic;
5
4
  using System.IO;
6
5
  using System.Linq;
@@ -15,8 +14,6 @@ namespace Amanotes.Core.Internal
15
14
  {
16
15
  public static partial class GDKUtils
17
16
  {
18
-
19
-
20
17
  public static bool IsFromTemplate(string formattedString, string template)
21
18
  {
22
19
  // Split the original template into segments based on the positions of placeholders
@@ -271,7 +268,7 @@ namespace Amanotes.Core.Internal
271
268
 
272
269
  public static void ClearCacheData()
273
270
  {
274
- var sb = new StringBuilder();
271
+ var sb = GDKPool.GetStringBuilder();
275
272
  sb.AppendLine("[AmaGDK] All cached data & stats cleared");
276
273
 
277
274
  if (!Directory.Exists(basePath))
@@ -408,16 +405,16 @@ namespace Amanotes.Core.Internal
408
405
  return DictionaryToJson(new Dictionary<string, object>
409
406
  { { key, value } });
410
407
  }
411
-
408
+
412
409
  public static string DictionaryToJson(IDictionary inputDict, bool format = false, bool checkJsonInString = true)
413
410
  {
414
411
  if (inputDict == null) return "{}";
415
412
 
416
413
  Profiler.BeginSample("AmaGDK.JsonUtils.DictionaryToJson()");
417
- var jsonSb = new StringBuilder();
414
+ var jsonSb = GDKPool.GetStringBuilder(8);
418
415
  jsonSb.Append("{");
419
416
  bool isFirstElement = true;
420
-
417
+
421
418
  foreach (DictionaryEntry entry in inputDict)
422
419
  {
423
420
  if (entry.Key == null || string.IsNullOrEmpty(entry.Key.ToString())) continue;
@@ -432,9 +429,9 @@ namespace Amanotes.Core.Internal
432
429
  jsonSb.AppendFormat("{0}\"{1}\":", format ? "\n" : "", key);
433
430
  AppendJsonValue(ref jsonSb, key, value, format, checkJsonInString);
434
431
  }
435
-
432
+
436
433
  jsonSb.AppendFormat("{0}{1}", format ? "\n" : "", "}");
437
- string result = jsonSb.ToString();
434
+ var result = GDKPool.ReturnString(jsonSb);
438
435
  Profiler.EndSample();
439
436
 
440
437
  return result;
@@ -470,7 +467,7 @@ namespace Amanotes.Core.Internal
470
467
 
471
468
  if (value is string)
472
469
  {
473
- string s = value.ToString();
470
+ var s = value.ToString();
474
471
  if (checkJsonInString)
475
472
  {
476
473
  if (s.StartsWith("{") && s.EndsWith("}"))
@@ -484,7 +481,9 @@ namespace Amanotes.Core.Internal
484
481
  return;
485
482
  }
486
483
  }
487
- jsonSb.Append($"\"{EscapeJsonString(s)}\"");
484
+ jsonSb.Append($"\"");
485
+ AppendEscapeJsonString(jsonSb, s);
486
+ jsonSb.Append($"\"");
488
487
  return;
489
488
  }
490
489
 
@@ -521,81 +520,33 @@ namespace Amanotes.Core.Internal
521
520
  /// </summary>
522
521
  /// <param name="input">The input string to be escaped.</param>
523
522
  /// <returns>The escaped string.</returns>
524
- public static string EscapeJsonString(string input)
523
+ private static readonly Dictionary<char, string> escapeSequences = new Dictionary<char, string>
524
+ {
525
+ { '\"', "\\\"" },
526
+ { '\\', @"\\" },
527
+ { '\b', "\\b" },
528
+ { '\f', "\\f" },
529
+ { '\n', "\\n" },
530
+ { '\r', "\\r" },
531
+ { '\t', "\\t" }
532
+ };
533
+ public static void AppendEscapeJsonString(StringBuilder sb, string input)
525
534
  {
526
- var sb = new StringBuilder();
527
535
  foreach (char c in input)
528
536
  {
529
- switch (c)
537
+ if (escapeSequences.TryGetValue(c, out var escapedValue))
538
+ {
539
+ sb.Append(escapedValue);
540
+ }
541
+ else if (c < 0x20 || char.IsSurrogate(c))
530
542
  {
531
- case '\"': sb.Append("\\\""); break;
532
- case '\\': sb.Append("\\\\"); break;
533
- case '\b': sb.Append("\\b"); break;
534
- case '\f': sb.Append("\\f"); break;
535
- case '\n': sb.Append("\\n"); break;
536
- case '\r': sb.Append("\\r"); break;
537
- case '\t': sb.Append("\\t"); break;
538
- default:
539
- if (c < 0x20)
540
- {
541
- // Encode as \uXXXX for control characters
542
- sb.Append("\\u" + ((int)c).ToString("X4"));
543
- }
544
- else if (char.IsSurrogate(c))
545
- {
546
- // Handle surrogate pairs
547
- if (char.IsHighSurrogate(c))
548
- {
549
- sb.Append("\\u" + ((int)c).ToString("X4"));
550
- }
551
- else if (char.IsLowSurrogate(c))
552
- {
553
- sb.Append("\\u" + ((int)c).ToString("X4"));
554
- }
555
- }
556
- else
557
- {
558
- // Keep characters as is if they are in the UTF-8 range
559
- sb.Append(c);
560
- }
561
- break;
543
+ sb.AppendFormat("\\u{0:X4}", (int)c);
544
+ }
545
+ else
546
+ {
547
+ sb.Append(c);
562
548
  }
563
549
  }
564
- return sb.ToString();
565
- }
566
-
567
-
568
- //public static T[] FromJsonToArray<T>(string json)
569
- //{
570
- // if (!json.StartsWith("{\"items\":"))
571
- // {
572
- // json = $"{{\"items\":{json}}}";
573
- // }
574
- // Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(json);
575
- // return wrapper.items;
576
- //}
577
-
578
- //public static T FromJsonToObject<T>(string json)
579
- //{
580
- // return JsonUtility.FromJson<T>(json);
581
- //}
582
-
583
- //public static string ToJson<T>(T[] array)
584
- //{
585
- // Wrapper<T> wrapper = new Wrapper<T>();
586
- // wrapper.items = array;
587
- // return JsonUtility.ToJson(wrapper);
588
- //}
589
-
590
- //public static string ToJson<T>(T obj)
591
- //{
592
- // return JsonUtility.ToJson(obj);
593
- //}
594
-
595
- //[Serializable]
596
- //private class Wrapper<T>
597
- //{
598
- // public T[] items;
599
- //}
550
+ }
600
551
  }
601
552
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.amanotes.gdk",
3
- "version": "0.2.69",
3
+ "version": "0.2.70",
4
4
  "displayName": "AmaGDK",
5
5
  "description": "Amanotes Game Development Kit",
6
6
  "unity": "2019.4",