Skip to content

Commit 2c34b81

Browse files
committed
when writing to a file, use a fast path direct to file to save memory and overcome the 2gb limit of MemoryStream
1 parent dc19976 commit 2c34b81

File tree

1 file changed

+67
-36
lines changed

1 file changed

+67
-36
lines changed

src/SharpGLTF.Core/Schema2/Serialization.WriteContext.cs

Lines changed: 67 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -33,45 +33,44 @@ public class WriteContext : WriteSettings
3333
{
3434
#region lifecycle
3535

36-
public static WriteContext Create(FileWriterCallback fileCallback)
36+
public static WriteContext Create(FileWriterCallback fileCallback, Func<string, System.IO.Stream> streamWriteCallback = null)
3737
{
3838
Guard.NotNull(fileCallback, nameof(fileCallback));
3939

40-
var context = new WriteContext(fileCallback)
40+
var context = new WriteContext(fileCallback, streamWriteCallback)
4141
{
4242
_UpdateSupportedExtensions = true
4343
};
4444

4545
return context;
46-
}
47-
48-
[Obsolete("Use CreateFromDirectory", true)]
49-
public static WriteContext CreateFromFile(string filePath)
50-
{
51-
Guard.FilePathMustBeValid(filePath, nameof(filePath));
52-
53-
var finfo = new System.IO.FileInfo(filePath);
54-
55-
return CreateFromDirectory(finfo.Directory);
56-
}
46+
}
5747

5848
public static WriteContext CreateFromDirectory(DirectoryInfo dinfo)
5949
{
6050
Guard.NotNull(dinfo, nameof(dinfo));
6151
Guard.MustExist(dinfo, nameof(dinfo));
6252

63-
void _saveFile(string rawUri, BYTES data)
53+
void _writeBytes(string rawUri, BYTES data)
6454
{
6555
var path = Uri.UnescapeDataString(rawUri);
6656
path = Path.Combine(dinfo.FullName, path);
6757

6858
File.WriteAllBytes(path, data.ToUnderlayingArray());
6959
}
7060

71-
var context = Create(_saveFile);
61+
System.IO.Stream _OpenStream(string rawUri)
62+
{
63+
var path = Uri.UnescapeDataString(rawUri);
64+
path = Path.Combine(dinfo.FullName, path);
65+
66+
return System.IO.File.Create(path);
67+
}
68+
69+
var context = Create(_writeBytes, _OpenStream);
7270
context.ImageWriting = ResourceWriteMode.Default;
7371
context.JsonIndented = true;
74-
context.CurrentDirectory = dinfo;
72+
context.CurrentDirectory = dinfo;
73+
7574
return context;
7675
}
7776

@@ -143,16 +142,28 @@ internal WriteContext WithDeepCloneSettings()
143142
return this;
144143
}
145144

146-
private WriteContext(FileWriterCallback fileCallback)
145+
private WriteContext(FileWriterCallback byteWriteCallback, Func<string, System.IO.Stream> streamWriteCallback)
147146
{
148-
_FileWriter = fileCallback;
147+
_ByteWriter = byteWriteCallback;
148+
_StreamWriter = streamWriteCallback;
149149
}
150150

151151
#endregion
152152

153153
#region data
154154

155-
private readonly FileWriterCallback _FileWriter;
155+
/// <summary>
156+
/// callback used to write named binary blogs to the current context
157+
/// </summary>
158+
private readonly FileWriterCallback _ByteWriter;
159+
160+
/// <summary>
161+
/// alternate callback used to write directly to a file.
162+
/// </summary>
163+
/// <remarks>
164+
/// If this callback is null, <see cref="_ByteWriter"/> must be used as fallback.
165+
/// </remarks>
166+
private readonly Func<string, System.IO.Stream> _StreamWriter;
156167

157168
#endregion
158169

@@ -172,11 +183,11 @@ private WriteContext(FileWriterCallback fileCallback)
172183

173184
#endregion
174185

175-
#region API
186+
#region API
176187

177188
public void WriteAllBytesToEnd(string fileName, BYTES data)
178189
{
179-
this._FileWriter(fileName, data);
190+
this._ByteWriter(fileName, data);
180191
}
181192

182193
public string WriteImage(string assetName, MemoryImage image)
@@ -223,13 +234,22 @@ public void WriteTextSchema2(string name, MODEL model)
223234

224235
_ValidateBeforeWriting(model);
225236

226-
using (var m = new MemoryStream())
227-
{
228-
model._WriteJSON(m, this.JsonOptions, this.JsonPostprocessor);
229-
230-
string finalName = Path.HasExtension(name) ? name : $"{name}.gltf";
237+
string finalName = Path.HasExtension(name) ? name : $"{name}.gltf";
231238

232-
WriteAllBytesToEnd(finalName, m.ToArraySegment());
239+
if (_StreamWriter != null) // write directly to a stream
240+
{
241+
using (var f = _StreamWriter.Invoke(finalName))
242+
{
243+
model._WriteJSON(f, this.JsonOptions, this.JsonPostprocessor);
244+
}
245+
}
246+
else // write to bytes
247+
{
248+
using (var m = new MemoryStream())
249+
{
250+
model._WriteJSON(m, this.JsonOptions, this.JsonPostprocessor);
251+
WriteAllBytesToEnd(finalName, m.ToArraySegment());
252+
}
233253
}
234254

235255
model._AfterWriting();
@@ -249,9 +269,7 @@ public void WriteBinarySchema2(string name, MODEL model)
249269
Guard.FilePathMustBeValid(name, nameof(name));
250270
if (System.IO.Path.IsPathRooted(name)) throw new ArgumentException("path must be relative", nameof(name));
251271

252-
Guard.NotNull(model, nameof(model));
253-
254-
272+
Guard.NotNull(model, nameof(model));
255273

256274
// merge images for all cases except for satellite files
257275
var mergeImages = this.ImageWriting != ResourceWriteMode.SatelliteFile;
@@ -270,16 +288,29 @@ public void WriteBinarySchema2(string name, MODEL model)
270288

271289
_ValidateBeforeWriting(model);
272290

273-
using (var m = new MemoryStream())
291+
string finalName = Path.HasExtension(name) ? name : $"{name}.glb";
292+
293+
if (_StreamWriter != null) // write to stream
274294
{
275-
using (var w = new BinaryWriter(m))
295+
using(var s = _StreamWriter.Invoke(finalName))
276296
{
277-
_BinarySerialization.WriteBinaryModel(w, model);
297+
using (var w = new BinaryWriter(s))
298+
{
299+
_BinarySerialization.WriteBinaryModel(w, model);
300+
}
278301
}
302+
}
303+
else // write to bytes
304+
{
305+
using (var m = new MemoryStream())
306+
{
307+
using (var w = new BinaryWriter(m))
308+
{
309+
_BinarySerialization.WriteBinaryModel(w, model);
310+
}
279311

280-
string finalName = Path.HasExtension(name) ? name : $"{name}.glb";
281-
282-
WriteAllBytesToEnd(finalName, m.ToArraySegment());
312+
WriteAllBytesToEnd(finalName, m.ToArraySegment());
313+
}
283314
}
284315

285316
model._AfterWriting();

0 commit comments

Comments
 (0)