Skip to content
This repository was archived by the owner on Feb 3, 2023. It is now read-only.

Commit a9e1523

Browse files
carlosmnnulltoken
authored andcommitted
ObjectDatabase: stream objects when we know we can
When given a size and no path, we know that we do not need to buffer the content or apply any filters, so we can create an write-stream into the object database and put in our content directly, avoiding the temporary file and callbacks altogether.
1 parent 9b0b947 commit a9e1523

File tree

6 files changed

+123
-4
lines changed

6 files changed

+123
-4
lines changed

LibGit2Sharp.Tests/ObjectDatabaseFixture.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,19 @@ public void CanCreateABlobFromAStreamWithANumberOfBytesToConsume(int contentSize
137137
}
138138

139139
[Theory]
140-
[InlineData(16, 32)]
141-
[InlineData(7854, 9785)]
142-
public void CreatingABlobFromTooShortAStreamThrows(int contentSize, int numberOfBytesToConsume)
140+
[InlineData(16, 32, null)]
141+
[InlineData(7854, 9785, null)]
142+
[InlineData(16, 32, "binary.bin")]
143+
[InlineData(7854, 9785, "binary.bin")]
144+
public void CreatingABlobFromTooShortAStreamThrows(int contentSize, int numberOfBytesToConsume, string hintpath)
143145
{
144146
string path = InitNewRepository();
145147

146148
using (var repo = new Repository(path))
147149
{
148150
using (var stream = PrepareMemoryStream(contentSize))
149151
{
150-
Assert.Throws<EndOfStreamException>(() => repo.ObjectDatabase.CreateBlob(stream, numberOfBytesToConsume: numberOfBytesToConsume));
152+
Assert.Throws<EndOfStreamException>(() => repo.ObjectDatabase.CreateBlob(stream, hintpath, numberOfBytesToConsume));
151153
}
152154
}
153155
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace LibGit2Sharp.Core.Handles
2+
{
3+
internal class OdbStreamSafeHandle : SafeHandleBase
4+
{
5+
protected override bool ReleaseHandleImpl()
6+
{
7+
Proxy.git_odb_stream_free(handle);
8+
return true;
9+
}
10+
}
11+
}

LibGit2Sharp/Core/NativeMethods.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,12 +712,24 @@ internal static extern int git_odb_foreach(
712712
git_odb_foreach_cb cb,
713713
IntPtr payload);
714714

715+
[DllImport(libgit2)]
716+
internal static extern int git_odb_open_wstream(out OdbStreamSafeHandle stream, ObjectDatabaseSafeHandle odb, UIntPtr size, GitObjectType type);
717+
715718
[DllImport(libgit2)]
716719
internal static extern void git_odb_free(IntPtr odb);
717720

718721
[DllImport(libgit2)]
719722
internal static extern void git_object_free(IntPtr obj);
720723

724+
[DllImport(libgit2)]
725+
internal static extern int git_odb_stream_write(OdbStreamSafeHandle Stream, IntPtr Buffer, UIntPtr len);
726+
727+
[DllImport(libgit2)]
728+
internal static extern int git_odb_stream_finalize_write(out GitOid id, OdbStreamSafeHandle stream);
729+
730+
[DllImport(libgit2)]
731+
internal static extern void git_odb_stream_free(IntPtr stream);
732+
721733
[DllImport(libgit2)]
722734
internal static extern OidSafeHandle git_object_id(GitObjectSafeHandle obj);
723735

LibGit2Sharp/Core/Proxy.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,11 +1296,57 @@ public static ICollection<TResult> git_odb_foreach<TResult>(
12961296
IntPtr.Zero));
12971297
}
12981298

1299+
public static OdbStreamSafeHandle git_odb_open_wstream(ObjectDatabaseSafeHandle odb, UIntPtr size, GitObjectType type)
1300+
{
1301+
using (ThreadAffinity())
1302+
{
1303+
OdbStreamSafeHandle stream;
1304+
int res = NativeMethods.git_odb_open_wstream(out stream, odb, size, type);
1305+
Ensure.ZeroResult(res);
1306+
1307+
return stream;
1308+
}
1309+
}
1310+
12991311
public static void git_odb_free(IntPtr odb)
13001312
{
13011313
NativeMethods.git_odb_free(odb);
13021314
}
13031315

1316+
public static void git_odb_stream_write(OdbStreamSafeHandle stream, byte[] data, int len)
1317+
{
1318+
using (ThreadAffinity())
1319+
{
1320+
int res;
1321+
unsafe
1322+
{
1323+
fixed (byte *p = data)
1324+
{
1325+
res = NativeMethods.git_odb_stream_write(stream, (IntPtr) p, (UIntPtr) len);
1326+
}
1327+
}
1328+
1329+
Ensure.ZeroResult(res);
1330+
}
1331+
}
1332+
1333+
public static ObjectId git_odb_stream_finalize_write(OdbStreamSafeHandle stream)
1334+
{
1335+
using (ThreadAffinity())
1336+
{
1337+
GitOid id;
1338+
int res = NativeMethods.git_odb_stream_finalize_write(out id, stream);
1339+
Ensure.ZeroResult(res);
1340+
1341+
return id;
1342+
}
1343+
}
1344+
1345+
public static void git_odb_stream_free(IntPtr stream)
1346+
{
1347+
NativeMethods.git_odb_stream_free(stream);
1348+
}
1349+
13041350
#endregion
13051351

13061352
#region git_patch_

LibGit2Sharp/LibGit2Sharp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@
312312
<Compile Include="UsernamePasswordCredentials.cs" />
313313
<Compile Include="VoidReference.cs" />
314314
<Compile Include="Core\RawContentStream.cs" />
315+
<Compile Include="Core\Handles\OdbStreamSafeHandle.cs" />
315316
</ItemGroup>
316317
<ItemGroup>
317318
<CodeAnalysisDictionary Include="CustomDictionary.xml" />

LibGit2Sharp/ObjectDatabase.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ public virtual Blob CreateBlob(Stream stream, string hintpath = null, int? numbe
170170
{
171171
Ensure.ArgumentNotNull(stream, "stream");
172172

173+
// there's no need to buffer the file for filtering, so simply use a stream
174+
if (hintpath == null && numberOfBytesToConsume.HasValue)
175+
{
176+
return CreateBlob(stream, numberOfBytesToConsume.Value);
177+
}
178+
173179
if (!stream.CanRead)
174180
{
175181
throw new ArgumentException("The stream cannot be read from.", "stream");
@@ -181,6 +187,47 @@ public virtual Blob CreateBlob(Stream stream, string hintpath = null, int? numbe
181187
return repo.Lookup<Blob>(id);
182188
}
183189

190+
/// <summary>
191+
/// Inserts a <see cref="Blob"/> into the object database created from the content of the stream.
192+
/// </summary>
193+
/// <param name="stream">The stream from which will be read the content of the blob to be created.</param>
194+
/// <param name="numberOfBytesToConsume">Number of bytes to consume from the stream.</param>
195+
/// <returns>The created <see cref="Blob"/>.</returns>
196+
public virtual Blob CreateBlob(Stream stream, int numberOfBytesToConsume)
197+
{
198+
Ensure.ArgumentNotNull(stream, "stream");
199+
200+
if (!stream.CanRead)
201+
{
202+
throw new ArgumentException("The stream cannot be read from.", "stream");
203+
}
204+
205+
using (var odbStream = Proxy.git_odb_open_wstream(handle, (UIntPtr)numberOfBytesToConsume, GitObjectType.Blob))
206+
{
207+
var buffer = new byte[4*1024];
208+
int totalRead = 0;
209+
210+
while (totalRead < numberOfBytesToConsume)
211+
{
212+
var left = numberOfBytesToConsume - totalRead;
213+
var toRead = left < buffer.Length ? left : buffer.Length;
214+
var read = stream.Read(buffer, 0, toRead);
215+
216+
if (read == 0)
217+
{
218+
throw new EndOfStreamException("The stream ended unexpectedly");
219+
}
220+
221+
Proxy.git_odb_stream_write(odbStream, buffer, read);
222+
totalRead += read;
223+
}
224+
225+
var id = Proxy.git_odb_stream_finalize_write(odbStream);
226+
227+
return repo.Lookup<Blob>(id);
228+
}
229+
}
230+
184231
/// <summary>
185232
/// Inserts a <see cref="Tree"/> into the object database, created from a <see cref="TreeDefinition"/>.
186233
/// </summary>

0 commit comments

Comments
 (0)