Commit 162b656f authored by Ayende Rahien's avatar Ayende Rahien

Implementing string interning

parent e3aff478
using System;
namespace Google.ProtocolBuffers
{
public class ByteBuffer
{
public byte[] Buffer;
public int Offset;
public int Length;
private int hash;
public void ResetHash()
{
hash = 23;
for (var i = Offset; i < Offset + Length; i++)
{
hash = (hash * 23) ^ Buffer[i];
}
}
public ByteBuffer(byte[] buffer, int offset, int length)
{
Buffer = buffer;
Offset = offset;
Length = length;
ResetHash();
}
public ByteString ToByteString()
{
return ByteString.CopyFrom(Buffer, Offset, Length);
}
public override int GetHashCode()
{
return hash;
}
public override bool Equals(object obj)
{
var other = obj as ByteBuffer;
if (other == null)
return false;
if (other.Offset != Offset)
return false;
if (other.Length != Length)
return false;
for (int i = Offset; i < Offset + Length; i++)
{
if (Buffer[i] != other.Buffer[i])
return false;
}
return true;
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Google.ProtocolBuffers
{
/// <summary>
/// This class tries hard to allow us to generate strings directly from buffer outputs without having to
///
/// Note, non thread safe
/// </summary>
public class ByteStringStringInterning
{
private class ByteStringOrByteBuffer : IEquatable<ByteStringOrByteBuffer>
{
private readonly ByteString str;
private readonly ByteBuffer buffer;
public ByteStringOrByteBuffer(ByteString str)
{
this.str = str;
}
public ByteStringOrByteBuffer(ByteBuffer buffer)
{
this.buffer = buffer;
}
public bool Equals(ByteStringOrByteBuffer other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
if(other.str!=null && str != null)
return Equals(other.str, str);
if (other.buffer != null && buffer != null)
return Equals(other.buffer, buffer);
if (other.str != null && str == null)
return StringEqualsToBuffer(other.str, buffer);
return StringEqualsToBuffer(str, other.buffer);
}
private static bool StringEqualsToBuffer(ByteString byteString, ByteBuffer byteBuffer)
{
var strLen = byteString.Length;
if(strLen != byteBuffer.Length)
return false;
for (int i = 0; i < strLen; i++)
{
if(byteString.bytes[i] != byteBuffer.Buffer[byteBuffer.Offset+i])
return false;
}
return true;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals(obj as ByteStringOrByteBuffer);
}
public override int GetHashCode()
{
return str != null ? str.GetHashCode() : buffer.GetHashCode();
}
}
private readonly int limit;
private int timestamp;
private readonly IDictionary<ByteStringOrByteBuffer, Data> strings = new Dictionary<ByteStringOrByteBuffer, Data>();
public static ByteStringStringInterning CreateInstance()
{
return new ByteStringStringInterning(65536);
}
[Serializable]
private class Data
{
public string Value;
public int Timestamp;
}
private ByteStringStringInterning(int limit)
{
this.limit = limit;
}
public void Clear()
{
strings.Clear();
}
public string Intern(ByteBuffer str)
{
Data val;
int currentTimestamp = Interlocked.Increment(ref timestamp);
if (strings.TryGetValue(new ByteStringOrByteBuffer(str), out val))
{
Interlocked.Exchange(ref val.Timestamp, currentTimestamp);
return val.Value;
}
var byteString = str.ToByteString();
val = new Data { Timestamp = currentTimestamp, Value = byteString.ToStringUtf8() };
strings.Add(new ByteStringOrByteBuffer(byteString), val);
DoCleanupIfNeeded();
return val.Value;
}
private void DoCleanupIfNeeded()
{
if (strings.Count <= limit)
return;
// to avoid frequent thrashing, we will remove the bottom 10% of the current pool in one go
// that means that we will hit the limit fairly infrequently
var list = new List<KeyValuePair<ByteStringOrByteBuffer, Data>>(strings);
list.Sort((x, y) => x.Value.Timestamp - y.Value.Timestamp);
for (int i = 0; i < limit/10; i++)
{
strings.Remove(list[i].Key);
}
}
}
}
\ No newline at end of file
...@@ -62,7 +62,10 @@ namespace Google.ProtocolBuffers { ...@@ -62,7 +62,10 @@ namespace Google.ProtocolBuffers {
private int bufferSizeAfterLimit = 0; private int bufferSizeAfterLimit = 0;
private int bufferPos = 0; private int bufferPos = 0;
private readonly Stream input; private readonly Stream input;
private uint lastTag = 0; private uint lastTag = 0;
private readonly ByteBuffer rawBytesBuffer = new ByteBuffer(new byte[BufferSize], 0, 0);
private readonly ByteStringStringInterning byteStringStringInterning = ByteStringStringInterning.CreateInstance();
internal const int DefaultRecursionLimit = 64; internal const int DefaultRecursionLimit = 64;
internal const int DefaultSizeLimit = 64 << 20; // 64MB internal const int DefaultSizeLimit = 64 << 20; // 64MB
...@@ -237,13 +240,13 @@ namespace Google.ProtocolBuffers { ...@@ -237,13 +240,13 @@ namespace Google.ProtocolBuffers {
} }
if (size <= bufferSize - bufferPos) { if (size <= bufferSize - bufferPos) {
// Fast path: We already have the bytes in a contiguous buffer, so // Fast path: We already have the bytes in a contiguous buffer, so
// just copy directly from it. // just copy directly from it.
String result = Encoding.UTF8.GetString(buffer, bufferPos, size); String result = byteStringStringInterning.Intern(new ByteBuffer(buffer, bufferPos, size));
bufferPos += size; bufferPos += size;
return result; return result;
} }
// Slow path: Build a byte array first then copy it. // Slow path: Build a byte array first then copy it.
return Encoding.UTF8.GetString(ReadRawBytes(size), 0, size); return byteStringStringInterning.Intern(ReadRawBytes(size));
} }
/// <summary> /// <summary>
...@@ -302,8 +305,9 @@ namespace Google.ProtocolBuffers { ...@@ -302,8 +305,9 @@ namespace Google.ProtocolBuffers {
bufferPos += size; bufferPos += size;
return result; return result;
} else { } else {
// Slow path: Build a byte array first then copy it. // Slow path: Build a byte array first then copy it.
return ByteString.CopyFrom(ReadRawBytes(size)); ByteBuffer rawBytes = ReadRawBytes(size);
return ByteString.CopyFrom(rawBytes.Buffer, rawBytes.Offset, rawBytes.Length);
} }
} }
...@@ -763,7 +767,7 @@ namespace Google.ProtocolBuffers { ...@@ -763,7 +767,7 @@ namespace Google.ProtocolBuffers {
/// <exception cref="InvalidProtocolBufferException"> /// <exception cref="InvalidProtocolBufferException">
/// the end of the stream or the current limit was reached /// the end of the stream or the current limit was reached
/// </exception> /// </exception>
public byte[] ReadRawBytes(int size) { public ByteBuffer ReadRawBytes(int size) {
if (size < 0) { if (size < 0) {
throw InvalidProtocolBufferException.NegativeSize(); throw InvalidProtocolBufferException.NegativeSize();
} }
...@@ -776,19 +780,19 @@ namespace Google.ProtocolBuffers { ...@@ -776,19 +780,19 @@ namespace Google.ProtocolBuffers {
} }
if (size <= bufferSize - bufferPos) { if (size <= bufferSize - bufferPos) {
// We have all the bytes we need already. // We have all the bytes we need already.
byte[] bytes = new byte[size]; var result = new ByteBuffer(buffer, bufferPos, size);
Array.Copy(buffer, bufferPos, bytes, 0, size); bufferPos += size;
bufferPos += size; return result;
return bytes;
} else if (size < BufferSize) { } else if (size < BufferSize) {
// Reading more bytes than are in the buffer, but not an excessive number // Reading more bytes than are in the buffer, but not an excessive number
// of bytes. We can safely allocate the resulting array ahead of time. // of bytes. We can safely allocate the resulting array ahead of time.
// First copy what we have. // First copy what we have.
byte[] bytes = new byte[size]; rawBytesBuffer.Length = size;
rawBytesBuffer.Offset = 0;
int pos = bufferSize - bufferPos; int pos = bufferSize - bufferPos;
Array.Copy(buffer, bufferPos, bytes, 0, pos); Array.Copy(buffer, bufferPos, rawBytesBuffer.Buffer, 0, pos);
bufferPos = bufferSize; bufferPos = bufferSize;
// We want to use RefillBuffer() and then copy from the buffer into our // We want to use RefillBuffer() and then copy from the buffer into our
...@@ -797,16 +801,16 @@ namespace Google.ProtocolBuffers { ...@@ -797,16 +801,16 @@ namespace Google.ProtocolBuffers {
RefillBuffer(true); RefillBuffer(true);
while (size - pos > bufferSize) { while (size - pos > bufferSize) {
Array.Copy(buffer, 0, bytes, pos, bufferSize); Array.Copy(buffer, 0, rawBytesBuffer.Buffer, pos, bufferSize);
pos += bufferSize; pos += bufferSize;
bufferPos = bufferSize; bufferPos = bufferSize;
RefillBuffer(true); RefillBuffer(true);
} }
Array.Copy(buffer, 0, bytes, pos, size - pos); Array.Copy(buffer, 0, rawBytesBuffer.Buffer, pos, size - pos);
bufferPos = size - pos; bufferPos = size - pos;
rawBytesBuffer.ResetHash();
return bytes; return rawBytesBuffer;
} else { } else {
// The size is very large. For security reasons, we can't allocate the // The size is very large. For security reasons, we can't allocate the
// entire byte array yet. The size comes directly from the input, so a // entire byte array yet. The size comes directly from the input, so a
...@@ -859,7 +863,7 @@ namespace Google.ProtocolBuffers { ...@@ -859,7 +863,7 @@ namespace Google.ProtocolBuffers {
} }
// Done. // Done.
return bytes; return new ByteBuffer(buffer, 0, size);
} }
} }
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion> <ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{6908BDCE-D925-43F3-94AC-A531E6DF2591}</ProjectGuid> <ProjectGuid>{6908BDCE-D925-43F3-94AC-A531E6DF2591}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Google.ProtocolBuffers</RootNamespace> <RootNamespace>Google.ProtocolBuffers</RootNamespace>
<AssemblyName>Google.ProtocolBuffers</AssemblyName> <AssemblyName>Google.ProtocolBuffers</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion> <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Properties\Google.ProtocolBuffers.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>Properties\Google.ProtocolBuffers.snk</AssemblyOriginatorKeyFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants> <DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib> <NoStdLib>true</NoStdLib>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<NoStdLib>true</NoStdLib> <NoStdLib>true</NoStdLib>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Silverlight2|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Silverlight2|AnyCPU' ">
<OutputPath>bin\Silverlight2\</OutputPath> <OutputPath>bin\Silverlight2\</OutputPath>
<DefineConstants>TRACE;SILVERLIGHT2</DefineConstants> <DefineConstants>TRACE;SILVERLIGHT2</DefineConstants>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<NoStdLib>true</NoStdLib> <NoStdLib>true</NoStdLib>
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="mscorlib" /> <Reference Include="mscorlib" />
<Reference Include="System" /> <Reference Include="System" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AbstractBuilder.cs" /> <Compile Include="AbstractBuilder.cs" />
<Compile Include="AbstractMessage.cs" /> <Compile Include="AbstractMessage.cs" />
<Compile Include="ByteString.cs" /> <Compile Include="ByteBuffer.cs" />
<Compile Include="Collections\Enumerables.cs" /> <Compile Include="ByteString.cs" />
<Compile Include="Collections\IPopsicleList.cs" /> <Compile Include="ByteStringStringInterning.cs" />
<Compile Include="Collections\PopsicleList.cs" /> <Compile Include="Collections\Enumerables.cs" />
<Compile Include="Delegates.cs" /> <Compile Include="Collections\IPopsicleList.cs" />
<Compile Include="CodedInputStream.cs" /> <Compile Include="Collections\PopsicleList.cs" />
<Compile Include="CodedOutputStream.cs" /> <Compile Include="Delegates.cs" />
<Compile Include="Collections\Dictionaries.cs" /> <Compile Include="CodedInputStream.cs" />
<Compile Include="Collections\Lists.cs" /> <Compile Include="CodedOutputStream.cs" />
<Compile Include="Collections\ReadOnlyDictionary.cs" /> <Compile Include="Collections\Dictionaries.cs" />
<Compile Include="DescriptorProtos\CSharpOptions.cs" /> <Compile Include="Collections\Lists.cs" />
<Compile Include="DescriptorProtos\DescriptorProtoFile.cs" /> <Compile Include="Collections\ReadOnlyDictionary.cs" />
<Compile Include="DescriptorProtos\IDescriptorProto.cs" /> <Compile Include="DescriptorProtos\CSharpOptions.cs" />
<Compile Include="DescriptorProtos\PartialClasses.cs" /> <Compile Include="DescriptorProtos\DescriptorProtoFile.cs" />
<Compile Include="Descriptors\DescriptorBase.cs" /> <Compile Include="DescriptorProtos\IDescriptorProto.cs" />
<Compile Include="Descriptors\DescriptorPool.cs" /> <Compile Include="DescriptorProtos\PartialClasses.cs" />
<Compile Include="Descriptors\DescriptorUtil.cs" /> <Compile Include="Descriptors\DescriptorBase.cs" />
<Compile Include="Descriptors\DescriptorValidationException.cs" /> <Compile Include="Descriptors\DescriptorPool.cs" />
<Compile Include="Descriptors\EnumDescriptor.cs" /> <Compile Include="Descriptors\DescriptorUtil.cs" />
<Compile Include="Descriptors\EnumValueDescriptor.cs" /> <Compile Include="Descriptors\DescriptorValidationException.cs" />
<Compile Include="Descriptors\FieldDescriptor.cs" /> <Compile Include="Descriptors\EnumDescriptor.cs" />
<Compile Include="Descriptors\FieldMappingAttribute.cs" /> <Compile Include="Descriptors\EnumValueDescriptor.cs" />
<Compile Include="Descriptors\FieldType.cs" /> <Compile Include="Descriptors\FieldDescriptor.cs" />
<Compile Include="Descriptors\FileDescriptor.cs" /> <Compile Include="Descriptors\FieldMappingAttribute.cs" />
<Compile Include="Descriptors\IDescriptor.cs" /> <Compile Include="Descriptors\FieldType.cs" />
<Compile Include="Descriptors\IndexedDescriptorBase.cs" /> <Compile Include="Descriptors\FileDescriptor.cs" />
<Compile Include="Descriptors\MappedType.cs" /> <Compile Include="Descriptors\IDescriptor.cs" />
<Compile Include="Descriptors\MessageDescriptor.cs" /> <Compile Include="Descriptors\IndexedDescriptorBase.cs" />
<Compile Include="Descriptors\MethodDescriptor.cs" /> <Compile Include="Descriptors\MappedType.cs" />
<Compile Include="Descriptors\PackageDescriptor.cs" /> <Compile Include="Descriptors\MessageDescriptor.cs" />
<Compile Include="Descriptors\ServiceDescriptor.cs" /> <Compile Include="Descriptors\MethodDescriptor.cs" />
<Compile Include="DynamicMessage.cs" /> <Compile Include="Descriptors\PackageDescriptor.cs" />
<Compile Include="ExtendableBuilder.cs" /> <Compile Include="Descriptors\ServiceDescriptor.cs" />
<Compile Include="ExtendableMessage.cs" /> <Compile Include="DynamicMessage.cs" />
<Compile Include="ExtensionInfo.cs" /> <Compile Include="ExtendableBuilder.cs" />
<Compile Include="ExtensionRegistry.cs" /> <Compile Include="ExtendableMessage.cs" />
<Compile Include="FieldAccess\ReflectionUtil.cs" /> <Compile Include="ExtensionInfo.cs" />
<Compile Include="FieldAccess\SingleEnumAccessor.cs" /> <Compile Include="ExtensionRegistry.cs" />
<Compile Include="FieldAccess\SingleMessageAccessor.cs" /> <Compile Include="FieldAccess\ReflectionUtil.cs" />
<Compile Include="FieldAccess\SinglePrimitiveAccessor.cs" /> <Compile Include="FieldAccess\SingleEnumAccessor.cs" />
<Compile Include="FieldAccess\RepeatedPrimitiveAccessor.cs" /> <Compile Include="FieldAccess\SingleMessageAccessor.cs" />
<Compile Include="FieldAccess\RepeatedEnumAccessor.cs" /> <Compile Include="FieldAccess\SinglePrimitiveAccessor.cs" />
<Compile Include="FieldAccess\IFieldAccessor.cs" /> <Compile Include="FieldAccess\RepeatedPrimitiveAccessor.cs" />
<Compile Include="FieldAccess\FieldAccessorTable.cs" /> <Compile Include="FieldAccess\RepeatedEnumAccessor.cs" />
<Compile Include="FieldAccess\RepeatedMessageAccessor.cs" /> <Compile Include="FieldAccess\IFieldAccessor.cs" />
<Compile Include="FieldSet.cs" /> <Compile Include="FieldAccess\FieldAccessorTable.cs" />
<Compile Include="GeneratedBuilder.cs" /> <Compile Include="FieldAccess\RepeatedMessageAccessor.cs" />
<Compile Include="GeneratedRepeatExtension.cs" /> <Compile Include="FieldSet.cs" />
<Compile Include="GeneratedSingleExtension.cs" /> <Compile Include="GeneratedBuilder.cs" />
<Compile Include="GeneratedMessage.cs" /> <Compile Include="GeneratedRepeatExtension.cs" />
<Compile Include="IBuilder.cs" /> <Compile Include="GeneratedSingleExtension.cs" />
<Compile Include="GeneratedExtensionBase.cs" /> <Compile Include="GeneratedMessage.cs" />
<Compile Include="IMessage.cs" /> <Compile Include="IBuilder.cs" />
<Compile Include="InvalidProtocolBufferException.cs" /> <Compile Include="GeneratedExtensionBase.cs" />
<Compile Include="IRpcChannel.cs" /> <Compile Include="IMessage.cs" />
<Compile Include="IRpcController.cs" /> <Compile Include="InvalidProtocolBufferException.cs" />
<Compile Include="IService.cs" /> <Compile Include="IRpcChannel.cs" />
<Compile Include="MessageStreamIterator.cs" /> <Compile Include="IRpcController.cs" />
<Compile Include="MessageStreamWriter.cs" /> <Compile Include="IService.cs" />
<Compile Include="MessageUtil.cs" /> <Compile Include="MessageStreamIterator.cs" />
<Compile Include="NameHelpers.cs" /> <Compile Include="MessageStreamWriter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="MessageUtil.cs" />
<Compile Include="RpcUtil.cs" /> <Compile Include="NameHelpers.cs" />
<Compile Include="SilverlightCompatibility.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SortedList.cs" /> <Compile Include="RpcUtil.cs" />
<Compile Include="TextFormat.cs" /> <Compile Include="SilverlightCompatibility.cs" />
<Compile Include="TextGenerator.cs" /> <Compile Include="SortedList.cs" />
<Compile Include="TextTokenizer.cs" /> <Compile Include="TextFormat.cs" />
<Compile Include="ThrowHelper.cs" /> <Compile Include="TextGenerator.cs" />
<Compile Include="UninitializedMessageException.cs" /> <Compile Include="TextTokenizer.cs" />
<Compile Include="UnknownField.cs" /> <Compile Include="ThrowHelper.cs" />
<Compile Include="UnknownFieldSet.cs" /> <Compile Include="UninitializedMessageException.cs" />
<Compile Include="WireFormat.cs" /> <Compile Include="UnknownField.cs" />
</ItemGroup> <Compile Include="UnknownFieldSet.cs" />
<ItemGroup> <Compile Include="WireFormat.cs" />
<None Include="Properties\Google.ProtocolBuffers.snk" /> </ItemGroup>
</ItemGroup> <ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" Condition=" '$(Configuration)' != 'Silverlight2' " /> <None Include="Properties\Google.ProtocolBuffers.snk" />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight\v2.0\Microsoft.Silverlight.CSharp.targets" Condition=" '$(Configuration)' == 'Silverlight2' " /> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" Condition=" '$(Configuration)' != 'Silverlight2' " />
<Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight\v2.0\Microsoft.Silverlight.CSharp.targets" Condition=" '$(Configuration)' == 'Silverlight2' " />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
</Target> </Target>
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment