Skip to content

Introduce Blob.GetFilteredContent#534

Merged
nulltoken merged 1 commit intolibgit2:vNextfrom
ethomson:filtered_content
Oct 18, 2013
Merged

Introduce Blob.GetFilteredContent#534
nulltoken merged 1 commit intolibgit2:vNextfrom
ethomson:filtered_content

Conversation

@ethomson
Copy link
Member

Get the content of a blob as it would be checked out to the working directory via the filters.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in general we have been pretty lax about checking / handling the case where an UIntPtr overflows an int (how often will this happen...) - but we could check that the value we are getting back can be handled by an int and throw an exception if it doesn't...

@nulltoken
Copy link
Member

@ethomson The Travis build is failing. Could you please take a look at this, please?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we drop this method and make the callers rather invoke git_blob_filtered_content_stream?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jamill it looks like we're consuming byte[] instead of Streams, would removing this hurt you?

But still, it seems like nice parity with the Content property.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can work with the stream as well. I think this property is still nice parity with the Content property.

@ethomson
Copy link
Member Author

@nulltoken This introduces a FilteringOptions and overloaded methods that take it.

@arrbee
Copy link
Member

arrbee commented Oct 16, 2013

For reference, I'm open to changing the behavior of git_blob_filtered_content to return the raw data for blobs with binary data when check_for_binary_data is true.

@nulltoken
Copy link
Member

@ethomson Very nice refactoring. Thanks!

One last nitpick: The travis build still seems to be unhappy. Could you please troubleshoot these failures?

LibGit2Sharp.Tests.BlobFixture.CanReadBlobFilteredStream: System.NullReferenceException : Object reference not set to an instance of an object
  at LibGit2Sharp.Tests.BlobFixture.CanReadBlobFilteredStream () [0x00000] in <filename unknown>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0 


LibGit2Sharp.Tests.BlobFixture.CanGetBlobAsFilteredText: System.NullReferenceException : Object reference not set to an instance of an object
  at LibGit2Sharp.Tests.BlobFixture.CanGetBlobAsFilteredText () [0x00000] in <filename unknown>:0 
  at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
  at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0 

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The git_blob_filtered_content doc states the following

If no filters need to be applied, then the out buffer will just be populated with
a pointer to the raw content of the blob. In that case, be careful to not free the
blob until done with the buffer.

Considering the case where no filter is applied, wouldn't we streaming back garbage data when osw is eventually disposed (and the associated pointer is freed)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this ^^, I wonder if we couldn't somehow reuse the RawContentStream introduced with #324.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that there's any way to coerce this into a RawContentStream, no; we need to get the git_buf before constructing the stream. If you can think of something clever, though, I'm all ears.

@ethomson
Copy link
Member Author

I rebased this (and updated the stream to handle disposing the ObjectSafeWrapper.) I'll dig in to the mono failures directly.

@ethomson
Copy link
Member Author

Rebased and fixed the mono failures...!

@nulltoken
Copy link
Member

Should fix #178

@nulltoken
Copy link
Member

I don't think that there's any way to coerce this into a RawContentStream, no; we need to get the git_buf before constructing the stream. If you can think of something clever, though, I'm all ears.

@ethomson How much do you hate this?

diff --git a/LibGit2Sharp.Tests/BlobFixture.cs b/LibGit2Sharp.Tests/BlobFixture.cs
index 313be99..c35437f 100644
--- a/LibGit2Sharp.Tests/BlobFixture.cs
+++ b/LibGit2Sharp.Tests/BlobFixture.cs
@@ -1,4 +1,5 @@
-using System.IO;
+using System;
+using System.IO;
 using System.Linq;
 using System.Text;
 using LibGit2Sharp.Tests.TestHelpers;
@@ -31,16 +32,7 @@ public void CanGetBlobAsFilteredText()

                 var text = blob.ContentAsText(new FilteringOptions("foo.txt"));

-                ConfigurationEntry<bool> autocrlf = repo.Config.Get<bool>("core.autocrlf");
-
-                if (autocrlf != null && autocrlf.Value)
-                {
-                    Assert.Equal("hey there\r\n", text);
-                }
-                else
-                {
-                    Assert.Equal("hey there\n", text);
-                }
+                Assert.Equal("hey there" + Environment.NewLine, text);
             }
         }

@@ -140,16 +132,7 @@ public void CanReadBlobFilteredStream()
                 {
                     string content = tr.ReadToEnd();

-                    ConfigurationEntry<bool> autocrlf = repo.Config.Get<bool>("core.autocrlf");
-
-                    if (autocrlf != null && autocrlf.Value)
-                    {
-                        Assert.Equal("hey there\r\n", content);
-                    }
-                    else
-                    {
-                        Assert.Equal("hey there\n", content);
-                    }
+                    Assert.Equal("hey there" + Environment.NewLine, content);
                 }
             }
         }
diff --git a/LibGit2Sharp/Core/GitBufStream.cs b/LibGit2Sharp/Core/GitBufStream.cs
deleted file mode 100644
index c686ae9..0000000
--- a/LibGit2Sharp/Core/GitBufStream.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using System.IO;
-using LibGit2Sharp.Core.Handles;
-
-namespace LibGit2Sharp.Core
-{
-    internal class GitBufStream : UnmanagedMemoryStream
-    {
-        private readonly GitBuf buf;
-        private readonly ObjectSafeWrapper wrapper;
-
-        internal unsafe GitBufStream(GitBuf buf, ObjectSafeWrapper wrapper) : base((byte*)buf.ptr, (int)buf.size)
-        {
-            this.buf = buf;
-            this.wrapper = wrapper;
-        }
-
-        protected override void Dispose(bool disposing)
-        {
-            base.Dispose(disposing);
-            buf.Dispose();
-            wrapper.Dispose();
-        }
-    }
-}
diff --git a/LibGit2Sharp/Core/Proxy.cs b/LibGit2Sharp/Core/Proxy.cs
index e9be97f..bb131ac 100644
--- a/LibGit2Sharp/Core/Proxy.cs
+++ b/LibGit2Sharp/Core/Proxy.cs
@@ -76,20 +76,15 @@ public static ObjectId git_blob_create_fromfile(RepositorySafeHandle repo, FileP
         public static UnmanagedMemoryStream git_blob_filtered_content_stream(RepositorySafeHandle repo, ObjectId id, FilePath path, bool check_for_binary_data)
         {
             var buf = new GitBuf();
-            var osw = new ObjectSafeWrapper(id, repo);
+            var handle = new ObjectSafeWrapper(id, repo).ObjectPtr;

-            try
-            {
-                Ensure.ZeroResult(NativeMethods.git_blob_filtered_content(buf, osw.ObjectPtr, path, check_for_binary_data));
-            }
-            catch (Exception)
-            {
-                osw.Dispose();
-                buf.Dispose();
-                throw;
-            }
-
-            return new GitBufStream(buf, osw);
+            return new RawContentStream(handle, h =>
+                                                {
+                                                    Ensure.ZeroResult(NativeMethods.git_blob_filtered_content(buf, h, path, check_for_binary_data));
+                                                    return buf.ptr;
+                                                },
+                                                h => (long) buf.size,
+                                                new[] { buf });
         }

         public static byte[] git_blob_rawcontent(RepositorySafeHandle repo, ObjectId id, int size)
@@ -104,7 +99,8 @@ public static byte[] git_blob_rawcontent(RepositorySafeHandle repo, ObjectId id,

         public static UnmanagedMemoryStream git_blob_rawcontent_stream(RepositorySafeHandle repo, ObjectId id, Int64 size)
         {
-            return new RawContentStream(id, repo, NativeMethods.git_blob_rawcontent, size);
+            var handle = new ObjectSafeWrapper(id, repo).ObjectPtr;
+            return new RawContentStream(handle, NativeMethods.git_blob_rawcontent, h => size);
         }

         public static Int64 git_blob_rawsize(GitObjectSafeHandle obj)
diff --git a/LibGit2Sharp/Core/RawContentStream.cs b/LibGit2Sharp/Core/RawContentStream.cs
dissimilarity index 63%
index 825b097..7b788eb 100644
--- a/LibGit2Sharp/Core/RawContentStream.cs
+++ b/LibGit2Sharp/Core/RawContentStream.cs
@@ -1,30 +1,69 @@
-using System;
-using System.IO;
-using LibGit2Sharp.Core.Handles;
-
-namespace LibGit2Sharp.Core
-{
-    internal class RawContentStream : UnmanagedMemoryStream
-    {
-        private readonly ObjectSafeWrapper wrapper;
-
-        internal RawContentStream(ObjectId id, RepositorySafeHandle repo,
-            Func<GitObjectSafeHandle, IntPtr> bytePtrProvider, long length)
-            : this(new ObjectSafeWrapper(id, repo), bytePtrProvider, length)
-        {
-        }
-
-        unsafe RawContentStream(ObjectSafeWrapper wrapper,
-            Func<GitObjectSafeHandle, IntPtr> bytePtrProvider, long length)
-            : base((byte*)bytePtrProvider(wrapper.ObjectPtr).ToPointer(), length)
-        {
-            this.wrapper = wrapper;
-        }
-
-        protected override void Dispose(bool disposing)
-        {
-            base.Dispose(disposing);
-            wrapper.SafeDispose();
-        }
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using LibGit2Sharp.Core.Handles;
+
+namespace LibGit2Sharp.Core
+{
+    internal class RawContentStream : UnmanagedMemoryStream
+    {
+        private readonly GitObjectSafeHandle handle;
+        private readonly ICollection<IDisposable> linkedResources;
+
+        internal unsafe RawContentStream(
+            GitObjectSafeHandle handle,
+            Func<GitObjectSafeHandle, IntPtr> bytePtrProvider,
+            Func<GitObjectSafeHandle, long> sizeProvider,
+            ICollection<IDisposable> linkedResources = null)
+            : base((byte*)Wrap(handle, bytePtrProvider, linkedResources).ToPointer(),
+            Wrap(handle, sizeProvider, linkedResources))
+        {
+            this.handle = handle;
+            this.linkedResources = linkedResources;
+        }
+
+        private static T Wrap<T>(
+            GitObjectSafeHandle handle,
+            Func<GitObjectSafeHandle, T> provider,
+            IEnumerable<IDisposable> linkedResources)
+        {
+            T value;
+
+            try
+            {
+                value = provider(handle);
+            }
+            catch
+            {
+                Dispose(handle, linkedResources);
+                throw;
+            }
+
+            return value;
+        }
+
+        private static void Dispose(
+            GitObjectSafeHandle handle,
+            IEnumerable<IDisposable> linkedResources)
+        {
+            handle.SafeDispose();
+
+            if (linkedResources == null)
+            {
+                return;
+            }
+
+            foreach (IDisposable linkedResource in linkedResources)
+            {
+                linkedResource.Dispose();
+            }
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            base.Dispose(disposing);
+
+            Dispose(handle, linkedResources);
+        }
+    }
+}
diff --git a/LibGit2Sharp/LibGit2Sharp.csproj b/LibGit2Sharp/LibGit2Sharp.csproj
index 6931791..620847f 100644
--- a/LibGit2Sharp/LibGit2Sharp.csproj
+++ b/LibGit2Sharp/LibGit2Sharp.csproj
@@ -74,7 +74,6 @@
     <Compile Include="Core\EncodingMarshaler.cs" />
     <Compile Include="PushOptions.cs" />
     <Compile Include="Core\GitBuf.cs" />
-    <Compile Include="Core\GitBufStream.cs" />
     <Compile Include="FilteringOptions.cs" />
     <Compile Include="UnbornBranchException.cs" />
     <Compile Include="LockedFileException.cs" />

@ethomson
Copy link
Member Author

Sure!

@ethomson
Copy link
Member Author

Updated with your fancy new RawContentStream, @nulltoken .

Get the content of a blob as it would be checked out to the
working directory via the filters.
@nulltoken nulltoken merged commit 31291fe into libgit2:vNext Oct 18, 2013
@nulltoken
Copy link
Member

AWE-SOME ❤️

@ethomson
Copy link
Member Author

Great stuff, thanks @nulltoken !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants