Skip to content

Custom filter Streams #946

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Feb 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2c0b022
Implement GitBufReadStream and GitBufWriteStream
dahlbyk Feb 7, 2015
09dc86e
Use Streams for Filter
dahlbyk Feb 7, 2015
4a0ddd1
fixup! Use Streams for Filter
dahlbyk Feb 10, 2015
703a8f7
Drop useless import
nulltoken Feb 10, 2015
08f2ff4
Drop unused native method
nulltoken Feb 10, 2015
d2da645
Add missing trailing newlines
nulltoken Feb 10, 2015
e0177f5
Drop trailing whitechars
nulltoken Feb 10, 2015
849a417
Update libgit2 to a2012c4
nulltoken Feb 10, 2015
fbfc5d1
leverage git_buf_put()
nulltoken Feb 10, 2015
3e8bb70
Ensure filter streams expose properties that makes sense
nulltoken Feb 10, 2015
7336dab
fixup! leverage git_buf_put()
nulltoken Feb 10, 2015
91c1a79
cleanup
nulltoken Feb 10, 2015
1a3e4c9
Don't crash the test runner
nulltoken Feb 10, 2015
a803a1f
Revert "Drop unused native method"
nulltoken Feb 11, 2015
5e02299
push down into proxy
nulltoken Feb 11, 2015
3b84e2d
IntPtr all the things!
nulltoken Feb 11, 2015
616ab06
fixup! Don't crash the test runner
nulltoken Feb 11, 2015
1238c81
fixup! Ensure filter streams expose properties that makes sense
nulltoken Feb 11, 2015
1adcff1
Reduce the number of reallocations
nulltoken Feb 11, 2015
89d9908
Fixes following @ammeep's review
nulltoken Feb 11, 2015
8eb623b
Drop unused var
nulltoken Feb 11, 2015
bf51ca9
Alternative proposal
nulltoken Feb 11, 2015
2b2c0f8
Drop leftover code
nulltoken Feb 12, 2015
10df3b1
Kill useless allocation
nulltoken Feb 12, 2015
21a6381
Merge pull request #948 from libgit2/ntk/filters_stream_rebound
fat16 Feb 13, 2015
2b0b714
Merge branch 'register-custom-filters' into dahlbyk/register-custom-f…
ammeep Feb 13, 2015
e070901
:fire: not used
ammeep Feb 13, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added Lib/NativeBinaries/amd64/git2-a2012c4.dll
Binary file not shown.
Binary file not shown.
Binary file removed Lib/NativeBinaries/amd64/git2-e0902fb.dll
Binary file not shown.
Binary file added Lib/NativeBinaries/x86/git2-a2012c4.dll
Binary file not shown.
Binary file not shown.
Binary file removed Lib/NativeBinaries/x86/git2-e0902fb.dll
Binary file not shown.
73 changes: 60 additions & 13 deletions LibGit2Sharp.Tests/FilterFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LibGit2Sharp.Core;
using LibGit2Sharp.Tests.TestHelpers;
using Xunit;

Expand All @@ -13,7 +12,7 @@ public class FilterFixture : BaseFixture
private const int GitPassThrough = -30;

readonly Func<FilterSource, IEnumerable<string>, int> checkPassThrough = (source, attr) => GitPassThrough;
readonly Func<GitBufReader, GitBufWriter, int> successCallback = (reader, writer) => 0;
readonly Func<Stream, Stream, int> successCallback = (reader, writer) => 0;
readonly Func<FilterSource, IEnumerable<string>, int> checkSuccess = (source, attr) => 0;

private const string FilterName = "the-filter";
Expand Down Expand Up @@ -106,7 +105,7 @@ public void ApplyCallbackMadeWhenCheckCallbackReturnsZero()
{
bool called = false;

Func<GitBufReader, GitBufWriter, int> applyCallback = (reader, writer) =>
Func<Stream, Stream, int> applyCallback = (reader, writer) =>
{
called = true;
return 0; //successCallback
Expand All @@ -131,7 +130,7 @@ public void ApplyCallbackNotMadeWhenCheckCallbackReturnsPassThrough()
{
bool called = false;

Func<GitBufReader, GitBufWriter, int> applyCallback = (reader, writer) =>
Func<Stream, Stream, int> applyCallback = (reader, writer) =>
{
called = true;
return 0;
Expand Down Expand Up @@ -271,7 +270,7 @@ public void WhenStagingFileApplyIsCalledWithCleanForCorrectPath()
string repoPath = InitNewRepository();
bool called = false;

Func<GitBufReader, GitBufWriter, int> clean = (reader, writer) =>
Func<Stream, Stream, int> clean = (reader, writer) =>
{
called = true;
return GitPassThrough;
Expand All @@ -297,7 +296,7 @@ public void CleanFilterWritesOutputToObjectTree()

string repoPath = InitNewRepository();

Func<GitBufReader, GitBufWriter, int> cleanCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;
Func<Stream, Stream, int> cleanCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;

var filter = new FakeFilter(FilterName + 16, attributes, checkSuccess, cleanCallback);

Expand Down Expand Up @@ -327,7 +326,7 @@ public void WhenCheckingOutAFileFileSmudgeWritesCorrectFileToWorkingDirectory()
const string branchName = "branch";
string repoPath = InitNewRepository();

Func<GitBufReader, GitBufWriter, int> smudgeCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;
Func<Stream, Stream, int> smudgeCallback = SubstitutionCipherFilter.RotateByThirteenPlaces;

var filter = new FakeFilter(FilterName + 17, attributes, checkSuccess, null, smudgeCallback);
GlobalSettings.RegisterFilter(filter);
Expand All @@ -341,6 +340,54 @@ public void WhenCheckingOutAFileFileSmudgeWritesCorrectFileToWorkingDirectory()
GlobalSettings.DeregisterFilter(filter.Name);
}

[Fact]
public void FilterStreamsAreCoherent()
{
string repoPath = InitNewRepository();

bool? inputCanWrite = null, inputCanRead = null, inputCanSeek = null;
bool? outputCanWrite = null, outputCanRead = null, outputCanSeek = null;

Func<Stream, Stream, int> assertor = (input, output) =>
{
inputCanRead = input.CanRead;
inputCanWrite = input.CanWrite;
inputCanSeek = input.CanSeek;

outputCanRead = output.CanRead;
outputCanWrite = output.CanWrite;
outputCanSeek = output.CanSeek;

return GitPassThrough;
};

var filter = new FakeFilter(FilterName + 18, attributes, checkSuccess, assertor, assertor);

GlobalSettings.RegisterFilter(filter);

using (var repo = CreateTestRepository(repoPath))
{
StageNewFile(repo);
}

GlobalSettings.DeregisterFilter(filter.Name);

Assert.True(inputCanRead.HasValue);
Assert.True(inputCanWrite.HasValue);
Assert.True(inputCanSeek.HasValue);
Assert.True(outputCanRead.HasValue);
Assert.True(outputCanWrite.HasValue);
Assert.True(outputCanSeek.HasValue);

Assert.True(inputCanRead.Value);
Assert.False(inputCanWrite.Value);
Assert.False(inputCanSeek.Value);

Assert.False(outputCanRead.Value);
Assert.True(outputCanWrite.Value);
Assert.False(outputCanSeek.Value);
}

private FileInfo CheckoutFileForSmudge(string repoPath, string branchName, string content)
{
FileInfo expectedPath;
Expand Down Expand Up @@ -394,14 +441,14 @@ public EmptyFilter(string name, IEnumerable<string> attributes)
class FakeFilter : Filter
{
private readonly Func<FilterSource, IEnumerable<string>, int> checkCallBack;
private readonly Func<GitBufReader, GitBufWriter, int> cleanCallback;
private readonly Func<GitBufReader, GitBufWriter, int> smudgeCallback;
private readonly Func<Stream, Stream, int> cleanCallback;
private readonly Func<Stream, Stream, int> smudgeCallback;
private readonly Func<int> initCallback;

public FakeFilter(string name, IEnumerable<string> attributes,
Func<FilterSource, IEnumerable<string>, int> checkCallBack = null,
Func<GitBufReader, GitBufWriter, int> cleanCallback = null,
Func<GitBufReader, GitBufWriter, int> smudgeCallback = null,
Func<Stream, Stream, int> cleanCallback = null,
Func<Stream, Stream, int> smudgeCallback = null,
Func<int> initCallback = null)
: base(name, attributes)
{
Expand All @@ -416,12 +463,12 @@ protected override int Check(IEnumerable<string> attributes, FilterSource filter
return checkCallBack != null ? checkCallBack(filterSource, attributes) : base.Check(attributes, filterSource);
}

protected override int Clean(string path, GitBufReader input, GitBufWriter output)
protected override int Clean(string path, Stream input, Stream output)
{
return cleanCallback != null ? cleanCallback(input, output) : base.Clean(path, input, output);
}

protected override int Smudge(string path, GitBufReader input, GitBufWriter output)
protected override int Smudge(string path, Stream input, Stream output)
{
return smudgeCallback != null ? smudgeCallback(input, output) : base.Smudge(path, input, output);
}
Expand Down
2 changes: 1 addition & 1 deletion LibGit2Sharp.Tests/SubstitutionCipherFilterFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,4 @@ private static Blob CommitOnBranchAndReturnDatabaseBlob(Repository repo, string
return blob;
}
}
}
}
41 changes: 24 additions & 17 deletions LibGit2Sharp.Tests/TestHelpers/SubstitutionCipherFilter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using LibGit2Sharp.Core;

namespace LibGit2Sharp.Tests.TestHelpers
{
Expand All @@ -21,34 +21,41 @@ protected override int Check(IEnumerable<string> attributes, FilterSource filter
return base.Check(attributes, filterSource);
}

protected override int Clean(string path, GitBufReader input, GitBufWriter output)
protected override int Clean(string path, Stream input, Stream output)
{
CleanCalledCount++;
return RotateByThirteenPlaces(input, output);
}

protected override int Smudge(string path, GitBufReader input, GitBufWriter output)
protected override int Smudge(string path, Stream input, Stream output)
{
SmudgeCalledCount++;
return RotateByThirteenPlaces(input, output);
}

public static int RotateByThirteenPlaces(GitBufReader input, GitBufWriter output)
public static int RotateByThirteenPlaces(Stream input, Stream output)
{
var inputString = Encoding.UTF8.GetString(input.ReadAll());
char[] array = inputString.ToCharArray();
char value;
for (int i = 0; i < inputString.Length; i++)

using (var streamReader = new StreamReader(input, Encoding.UTF8))
{
value = inputString[i];
if ((value >= 'a' && value <= 'm') || (value >= 'A' && value <= 'M'))
array[i] = (char)(value + 13);
else if ((value >= 'n' && value <= 'z') || (value >= 'N' && value <= 'Z'))
array[i] = (char)(value - 13);
var inputString = streamReader.ReadToEnd();
char[] array = inputString.ToCharArray();
for (int i = 0; i < inputString.Length; i++)
{
var value = inputString[i];
if ((value >= 'a' && value <= 'm') || (value >= 'A' && value <= 'M'))
array[i] = (char)(value + 13);
else if ((value >= 'n' && value <= 'z') || (value >= 'N' && value <= 'Z'))
array[i] = (char)(value - 13);
}

using (var streamWriter = new StreamWriter(output, Encoding.UTF8))
{
streamWriter.Write(array);
}

return 0;
}
var outputString = new string(array);
output.Write(Encoding.UTF8.GetBytes(outputString));
return 0;
}
}
}
}
48 changes: 48 additions & 0 deletions LibGit2Sharp/Core/GitBufReadStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Globalization;
using System.IO;
using LibGit2Sharp.Core.Handles;

namespace LibGit2Sharp.Core
{
/// <summary>
/// Reads data from a <see cref="GitBuf"/> pointer
/// </summary>
internal class GitBufReadStream : UnmanagedMemoryStream
{
internal GitBufReadStream(IntPtr gitBufPointer)
: this(gitBufPointer.MarshalAs<GitBuf>())
{ }

private unsafe GitBufReadStream(GitBuf gitBuf)
: base((byte*)gitBuf.ptr,
ConvertToLong(gitBuf.size),
ConvertToLong(gitBuf.asize),
FileAccess.Read)
{ }

private static long ConvertToLong(UIntPtr len)
{
if (len.ToUInt64() > long.MaxValue)
{
throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"Provided length ({0}) exceeds long.MaxValue ({1}).",
len.ToUInt64(), long.MaxValue));
}

return (long)len.ToUInt64();
}

public override long Seek(long offset, SeekOrigin loc)
{
throw new NotSupportedException();
}

public override bool CanSeek
{
get { return false; }
}
}
}
91 changes: 91 additions & 0 deletions LibGit2Sharp/Core/GitBufWriteStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System;
using System.IO;
using LibGit2Sharp.Core.Handles;

namespace LibGit2Sharp.Core
{
internal class GitBufWriteStream : Stream
{
private readonly IntPtr gitBufPointer;

internal GitBufWriteStream(IntPtr gitBufPointer)
{
this.gitBufPointer = gitBufPointer;

//Preallocate the buffer
Proxy.git_buf_grow(gitBufPointer, 1024);
}

public override void Flush()
{
}

public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}

public override void SetLength(long value)
{
throw new NotSupportedException();
}

public override int Read(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}

public override void Write(byte[] buffer, int offset, int count)
{
AutoGrowBuffer(count);

Proxy.git_buf_put(gitBufPointer, buffer, offset, count);
}

private void AutoGrowBuffer(int count)
{
var gitBuf = gitBufPointer.MarshalAs<GitBuf>();

var asize = (uint)gitBuf.asize;
var size = (uint)gitBuf.size;

var isBufferLargeEnoughToHoldTheNewData = (asize - size) > count;
var filledBufferPercentage = (100.0 * size / asize);

if (isBufferLargeEnoughToHoldTheNewData && filledBufferPercentage < 90)
{
return;
}

var targetSize = (uint)(1.5 * (asize + count));

Proxy.git_buf_grow(gitBufPointer, targetSize);
}

public override bool CanRead
{
get { return false; }
}

public override bool CanSeek
{
get { return false; }
}

public override bool CanWrite
{
get { return true; }
}

public override long Length
{
get { throw new NotSupportedException(); }
}

public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
}
}
2 changes: 1 addition & 1 deletion LibGit2Sharp/Core/NativeDllName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ namespace LibGit2Sharp.Core
{
internal static class NativeDllName
{
public const string Name = "git2-e0902fb";
public const string Name = "git2-a2012c4";
}
}
4 changes: 2 additions & 2 deletions LibGit2Sharp/Core/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,10 @@ internal static extern int git_branch_remote_name(
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalCookie = UniqueId.UniqueIdentifier, MarshalTypeRef = typeof(StrictUtf8Marshaler))] string canonical_branch_name);

[DllImport(libgit2)]
internal static extern int git_buf_grow(GitBuf buffer, UIntPtr targetSize);
internal static extern int git_buf_grow(IntPtr buffer, UIntPtr targetSize);

[DllImport(libgit2)]
internal static extern int git_buf_set(GitBuf buffer, IntPtr data, UIntPtr targetSize);
internal static extern int git_buf_put(IntPtr buffer, IntPtr data, UIntPtr len);

[DllImport(libgit2)]
internal static extern int git_remote_rename(
Expand Down
Loading