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 2 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
25 changes: 12 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 @@ -113,7 +112,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 @@ -138,7 +137,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 @@ -278,7 +277,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 @@ -304,7 +303,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, Attribute, checkSuccess, cleanCallback);

Expand Down Expand Up @@ -334,7 +333,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, Attribute, checkSuccess, null, smudgeCallback);
GlobalSettings.RegisterFilter(filter);
Expand Down Expand Up @@ -401,14 +400,14 @@ public EmptyFilter(string name, 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, 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 @@ -423,12 +422,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
38 changes: 22 additions & 16 deletions LibGit2Sharp.Tests/TestHelpers/SubstitutionCipherFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.IO;
using System.Linq;
using System.Text;
using LibGit2Sharp.Core;

namespace LibGit2Sharp.Tests.TestHelpers
{
Expand All @@ -23,34 +22,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;
}
}
}
50 changes: 50 additions & 0 deletions LibGit2Sharp/Core/GitBufReadStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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
{
private readonly GitBuf gitBuf;

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)
{
this.gitBuf = gitBuf;
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);

if (disposing && gitBuf != default(GitBuf))
Copy link
Member

Choose a reason for hiding this comment

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

You will need to 🔥 this.

You can't dispose of the underlying git_buf. Libgit2 allocates two of these for the entire filter chain and swaps the input and output as it moves between filters in the registry. It also free's the buffers after the filter registry has been executed. This is why the tests are failing on this branch 😄

gitBuf.Dispose();
}

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();
}
}
}
54 changes: 54 additions & 0 deletions LibGit2Sharp/Core/GitBufWriteStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using LibGit2Sharp.Core.Handles;

namespace LibGit2Sharp.Core
{
/// <summary>
/// Writes data to a <see cref="GitBuf"/> pointer
/// </summary>
internal class GitBufWriteStream : MemoryStream
{
private readonly IntPtr gitBufPointer;

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

protected override void Dispose(bool disposing)
{
using (var gitBuf = gitBufPointer.MarshalAs<GitBuf>())
WriteTo(gitBuf);

base.Dispose(disposing);
}

private void WriteTo(GitBuf gitBuf)
{
if (!base.CanSeek)
{
// Already closed; already written
return;
}

Seek(0, SeekOrigin.Begin);

var length = (int)Length;
var bytes = new byte[length];
Read(bytes, 0, length);

IntPtr reverseBytesPointer = Marshal.AllocHGlobal(length);
Marshal.Copy(bytes, 0, reverseBytesPointer, bytes.Length);

var size = (UIntPtr)length;
var allocatedSize = (UIntPtr)length;
Copy link
Member Author

Choose a reason for hiding this comment

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

In @ammeep's original implementation this was (UIntPtr)bytes.LongLength + 1. Based on the AllocHGlobal() call above this seems more correct, but I thought I'd call it out.

NativeMethods.git_buf_set(gitBuf, reverseBytesPointer, size);
gitBuf.size = size;
gitBuf.asize = allocatedSize;

Marshal.StructureToPtr(gitBuf, gitBufPointer, true);
}
}
}
105 changes: 0 additions & 105 deletions LibGit2Sharp/Core/GitBufWriter.cs

This file was deleted.

Loading