Browse Source

Optimized the generation of C# by not splitting any strings.

Our indention of lines in blocks was inadequate so we used to split strings by new lines, indent each line and put it back together. Creation of new strings is incredibly slow so this bogged our code generation down. I have now fixed indention properly and we no longer need to split already written blocks to indent them.

Signed-off-by: Dimitar Dobrev <dpldobrev@protonmail.com>
pull/1129/head
Dimitar Dobrev 7 years ago committed by João Matos
parent
commit
d243780e67
  1. 2
      src/Generator/Generators/CLI/CLIMarshal.cs
  2. 22
      src/Generator/Generators/CLI/CLISources.cs
  3. 9
      src/Generator/Generators/CSharp/CSharpMarshal.cs
  4. 73
      src/Generator/Generators/CSharp/CSharpSources.cs
  5. 15
      src/Generator/Generators/Marshal.cs
  6. 4
      src/Generator/Types/Std/Stdlib.cs
  7. 14
      src/Generator/Utils/BlockGenerator.cs
  8. 19
      src/Generator/Utils/TextGenerator.cs
  9. 21
      src/Generator/Utils/Utils.cs

2
src/Generator/Generators/CLI/CLIMarshal.cs

@ -775,7 +775,7 @@ namespace CppSharp.Generators.CLI
var fieldRef = string.Format("{0}.{1}", Context.Parameter.Name, var fieldRef = string.Format("{0}.{1}", Context.Parameter.Name,
property.Name); property.Name);
var marshalCtx = new MarshalContext(Context.Context) var marshalCtx = new MarshalContext(Context.Context, Context.Indent)
{ {
ArgName = fieldRef, ArgName = fieldRef,
ParameterIndex = Context.ParameterIndex++, ParameterIndex = Context.ParameterIndex++,

22
src/Generator/Generators/CLI/CLISources.cs

@ -396,7 +396,7 @@ namespace CppSharp.Generators.CLI
else else
variable = $"((::{@class.QualifiedOriginalName}*)NativePtr)->{decl.OriginalName}"; variable = $"((::{@class.QualifiedOriginalName}*)NativePtr)->{decl.OriginalName}";
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
Parameter = param, Parameter = param,
ArgName = param.Name, ArgName = param.Name,
@ -408,7 +408,7 @@ namespace CppSharp.Generators.CLI
if (isIndexer) if (isIndexer)
{ {
var ctx2 = new MarshalContext(Context) var ctx2 = new MarshalContext(Context, CurrentIndent)
{ {
Parameter = indexParameter, Parameter = indexParameter,
ArgName = indexParameter.Name ArgName = indexParameter.Name
@ -483,7 +483,7 @@ namespace CppSharp.Generators.CLI
variable = string.Format("((::{0}*)NativePtr)->{1}", variable = string.Format("((::{0}*)NativePtr)->{1}",
@class.QualifiedOriginalName, decl.OriginalName); @class.QualifiedOriginalName, decl.OriginalName);
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
Declaration = decl, Declaration = decl,
ArgName = decl.Name, ArgName = decl.Name,
@ -592,7 +592,7 @@ namespace CppSharp.Generators.CLI
var returns = new List<string>(); var returns = new List<string>();
foreach (var param in @event.Parameters) foreach (var param in @event.Parameters)
{ {
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
ReturnVarName = param.Name, ReturnVarName = param.Name,
ReturnType = param.QualifiedType ReturnType = param.QualifiedType
@ -686,7 +686,7 @@ namespace CppSharp.Generators.CLI
var nativeField = string.Format("{0}{1}", var nativeField = string.Format("{0}{1}",
nativeVar, property.Field.OriginalName); nativeVar, property.Field.OriginalName);
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
ArgName = property.Name, ArgName = property.Name,
ReturnVarName = nativeField, ReturnVarName = nativeField,
@ -834,7 +834,7 @@ namespace CppSharp.Generators.CLI
var paramIndex = 0; var paramIndex = 0;
foreach (var param in method.Parameters) foreach (var param in method.Parameters)
{ {
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
Function = method, Function = method,
Parameter = param, Parameter = param,
@ -870,7 +870,7 @@ namespace CppSharp.Generators.CLI
var varName = string.Format("_native.{0}", property.Field.OriginalName); var varName = string.Format("_native.{0}", property.Field.OriginalName);
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
ReturnVarName = varName, ReturnVarName = varName,
ReturnType = property.QualifiedType ReturnType = property.QualifiedType
@ -946,7 +946,7 @@ namespace CppSharp.Generators.CLI
WriteLine("auto {0} = ::{1}();", valueMarshalName, @class.QualifiedOriginalName); WriteLine("auto {0} = ::{1}();", valueMarshalName, @class.QualifiedOriginalName);
var param = new Parameter { Name = "(*this)" , Namespace = function.Namespace }; var param = new Parameter { Name = "(*this)" , Namespace = function.Namespace };
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
MarshalVarPrefix = valueMarshalName, MarshalVarPrefix = valueMarshalName,
Parameter = param Parameter = param
@ -1021,7 +1021,7 @@ namespace CppSharp.Generators.CLI
var nativeVarName = paramInfo.Name; var nativeVarName = paramInfo.Name;
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
ArgName = nativeVarName, ArgName = nativeVarName,
ReturnVarName = nativeVarName, ReturnVarName = nativeVarName,
@ -1054,7 +1054,7 @@ namespace CppSharp.Generators.CLI
isIntPtr ? "System::IntPtr()" : "nullptr"); isIntPtr ? "System::IntPtr()" : "nullptr");
} }
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
ArgName = returnIdentifier, ArgName = returnIdentifier,
ReturnVarName = returnIdentifier, ReturnVarName = returnIdentifier,
@ -1167,7 +1167,7 @@ namespace CppSharp.Generators.CLI
QualifiedType = new QualifiedType(paramType) QualifiedType = new QualifiedType(paramType)
}; };
var ctx = new MarshalContext(Context) var ctx = new MarshalContext(Context, CurrentIndent)
{ {
Parameter = effectiveParam, Parameter = effectiveParam,
ParameterIndex = paramIndex, ParameterIndex = paramIndex,

9
src/Generator/Generators/CSharp/CSharpMarshal.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using CppSharp.AST; using CppSharp.AST;
@ -10,11 +11,13 @@ namespace CppSharp.Generators.CSharp
{ {
public class CSharpMarshalContext : MarshalContext public class CSharpMarshalContext : MarshalContext
{ {
public CSharpMarshalContext(BindingContext context) public CSharpMarshalContext(BindingContext context, Stack<uint> indent)
: base(context) : base(context, indent)
{ {
ArgumentPrefix = new TextGenerator(); ArgumentPrefix = new TextGenerator();
indent.PushTo(ArgumentPrefix.CurrentIndent);
Cleanup = new TextGenerator(); Cleanup = new TextGenerator();
indent.PushTo(Cleanup.CurrentIndent);
} }
public TextGenerator ArgumentPrefix { get; private set; } public TextGenerator ArgumentPrefix { get; private set; }
@ -335,7 +338,7 @@ namespace CppSharp.Generators.CSharp
if (parameter.Usage == ParameterUsage.Unknown || parameter.IsIn) if (parameter.Usage == ParameterUsage.Unknown || parameter.IsIn)
return base.VisitParameterDecl(parameter); return base.VisitParameterDecl(parameter);
var ctx = new CSharpMarshalContext(Context.Context) var ctx = new CSharpMarshalContext(Context.Context, Context.Indent)
{ {
ReturnType = Context.ReturnType, ReturnType = Context.ReturnType,
ReturnVarName = Context.ReturnVarName ReturnVarName = Context.ReturnVarName

73
src/Generator/Generators/CSharp/CSharpSources.cs

@ -820,7 +820,7 @@ namespace CppSharp.Generators.CSharp
QualifiedType = var.QualifiedType QualifiedType = var.QualifiedType
}; };
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
Parameter = param, Parameter = param,
ArgName = param.Name, ArgName = param.Name,
@ -880,38 +880,40 @@ namespace CppSharp.Generators.CSharp
private void GenerateFieldSetter(Field field, Class @class, QualifiedType fieldType) private void GenerateFieldSetter(Field field, Class @class, QualifiedType fieldType)
{ {
string returnVar;
Type type = field.Type.Desugar();
var arrayType = type as ArrayType;
if (arrayType != null && @class.IsValueType)
{
returnVar = HandleValueArray(arrayType, field);
}
else
{
var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name;
var identifier = SafeIdentifier(name);
if (@class.IsValueType)
returnVar = $"{Helpers.InstanceField}.{identifier}";
else
returnVar = $"(({TypePrinter.PrintNative(@class)}*){Helpers.InstanceIdentifier})->{identifier}";
}
var param = new Parameter var param = new Parameter
{ {
Name = "value", Name = "value",
QualifiedType = field.QualifiedType QualifiedType = field.QualifiedType
}; };
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
Parameter = param, Parameter = param,
ArgName = param.Name, ArgName = param.Name,
ReturnVarName = returnVar
}; };
ctx.PushMarshalKind(MarshalKind.NativeField); ctx.PushMarshalKind(MarshalKind.NativeField);
var marshal = new CSharpMarshalManagedToNativePrinter(ctx); var marshal = new CSharpMarshalManagedToNativePrinter(ctx);
ctx.Declaration = field; ctx.Declaration = field;
Type type = field.Type.Desugar();
var arrayType = type as ArrayType;
if (arrayType != null && @class.IsValueType)
{
ctx.ReturnVarName = HandleValueArray(arrayType, field);
}
else
{
var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name;
var identifier = SafeIdentifier(name);
if (@class.IsValueType)
ctx.ReturnVarName = $"{Helpers.InstanceField}.{identifier}";
else
ctx.ReturnVarName = $"(({TypePrinter.PrintNative(@class)}*){Helpers.InstanceIdentifier})->{identifier}";
}
param.Visit(marshal); param.Visit(marshal);
if (!string.IsNullOrWhiteSpace(marshal.Context.Before)) if (!string.IsNullOrWhiteSpace(marshal.Context.Before))
@ -987,7 +989,7 @@ namespace CppSharp.Generators.CSharp
function.OriginalReturnType.Type.IsPointerTo(out type); function.OriginalReturnType.Type.IsPointerTo(out type);
var @internal = TypePrinter.PrintNative(function.Namespace); var @internal = TypePrinter.PrintNative(function.Namespace);
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
Parameter = new Parameter Parameter = new Parameter
{ {
@ -1108,7 +1110,7 @@ namespace CppSharp.Generators.CSharp
TypePrinter.PopContext(); TypePrinter.PopContext();
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
ArgName = var.Name, ArgName = var.Name,
ReturnType = new QualifiedType(var.Type) ReturnType = new QualifiedType(var.Type)
@ -1171,12 +1173,18 @@ namespace CppSharp.Generators.CSharp
private void GenerateFieldGetter(Field field, Class @class, QualifiedType returnType) private void GenerateFieldGetter(Field field, Class @class, QualifiedType returnType)
{ {
var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name; var name = @class.Layout.Fields.First(f => f.FieldPtr == field.OriginalPtr).Name;
String returnVar; string returnVar;
var arrayType = field.Type.Desugar() as ArrayType;
if (@class.IsValueType) if (@class.IsValueType)
returnVar = $@"{Helpers.InstanceField}.{SafeIdentifier(name)}"; {
if (arrayType != null)
returnVar = HandleValueArray(arrayType, field);
else
returnVar = $"{Helpers.InstanceField}.{SafeIdentifier(name)}";
}
else else
{ {
returnVar = $@"(({TypePrinter.PrintNative(@class)}*) {Helpers.InstanceIdentifier})->{SafeIdentifier(name)}"; returnVar = $"(({TypePrinter.PrintNative(@class)}*) {Helpers.InstanceIdentifier})->{SafeIdentifier(name)}";
// Class field getter should return a reference object instead of a copy. Wrapping `returnVar` in // Class field getter should return a reference object instead of a copy. Wrapping `returnVar` in
// IntPtr ensures that non-copying object constructor is invoked. // IntPtr ensures that non-copying object constructor is invoked.
Class typeClass; Class typeClass;
@ -1185,7 +1193,7 @@ namespace CppSharp.Generators.CSharp
returnVar = $"new {CSharpTypePrinter.IntPtrType}(&{returnVar})"; returnVar = $"new {CSharpTypePrinter.IntPtrType}(&{returnVar})";
} }
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
ArgName = field.Name, ArgName = field.Name,
Declaration = field, Declaration = field,
@ -1194,11 +1202,6 @@ namespace CppSharp.Generators.CSharp
}; };
ctx.PushMarshalKind(MarshalKind.NativeField); ctx.PushMarshalKind(MarshalKind.NativeField);
var arrayType = field.Type.Desugar() as ArrayType;
if (arrayType != null && @class.IsValueType)
ctx.ReturnVarName = HandleValueArray(arrayType, field);
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); var marshal = new CSharpMarshalNativeToManagedPrinter(ctx);
field.QualifiedType.Visit(marshal); field.QualifiedType.Visit(marshal);
@ -1666,7 +1669,7 @@ namespace CppSharp.Generators.CSharp
if (param.Kind == ParameterKind.IndirectReturnType) if (param.Kind == ParameterKind.IndirectReturnType)
continue; continue;
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
ReturnType = param.QualifiedType, ReturnType = param.QualifiedType,
ReturnVarName = param.Name, ReturnVarName = param.Name,
@ -1723,7 +1726,7 @@ namespace CppSharp.Generators.CSharp
}; };
// Marshal the managed result to native // Marshal the managed result to native
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
ArgName = Helpers.ReturnIdentifier, ArgName = Helpers.ReturnIdentifier,
Parameter = param, Parameter = param,
@ -1901,7 +1904,7 @@ namespace CppSharp.Generators.CSharp
var returns = new List<string>(); var returns = new List<string>();
foreach (var param in @event.Parameters) foreach (var param in @event.Parameters)
{ {
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
ReturnVarName = param.Name, ReturnVarName = param.Name,
ReturnType = param.QualifiedType ReturnType = param.QualifiedType
@ -2853,7 +2856,7 @@ namespace CppSharp.Generators.CSharp
if (needsReturn) if (needsReturn)
{ {
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
ArgName = Helpers.ReturnIdentifier, ArgName = Helpers.ReturnIdentifier,
ReturnVarName = Helpers.ReturnIdentifier, ReturnVarName = Helpers.ReturnIdentifier,
@ -2944,7 +2947,7 @@ namespace CppSharp.Generators.CSharp
var nativeVarName = paramInfo.Name; var nativeVarName = paramInfo.Name;
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
Parameter = param, Parameter = param,
ArgName = nativeVarName, ArgName = nativeVarName,
@ -3012,7 +3015,7 @@ namespace CppSharp.Generators.CSharp
} }
} }
var ctx = new CSharpMarshalContext(Context) var ctx = new CSharpMarshalContext(Context, CurrentIndent)
{ {
Parameter = param, Parameter = param,
ParameterIndex = paramIndex, ParameterIndex = paramIndex,

15
src/Generator/Generators/Marshal.cs

@ -1,23 +1,27 @@
using CppSharp.AST; using CppSharp.AST;
using System.Collections.Generic;
namespace CppSharp.Generators namespace CppSharp.Generators
{ {
public class MarshalContext : TypePrinter public class MarshalContext : TypePrinter
{ {
public MarshalContext(BindingContext context) public MarshalContext(BindingContext context, Stack<uint> indent)
{ {
Context = context; Context = context;
Before = new TextGenerator(); Before = new TextGenerator();
indent.PushTo(Before.CurrentIndent);
Return = new TextGenerator(); Return = new TextGenerator();
indent.PushTo(Return.CurrentIndent);
MarshalVarPrefix = string.Empty; MarshalVarPrefix = string.Empty;
this.Indent = indent;
} }
public BindingContext Context { get; private set; } public BindingContext Context { get; }
public MarshalPrinter<MarshalContext> MarshalToNative; public MarshalPrinter<MarshalContext> MarshalToNative;
public TextGenerator Before { get; private set; } public TextGenerator Before { get; }
public TextGenerator Return { get; private set; } public TextGenerator Return { get; }
public string ReturnVarName { get; set; } public string ReturnVarName { get; set; }
public QualifiedType ReturnType { get; set; } public QualifiedType ReturnType { get; set; }
@ -27,11 +31,12 @@ namespace CppSharp.Generators
public Function Function { get; set; } public Function Function { get; set; }
public string MarshalVarPrefix { get; set; } public string MarshalVarPrefix { get; set; }
public Stack<uint> Indent { get; }
} }
public abstract class MarshalPrinter<T> : AstVisitor where T : MarshalContext public abstract class MarshalPrinter<T> : AstVisitor where T : MarshalContext
{ {
public T Context { get; private set; } public T Context { get; }
protected MarshalPrinter(T ctx) protected MarshalPrinter(T ctx)
{ {

4
src/Generator/Types/Std/Stdlib.cs

@ -235,7 +235,7 @@ namespace CppSharp.Types.Std
QualifiedType = type QualifiedType = type
}; };
var elementCtx = new MarshalContext(ctx.Context) var elementCtx = new MarshalContext(ctx.Context, ctx.Indent)
{ {
Parameter = param, Parameter = param,
ArgName = param.Name, ArgName = param.Name,
@ -280,7 +280,7 @@ namespace CppSharp.Types.Std
ctx.ReturnVarName); ctx.ReturnVarName);
ctx.Before.WriteStartBraceIndent(); ctx.Before.WriteStartBraceIndent();
{ {
var elementCtx = new MarshalContext(ctx.Context) var elementCtx = new MarshalContext(ctx.Context, ctx.Indent)
{ {
ReturnVarName = "_element", ReturnVarName = "_element",
ReturnType = type ReturnType = type

14
src/Generator/Utils/BlockGenerator.cs

@ -176,8 +176,6 @@ namespace CppSharp
#region ITextGenerator implementation #region ITextGenerator implementation
public uint Indent { get { return Text.Indent; } }
public void Write(string msg, params object[] args) public void Write(string msg, params object[] args)
{ {
Text.Write(msg, args); Text.Write(msg, args);
@ -240,8 +238,9 @@ namespace CppSharp
public abstract class BlockGenerator : ITextGenerator public abstract class BlockGenerator : ITextGenerator
{ {
public Block RootBlock { get; private set; } public Block RootBlock { get; }
public Block ActiveBlock { get; private set; } public Block ActiveBlock { get; private set; }
public Stack<uint> CurrentIndent => ActiveBlock.Text.CurrentIndent;
protected BlockGenerator() protected BlockGenerator()
{ {
@ -264,12 +263,7 @@ namespace CppSharp
public void PushBlock(BlockKind kind = BlockKind.Unknown, object obj = null) public void PushBlock(BlockKind kind = BlockKind.Unknown, object obj = null)
{ {
var block = new Block { Kind = kind, Object = obj }; var block = new Block { Kind = kind, Object = obj };
var array = new uint[ActiveBlock.Text.CurrentIndent.Count]; CurrentIndent.PushTo(block.Text.CurrentIndent);
ActiveBlock.Text.CurrentIndent.CopyTo(array, 0);
foreach (var indent in array.Reverse())
{
block.Text.CurrentIndent.Push(indent);
}
block.Text.IsStartOfLine = ActiveBlock.Text.IsStartOfLine; block.Text.IsStartOfLine = ActiveBlock.Text.IsStartOfLine;
block.Text.NeedsNewLine = ActiveBlock.Text.NeedsNewLine; block.Text.NeedsNewLine = ActiveBlock.Text.NeedsNewLine;
PushBlock(block); PushBlock(block);
@ -306,8 +300,6 @@ namespace CppSharp
#region ITextGenerator implementation #region ITextGenerator implementation
public uint Indent { get { return ActiveBlock.Indent; } }
public void Write(string msg, params object[] args) public void Write(string msg, params object[] args)
{ {
ActiveBlock.Write(msg, args); ActiveBlock.Write(msg, args);

19
src/Generator/Utils/TextGenerator.cs

@ -7,7 +7,6 @@ namespace CppSharp
{ {
public interface ITextGenerator public interface ITextGenerator
{ {
uint Indent { get; }
void Write(string msg, params object[] args); void Write(string msg, params object[] args);
void WriteLine(string msg, params object[] args); void WriteLine(string msg, params object[] args);
void WriteLineIndent(string msg, params object[] args); void WriteLineIndent(string msg, params object[] args);
@ -30,11 +29,6 @@ namespace CppSharp
public bool NeedsNewLine { get; set; } public bool NeedsNewLine { get; set; }
public Stack<uint> CurrentIndent { get; } = new Stack<uint>(); public Stack<uint> CurrentIndent { get; } = new Stack<uint>();
public uint Indent
{
get { return (uint)CurrentIndent.Sum(u => (int)u); }
}
public TextGenerator() public TextGenerator()
{ {
} }
@ -60,16 +54,13 @@ namespace CppSharp
if (args.Length > 0) if (args.Length > 0)
msg = string.Format(msg, args); msg = string.Format(msg, args);
foreach(var line in msg.SplitAndKeep(Environment.NewLine)) if (IsStartOfLine && !string.IsNullOrWhiteSpace(msg))
{ StringBuilder.Append(new string(' ', (int) CurrentIndent.Sum(u => u)));
if (IsStartOfLine && !string.IsNullOrWhiteSpace(line))
StringBuilder.Append(new string(' ', (int) CurrentIndent.Sum(u => u)));
if (line.Length > 0) if (msg.Length > 0)
IsStartOfLine = line.EndsWith(Environment.NewLine); IsStartOfLine = msg.EndsWith(Environment.NewLine);
StringBuilder.Append(line); StringBuilder.Append(msg);
}
} }
public void WriteLine(string msg, params object[] args) public void WriteLine(string msg, params object[] args)

21
src/Generator/Utils/Utils.cs

@ -49,13 +49,6 @@ namespace CppSharp
return str.Trim().Split(); return str.Trim().Split();
} }
public static IEnumerable<string> SplitAndKeep(this string s, string seperator)
{
string[] obj = s.Split(new[] { seperator }, StringSplitOptions.None);
return obj.Select((t, i) => i == obj.Length - 1 ? t : t + seperator);
}
public static string Capitalize(string s) public static string Capitalize(string s)
{ {
// Check for empty string. // Check for empty string.
@ -134,6 +127,18 @@ namespace CppSharp
return uri1.MakeRelativeUri(uri2).ToString(); return uri1.MakeRelativeUri(uri2).ToString();
} }
}
public static class CollectionExtensions
{
public static void PushTo<T>(this Stack<T> source, Stack<T> destination)
{
var array = new T[source.Count];
source.CopyTo(array, 0);
foreach (var indent in array.Reverse())
{
destination.Push(indent);
}
}
} }
} }

Loading…
Cancel
Save