gg.easy.airship 0.1.2136 → 0.1.2137

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.
@@ -150,7 +150,11 @@ namespace Airship.Editor {
150
150
  /// Will try to get the script asset data associated with the specified script (if applicable)
151
151
  /// </summary>
152
152
  internal bool TryGetScriptAssetData(AirshipScript script, out ComponentScriptAssetData assetData) {
153
- var item = scripts.FirstOrDefault(f => f.script == script.assetPath);
153
+ return TryGetScriptAssetDataFromPath(script.assetPath, out assetData);
154
+ }
155
+
156
+ internal bool TryGetScriptAssetDataFromPath(string path, out ComponentScriptAssetData assetData) {
157
+ var item = scripts.FirstOrDefault(f => f.script == path);
154
158
  if (item != null) {
155
159
  assetData = item;
156
160
  return true;
@@ -225,18 +225,42 @@ using Object = UnityEngine.Object;
225
225
  return;
226
226
  }
227
227
 
228
+ var artifacts = AirshipLocalArtifactDatabase.instance;
229
+ var modifiedDatabase = false;
230
+
228
231
  try {
229
232
  AssetDatabase.StartAssetEditing();
230
233
  var compileFileList = CompiledFileQueue.ToArray();
234
+
231
235
  foreach (var file in compileFileList) {
232
- // var asset = AssetDatabase.LoadAssetAtPath<AirshipScript>(file);
233
- AssetDatabase.ImportAsset(file, ImportAssetOptions.Default);
236
+ var outFileHash = TypescriptProjectsService.Project.GetOutputFileHash(file);
237
+
238
+ if (artifacts.TryGetScriptAssetDataFromPath(PosixPath.ToPosix(file), out var data)) {
239
+ if (outFileHash != data.metadata.compiledHash) {
240
+ AssetDatabase.ImportAsset(file, ImportAssetOptions.Default);
241
+ data.metadata.compiledHash = outFileHash;
242
+ modifiedDatabase = true;
243
+ }
244
+ }
245
+ else {
246
+ var scriptData = artifacts.GetOrCreateScriptAssetData(AssetDatabase.LoadAssetAtPath<AirshipScript>(file));
247
+ scriptData.metadata = new TypescriptCompilerMetadata() {
248
+ compiledHash = outFileHash
249
+ };
250
+
251
+ AssetDatabase.ImportAsset(file, ImportAssetOptions.Default);
252
+ modifiedDatabase = true;
253
+ }
234
254
  }
255
+
235
256
  AssetDatabase.Refresh();
236
257
  } catch (Exception ex) {
237
- Debug.LogError("[Airship] Failed to reimport compiled files: " + ex);
258
+ Debug.LogException(ex);
238
259
  } finally {
239
260
  AssetDatabase.StopAssetEditing();
261
+ if (modifiedDatabase) {
262
+ artifacts.Modify();
263
+ }
240
264
  }
241
265
 
242
266
  EditorApplication.update -= ReimportCompiledFiles;
@@ -65,7 +65,7 @@ namespace Airship.Editor {
65
65
  return;
66
66
  #endif
67
67
  // On project load we'll force a full compile to try and get all the refs up to date
68
- if (!SessionState.GetBool("TypescriptInitialBoot", false)) {
68
+ if (!SessionState.GetBool("TypescriptInitialBoot", false) && IsValidEditor) {
69
69
  SessionState.SetBool("TypescriptInitialBoot", true);
70
70
  TypescriptCompilationService.BuildTypescript(TypeScriptCompileFlags.FullClean | TypeScriptCompileFlags.Setup | TypeScriptCompileFlags.DisplayProgressBar);
71
71
  }
@@ -18,6 +18,8 @@ namespace Luau {
18
18
  /// The file hash at the compile time
19
19
  /// </summary>
20
20
  public string hash;
21
+
22
+ public string compiledHash;
21
23
  /// <summary>
22
24
  /// The timestamp when this was compiled
23
25
  /// </summary>
@@ -102,12 +102,10 @@ namespace Code.Network.Simulation
102
102
  */
103
103
  public event Action<int, double, double> OnLagCompensationCheck;
104
104
 
105
- /**
106
- * This action tells all watching components that they need to perform a tick.
107
- * A Physics.Simulate() call will be made after PerformTick completes.
108
- */
109
- public event Action<double, bool> OnPerformTick;
110
-
105
+ /// <summary>
106
+ /// This action tells all watching components that they need to perform a tick.
107
+ /// A Physics.Simulate() call will be made after PerformTick completes.
108
+ /// </summary>
111
109
  public event Action<object, object> OnTick;
112
110
 
113
111
  /**
@@ -184,7 +182,6 @@ namespace Code.Network.Simulation
184
182
  if (resimBackTo != time) this.PerformResimulation(resimBackTo);
185
183
 
186
184
  // Perform the standard tick behavior
187
- OnPerformTick?.Invoke(time, false);
188
185
  OnTick?.Invoke(time, false);
189
186
  // Debug.Log("Simulate call. Main Tick: " + NetworkTime.time);
190
187
  Physics.Simulate(Time.fixedDeltaTime);
@@ -310,13 +307,14 @@ namespace Code.Network.Simulation
310
307
  this.ScheduleResimulation((resim => resim(time)));
311
308
  }
312
309
 
313
- /**
314
- * Requests a simulation based on the provided time. Requesting a simulation will roll back the physics
315
- * world to the snapshot just before or at the base time provided. Calling the returned tick function
316
- * will advance the simulation and re-simulate the calls to OnPerformTick, Physics.Simulate(), and OnCaptureSnapshot
317
- *
318
- * This function is used internally to implement the scheduled resimulations.
319
- */
310
+ /// <summary>
311
+ /// Requests a simulation based on the provided time. Requesting a simulation will roll back the physics
312
+ /// world to the snapshot just before or at the base time provided. Calling the returned tick function
313
+ /// will advance the simulation and re-simulate the calls to <see cref="OnTick"/>, Physics.Simulate,
314
+ /// and <see cref="OnCaptureSnapshot"/>
315
+ ///
316
+ /// This function is used internally to implement the scheduled resimulations.
317
+ /// </summary>
320
318
  private void PerformResimulation(double baseTime)
321
319
  {
322
320
  Debug.Log($"T:{Time.unscaledTimeAsDouble} Resimulating from {baseTime}");
@@ -343,7 +341,7 @@ namespace Code.Network.Simulation
343
341
 
344
342
  while (tickIndex < this.tickTimes.Count)
345
343
  {
346
- OnPerformTick?.Invoke(this.tickTimes[tickIndex], true);
344
+ OnTick?.Invoke(this.tickTimes[tickIndex], true);
347
345
  // Debug.Log("Simulate call. Replay Tick: " + this.tickTimes[tickIndex]);
348
346
  Physics.Simulate(Time.fixedDeltaTime);
349
347
  OnCaptureSnapshot?.Invoke(this.tickTimes[tickIndex], true);
@@ -165,7 +165,7 @@ namespace Code.Network.StateSystem
165
165
  private void Awake()
166
166
  {
167
167
  AirshipSimulationManager.Instance.ActivateSimulationManager();
168
- AirshipSimulationManager.Instance.OnPerformTick += this.OnPerformTick;
168
+ AirshipSimulationManager.Instance.OnTick += this.OnTick;
169
169
  AirshipSimulationManager.Instance.OnSetSnapshot += this.OnSetSnapshot;
170
170
  AirshipSimulationManager.Instance.OnCaptureSnapshot += this.OnCaptureSnapshot;
171
171
  AirshipSimulationManager.Instance.OnLagCompensationCheck += this.OnLagCompensationCheck;
@@ -195,7 +195,7 @@ namespace Code.Network.StateSystem
195
195
 
196
196
  public void OnDestroy()
197
197
  {
198
- AirshipSimulationManager.Instance.OnPerformTick -= this.OnPerformTick;
198
+ AirshipSimulationManager.Instance.OnTick -= this.OnTick;
199
199
  AirshipSimulationManager.Instance.OnSetSnapshot -= this.OnSetSnapshot;
200
200
  AirshipSimulationManager.Instance.OnCaptureSnapshot -= this.OnCaptureSnapshot;
201
201
  AirshipSimulationManager.Instance.OnLagCompensationCheck -= this.OnLagCompensationCheck;
@@ -282,8 +282,9 @@ namespace Code.Network.StateSystem
282
282
 
283
283
  #region Top Level Event Functions
284
284
 
285
- private void OnPerformTick(double time, bool replay)
286
- {
285
+ private void OnTick(object timeObj, object replayObj) {
286
+ if (timeObj is not double time || replayObj is not bool replay) return;
287
+
287
288
  // We are in shared mode
288
289
  if (isServer && isClient)
289
290
  {
@@ -27,7 +27,8 @@
27
27
  "GUID:199bc21c5ec6a3e41b1d67e225a7bd04",
28
28
  "GUID:30817c1a0e6d646d99c048fc403f5979",
29
29
  "GUID:325984b52e4128546bc7558552f8b1d2",
30
- "GUID:72872094b21c16e48b631b2224833d49"
30
+ "GUID:72872094b21c16e48b631b2224833d49",
31
+ "GUID:befe48b9a36afc04ea625c93daad910e"
31
32
  ],
32
33
  "includePlatforms": [],
33
34
  "excludePlatforms": [],
@@ -1,11 +1,15 @@
1
- using System.IO;
1
+ using System;
2
+ using System.IO;
2
3
  using System.IO.Compression;
4
+ using Code.Zstd;
3
5
 
4
6
  public static class VoxelCompressUtil {
7
+ private const ulong ZstdScratchBufferSize = 1024 * 128;
8
+
5
9
  /// <summary>
6
10
  /// Compress the given stream to a byte array.
7
11
  /// </summary>
8
- public static byte[] CompressToByteArray(Stream stream) {
12
+ public static byte[] CompressToByteArrayV1(Stream stream) {
9
13
  using var compressedStream = new MemoryStream();
10
14
  using var compressor = new DeflateStream(compressedStream, CompressionMode.Compress);
11
15
 
@@ -22,7 +26,7 @@ public static class VoxelCompressUtil {
22
26
  /// Decompress the compressed byte array into a new MemoryStream. The returned
23
27
  /// stream must be disposed once done.
24
28
  /// </summary>
25
- public static MemoryStream DecompressToMemoryStream(byte[] data) {
29
+ public static MemoryStream DecompressToMemoryStreamV1(byte[] data) {
26
30
  var decompressedStream = new MemoryStream();
27
31
 
28
32
  using var compressedStream = new MemoryStream(data);
@@ -34,4 +38,23 @@ public static class VoxelCompressUtil {
34
38
 
35
39
  return decompressedStream;
36
40
  }
41
+
42
+ /// <summary>
43
+ /// Compress the given data to a byte array.
44
+ /// </summary>
45
+ public static byte[] CompressToByteArrayV2(ReadOnlySpan<byte> data) {
46
+ using var zstd = new Zstd(ZstdScratchBufferSize);
47
+ var compressed = zstd.Compress(data, Zstd.MaxCompressionLevel);
48
+ return compressed;
49
+ }
50
+
51
+ /// <summary>
52
+ /// Decompress the compressed byte array into a new MemoryStream. The returned
53
+ /// stream must be disposed once done.
54
+ /// </summary>
55
+ public static MemoryStream DecompressToMemoryStreamV2(ReadOnlySpan<byte> data) {
56
+ using var zstd = new Zstd(ZstdScratchBufferSize);
57
+ var decompressed = zstd.Decompress(data);
58
+ return new MemoryStream(decompressed, false);
59
+ }
37
60
  }
@@ -443,7 +443,7 @@ namespace VoxelWorldStuff {
443
443
  /// </summary>
444
444
  public void WriteTemporaryCollision(Vector3 position, bool hasCollision) {
445
445
  if (hasCollision) {
446
- VoxelWorldCollision.MakeCollider(this, position, Vector3Int.one);
446
+ VoxelWorldCollision.MakeCollider(this, Vector3Int.FloorToInt(position) + Vector3.one / 2, Vector3Int.one);
447
447
  } else {
448
448
  VoxelWorldCollision.RemoveSingleVoxelCollision(this, position);
449
449
  }
@@ -6,7 +6,6 @@ using UnityEngine.Profiling;
6
6
  using VoxelData = System.UInt16;
7
7
  using BlockId = System.UInt16;
8
8
 
9
-
10
9
  [Serializable]
11
10
  [LuauAPI]
12
11
  [CreateAssetMenu(fileName = "VoxelWorldSaveFile", menuName = "Airship/VoxelWorld/VoxelWorldSaveFile", order = 0)]
@@ -15,6 +14,7 @@ public class WorldSaveFile : ScriptableObject {
15
14
  public List<BlockIdToScopedName> blockIdToScopeName = new();
16
15
 
17
16
  public byte[] chunksCompressed;
17
+ [HideInInspector] public bool chunksCompressedV2;
18
18
 
19
19
  [Serializable]
20
20
  public struct BlockIdToScopedName {
@@ -131,9 +131,6 @@ public class WorldSaveFile : ScriptableObject {
131
131
  }
132
132
  }
133
133
 
134
-
135
-
136
-
137
134
  public BlockId GetFileBlockIdFromStringId(string blockTypeId) {
138
135
  foreach (var pair in this.blockIdToScopeName) {
139
136
  if (pair.name == blockTypeId) {
@@ -216,7 +213,10 @@ public class WorldSaveFile : ScriptableObject {
216
213
  }
217
214
 
218
215
  // Compress:
219
- chunksCompressed = VoxelCompressUtil.CompressToByteArray(memStream);
216
+ var buffer = memStream.GetBuffer();
217
+ var bufferSpan = new ReadOnlySpan<byte>(buffer, 0, (int)memStream.Length);
218
+ chunksCompressed = VoxelCompressUtil.CompressToByteArrayV2(bufferSpan);
219
+ chunksCompressedV2 = true;
220
220
 
221
221
  Profiler.EndSample();
222
222
 
@@ -299,7 +299,9 @@ public class WorldSaveFile : ScriptableObject {
299
299
 
300
300
  // Decompress and deserialize chunks:
301
301
  Profiler.BeginSample("DecompressChunks");
302
- using var decompressedStream = VoxelCompressUtil.DecompressToMemoryStream(chunksCompressed);
302
+ using var decompressedStream = chunksCompressedV2
303
+ ? VoxelCompressUtil.DecompressToMemoryStreamV2(chunksCompressed)
304
+ : VoxelCompressUtil.DecompressToMemoryStreamV1(chunksCompressed);
303
305
  Profiler.EndSample();
304
306
 
305
307
  var reader = new BinaryReader(decompressedStream);
@@ -1,6 +1,5 @@
1
1
  using System;
2
- using System.Runtime.InteropServices;
3
-
2
+ using System.Buffers;
4
3
  using static Code.Zstd.ZstdNative;
5
4
 
6
5
  namespace Code.Zstd {
@@ -33,32 +32,85 @@ namespace Code.Zstd {
33
32
  /// and <c>Zstd.MaxCompressionLevel</c>. Most use-cases should use <c>Zstd.DefaultCompressionLevel</c>.
34
33
  /// </summary>
35
34
  public byte[] Compress(byte[] data, int compressionLevel) {
36
- return CompressData(data, compressionLevel, _ctx);
35
+ return Compress(new ReadOnlySpan<byte>(data), compressionLevel);
36
+ }
37
+
38
+ /// <summary>
39
+ /// Compress the data. The compression level can be between <c>Zstd.MinCompressionLevel</c>
40
+ /// and <c>Zstd.MaxCompressionLevel</c>. Most use-cases should use <c>Zstd.DefaultCompressionLevel</c>.
41
+ /// </summary>
42
+ public byte[] Compress(byte[] data, int start, int length, int compressionLevel) {
43
+ return Compress(new ReadOnlySpan<byte>(data, start, length), compressionLevel);
37
44
  }
38
45
 
39
46
  /// <summary>
40
47
  /// Compress the data using the default compression level.
41
48
  /// </summary>
42
49
  public byte[] Compress(byte[] data) {
43
- return CompressData(data, DefaultCompressionLevel, _ctx);
50
+ return Compress(new ReadOnlySpan<byte>(data));
51
+ }
52
+
53
+ /// <summary>
54
+ /// Compress the data using the default compression level.
55
+ /// </summary>
56
+ public byte[] Compress(byte[] data, int start, int length) {
57
+ return Compress(new ReadOnlySpan<byte>(data, start, length));
58
+ }
59
+
60
+ /// <summary>
61
+ /// Compress the data using the default compression level.
62
+ /// </summary>
63
+ public byte[] Compress(ReadOnlySpan<byte> data) {
64
+ return Compress(data, DefaultCompressionLevel);
65
+ }
66
+
67
+ /// <summary>
68
+ /// Compress the data. The compression level can be between <c>Zstd.MinCompressionLevel</c>
69
+ /// and <c>Zstd.MaxCompressionLevel</c>. Most use-cases should use <c>Zstd.DefaultCompressionLevel</c>.
70
+ /// </summary>
71
+ public byte[] Compress(ReadOnlySpan<byte> data, int compressionLevel) {
72
+ return CompressData(data, compressionLevel, _ctx);
44
73
  }
45
74
 
46
75
  /// <summary>
47
76
  /// Decompress the data.
48
77
  /// </summary>
49
78
  public byte[] Decompress(byte[] data) {
79
+ return Decompress(new ReadOnlySpan<byte>(data));
80
+ }
81
+
82
+ /// <summary>
83
+ /// Decompress the data.
84
+ /// </summary>
85
+ public byte[] Decompress(byte[] data, int start, int length) {
86
+ return Decompress(new ReadOnlySpan<byte>(data, start, length));
87
+ }
88
+
89
+ /// <summary>
90
+ /// Decompress the data.
91
+ /// </summary>
92
+ public byte[] Decompress(ReadOnlySpan<byte> data) {
50
93
  return DecompressData(data, _ctx);
51
94
  }
52
95
 
53
96
  public void Dispose() {
97
+ Dispose(true);
98
+ GC.SuppressFinalize(this);
99
+ }
100
+
101
+ protected virtual void Dispose(bool disposing) {
54
102
  _ctx.Dispose();
55
103
  }
56
104
 
105
+ ~Zstd() {
106
+ Dispose(false);
107
+ }
108
+
57
109
  /// <summary>
58
110
  /// Compress the bytes. The compression level can be between <c>Zstd.MinCompressionLevel</c>
59
111
  /// and <c>Zstd.MaxCompressionLevel</c>. Most use-cases should use <c>Zstd.DefaultCompressionLevel</c>.
60
112
  /// </summary>
61
- public static byte[] CompressData(byte[] data, int compressionLevel, ZstdContext ctx = null) {
113
+ public static byte[] CompressData(ReadOnlySpan<byte> data, int compressionLevel, ZstdContext ctx = null) {
62
114
  var bound = ZSTD_compressBound((ulong)data.Length);
63
115
  if (ZSTD_isError(bound)) {
64
116
  throw new ZstdException(bound);
@@ -72,33 +124,34 @@ namespace Code.Zstd {
72
124
  /// <summary>
73
125
  /// Decompress the bytes.
74
126
  /// </summary>
75
- public static byte[] DecompressData(byte[] data, ZstdContext ctx = null) {
76
- var dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
77
- var rSize = ZSTD_getFrameContentSize(dataHandle.AddrOfPinnedObject(), (ulong)data.Length);
127
+ public static unsafe byte[] DecompressData(ReadOnlySpan<byte> data, ZstdContext ctx = null) {
128
+ ulong rSize;
129
+ fixed (byte* src = data) {
130
+ rSize = ZSTD_getFrameContentSize(new IntPtr(src), (ulong)data.Length);
131
+ }
78
132
  if (ZSTD_isError(rSize)) {
79
- dataHandle.Free();
80
133
  throw new ZstdException(rSize);
81
134
  }
82
135
  byte[] decompressedData;
83
136
  if (rSize <= MaxStackSize) {
84
- decompressedData = DecompressWithStack(data, dataHandle, rSize, ctx);
137
+ decompressedData = DecompressWithStack(data, rSize, ctx);
85
138
  } else {
86
- decompressedData = DecompressWithHeap(data, dataHandle, rSize, ctx);
139
+ decompressedData = DecompressWithHeap(data, rSize, ctx);
87
140
  }
88
- dataHandle.Free();
89
141
  return decompressedData;
90
142
  }
91
143
 
92
- private static unsafe byte[] DecompressWithStack(byte[] data, GCHandle dataHandle, ulong rSize, ZstdContext ctx) {
144
+ private static unsafe byte[] DecompressWithStack(ReadOnlySpan<byte> data, ulong rSize, ZstdContext ctx) {
93
145
  var decompressedData = stackalloc byte[(int)rSize];
94
146
  ulong decompressedSize;
95
- if (ctx != null) {
96
- decompressedSize = ZSTD_decompressDCtx(ctx.Dctx, new IntPtr(decompressedData), rSize, dataHandle.AddrOfPinnedObject(), (ulong)data.Length);
97
- } else {
98
- decompressedSize = ZSTD_decompress(new IntPtr(decompressedData), rSize, dataHandle.AddrOfPinnedObject(), (ulong)data.Length);
147
+ fixed (byte* src = data) {
148
+ if (ctx != null) {
149
+ decompressedSize = ZSTD_decompressDCtx(ctx.Dctx, new IntPtr(decompressedData), rSize, new IntPtr(src), (ulong)data.Length);
150
+ } else {
151
+ decompressedSize = ZSTD_decompress(new IntPtr(decompressedData), rSize, new IntPtr(src), (ulong)data.Length);
152
+ }
99
153
  }
100
154
  if (ZSTD_isError(decompressedSize)) {
101
- dataHandle.Free();
102
155
  throw new ZstdException(decompressedSize);
103
156
  }
104
157
  var decompressedBuffer = new byte[decompressedSize];
@@ -108,35 +161,36 @@ namespace Code.Zstd {
108
161
  return decompressedBuffer;
109
162
  }
110
163
 
111
- private static byte[] DecompressWithHeap(byte[] data, GCHandle dataHandle, ulong rSize, ZstdContext ctx) {
164
+ private static unsafe byte[] DecompressWithHeap(ReadOnlySpan<byte> data, ulong rSize, ZstdContext ctx) {
112
165
  var allocDst = ctx == null || rSize > (ulong)ctx.ScratchBuffer.Length;
113
166
  var decompressedData = allocDst ? new byte[rSize] : ctx.ScratchBuffer;
114
- var dstHandle = GCHandle.Alloc(decompressedData, GCHandleType.Pinned);
115
167
  ulong decompressedSize;
116
- if (ctx != null) {
117
- decompressedSize = ZSTD_decompressDCtx(ctx.Dctx, dstHandle.AddrOfPinnedObject(), rSize, dataHandle.AddrOfPinnedObject(), (ulong)data.Length);
118
- } else {
119
- decompressedSize = ZSTD_decompress(dstHandle.AddrOfPinnedObject(), rSize, dataHandle.AddrOfPinnedObject(), (ulong)data.Length);
168
+ fixed (byte* src = data) {
169
+ fixed (byte* dst = decompressedData) {
170
+ if (ctx != null) {
171
+ decompressedSize = ZSTD_decompressDCtx(ctx.Dctx, new IntPtr(dst), rSize, new IntPtr(src), (ulong)data.Length);
172
+ } else {
173
+ decompressedSize = ZSTD_decompress(new IntPtr(dst), rSize, new IntPtr(src), (ulong)data.Length);
174
+ }
175
+ }
120
176
  }
121
- dstHandle.Free();
122
177
  if (ZSTD_isError(decompressedSize)) {
123
- dataHandle.Free();
124
178
  throw new ZstdException(decompressedSize);
125
179
  }
126
180
  Array.Resize(ref decompressedData, (int)decompressedSize);
127
181
  return decompressedData;
128
182
  }
129
183
 
130
- private static unsafe byte[] CompressWithStack(byte[] data, ulong bound, int compressionLevel, ZstdContext ctx) {
184
+ private static unsafe byte[] CompressWithStack(ReadOnlySpan<byte> data, ulong bound, int compressionLevel, ZstdContext ctx) {
131
185
  var dst = stackalloc byte[(int)bound];
132
- var srcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
133
186
  ulong compressedSize;
134
- if (ctx != null) {
135
- compressedSize = ZSTD_compressCCtx(ctx.Cctx, new IntPtr(dst), bound, srcHandle.AddrOfPinnedObject(), (ulong)data.Length, compressionLevel);
136
- } else {
137
- compressedSize = ZSTD_compress(new IntPtr(dst), bound, srcHandle.AddrOfPinnedObject(), (ulong)data.Length, compressionLevel);
187
+ fixed (byte* src = data) {
188
+ if (ctx != null) {
189
+ compressedSize = ZSTD_compressCCtx(ctx.Cctx, new IntPtr(dst), bound, new IntPtr(src), (ulong)data.Length, compressionLevel);
190
+ } else {
191
+ compressedSize = ZSTD_compress(new IntPtr(dst), bound, new IntPtr(src), (ulong)data.Length, compressionLevel);
192
+ }
138
193
  }
139
- srcHandle.Free();
140
194
  if (ZSTD_isError(compressedSize)) {
141
195
  throw new ZstdException(compressedSize);
142
196
  }
@@ -147,40 +201,59 @@ namespace Code.Zstd {
147
201
  return compressedBuffer;
148
202
  }
149
203
 
150
- private static byte[] CompressWithHeap(byte[] data, ulong bound, int compressionLevel, ZstdContext ctx) {
204
+ private static unsafe byte[] CompressWithHeap(ReadOnlySpan<byte> data, ulong bound, int compressionLevel, ZstdContext ctx) {
151
205
  var allocDst = ctx == null || bound > (ulong)ctx.ScratchBuffer.Length;
152
- var dst = allocDst ? new byte[bound] : ctx.ScratchBuffer;
153
- var dstHandle = GCHandle.Alloc(dst, GCHandleType.Pinned);
154
- var srcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
206
+ var dstBuf = allocDst ? new byte[bound] : ctx.ScratchBuffer;
155
207
  ulong compressedSize;
156
- if (ctx != null) {
157
- compressedSize = ZSTD_compressCCtx(ctx.Cctx, dstHandle.AddrOfPinnedObject(), bound, srcHandle.AddrOfPinnedObject(), (ulong)data.Length, compressionLevel);
158
- } else {
159
- compressedSize = ZSTD_compress(dstHandle.AddrOfPinnedObject(), bound, srcHandle.AddrOfPinnedObject(), (ulong)data.Length, compressionLevel);
208
+ fixed (byte* src = data) {
209
+ fixed (byte* dst = dstBuf) {
210
+ if (ctx != null) {
211
+ compressedSize = ZSTD_compressCCtx(ctx.Cctx, new IntPtr(dst), bound, new IntPtr(src), (ulong)data.Length, compressionLevel);
212
+ }
213
+ else {
214
+ compressedSize = ZSTD_compress(new IntPtr(dst), bound, new IntPtr(src), (ulong)data.Length, compressionLevel);
215
+ }
216
+ }
160
217
  }
161
- dstHandle.Free();
162
- srcHandle.Free();
163
218
  if (ZSTD_isError(compressedSize)) {
164
219
  throw new ZstdException(compressedSize);
165
220
  }
166
- Array.Resize(ref dst, (int)compressedSize);
167
- return dst;
221
+ Array.Resize(ref dstBuf, (int)compressedSize);
222
+ return dstBuf;
168
223
  }
169
224
  }
170
225
 
171
- public class ZstdContext : IDisposable {
226
+ public sealed class ZstdContext : IDisposable {
172
227
  internal readonly byte[] ScratchBuffer;
173
228
 
174
229
  internal readonly IntPtr Cctx;
175
230
  internal readonly IntPtr Dctx;
231
+
232
+ private bool _disposed;
176
233
 
177
234
  public ZstdContext(ulong scratchBufferSize) {
178
- ScratchBuffer = new byte[scratchBufferSize];
235
+ ScratchBuffer = ArrayPool<byte>.Shared.Rent((int)scratchBufferSize);
179
236
  Cctx = ZSTD_createCCtx();
180
237
  Dctx = ZSTD_createDCtx();
181
238
  }
182
239
 
240
+ ~ZstdContext() {
241
+ Dispose(false);
242
+ }
243
+
183
244
  public void Dispose() {
245
+ Dispose(true);
246
+ GC.SuppressFinalize(this);
247
+ }
248
+
249
+ private void Dispose(bool disposing) {
250
+ if (_disposed) return;
251
+ _disposed = true;
252
+
253
+ if (disposing) {
254
+ ArrayPool<byte>.Shared.Return(ScratchBuffer);
255
+ }
256
+
184
257
  ZSTD_freeCCtx(Cctx);
185
258
  ZSTD_freeDCtx(Dctx);
186
259
  }
@@ -189,4 +262,9 @@ namespace Code.Zstd {
189
262
  public class ZstdException : Exception {
190
263
  public ZstdException(ulong code) : base(ZSTD_getErrorName(code)) { }
191
264
  }
265
+
266
+ public class ZstdStreamException : Exception {
267
+ public ZstdStreamException(string message) : base(message) { }
268
+ public ZstdStreamException(ulong code) : this(ZSTD_getErrorName(code)) { }
269
+ }
192
270
  }
@@ -0,0 +1,147 @@
1
+ using System;
2
+ using System.Buffers;
3
+ using System.IO;
4
+ using System.Runtime.InteropServices;
5
+ using static Code.Zstd.ZstdNative;
6
+
7
+ namespace Code.Zstd {
8
+ /// <summary>
9
+ /// Provides methods for compressing streams using the zstd algorithm.
10
+ /// </summary>
11
+ public sealed class ZstdCompressStream : Stream {
12
+ public override bool CanRead => false;
13
+ public override bool CanSeek => false;
14
+ public override bool CanWrite => true;
15
+ public override long Length => _compressedStream.Length;
16
+ public override long Position { get; set; }
17
+
18
+ private readonly Stream _compressedStream;
19
+ private readonly bool _leaveOpen;
20
+
21
+ private IntPtr _cctx;
22
+ private GCHandle _outHandle;
23
+ private readonly byte[] _bufOut;
24
+ private readonly ulong _bufOutSize;
25
+
26
+ private bool _disposed;
27
+
28
+ /// <summary>
29
+ /// Constructs a new ZstdCompressionStream.
30
+ /// </summary>
31
+ /// <param name="compressedStream">The stream to which compressed data is written.</param>
32
+ /// <param name="leaveOpen">Optionally leave the <c>compressedStream</c> open after closing (defaults to <c>false</c>).</param>
33
+ public ZstdCompressStream(Stream compressedStream, bool leaveOpen = false) : this(compressedStream, Zstd.DefaultCompressionLevel, leaveOpen) { }
34
+
35
+ /// <summary>
36
+ /// Constructs a new ZstdCompressionStream.
37
+ /// </summary>
38
+ /// <param name="compressedStream">The stream to which compressed data is written.</param>
39
+ /// <param name="compressionLevel">The zstd compression level. This should be in the range of <c>Zstd.MinCompressionLevel</c> and <c>Zstd.MaxCompressionLevel</c>. Most use-cases should use <c>Zstd.DefaultCompressionLevel</c>.</param>
40
+ /// <param name="leaveOpen">Optionally leave the <c>compressedStream</c> open after closing (defaults to <c>false</c>).</param>
41
+ public ZstdCompressStream(Stream compressedStream, int compressionLevel, bool leaveOpen = false) {
42
+ _compressedStream = compressedStream;
43
+ _leaveOpen = leaveOpen;
44
+
45
+ _cctx = ZSTD_createCCtx();
46
+
47
+ _bufOutSize = ZSTD_CStreamOutSize();
48
+ _bufOut = ArrayPool<byte>.Shared.Rent((int)_bufOutSize);
49
+
50
+ _outHandle = GCHandle.Alloc(_bufOut, GCHandleType.Pinned);
51
+
52
+ ZSTD_CCtx_setParameter(_cctx, ZSTD_cParameter.ZSTD_c_compressionLevel, compressionLevel);
53
+ }
54
+
55
+ public override void Flush() {
56
+ var finished = false;
57
+ do {
58
+ var output = new ZSTD_outBuffer {
59
+ dst = _outHandle.AddrOfPinnedObject(),
60
+ size = _bufOutSize,
61
+ pos = 0,
62
+ };
63
+ var remaining = ZSTD_flushStream(_cctx, ref output);
64
+ if (ZSTD_isError(remaining)) {
65
+ throw new ZstdStreamException(remaining);
66
+ }
67
+ _compressedStream.Write(_bufOut, 0, (int)output.pos);
68
+ finished = remaining == 0;
69
+ } while (!finished);
70
+ }
71
+
72
+ public override int Read(byte[] buffer, int offset, int count) {
73
+ throw new NotSupportedException();
74
+ }
75
+
76
+ public override long Seek(long offset, SeekOrigin origin) {
77
+ throw new NotSupportedException();
78
+ }
79
+
80
+ public override void SetLength(long value) {
81
+ throw new NotSupportedException();
82
+ }
83
+
84
+ public override void Write(byte[] buffer, int offset, int count) {
85
+ WriteCore(new ReadOnlySpan<byte>(buffer, offset, count));
86
+ }
87
+
88
+ public override void Write(ReadOnlySpan<byte> buffer) {
89
+ WriteCore(buffer);
90
+ }
91
+
92
+ private unsafe void WriteCore(ReadOnlySpan<byte> buffer) {
93
+ fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer)) {
94
+ var input = new ZSTD_inBuffer {
95
+ src = (IntPtr)bufferPtr,
96
+ size = (ulong)buffer.Length,
97
+ pos = 0,
98
+ };
99
+
100
+ do {
101
+ var output = new ZSTD_outBuffer {
102
+ dst = _outHandle.AddrOfPinnedObject(),
103
+ size = _bufOutSize,
104
+ pos = 0,
105
+ };
106
+ var ret = ZSTD_compressStream(_cctx, ref output, ref input);
107
+ if (ZSTD_isError(ret)) {
108
+ throw new ZstdStreamException(ret);
109
+ }
110
+ _compressedStream.Write(_bufOut, 0, (int)output.pos);
111
+ } while (input.pos != input.size);
112
+ }
113
+ }
114
+
115
+ protected override void Dispose(bool disposing) {
116
+ base.Dispose(disposing);
117
+ if (disposing && !_disposed) {
118
+ var finished = false;
119
+ do {
120
+ var output = new ZSTD_outBuffer {
121
+ dst = _outHandle.AddrOfPinnedObject(),
122
+ size = _bufOutSize,
123
+ pos = 0,
124
+ };
125
+ var remaining = ZSTD_endStream(_cctx, ref output);
126
+ if (ZSTD_isError(remaining)) {
127
+ throw new ZstdStreamException(remaining);
128
+ }
129
+ _compressedStream.Write(_bufOut, 0, (int)output.pos);
130
+ finished = remaining == 0;
131
+ } while (!finished);
132
+
133
+ _outHandle.Free();
134
+ ArrayPool<byte>.Shared.Return(_bufOut);
135
+
136
+ if (!_leaveOpen) {
137
+ _compressedStream.Close();
138
+ }
139
+ }
140
+
141
+ ZSTD_freeCCtx(_cctx);
142
+ _cctx = IntPtr.Zero;
143
+
144
+ _disposed = true;
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 104c04ecb4754bbbb6d840b586afa217
3
+ timeCreated: 1748324334
@@ -0,0 +1,196 @@
1
+ using System;
2
+ using System.Buffers;
3
+ using System.IO;
4
+ using System.Runtime.InteropServices;
5
+ using static Code.Zstd.ZstdNative;
6
+
7
+ namespace Code.Zstd {
8
+ /// <summary>
9
+ /// Provides methods for decompressing streams using the zstd algorithm.
10
+ /// </summary>
11
+ public sealed class ZstdDecompressStream : Stream {
12
+ public override bool CanRead => true;
13
+ public override bool CanSeek => false;
14
+ public override bool CanWrite => false;
15
+ public override long Length => _compressedStream.Length;
16
+ public override long Position { get; set; }
17
+
18
+ private readonly Stream _compressedStream;
19
+ private readonly bool _leaveOpen;
20
+
21
+ private IntPtr _dctx;
22
+ private GCHandle _outHandle;
23
+ private GCHandle _inHandle;
24
+ private readonly byte[] _bufOut;
25
+ private readonly byte[] _bufIn;
26
+ private readonly ulong _bufOutSize;
27
+ private readonly ulong _bufInSize;
28
+
29
+ private readonly MemoryStream _bufOutStream;
30
+
31
+ private int _currentBufOutOffset;
32
+ private int _currentBufIn;
33
+
34
+ private bool _disposed;
35
+
36
+ /// <summary>
37
+ /// Constructs a new ZstdDecompressionStream.
38
+ /// </summary>
39
+ /// <param name="compressedStream">The source stream from which compressed data is decompressed.</param>
40
+ /// <param name="leaveOpen">Optionally leave the <c>compressedStream</c> open after closing (defaults to <c>false</c>).</param>
41
+ public ZstdDecompressStream(Stream compressedStream, bool leaveOpen = false) {
42
+ _compressedStream = compressedStream;
43
+ _leaveOpen = leaveOpen;
44
+
45
+ _dctx = ZSTD_createDCtx();
46
+
47
+ _bufOutSize = ZSTD_DStreamOutSize();
48
+ _bufInSize = ZSTD_DStreamInSize();
49
+
50
+ _bufOut = ArrayPool<byte>.Shared.Rent((int)_bufOutSize);
51
+ _bufIn = ArrayPool<byte>.Shared.Rent((int)_bufInSize);
52
+
53
+ _outHandle = GCHandle.Alloc(_bufOut, GCHandleType.Pinned);
54
+ _inHandle = GCHandle.Alloc(_bufIn, GCHandleType.Pinned);
55
+
56
+ _bufOutStream = new MemoryStream((int)ZSTD_DStreamOutSize());
57
+ }
58
+
59
+ public override void Flush() {
60
+ throw new NotSupportedException();
61
+ }
62
+
63
+ public override int Read(byte[] buffer, int offset, int count) {
64
+ return ReadCore(new Span<byte>(buffer, offset, count));
65
+ }
66
+
67
+ public override int Read(Span<byte> buffer) {
68
+ return ReadCore(buffer);
69
+ }
70
+
71
+ private int ReadCore(Span<byte> buffer) {
72
+ var read = 0;
73
+ var lastRet = 0ul;
74
+ var streamEmpty = false;
75
+ var bufferOffset = 0;
76
+
77
+ if (buffer.IsEmpty) {
78
+ throw new ZstdStreamException("Empty buffer");
79
+ }
80
+
81
+ while (true) {
82
+ if (_bufOutStream.Length > 0) {
83
+ var len = Math.Min(buffer.Length, (int)(_bufOutStream.Length - _currentBufOutOffset));
84
+ _bufOutStream.Seek(_currentBufOutOffset, SeekOrigin.Begin);
85
+ var bufOutRead = _bufOutStream.Read(buffer.Slice(bufferOffset, len));
86
+ if (bufOutRead != len) {
87
+ throw new ZstdStreamException("Failed to read full amount into buffer");
88
+ }
89
+ _currentBufOutOffset += len;
90
+ bufferOffset += len;
91
+ read += len;
92
+
93
+ if (_currentBufOutOffset == _bufOutStream.Length) {
94
+ _currentBufOutOffset = 0;
95
+ _bufOutStream.Seek(0, SeekOrigin.Begin);
96
+ _bufOutStream.SetLength(0);
97
+ }
98
+
99
+ if (len == buffer.Length) {
100
+ break;
101
+ }
102
+ }
103
+
104
+ if (_bufOutStream.Length == 0) {
105
+ if (_currentBufIn == 0) {
106
+ var bytesFromStream = ReadInCompressedChunkFromStream();
107
+ streamEmpty = bytesFromStream == 0;
108
+ }
109
+
110
+ if (!streamEmpty) {
111
+ lastRet = Decompress();
112
+ }
113
+ }
114
+
115
+ if (streamEmpty && _bufOutStream.Length == 0) {
116
+ break;
117
+ }
118
+ }
119
+
120
+ if (streamEmpty && _bufOutStream.Length == 0 && lastRet != 0) {
121
+ throw new ZstdStreamException($"EOF before end of stream: {lastRet}");
122
+ }
123
+
124
+ return read;
125
+ }
126
+
127
+ private ulong Decompress() {
128
+ var lastRet = 0ul;
129
+
130
+ var input = new ZSTD_inBuffer {
131
+ src = _inHandle.AddrOfPinnedObject(),
132
+ size = (ulong)_currentBufIn,
133
+ pos = 0,
134
+ };
135
+
136
+ while (input.pos < input.size) {
137
+ var output = new ZSTD_outBuffer {
138
+ dst = _outHandle.AddrOfPinnedObject(),
139
+ size = _bufOutSize,
140
+ pos = 0,
141
+ };
142
+
143
+ lastRet = ZSTD_decompressStream(_dctx, ref output, ref input);
144
+ if (ZSTD_isError(lastRet)) {
145
+ throw new ZstdStreamException(lastRet);
146
+ }
147
+
148
+ _bufOutStream.Write(_bufOut, 0, (int)output.pos);
149
+ }
150
+
151
+ _currentBufIn = 0;
152
+
153
+ return lastRet;
154
+ }
155
+
156
+ private int ReadInCompressedChunkFromStream() {
157
+ var streamRead = 0;
158
+ if (_currentBufIn < (int)_bufInSize) {
159
+ streamRead = _compressedStream.Read(_bufIn, _currentBufIn, (int)_bufInSize - _currentBufIn);
160
+ _currentBufIn += streamRead;
161
+ }
162
+ return streamRead;
163
+ }
164
+
165
+ public override long Seek(long offset, SeekOrigin origin) {
166
+ throw new NotSupportedException();
167
+ }
168
+
169
+ public override void SetLength(long value) {
170
+ throw new NotSupportedException();
171
+ }
172
+
173
+ public override void Write(byte[] buffer, int offset, int count) {
174
+ throw new NotSupportedException();
175
+ }
176
+
177
+ protected override void Dispose(bool disposing) {
178
+ base.Dispose(disposing);
179
+ if (disposing && !_disposed) {
180
+ _outHandle.Free();
181
+ _inHandle.Free();
182
+ ArrayPool<byte>.Shared.Return(_bufOut);
183
+ ArrayPool<byte>.Shared.Return(_bufIn);
184
+ if (!_leaveOpen) {
185
+ _compressedStream.Close();
186
+ }
187
+ _bufOutStream.Dispose();
188
+ }
189
+
190
+ ZSTD_freeDCtx(_dctx);
191
+ _dctx = IntPtr.Zero;
192
+
193
+ _disposed = true;
194
+ }
195
+ }
196
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 17b1cf21ff70448ba38b34ea9f3428c1
3
+ timeCreated: 1748328585
@@ -327,7 +327,7 @@ namespace Code.Zstd {
327
327
  #else
328
328
  [DllImport("LuauPlugin")]
329
329
  #endif
330
- internal static extern ulong ZSTD_compressStream2(IntPtr cctx, ZSTD_outBuffer output, ZSTD_inBuffer input, ZSTD_EndDirective endOp);
330
+ internal static extern ulong ZSTD_compressStream2(IntPtr cctx, ref ZSTD_outBuffer output, ref ZSTD_inBuffer input, ZSTD_EndDirective endOp);
331
331
 
332
332
  #if UNITY_IPHONE
333
333
  [DllImport("__Internal")]
@@ -355,21 +355,21 @@ namespace Code.Zstd {
355
355
  #else
356
356
  [DllImport("LuauPlugin")]
357
357
  #endif
358
- internal static extern ulong ZSTD_compressStream(IntPtr zcs, ZSTD_outBuffer output, ZSTD_inBuffer input);
358
+ internal static extern ulong ZSTD_compressStream(IntPtr zcs, ref ZSTD_outBuffer output, ref ZSTD_inBuffer input);
359
359
 
360
360
  #if UNITY_IPHONE
361
361
  [DllImport("__Internal")]
362
362
  #else
363
363
  [DllImport("LuauPlugin")]
364
364
  #endif
365
- internal static extern ulong ZSTD_flushStream(IntPtr zcs, ZSTD_outBuffer output);
365
+ internal static extern ulong ZSTD_flushStream(IntPtr zcs, ref ZSTD_outBuffer output);
366
366
 
367
367
  #if UNITY_IPHONE
368
368
  [DllImport("__Internal")]
369
369
  #else
370
370
  [DllImport("LuauPlugin")]
371
371
  #endif
372
- internal static extern ulong ZSTD_endStream(IntPtr zcs, ZSTD_outBuffer output);
372
+ internal static extern ulong ZSTD_endStream(IntPtr zcs, ref ZSTD_outBuffer output);
373
373
 
374
374
  #if UNITY_IPHONE
375
375
  [DllImport("__Internal")]
@@ -397,7 +397,7 @@ namespace Code.Zstd {
397
397
  #else
398
398
  [DllImport("LuauPlugin")]
399
399
  #endif
400
- internal static extern ulong ZSTD_decompressStream(IntPtr zds, ZSTD_outBuffer output, ZSTD_inBuffer input);
400
+ internal static extern ulong ZSTD_decompressStream(IntPtr zds, ref ZSTD_outBuffer output, ref ZSTD_inBuffer input);
401
401
 
402
402
  #if UNITY_IPHONE
403
403
  [DllImport("__Internal")]
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gg.easy.airship",
3
- "version": "0.1.2136",
3
+ "version": "0.1.2137",
4
4
  "displayName": "Airship",
5
5
  "unity": "2021.3",
6
6
  "unityRelease": "12f1",