Whitespace changes
This commit is contained in:
parent
f965662031
commit
8879e98229
54
Helpers.cs
54
Helpers.cs
@ -1,8 +1,8 @@
|
||||
|
||||
|
||||
/*
|
||||
* TODO: Need to verify types are correct when deserializing.
|
||||
*/
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -22,10 +22,10 @@ namespace lib
|
||||
{
|
||||
FileStream fs = new FileStream( filename, FileMode.Create, FileAccess.Write );
|
||||
|
||||
XmlSerializer xs = new XmlSerializer( obj.GetType() );
|
||||
//MemoryStream memoryStream = new MemoryStream( StringToUTF8ByteArray( pXmlizedString ) );
|
||||
//XmlTextReader reader = new XmlTextReader( fs, Encoding.UTF8 );
|
||||
|
||||
XmlSerializer xs = new XmlSerializer( obj.GetType() );
|
||||
//MemoryStream memoryStream = new MemoryStream( StringToUTF8ByteArray( pXmlizedString ) );
|
||||
//XmlTextReader reader = new XmlTextReader( fs, Encoding.UTF8 );
|
||||
|
||||
xs.Serialize( fs, obj );
|
||||
}
|
||||
|
||||
@ -33,10 +33,10 @@ namespace lib
|
||||
{
|
||||
FileStream fs = new FileStream( filename, FileMode.Open, FileAccess.Read );
|
||||
|
||||
XmlSerializer xs = new XmlSerializer( typeof( TType ) );
|
||||
//MemoryStream memoryStream = new MemoryStream( StringToUTF8ByteArray( pXmlizedString ) );
|
||||
//XmlTextReader reader = new XmlTextReader( fs, Encoding.UTF8 );
|
||||
|
||||
XmlSerializer xs = new XmlSerializer( typeof( TType ) );
|
||||
//MemoryStream memoryStream = new MemoryStream( StringToUTF8ByteArray( pXmlizedString ) );
|
||||
//XmlTextReader reader = new XmlTextReader( fs, Encoding.UTF8 );
|
||||
|
||||
return xs.Deserialize( fs );
|
||||
}
|
||||
|
||||
@ -72,10 +72,10 @@ namespace lib
|
||||
{
|
||||
XmlTextWriter xmlWriter = new XmlTextWriter( filename, null );
|
||||
|
||||
xmlWriter.Formatting = Formatting.Indented;
|
||||
|
||||
//xmlWriter.WriteStartDocument();
|
||||
|
||||
xmlWriter.Formatting = Formatting.Indented;
|
||||
|
||||
//xmlWriter.WriteStartDocument();
|
||||
|
||||
xmlWriter.WriteStartElement( "dictionary" );
|
||||
|
||||
Type[] types = dict.GetType().GetGenericArguments();
|
||||
@ -93,10 +93,10 @@ namespace lib
|
||||
xmlWriter.WriteEndElement();
|
||||
}
|
||||
|
||||
xmlWriter.WriteEndElement();
|
||||
|
||||
//xmlWriter.WriteEndDocument();
|
||||
|
||||
xmlWriter.WriteEndElement();
|
||||
|
||||
//xmlWriter.WriteEndDocument();
|
||||
|
||||
xmlWriter.Close();
|
||||
}
|
||||
|
||||
@ -106,10 +106,10 @@ namespace lib
|
||||
|
||||
XmlDocument doc = new XmlDocument();
|
||||
|
||||
doc.Load( fs );
|
||||
|
||||
//CreateTypeFor()
|
||||
|
||||
doc.Load( fs );
|
||||
|
||||
//CreateTypeFor()
|
||||
|
||||
XmlElement docElem = doc.DocumentElement;
|
||||
|
||||
if( docElem.Name == "dictionary" )
|
||||
@ -125,10 +125,10 @@ namespace lib
|
||||
{
|
||||
XmlNodeList nodeList = docElem.ChildNodes;
|
||||
|
||||
object[] args = new object[ 1 ];
|
||||
|
||||
//fi.SetValue( newObj, obj );
|
||||
|
||||
object[] args = new object[ 1 ];
|
||||
|
||||
//fi.SetValue( newObj, obj );
|
||||
|
||||
foreach( XmlElement node in nodeList )
|
||||
{
|
||||
if( node.Name == "kvp" )
|
||||
|
||||
108
Token.cs
108
Token.cs
@ -1,54 +1,54 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
|
||||
//TODO PERF fix this and make it fast.
|
||||
|
||||
[Serializable]
|
||||
public struct Token
|
||||
{
|
||||
public string str { get { return m_str; } }
|
||||
|
||||
public Token( String str )
|
||||
{
|
||||
m_str = str;
|
||||
m_hash = m_str.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals( object obj )
|
||||
{
|
||||
if( !( obj is Token ) )
|
||||
return false;
|
||||
|
||||
//This doesnt use as because Token is a struct
|
||||
var otherId = (Token)obj;
|
||||
|
||||
if( m_hash != otherId.m_hash )
|
||||
return false;
|
||||
|
||||
return m_str == otherId.m_str;
|
||||
}
|
||||
|
||||
|
||||
public bool Equals_fast( Token other )
|
||||
{
|
||||
return m_hash == other.m_hash && m_str == other.m_str;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return m_str;
|
||||
}
|
||||
|
||||
int m_hash;
|
||||
String m_str;
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
|
||||
//TODO PERF fix this and make it fast.
|
||||
|
||||
[Serializable]
|
||||
public struct Token
|
||||
{
|
||||
public string str { get { return m_str; } }
|
||||
|
||||
public Token( String str )
|
||||
{
|
||||
m_str = str;
|
||||
m_hash = m_str.GetHashCode();
|
||||
}
|
||||
|
||||
public override bool Equals( object obj )
|
||||
{
|
||||
if( !( obj is Token ) )
|
||||
return false;
|
||||
|
||||
//This doesnt use as because Token is a struct
|
||||
var otherId = (Token)obj;
|
||||
|
||||
if( m_hash != otherId.m_hash )
|
||||
return false;
|
||||
|
||||
return m_str == otherId.m_str;
|
||||
}
|
||||
|
||||
|
||||
public bool Equals_fast( Token other )
|
||||
{
|
||||
return m_hash == other.m_hash && m_str == other.m_str;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_hash;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return m_str;
|
||||
}
|
||||
|
||||
int m_hash;
|
||||
String m_str;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,32 +1,32 @@
|
||||
// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
//
|
||||
// Copyright (c) 2010-2012 SharpDX - Alexandre Mutel
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
//
|
||||
// Copyright (c) 2010-2012 SharpDX - Alexandre Mutel
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
#pragma warning disable SA1300 // Element must begin with upper-case letter
|
||||
#pragma warning disable SA1649 // File name must match first type name
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class.
|
||||
/// </summary>
|
||||
|
||||
1700
Utilities.cs
1700
Utilities.cs
File diff suppressed because it is too large
Load Diff
@ -1,73 +1,73 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compression application using adaptive arithmetic coding.
|
||||
/// <para>Usage: java AdaptiveArithmeticCompress InputFile OutputFile</para>
|
||||
/// <para>Then use the corresponding "AdaptiveArithmeticDecompress" application to recreate the original input file.</para>
|
||||
/// <para>Note that the application starts with a flat frequency table of 257 symbols (all set to a frequency of 1),
|
||||
/// and updates it after each byte encoded. The corresponding decompressor program also starts with a flat
|
||||
/// frequency table and updates it after each byte decoded. It is by design that the compressor and
|
||||
/// decompressor have synchronized states, so that the data can be decompressed properly.</para>
|
||||
/// </summary>
|
||||
public class AdaptiveArithmeticCompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@@@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java AdaptiveArithmeticCompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Perform file compression
|
||||
using (Stream @in = new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read)), BitOutputStream @out = new BitOutputStream(new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write))))
|
||||
{
|
||||
compress(@in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void compress(java.io.InputStream in, BitOutputStream out) throws java.io.IOException
|
||||
internal static void compress(Stream @in, BitOutputStream @out)
|
||||
{
|
||||
FlatFrequencyTable initFreqs = new FlatFrequencyTable(257);
|
||||
FrequencyTable freqs = new SimpleFrequencyTable(initFreqs);
|
||||
ArithmeticEncoder enc = new ArithmeticEncoder(32, @out);
|
||||
while (true)
|
||||
{
|
||||
// Read and encode one byte
|
||||
int symbol = @in.ReadByte();
|
||||
if (symbol == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
enc.write(freqs, symbol);
|
||||
freqs.increment(symbol);
|
||||
}
|
||||
enc.write(freqs, 256); // EOF
|
||||
enc.finish(); // Flush remaining code bits
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compression application using adaptive arithmetic coding.
|
||||
/// <para>Usage: java AdaptiveArithmeticCompress InputFile OutputFile</para>
|
||||
/// <para>Then use the corresponding "AdaptiveArithmeticDecompress" application to recreate the original input file.</para>
|
||||
/// <para>Note that the application starts with a flat frequency table of 257 symbols (all set to a frequency of 1),
|
||||
/// and updates it after each byte encoded. The corresponding decompressor program also starts with a flat
|
||||
/// frequency table and updates it after each byte decoded. It is by design that the compressor and
|
||||
/// decompressor have synchronized states, so that the data can be decompressed properly.</para>
|
||||
/// </summary>
|
||||
public class AdaptiveArithmeticCompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@@@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java AdaptiveArithmeticCompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Perform file compression
|
||||
using (Stream @in = new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read)), BitOutputStream @out = new BitOutputStream(new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write))))
|
||||
{
|
||||
compress(@in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void compress(java.io.InputStream in, BitOutputStream out) throws java.io.IOException
|
||||
internal static void compress(Stream @in, BitOutputStream @out)
|
||||
{
|
||||
FlatFrequencyTable initFreqs = new FlatFrequencyTable(257);
|
||||
FrequencyTable freqs = new SimpleFrequencyTable(initFreqs);
|
||||
ArithmeticEncoder enc = new ArithmeticEncoder(32, @out);
|
||||
while (true)
|
||||
{
|
||||
// Read and encode one byte
|
||||
int symbol = @in.ReadByte();
|
||||
if (symbol == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
enc.write(freqs, symbol);
|
||||
freqs.increment(symbol);
|
||||
}
|
||||
enc.write(freqs, 256); // EOF
|
||||
enc.finish(); // Flush remaining code bits
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,67 +1,67 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decompression application using adaptive arithmetic coding.
|
||||
/// <para>Usage: java AdaptiveArithmeticDecompress InputFile OutputFile</para>
|
||||
/// <para>This decompresses files generated by the "AdaptiveArithmeticCompress" application.</para>
|
||||
/// </summary>
|
||||
public class AdaptiveArithmeticDecompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@@@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java AdaptiveArithmeticDecompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Perform file decompression
|
||||
using (BitInputStream @in = new BitInputStream(new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read))), Stream @out = new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write)))
|
||||
{
|
||||
decompress(@in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void decompress(BitInputStream in, java.io.OutputStream out) throws java.io.IOException
|
||||
internal static void decompress(BitInputStream @in, Stream @out)
|
||||
{
|
||||
FlatFrequencyTable initFreqs = new FlatFrequencyTable(257);
|
||||
FrequencyTable freqs = new SimpleFrequencyTable(initFreqs);
|
||||
ArithmeticDecoder dec = new ArithmeticDecoder(32, @in);
|
||||
while (true)
|
||||
{
|
||||
// Decode and write one byte
|
||||
int symbol = dec.read(freqs);
|
||||
if (symbol == 256) // EOF symbol
|
||||
{
|
||||
break;
|
||||
}
|
||||
@out.WriteByte((byte)symbol);
|
||||
freqs.increment(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decompression application using adaptive arithmetic coding.
|
||||
/// <para>Usage: java AdaptiveArithmeticDecompress InputFile OutputFile</para>
|
||||
/// <para>This decompresses files generated by the "AdaptiveArithmeticCompress" application.</para>
|
||||
/// </summary>
|
||||
public class AdaptiveArithmeticDecompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@@@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java AdaptiveArithmeticDecompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Perform file decompression
|
||||
using (BitInputStream @in = new BitInputStream(new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read))), Stream @out = new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write)))
|
||||
{
|
||||
decompress(@in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void decompress(BitInputStream in, java.io.OutputStream out) throws java.io.IOException
|
||||
internal static void decompress(BitInputStream @in, Stream @out)
|
||||
{
|
||||
FlatFrequencyTable initFreqs = new FlatFrequencyTable(257);
|
||||
FrequencyTable freqs = new SimpleFrequencyTable(initFreqs);
|
||||
ArithmeticDecoder dec = new ArithmeticDecoder(32, @in);
|
||||
while (true)
|
||||
{
|
||||
// Decode and write one byte
|
||||
int symbol = dec.read(freqs);
|
||||
if (symbol == 256) // EOF symbol
|
||||
{
|
||||
break;
|
||||
}
|
||||
@out.WriteByte((byte)symbol);
|
||||
freqs.increment(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,185 +1,185 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides the state and behaviors that arithmetic coding encoders and decoders share. </summary>
|
||||
/// <seealso cref= ArithmeticEncoder </seealso>
|
||||
/// <seealso cref= ArithmeticDecoder </seealso>
|
||||
public abstract class ArithmeticCoderBase
|
||||
{
|
||||
|
||||
/*---- Configuration fields ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Number of bits for the 'low' and 'high' state variables. Must be in the range [1, 62].
|
||||
/// <ul>
|
||||
/// <li>For state sizes less than the midpoint of around 32, larger values are generally better -
|
||||
/// they allow a larger maximum frequency total (maximumTotal), and they reduce the approximation
|
||||
/// error inherent in adapting fractions to integers; both effects reduce the data encoding loss
|
||||
/// and asymptotically approach the efficiency of arithmetic coding using exact fractions.</li>
|
||||
/// <li>But for state sizes greater than the midpoint, because intermediate computations are limited
|
||||
/// to the long integer type's 63-bit unsigned precision, larger state sizes will decrease the
|
||||
/// maximum frequency total, which might constrain the user-supplied probability model.</li>
|
||||
/// <li>Therefore numStateBits=32 is recommended as the most versatile setting
|
||||
/// because it maximizes maximumTotal (which ends up being slightly over 2^30).</li>
|
||||
/// <li>Note that numStateBits=62 is legal but useless because it implies maximumTotal=1,
|
||||
/// which means a frequency table can only support one symbol with non-zero frequency.</li>
|
||||
/// </ul>
|
||||
/// </summary>
|
||||
protected internal readonly int numStateBits;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum range (high+1-low) during coding (trivial), which is 2^numStateBits = 1000...000. </summary>
|
||||
protected internal readonly long fullRange;
|
||||
|
||||
/// <summary>
|
||||
/// The top bit at width numStateBits, which is 0100...000. </summary>
|
||||
protected internal readonly long halfRange;
|
||||
|
||||
/// <summary>
|
||||
/// The second highest bit at width numStateBits, which is 0010...000. This is zero when numStateBits=1. </summary>
|
||||
protected internal readonly long quarterRange;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum range (high+1-low) during coding (non-trivial), which is 0010...010. </summary>
|
||||
protected internal readonly long minimumRange;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum allowed total from a frequency table at all times during coding. </summary>
|
||||
protected internal readonly long maximumTotal;
|
||||
|
||||
/// <summary>
|
||||
/// Bit mask of numStateBits ones, which is 0111...111. </summary>
|
||||
protected internal readonly long stateMask;
|
||||
|
||||
|
||||
|
||||
/*---- State fields ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Low end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 0s.
|
||||
/// </summary>
|
||||
protected internal long low;
|
||||
|
||||
/// <summary>
|
||||
/// High end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 1s.
|
||||
/// </summary>
|
||||
protected internal long high;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an arithmetic coder, which initializes the code range. </summary>
|
||||
/// <param name="numBits"> the number of bits for the arithmetic coding range </param>
|
||||
/// <exception cref="IllegalArgumentException"> if stateSize is outside the range [1, 62] </exception>
|
||||
public ArithmeticCoderBase(int numBits)
|
||||
{
|
||||
if (numBits < 1 || numBits > 62)
|
||||
{
|
||||
throw new System.ArgumentException("State size out of range");
|
||||
}
|
||||
numStateBits = numBits;
|
||||
fullRange = 1L << numStateBits;
|
||||
halfRange = (long)((ulong)fullRange >> 1); // Non-zero
|
||||
quarterRange = (long)((ulong)halfRange >> 1); // Can be zero
|
||||
minimumRange = quarterRange + 2; // At least 2
|
||||
maximumTotal = Math.Min(long.MaxValue / fullRange, minimumRange);
|
||||
stateMask = fullRange - 1;
|
||||
|
||||
low = 0;
|
||||
high = stateMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Updates the code range (low and high) of this arithmetic coder as a result
|
||||
/// of processing the specified symbol with the specified frequency table.
|
||||
/// <para>Invariants that are true before and after encoding/decoding each symbol
|
||||
/// (letting fullRange = 2<sup>numStateBits</sup>):</para>
|
||||
/// <ul>
|
||||
/// <li>0 ≤ low ≤ code ≤ high < fullRange. ('code' exists only in the decoder.)
|
||||
/// Therefore these variables are unsigned integers of numStateBits bits.</li>
|
||||
/// <li>low < 1/2 × fullRange ≤ high.
|
||||
/// In other words, they are in different halves of the full range.</li>
|
||||
/// <li>(low < 1/4 × fullRange) || (high ≥ 3/4 × fullRange).
|
||||
/// In other words, they are not both in the middle two quarters.</li>
|
||||
/// <li>Let range = high − low + 1, then fullRange/4 < minimumRange ≤ range ≤
|
||||
/// fullRange. These invariants for 'range' essentially dictate the maximum total that the
|
||||
/// incoming frequency table can have, such that intermediate calculations don't overflow.</li>
|
||||
/// </ul> </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <param name="symbol"> the symbol that was processed </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol has zero frequency or the frequency table's total is too large </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void update(CheckedFrequencyTable freqs, int symbol) throws java.io.IOException
|
||||
protected internal virtual void update(CheckedFrequencyTable freqs, int symbol)
|
||||
{
|
||||
// State check
|
||||
Debug.Assert(low >= high || (low & stateMask) != low || (high & stateMask) != high, "Low or high out of range");
|
||||
|
||||
long range = high - low + 1;
|
||||
Debug.Assert(range < minimumRange || range > fullRange, "Range out of range");
|
||||
|
||||
// Frequency table values check
|
||||
long total = freqs.Total;
|
||||
long symLow = freqs.getLow(symbol);
|
||||
long symHigh = freqs.getHigh(symbol);
|
||||
Debug.Assert( symLow == symHigh, "Symbol has zero frequency");
|
||||
|
||||
Debug.Assert( total > maximumTotal, "Cannot code symbol because total is too large");
|
||||
|
||||
// Update range
|
||||
long newLow = low + symLow * range / total;
|
||||
long newHigh = low + symHigh * range / total - 1;
|
||||
low = newLow;
|
||||
high = newHigh;
|
||||
|
||||
// While low and high have the same top bit value, shift them out
|
||||
while (((low ^ high) & halfRange) == 0)
|
||||
{
|
||||
shift();
|
||||
low = ((low << 1) & stateMask);
|
||||
high = ((high << 1) & stateMask) | 1;
|
||||
}
|
||||
// Now low's top bit must be 0 and high's top bit must be 1
|
||||
|
||||
// While low's top two bits are 01 and high's are 10, delete the second highest bit of both
|
||||
while ((low & ~high & quarterRange) != 0)
|
||||
{
|
||||
underflow();
|
||||
low = (low << 1) ^ halfRange;
|
||||
high = ((high ^ halfRange) << 1) | halfRange | 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle the situation when the top bit of {@code low} and {@code high} are equal. </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected abstract void shift() throws java.io.IOException;
|
||||
protected internal abstract void shift();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle the situation when low=01(...) and high=10(...). </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected abstract void underflow() throws java.io.IOException;
|
||||
protected internal abstract void underflow();
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides the state and behaviors that arithmetic coding encoders and decoders share. </summary>
|
||||
/// <seealso cref= ArithmeticEncoder </seealso>
|
||||
/// <seealso cref= ArithmeticDecoder </seealso>
|
||||
public abstract class ArithmeticCoderBase
|
||||
{
|
||||
|
||||
/*---- Configuration fields ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Number of bits for the 'low' and 'high' state variables. Must be in the range [1, 62].
|
||||
/// <ul>
|
||||
/// <li>For state sizes less than the midpoint of around 32, larger values are generally better -
|
||||
/// they allow a larger maximum frequency total (maximumTotal), and they reduce the approximation
|
||||
/// error inherent in adapting fractions to integers; both effects reduce the data encoding loss
|
||||
/// and asymptotically approach the efficiency of arithmetic coding using exact fractions.</li>
|
||||
/// <li>But for state sizes greater than the midpoint, because intermediate computations are limited
|
||||
/// to the long integer type's 63-bit unsigned precision, larger state sizes will decrease the
|
||||
/// maximum frequency total, which might constrain the user-supplied probability model.</li>
|
||||
/// <li>Therefore numStateBits=32 is recommended as the most versatile setting
|
||||
/// because it maximizes maximumTotal (which ends up being slightly over 2^30).</li>
|
||||
/// <li>Note that numStateBits=62 is legal but useless because it implies maximumTotal=1,
|
||||
/// which means a frequency table can only support one symbol with non-zero frequency.</li>
|
||||
/// </ul>
|
||||
/// </summary>
|
||||
protected internal readonly int numStateBits;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum range (high+1-low) during coding (trivial), which is 2^numStateBits = 1000...000. </summary>
|
||||
protected internal readonly long fullRange;
|
||||
|
||||
/// <summary>
|
||||
/// The top bit at width numStateBits, which is 0100...000. </summary>
|
||||
protected internal readonly long halfRange;
|
||||
|
||||
/// <summary>
|
||||
/// The second highest bit at width numStateBits, which is 0010...000. This is zero when numStateBits=1. </summary>
|
||||
protected internal readonly long quarterRange;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum range (high+1-low) during coding (non-trivial), which is 0010...010. </summary>
|
||||
protected internal readonly long minimumRange;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum allowed total from a frequency table at all times during coding. </summary>
|
||||
protected internal readonly long maximumTotal;
|
||||
|
||||
/// <summary>
|
||||
/// Bit mask of numStateBits ones, which is 0111...111. </summary>
|
||||
protected internal readonly long stateMask;
|
||||
|
||||
|
||||
|
||||
/*---- State fields ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Low end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 0s.
|
||||
/// </summary>
|
||||
protected internal long low;
|
||||
|
||||
/// <summary>
|
||||
/// High end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 1s.
|
||||
/// </summary>
|
||||
protected internal long high;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an arithmetic coder, which initializes the code range. </summary>
|
||||
/// <param name="numBits"> the number of bits for the arithmetic coding range </param>
|
||||
/// <exception cref="IllegalArgumentException"> if stateSize is outside the range [1, 62] </exception>
|
||||
public ArithmeticCoderBase(int numBits)
|
||||
{
|
||||
if (numBits < 1 || numBits > 62)
|
||||
{
|
||||
throw new System.ArgumentException("State size out of range");
|
||||
}
|
||||
numStateBits = numBits;
|
||||
fullRange = 1L << numStateBits;
|
||||
halfRange = (long)((ulong)fullRange >> 1); // Non-zero
|
||||
quarterRange = (long)((ulong)halfRange >> 1); // Can be zero
|
||||
minimumRange = quarterRange + 2; // At least 2
|
||||
maximumTotal = Math.Min(long.MaxValue / fullRange, minimumRange);
|
||||
stateMask = fullRange - 1;
|
||||
|
||||
low = 0;
|
||||
high = stateMask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Updates the code range (low and high) of this arithmetic coder as a result
|
||||
/// of processing the specified symbol with the specified frequency table.
|
||||
/// <para>Invariants that are true before and after encoding/decoding each symbol
|
||||
/// (letting fullRange = 2<sup>numStateBits</sup>):</para>
|
||||
/// <ul>
|
||||
/// <li>0 ≤ low ≤ code ≤ high < fullRange. ('code' exists only in the decoder.)
|
||||
/// Therefore these variables are unsigned integers of numStateBits bits.</li>
|
||||
/// <li>low < 1/2 × fullRange ≤ high.
|
||||
/// In other words, they are in different halves of the full range.</li>
|
||||
/// <li>(low < 1/4 × fullRange) || (high ≥ 3/4 × fullRange).
|
||||
/// In other words, they are not both in the middle two quarters.</li>
|
||||
/// <li>Let range = high − low + 1, then fullRange/4 < minimumRange ≤ range ≤
|
||||
/// fullRange. These invariants for 'range' essentially dictate the maximum total that the
|
||||
/// incoming frequency table can have, such that intermediate calculations don't overflow.</li>
|
||||
/// </ul> </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <param name="symbol"> the symbol that was processed </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol has zero frequency or the frequency table's total is too large </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void update(CheckedFrequencyTable freqs, int symbol) throws java.io.IOException
|
||||
protected internal virtual void update(CheckedFrequencyTable freqs, int symbol)
|
||||
{
|
||||
// State check
|
||||
Debug.Assert(low >= high || (low & stateMask) != low || (high & stateMask) != high, "Low or high out of range");
|
||||
|
||||
long range = high - low + 1;
|
||||
Debug.Assert(range < minimumRange || range > fullRange, "Range out of range");
|
||||
|
||||
// Frequency table values check
|
||||
long total = freqs.Total;
|
||||
long symLow = freqs.getLow(symbol);
|
||||
long symHigh = freqs.getHigh(symbol);
|
||||
Debug.Assert( symLow == symHigh, "Symbol has zero frequency");
|
||||
|
||||
Debug.Assert( total > maximumTotal, "Cannot code symbol because total is too large");
|
||||
|
||||
// Update range
|
||||
long newLow = low + symLow * range / total;
|
||||
long newHigh = low + symHigh * range / total - 1;
|
||||
low = newLow;
|
||||
high = newHigh;
|
||||
|
||||
// While low and high have the same top bit value, shift them out
|
||||
while (((low ^ high) & halfRange) == 0)
|
||||
{
|
||||
shift();
|
||||
low = ((low << 1) & stateMask);
|
||||
high = ((high << 1) & stateMask) | 1;
|
||||
}
|
||||
// Now low's top bit must be 0 and high's top bit must be 1
|
||||
|
||||
// While low's top two bits are 01 and high's are 10, delete the second highest bit of both
|
||||
while ((low & ~high & quarterRange) != 0)
|
||||
{
|
||||
underflow();
|
||||
low = (low << 1) ^ halfRange;
|
||||
high = ((high ^ halfRange) << 1) | halfRange | 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle the situation when the top bit of {@code low} and {@code high} are equal. </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected abstract void shift() throws java.io.IOException;
|
||||
protected internal abstract void shift();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle the situation when low=01(...) and high=10(...). </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected abstract void underflow() throws java.io.IOException;
|
||||
protected internal abstract void underflow();
|
||||
|
||||
}
|
||||
|
||||
@ -1,127 +1,127 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compression application using static arithmetic coding.
|
||||
/// <para>Usage: java ArithmeticCompress InputFile OutputFile</para>
|
||||
/// <para>Then use the corresponding "ArithmeticDecompress" application to recreate the original input file.</para>
|
||||
/// <para>Note that the application uses an alphabet of 257 symbols - 256 symbols for the byte
|
||||
/// values and 1 symbol for the EOF marker. The compressed file format starts with a list
|
||||
/// of 256 symbol frequencies, and then followed by the arithmetic-coded data.</para>
|
||||
/// </summary>
|
||||
public class ArithmeticCompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java ArithmeticCompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Read input file once to compute symbol frequencies
|
||||
FrequencyTable freqs = getFrequencies(inputFile);
|
||||
freqs.increment(256); // EOF symbol gets a frequency of 1
|
||||
|
||||
// Read input file again, compress with arithmetic coding, and write output file
|
||||
using (Stream @in = new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read)), BitOutputStream @out = new BitOutputStream(new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write))))
|
||||
{
|
||||
writeFrequencies(@out, freqs);
|
||||
compress(freqs, @in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// Returns a frequency table based on the bytes in the given file.
|
||||
// Also contains an extra entry for symbol 256, whose frequency is set to 0.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static FrequencyTable getFrequencies(java.io.File file) throws java.io.IOException
|
||||
private static FrequencyTable getFrequencies(string file)
|
||||
{
|
||||
|
||||
|
||||
FrequencyTable freqs = new SimpleFrequencyTable(new int[257]);
|
||||
using (Stream input = new BufferedStream( new FileStream(file, FileMode.Open, FileAccess.Read)))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int b = input.ReadByte();
|
||||
if (b == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
freqs.increment(b);
|
||||
}
|
||||
}
|
||||
return freqs;
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void writeFrequencies(BitOutputStream out, FrequencyTable freqs) throws java.io.IOException
|
||||
internal static void writeFrequencies(BitOutputStream @out, FrequencyTable freqs)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
writeInt(@out, 32, freqs.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void compress(FrequencyTable freqs, java.io.InputStream in, BitOutputStream out) throws java.io.IOException
|
||||
internal static void compress(FrequencyTable freqs, Stream @in, BitOutputStream @out)
|
||||
{
|
||||
ArithmeticEncoder enc = new ArithmeticEncoder(32, @out);
|
||||
while (true)
|
||||
{
|
||||
int symbol = @in.ReadByte();
|
||||
if (symbol == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
enc.write(freqs, symbol);
|
||||
}
|
||||
enc.write(freqs, 256); // EOF
|
||||
enc.finish(); // Flush remaining code bits
|
||||
}
|
||||
|
||||
|
||||
// Writes an unsigned integer of the given bit width to the given stream.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static void writeInt(BitOutputStream out, int numBits, int value) throws java.io.IOException
|
||||
private static void writeInt(BitOutputStream @out, int numBits, int value)
|
||||
{
|
||||
if (numBits < 0 || numBits > 32)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
for (int i = numBits - 1; i >= 0; i--)
|
||||
{
|
||||
@out.write(((int)((uint)value >> i)) & 1); // Big endian
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compression application using static arithmetic coding.
|
||||
/// <para>Usage: java ArithmeticCompress InputFile OutputFile</para>
|
||||
/// <para>Then use the corresponding "ArithmeticDecompress" application to recreate the original input file.</para>
|
||||
/// <para>Note that the application uses an alphabet of 257 symbols - 256 symbols for the byte
|
||||
/// values and 1 symbol for the EOF marker. The compressed file format starts with a list
|
||||
/// of 256 symbol frequencies, and then followed by the arithmetic-coded data.</para>
|
||||
/// </summary>
|
||||
public class ArithmeticCompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java ArithmeticCompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Read input file once to compute symbol frequencies
|
||||
FrequencyTable freqs = getFrequencies(inputFile);
|
||||
freqs.increment(256); // EOF symbol gets a frequency of 1
|
||||
|
||||
// Read input file again, compress with arithmetic coding, and write output file
|
||||
using (Stream @in = new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read)), BitOutputStream @out = new BitOutputStream(new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write))))
|
||||
{
|
||||
writeFrequencies(@out, freqs);
|
||||
compress(freqs, @in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// Returns a frequency table based on the bytes in the given file.
|
||||
// Also contains an extra entry for symbol 256, whose frequency is set to 0.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static FrequencyTable getFrequencies(java.io.File file) throws java.io.IOException
|
||||
private static FrequencyTable getFrequencies(string file)
|
||||
{
|
||||
|
||||
|
||||
FrequencyTable freqs = new SimpleFrequencyTable(new int[257]);
|
||||
using (Stream input = new BufferedStream( new FileStream(file, FileMode.Open, FileAccess.Read)))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int b = input.ReadByte();
|
||||
if (b == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
freqs.increment(b);
|
||||
}
|
||||
}
|
||||
return freqs;
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void writeFrequencies(BitOutputStream out, FrequencyTable freqs) throws java.io.IOException
|
||||
internal static void writeFrequencies(BitOutputStream @out, FrequencyTable freqs)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
writeInt(@out, 32, freqs.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void compress(FrequencyTable freqs, java.io.InputStream in, BitOutputStream out) throws java.io.IOException
|
||||
internal static void compress(FrequencyTable freqs, Stream @in, BitOutputStream @out)
|
||||
{
|
||||
ArithmeticEncoder enc = new ArithmeticEncoder(32, @out);
|
||||
while (true)
|
||||
{
|
||||
int symbol = @in.ReadByte();
|
||||
if (symbol == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
enc.write(freqs, symbol);
|
||||
}
|
||||
enc.write(freqs, 256); // EOF
|
||||
enc.finish(); // Flush remaining code bits
|
||||
}
|
||||
|
||||
|
||||
// Writes an unsigned integer of the given bit width to the given stream.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static void writeInt(BitOutputStream out, int numBits, int value) throws java.io.IOException
|
||||
private static void writeInt(BitOutputStream @out, int numBits, int value)
|
||||
{
|
||||
if (numBits < 0 || numBits > 32)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
for (int i = numBits - 1; i >= 0; i--)
|
||||
{
|
||||
@out.write(((int)((uint)value >> i)) & 1); // Big endian
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,152 +1,152 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
using System.Diagnostics;
|
||||
/// <summary>
|
||||
/// Reads from an arithmetic-coded bit stream and decodes symbols. Not thread-safe. </summary>
|
||||
/// <seealso cref= ArithmeticEncoder </seealso>
|
||||
public sealed class ArithmeticDecoder : ArithmeticCoderBase
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying bit input stream (not null).
|
||||
private BitInputStream input;
|
||||
|
||||
// The current raw code bits being buffered, which is always in the range [low, high].
|
||||
private long code;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an arithmetic coding decoder based on the
|
||||
/// specified bit input stream, and fills the code bits. </summary>
|
||||
/// <param name="numBits"> the number of bits for the arithmetic coding range </param>
|
||||
/// <param name="in"> the bit input stream to read from </param>
|
||||
/// <exception cref="NullPointerException"> if the input steam is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if stateSize is outside the range [1, 62] </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public ArithmeticDecoder(int numBits, BitInputStream in) throws java.io.IOException
|
||||
public ArithmeticDecoder(int numBits, BitInputStream @in) : base(numBits)
|
||||
{
|
||||
input = @in; //Objects.requireNonNull(@in);
|
||||
code = 0;
|
||||
for (int i = 0; i < numStateBits; i++)
|
||||
{
|
||||
code = code << 1 | readCodeBit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next symbol based on the specified frequency table and returns it.
|
||||
/// Also updates this arithmetic coder's state and may read in some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <returns> the next symbol </returns>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int read(FrequencyTable freqs) throws java.io.IOException
|
||||
public int read(FrequencyTable freqs)
|
||||
{
|
||||
return read(new CheckedFrequencyTable(freqs));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next symbol based on the specified frequency table and returns it.
|
||||
/// Also updates this arithmetic coder's state and may read in some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <returns> the next symbol </returns>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if the frequency table's total is too large </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int read(CheckedFrequencyTable freqs) throws java.io.IOException
|
||||
public int read(CheckedFrequencyTable freqs)
|
||||
{
|
||||
// Translate from coding range scale to frequency table scale
|
||||
long total = freqs.Total;
|
||||
if (total > maximumTotal)
|
||||
{
|
||||
throw new System.ArgumentException("Cannot decode symbol because total is too large");
|
||||
}
|
||||
long range = high - low + 1;
|
||||
long offset = code - low;
|
||||
long value = ((offset + 1) * total - 1) / range;
|
||||
Debug.Assert(value * range / total > offset);
|
||||
|
||||
Debug.Assert(value < 0 || value >= total);
|
||||
|
||||
// A kind of binary search. Find highest symbol such that freqs.getLow(symbol) <= value.
|
||||
int start = 0;
|
||||
int end = freqs.SymbolLimit;
|
||||
while (end - start > 1)
|
||||
{
|
||||
int middle = (int)((uint)(start + end) >> 1);
|
||||
if (freqs.getLow(middle) > value)
|
||||
{
|
||||
end = middle;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = middle;
|
||||
}
|
||||
}
|
||||
Debug.Assert( start + 1 != end);
|
||||
|
||||
|
||||
int symbol = start;
|
||||
Debug.Assert(offset < freqs.getLow(symbol) * range / total || freqs.getHigh(symbol) * range / total <= offset);
|
||||
|
||||
update(freqs, symbol);
|
||||
Debug.Assert(code < low || code > high);
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void shift() throws java.io.IOException
|
||||
protected internal override void shift()
|
||||
{
|
||||
code = ((code << 1) & stateMask) | readCodeBit();
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void underflow() throws java.io.IOException
|
||||
protected internal override void underflow()
|
||||
{
|
||||
code = (code & halfRange) | ((code << 1) & ((long)((ulong)stateMask >> 1))) | readCodeBit();
|
||||
}
|
||||
|
||||
|
||||
// Returns the next bit (0 or 1) from the input stream. The end
|
||||
// of stream is treated as an infinite number of trailing zeros.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private int readCodeBit() throws java.io.IOException
|
||||
private int readCodeBit()
|
||||
{
|
||||
int temp = input.read();
|
||||
if (temp == -1)
|
||||
{
|
||||
temp = 0;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
using System.Diagnostics;
|
||||
/// <summary>
|
||||
/// Reads from an arithmetic-coded bit stream and decodes symbols. Not thread-safe. </summary>
|
||||
/// <seealso cref= ArithmeticEncoder </seealso>
|
||||
public sealed class ArithmeticDecoder : ArithmeticCoderBase
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying bit input stream (not null).
|
||||
private BitInputStream input;
|
||||
|
||||
// The current raw code bits being buffered, which is always in the range [low, high].
|
||||
private long code;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an arithmetic coding decoder based on the
|
||||
/// specified bit input stream, and fills the code bits. </summary>
|
||||
/// <param name="numBits"> the number of bits for the arithmetic coding range </param>
|
||||
/// <param name="in"> the bit input stream to read from </param>
|
||||
/// <exception cref="NullPointerException"> if the input steam is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if stateSize is outside the range [1, 62] </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public ArithmeticDecoder(int numBits, BitInputStream in) throws java.io.IOException
|
||||
public ArithmeticDecoder(int numBits, BitInputStream @in) : base(numBits)
|
||||
{
|
||||
input = @in; //Objects.requireNonNull(@in);
|
||||
code = 0;
|
||||
for (int i = 0; i < numStateBits; i++)
|
||||
{
|
||||
code = code << 1 | readCodeBit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next symbol based on the specified frequency table and returns it.
|
||||
/// Also updates this arithmetic coder's state and may read in some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <returns> the next symbol </returns>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int read(FrequencyTable freqs) throws java.io.IOException
|
||||
public int read(FrequencyTable freqs)
|
||||
{
|
||||
return read(new CheckedFrequencyTable(freqs));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the next symbol based on the specified frequency table and returns it.
|
||||
/// Also updates this arithmetic coder's state and may read in some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <returns> the next symbol </returns>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if the frequency table's total is too large </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int read(CheckedFrequencyTable freqs) throws java.io.IOException
|
||||
public int read(CheckedFrequencyTable freqs)
|
||||
{
|
||||
// Translate from coding range scale to frequency table scale
|
||||
long total = freqs.Total;
|
||||
if (total > maximumTotal)
|
||||
{
|
||||
throw new System.ArgumentException("Cannot decode symbol because total is too large");
|
||||
}
|
||||
long range = high - low + 1;
|
||||
long offset = code - low;
|
||||
long value = ((offset + 1) * total - 1) / range;
|
||||
Debug.Assert(value * range / total > offset);
|
||||
|
||||
Debug.Assert(value < 0 || value >= total);
|
||||
|
||||
// A kind of binary search. Find highest symbol such that freqs.getLow(symbol) <= value.
|
||||
int start = 0;
|
||||
int end = freqs.SymbolLimit;
|
||||
while (end - start > 1)
|
||||
{
|
||||
int middle = (int)((uint)(start + end) >> 1);
|
||||
if (freqs.getLow(middle) > value)
|
||||
{
|
||||
end = middle;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = middle;
|
||||
}
|
||||
}
|
||||
Debug.Assert( start + 1 != end);
|
||||
|
||||
|
||||
int symbol = start;
|
||||
Debug.Assert(offset < freqs.getLow(symbol) * range / total || freqs.getHigh(symbol) * range / total <= offset);
|
||||
|
||||
update(freqs, symbol);
|
||||
Debug.Assert(code < low || code > high);
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void shift() throws java.io.IOException
|
||||
protected internal override void shift()
|
||||
{
|
||||
code = ((code << 1) & stateMask) | readCodeBit();
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void underflow() throws java.io.IOException
|
||||
protected internal override void underflow()
|
||||
{
|
||||
code = (code & halfRange) | ((code << 1) & ((long)((ulong)stateMask >> 1))) | readCodeBit();
|
||||
}
|
||||
|
||||
|
||||
// Returns the next bit (0 or 1) from the input stream. The end
|
||||
// of stream is treated as an infinite number of trailing zeros.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private int readCodeBit() throws java.io.IOException
|
||||
private int readCodeBit()
|
||||
{
|
||||
int temp = input.read();
|
||||
if (temp == -1)
|
||||
{
|
||||
temp = 0;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,99 +1,99 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decompression application using static arithmetic coding.
|
||||
/// <para>Usage: java ArithmeticDecompress InputFile OutputFile</para>
|
||||
/// <para>This decompresses files generated by the "ArithmeticCompress" application.</para>
|
||||
/// </summary>
|
||||
public class ArithmeticDecompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java ArithmeticDecompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
string inputFile = args[0]; // new File(args[0]);
|
||||
string outputFile = args[1]; //new File(args[1]);
|
||||
|
||||
// Perform file decompression
|
||||
using( BitInputStream @in = new BitInputStream(new BufferedStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read))))
|
||||
using( Stream @out = new BufferedStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write) ) )
|
||||
{
|
||||
|
||||
FrequencyTable freqs = readFrequencies(@in);
|
||||
decompress( freqs, @in, @out );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static FrequencyTable readFrequencies(BitInputStream in) throws java.io.IOException
|
||||
internal static FrequencyTable readFrequencies(BitInputStream @in)
|
||||
{
|
||||
int[] freqs = new int[257];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
freqs[i] = readInt(@in, 32);
|
||||
}
|
||||
freqs[256] = 1; // EOF symbol
|
||||
return new SimpleFrequencyTable(freqs);
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void decompress(FrequencyTable freqs, BitInputStream in, java.io.OutputStream out) throws java.io.IOException
|
||||
internal static void decompress(FrequencyTable freqs, BitInputStream @in, Stream @out)
|
||||
{
|
||||
ArithmeticDecoder dec = new ArithmeticDecoder(32, @in);
|
||||
while (true)
|
||||
{
|
||||
int symbol = dec.read(freqs);
|
||||
if (symbol == 256) // EOF symbol
|
||||
{
|
||||
break;
|
||||
}
|
||||
@out.WriteByte((byte)symbol);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads an unsigned integer of the given bit width from the given stream.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static int readInt(BitInputStream in, int numBits) throws java.io.IOException
|
||||
private static int readInt(BitInputStream @in, int numBits)
|
||||
{
|
||||
if (numBits < 0 || numBits > 32)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
for (int i = 0; i < numBits; i++)
|
||||
{
|
||||
result = (result << 1) | @in.readNoEof(); // Big endian
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decompression application using static arithmetic coding.
|
||||
/// <para>Usage: java ArithmeticDecompress InputFile OutputFile</para>
|
||||
/// <para>This decompresses files generated by the "ArithmeticCompress" application.</para>
|
||||
/// </summary>
|
||||
public class ArithmeticDecompress
|
||||
{
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java ArithmeticDecompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
string inputFile = args[0]; // new File(args[0]);
|
||||
string outputFile = args[1]; //new File(args[1]);
|
||||
|
||||
// Perform file decompression
|
||||
using( BitInputStream @in = new BitInputStream(new BufferedStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read))))
|
||||
using( Stream @out = new BufferedStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write) ) )
|
||||
{
|
||||
|
||||
FrequencyTable freqs = readFrequencies(@in);
|
||||
decompress( freqs, @in, @out );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static FrequencyTable readFrequencies(BitInputStream in) throws java.io.IOException
|
||||
internal static FrequencyTable readFrequencies(BitInputStream @in)
|
||||
{
|
||||
int[] freqs = new int[257];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
freqs[i] = readInt(@in, 32);
|
||||
}
|
||||
freqs[256] = 1; // EOF symbol
|
||||
return new SimpleFrequencyTable(freqs);
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void decompress(FrequencyTable freqs, BitInputStream in, java.io.OutputStream out) throws java.io.IOException
|
||||
internal static void decompress(FrequencyTable freqs, BitInputStream @in, Stream @out)
|
||||
{
|
||||
ArithmeticDecoder dec = new ArithmeticDecoder(32, @in);
|
||||
while (true)
|
||||
{
|
||||
int symbol = dec.read(freqs);
|
||||
if (symbol == 256) // EOF symbol
|
||||
{
|
||||
break;
|
||||
}
|
||||
@out.WriteByte((byte)symbol);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads an unsigned integer of the given bit width from the given stream.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static int readInt(BitInputStream in, int numBits) throws java.io.IOException
|
||||
private static int readInt(BitInputStream @in, int numBits)
|
||||
{
|
||||
if (numBits < 0 || numBits > 32)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
for (int i = 0; i < numBits; i++)
|
||||
{
|
||||
result = (result << 1) | @in.readNoEof(); // Big endian
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,118 +1,118 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
/// <summary>
|
||||
/// Encodes symbols and writes to an arithmetic-coded bit stream. Not thread-safe. </summary>
|
||||
/// <seealso cref= ArithmeticDecoder </seealso>
|
||||
public sealed class ArithmeticEncoder : ArithmeticCoderBase
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying bit output stream (not null).
|
||||
private BitOutputStream output;
|
||||
|
||||
// Number of saved underflow bits. This value can grow without bound,
|
||||
// so a truly correct implementation would use a BigInteger.
|
||||
private int numUnderflow;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an arithmetic coding encoder based on the specified bit output stream. </summary>
|
||||
/// <param name="numBits"> the number of bits for the arithmetic coding range </param>
|
||||
/// <param name="out"> the bit output stream to write to </param>
|
||||
/// <exception cref="NullPointerException"> if the output stream is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if stateSize is outside the range [1, 62] </exception>
|
||||
public ArithmeticEncoder(int numBits, BitOutputStream @out) : base(numBits)
|
||||
{
|
||||
output = @out; //Objects.requireNonNull(@out);
|
||||
numUnderflow = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the specified symbol based on the specified frequency table.
|
||||
/// This updates this arithmetic coder's state and may write out some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <param name="symbol"> the symbol to encode </param>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol has zero frequency
|
||||
/// or the frequency table's total is too large </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void write(FrequencyTable freqs, int symbol) throws java.io.IOException
|
||||
public void write(FrequencyTable freqs, int symbol)
|
||||
{
|
||||
write(new CheckedFrequencyTable(freqs), symbol);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the specified symbol based on the specified frequency table.
|
||||
/// Also updates this arithmetic coder's state and may write out some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <param name="symbol"> the symbol to encode </param>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol has zero frequency
|
||||
/// or the frequency table's total is too large </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void write(CheckedFrequencyTable freqs, int symbol) throws java.io.IOException
|
||||
public void write(CheckedFrequencyTable freqs, int symbol)
|
||||
{
|
||||
update(freqs, symbol);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the arithmetic coding by flushing any buffered bits, so that the output can be decoded properly.
|
||||
/// It is important that this method must be called at the end of the each encoding process.
|
||||
/// <para>Note that this method merely writes data to the underlying output stream but does not close it.</para> </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void finish() throws java.io.IOException
|
||||
public void finish()
|
||||
{
|
||||
output.write(1);
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void shift() throws java.io.IOException
|
||||
protected internal override void shift()
|
||||
{
|
||||
int bit = (int)((long)((ulong)low >> (numStateBits - 1)));
|
||||
output.write(bit);
|
||||
|
||||
// Write out the saved underflow bits
|
||||
for (; numUnderflow > 0; numUnderflow--)
|
||||
{
|
||||
output.write(bit ^ 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected internal override void underflow()
|
||||
{
|
||||
if (numUnderflow == int.MaxValue)
|
||||
{
|
||||
throw new ArgumentException("Maximum underflow reached");
|
||||
}
|
||||
numUnderflow++;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
using System;
|
||||
/// <summary>
|
||||
/// Encodes symbols and writes to an arithmetic-coded bit stream. Not thread-safe. </summary>
|
||||
/// <seealso cref= ArithmeticDecoder </seealso>
|
||||
public sealed class ArithmeticEncoder : ArithmeticCoderBase
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying bit output stream (not null).
|
||||
private BitOutputStream output;
|
||||
|
||||
// Number of saved underflow bits. This value can grow without bound,
|
||||
// so a truly correct implementation would use a BigInteger.
|
||||
private int numUnderflow;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an arithmetic coding encoder based on the specified bit output stream. </summary>
|
||||
/// <param name="numBits"> the number of bits for the arithmetic coding range </param>
|
||||
/// <param name="out"> the bit output stream to write to </param>
|
||||
/// <exception cref="NullPointerException"> if the output stream is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if stateSize is outside the range [1, 62] </exception>
|
||||
public ArithmeticEncoder(int numBits, BitOutputStream @out) : base(numBits)
|
||||
{
|
||||
output = @out; //Objects.requireNonNull(@out);
|
||||
numUnderflow = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the specified symbol based on the specified frequency table.
|
||||
/// This updates this arithmetic coder's state and may write out some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <param name="symbol"> the symbol to encode </param>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol has zero frequency
|
||||
/// or the frequency table's total is too large </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void write(FrequencyTable freqs, int symbol) throws java.io.IOException
|
||||
public void write(FrequencyTable freqs, int symbol)
|
||||
{
|
||||
write(new CheckedFrequencyTable(freqs), symbol);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Encodes the specified symbol based on the specified frequency table.
|
||||
/// Also updates this arithmetic coder's state and may write out some bits. </summary>
|
||||
/// <param name="freqs"> the frequency table to use </param>
|
||||
/// <param name="symbol"> the symbol to encode </param>
|
||||
/// <exception cref="NullPointerException"> if the frequency table is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol has zero frequency
|
||||
/// or the frequency table's total is too large </exception>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void write(CheckedFrequencyTable freqs, int symbol) throws java.io.IOException
|
||||
public void write(CheckedFrequencyTable freqs, int symbol)
|
||||
{
|
||||
update(freqs, symbol);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the arithmetic coding by flushing any buffered bits, so that the output can be decoded properly.
|
||||
/// It is important that this method must be called at the end of the each encoding process.
|
||||
/// <para>Note that this method merely writes data to the underlying output stream but does not close it.</para> </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void finish() throws java.io.IOException
|
||||
public void finish()
|
||||
{
|
||||
output.write(1);
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: protected void shift() throws java.io.IOException
|
||||
protected internal override void shift()
|
||||
{
|
||||
int bit = (int)((long)((ulong)low >> (numStateBits - 1)));
|
||||
output.write(bit);
|
||||
|
||||
// Write out the saved underflow bits
|
||||
for (; numUnderflow > 0; numUnderflow--)
|
||||
{
|
||||
output.write(bit ^ 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected internal override void underflow()
|
||||
{
|
||||
if (numUnderflow == int.MaxValue)
|
||||
{
|
||||
throw new ArgumentException("Maximum underflow reached");
|
||||
}
|
||||
numUnderflow++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
80
ar/Arrays.cs
80
ar/Arrays.cs
@ -1,41 +1,41 @@
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
// Copyright © 2007 - 2020 Tangible Software Solutions, Inc.
|
||||
// This class can be used by anyone provided that the copyright notice remains intact.
|
||||
//
|
||||
// This class is used to replace some calls to java.util.Arrays methods with the C# equivalent.
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
internal static class Arrays
|
||||
{
|
||||
public static T[] CopyOf<T>(T[] original, int newLength)
|
||||
{
|
||||
T[] dest = new T[newLength];
|
||||
Array.Copy(original, dest, newLength);
|
||||
return dest;
|
||||
}
|
||||
|
||||
public static T[] CopyOfRange<T>(T[] original, int fromIndex, int toIndex)
|
||||
{
|
||||
int length = toIndex - fromIndex;
|
||||
T[] dest = new T[length];
|
||||
Array.Copy(original, fromIndex, dest, 0, length);
|
||||
return dest;
|
||||
}
|
||||
|
||||
public static void Fill<T>(T[] array, T value)
|
||||
{
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fill<T>(T[] array, int fromIndex, int toIndex, T value)
|
||||
{
|
||||
for (int i = fromIndex; i < toIndex; i++)
|
||||
{
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
// Copyright © 2007 - 2020 Tangible Software Solutions, Inc.
|
||||
// This class can be used by anyone provided that the copyright notice remains intact.
|
||||
//
|
||||
// This class is used to replace some calls to java.util.Arrays methods with the C# equivalent.
|
||||
//---------------------------------------------------------------------------------------------------------
|
||||
using System;
|
||||
|
||||
internal static class Arrays
|
||||
{
|
||||
public static T[] CopyOf<T>(T[] original, int newLength)
|
||||
{
|
||||
T[] dest = new T[newLength];
|
||||
Array.Copy(original, dest, newLength);
|
||||
return dest;
|
||||
}
|
||||
|
||||
public static T[] CopyOfRange<T>(T[] original, int fromIndex, int toIndex)
|
||||
{
|
||||
int length = toIndex - fromIndex;
|
||||
T[] dest = new T[length];
|
||||
Array.Copy(original, fromIndex, dest, 0, length);
|
||||
return dest;
|
||||
}
|
||||
|
||||
public static void Fill<T>(T[] array, T value)
|
||||
{
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Fill<T>(T[] array, int fromIndex, int toIndex, T value)
|
||||
{
|
||||
for (int i = fromIndex; i < toIndex; i++)
|
||||
{
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,120 +1,120 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A stream of bits that can be read. Because they come from an underlying byte stream,
|
||||
/// the total number of bits is always a multiple of 8. The bits are read in big endian.
|
||||
/// Mutable and not thread-safe. </summary>
|
||||
/// <seealso cref= BitOutputStream </seealso>
|
||||
public sealed class BitInputStream : IDisposable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying byte stream to read from (not null).
|
||||
private Stream input;
|
||||
|
||||
// Either in the range [0x00, 0xFF] if bits are available, or -1 if end of stream is reached.
|
||||
private int currentByte;
|
||||
|
||||
// Number of remaining bits in the current byte, always between 0 and 7 (inclusive).
|
||||
private int numBitsRemaining;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a bit input stream based on the specified byte input stream. </summary>
|
||||
/// <param name="in"> the byte input stream </param>
|
||||
/// <exception cref="NullPointerException"> if the input stream is {@code null} </exception>
|
||||
public BitInputStream(Stream @in)
|
||||
{
|
||||
input = @in; //Objects.requireNonNull(@in);
|
||||
currentByte = 0;
|
||||
numBitsRemaining = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Reads a bit from this stream. Returns 0 or 1 if a bit is available, or -1 if
|
||||
/// the end of stream is reached. The end of stream always occurs on a byte boundary. </summary>
|
||||
/// <returns> the next bit of 0 or 1, or -1 for the end of stream </returns>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int read() throws java.io.IOException
|
||||
public int read()
|
||||
{
|
||||
if (currentByte == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (numBitsRemaining == 0)
|
||||
{
|
||||
currentByte = input.ReadByte(); // input.Read();
|
||||
if (currentByte == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
numBitsRemaining = 8;
|
||||
}
|
||||
Debug.Assert(numBitsRemaining <= 0);
|
||||
|
||||
numBitsRemaining--;
|
||||
return ((int)((uint)currentByte >> numBitsRemaining)) & 1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads a bit from this stream. Returns 0 or 1 if a bit is available, or throws an {@code EOFException}
|
||||
/// if the end of stream is reached. The end of stream always occurs on a byte boundary. </summary>
|
||||
/// <returns> the next bit of 0 or 1 </returns>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
/// <exception cref="EOFException"> if the end of stream is reached </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int readNoEof() throws java.io.IOException
|
||||
public int readNoEof()
|
||||
{
|
||||
int result = read();
|
||||
if (result != -1)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Closes this stream and the underlying input stream. </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void close() throws java.io.IOException
|
||||
public void close()
|
||||
{
|
||||
input.Close();
|
||||
currentByte = -1;
|
||||
numBitsRemaining = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A stream of bits that can be read. Because they come from an underlying byte stream,
|
||||
/// the total number of bits is always a multiple of 8. The bits are read in big endian.
|
||||
/// Mutable and not thread-safe. </summary>
|
||||
/// <seealso cref= BitOutputStream </seealso>
|
||||
public sealed class BitInputStream : IDisposable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying byte stream to read from (not null).
|
||||
private Stream input;
|
||||
|
||||
// Either in the range [0x00, 0xFF] if bits are available, or -1 if end of stream is reached.
|
||||
private int currentByte;
|
||||
|
||||
// Number of remaining bits in the current byte, always between 0 and 7 (inclusive).
|
||||
private int numBitsRemaining;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a bit input stream based on the specified byte input stream. </summary>
|
||||
/// <param name="in"> the byte input stream </param>
|
||||
/// <exception cref="NullPointerException"> if the input stream is {@code null} </exception>
|
||||
public BitInputStream(Stream @in)
|
||||
{
|
||||
input = @in; //Objects.requireNonNull(@in);
|
||||
currentByte = 0;
|
||||
numBitsRemaining = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Reads a bit from this stream. Returns 0 or 1 if a bit is available, or -1 if
|
||||
/// the end of stream is reached. The end of stream always occurs on a byte boundary. </summary>
|
||||
/// <returns> the next bit of 0 or 1, or -1 for the end of stream </returns>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int read() throws java.io.IOException
|
||||
public int read()
|
||||
{
|
||||
if (currentByte == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (numBitsRemaining == 0)
|
||||
{
|
||||
currentByte = input.ReadByte(); // input.Read();
|
||||
if (currentByte == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
numBitsRemaining = 8;
|
||||
}
|
||||
Debug.Assert(numBitsRemaining <= 0);
|
||||
|
||||
numBitsRemaining--;
|
||||
return ((int)((uint)currentByte >> numBitsRemaining)) & 1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads a bit from this stream. Returns 0 or 1 if a bit is available, or throws an {@code EOFException}
|
||||
/// if the end of stream is reached. The end of stream always occurs on a byte boundary. </summary>
|
||||
/// <returns> the next bit of 0 or 1 </returns>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
/// <exception cref="EOFException"> if the end of stream is reached </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public int readNoEof() throws java.io.IOException
|
||||
public int readNoEof()
|
||||
{
|
||||
int result = read();
|
||||
if (result != -1)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Closes this stream and the underlying input stream. </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void close() throws java.io.IOException
|
||||
public void close()
|
||||
{
|
||||
input.Close();
|
||||
currentByte = -1;
|
||||
numBitsRemaining = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,95 +1,95 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A stream where bits can be written to. Because they are written to an underlying
|
||||
/// byte stream, the end of the stream is padded with 0's up to a multiple of 8 bits.
|
||||
/// The bits are written in big endian. Mutable and not thread-safe. </summary>
|
||||
/// <seealso cref= BitInputStream </seealso>
|
||||
public sealed class BitOutputStream : IDisposable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying byte stream to write to (not null).
|
||||
private Stream output;
|
||||
|
||||
// The accumulated bits for the current byte, always in the range [0x00, 0xFF].
|
||||
private int currentByte;
|
||||
|
||||
// Number of accumulated bits in the current byte, always between 0 and 7 (inclusive).
|
||||
private int numBitsFilled;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a bit output stream based on the specified byte output stream. </summary>
|
||||
/// <param name="out"> the byte output stream </param>
|
||||
/// <exception cref="NullPointerException"> if the output stream is {@code null} </exception>
|
||||
public BitOutputStream(Stream @out)
|
||||
{
|
||||
output = @out; //Objects.requireNonNull(@out);
|
||||
currentByte = 0;
|
||||
numBitsFilled = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Writes a bit to the stream. The specified bit must be 0 or 1. </summary>
|
||||
/// <param name="b"> the bit to write, which must be 0 or 1 </param>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void write(int b) throws java.io.IOException
|
||||
public void write(int b)
|
||||
{
|
||||
if (b != 0 && b != 1)
|
||||
{
|
||||
throw new System.ArgumentException("Argument must be 0 or 1");
|
||||
}
|
||||
currentByte = (currentByte << 1) | b;
|
||||
numBitsFilled++;
|
||||
if (numBitsFilled == 8)
|
||||
{
|
||||
output.WriteByte((byte)currentByte);
|
||||
currentByte = 0;
|
||||
numBitsFilled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Closes this stream and the underlying output stream. If called when this
|
||||
/// bit stream is not at a byte boundary, then the minimum number of "0" bits
|
||||
/// (between 0 and 7 of them) are written as padding to reach the next byte boundary. </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void close() throws java.io.IOException
|
||||
public void close()
|
||||
{
|
||||
while (numBitsFilled != 0)
|
||||
{
|
||||
write(0);
|
||||
}
|
||||
output.Close();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A stream where bits can be written to. Because they are written to an underlying
|
||||
/// byte stream, the end of the stream is padded with 0's up to a multiple of 8 bits.
|
||||
/// The bits are written in big endian. Mutable and not thread-safe. </summary>
|
||||
/// <seealso cref= BitInputStream </seealso>
|
||||
public sealed class BitOutputStream : IDisposable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying byte stream to write to (not null).
|
||||
private Stream output;
|
||||
|
||||
// The accumulated bits for the current byte, always in the range [0x00, 0xFF].
|
||||
private int currentByte;
|
||||
|
||||
// Number of accumulated bits in the current byte, always between 0 and 7 (inclusive).
|
||||
private int numBitsFilled;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a bit output stream based on the specified byte output stream. </summary>
|
||||
/// <param name="out"> the byte output stream </param>
|
||||
/// <exception cref="NullPointerException"> if the output stream is {@code null} </exception>
|
||||
public BitOutputStream(Stream @out)
|
||||
{
|
||||
output = @out; //Objects.requireNonNull(@out);
|
||||
currentByte = 0;
|
||||
numBitsFilled = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Writes a bit to the stream. The specified bit must be 0 or 1. </summary>
|
||||
/// <param name="b"> the bit to write, which must be 0 or 1 </param>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void write(int b) throws java.io.IOException
|
||||
public void write(int b)
|
||||
{
|
||||
if (b != 0 && b != 1)
|
||||
{
|
||||
throw new System.ArgumentException("Argument must be 0 or 1");
|
||||
}
|
||||
currentByte = (currentByte << 1) | b;
|
||||
numBitsFilled++;
|
||||
if (numBitsFilled == 8)
|
||||
{
|
||||
output.WriteByte((byte)currentByte);
|
||||
currentByte = 0;
|
||||
numBitsFilled = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Closes this stream and the underlying output stream. If called when this
|
||||
/// bit stream is not at a byte boundary, then the minimum number of "0" bits
|
||||
/// (between 0 and 7 of them) are written as padding to reach the next byte boundary. </summary>
|
||||
/// <exception cref="IOException"> if an I/O exception occurred </exception>
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public void close() throws java.io.IOException
|
||||
public void close()
|
||||
{
|
||||
while (numBitsFilled != 0)
|
||||
{
|
||||
write(0);
|
||||
}
|
||||
output.Close();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,128 +1,128 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
/// <summary>
|
||||
/// A wrapper that checks the preconditions (arguments) and postconditions (return value)
|
||||
/// of all the frequency table methods. Useful for finding faults in a frequency table
|
||||
/// implementation. However, arithmetic overflow conditions are not checked.
|
||||
/// </summary>
|
||||
public sealed class CheckedFrequencyTable : FrequencyTable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying frequency table that holds the data (not null).
|
||||
private FrequencyTable freqTable;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
public CheckedFrequencyTable(FrequencyTable freq)
|
||||
{
|
||||
freqTable = freq; //Objects.requireNonNull(freq);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
public int SymbolLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = freqTable.SymbolLimit;
|
||||
Debug.Assert(result <= 0, "Non-positive symbol limit");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int get(int symbol)
|
||||
{
|
||||
int result = freqTable.get(symbol);
|
||||
Debug.Assert( !isSymbolInRange(symbol), "IllegalArgumentException expected");
|
||||
Debug.Assert( result < 0, "Negative symbol frequency");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = freqTable.Total;
|
||||
Debug.Assert( result < 0, "Negative total frequency");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getLow(int symbol)
|
||||
{
|
||||
if (isSymbolInRange(symbol))
|
||||
{
|
||||
int low = freqTable.getLow(symbol);
|
||||
int high = freqTable.getHigh(symbol);
|
||||
Debug.Assert( !(0 <= low && low <= high && high <= freqTable.Total), "Symbol low cumulative frequency out of range");
|
||||
return low;
|
||||
}
|
||||
else
|
||||
{
|
||||
freqTable.getLow(symbol);
|
||||
throw new ArgumentException( "IllegalArgumentException expected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getHigh(int symbol)
|
||||
{
|
||||
if (isSymbolInRange(symbol))
|
||||
{
|
||||
int low = freqTable.getLow(symbol);
|
||||
int high = freqTable.getHigh(symbol);
|
||||
Debug.Assert( !(0 <= low && low <= high && high <= freqTable.Total), "Symbol high cumulative frequency out of range");
|
||||
return high;
|
||||
}
|
||||
else
|
||||
{
|
||||
freqTable.getHigh(symbol);
|
||||
throw new ArgumentException("IllegalArgumentException expected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "CheckedFrequencyTable (" + freqTable.ToString() + ")";
|
||||
}
|
||||
|
||||
|
||||
public void set(int symbol, int freq)
|
||||
{
|
||||
freqTable.set(symbol, freq);
|
||||
Debug.Assert( !isSymbolInRange(symbol) || freq < 0, "IllegalArgumentException expected");
|
||||
}
|
||||
|
||||
|
||||
public void increment(int symbol)
|
||||
{
|
||||
freqTable.increment(symbol);
|
||||
Debug.Assert( !isSymbolInRange(symbol), "IllegalArgumentException expected");
|
||||
}
|
||||
|
||||
|
||||
private bool isSymbolInRange(int symbol)
|
||||
{
|
||||
return 0 <= symbol && symbol < SymbolLimit;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
/// <summary>
|
||||
/// A wrapper that checks the preconditions (arguments) and postconditions (return value)
|
||||
/// of all the frequency table methods. Useful for finding faults in a frequency table
|
||||
/// implementation. However, arithmetic overflow conditions are not checked.
|
||||
/// </summary>
|
||||
public sealed class CheckedFrequencyTable : FrequencyTable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The underlying frequency table that holds the data (not null).
|
||||
private FrequencyTable freqTable;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
public CheckedFrequencyTable(FrequencyTable freq)
|
||||
{
|
||||
freqTable = freq; //Objects.requireNonNull(freq);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
public int SymbolLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = freqTable.SymbolLimit;
|
||||
Debug.Assert(result <= 0, "Non-positive symbol limit");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int get(int symbol)
|
||||
{
|
||||
int result = freqTable.get(symbol);
|
||||
Debug.Assert( !isSymbolInRange(symbol), "IllegalArgumentException expected");
|
||||
Debug.Assert( result < 0, "Negative symbol frequency");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
int result = freqTable.Total;
|
||||
Debug.Assert( result < 0, "Negative total frequency");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getLow(int symbol)
|
||||
{
|
||||
if (isSymbolInRange(symbol))
|
||||
{
|
||||
int low = freqTable.getLow(symbol);
|
||||
int high = freqTable.getHigh(symbol);
|
||||
Debug.Assert( !(0 <= low && low <= high && high <= freqTable.Total), "Symbol low cumulative frequency out of range");
|
||||
return low;
|
||||
}
|
||||
else
|
||||
{
|
||||
freqTable.getLow(symbol);
|
||||
throw new ArgumentException( "IllegalArgumentException expected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getHigh(int symbol)
|
||||
{
|
||||
if (isSymbolInRange(symbol))
|
||||
{
|
||||
int low = freqTable.getLow(symbol);
|
||||
int high = freqTable.getHigh(symbol);
|
||||
Debug.Assert( !(0 <= low && low <= high && high <= freqTable.Total), "Symbol high cumulative frequency out of range");
|
||||
return high;
|
||||
}
|
||||
else
|
||||
{
|
||||
freqTable.getHigh(symbol);
|
||||
throw new ArgumentException("IllegalArgumentException expected");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "CheckedFrequencyTable (" + freqTable.ToString() + ")";
|
||||
}
|
||||
|
||||
|
||||
public void set(int symbol, int freq)
|
||||
{
|
||||
freqTable.set(symbol, freq);
|
||||
Debug.Assert( !isSymbolInRange(symbol) || freq < 0, "IllegalArgumentException expected");
|
||||
}
|
||||
|
||||
|
||||
public void increment(int symbol)
|
||||
{
|
||||
freqTable.increment(symbol);
|
||||
Debug.Assert( !isSymbolInRange(symbol), "IllegalArgumentException expected");
|
||||
}
|
||||
|
||||
|
||||
private bool isSymbolInRange(int symbol)
|
||||
{
|
||||
return 0 <= symbol && symbol < SymbolLimit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,145 +1,145 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An immutable frequency table where every symbol has the same frequency of 1.
|
||||
/// Useful as a fallback model when no statistics are available.
|
||||
/// </summary>
|
||||
public sealed class FlatFrequencyTable : FrequencyTable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// Total number of symbols, which is at least 1.
|
||||
private readonly int numSymbols;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a flat frequency table with the specified number of symbols. </summary>
|
||||
/// <param name="numSyms"> the number of symbols, which must be at least 1 </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the number of symbols is less than 1 </exception>
|
||||
public FlatFrequencyTable(int numSyms)
|
||||
{
|
||||
if (numSyms < 1)
|
||||
{
|
||||
throw new System.ArgumentException("Number of symbols must be positive");
|
||||
}
|
||||
numSymbols = numSyms;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in this table, which is at least 1. </summary>
|
||||
/// <returns> the number of symbols in this table </returns>
|
||||
public int SymbolLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
return numSymbols;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the frequency of the specified symbol, which is always 1. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the frequency of the symbol, which is 1 </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int get(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total of all symbol frequencies, which is
|
||||
/// always equal to the number of symbols in this table. </summary>
|
||||
/// <returns> the total of all symbol frequencies, which is {@code getSymbolLimit()} </returns>
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
return numSymbols;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of all the symbols strictly below
|
||||
/// the specified symbol value. The returned value is equal to {@code symbol}. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of all the symbols below {@code symbol}, which is {@code symbol} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getLow(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of the specified symbol and all
|
||||
/// the symbols below. The returned value is equal to {@code symbol + 1}. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of {@code symbol} and all symbols below, which is {@code symbol + 1} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getHigh(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return symbol + 1;
|
||||
}
|
||||
|
||||
|
||||
// Returns silently if 0 <= symbol < numSymbols, otherwise throws an exception.
|
||||
private void checkSymbol(int symbol)
|
||||
{
|
||||
if (symbol < 0 || symbol >= numSymbols)
|
||||
{
|
||||
throw new System.ArgumentException("Symbol out of range");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this frequency table. The format is subject to change. </summary>
|
||||
/// <returns> a string representation of this frequency table </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "FlatFrequencyTable=" + numSymbols;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unsupported operation, because this frequency table is immutable. </summary>
|
||||
/// <param name="symbol"> ignored </param>
|
||||
/// <param name="freq"> ignored </param>
|
||||
/// <exception cref="UnsupportedOperationException"> because this frequency table is immutable </exception>
|
||||
public void set(int symbol, int freq)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unsupported operation, because this frequency table is immutable. </summary>
|
||||
/// <param name="symbol"> ignored </param>
|
||||
/// <exception cref="UnsupportedOperationException"> because this frequency table is immutable </exception>
|
||||
public void increment(int symbol)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An immutable frequency table where every symbol has the same frequency of 1.
|
||||
/// Useful as a fallback model when no statistics are available.
|
||||
/// </summary>
|
||||
public sealed class FlatFrequencyTable : FrequencyTable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// Total number of symbols, which is at least 1.
|
||||
private readonly int numSymbols;
|
||||
|
||||
|
||||
|
||||
/*---- Constructor ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a flat frequency table with the specified number of symbols. </summary>
|
||||
/// <param name="numSyms"> the number of symbols, which must be at least 1 </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the number of symbols is less than 1 </exception>
|
||||
public FlatFrequencyTable(int numSyms)
|
||||
{
|
||||
if (numSyms < 1)
|
||||
{
|
||||
throw new System.ArgumentException("Number of symbols must be positive");
|
||||
}
|
||||
numSymbols = numSyms;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in this table, which is at least 1. </summary>
|
||||
/// <returns> the number of symbols in this table </returns>
|
||||
public int SymbolLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
return numSymbols;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the frequency of the specified symbol, which is always 1. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the frequency of the symbol, which is 1 </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int get(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total of all symbol frequencies, which is
|
||||
/// always equal to the number of symbols in this table. </summary>
|
||||
/// <returns> the total of all symbol frequencies, which is {@code getSymbolLimit()} </returns>
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
return numSymbols;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of all the symbols strictly below
|
||||
/// the specified symbol value. The returned value is equal to {@code symbol}. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of all the symbols below {@code symbol}, which is {@code symbol} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getLow(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return symbol;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of the specified symbol and all
|
||||
/// the symbols below. The returned value is equal to {@code symbol + 1}. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of {@code symbol} and all symbols below, which is {@code symbol + 1} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getHigh(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return symbol + 1;
|
||||
}
|
||||
|
||||
|
||||
// Returns silently if 0 <= symbol < numSymbols, otherwise throws an exception.
|
||||
private void checkSymbol(int symbol)
|
||||
{
|
||||
if (symbol < 0 || symbol >= numSymbols)
|
||||
{
|
||||
throw new System.ArgumentException("Symbol out of range");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this frequency table. The format is subject to change. </summary>
|
||||
/// <returns> a string representation of this frequency table </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return "FlatFrequencyTable=" + numSymbols;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unsupported operation, because this frequency table is immutable. </summary>
|
||||
/// <param name="symbol"> ignored </param>
|
||||
/// <param name="freq"> ignored </param>
|
||||
/// <exception cref="UnsupportedOperationException"> because this frequency table is immutable </exception>
|
||||
public void set(int symbol, int freq)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Unsupported operation, because this frequency table is immutable. </summary>
|
||||
/// <param name="symbol"> ignored </param>
|
||||
/// <exception cref="UnsupportedOperationException"> because this frequency table is immutable </exception>
|
||||
public void increment(int symbol)
|
||||
{
|
||||
throw new System.NotSupportedException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,76 +1,76 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A table of symbol frequencies. The table holds data for symbols numbered from 0
|
||||
/// to getSymbolLimit()−1. Each symbol has a frequency, which is a non-negative integer.
|
||||
/// <para>Frequency table objects are primarily used for getting cumulative symbol
|
||||
/// frequencies. These objects can be mutable depending on the implementation.
|
||||
/// The total of all symbol frequencies must not exceed Integer.MAX_VALUE.</para>
|
||||
/// </summary>
|
||||
public interface FrequencyTable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in this frequency table, which is a positive number. </summary>
|
||||
/// <returns> the number of symbols in this frequency table </returns>
|
||||
int SymbolLimit {get;}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the frequency of the specified symbol. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the frequency of the symbol </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
int get(int symbol);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the frequency of the specified symbol to the specified value.
|
||||
/// The frequency value must be at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to set </param>
|
||||
/// <param name="freq"> the frequency value to set </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the frequency is negative or the symbol is out of range </exception>
|
||||
/// <exception cref="ArithmeticException"> if an arithmetic overflow occurs </exception>
|
||||
void set(int symbol, int freq);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Increments the frequency of the specified symbol. </summary>
|
||||
/// <param name="symbol"> the symbol whose frequency to increment </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
/// <exception cref="ArithmeticException"> if an arithmetic overflow occurs </exception>
|
||||
void increment(int symbol);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total of all symbol frequencies. The returned value is at
|
||||
/// least 0 and is always equal to {@code getHigh(getSymbolLimit() - 1)}. </summary>
|
||||
/// <returns> the total of all symbol frequencies </returns>
|
||||
int Total {get;}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of all the symbols strictly
|
||||
/// below the specified symbol value. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of all the symbols below {@code symbol} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
int getLow(int symbol);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of the specified symbol
|
||||
/// and all the symbols below. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of {@code symbol} and all symbols below </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
int getHigh(int symbol);
|
||||
|
||||
}
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A table of symbol frequencies. The table holds data for symbols numbered from 0
|
||||
/// to getSymbolLimit()−1. Each symbol has a frequency, which is a non-negative integer.
|
||||
/// <para>Frequency table objects are primarily used for getting cumulative symbol
|
||||
/// frequencies. These objects can be mutable depending on the implementation.
|
||||
/// The total of all symbol frequencies must not exceed Integer.MAX_VALUE.</para>
|
||||
/// </summary>
|
||||
public interface FrequencyTable
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in this frequency table, which is a positive number. </summary>
|
||||
/// <returns> the number of symbols in this frequency table </returns>
|
||||
int SymbolLimit {get;}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the frequency of the specified symbol. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the frequency of the symbol </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
int get(int symbol);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the frequency of the specified symbol to the specified value.
|
||||
/// The frequency value must be at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to set </param>
|
||||
/// <param name="freq"> the frequency value to set </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the frequency is negative or the symbol is out of range </exception>
|
||||
/// <exception cref="ArithmeticException"> if an arithmetic overflow occurs </exception>
|
||||
void set(int symbol, int freq);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Increments the frequency of the specified symbol. </summary>
|
||||
/// <param name="symbol"> the symbol whose frequency to increment </param>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
/// <exception cref="ArithmeticException"> if an arithmetic overflow occurs </exception>
|
||||
void increment(int symbol);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total of all symbol frequencies. The returned value is at
|
||||
/// least 0 and is always equal to {@code getHigh(getSymbolLimit() - 1)}. </summary>
|
||||
/// <returns> the total of all symbol frequencies </returns>
|
||||
int Total {get;}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of all the symbols strictly
|
||||
/// below the specified symbol value. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of all the symbols below {@code symbol} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
int getLow(int symbol);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of the specified symbol
|
||||
/// and all the symbols below. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of {@code symbol} and all symbols below </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if the symbol is out of range </exception>
|
||||
int getHigh(int symbol);
|
||||
|
||||
}
|
||||
|
||||
@ -1,129 +1,129 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compression application using prediction by partial matching (PPM) with arithmetic coding.
|
||||
/// <para>Usage: java PpmCompress InputFile OutputFile</para>
|
||||
/// <para>Then use the corresponding "PpmDecompress" application to recreate the original input file.</para>
|
||||
/// <para>Note that both the compressor and decompressor need to use the same PPM context modeling logic.
|
||||
/// The PPM algorithm can be thought of as a powerful generalization of adaptive arithmetic coding.</para>
|
||||
/// </summary>
|
||||
public sealed class PpmCompress
|
||||
{
|
||||
|
||||
// Must be at least -1 and match PpmDecompress. Warning: Exponential memory usage at O(257^n).
|
||||
private const int MODEL_ORDER = 3;
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@@@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java PpmCompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Perform file compression
|
||||
using (Stream @in = new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read)), BitOutputStream @out = new BitOutputStream(new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write))))
|
||||
{
|
||||
compress(@in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void compress(java.io.InputStream in, BitOutputStream out) throws java.io.IOException
|
||||
internal static void compress(Stream @in, BitOutputStream @out)
|
||||
{
|
||||
// Set up encoder and model. In this PPM model, symbol 256 represents EOF;
|
||||
// its frequency is 1 in the order -1 context but its frequency
|
||||
// is 0 in all other contexts (which have non-negative order).
|
||||
ArithmeticEncoder enc = new ArithmeticEncoder(32, @out);
|
||||
PpmModel model = new PpmModel(MODEL_ORDER, 257, 256);
|
||||
int[] history = new int[0];
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Read and encode one byte
|
||||
int symbol = @in.ReadByte();
|
||||
if (symbol == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
encodeSymbol(model, history, symbol, enc);
|
||||
model.incrementContexts(history, symbol);
|
||||
|
||||
if (model.modelOrder >= 1)
|
||||
{
|
||||
// Prepend current symbol, dropping oldest symbol if necessary
|
||||
if (history.Length < model.modelOrder)
|
||||
{
|
||||
history = Arrays.CopyOf(history, history.Length + 1);
|
||||
}
|
||||
Array.Copy(history, 0, history, 1, history.Length - 1);
|
||||
history[0] = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
encodeSymbol(model, history, 256, enc); // EOF
|
||||
enc.finish(); // Flush remaining code bits
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static void encodeSymbol(PpmModel model, int[] history, int symbol, ArithmeticEncoder enc) throws java.io.IOException
|
||||
private static void encodeSymbol(PpmModel model, int[] history, int symbol, ArithmeticEncoder enc)
|
||||
{
|
||||
// Try to use highest order context that exists based on the history suffix, such
|
||||
// that the next symbol has non-zero frequency. When symbol 256 is produced at a context
|
||||
// at any non-negative order, it means "escape to the next lower order with non-empty
|
||||
// context". When symbol 256 is produced at the order -1 context, it means "EOF".
|
||||
for (int order = history.Length; order >= 0; order--)
|
||||
{
|
||||
PpmModel.Context ctx = model.rootContext;
|
||||
for (int i = 0; i < order; i++)
|
||||
{
|
||||
Debug.Assert(ctx.subcontexts == null);
|
||||
|
||||
ctx = ctx.subcontexts[history[i]];
|
||||
if (ctx == null)
|
||||
{
|
||||
goto outerContinue;
|
||||
}
|
||||
}
|
||||
if (symbol != 256 && ctx.frequencies.get(symbol) > 0)
|
||||
{
|
||||
enc.write(ctx.frequencies, symbol);
|
||||
return;
|
||||
}
|
||||
// Else write context escape symbol and continue decrementing the order
|
||||
enc.write(ctx.frequencies, 256);
|
||||
outerContinue:;
|
||||
}
|
||||
|
||||
//outerBreak:
|
||||
// Logic for order = -1
|
||||
enc.write(model.orderMinus1Freqs, symbol);
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Compression application using prediction by partial matching (PPM) with arithmetic coding.
|
||||
/// <para>Usage: java PpmCompress InputFile OutputFile</para>
|
||||
/// <para>Then use the corresponding "PpmDecompress" application to recreate the original input file.</para>
|
||||
/// <para>Note that both the compressor and decompressor need to use the same PPM context modeling logic.
|
||||
/// The PPM algorithm can be thought of as a powerful generalization of adaptive arithmetic coding.</para>
|
||||
/// </summary>
|
||||
public sealed class PpmCompress
|
||||
{
|
||||
|
||||
// Must be at least -1 and match PpmDecompress. Warning: Exponential memory usage at O(257^n).
|
||||
private const int MODEL_ORDER = 3;
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
/* @@@@ PORT
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java PpmCompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(args[0]);
|
||||
File outputFile = new File(args[1]);
|
||||
|
||||
// Perform file compression
|
||||
using (Stream @in = new BufferedInputStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read)), BitOutputStream @out = new BitOutputStream(new BufferedOutputStream(new FileStream(outputFile, FileMode.Create, FileAccess.Write))))
|
||||
{
|
||||
compress(@in, @out);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void compress(java.io.InputStream in, BitOutputStream out) throws java.io.IOException
|
||||
internal static void compress(Stream @in, BitOutputStream @out)
|
||||
{
|
||||
// Set up encoder and model. In this PPM model, symbol 256 represents EOF;
|
||||
// its frequency is 1 in the order -1 context but its frequency
|
||||
// is 0 in all other contexts (which have non-negative order).
|
||||
ArithmeticEncoder enc = new ArithmeticEncoder(32, @out);
|
||||
PpmModel model = new PpmModel(MODEL_ORDER, 257, 256);
|
||||
int[] history = new int[0];
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Read and encode one byte
|
||||
int symbol = @in.ReadByte();
|
||||
if (symbol == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
encodeSymbol(model, history, symbol, enc);
|
||||
model.incrementContexts(history, symbol);
|
||||
|
||||
if (model.modelOrder >= 1)
|
||||
{
|
||||
// Prepend current symbol, dropping oldest symbol if necessary
|
||||
if (history.Length < model.modelOrder)
|
||||
{
|
||||
history = Arrays.CopyOf(history, history.Length + 1);
|
||||
}
|
||||
Array.Copy(history, 0, history, 1, history.Length - 1);
|
||||
history[0] = symbol;
|
||||
}
|
||||
}
|
||||
|
||||
encodeSymbol(model, history, 256, enc); // EOF
|
||||
enc.finish(); // Flush remaining code bits
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static void encodeSymbol(PpmModel model, int[] history, int symbol, ArithmeticEncoder enc) throws java.io.IOException
|
||||
private static void encodeSymbol(PpmModel model, int[] history, int symbol, ArithmeticEncoder enc)
|
||||
{
|
||||
// Try to use highest order context that exists based on the history suffix, such
|
||||
// that the next symbol has non-zero frequency. When symbol 256 is produced at a context
|
||||
// at any non-negative order, it means "escape to the next lower order with non-empty
|
||||
// context". When symbol 256 is produced at the order -1 context, it means "EOF".
|
||||
for (int order = history.Length; order >= 0; order--)
|
||||
{
|
||||
PpmModel.Context ctx = model.rootContext;
|
||||
for (int i = 0; i < order; i++)
|
||||
{
|
||||
Debug.Assert(ctx.subcontexts == null);
|
||||
|
||||
ctx = ctx.subcontexts[history[i]];
|
||||
if (ctx == null)
|
||||
{
|
||||
goto outerContinue;
|
||||
}
|
||||
}
|
||||
if (symbol != 256 && ctx.frequencies.get(symbol) > 0)
|
||||
{
|
||||
enc.write(ctx.frequencies, symbol);
|
||||
return;
|
||||
}
|
||||
// Else write context escape symbol and continue decrementing the order
|
||||
enc.write(ctx.frequencies, 256);
|
||||
outerContinue:;
|
||||
}
|
||||
|
||||
//outerBreak:
|
||||
// Logic for order = -1
|
||||
enc.write(model.orderMinus1Freqs, symbol);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,122 +1,122 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decompression application using prediction by partial matching (PPM) with arithmetic coding.
|
||||
/// <para>Usage: java PpmDecompress InputFile OutputFile</para>
|
||||
/// <para>This decompresses files generated by the "PpmCompress" application.</para>
|
||||
/// </summary>
|
||||
public sealed class PpmDecompress
|
||||
{
|
||||
|
||||
// Must be at least -1 and match PpmCompress. Warning: Exponential memory usage at O(257^n).
|
||||
private const int MODEL_ORDER = 3;
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java PpmDecompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
string inputFile = args[0]; //new File(args[0]);
|
||||
string outputFile =args[1]; //new File(args[1]);
|
||||
|
||||
// Perform file decompression
|
||||
using( BitInputStream @in = new BitInputStream(new BufferedStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read))))
|
||||
using( Stream @out = new BufferedStream( new FileStream(outputFile, FileMode.Create, FileAccess.Write)))
|
||||
{
|
||||
decompress(@in, @out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void decompress(BitInputStream in, java.io.OutputStream out) throws java.io.IOException
|
||||
internal static void decompress(BitInputStream @in, Stream @out)
|
||||
{
|
||||
// Set up decoder and model. In this PPM model, symbol 256 represents EOF;
|
||||
// its frequency is 1 in the order -1 context but its frequency
|
||||
// is 0 in all other contexts (which have non-negative order).
|
||||
ArithmeticDecoder dec = new ArithmeticDecoder(32, @in);
|
||||
PpmModel model = new PpmModel(MODEL_ORDER, 257, 256);
|
||||
int[] history = new int[0];
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Decode and write one byte
|
||||
int symbol = decodeSymbol(dec, model, history);
|
||||
if (symbol == 256) // EOF symbol
|
||||
{
|
||||
break;
|
||||
}
|
||||
@out.WriteByte((byte)symbol);
|
||||
model.incrementContexts(history, symbol);
|
||||
|
||||
if (model.modelOrder >= 1)
|
||||
{
|
||||
// Prepend current symbol, dropping oldest symbol if necessary
|
||||
if (history.Length < model.modelOrder)
|
||||
{
|
||||
history = Arrays.CopyOf(history, history.Length + 1);
|
||||
}
|
||||
Array.Copy(history, 0, history, 1, history.Length - 1);
|
||||
history[0] = symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static int decodeSymbol(ArithmeticDecoder dec, PpmModel model, int[] history) throws java.io.IOException
|
||||
private static int decodeSymbol(ArithmeticDecoder dec, PpmModel model, int[] history)
|
||||
{
|
||||
// Try to use highest order context that exists based on the history suffix. When symbol 256
|
||||
// is consumed at a context at any non-negative order, it means "escape to the next lower order
|
||||
// with non-empty context". When symbol 256 is consumed at the order -1 context, it means "EOF".
|
||||
for (int order = history.Length; order >= 0; order--)
|
||||
{
|
||||
PpmModel.Context ctx = model.rootContext;
|
||||
for (int i = 0; i < order; i++)
|
||||
{
|
||||
Debug.Assert(ctx.subcontexts == null);
|
||||
|
||||
ctx = ctx.subcontexts[history[i]];
|
||||
if (ctx == null)
|
||||
{
|
||||
goto outerContinue;
|
||||
}
|
||||
}
|
||||
int symbol = dec.read(ctx.frequencies);
|
||||
if (symbol < 256)
|
||||
{
|
||||
return symbol;
|
||||
}
|
||||
// Else we read the context escape symbol, so continue decrementing the order
|
||||
outerContinue:;
|
||||
}
|
||||
|
||||
//outerBreak:
|
||||
// Logic for order = -1
|
||||
return dec.read(model.orderMinus1Freqs);
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decompression application using prediction by partial matching (PPM) with arithmetic coding.
|
||||
/// <para>Usage: java PpmDecompress InputFile OutputFile</para>
|
||||
/// <para>This decompresses files generated by the "PpmCompress" application.</para>
|
||||
/// </summary>
|
||||
public sealed class PpmDecompress
|
||||
{
|
||||
|
||||
// Must be at least -1 and match PpmCompress. Warning: Exponential memory usage at O(257^n).
|
||||
private const int MODEL_ORDER = 3;
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: public static void main(String[] args) throws java.io.IOException
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Handle command line arguments
|
||||
if (args.Length != 2)
|
||||
{
|
||||
Console.Error.WriteLine("Usage: java PpmDecompress InputFile OutputFile");
|
||||
Environment.Exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
string inputFile = args[0]; //new File(args[0]);
|
||||
string outputFile =args[1]; //new File(args[1]);
|
||||
|
||||
// Perform file decompression
|
||||
using( BitInputStream @in = new BitInputStream(new BufferedStream(new FileStream(inputFile, FileMode.Open, FileAccess.Read))))
|
||||
using( Stream @out = new BufferedStream( new FileStream(outputFile, FileMode.Create, FileAccess.Write)))
|
||||
{
|
||||
decompress(@in, @out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// To allow unit testing, this method is package-private instead of private.
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: static void decompress(BitInputStream in, java.io.OutputStream out) throws java.io.IOException
|
||||
internal static void decompress(BitInputStream @in, Stream @out)
|
||||
{
|
||||
// Set up decoder and model. In this PPM model, symbol 256 represents EOF;
|
||||
// its frequency is 1 in the order -1 context but its frequency
|
||||
// is 0 in all other contexts (which have non-negative order).
|
||||
ArithmeticDecoder dec = new ArithmeticDecoder(32, @in);
|
||||
PpmModel model = new PpmModel(MODEL_ORDER, 257, 256);
|
||||
int[] history = new int[0];
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Decode and write one byte
|
||||
int symbol = decodeSymbol(dec, model, history);
|
||||
if (symbol == 256) // EOF symbol
|
||||
{
|
||||
break;
|
||||
}
|
||||
@out.WriteByte((byte)symbol);
|
||||
model.incrementContexts(history, symbol);
|
||||
|
||||
if (model.modelOrder >= 1)
|
||||
{
|
||||
// Prepend current symbol, dropping oldest symbol if necessary
|
||||
if (history.Length < model.modelOrder)
|
||||
{
|
||||
history = Arrays.CopyOf(history, history.Length + 1);
|
||||
}
|
||||
Array.Copy(history, 0, history, 1, history.Length - 1);
|
||||
history[0] = symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in C#:
|
||||
//ORIGINAL LINE: private static int decodeSymbol(ArithmeticDecoder dec, PpmModel model, int[] history) throws java.io.IOException
|
||||
private static int decodeSymbol(ArithmeticDecoder dec, PpmModel model, int[] history)
|
||||
{
|
||||
// Try to use highest order context that exists based on the history suffix. When symbol 256
|
||||
// is consumed at a context at any non-negative order, it means "escape to the next lower order
|
||||
// with non-empty context". When symbol 256 is consumed at the order -1 context, it means "EOF".
|
||||
for (int order = history.Length; order >= 0; order--)
|
||||
{
|
||||
PpmModel.Context ctx = model.rootContext;
|
||||
for (int i = 0; i < order; i++)
|
||||
{
|
||||
Debug.Assert(ctx.subcontexts == null);
|
||||
|
||||
ctx = ctx.subcontexts[history[i]];
|
||||
if (ctx == null)
|
||||
{
|
||||
goto outerContinue;
|
||||
}
|
||||
}
|
||||
int symbol = dec.read(ctx.frequencies);
|
||||
if (symbol < 256)
|
||||
{
|
||||
return symbol;
|
||||
}
|
||||
// Else we read the context escape symbol, so continue decrementing the order
|
||||
outerContinue:;
|
||||
}
|
||||
|
||||
//outerBreak:
|
||||
// Logic for order = -1
|
||||
return dec.read(model.orderMinus1Freqs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
226
ar/PpmModel.cs
226
ar/PpmModel.cs
@ -1,113 +1,113 @@
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
internal sealed class PpmModel
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
public readonly int modelOrder;
|
||||
|
||||
private readonly int symbolLimit;
|
||||
private readonly int escapeSymbol;
|
||||
|
||||
public readonly Context rootContext;
|
||||
public readonly FrequencyTable orderMinus1Freqs;
|
||||
|
||||
|
||||
|
||||
/*---- Constructors ----*/
|
||||
|
||||
public PpmModel(int order, int symbolLimit, int escapeSymbol)
|
||||
{
|
||||
if (order < -1 || symbolLimit <= 0 || escapeSymbol < 0 || escapeSymbol >= symbolLimit)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
this.modelOrder = order;
|
||||
this.symbolLimit = symbolLimit;
|
||||
this.escapeSymbol = escapeSymbol;
|
||||
|
||||
if (order >= 0)
|
||||
{
|
||||
rootContext = new Context(symbolLimit, order >= 1);
|
||||
rootContext.frequencies.increment(escapeSymbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootContext = null;
|
||||
}
|
||||
orderMinus1Freqs = new FlatFrequencyTable(symbolLimit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
public void incrementContexts(int[] history, int symbol)
|
||||
{
|
||||
if (modelOrder == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (history.Length > modelOrder || symbol < 0 || symbol >= symbolLimit)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
Context ctx = rootContext;
|
||||
ctx.frequencies.increment(symbol);
|
||||
int i = 0;
|
||||
foreach (int sym in history)
|
||||
{
|
||||
Context[] subctxs = ctx.subcontexts;
|
||||
Debug.Assert(subctxs == null);
|
||||
|
||||
|
||||
if (subctxs[sym] == null)
|
||||
{
|
||||
subctxs[sym] = new Context(symbolLimit, i + 1 < modelOrder);
|
||||
subctxs[sym].frequencies.increment(escapeSymbol);
|
||||
}
|
||||
ctx = subctxs[sym];
|
||||
ctx.frequencies.increment(symbol);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Helper structure ----*/
|
||||
|
||||
public sealed class Context
|
||||
{
|
||||
|
||||
public readonly FrequencyTable frequencies;
|
||||
|
||||
public readonly Context[] subcontexts;
|
||||
|
||||
|
||||
public Context(int symbols, bool hasSubctx)
|
||||
{
|
||||
frequencies = new SimpleFrequencyTable(new int[symbols]);
|
||||
if (hasSubctx)
|
||||
{
|
||||
subcontexts = new Context[symbols];
|
||||
}
|
||||
else
|
||||
{
|
||||
subcontexts = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
internal sealed class PpmModel
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
public readonly int modelOrder;
|
||||
|
||||
private readonly int symbolLimit;
|
||||
private readonly int escapeSymbol;
|
||||
|
||||
public readonly Context rootContext;
|
||||
public readonly FrequencyTable orderMinus1Freqs;
|
||||
|
||||
|
||||
|
||||
/*---- Constructors ----*/
|
||||
|
||||
public PpmModel(int order, int symbolLimit, int escapeSymbol)
|
||||
{
|
||||
if (order < -1 || symbolLimit <= 0 || escapeSymbol < 0 || escapeSymbol >= symbolLimit)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
this.modelOrder = order;
|
||||
this.symbolLimit = symbolLimit;
|
||||
this.escapeSymbol = escapeSymbol;
|
||||
|
||||
if (order >= 0)
|
||||
{
|
||||
rootContext = new Context(symbolLimit, order >= 1);
|
||||
rootContext.frequencies.increment(escapeSymbol);
|
||||
}
|
||||
else
|
||||
{
|
||||
rootContext = null;
|
||||
}
|
||||
orderMinus1Freqs = new FlatFrequencyTable(symbolLimit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
public void incrementContexts(int[] history, int symbol)
|
||||
{
|
||||
if (modelOrder == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (history.Length > modelOrder || symbol < 0 || symbol >= symbolLimit)
|
||||
{
|
||||
throw new System.ArgumentException();
|
||||
}
|
||||
|
||||
Context ctx = rootContext;
|
||||
ctx.frequencies.increment(symbol);
|
||||
int i = 0;
|
||||
foreach (int sym in history)
|
||||
{
|
||||
Context[] subctxs = ctx.subcontexts;
|
||||
Debug.Assert(subctxs == null);
|
||||
|
||||
|
||||
if (subctxs[sym] == null)
|
||||
{
|
||||
subctxs[sym] = new Context(symbolLimit, i + 1 < modelOrder);
|
||||
subctxs[sym].frequencies.increment(escapeSymbol);
|
||||
}
|
||||
ctx = subctxs[sym];
|
||||
ctx.frequencies.increment(symbol);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Helper structure ----*/
|
||||
|
||||
public sealed class Context
|
||||
{
|
||||
|
||||
public readonly FrequencyTable frequencies;
|
||||
|
||||
public readonly Context[] subcontexts;
|
||||
|
||||
|
||||
public Context(int symbols, bool hasSubctx)
|
||||
{
|
||||
frequencies = new SimpleFrequencyTable(new int[symbols]);
|
||||
if (hasSubctx)
|
||||
{
|
||||
subcontexts = new Context[symbols];
|
||||
}
|
||||
else
|
||||
{
|
||||
subcontexts = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,260 +1,260 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A mutable table of symbol frequencies. The number of symbols cannot be changed
|
||||
/// after construction. The current algorithm for calculating cumulative frequencies
|
||||
/// takes linear time, but there exist faster algorithms such as Fenwick trees.
|
||||
/// </summary>
|
||||
public sealed class SimpleFrequencyTable : FrequencyTable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The frequency for each symbol. Its length is at least 1, and each element is non-negative.
|
||||
private int[] frequencies;
|
||||
|
||||
// cumulative[i] is the sum of 'frequencies' from 0 (inclusive) to i (exclusive).
|
||||
// Initialized lazily. When this is not null, the data is valid.
|
||||
private int[] cumulative;
|
||||
|
||||
// Always equal to the sum of 'frequencies'.
|
||||
private int total;
|
||||
|
||||
|
||||
|
||||
/*---- Constructors ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a frequency table from the specified array of symbol frequencies. There must be at least
|
||||
/// 1 symbol, no symbol has a negative frequency, and the total must not exceed {@code Integer.MAX_VALUE}. </summary>
|
||||
/// <param name="freqs"> the array of symbol frequencies </param>
|
||||
/// <exception cref="NullPointerException"> if the array is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code freqs.length} < 1,
|
||||
/// {@code freqs.length} = {@code Integer.MAX_VALUE}, or any element {@code freqs[i]} < 0 </exception>
|
||||
/// <exception cref="ArithmeticException"> if the total of {@code freqs} exceeds {@code Integer.MAX_VALUE} </exception>
|
||||
public SimpleFrequencyTable(int[] freqs)
|
||||
{
|
||||
//Objects.requireNonNull(freqs);
|
||||
if (freqs.Length < 1)
|
||||
{
|
||||
throw new System.ArgumentException("At least 1 symbol needed");
|
||||
}
|
||||
if (freqs.Length > int.MaxValue - 1)
|
||||
{
|
||||
throw new System.ArgumentException("Too many symbols");
|
||||
}
|
||||
|
||||
frequencies = (int[])freqs.Clone(); // Make copy
|
||||
total = 0;
|
||||
foreach (int x in frequencies)
|
||||
{
|
||||
if (x < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Negative frequency");
|
||||
}
|
||||
total = checkedAdd(x, total);
|
||||
}
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a frequency table by copying the specified frequency table. </summary>
|
||||
/// <param name="freqs"> the frequency table to copy </param>
|
||||
/// <exception cref="NullPointerException"> if {@code freqs} is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code freqs.getSymbolLimit()} < 1
|
||||
/// or any element {@code freqs.get(i)} < 0 </exception>
|
||||
/// <exception cref="ArithmeticException"> if the total of all {@code freqs} elements exceeds {@code Integer.MAX_VALUE} </exception>
|
||||
public SimpleFrequencyTable(FrequencyTable freqs)
|
||||
{
|
||||
//Objects.requireNonNull(freqs);
|
||||
int numSym = freqs.SymbolLimit;
|
||||
Debug.Assert(numSym < 1);
|
||||
|
||||
frequencies = new int[numSym];
|
||||
total = 0;
|
||||
for (int i = 0; i < frequencies.Length; i++)
|
||||
{
|
||||
int x = freqs.get(i);
|
||||
Debug.Assert(x < 0);
|
||||
|
||||
frequencies[i] = x;
|
||||
total = checkedAdd(x, total);
|
||||
}
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in this frequency table, which is at least 1. </summary>
|
||||
/// <returns> the number of symbols in this frequency table </returns>
|
||||
public int SymbolLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
return frequencies.Length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the frequency of the specified symbol. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the frequency of the specified symbol </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int get(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return frequencies[symbol];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the frequency of the specified symbol to the specified value. The frequency value
|
||||
/// must be at least 0. If an exception is thrown, then the state is left unchanged. </summary>
|
||||
/// <param name="symbol"> the symbol to set </param>
|
||||
/// <param name="freq"> the frequency value to set </param>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
/// <exception cref="ArithmeticException"> if this set request would cause the total to exceed {@code Integer.MAX_VALUE} </exception>
|
||||
public void set(int symbol, int freq)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
if (freq < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Negative frequency");
|
||||
}
|
||||
|
||||
int temp = total - frequencies[symbol];
|
||||
Debug.Assert( temp < 0);
|
||||
|
||||
total = checkedAdd(temp, freq);
|
||||
frequencies[symbol] = freq;
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Increments the frequency of the specified symbol. </summary>
|
||||
/// <param name="symbol"> the symbol whose frequency to increment </param>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public void increment(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
Debug.Assert( frequencies[symbol] == int.MaxValue );
|
||||
|
||||
total = checkedAdd(total, 1);
|
||||
frequencies[symbol]++;
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total of all symbol frequencies. The returned value is at
|
||||
/// least 0 and is always equal to {@code getHigh(getSymbolLimit() - 1)}. </summary>
|
||||
/// <returns> the total of all symbol frequencies </returns>
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of all the symbols strictly
|
||||
/// below the specified symbol value. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of all the symbols below {@code symbol} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getLow(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
if (cumulative == null)
|
||||
{
|
||||
initCumulative();
|
||||
}
|
||||
return cumulative[symbol];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of the specified symbol
|
||||
/// and all the symbols below. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of {@code symbol} and all symbols below </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getHigh(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
if (cumulative == null)
|
||||
{
|
||||
initCumulative();
|
||||
}
|
||||
return cumulative[symbol + 1];
|
||||
}
|
||||
|
||||
|
||||
// Recomputes the array of cumulative symbol frequencies.
|
||||
private void initCumulative()
|
||||
{
|
||||
cumulative = new int[frequencies.Length + 1];
|
||||
int sum = 0;
|
||||
for (int i = 0; i < frequencies.Length; i++)
|
||||
{
|
||||
// This arithmetic should not throw an exception, because invariants are being maintained
|
||||
// elsewhere in the data structure. This implementation is just a defensive measure.
|
||||
sum = checkedAdd(frequencies[i], sum);
|
||||
cumulative[i + 1] = sum;
|
||||
}
|
||||
Debug.Assert( sum != total );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Returns silently if 0 <= symbol < frequencies.length, otherwise throws an exception.
|
||||
private void checkSymbol(int symbol)
|
||||
{
|
||||
Debug.Assert( symbol < 0 || symbol >= frequencies.Length );
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this frequency table,
|
||||
/// useful for debugging only, and the format is subject to change. </summary>
|
||||
/// <returns> a string representation of this frequency table </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < frequencies.Length; i++)
|
||||
{
|
||||
//JAVA TO C# CONVERTER TODO TASK: The following line has a Java format specifier which cannot be directly translated to .NET:
|
||||
sb.Append(string.Format("%d\t%d%n", i, frequencies[i]));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
// Adds the given integers, or throws an exception if the result cannot be represented as an int (i.e. overflow).
|
||||
private static int checkedAdd(int x, int y)
|
||||
{
|
||||
int z = x + y;
|
||||
Debug.Assert( y > 0 && z < x || y < 0 && z > x );
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
}
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
/*
|
||||
* Reference arithmetic coding
|
||||
* Copyright (c) Project Nayuki
|
||||
*
|
||||
* https://www.nayuki.io/page/reference-arithmetic-coding
|
||||
* https://github.com/nayuki/Reference-arithmetic-coding
|
||||
*/
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A mutable table of symbol frequencies. The number of symbols cannot be changed
|
||||
/// after construction. The current algorithm for calculating cumulative frequencies
|
||||
/// takes linear time, but there exist faster algorithms such as Fenwick trees.
|
||||
/// </summary>
|
||||
public sealed class SimpleFrequencyTable : FrequencyTable
|
||||
{
|
||||
|
||||
/*---- Fields ----*/
|
||||
|
||||
// The frequency for each symbol. Its length is at least 1, and each element is non-negative.
|
||||
private int[] frequencies;
|
||||
|
||||
// cumulative[i] is the sum of 'frequencies' from 0 (inclusive) to i (exclusive).
|
||||
// Initialized lazily. When this is not null, the data is valid.
|
||||
private int[] cumulative;
|
||||
|
||||
// Always equal to the sum of 'frequencies'.
|
||||
private int total;
|
||||
|
||||
|
||||
|
||||
/*---- Constructors ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a frequency table from the specified array of symbol frequencies. There must be at least
|
||||
/// 1 symbol, no symbol has a negative frequency, and the total must not exceed {@code Integer.MAX_VALUE}. </summary>
|
||||
/// <param name="freqs"> the array of symbol frequencies </param>
|
||||
/// <exception cref="NullPointerException"> if the array is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code freqs.length} < 1,
|
||||
/// {@code freqs.length} = {@code Integer.MAX_VALUE}, or any element {@code freqs[i]} < 0 </exception>
|
||||
/// <exception cref="ArithmeticException"> if the total of {@code freqs} exceeds {@code Integer.MAX_VALUE} </exception>
|
||||
public SimpleFrequencyTable(int[] freqs)
|
||||
{
|
||||
//Objects.requireNonNull(freqs);
|
||||
if (freqs.Length < 1)
|
||||
{
|
||||
throw new System.ArgumentException("At least 1 symbol needed");
|
||||
}
|
||||
if (freqs.Length > int.MaxValue - 1)
|
||||
{
|
||||
throw new System.ArgumentException("Too many symbols");
|
||||
}
|
||||
|
||||
frequencies = (int[])freqs.Clone(); // Make copy
|
||||
total = 0;
|
||||
foreach (int x in frequencies)
|
||||
{
|
||||
if (x < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Negative frequency");
|
||||
}
|
||||
total = checkedAdd(x, total);
|
||||
}
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a frequency table by copying the specified frequency table. </summary>
|
||||
/// <param name="freqs"> the frequency table to copy </param>
|
||||
/// <exception cref="NullPointerException"> if {@code freqs} is {@code null} </exception>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code freqs.getSymbolLimit()} < 1
|
||||
/// or any element {@code freqs.get(i)} < 0 </exception>
|
||||
/// <exception cref="ArithmeticException"> if the total of all {@code freqs} elements exceeds {@code Integer.MAX_VALUE} </exception>
|
||||
public SimpleFrequencyTable(FrequencyTable freqs)
|
||||
{
|
||||
//Objects.requireNonNull(freqs);
|
||||
int numSym = freqs.SymbolLimit;
|
||||
Debug.Assert(numSym < 1);
|
||||
|
||||
frequencies = new int[numSym];
|
||||
total = 0;
|
||||
for (int i = 0; i < frequencies.Length; i++)
|
||||
{
|
||||
int x = freqs.get(i);
|
||||
Debug.Assert(x < 0);
|
||||
|
||||
frequencies[i] = x;
|
||||
total = checkedAdd(x, total);
|
||||
}
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*---- Methods ----*/
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in this frequency table, which is at least 1. </summary>
|
||||
/// <returns> the number of symbols in this frequency table </returns>
|
||||
public int SymbolLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
return frequencies.Length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the frequency of the specified symbol. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the frequency of the specified symbol </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int get(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
return frequencies[symbol];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the frequency of the specified symbol to the specified value. The frequency value
|
||||
/// must be at least 0. If an exception is thrown, then the state is left unchanged. </summary>
|
||||
/// <param name="symbol"> the symbol to set </param>
|
||||
/// <param name="freq"> the frequency value to set </param>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
/// <exception cref="ArithmeticException"> if this set request would cause the total to exceed {@code Integer.MAX_VALUE} </exception>
|
||||
public void set(int symbol, int freq)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
if (freq < 0)
|
||||
{
|
||||
throw new System.ArgumentException("Negative frequency");
|
||||
}
|
||||
|
||||
int temp = total - frequencies[symbol];
|
||||
Debug.Assert( temp < 0);
|
||||
|
||||
total = checkedAdd(temp, freq);
|
||||
frequencies[symbol] = freq;
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Increments the frequency of the specified symbol. </summary>
|
||||
/// <param name="symbol"> the symbol whose frequency to increment </param>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public void increment(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
Debug.Assert( frequencies[symbol] == int.MaxValue );
|
||||
|
||||
total = checkedAdd(total, 1);
|
||||
frequencies[symbol]++;
|
||||
cumulative = null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the total of all symbol frequencies. The returned value is at
|
||||
/// least 0 and is always equal to {@code getHigh(getSymbolLimit() - 1)}. </summary>
|
||||
/// <returns> the total of all symbol frequencies </returns>
|
||||
public int Total
|
||||
{
|
||||
get
|
||||
{
|
||||
return total;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of all the symbols strictly
|
||||
/// below the specified symbol value. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of all the symbols below {@code symbol} </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getLow(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
if (cumulative == null)
|
||||
{
|
||||
initCumulative();
|
||||
}
|
||||
return cumulative[symbol];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of the frequencies of the specified symbol
|
||||
/// and all the symbols below. The returned value is at least 0. </summary>
|
||||
/// <param name="symbol"> the symbol to query </param>
|
||||
/// <returns> the sum of the frequencies of {@code symbol} and all symbols below </returns>
|
||||
/// <exception cref="IllegalArgumentException"> if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} </exception>
|
||||
public int getHigh(int symbol)
|
||||
{
|
||||
checkSymbol(symbol);
|
||||
if (cumulative == null)
|
||||
{
|
||||
initCumulative();
|
||||
}
|
||||
return cumulative[symbol + 1];
|
||||
}
|
||||
|
||||
|
||||
// Recomputes the array of cumulative symbol frequencies.
|
||||
private void initCumulative()
|
||||
{
|
||||
cumulative = new int[frequencies.Length + 1];
|
||||
int sum = 0;
|
||||
for (int i = 0; i < frequencies.Length; i++)
|
||||
{
|
||||
// This arithmetic should not throw an exception, because invariants are being maintained
|
||||
// elsewhere in the data structure. This implementation is just a defensive measure.
|
||||
sum = checkedAdd(frequencies[i], sum);
|
||||
cumulative[i + 1] = sum;
|
||||
}
|
||||
Debug.Assert( sum != total );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Returns silently if 0 <= symbol < frequencies.length, otherwise throws an exception.
|
||||
private void checkSymbol(int symbol)
|
||||
{
|
||||
Debug.Assert( symbol < 0 || symbol >= frequencies.Length );
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this frequency table,
|
||||
/// useful for debugging only, and the format is subject to change. </summary>
|
||||
/// <returns> a string representation of this frequency table </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < frequencies.Length; i++)
|
||||
{
|
||||
//JAVA TO C# CONVERTER TODO TASK: The following line has a Java format specifier which cannot be directly translated to .NET:
|
||||
sb.Append(string.Format("%d\t%d%n", i, frequencies[i]));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
// Adds the given integers, or throws an exception if the result cannot be represented as an int (i.e. overflow).
|
||||
private static int checkedAdd(int x, int y)
|
||||
{
|
||||
int z = x + y;
|
||||
Debug.Assert( y > 0 && z < x || y < 0 && z > x );
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
/* MIT License
|
||||
|
||||
Copyright (c) 2016 JetBrains http://www.jetbrains.com
|
||||
@ -21,12 +21,12 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE. */
|
||||
|
||||
SOFTWARE. */
|
||||
|
||||
using System;
|
||||
|
||||
namespace att
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the value of the marked element could be <c>null</c> sometimes, so the check for <c>null</c>
|
||||
/// is necessary before its usage.
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
// Copyright (c) Xenko contributors (https://xenko.com) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
||||
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace att
|
||||
{
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the value of the marked element could never be <c>null</c>.
|
||||
/// </summary>
|
||||
|
||||
192
db/Act.cs
192
db/Act.cs
@ -1,96 +1,96 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace db
|
||||
{
|
||||
public class Act
|
||||
{
|
||||
public Func<CommitResults> Fn => m_act;
|
||||
|
||||
|
||||
public string DebugInfo { get; private set; } = "";
|
||||
public string Path { get; private set; } = "";
|
||||
public int Line { get; private set; } = -1;
|
||||
public string Member { get; private set; } = "";
|
||||
|
||||
private Act( Func<CommitResults> act, string debugInfo = "{unknown_base}", string path = "", int line = -1, string member = "" )
|
||||
{
|
||||
m_act = act;
|
||||
|
||||
DebugInfo = debugInfo;
|
||||
Path = path;
|
||||
Line = line;
|
||||
Member = member;
|
||||
|
||||
//ExtractValue( act );
|
||||
}
|
||||
|
||||
static public Act create( Func<CommitResults> act, string debugInfo = "{unknown}", [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
|
||||
{
|
||||
//ExtractValue( act );
|
||||
|
||||
return new Act( act, debugInfo, path, line, member );
|
||||
}
|
||||
|
||||
public static Act create<T>( Func<T, CommitResults> act, T p0, string debugInfo = "{unknown}", [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
|
||||
{
|
||||
//ExtractValue( act );
|
||||
|
||||
//return new Act( act );
|
||||
|
||||
return new Act( () => { return act( p0 ); }, debugInfo, path, line, member );
|
||||
}
|
||||
|
||||
// If we're not doing any commit ops we can just use these.
|
||||
static public Act create( Action act, string debugInfo = "{unknown}", [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
|
||||
{
|
||||
//ExtractValue( act );
|
||||
|
||||
return new Act( () => { act(); return CommitResults.Perfect; }, debugInfo, path, line, member );
|
||||
}
|
||||
|
||||
public static Act create<T>( Action<T> act, T p0, string debugInfo = "{unknown}", [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
|
||||
{
|
||||
//ExtractValue( act );
|
||||
|
||||
//return new Act( act );
|
||||
|
||||
return new Act( () => { act( p0 ); return CommitResults.Perfect; }, debugInfo, path, line, member );
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void ExtractValue( Delegate lambda )
|
||||
{
|
||||
var lambdaType = lambda.GetType();
|
||||
|
||||
var methodType = lambda.Method.GetType();
|
||||
|
||||
//Nothing here.
|
||||
//var locals = lambda.Method.GetMethodBody().LocalVariables;
|
||||
|
||||
var targetType = lambda.Target?.GetType();
|
||||
|
||||
var fields = lambda.Method.DeclaringType?.GetFields
|
||||
(
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.Static
|
||||
);
|
||||
//.SingleOrDefault(x => x.Name == variableName);
|
||||
|
||||
//return (TValue)field.GetValue( lambda.Target );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Func<CommitResults> m_act;
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace db
|
||||
{
|
||||
public class Act
|
||||
{
|
||||
public Func<CommitResults> Fn => m_act;
|
||||
|
||||
|
||||
public string DebugInfo { get; private set; } = "";
|
||||
public string Path { get; private set; } = "";
|
||||
public int Line { get; private set; } = -1;
|
||||
public string Member { get; private set; } = "";
|
||||
|
||||
private Act( Func<CommitResults> act, string debugInfo = "{unknown_base}", string path = "", int line = -1, string member = "" )
|
||||
{
|
||||
m_act = act;
|
||||
|
||||
DebugInfo = debugInfo;
|
||||
Path = path;
|
||||
Line = line;
|
||||
Member = member;
|
||||
|
||||
//ExtractValue( act );
|
||||
}
|
||||
|
||||
static public Act create( Func<CommitResults> act, string debugInfo = "{unknown}", [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
|
||||
{
|
||||
//ExtractValue( act );
|
||||
|
||||
return new Act( act, debugInfo, path, line, member );
|
||||
}
|
||||
|
||||
public static Act create<T>( Func<T, CommitResults> act, T p0, string debugInfo = "{unknown}", [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
|
||||
{
|
||||
//ExtractValue( act );
|
||||
|
||||
//return new Act( act );
|
||||
|
||||
return new Act( () => { return act( p0 ); }, debugInfo, path, line, member );
|
||||
}
|
||||
|
||||
// If we're not doing any commit ops we can just use these.
|
||||
static public Act create( Action act, string debugInfo = "{unknown}", [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
|
||||
{
|
||||
//ExtractValue( act );
|
||||
|
||||
return new Act( () => { act(); return CommitResults.Perfect; }, debugInfo, path, line, member );
|
||||
}
|
||||
|
||||
public static Act create<T>( Action<T> act, T p0, string debugInfo = "{unknown}", [CallerFilePath] string path = "", [CallerLineNumber] int line = -1, [CallerMemberName] string member = "" )
|
||||
{
|
||||
//ExtractValue( act );
|
||||
|
||||
//return new Act( act );
|
||||
|
||||
return new Act( () => { act( p0 ); return CommitResults.Perfect; }, debugInfo, path, line, member );
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void ExtractValue( Delegate lambda )
|
||||
{
|
||||
var lambdaType = lambda.GetType();
|
||||
|
||||
var methodType = lambda.Method.GetType();
|
||||
|
||||
//Nothing here.
|
||||
//var locals = lambda.Method.GetMethodBody().LocalVariables;
|
||||
|
||||
var targetType = lambda.Target?.GetType();
|
||||
|
||||
var fields = lambda.Method.DeclaringType?.GetFields
|
||||
(
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.Static
|
||||
);
|
||||
//.SingleOrDefault(x => x.Name == variableName);
|
||||
|
||||
//return (TValue)field.GetValue( lambda.Target );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Func<CommitResults> m_act;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
442
db/DB.cs
442
db/DB.cs
@ -1,221 +1,221 @@
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using Optional;
|
||||
using static Optional.OptionExtensions;
|
||||
using static System.Collections.Immutable.ImmutableInterlocked;
|
||||
|
||||
/*
|
||||
???? Should we have an explicit transaction class/ID?
|
||||
???? Should we split things into threaded vs action
|
||||
*/
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
public enum CommitResults
|
||||
{
|
||||
Invalid,
|
||||
Perfect,
|
||||
Collisions,
|
||||
}
|
||||
|
||||
public interface IID<TS>
|
||||
{
|
||||
TS id { get; }
|
||||
}
|
||||
|
||||
public class DB<TID, T> where T : IID<TID>
|
||||
{
|
||||
//Current snapshot of the DB.
|
||||
ImmutableDictionary<TID, T> m_objs = ImmutableDictionary<TID, T>.Empty;
|
||||
|
||||
//List of committed Ids based on when they were committed.
|
||||
ImmutableList<TID> m_committed = ImmutableList<TID>.Empty;
|
||||
|
||||
ImmutableDictionary<TID, T> Objects => m_objs;
|
||||
|
||||
// @@@@ TODO This returns an entity that can be changing. It should be a lazy instantiated copy
|
||||
public Option<T> lookup( TID id )
|
||||
{
|
||||
if( m_objs.TryGetValue( id, out T obj ) )
|
||||
{
|
||||
return obj.Some();
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG
|
||||
}
|
||||
|
||||
return obj.None();
|
||||
}
|
||||
|
||||
public (Tx<TID, T>, Option<T>) checkout( TID id )
|
||||
{
|
||||
var tx = new Tx<TID, T>( m_committed.Count, m_activeTransaction, this );
|
||||
|
||||
var v = lookup( id );
|
||||
|
||||
v.Match( t => {
|
||||
tx.checkout( id );
|
||||
}, () => {
|
||||
} );
|
||||
|
||||
return (tx, v);
|
||||
}
|
||||
|
||||
public Tx<TID, T> checkout( TID id, out Option<T> tOut )
|
||||
{
|
||||
var (tx, v) = checkout(id);
|
||||
|
||||
tOut = v;
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
public Tx<TID, T> checkout()
|
||||
{
|
||||
var tx = new Tx<TID, T>( m_committed.Count, m_activeTransaction, this );
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
public CommitResults commit( ref Tx<TID, T> co )
|
||||
{
|
||||
co = null;
|
||||
return commit_internal_single( co );
|
||||
}
|
||||
|
||||
public ImmutableDictionary<TID, T> getSnapshot()
|
||||
{
|
||||
ImmutableDictionary<TID, T> res = m_objs;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
internal CommitResults commit_internal_single( Tx<TID, T> tx )
|
||||
{
|
||||
//var collision = false;
|
||||
|
||||
//Check for previously committed things
|
||||
var start = tx.Start;
|
||||
|
||||
var curCommitted = m_committed;
|
||||
|
||||
foreach( var t in tx.Checkouts )
|
||||
{
|
||||
for( int i = start; i < curCommitted.Count; ++i )
|
||||
{
|
||||
if( !t.id.Equals( curCommitted[i] ) ) { }
|
||||
else
|
||||
{
|
||||
//collision = true;
|
||||
return CommitResults.Collisions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @@@@ LOCK
|
||||
lock( m_committed )
|
||||
{
|
||||
TID[] committed = new TID[tx.Checkouts.Count];
|
||||
|
||||
for( var i = 0; i < tx.Checkouts.Count; ++i )
|
||||
{
|
||||
committed[i] = tx.Checkouts[i].id;
|
||||
m_objs = m_objs.Add( tx.Checkouts[i].id, tx.Checkouts[i] );
|
||||
}
|
||||
|
||||
m_committed = m_committed.AddRange(committed);
|
||||
|
||||
foreach( var v in tx.Adds )
|
||||
{
|
||||
m_objs = m_objs.Add( v.id, v );
|
||||
}
|
||||
|
||||
return CommitResults.Perfect;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Option<Tx<TID, T>> m_activeTransaction = Option.None<Tx<TID, T>>();
|
||||
|
||||
}
|
||||
|
||||
public enum TxStates
|
||||
{
|
||||
Invalid,
|
||||
Running,
|
||||
Committed,
|
||||
}
|
||||
|
||||
|
||||
//This only works for a single thread
|
||||
public class Tx<TID, T>: IDisposable where T : IID<TID>
|
||||
{
|
||||
internal ImmutableList<T> Checkouts => m_checkouts;
|
||||
internal TxStates State => m_state;
|
||||
internal int Start => m_start;
|
||||
internal ImmutableList<T> Adds => m_adds;
|
||||
|
||||
internal Tx( int start, DB<TID, T> db )
|
||||
:
|
||||
this(start, Option.None<Tx<TID, T>>(), db)
|
||||
{
|
||||
}
|
||||
|
||||
internal Tx( int start, Option<Tx<TID, T>> parentTx, DB<TID, T> db )
|
||||
{
|
||||
m_start = start;
|
||||
m_parentTx = parentTx;
|
||||
m_childTx = m_childTx.Add(this);
|
||||
m_db = db;
|
||||
m_state = TxStates.Running;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose of unmanaged resources.
|
||||
Dispose( true );
|
||||
// Suppress finalization.
|
||||
GC.SuppressFinalize( this );
|
||||
}
|
||||
|
||||
public void Dispose(bool isFromDispose )
|
||||
{
|
||||
if( isFromDispose )
|
||||
{
|
||||
m_db.commit_internal_single( this );
|
||||
}
|
||||
}
|
||||
|
||||
public Option<T> checkout( TID id )
|
||||
{
|
||||
var v = m_db.lookup( id );
|
||||
|
||||
v.MatchSome( t => { m_checkouts = m_checkouts.Add( t ); } );
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public void add( T obj )
|
||||
{
|
||||
m_adds = m_adds.Add(obj);
|
||||
}
|
||||
|
||||
|
||||
int m_start = -1;
|
||||
DB<TID, T> m_db;
|
||||
|
||||
//Do we need these? Do we need both?
|
||||
Option<Tx<TID, T>> m_parentTx;
|
||||
ImmutableList<Tx<TID, T>> m_childTx = ImmutableList<Tx<TID, T>>.Empty;
|
||||
|
||||
TxStates m_state = TxStates.Invalid;
|
||||
ImmutableList<T> m_checkouts = ImmutableList<T>.Empty;
|
||||
|
||||
// New objects created this pass
|
||||
ImmutableList<T> m_adds = ImmutableList<T>.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using Optional;
|
||||
using static Optional.OptionExtensions;
|
||||
using static System.Collections.Immutable.ImmutableInterlocked;
|
||||
|
||||
/*
|
||||
???? Should we have an explicit transaction class/ID?
|
||||
???? Should we split things into threaded vs action
|
||||
*/
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
public enum CommitResults
|
||||
{
|
||||
Invalid,
|
||||
Perfect,
|
||||
Collisions,
|
||||
}
|
||||
|
||||
public interface IID<TS>
|
||||
{
|
||||
TS id { get; }
|
||||
}
|
||||
|
||||
public class DB<TID, T> where T : IID<TID>
|
||||
{
|
||||
//Current snapshot of the DB.
|
||||
ImmutableDictionary<TID, T> m_objs = ImmutableDictionary<TID, T>.Empty;
|
||||
|
||||
//List of committed Ids based on when they were committed.
|
||||
ImmutableList<TID> m_committed = ImmutableList<TID>.Empty;
|
||||
|
||||
ImmutableDictionary<TID, T> Objects => m_objs;
|
||||
|
||||
// @@@@ TODO This returns an entity that can be changing. It should be a lazy instantiated copy
|
||||
public Option<T> lookup( TID id )
|
||||
{
|
||||
if( m_objs.TryGetValue( id, out T obj ) )
|
||||
{
|
||||
return obj.Some();
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOG
|
||||
}
|
||||
|
||||
return obj.None();
|
||||
}
|
||||
|
||||
public (Tx<TID, T>, Option<T>) checkout( TID id )
|
||||
{
|
||||
var tx = new Tx<TID, T>( m_committed.Count, m_activeTransaction, this );
|
||||
|
||||
var v = lookup( id );
|
||||
|
||||
v.Match( t => {
|
||||
tx.checkout( id );
|
||||
}, () => {
|
||||
} );
|
||||
|
||||
return (tx, v);
|
||||
}
|
||||
|
||||
public Tx<TID, T> checkout( TID id, out Option<T> tOut )
|
||||
{
|
||||
var (tx, v) = checkout(id);
|
||||
|
||||
tOut = v;
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
public Tx<TID, T> checkout()
|
||||
{
|
||||
var tx = new Tx<TID, T>( m_committed.Count, m_activeTransaction, this );
|
||||
|
||||
return tx;
|
||||
}
|
||||
|
||||
public CommitResults commit( ref Tx<TID, T> co )
|
||||
{
|
||||
co = null;
|
||||
return commit_internal_single( co );
|
||||
}
|
||||
|
||||
public ImmutableDictionary<TID, T> getSnapshot()
|
||||
{
|
||||
ImmutableDictionary<TID, T> res = m_objs;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
internal CommitResults commit_internal_single( Tx<TID, T> tx )
|
||||
{
|
||||
//var collision = false;
|
||||
|
||||
//Check for previously committed things
|
||||
var start = tx.Start;
|
||||
|
||||
var curCommitted = m_committed;
|
||||
|
||||
foreach( var t in tx.Checkouts )
|
||||
{
|
||||
for( int i = start; i < curCommitted.Count; ++i )
|
||||
{
|
||||
if( !t.id.Equals( curCommitted[i] ) ) { }
|
||||
else
|
||||
{
|
||||
//collision = true;
|
||||
return CommitResults.Collisions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @@@@ LOCK
|
||||
lock( m_committed )
|
||||
{
|
||||
TID[] committed = new TID[tx.Checkouts.Count];
|
||||
|
||||
for( var i = 0; i < tx.Checkouts.Count; ++i )
|
||||
{
|
||||
committed[i] = tx.Checkouts[i].id;
|
||||
m_objs = m_objs.Add( tx.Checkouts[i].id, tx.Checkouts[i] );
|
||||
}
|
||||
|
||||
m_committed = m_committed.AddRange(committed);
|
||||
|
||||
foreach( var v in tx.Adds )
|
||||
{
|
||||
m_objs = m_objs.Add( v.id, v );
|
||||
}
|
||||
|
||||
return CommitResults.Perfect;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Option<Tx<TID, T>> m_activeTransaction = Option.None<Tx<TID, T>>();
|
||||
|
||||
}
|
||||
|
||||
public enum TxStates
|
||||
{
|
||||
Invalid,
|
||||
Running,
|
||||
Committed,
|
||||
}
|
||||
|
||||
|
||||
//This only works for a single thread
|
||||
public class Tx<TID, T>: IDisposable where T : IID<TID>
|
||||
{
|
||||
internal ImmutableList<T> Checkouts => m_checkouts;
|
||||
internal TxStates State => m_state;
|
||||
internal int Start => m_start;
|
||||
internal ImmutableList<T> Adds => m_adds;
|
||||
|
||||
internal Tx( int start, DB<TID, T> db )
|
||||
:
|
||||
this(start, Option.None<Tx<TID, T>>(), db)
|
||||
{
|
||||
}
|
||||
|
||||
internal Tx( int start, Option<Tx<TID, T>> parentTx, DB<TID, T> db )
|
||||
{
|
||||
m_start = start;
|
||||
m_parentTx = parentTx;
|
||||
m_childTx = m_childTx.Add(this);
|
||||
m_db = db;
|
||||
m_state = TxStates.Running;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose of unmanaged resources.
|
||||
Dispose( true );
|
||||
// Suppress finalization.
|
||||
GC.SuppressFinalize( this );
|
||||
}
|
||||
|
||||
public void Dispose(bool isFromDispose )
|
||||
{
|
||||
if( isFromDispose )
|
||||
{
|
||||
m_db.commit_internal_single( this );
|
||||
}
|
||||
}
|
||||
|
||||
public Option<T> checkout( TID id )
|
||||
{
|
||||
var v = m_db.lookup( id );
|
||||
|
||||
v.MatchSome( t => { m_checkouts = m_checkouts.Add( t ); } );
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public void add( T obj )
|
||||
{
|
||||
m_adds = m_adds.Add(obj);
|
||||
}
|
||||
|
||||
|
||||
int m_start = -1;
|
||||
DB<TID, T> m_db;
|
||||
|
||||
//Do we need these? Do we need both?
|
||||
Option<Tx<TID, T>> m_parentTx;
|
||||
ImmutableList<Tx<TID, T>> m_childTx = ImmutableList<Tx<TID, T>>.Empty;
|
||||
|
||||
TxStates m_state = TxStates.Invalid;
|
||||
ImmutableList<T> m_checkouts = ImmutableList<T>.Empty;
|
||||
|
||||
// New objects created this pass
|
||||
ImmutableList<T> m_adds = ImmutableList<T>.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
244
db/Processor.cs
244
db/Processor.cs
@ -1,122 +1,122 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using Optional.Unsafe;
|
||||
|
||||
namespace db
|
||||
{
|
||||
public enum State
|
||||
{
|
||||
Invalid,
|
||||
Prestartup,
|
||||
Active,
|
||||
Waiting,
|
||||
Stopped,
|
||||
}
|
||||
|
||||
|
||||
public class Processor<TID, T> where T : IID<TID>
|
||||
{
|
||||
|
||||
|
||||
public DB<TID, T> DB { get; private set; }
|
||||
|
||||
public System<TID, T> Sys { get; private set; }
|
||||
|
||||
public State State => m_state;
|
||||
|
||||
//public SemaphoreSlim Semaphore { get; private set; } = new SemaphoreSlim( 1 );
|
||||
public int Processed => m_processed;
|
||||
|
||||
public Act DebugCurrentAct => m_debugCurrentAct;
|
||||
|
||||
public Processor( DB<TID, T> db, System<TID, T> sys )
|
||||
{
|
||||
DB = db;
|
||||
Sys= sys;
|
||||
m_state = State.Prestartup;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
m_state = State.Active;
|
||||
|
||||
|
||||
while( Sys.Running )
|
||||
{
|
||||
tick();
|
||||
}
|
||||
|
||||
m_state = State.Stopped;
|
||||
}
|
||||
|
||||
public void tick()
|
||||
{
|
||||
var actOpt = Sys.getNextAct();
|
||||
|
||||
if( !actOpt.HasValue )
|
||||
{
|
||||
//log.trace( $"{Thread.CurrentThread.Name} Processed {m_processed} acts" );
|
||||
|
||||
/*
|
||||
m_state = State.Waiting;
|
||||
Semaphore.Wait();
|
||||
|
||||
m_state = State.Active;
|
||||
|
||||
m_processed = 0;
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var act = actOpt.ValueOrDefault();
|
||||
|
||||
m_debugCurrentAct = act;
|
||||
|
||||
// @@@ TODO Put a timer around this and make sure any particular act is shorter than that. Probably 1ms and 5ms.
|
||||
|
||||
act.Fn();
|
||||
|
||||
++m_processed;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
public void kick()
|
||||
{
|
||||
Semaphore.Release();
|
||||
}
|
||||
*/
|
||||
|
||||
volatile State m_state;
|
||||
int m_processed = 0;
|
||||
//volatile string ProcessingDebug = "";
|
||||
|
||||
Act m_debugCurrentAct = null;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using Optional.Unsafe;
|
||||
|
||||
namespace db
|
||||
{
|
||||
public enum State
|
||||
{
|
||||
Invalid,
|
||||
Prestartup,
|
||||
Active,
|
||||
Waiting,
|
||||
Stopped,
|
||||
}
|
||||
|
||||
|
||||
public class Processor<TID, T> where T : IID<TID>
|
||||
{
|
||||
|
||||
|
||||
public DB<TID, T> DB { get; private set; }
|
||||
|
||||
public System<TID, T> Sys { get; private set; }
|
||||
|
||||
public State State => m_state;
|
||||
|
||||
//public SemaphoreSlim Semaphore { get; private set; } = new SemaphoreSlim( 1 );
|
||||
public int Processed => m_processed;
|
||||
|
||||
public Act DebugCurrentAct => m_debugCurrentAct;
|
||||
|
||||
public Processor( DB<TID, T> db, System<TID, T> sys )
|
||||
{
|
||||
DB = db;
|
||||
Sys= sys;
|
||||
m_state = State.Prestartup;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
m_state = State.Active;
|
||||
|
||||
|
||||
while( Sys.Running )
|
||||
{
|
||||
tick();
|
||||
}
|
||||
|
||||
m_state = State.Stopped;
|
||||
}
|
||||
|
||||
public void tick()
|
||||
{
|
||||
var actOpt = Sys.getNextAct();
|
||||
|
||||
if( !actOpt.HasValue )
|
||||
{
|
||||
//log.trace( $"{Thread.CurrentThread.Name} Processed {m_processed} acts" );
|
||||
|
||||
/*
|
||||
m_state = State.Waiting;
|
||||
Semaphore.Wait();
|
||||
|
||||
m_state = State.Active;
|
||||
|
||||
m_processed = 0;
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var act = actOpt.ValueOrDefault();
|
||||
|
||||
m_debugCurrentAct = act;
|
||||
|
||||
// @@@ TODO Put a timer around this and make sure any particular act is shorter than that. Probably 1ms and 5ms.
|
||||
|
||||
act.Fn();
|
||||
|
||||
++m_processed;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
public void kick()
|
||||
{
|
||||
Semaphore.Release();
|
||||
}
|
||||
*/
|
||||
|
||||
volatile State m_state;
|
||||
int m_processed = 0;
|
||||
//volatile string ProcessingDebug = "";
|
||||
|
||||
Act m_debugCurrentAct = null;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
616
db/System.cs
616
db/System.cs
@ -1,308 +1,308 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Optional;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
struct TimedAction : IComparable<TimedAction>
|
||||
{
|
||||
public long when;
|
||||
public Act act;
|
||||
|
||||
public TimedAction( long when, Act act )
|
||||
{
|
||||
this.when = when;
|
||||
this.act = act;
|
||||
}
|
||||
|
||||
public int CompareTo( TimedAction other )
|
||||
{
|
||||
return when.CompareTo( other.when );
|
||||
}
|
||||
|
||||
public override bool Equals( object obj )
|
||||
{
|
||||
return obj is TimedAction action &&
|
||||
when == action.when &&
|
||||
EqualityComparer<Act>.Default.Equals( act, action.act );
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hc = when.GetHashCode() ^ act.GetHashCode();
|
||||
return hc;
|
||||
}
|
||||
}
|
||||
|
||||
public class SystemCfg : lib.Config
|
||||
{
|
||||
public readonly float Cores = 1;
|
||||
}
|
||||
|
||||
public class System<TID, T> where T : IID<TID>
|
||||
{
|
||||
//public static System Current => s_system;
|
||||
|
||||
public SemaphoreSlim ActsExist => m_actsExist;
|
||||
public DB<TID, T> DB { get; private set; }
|
||||
|
||||
public bool Running { get; private set; }
|
||||
|
||||
public System( res.Ref<SystemCfg> cfg, DB<TID, T> db )
|
||||
{
|
||||
m_cfg = cfg;
|
||||
DB = db;
|
||||
|
||||
var procCount = Environment.ProcessorCount;
|
||||
|
||||
//Exact comparison
|
||||
if( m_cfg.res.Cores != 0.0f )
|
||||
{
|
||||
//If its less than 1, then use it as a multiplier
|
||||
if( m_cfg.res.Cores < 0.0f )
|
||||
{
|
||||
procCount = Environment.ProcessorCount - (int)m_cfg.res.Cores;
|
||||
}
|
||||
else if( m_cfg.res.Cores < 1.0f )
|
||||
{
|
||||
procCount = (int) ((float)Environment.ProcessorCount * m_cfg.res.Cores);
|
||||
}
|
||||
else
|
||||
{
|
||||
procCount = (int)m_cfg.res.Cores;
|
||||
}
|
||||
}
|
||||
|
||||
log.info( $"Running {procCount} cores out of a total cores {Environment.ProcessorCount} via a config Cores value of {m_cfg.res.Cores}" );
|
||||
|
||||
Processor<TID, T>[] procs = new Processor<TID, T>[procCount];
|
||||
|
||||
for( var i = 0; i < procCount; ++i )
|
||||
{
|
||||
var proc = new Processor<TID, T>( db, this );
|
||||
|
||||
procs[i] = proc;
|
||||
}
|
||||
|
||||
m_processors = m_processors.AddRange( procs );
|
||||
|
||||
Running = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void forcedThisTick( Act act )
|
||||
{
|
||||
m_current.Add( act );
|
||||
|
||||
m_actsExist.Release();
|
||||
}
|
||||
|
||||
public void next( Act act )
|
||||
{
|
||||
m_next.Add( act );
|
||||
}
|
||||
|
||||
//Most things dont need accurate next frame processing, so split them between the next frame N frames
|
||||
const double s_variance = 1.0 / 15.0;
|
||||
|
||||
public void future( Act act, double future, double maxVariance = s_variance )
|
||||
{
|
||||
//m_actions.Add( act );
|
||||
|
||||
var variance = m_rand.NextDouble() * maxVariance;
|
||||
|
||||
var nextTime = future + variance;
|
||||
|
||||
if( nextTime < 1.0 / 60.0 )
|
||||
{
|
||||
next( act );
|
||||
return;
|
||||
}
|
||||
|
||||
var ts = TimeSpan.FromSeconds( nextTime );
|
||||
|
||||
var tsTicks = ts.Ticks;
|
||||
|
||||
// @@@ TIMING Should we use a fixed time at the front of the frame for this?
|
||||
var ticks = tsTicks + DateTime.Now.Ticks;
|
||||
|
||||
var ta = new TimedAction( ticks, act );
|
||||
|
||||
var newFuture = m_futureActions.Add( ta );
|
||||
|
||||
Interlocked.Exchange( ref m_futureActions, newFuture );
|
||||
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
int count = 0;
|
||||
foreach( var p in m_processors )
|
||||
{
|
||||
var start = new ThreadStart( p.run );
|
||||
|
||||
var th = new Thread( start );
|
||||
th.Name = $"Processor_{count}";
|
||||
|
||||
th.Start();
|
||||
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
public void tick()
|
||||
{
|
||||
//Debug.Assert( m_current.IsEmpty );
|
||||
|
||||
addTimedActions();
|
||||
|
||||
var current = m_current;
|
||||
m_current = m_next;
|
||||
m_next = current;
|
||||
|
||||
while( !m_current.IsEmpty )
|
||||
{
|
||||
m_actsExist.Release();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
foreach( var proc in m_processors )
|
||||
{
|
||||
//Debug.Assert( proc.State == State.Waiting );
|
||||
|
||||
proc.kick();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
public void wait_blah( int targetMs, int maxMs )
|
||||
{
|
||||
var done = 0;
|
||||
|
||||
var start = DateTime.Now;
|
||||
var delta = start - start;
|
||||
|
||||
while( done < m_processors.Count && delta.TotalMilliseconds < maxMs )
|
||||
{
|
||||
done = 0;
|
||||
|
||||
foreach( var proc in m_processors )
|
||||
{
|
||||
if( proc.State != State.Active )
|
||||
{
|
||||
++done;
|
||||
}
|
||||
}
|
||||
|
||||
delta = DateTime.Now - start;
|
||||
}
|
||||
|
||||
if( done != m_processors.Count )
|
||||
{
|
||||
log.warn( $"Processing took significantly too long {delta.TotalSeconds}sec." );
|
||||
|
||||
foreach( var proc in m_processors )
|
||||
{
|
||||
Act debugAct = proc.DebugCurrentAct;
|
||||
|
||||
if( proc.State == State.Active )
|
||||
{
|
||||
log.warn( $"Proc is still running\n{debugAct.Path}({debugAct.Line}): In method {debugAct.Member}" );
|
||||
|
||||
// @@@ TODO Should we kill the procedure? Let it continue to run?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( delta.TotalMilliseconds > targetMs )
|
||||
{
|
||||
log.warn( $"Missed our target {delta.TotalMilliseconds} framerate." );
|
||||
}
|
||||
|
||||
}
|
||||
//*/
|
||||
|
||||
public void addTimedActions()
|
||||
{
|
||||
var sortedFutureActions = m_futureActions.Sort( );
|
||||
|
||||
var future = TimeSpan.FromMilliseconds( 33.33333 );
|
||||
|
||||
var time = DateTime.Now + future;
|
||||
|
||||
foreach( var action in sortedFutureActions )
|
||||
{
|
||||
if( action.when < time.Ticks )
|
||||
{
|
||||
next( action.act );
|
||||
|
||||
var newActions = m_futureActions.Remove( action );
|
||||
|
||||
Interlocked.Exchange( ref m_futureActions, newActions );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopRunning()
|
||||
{
|
||||
Running = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
internal Option<Act> getNextAct()
|
||||
{
|
||||
if( m_current.TryTake( out Act res ) )
|
||||
{
|
||||
return res.Some();
|
||||
}
|
||||
|
||||
m_actsExist.Wait();
|
||||
|
||||
return Option.None<Act>();
|
||||
}
|
||||
|
||||
res.Ref<SystemCfg> m_cfg;
|
||||
|
||||
SemaphoreSlim m_actsExist = new SemaphoreSlim(0);
|
||||
|
||||
Random m_rand = new Random();
|
||||
|
||||
ConcurrentBag<Act> m_current = new ConcurrentBag<Act>();
|
||||
ConcurrentBag<Act> m_next = new ConcurrentBag<Act>();
|
||||
|
||||
// @@ TODO Keep an eye on the timing of this.
|
||||
ImmutableList<TimedAction> m_futureActions = ImmutableList<TimedAction>.Empty;
|
||||
|
||||
/*
|
||||
TimedAction[] m_sortedFutureActions = new TimedAction[16 * 1024];
|
||||
int m_sfaStart = 0;
|
||||
int m_sfaEnd = 0;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
ImmutableList<Processor<TID, T>> m_processors = ImmutableList<Processor<TID, T>>.Empty;
|
||||
|
||||
//private static System s_system;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Optional;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
||||
struct TimedAction : IComparable<TimedAction>
|
||||
{
|
||||
public long when;
|
||||
public Act act;
|
||||
|
||||
public TimedAction( long when, Act act )
|
||||
{
|
||||
this.when = when;
|
||||
this.act = act;
|
||||
}
|
||||
|
||||
public int CompareTo( TimedAction other )
|
||||
{
|
||||
return when.CompareTo( other.when );
|
||||
}
|
||||
|
||||
public override bool Equals( object obj )
|
||||
{
|
||||
return obj is TimedAction action &&
|
||||
when == action.when &&
|
||||
EqualityComparer<Act>.Default.Equals( act, action.act );
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hc = when.GetHashCode() ^ act.GetHashCode();
|
||||
return hc;
|
||||
}
|
||||
}
|
||||
|
||||
public class SystemCfg : lib.Config
|
||||
{
|
||||
public readonly float Cores = 1;
|
||||
}
|
||||
|
||||
public class System<TID, T> where T : IID<TID>
|
||||
{
|
||||
//public static System Current => s_system;
|
||||
|
||||
public SemaphoreSlim ActsExist => m_actsExist;
|
||||
public DB<TID, T> DB { get; private set; }
|
||||
|
||||
public bool Running { get; private set; }
|
||||
|
||||
public System( res.Ref<SystemCfg> cfg, DB<TID, T> db )
|
||||
{
|
||||
m_cfg = cfg;
|
||||
DB = db;
|
||||
|
||||
var procCount = Environment.ProcessorCount;
|
||||
|
||||
//Exact comparison
|
||||
if( m_cfg.res.Cores != 0.0f )
|
||||
{
|
||||
//If its less than 1, then use it as a multiplier
|
||||
if( m_cfg.res.Cores < 0.0f )
|
||||
{
|
||||
procCount = Environment.ProcessorCount - (int)m_cfg.res.Cores;
|
||||
}
|
||||
else if( m_cfg.res.Cores < 1.0f )
|
||||
{
|
||||
procCount = (int) ((float)Environment.ProcessorCount * m_cfg.res.Cores);
|
||||
}
|
||||
else
|
||||
{
|
||||
procCount = (int)m_cfg.res.Cores;
|
||||
}
|
||||
}
|
||||
|
||||
log.info( $"Running {procCount} cores out of a total cores {Environment.ProcessorCount} via a config Cores value of {m_cfg.res.Cores}" );
|
||||
|
||||
Processor<TID, T>[] procs = new Processor<TID, T>[procCount];
|
||||
|
||||
for( var i = 0; i < procCount; ++i )
|
||||
{
|
||||
var proc = new Processor<TID, T>( db, this );
|
||||
|
||||
procs[i] = proc;
|
||||
}
|
||||
|
||||
m_processors = m_processors.AddRange( procs );
|
||||
|
||||
Running = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void forcedThisTick( Act act )
|
||||
{
|
||||
m_current.Add( act );
|
||||
|
||||
m_actsExist.Release();
|
||||
}
|
||||
|
||||
public void next( Act act )
|
||||
{
|
||||
m_next.Add( act );
|
||||
}
|
||||
|
||||
//Most things dont need accurate next frame processing, so split them between the next frame N frames
|
||||
const double s_variance = 1.0 / 15.0;
|
||||
|
||||
public void future( Act act, double future, double maxVariance = s_variance )
|
||||
{
|
||||
//m_actions.Add( act );
|
||||
|
||||
var variance = m_rand.NextDouble() * maxVariance;
|
||||
|
||||
var nextTime = future + variance;
|
||||
|
||||
if( nextTime < 1.0 / 60.0 )
|
||||
{
|
||||
next( act );
|
||||
return;
|
||||
}
|
||||
|
||||
var ts = TimeSpan.FromSeconds( nextTime );
|
||||
|
||||
var tsTicks = ts.Ticks;
|
||||
|
||||
// @@@ TIMING Should we use a fixed time at the front of the frame for this?
|
||||
var ticks = tsTicks + DateTime.Now.Ticks;
|
||||
|
||||
var ta = new TimedAction( ticks, act );
|
||||
|
||||
var newFuture = m_futureActions.Add( ta );
|
||||
|
||||
Interlocked.Exchange( ref m_futureActions, newFuture );
|
||||
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
int count = 0;
|
||||
foreach( var p in m_processors )
|
||||
{
|
||||
var start = new ThreadStart( p.run );
|
||||
|
||||
var th = new Thread( start );
|
||||
th.Name = $"Processor_{count}";
|
||||
|
||||
th.Start();
|
||||
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
public void tick()
|
||||
{
|
||||
//Debug.Assert( m_current.IsEmpty );
|
||||
|
||||
addTimedActions();
|
||||
|
||||
var current = m_current;
|
||||
m_current = m_next;
|
||||
m_next = current;
|
||||
|
||||
while( !m_current.IsEmpty )
|
||||
{
|
||||
m_actsExist.Release();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
foreach( var proc in m_processors )
|
||||
{
|
||||
//Debug.Assert( proc.State == State.Waiting );
|
||||
|
||||
proc.kick();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
public void wait_blah( int targetMs, int maxMs )
|
||||
{
|
||||
var done = 0;
|
||||
|
||||
var start = DateTime.Now;
|
||||
var delta = start - start;
|
||||
|
||||
while( done < m_processors.Count && delta.TotalMilliseconds < maxMs )
|
||||
{
|
||||
done = 0;
|
||||
|
||||
foreach( var proc in m_processors )
|
||||
{
|
||||
if( proc.State != State.Active )
|
||||
{
|
||||
++done;
|
||||
}
|
||||
}
|
||||
|
||||
delta = DateTime.Now - start;
|
||||
}
|
||||
|
||||
if( done != m_processors.Count )
|
||||
{
|
||||
log.warn( $"Processing took significantly too long {delta.TotalSeconds}sec." );
|
||||
|
||||
foreach( var proc in m_processors )
|
||||
{
|
||||
Act debugAct = proc.DebugCurrentAct;
|
||||
|
||||
if( proc.State == State.Active )
|
||||
{
|
||||
log.warn( $"Proc is still running\n{debugAct.Path}({debugAct.Line}): In method {debugAct.Member}" );
|
||||
|
||||
// @@@ TODO Should we kill the procedure? Let it continue to run?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( delta.TotalMilliseconds > targetMs )
|
||||
{
|
||||
log.warn( $"Missed our target {delta.TotalMilliseconds} framerate." );
|
||||
}
|
||||
|
||||
}
|
||||
//*/
|
||||
|
||||
public void addTimedActions()
|
||||
{
|
||||
var sortedFutureActions = m_futureActions.Sort( );
|
||||
|
||||
var future = TimeSpan.FromMilliseconds( 33.33333 );
|
||||
|
||||
var time = DateTime.Now + future;
|
||||
|
||||
foreach( var action in sortedFutureActions )
|
||||
{
|
||||
if( action.when < time.Ticks )
|
||||
{
|
||||
next( action.act );
|
||||
|
||||
var newActions = m_futureActions.Remove( action );
|
||||
|
||||
Interlocked.Exchange( ref m_futureActions, newActions );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopRunning()
|
||||
{
|
||||
Running = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
internal Option<Act> getNextAct()
|
||||
{
|
||||
if( m_current.TryTake( out Act res ) )
|
||||
{
|
||||
return res.Some();
|
||||
}
|
||||
|
||||
m_actsExist.Wait();
|
||||
|
||||
return Option.None<Act>();
|
||||
}
|
||||
|
||||
res.Ref<SystemCfg> m_cfg;
|
||||
|
||||
SemaphoreSlim m_actsExist = new SemaphoreSlim(0);
|
||||
|
||||
Random m_rand = new Random();
|
||||
|
||||
ConcurrentBag<Act> m_current = new ConcurrentBag<Act>();
|
||||
ConcurrentBag<Act> m_next = new ConcurrentBag<Act>();
|
||||
|
||||
// @@ TODO Keep an eye on the timing of this.
|
||||
ImmutableList<TimedAction> m_futureActions = ImmutableList<TimedAction>.Empty;
|
||||
|
||||
/*
|
||||
TimedAction[] m_sortedFutureActions = new TimedAction[16 * 1024];
|
||||
int m_sfaStart = 0;
|
||||
int m_sfaEnd = 0;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
ImmutableList<Processor<TID, T>> m_processors = ImmutableList<Processor<TID, T>>.Empty;
|
||||
|
||||
//private static System s_system;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
92
math/Pos.cs
92
math/Pos.cs
@ -1,52 +1,52 @@
|
||||
using System;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
|
||||
[Serializable]
|
||||
public struct Pos
|
||||
{
|
||||
public float x { get; private set; }
|
||||
public float y { get; private set; }
|
||||
public float z { get; private set; }
|
||||
|
||||
|
||||
public Pos( float _x, float _y, float _z ) : this()
|
||||
{
|
||||
x = _x;
|
||||
y = _y;
|
||||
z = _z;
|
||||
}
|
||||
|
||||
// overload operator +
|
||||
public static Pos operator +( Pos a, Pos b )
|
||||
{
|
||||
return new Pos( a.x + b.x, a.y + b.y, a.z + b.z );
|
||||
}
|
||||
|
||||
public static Pos operator -( Pos a, Pos b )
|
||||
{
|
||||
return new Pos( a.x - b.x, a.y - b.y, a.z - b.z );
|
||||
}
|
||||
|
||||
public static Pos operator /( Pos a, float val )
|
||||
{
|
||||
return new Pos( a.x / val, a.y / val, a.z / val );
|
||||
}
|
||||
|
||||
public static Pos operator *( Pos a, float val )
|
||||
{
|
||||
return new Pos( a.x * val, a.y * val, a.z * val );
|
||||
}
|
||||
|
||||
public float distSqr( Pos other )
|
||||
{
|
||||
float dx = x - other.x;
|
||||
float dy = y - other.y;
|
||||
float dz = z - other.z;
|
||||
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
{
|
||||
|
||||
[Serializable]
|
||||
public struct Pos
|
||||
{
|
||||
public float x { get; private set; }
|
||||
public float y { get; private set; }
|
||||
public float z { get; private set; }
|
||||
|
||||
|
||||
public Pos( float _x, float _y, float _z ) : this()
|
||||
{
|
||||
x = _x;
|
||||
y = _y;
|
||||
z = _z;
|
||||
}
|
||||
|
||||
// overload operator +
|
||||
public static Pos operator +( Pos a, Pos b )
|
||||
{
|
||||
return new Pos( a.x + b.x, a.y + b.y, a.z + b.z );
|
||||
}
|
||||
|
||||
public static Pos operator -( Pos a, Pos b )
|
||||
{
|
||||
return new Pos( a.x - b.x, a.y - b.y, a.z - b.z );
|
||||
}
|
||||
|
||||
public static Pos operator /( Pos a, float val )
|
||||
{
|
||||
return new Pos( a.x / val, a.y / val, a.z / val );
|
||||
}
|
||||
|
||||
public static Pos operator *( Pos a, float val )
|
||||
{
|
||||
return new Pos( a.x * val, a.y * val, a.z * val );
|
||||
}
|
||||
|
||||
public float distSqr( Pos other )
|
||||
{
|
||||
float dx = x - other.x;
|
||||
float dy = y - other.y;
|
||||
float dz = z - other.z;
|
||||
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
208
math/fn.cs
208
math/fn.cs
@ -1,113 +1,113 @@
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace math
|
||||
{
|
||||
|
||||
static public class fn
|
||||
{
|
||||
|
||||
static public float ToDeg( float rad ) => ( float )(180.0 / Math.PI) * rad;
|
||||
static public float ToRad( float deg ) => ( float )(Math.PI / 180.0) * deg;
|
||||
|
||||
static public float Clamp( float v, float min, float max )
|
||||
{
|
||||
return v < min ? min : v > max ? max : v;
|
||||
}
|
||||
|
||||
static public float LogisticsUnit( float v, float spread = 4.0f, float height = 1.0f )
|
||||
{
|
||||
return LogisticsFull( v, height, spread, 0, 0.1f, -0.5f );
|
||||
}
|
||||
|
||||
static public float LogisticsFull( float v, float height, float spread, float f, float g, float h )
|
||||
{
|
||||
float res = height / (1.0f + (float)Math.Pow( g, (spread * (v+h))) ) + f;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static public float LogisticsSymmetric( float v, float height = 1.0f, float spread = 3.65f )
|
||||
{
|
||||
|
||||
float fullHeight = height * 2.0f;
|
||||
|
||||
float negF = -height;
|
||||
|
||||
float res = LogisticsFull( v, fullHeight, spread, negF, 0.1f, 0.0f );
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//Tracked these down in Desmos
|
||||
static public float s_a = 0.0f;
|
||||
static public float s_b = 0.155f;
|
||||
static public float s_c = 1.03f;
|
||||
static public float s_d = 6.13f;
|
||||
static public float s_f = -10.2f;
|
||||
static public float s_g = 4.06f;
|
||||
|
||||
static public float Quintic( float v )
|
||||
{
|
||||
var vv = v * v;
|
||||
var vvv = vv * v;
|
||||
var vvvv = vvv * v;
|
||||
var vvvvv= vvvv * v;
|
||||
|
||||
var res = s_a + s_b*v + s_c*vv + s_d*vvv + s_f*vvvv + s_g * vvvvv;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static public float s_p = 0.37f;
|
||||
static public float s_o = 0.15f;
|
||||
static public float s_m = 2.11f;
|
||||
static public float s_n = -0.57f;
|
||||
|
||||
static public float PerlinToContinent( float h )
|
||||
{
|
||||
var res = Quintic( s_m * h + s_n ) * s_o + s_p;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static public float SmoothStepCos( float v )
|
||||
{
|
||||
var dV = (double)v;
|
||||
|
||||
var newV = 0.5 - 0.5 * Math.Cos( Math.PI * dV );
|
||||
|
||||
return (float)newV;
|
||||
|
||||
}
|
||||
|
||||
static public float SmoothStepSquare( float v )
|
||||
{
|
||||
var dV = (double)v;
|
||||
|
||||
var newV = dV * dV * (3.0 - 2.0 * dV);
|
||||
|
||||
return (float)newV;
|
||||
|
||||
}
|
||||
|
||||
static public float SmoothStepCube( float v )
|
||||
{
|
||||
var dV = (double)v;
|
||||
|
||||
var newV = dV * dV * dV * ( 6.0 * dV * dV - 15.0 * dV + 10.0 );
|
||||
|
||||
return (float)newV;
|
||||
|
||||
}
|
||||
}
|
||||
{
|
||||
|
||||
static public class fn
|
||||
{
|
||||
|
||||
static public float ToDeg( float rad ) => ( float )(180.0 / Math.PI) * rad;
|
||||
static public float ToRad( float deg ) => ( float )(Math.PI / 180.0) * deg;
|
||||
|
||||
static public float Clamp( float v, float min, float max )
|
||||
{
|
||||
return v < min ? min : v > max ? max : v;
|
||||
}
|
||||
|
||||
static public float LogisticsUnit( float v, float spread = 4.0f, float height = 1.0f )
|
||||
{
|
||||
return LogisticsFull( v, height, spread, 0, 0.1f, -0.5f );
|
||||
}
|
||||
|
||||
static public float LogisticsFull( float v, float height, float spread, float f, float g, float h )
|
||||
{
|
||||
float res = height / (1.0f + (float)Math.Pow( g, (spread * (v+h))) ) + f;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static public float LogisticsSymmetric( float v, float height = 1.0f, float spread = 3.65f )
|
||||
{
|
||||
|
||||
float fullHeight = height * 2.0f;
|
||||
|
||||
float negF = -height;
|
||||
|
||||
float res = LogisticsFull( v, fullHeight, spread, negF, 0.1f, 0.0f );
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//Tracked these down in Desmos
|
||||
static public float s_a = 0.0f;
|
||||
static public float s_b = 0.155f;
|
||||
static public float s_c = 1.03f;
|
||||
static public float s_d = 6.13f;
|
||||
static public float s_f = -10.2f;
|
||||
static public float s_g = 4.06f;
|
||||
|
||||
static public float Quintic( float v )
|
||||
{
|
||||
var vv = v * v;
|
||||
var vvv = vv * v;
|
||||
var vvvv = vvv * v;
|
||||
var vvvvv= vvvv * v;
|
||||
|
||||
var res = s_a + s_b*v + s_c*vv + s_d*vvv + s_f*vvvv + s_g * vvvvv;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static public float s_p = 0.37f;
|
||||
static public float s_o = 0.15f;
|
||||
static public float s_m = 2.11f;
|
||||
static public float s_n = -0.57f;
|
||||
|
||||
static public float PerlinToContinent( float h )
|
||||
{
|
||||
var res = Quintic( s_m * h + s_n ) * s_o + s_p;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
static public float SmoothStepCos( float v )
|
||||
{
|
||||
var dV = (double)v;
|
||||
|
||||
var newV = 0.5 - 0.5 * Math.Cos( Math.PI * dV );
|
||||
|
||||
return (float)newV;
|
||||
|
||||
}
|
||||
|
||||
static public float SmoothStepSquare( float v )
|
||||
{
|
||||
var dV = (double)v;
|
||||
|
||||
var newV = dV * dV * (3.0 - 2.0 * dV);
|
||||
|
||||
return (float)newV;
|
||||
|
||||
}
|
||||
|
||||
static public float SmoothStepCube( float v )
|
||||
{
|
||||
var dV = (double)v;
|
||||
|
||||
var newV = dV * dV * dV * ( 6.0 * dV * dV - 15.0 * dV + 10.0 );
|
||||
|
||||
return (float)newV;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
//using System.Threading.Tasks;
|
||||
using System.Text;
|
||||
//using System.Threading.Tasks;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
390
net/Conn.cs
390
net/Conn.cs
@ -1,195 +1,195 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Net.Sockets;
|
||||
using System.IO;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
//using Util;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
|
||||
public interface IFormatter
|
||||
{
|
||||
//
|
||||
// Summary:
|
||||
// Gets or sets the System.Runtime.Serialization.SerializationBinder that performs
|
||||
// type lookups during deserialization.
|
||||
//
|
||||
// Returns:
|
||||
// The System.Runtime.Serialization.SerializationBinder that performs type lookups
|
||||
// during deserialization.
|
||||
SerializationBinder? Binder { get; set; }
|
||||
//
|
||||
// Summary:
|
||||
// Gets or sets the System.Runtime.Serialization.StreamingContext used for serialization
|
||||
// and deserialization.
|
||||
//
|
||||
// Returns:
|
||||
// The System.Runtime.Serialization.StreamingContext used for serialization and
|
||||
// deserialization.
|
||||
StreamingContext Context { get; set; }
|
||||
//
|
||||
// Summary:
|
||||
// Gets or sets the System.Runtime.Serialization.SurrogateSelector used by the current
|
||||
// formatter.
|
||||
//
|
||||
// Returns:
|
||||
// The System.Runtime.Serialization.SurrogateSelector used by this formatter.
|
||||
ISurrogateSelector? SurrogateSelector { get; set; }
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Deserializes the data on the provided stream and reconstitutes the graph of objects.
|
||||
//
|
||||
//
|
||||
// Parameters:
|
||||
// serializationStream:
|
||||
// The stream that contains the data to deserialize.
|
||||
//
|
||||
// Returns:
|
||||
// The top object of the deserialized graph.
|
||||
[RequiresDynamicCode("BinaryFormatter serialization uses dynamic code generation, the type of objects being processed cannot be statically discovered.")]
|
||||
[RequiresUnreferencedCode("BinaryFormatter serialization is not trim compatible because the type of objects being processed cannot be statically discovered.")]
|
||||
object Deserialize(Stream serializationStream);
|
||||
//
|
||||
// Summary:
|
||||
// Serializes an object, or graph of objects with the given root to the provided
|
||||
// stream.
|
||||
//
|
||||
// Parameters:
|
||||
// serializationStream:
|
||||
// The stream where the formatter puts the serialized data. This stream can reference
|
||||
// a variety of backing stores (such as files, network, memory, and so on).
|
||||
//
|
||||
// graph:
|
||||
// The object, or root of the object graph, to serialize. All child objects of this
|
||||
// root object are automatically serialized.
|
||||
[RequiresUnreferencedCode("BinaryFormatter serialization is not trim compatible because the type of objects being processed cannot be statically discovered.")]
|
||||
void Serialize(Stream serializationStream, object graph);
|
||||
}
|
||||
|
||||
|
||||
public interface IProcess
|
||||
{
|
||||
void process( object obj );
|
||||
}
|
||||
|
||||
|
||||
public interface ISerDes<T> where T : IFormatter
|
||||
{
|
||||
|
||||
T getInstance();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class NewEveryCall<T>: ISerDes<T> where T : IFormatter, new()
|
||||
{
|
||||
public T getInstance()
|
||||
{
|
||||
return new T();
|
||||
}
|
||||
}
|
||||
|
||||
public class Conn
|
||||
{
|
||||
public static int BufferSize = 2048;
|
||||
}
|
||||
|
||||
|
||||
public class Conn<T, TInst> : Conn
|
||||
where T : IFormatter, new()
|
||||
where TInst : ISerDes<T>, new()
|
||||
{
|
||||
public Socket Sock { get { return m_socket; } }
|
||||
public Stream Stream { get { return m_streamNet; } }
|
||||
|
||||
|
||||
private TInst m_formatter = new TInst();
|
||||
|
||||
|
||||
public Conn( Socket sock, IProcess proc )
|
||||
{
|
||||
m_socket = sock;
|
||||
|
||||
sock.NoDelay = true;
|
||||
|
||||
m_streamNet = new NetworkStream( m_socket );
|
||||
|
||||
m_proc = proc;
|
||||
}
|
||||
|
||||
public object recieveObject()
|
||||
{
|
||||
return recieveObject( Stream );
|
||||
}
|
||||
|
||||
public object recieveObject( Stream stream )
|
||||
{
|
||||
object obj = null;
|
||||
|
||||
var formatter = m_formatter.getInstance();
|
||||
|
||||
try
|
||||
{
|
||||
obj = formatter.Deserialize( stream );
|
||||
}
|
||||
catch( System.Xml.XmlException ex )
|
||||
{
|
||||
log.error( $"Outer Exception {ex.Message}" );
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public void send( object obj )
|
||||
{
|
||||
|
||||
var formatter = m_formatter.getInstance();
|
||||
|
||||
try
|
||||
{
|
||||
var ms = new MemoryStream( BufferSize );
|
||||
formatter.Serialize( ms, obj );
|
||||
|
||||
//var str = System.Text.Encoding.Default.GetString( mm_buffer, 0, (int)ms.Position );
|
||||
//log.info( $"Sent data {str} of length {ms.Position}" );
|
||||
//log.info( $"Sent {obj}" );
|
||||
|
||||
byte[] byteSize = BitConverter.GetBytes( (uint)ms.Position );
|
||||
m_streamNet.Write( byteSize, 0, 4 );
|
||||
m_streamNet.Write( ms.GetBuffer(), 0, (int)ms.Position );
|
||||
|
||||
m_streamNet.Flush();
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
log.warn( $"Exception sending obj {obj} of {e}" );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void recieve( object obj )
|
||||
{
|
||||
if( m_proc != null )
|
||||
m_proc.process( obj );
|
||||
}
|
||||
|
||||
Socket m_socket;
|
||||
|
||||
NetworkStream m_streamNet;
|
||||
|
||||
IProcess m_proc;
|
||||
|
||||
|
||||
|
||||
//private BufferedStream m_streamBufIn;
|
||||
//private BufferedStream m_streamBufOut;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Net.Sockets;
|
||||
using System.IO;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
//using Util;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
|
||||
public interface IFormatter
|
||||
{
|
||||
//
|
||||
// Summary:
|
||||
// Gets or sets the System.Runtime.Serialization.SerializationBinder that performs
|
||||
// type lookups during deserialization.
|
||||
//
|
||||
// Returns:
|
||||
// The System.Runtime.Serialization.SerializationBinder that performs type lookups
|
||||
// during deserialization.
|
||||
SerializationBinder? Binder { get; set; }
|
||||
//
|
||||
// Summary:
|
||||
// Gets or sets the System.Runtime.Serialization.StreamingContext used for serialization
|
||||
// and deserialization.
|
||||
//
|
||||
// Returns:
|
||||
// The System.Runtime.Serialization.StreamingContext used for serialization and
|
||||
// deserialization.
|
||||
StreamingContext Context { get; set; }
|
||||
//
|
||||
// Summary:
|
||||
// Gets or sets the System.Runtime.Serialization.SurrogateSelector used by the current
|
||||
// formatter.
|
||||
//
|
||||
// Returns:
|
||||
// The System.Runtime.Serialization.SurrogateSelector used by this formatter.
|
||||
ISurrogateSelector? SurrogateSelector { get; set; }
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Deserializes the data on the provided stream and reconstitutes the graph of objects.
|
||||
//
|
||||
//
|
||||
// Parameters:
|
||||
// serializationStream:
|
||||
// The stream that contains the data to deserialize.
|
||||
//
|
||||
// Returns:
|
||||
// The top object of the deserialized graph.
|
||||
[RequiresDynamicCode("BinaryFormatter serialization uses dynamic code generation, the type of objects being processed cannot be statically discovered.")]
|
||||
[RequiresUnreferencedCode("BinaryFormatter serialization is not trim compatible because the type of objects being processed cannot be statically discovered.")]
|
||||
object Deserialize(Stream serializationStream);
|
||||
//
|
||||
// Summary:
|
||||
// Serializes an object, or graph of objects with the given root to the provided
|
||||
// stream.
|
||||
//
|
||||
// Parameters:
|
||||
// serializationStream:
|
||||
// The stream where the formatter puts the serialized data. This stream can reference
|
||||
// a variety of backing stores (such as files, network, memory, and so on).
|
||||
//
|
||||
// graph:
|
||||
// The object, or root of the object graph, to serialize. All child objects of this
|
||||
// root object are automatically serialized.
|
||||
[RequiresUnreferencedCode("BinaryFormatter serialization is not trim compatible because the type of objects being processed cannot be statically discovered.")]
|
||||
void Serialize(Stream serializationStream, object graph);
|
||||
}
|
||||
|
||||
|
||||
public interface IProcess
|
||||
{
|
||||
void process( object obj );
|
||||
}
|
||||
|
||||
|
||||
public interface ISerDes<T> where T : IFormatter
|
||||
{
|
||||
|
||||
T getInstance();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class NewEveryCall<T>: ISerDes<T> where T : IFormatter, new()
|
||||
{
|
||||
public T getInstance()
|
||||
{
|
||||
return new T();
|
||||
}
|
||||
}
|
||||
|
||||
public class Conn
|
||||
{
|
||||
public static int BufferSize = 2048;
|
||||
}
|
||||
|
||||
|
||||
public class Conn<T, TInst> : Conn
|
||||
where T : IFormatter, new()
|
||||
where TInst : ISerDes<T>, new()
|
||||
{
|
||||
public Socket Sock { get { return m_socket; } }
|
||||
public Stream Stream { get { return m_streamNet; } }
|
||||
|
||||
|
||||
private TInst m_formatter = new TInst();
|
||||
|
||||
|
||||
public Conn( Socket sock, IProcess proc )
|
||||
{
|
||||
m_socket = sock;
|
||||
|
||||
sock.NoDelay = true;
|
||||
|
||||
m_streamNet = new NetworkStream( m_socket );
|
||||
|
||||
m_proc = proc;
|
||||
}
|
||||
|
||||
public object recieveObject()
|
||||
{
|
||||
return recieveObject( Stream );
|
||||
}
|
||||
|
||||
public object recieveObject( Stream stream )
|
||||
{
|
||||
object obj = null;
|
||||
|
||||
var formatter = m_formatter.getInstance();
|
||||
|
||||
try
|
||||
{
|
||||
obj = formatter.Deserialize( stream );
|
||||
}
|
||||
catch( System.Xml.XmlException ex )
|
||||
{
|
||||
log.error( $"Outer Exception {ex.Message}" );
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public void send( object obj )
|
||||
{
|
||||
|
||||
var formatter = m_formatter.getInstance();
|
||||
|
||||
try
|
||||
{
|
||||
var ms = new MemoryStream( BufferSize );
|
||||
formatter.Serialize( ms, obj );
|
||||
|
||||
//var str = System.Text.Encoding.Default.GetString( mm_buffer, 0, (int)ms.Position );
|
||||
//log.info( $"Sent data {str} of length {ms.Position}" );
|
||||
//log.info( $"Sent {obj}" );
|
||||
|
||||
byte[] byteSize = BitConverter.GetBytes( (uint)ms.Position );
|
||||
m_streamNet.Write( byteSize, 0, 4 );
|
||||
m_streamNet.Write( ms.GetBuffer(), 0, (int)ms.Position );
|
||||
|
||||
m_streamNet.Flush();
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
log.warn( $"Exception sending obj {obj} of {e}" );
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void recieve( object obj )
|
||||
{
|
||||
if( m_proc != null )
|
||||
m_proc.process( obj );
|
||||
}
|
||||
|
||||
Socket m_socket;
|
||||
|
||||
NetworkStream m_streamNet;
|
||||
|
||||
IProcess m_proc;
|
||||
|
||||
|
||||
|
||||
//private BufferedStream m_streamBufIn;
|
||||
//private BufferedStream m_streamBufOut;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
206
net/NetMsg.cs
206
net/NetMsg.cs
@ -1,103 +1,103 @@
|
||||
using System;
|
||||
|
||||
namespace lib.Net
|
||||
{
|
||||
[Serializable]
|
||||
public class Msg
|
||||
{
|
||||
public Msg()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Login
|
||||
{
|
||||
public Login( String name, String pass )
|
||||
{
|
||||
m_username = name;
|
||||
m_password = pass;
|
||||
}
|
||||
|
||||
public readonly String m_username;
|
||||
public readonly String m_password;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LoginResp
|
||||
{
|
||||
public LoginResp( bool resp )
|
||||
{
|
||||
m_resp = resp;
|
||||
}
|
||||
|
||||
public readonly bool m_resp;
|
||||
}
|
||||
|
||||
#region Admin Messages
|
||||
//Subclasses of this need to be on an admin client.
|
||||
[Serializable]
|
||||
public class Admin
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
[Serializable]
|
||||
public class CreateEntity: Admin
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class MoveEntity: Admin
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
[Serializable]
|
||||
public class EntityBase
|
||||
{
|
||||
public EntityBase( int id )
|
||||
{
|
||||
m_id = id;
|
||||
}
|
||||
|
||||
public readonly int m_id;
|
||||
};
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class EntityPos: EntityBase
|
||||
{
|
||||
public EntityPos( int id, float x, float y, float z ) :
|
||||
base( id )
|
||||
{
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
m_z = z;
|
||||
}
|
||||
|
||||
public readonly float m_x;
|
||||
public readonly float m_y;
|
||||
public readonly float m_z;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class EntityDesc: EntityBase
|
||||
{
|
||||
public EntityDesc( int id ) :
|
||||
base( id )
|
||||
{
|
||||
}
|
||||
|
||||
//Should an entity have a mesh? Be made up of multiple meshes?
|
||||
public readonly String m_mesh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace lib.Net
|
||||
{
|
||||
[Serializable]
|
||||
public class Msg
|
||||
{
|
||||
public Msg()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class Login
|
||||
{
|
||||
public Login( String name, String pass )
|
||||
{
|
||||
m_username = name;
|
||||
m_password = pass;
|
||||
}
|
||||
|
||||
public readonly String m_username;
|
||||
public readonly String m_password;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LoginResp
|
||||
{
|
||||
public LoginResp( bool resp )
|
||||
{
|
||||
m_resp = resp;
|
||||
}
|
||||
|
||||
public readonly bool m_resp;
|
||||
}
|
||||
|
||||
#region Admin Messages
|
||||
//Subclasses of this need to be on an admin client.
|
||||
[Serializable]
|
||||
public class Admin
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
[Serializable]
|
||||
public class CreateEntity: Admin
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class MoveEntity: Admin
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
[Serializable]
|
||||
public class EntityBase
|
||||
{
|
||||
public EntityBase( int id )
|
||||
{
|
||||
m_id = id;
|
||||
}
|
||||
|
||||
public readonly int m_id;
|
||||
};
|
||||
|
||||
|
||||
[Serializable]
|
||||
public class EntityPos: EntityBase
|
||||
{
|
||||
public EntityPos( int id, float x, float y, float z ) :
|
||||
base( id )
|
||||
{
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
m_z = z;
|
||||
}
|
||||
|
||||
public readonly float m_x;
|
||||
public readonly float m_y;
|
||||
public readonly float m_z;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class EntityDesc: EntityBase
|
||||
{
|
||||
public EntityDesc( int id ) :
|
||||
base( id )
|
||||
{
|
||||
}
|
||||
|
||||
//Should an entity have a mesh? Be made up of multiple meshes?
|
||||
public readonly String m_mesh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
698
scr/Script.cs
698
scr/Script.cs
@ -1,349 +1,349 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Emit;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
public class MemoryRefResolver : SourceReferenceResolver
|
||||
{
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override string NormalizePath(string path, string baseFilePath)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
public override Stream OpenRead(string resolvedPath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override SourceText ReadText(string resolvedPath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ResolveReference(string path, string baseFilePath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static class scr
|
||||
{
|
||||
|
||||
public static FieldInfo? GetFieldInfo(Type? t, string name)
|
||||
{
|
||||
if (t == null) return null;
|
||||
|
||||
var fi = t.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
|
||||
if (fi != null)
|
||||
return fi;
|
||||
|
||||
if (t.BaseType != null)
|
||||
return GetFieldInfo(t.BaseType, name);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// From stack overflow
|
||||
static Lazy<ISet<Type>> typeSetLazy =
|
||||
new Lazy<ISet<Type>>(() => {
|
||||
var types = AppDomain
|
||||
.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes()
|
||||
.Where(t => t.IsClass));
|
||||
var typesAndBaseTypes = types
|
||||
.Select(t => new { Type = t, t.BaseType })
|
||||
.ToList();
|
||||
var typesWithSubclasses = typesAndBaseTypes
|
||||
.Join(
|
||||
typesAndBaseTypes,
|
||||
t => t.Type,
|
||||
t => t.BaseType,
|
||||
(t1, t2) => t2.BaseType);
|
||||
var typesHs = new HashSet<Type>(types);
|
||||
typesHs.ExceptWith(typesWithSubclasses);
|
||||
return typesHs;
|
||||
});
|
||||
|
||||
static bool IsLeafType(this Type type)
|
||||
{
|
||||
return typeSetLazy.Value.Contains(type);
|
||||
}
|
||||
|
||||
static HashSet<char> s_badChars = new( new char[] { '<', '>', ' ', ',', '.', '+', '[', ']', '$', ':' } );
|
||||
|
||||
static public string TypeToIdentifier(string typename)
|
||||
{
|
||||
var safeStr = new StringBuilder( typename );
|
||||
|
||||
for( int i = 0; i < safeStr.Length; ++i )
|
||||
{
|
||||
if( s_badChars.Contains(safeStr[i]) ) safeStr[i] = '_';
|
||||
}
|
||||
|
||||
return safeStr.ToString();
|
||||
}
|
||||
|
||||
static public FileSystemWatcher s_watcher;
|
||||
static public Action<Assembly> s_fnAss = (ass) => {
|
||||
log.warn( $"Need to replace s_fnAss with custom function" );
|
||||
};
|
||||
|
||||
public static void WatchPluginDir( string dir, Action<Assembly> fnAss )
|
||||
{
|
||||
log.info( $"Watching {dir} for changes" );
|
||||
|
||||
s_fnAss = fnAss;
|
||||
|
||||
s_watcher = new FileSystemWatcher( dir );
|
||||
|
||||
s_watcher.Created += OnCreated;
|
||||
s_watcher.Deleted += OnDeleted;
|
||||
s_watcher.Renamed += OnRenamed;
|
||||
|
||||
s_watcher.Filter = "*.cs";
|
||||
s_watcher.IncludeSubdirectories = true;
|
||||
s_watcher.EnableRaisingEvents = true;
|
||||
|
||||
var existingFiles = Directory.GetFiles( dir, "*.cs", SearchOption.AllDirectories );
|
||||
|
||||
foreach( var filename in existingFiles )
|
||||
{
|
||||
Process( filename );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void OnCreated( object sender, FileSystemEventArgs e )
|
||||
{
|
||||
log.debug( $"{e.Name} got {e.ChangeType}" );
|
||||
|
||||
if( e.Name.EndsWith( ".cs" ) )
|
||||
{
|
||||
Process( e.FullPath );
|
||||
}
|
||||
}
|
||||
|
||||
static void OnDeleted( object sender, FileSystemEventArgs e )
|
||||
{
|
||||
log.debug( $"{e.Name} got {e.ChangeType}" );
|
||||
}
|
||||
|
||||
static void OnRenamed( object sender, FileSystemEventArgs e )
|
||||
{
|
||||
log.debug( $"{e.Name} got {e.ChangeType}" );
|
||||
|
||||
if( e.Name.EndsWith(".cs") )
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
try
|
||||
{
|
||||
Process( e.FullPath );
|
||||
return;
|
||||
}
|
||||
catch( System.IO.IOException ex )
|
||||
{
|
||||
|
||||
}
|
||||
catch( Exception ex )
|
||||
{
|
||||
log.error( $"Got ex {ex.GetType().Name} trying to process {e.FullPath}" );
|
||||
log.error( $"-> {ex.Message}" );
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.Sleep( 100 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Process( string filename )
|
||||
{
|
||||
CompileFile( filename, ( ass ) => { s_fnAss( ass ); }, ( diags ) => { } );
|
||||
}
|
||||
|
||||
public static void CompileFile( string filename, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure, Platform platform = Platform.X86 )
|
||||
{
|
||||
var fullpath = Path.GetFullPath( filename );
|
||||
|
||||
var stream = File.OpenRead( fullpath );
|
||||
|
||||
var sourceText = SourceText.From( stream );
|
||||
|
||||
Compile( sourceText, fullpath, onSuccess, onFailure );
|
||||
}
|
||||
|
||||
public static void Compile( string str, string uniquePath, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure, Platform platform = Platform.X86 )
|
||||
{
|
||||
var sourceText = SourceText.From( str );
|
||||
|
||||
Compile( sourceText, uniquePath, onSuccess, onFailure );
|
||||
}
|
||||
|
||||
|
||||
public static void Compile( SourceText sourceText, string uniquePath, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure, Platform platform = Platform.X86 )
|
||||
{
|
||||
string assemblyName = Path.GetRandomFileName();
|
||||
|
||||
var options = new CSharpParseOptions(documentationMode: DocumentationMode.Diagnose, kind: SourceCodeKind.Regular);
|
||||
|
||||
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText( sourceText, options, uniquePath );
|
||||
|
||||
var memRef = new MemoryRefResolver();
|
||||
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
using (var pdb = new MemoryStream())
|
||||
{
|
||||
var result = CompileAndEmit(assemblyName, new[] { syntaxTree }, ms, pdb, platform);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
if (onFailure == null)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
LogDiags( uniquePath, result.Diagnostics.Length, result.Diagnostics );
|
||||
|
||||
onFailure( result.Diagnostics );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
var assembly = Assembly.Load(ms.ToArray(), pdb.ToArray());
|
||||
|
||||
onSuccess( assembly );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogDiags( string uniquePath, int count, IEnumerable<Diagnostic> diags )
|
||||
{
|
||||
log.warn( $"{count} Problems building script with name {uniquePath}" );
|
||||
foreach( var diag in diags )
|
||||
{
|
||||
log.debug( $"{diag}" );
|
||||
}
|
||||
}
|
||||
|
||||
private static EmitResult CompileAndEmit(string assemblyName, SyntaxTree[] syntaxTrees, MemoryStream ms, MemoryStream pdb, Platform platform)
|
||||
{
|
||||
var memRef = new MemoryRefResolver();
|
||||
|
||||
// @@@@ TODO :: Config release / debug
|
||||
CSharpCompilation compilation = CSharpCompilation.Create(
|
||||
assemblyName,
|
||||
syntaxTrees: syntaxTrees,
|
||||
references: RefCache.References,
|
||||
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
|
||||
sourceReferenceResolver: memRef,
|
||||
optimizationLevel: OptimizationLevel.Release,
|
||||
platform: platform,
|
||||
specificDiagnosticOptions: new Dictionary<string, ReportDiagnostic>
|
||||
{
|
||||
{ "CS1701", ReportDiagnostic.Suppress }
|
||||
}));
|
||||
|
||||
return compilation.Emit(ms, pdb);
|
||||
}
|
||||
|
||||
private static class RefCache
|
||||
{
|
||||
// create the list of references on first use, but if two threads both *start* making the list thats fine since we'll just use whichever wins.
|
||||
private static readonly Lazy<ImmutableArray<MetadataReference>> lazyRef = new Lazy<ImmutableArray<MetadataReference>>(GetReferences, LazyThreadSafetyMode.PublicationOnly);
|
||||
|
||||
public static IReadOnlyList<MetadataReference> References => lazyRef.Value;
|
||||
|
||||
private static ImmutableArray<MetadataReference> GetReferences()
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<MetadataReference>();
|
||||
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach( var ass in assemblies )
|
||||
{
|
||||
if( ass != null && !ass.IsDynamic && ass.Location != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.Add( MetadataReference.CreateFromFile( ass.Location ) );
|
||||
}
|
||||
catch( Exception ex )
|
||||
{
|
||||
log.warn( $"Got {ex.GetType().Name} sayaing {ex.Message}" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
public static string PrettyName( Type t )
|
||||
{
|
||||
if( t.GetGenericArguments().Length == 0 )
|
||||
{
|
||||
return t.FullName.Replace( '+', '.' );
|
||||
}
|
||||
var genArgs = t.GetGenericArguments();
|
||||
var typeDef = t.FullName;
|
||||
|
||||
var indexOfTick = typeDef.IndexOf("`");
|
||||
|
||||
var unmangledOuterName = typeDef.Substring(0, typeDef.IndexOf('`')).Replace('+', '.');
|
||||
|
||||
var innerName = "";
|
||||
|
||||
//Check for inner class
|
||||
if( typeDef.ElementAt( indexOfTick + 2 ) != '[' )
|
||||
{
|
||||
var indexOfOpenBracket = typeDef.IndexOf('[', indexOfTick);
|
||||
|
||||
innerName = typeDef.Substring( indexOfTick + 2, indexOfOpenBracket - (indexOfTick + 2) ).Replace( '+', '.' );
|
||||
}
|
||||
|
||||
return unmangledOuterName + "<" + String.Join( ",", genArgs.Select( PrettyName ) ) + ">" + innerName;
|
||||
}
|
||||
|
||||
|
||||
private static void AddIfFirst<TKey, TValue>(IDictionary<TKey, TValue> dict, TKey key, TValue value)
|
||||
{
|
||||
if (!dict.ContainsKey(key))
|
||||
{
|
||||
dict.Add(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Emit;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
public class MemoryRefResolver : SourceReferenceResolver
|
||||
{
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override string NormalizePath(string path, string baseFilePath)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
public override Stream OpenRead(string resolvedPath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override SourceText ReadText(string resolvedPath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ResolveReference(string path, string baseFilePath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static class scr
|
||||
{
|
||||
|
||||
public static FieldInfo? GetFieldInfo(Type? t, string name)
|
||||
{
|
||||
if (t == null) return null;
|
||||
|
||||
var fi = t.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
|
||||
if (fi != null)
|
||||
return fi;
|
||||
|
||||
if (t.BaseType != null)
|
||||
return GetFieldInfo(t.BaseType, name);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// From stack overflow
|
||||
static Lazy<ISet<Type>> typeSetLazy =
|
||||
new Lazy<ISet<Type>>(() => {
|
||||
var types = AppDomain
|
||||
.CurrentDomain
|
||||
.GetAssemblies()
|
||||
.SelectMany(a => a.GetTypes()
|
||||
.Where(t => t.IsClass));
|
||||
var typesAndBaseTypes = types
|
||||
.Select(t => new { Type = t, t.BaseType })
|
||||
.ToList();
|
||||
var typesWithSubclasses = typesAndBaseTypes
|
||||
.Join(
|
||||
typesAndBaseTypes,
|
||||
t => t.Type,
|
||||
t => t.BaseType,
|
||||
(t1, t2) => t2.BaseType);
|
||||
var typesHs = new HashSet<Type>(types);
|
||||
typesHs.ExceptWith(typesWithSubclasses);
|
||||
return typesHs;
|
||||
});
|
||||
|
||||
static bool IsLeafType(this Type type)
|
||||
{
|
||||
return typeSetLazy.Value.Contains(type);
|
||||
}
|
||||
|
||||
static HashSet<char> s_badChars = new( new char[] { '<', '>', ' ', ',', '.', '+', '[', ']', '$', ':' } );
|
||||
|
||||
static public string TypeToIdentifier(string typename)
|
||||
{
|
||||
var safeStr = new StringBuilder( typename );
|
||||
|
||||
for( int i = 0; i < safeStr.Length; ++i )
|
||||
{
|
||||
if( s_badChars.Contains(safeStr[i]) ) safeStr[i] = '_';
|
||||
}
|
||||
|
||||
return safeStr.ToString();
|
||||
}
|
||||
|
||||
static public FileSystemWatcher s_watcher;
|
||||
static public Action<Assembly> s_fnAss = (ass) => {
|
||||
log.warn( $"Need to replace s_fnAss with custom function" );
|
||||
};
|
||||
|
||||
public static void WatchPluginDir( string dir, Action<Assembly> fnAss )
|
||||
{
|
||||
log.info( $"Watching {dir} for changes" );
|
||||
|
||||
s_fnAss = fnAss;
|
||||
|
||||
s_watcher = new FileSystemWatcher( dir );
|
||||
|
||||
s_watcher.Created += OnCreated;
|
||||
s_watcher.Deleted += OnDeleted;
|
||||
s_watcher.Renamed += OnRenamed;
|
||||
|
||||
s_watcher.Filter = "*.cs";
|
||||
s_watcher.IncludeSubdirectories = true;
|
||||
s_watcher.EnableRaisingEvents = true;
|
||||
|
||||
var existingFiles = Directory.GetFiles( dir, "*.cs", SearchOption.AllDirectories );
|
||||
|
||||
foreach( var filename in existingFiles )
|
||||
{
|
||||
Process( filename );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void OnCreated( object sender, FileSystemEventArgs e )
|
||||
{
|
||||
log.debug( $"{e.Name} got {e.ChangeType}" );
|
||||
|
||||
if( e.Name.EndsWith( ".cs" ) )
|
||||
{
|
||||
Process( e.FullPath );
|
||||
}
|
||||
}
|
||||
|
||||
static void OnDeleted( object sender, FileSystemEventArgs e )
|
||||
{
|
||||
log.debug( $"{e.Name} got {e.ChangeType}" );
|
||||
}
|
||||
|
||||
static void OnRenamed( object sender, FileSystemEventArgs e )
|
||||
{
|
||||
log.debug( $"{e.Name} got {e.ChangeType}" );
|
||||
|
||||
if( e.Name.EndsWith(".cs") )
|
||||
{
|
||||
while( true )
|
||||
{
|
||||
try
|
||||
{
|
||||
Process( e.FullPath );
|
||||
return;
|
||||
}
|
||||
catch( System.IO.IOException ex )
|
||||
{
|
||||
|
||||
}
|
||||
catch( Exception ex )
|
||||
{
|
||||
log.error( $"Got ex {ex.GetType().Name} trying to process {e.FullPath}" );
|
||||
log.error( $"-> {ex.Message}" );
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.Sleep( 100 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Process( string filename )
|
||||
{
|
||||
CompileFile( filename, ( ass ) => { s_fnAss( ass ); }, ( diags ) => { } );
|
||||
}
|
||||
|
||||
public static void CompileFile( string filename, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure, Platform platform = Platform.X86 )
|
||||
{
|
||||
var fullpath = Path.GetFullPath( filename );
|
||||
|
||||
var stream = File.OpenRead( fullpath );
|
||||
|
||||
var sourceText = SourceText.From( stream );
|
||||
|
||||
Compile( sourceText, fullpath, onSuccess, onFailure );
|
||||
}
|
||||
|
||||
public static void Compile( string str, string uniquePath, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure, Platform platform = Platform.X86 )
|
||||
{
|
||||
var sourceText = SourceText.From( str );
|
||||
|
||||
Compile( sourceText, uniquePath, onSuccess, onFailure );
|
||||
}
|
||||
|
||||
|
||||
public static void Compile( SourceText sourceText, string uniquePath, Action<Assembly> onSuccess, Action<ImmutableArray<Diagnostic>> onFailure, Platform platform = Platform.X86 )
|
||||
{
|
||||
string assemblyName = Path.GetRandomFileName();
|
||||
|
||||
var options = new CSharpParseOptions(documentationMode: DocumentationMode.Diagnose, kind: SourceCodeKind.Regular);
|
||||
|
||||
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText( sourceText, options, uniquePath );
|
||||
|
||||
var memRef = new MemoryRefResolver();
|
||||
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
using (var pdb = new MemoryStream())
|
||||
{
|
||||
var result = CompileAndEmit(assemblyName, new[] { syntaxTree }, ms, pdb, platform);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
if (onFailure == null)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
LogDiags( uniquePath, result.Diagnostics.Length, result.Diagnostics );
|
||||
|
||||
onFailure( result.Diagnostics );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
var assembly = Assembly.Load(ms.ToArray(), pdb.ToArray());
|
||||
|
||||
onSuccess( assembly );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogDiags( string uniquePath, int count, IEnumerable<Diagnostic> diags )
|
||||
{
|
||||
log.warn( $"{count} Problems building script with name {uniquePath}" );
|
||||
foreach( var diag in diags )
|
||||
{
|
||||
log.debug( $"{diag}" );
|
||||
}
|
||||
}
|
||||
|
||||
private static EmitResult CompileAndEmit(string assemblyName, SyntaxTree[] syntaxTrees, MemoryStream ms, MemoryStream pdb, Platform platform)
|
||||
{
|
||||
var memRef = new MemoryRefResolver();
|
||||
|
||||
// @@@@ TODO :: Config release / debug
|
||||
CSharpCompilation compilation = CSharpCompilation.Create(
|
||||
assemblyName,
|
||||
syntaxTrees: syntaxTrees,
|
||||
references: RefCache.References,
|
||||
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
|
||||
sourceReferenceResolver: memRef,
|
||||
optimizationLevel: OptimizationLevel.Release,
|
||||
platform: platform,
|
||||
specificDiagnosticOptions: new Dictionary<string, ReportDiagnostic>
|
||||
{
|
||||
{ "CS1701", ReportDiagnostic.Suppress }
|
||||
}));
|
||||
|
||||
return compilation.Emit(ms, pdb);
|
||||
}
|
||||
|
||||
private static class RefCache
|
||||
{
|
||||
// create the list of references on first use, but if two threads both *start* making the list thats fine since we'll just use whichever wins.
|
||||
private static readonly Lazy<ImmutableArray<MetadataReference>> lazyRef = new Lazy<ImmutableArray<MetadataReference>>(GetReferences, LazyThreadSafetyMode.PublicationOnly);
|
||||
|
||||
public static IReadOnlyList<MetadataReference> References => lazyRef.Value;
|
||||
|
||||
private static ImmutableArray<MetadataReference> GetReferences()
|
||||
{
|
||||
var builder = ImmutableArray.CreateBuilder<MetadataReference>();
|
||||
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
|
||||
foreach( var ass in assemblies )
|
||||
{
|
||||
if( ass != null && !ass.IsDynamic && ass.Location != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
builder.Add( MetadataReference.CreateFromFile( ass.Location ) );
|
||||
}
|
||||
catch( Exception ex )
|
||||
{
|
||||
log.warn( $"Got {ex.GetType().Name} sayaing {ex.Message}" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return builder.ToImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
public static string PrettyName( Type t )
|
||||
{
|
||||
if( t.GetGenericArguments().Length == 0 )
|
||||
{
|
||||
return t.FullName.Replace( '+', '.' );
|
||||
}
|
||||
var genArgs = t.GetGenericArguments();
|
||||
var typeDef = t.FullName;
|
||||
|
||||
var indexOfTick = typeDef.IndexOf("`");
|
||||
|
||||
var unmangledOuterName = typeDef.Substring(0, typeDef.IndexOf('`')).Replace('+', '.');
|
||||
|
||||
var innerName = "";
|
||||
|
||||
//Check for inner class
|
||||
if( typeDef.ElementAt( indexOfTick + 2 ) != '[' )
|
||||
{
|
||||
var indexOfOpenBracket = typeDef.IndexOf('[', indexOfTick);
|
||||
|
||||
innerName = typeDef.Substring( indexOfTick + 2, indexOfOpenBracket - (indexOfTick + 2) ).Replace( '+', '.' );
|
||||
}
|
||||
|
||||
return unmangledOuterName + "<" + String.Join( ",", genArgs.Select( PrettyName ) ) + ">" + innerName;
|
||||
}
|
||||
|
||||
|
||||
private static void AddIfFirst<TKey, TValue>(IDictionary<TKey, TValue> dict, TKey key, TValue value)
|
||||
{
|
||||
if (!dict.ContainsKey(key))
|
||||
{
|
||||
dict.Add(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,166 +1,166 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using System.Xml;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.IO;
|
||||
using System.Security.Permissions;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
[Serializable]
|
||||
public class SerializableDictionary<TKey, TVal>: Dictionary<TKey, TVal>, IXmlSerializable, ISerializable
|
||||
{
|
||||
#region Constants
|
||||
private const string DictionaryNodeName = "Dictionary";
|
||||
private const string ItemNodeName = "Item";
|
||||
private const string KeyNodeName = "Key";
|
||||
private const string ValueNodeName = "Value";
|
||||
#endregion
|
||||
#region Constructors
|
||||
public SerializableDictionary()
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( IDictionary<TKey, TVal> dictionary )
|
||||
: base( dictionary )
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( IEqualityComparer<TKey> comparer )
|
||||
: base( comparer )
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( int capacity )
|
||||
: base( capacity )
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( IDictionary<TKey, TVal> dictionary, IEqualityComparer<TKey> comparer )
|
||||
: base( dictionary, comparer )
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( int capacity, IEqualityComparer<TKey> comparer )
|
||||
: base( capacity, comparer )
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region ISerializable Members
|
||||
|
||||
protected SerializableDictionary( SerializationInfo info, StreamingContext context )
|
||||
{
|
||||
int itemCount = info.GetInt32("ItemCount");
|
||||
for( int i = 0; i < itemCount; i++ )
|
||||
{
|
||||
KeyValuePair<TKey, TVal> kvp = (KeyValuePair<TKey, TVal>)info.GetValue(String.Format( $"Item{i}" ), typeof(KeyValuePair<TKey, TVal>));
|
||||
this.Add( kvp.Key, kvp.Value );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//[SecurityPermission( SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter )]
|
||||
void ISerializable.GetObjectData( SerializationInfo info, StreamingContext context )
|
||||
{
|
||||
info.AddValue( "ItemCount", this.Count );
|
||||
int itemIdx = 0;
|
||||
foreach( KeyValuePair<TKey, TVal> kvp in this )
|
||||
{
|
||||
info.AddValue( String.Format( $"Item{itemIdx}" ), kvp, typeof( KeyValuePair<TKey, TVal> ) );
|
||||
itemIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region IXmlSerializable Members
|
||||
|
||||
void IXmlSerializable.WriteXml( System.Xml.XmlWriter writer )
|
||||
{
|
||||
//writer.WriteStartElement(DictionaryNodeName);
|
||||
foreach( KeyValuePair<TKey, TVal> kvp in this )
|
||||
{
|
||||
writer.WriteStartElement( ItemNodeName );
|
||||
writer.WriteStartElement( KeyNodeName );
|
||||
KeySerializer.Serialize( writer, kvp.Key );
|
||||
writer.WriteEndElement();
|
||||
writer.WriteStartElement( ValueNodeName );
|
||||
ValueSerializer.Serialize( writer, kvp.Value );
|
||||
writer.WriteEndElement();
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
//writer.WriteEndElement();
|
||||
}
|
||||
|
||||
void IXmlSerializable.ReadXml( System.Xml.XmlReader reader )
|
||||
{
|
||||
if( reader.IsEmptyElement )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Move past container
|
||||
if( !reader.Read() )
|
||||
{
|
||||
throw new XmlException( "Error in Deserialization of Dictionary" );
|
||||
}
|
||||
|
||||
//reader.ReadStartElement(DictionaryNodeName);
|
||||
while( reader.NodeType != XmlNodeType.EndElement )
|
||||
{
|
||||
reader.ReadStartElement( ItemNodeName );
|
||||
reader.ReadStartElement( KeyNodeName );
|
||||
TKey key = (TKey)KeySerializer.Deserialize(reader);
|
||||
reader.ReadEndElement();
|
||||
reader.ReadStartElement( ValueNodeName );
|
||||
TVal value = (TVal)ValueSerializer.Deserialize(reader);
|
||||
reader.ReadEndElement();
|
||||
reader.ReadEndElement();
|
||||
this.Add( key, value );
|
||||
reader.MoveToContent();
|
||||
}
|
||||
//reader.ReadEndElement();
|
||||
|
||||
reader.ReadEndElement(); // Read End Element to close Read of containing node
|
||||
}
|
||||
|
||||
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Private Properties
|
||||
protected XmlSerializer ValueSerializer
|
||||
{
|
||||
get
|
||||
{
|
||||
if( valueSerializer == null )
|
||||
{
|
||||
valueSerializer = new XmlSerializer( typeof( TVal ) );
|
||||
}
|
||||
return valueSerializer;
|
||||
}
|
||||
}
|
||||
|
||||
private XmlSerializer KeySerializer
|
||||
{
|
||||
get
|
||||
{
|
||||
if( keySerializer == null )
|
||||
{
|
||||
keySerializer = new XmlSerializer( typeof( TKey ) );
|
||||
}
|
||||
return keySerializer;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Private Members
|
||||
private XmlSerializer keySerializer = null;
|
||||
private XmlSerializer valueSerializer = null;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using System.Xml;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.IO;
|
||||
using System.Security.Permissions;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
[Serializable]
|
||||
public class SerializableDictionary<TKey, TVal>: Dictionary<TKey, TVal>, IXmlSerializable, ISerializable
|
||||
{
|
||||
#region Constants
|
||||
private const string DictionaryNodeName = "Dictionary";
|
||||
private const string ItemNodeName = "Item";
|
||||
private const string KeyNodeName = "Key";
|
||||
private const string ValueNodeName = "Value";
|
||||
#endregion
|
||||
#region Constructors
|
||||
public SerializableDictionary()
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( IDictionary<TKey, TVal> dictionary )
|
||||
: base( dictionary )
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( IEqualityComparer<TKey> comparer )
|
||||
: base( comparer )
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( int capacity )
|
||||
: base( capacity )
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( IDictionary<TKey, TVal> dictionary, IEqualityComparer<TKey> comparer )
|
||||
: base( dictionary, comparer )
|
||||
{
|
||||
}
|
||||
|
||||
public SerializableDictionary( int capacity, IEqualityComparer<TKey> comparer )
|
||||
: base( capacity, comparer )
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region ISerializable Members
|
||||
|
||||
protected SerializableDictionary( SerializationInfo info, StreamingContext context )
|
||||
{
|
||||
int itemCount = info.GetInt32("ItemCount");
|
||||
for( int i = 0; i < itemCount; i++ )
|
||||
{
|
||||
KeyValuePair<TKey, TVal> kvp = (KeyValuePair<TKey, TVal>)info.GetValue(String.Format( $"Item{i}" ), typeof(KeyValuePair<TKey, TVal>));
|
||||
this.Add( kvp.Key, kvp.Value );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//[SecurityPermission( SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter )]
|
||||
void ISerializable.GetObjectData( SerializationInfo info, StreamingContext context )
|
||||
{
|
||||
info.AddValue( "ItemCount", this.Count );
|
||||
int itemIdx = 0;
|
||||
foreach( KeyValuePair<TKey, TVal> kvp in this )
|
||||
{
|
||||
info.AddValue( String.Format( $"Item{itemIdx}" ), kvp, typeof( KeyValuePair<TKey, TVal> ) );
|
||||
itemIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region IXmlSerializable Members
|
||||
|
||||
void IXmlSerializable.WriteXml( System.Xml.XmlWriter writer )
|
||||
{
|
||||
//writer.WriteStartElement(DictionaryNodeName);
|
||||
foreach( KeyValuePair<TKey, TVal> kvp in this )
|
||||
{
|
||||
writer.WriteStartElement( ItemNodeName );
|
||||
writer.WriteStartElement( KeyNodeName );
|
||||
KeySerializer.Serialize( writer, kvp.Key );
|
||||
writer.WriteEndElement();
|
||||
writer.WriteStartElement( ValueNodeName );
|
||||
ValueSerializer.Serialize( writer, kvp.Value );
|
||||
writer.WriteEndElement();
|
||||
writer.WriteEndElement();
|
||||
}
|
||||
//writer.WriteEndElement();
|
||||
}
|
||||
|
||||
void IXmlSerializable.ReadXml( System.Xml.XmlReader reader )
|
||||
{
|
||||
if( reader.IsEmptyElement )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Move past container
|
||||
if( !reader.Read() )
|
||||
{
|
||||
throw new XmlException( "Error in Deserialization of Dictionary" );
|
||||
}
|
||||
|
||||
//reader.ReadStartElement(DictionaryNodeName);
|
||||
while( reader.NodeType != XmlNodeType.EndElement )
|
||||
{
|
||||
reader.ReadStartElement( ItemNodeName );
|
||||
reader.ReadStartElement( KeyNodeName );
|
||||
TKey key = (TKey)KeySerializer.Deserialize(reader);
|
||||
reader.ReadEndElement();
|
||||
reader.ReadStartElement( ValueNodeName );
|
||||
TVal value = (TVal)ValueSerializer.Deserialize(reader);
|
||||
reader.ReadEndElement();
|
||||
reader.ReadEndElement();
|
||||
this.Add( key, value );
|
||||
reader.MoveToContent();
|
||||
}
|
||||
//reader.ReadEndElement();
|
||||
|
||||
reader.ReadEndElement(); // Read End Element to close Read of containing node
|
||||
}
|
||||
|
||||
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region Private Properties
|
||||
protected XmlSerializer ValueSerializer
|
||||
{
|
||||
get
|
||||
{
|
||||
if( valueSerializer == null )
|
||||
{
|
||||
valueSerializer = new XmlSerializer( typeof( TVal ) );
|
||||
}
|
||||
return valueSerializer;
|
||||
}
|
||||
}
|
||||
|
||||
private XmlSerializer KeySerializer
|
||||
{
|
||||
get
|
||||
{
|
||||
if( keySerializer == null )
|
||||
{
|
||||
keySerializer = new XmlSerializer( typeof( TKey ) );
|
||||
}
|
||||
return keySerializer;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Private Members
|
||||
private XmlSerializer keySerializer = null;
|
||||
private XmlSerializer valueSerializer = null;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
2188
ser/XmlFormatter.cs
2188
ser/XmlFormatter.cs
File diff suppressed because it is too large
Load Diff
104
time/Clock.cs
104
time/Clock.cs
@ -1,52 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
public class Clock
|
||||
{
|
||||
public Clock( long timeOffset )
|
||||
{
|
||||
m_timer = new Timer();
|
||||
|
||||
m_lastTime = m_timer.Current;
|
||||
|
||||
m_totalMillis = timeOffset;
|
||||
m_totalSeconds = (double)m_totalMillis / 1000.0;
|
||||
}
|
||||
|
||||
public void tick()
|
||||
{
|
||||
long current = m_timer.Current;
|
||||
|
||||
m_dtMillis = (int)( current - m_lastTime );
|
||||
|
||||
m_dtSeconds = (double)m_dtMillis / 1000.0;
|
||||
|
||||
m_totalMillis += m_dtMillis;
|
||||
m_totalSeconds = (double)m_totalMillis / 1000.0;
|
||||
|
||||
m_lastTime = current;
|
||||
}
|
||||
|
||||
public int dtMs { get { return m_dtMillis; } }
|
||||
public double dtSec { get { return m_dtSeconds; } }
|
||||
|
||||
public long ms { get { return m_totalMillis; } }
|
||||
public double sec { get { return m_totalSeconds; } }
|
||||
|
||||
|
||||
Timer m_timer;
|
||||
|
||||
long m_lastTime = 0;
|
||||
|
||||
int m_dtMillis = 0;
|
||||
double m_dtSeconds = 0;
|
||||
|
||||
long m_totalMillis = 0;
|
||||
double m_totalSeconds = 0;
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
public class Clock
|
||||
{
|
||||
public Clock( long timeOffset )
|
||||
{
|
||||
m_timer = new Timer();
|
||||
|
||||
m_lastTime = m_timer.Current;
|
||||
|
||||
m_totalMillis = timeOffset;
|
||||
m_totalSeconds = (double)m_totalMillis / 1000.0;
|
||||
}
|
||||
|
||||
public void tick()
|
||||
{
|
||||
long current = m_timer.Current;
|
||||
|
||||
m_dtMillis = (int)( current - m_lastTime );
|
||||
|
||||
m_dtSeconds = (double)m_dtMillis / 1000.0;
|
||||
|
||||
m_totalMillis += m_dtMillis;
|
||||
m_totalSeconds = (double)m_totalMillis / 1000.0;
|
||||
|
||||
m_lastTime = current;
|
||||
}
|
||||
|
||||
public int dtMs { get { return m_dtMillis; } }
|
||||
public double dtSec { get { return m_dtSeconds; } }
|
||||
|
||||
public long ms { get { return m_totalMillis; } }
|
||||
public double sec { get { return m_totalSeconds; } }
|
||||
|
||||
|
||||
Timer m_timer;
|
||||
|
||||
long m_lastTime = 0;
|
||||
|
||||
int m_dtMillis = 0;
|
||||
double m_dtSeconds = 0;
|
||||
|
||||
long m_totalMillis = 0;
|
||||
double m_totalSeconds = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
754
time/Timer.cs
754
time/Timer.cs
@ -1,377 +1,377 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
|
||||
public class MicroStopwatch: System.Diagnostics.Stopwatch
|
||||
{
|
||||
readonly double _microSecPerTick
|
||||
= 1000000D / System.Diagnostics.Stopwatch.Frequency;
|
||||
|
||||
public MicroStopwatch()
|
||||
{
|
||||
if( !System.Diagnostics.Stopwatch.IsHighResolution )
|
||||
{
|
||||
throw new Exception( "On this system the high-resolution " +
|
||||
"performance counter is not available" );
|
||||
}
|
||||
}
|
||||
|
||||
public long ElapsedMicroseconds
|
||||
{
|
||||
get
|
||||
{
|
||||
return (long)( ElapsedTicks * _microSecPerTick );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MicroTimer class
|
||||
/// </summary>
|
||||
public class MicroTimer
|
||||
{
|
||||
public delegate void MicroTimerElapsedEventHandler(
|
||||
object sender,
|
||||
MicroTimerEventArgs timerEventArgs );
|
||||
public event MicroTimerElapsedEventHandler MicroTimerElapsed;
|
||||
|
||||
System.Threading.Thread _threadTimer = null;
|
||||
long _ignoreEventIfLateBy = long.MaxValue;
|
||||
long _timerIntervalInMicroSec = 0;
|
||||
bool _stopTimer = true;
|
||||
|
||||
public MicroTimer()
|
||||
{
|
||||
}
|
||||
|
||||
public MicroTimer( long timerIntervalInMicroseconds )
|
||||
{
|
||||
Interval = timerIntervalInMicroseconds;
|
||||
}
|
||||
|
||||
public long Interval
|
||||
{
|
||||
get
|
||||
{
|
||||
return System.Threading.Interlocked.Read(
|
||||
ref _timerIntervalInMicroSec );
|
||||
}
|
||||
set
|
||||
{
|
||||
System.Threading.Interlocked.Exchange(
|
||||
ref _timerIntervalInMicroSec, value );
|
||||
}
|
||||
}
|
||||
|
||||
public long IgnoreEventIfLateBy
|
||||
{
|
||||
get
|
||||
{
|
||||
return System.Threading.Interlocked.Read(
|
||||
ref _ignoreEventIfLateBy );
|
||||
}
|
||||
set
|
||||
{
|
||||
System.Threading.Interlocked.Exchange(
|
||||
ref _ignoreEventIfLateBy, value <= 0 ? long.MaxValue : value );
|
||||
}
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
set
|
||||
{
|
||||
if( value )
|
||||
{
|
||||
Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
get
|
||||
{
|
||||
return ( _threadTimer != null && _threadTimer.IsAlive );
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if( Enabled || Interval <= 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_stopTimer = false;
|
||||
|
||||
System.Threading.ThreadStart threadStart = delegate()
|
||||
{
|
||||
NotificationTimer(ref _timerIntervalInMicroSec,
|
||||
ref _ignoreEventIfLateBy,
|
||||
ref _stopTimer);
|
||||
};
|
||||
|
||||
_threadTimer = new System.Threading.Thread( threadStart );
|
||||
_threadTimer.Priority = System.Threading.ThreadPriority.Highest;
|
||||
_threadTimer.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_stopTimer = true;
|
||||
|
||||
if( _threadTimer != null && _threadTimer.ManagedThreadId ==
|
||||
System.Threading.Thread.CurrentThread.ManagedThreadId )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while( Enabled )
|
||||
{
|
||||
System.Threading.Thread.SpinWait( 10 );
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationTimer( ref long timerIntervalInMicroSec,
|
||||
ref long ignoreEventIfLateBy,
|
||||
ref bool stopTimer )
|
||||
{
|
||||
int timerCount = 0;
|
||||
long nextNotification = 0;
|
||||
|
||||
MicroStopwatch microStopwatch = new MicroStopwatch();
|
||||
microStopwatch.Start();
|
||||
|
||||
while( !stopTimer )
|
||||
{
|
||||
long callbackFunctionExecutionTime =
|
||||
microStopwatch.ElapsedMicroseconds - nextNotification;
|
||||
|
||||
long timerIntervalInMicroSecCurrent =
|
||||
System.Threading.Interlocked.Read(ref timerIntervalInMicroSec);
|
||||
long ignoreEventIfLateByCurrent =
|
||||
System.Threading.Interlocked.Read(ref ignoreEventIfLateBy);
|
||||
|
||||
nextNotification += timerIntervalInMicroSecCurrent;
|
||||
timerCount++;
|
||||
long elapsedMicroseconds = 0;
|
||||
|
||||
while( ( elapsedMicroseconds = microStopwatch.ElapsedMicroseconds )
|
||||
< nextNotification )
|
||||
{
|
||||
System.Threading.Thread.SpinWait( 10 );
|
||||
}
|
||||
|
||||
long timerLateBy = elapsedMicroseconds - nextNotification;
|
||||
|
||||
if( timerLateBy >= ignoreEventIfLateByCurrent )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MicroTimerEventArgs microTimerEventArgs =
|
||||
new MicroTimerEventArgs(timerCount,
|
||||
elapsedMicroseconds,
|
||||
timerLateBy,
|
||||
callbackFunctionExecutionTime);
|
||||
MicroTimerElapsed( this, microTimerEventArgs );
|
||||
}
|
||||
|
||||
microStopwatch.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MicroTimer Event Argument class
|
||||
/// </summary>
|
||||
public class MicroTimerEventArgs: EventArgs
|
||||
{
|
||||
// Simple counter, number times timed event (callback function) executed
|
||||
public int TimerCount { get; private set; }
|
||||
|
||||
// Time when timed event was called since timer started
|
||||
public long ElapsedMicroseconds { get; private set; }
|
||||
|
||||
// How late the timer was compared to when it should have been called
|
||||
public long TimerLateBy { get; private set; }
|
||||
|
||||
// Time it took to execute previous call to callback function (OnTimedEvent)
|
||||
public long CallbackFunctionExecutionTime { get; private set; }
|
||||
|
||||
public MicroTimerEventArgs( int timerCount,
|
||||
long elapsedMicroseconds,
|
||||
long timerLateBy,
|
||||
long callbackFunctionExecutionTime )
|
||||
{
|
||||
TimerCount = timerCount;
|
||||
ElapsedMicroseconds = elapsedMicroseconds;
|
||||
TimerLateBy = timerLateBy;
|
||||
CallbackFunctionExecutionTime = callbackFunctionExecutionTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Timer
|
||||
{
|
||||
MicroStopwatch m_watch;
|
||||
private long startTime;
|
||||
private long stopTime;
|
||||
private long freq;
|
||||
private long freq_millis;
|
||||
|
||||
public Timer()
|
||||
{
|
||||
m_watch = new MicroStopwatch();
|
||||
//startTime = m_watch.ElapsedMicroseconds;
|
||||
//stopTime = m_watch.ElapsedMicroseconds;
|
||||
freq = 1000 * 1000;
|
||||
freq_millis = freq / 1000;
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
// Start the timer
|
||||
|
||||
public Timer Start()
|
||||
{
|
||||
m_watch.Start();
|
||||
startTime = m_watch.ElapsedMicroseconds;
|
||||
stopTime = m_watch.ElapsedMicroseconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Stop the timer
|
||||
|
||||
public Timer Stop()
|
||||
{
|
||||
m_watch.Stop();
|
||||
stopTime = m_watch.ElapsedMicroseconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double Seconds
|
||||
{
|
||||
get
|
||||
{
|
||||
long current = m_watch.ElapsedMicroseconds;
|
||||
return (double)( current - startTime ) / freq;
|
||||
}
|
||||
}
|
||||
|
||||
public long Current
|
||||
{
|
||||
get
|
||||
{
|
||||
long current = m_watch.ElapsedMicroseconds;
|
||||
return ( current - startTime ) / freq_millis;
|
||||
}
|
||||
}
|
||||
|
||||
public double Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
return (double)( stopTime - startTime ) / (double)freq;
|
||||
}
|
||||
}
|
||||
|
||||
public long DurationMS
|
||||
{
|
||||
get { return ( stopTime - startTime ) / freq_millis; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TimerWin
|
||||
{
|
||||
[DllImport( "Kernel32.dll" )]
|
||||
private static extern bool QueryPerformanceCounter(
|
||||
out long lpPerformanceCount );
|
||||
|
||||
[DllImport( "Kernel32.dll" )]
|
||||
private static extern bool QueryPerformanceFrequency(
|
||||
out long lpFrequency );
|
||||
|
||||
private long startTime;
|
||||
private long stopTime;
|
||||
private long freq;
|
||||
private long freq_millis;
|
||||
|
||||
// Constructor
|
||||
|
||||
public TimerWin()
|
||||
{
|
||||
startTime = 0;
|
||||
stopTime = 0;
|
||||
|
||||
if( QueryPerformanceFrequency( out freq ) == false )
|
||||
{
|
||||
// high-performance counter not supported
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
freq_millis = freq / 1000;
|
||||
|
||||
}
|
||||
|
||||
// Start the timer
|
||||
|
||||
public void Start()
|
||||
{
|
||||
// lets do the waiting threads there work
|
||||
|
||||
//Thread.Sleep(0);
|
||||
|
||||
QueryPerformanceCounter( out startTime );
|
||||
}
|
||||
|
||||
// Stop the timer
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
QueryPerformanceCounter( out stopTime );
|
||||
}
|
||||
|
||||
public double Seconds
|
||||
{
|
||||
get
|
||||
{
|
||||
long current;
|
||||
|
||||
QueryPerformanceCounter( out current );
|
||||
|
||||
return (double)( current - startTime ) / freq;
|
||||
}
|
||||
}
|
||||
|
||||
public long Current
|
||||
{
|
||||
get
|
||||
{
|
||||
long current;
|
||||
|
||||
QueryPerformanceCounter( out current );
|
||||
|
||||
return ( current - startTime ) / freq_millis;
|
||||
}
|
||||
}
|
||||
|
||||
public double Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
return (double)( stopTime - startTime ) / (double)freq;
|
||||
}
|
||||
}
|
||||
|
||||
public long DurationMS
|
||||
{
|
||||
get { return ( stopTime - startTime ) / freq_millis; }
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
|
||||
namespace lib
|
||||
{
|
||||
|
||||
public class MicroStopwatch: System.Diagnostics.Stopwatch
|
||||
{
|
||||
readonly double _microSecPerTick
|
||||
= 1000000D / System.Diagnostics.Stopwatch.Frequency;
|
||||
|
||||
public MicroStopwatch()
|
||||
{
|
||||
if( !System.Diagnostics.Stopwatch.IsHighResolution )
|
||||
{
|
||||
throw new Exception( "On this system the high-resolution " +
|
||||
"performance counter is not available" );
|
||||
}
|
||||
}
|
||||
|
||||
public long ElapsedMicroseconds
|
||||
{
|
||||
get
|
||||
{
|
||||
return (long)( ElapsedTicks * _microSecPerTick );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MicroTimer class
|
||||
/// </summary>
|
||||
public class MicroTimer
|
||||
{
|
||||
public delegate void MicroTimerElapsedEventHandler(
|
||||
object sender,
|
||||
MicroTimerEventArgs timerEventArgs );
|
||||
public event MicroTimerElapsedEventHandler MicroTimerElapsed;
|
||||
|
||||
System.Threading.Thread _threadTimer = null;
|
||||
long _ignoreEventIfLateBy = long.MaxValue;
|
||||
long _timerIntervalInMicroSec = 0;
|
||||
bool _stopTimer = true;
|
||||
|
||||
public MicroTimer()
|
||||
{
|
||||
}
|
||||
|
||||
public MicroTimer( long timerIntervalInMicroseconds )
|
||||
{
|
||||
Interval = timerIntervalInMicroseconds;
|
||||
}
|
||||
|
||||
public long Interval
|
||||
{
|
||||
get
|
||||
{
|
||||
return System.Threading.Interlocked.Read(
|
||||
ref _timerIntervalInMicroSec );
|
||||
}
|
||||
set
|
||||
{
|
||||
System.Threading.Interlocked.Exchange(
|
||||
ref _timerIntervalInMicroSec, value );
|
||||
}
|
||||
}
|
||||
|
||||
public long IgnoreEventIfLateBy
|
||||
{
|
||||
get
|
||||
{
|
||||
return System.Threading.Interlocked.Read(
|
||||
ref _ignoreEventIfLateBy );
|
||||
}
|
||||
set
|
||||
{
|
||||
System.Threading.Interlocked.Exchange(
|
||||
ref _ignoreEventIfLateBy, value <= 0 ? long.MaxValue : value );
|
||||
}
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
set
|
||||
{
|
||||
if( value )
|
||||
{
|
||||
Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
get
|
||||
{
|
||||
return ( _threadTimer != null && _threadTimer.IsAlive );
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if( Enabled || Interval <= 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_stopTimer = false;
|
||||
|
||||
System.Threading.ThreadStart threadStart = delegate()
|
||||
{
|
||||
NotificationTimer(ref _timerIntervalInMicroSec,
|
||||
ref _ignoreEventIfLateBy,
|
||||
ref _stopTimer);
|
||||
};
|
||||
|
||||
_threadTimer = new System.Threading.Thread( threadStart );
|
||||
_threadTimer.Priority = System.Threading.ThreadPriority.Highest;
|
||||
_threadTimer.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_stopTimer = true;
|
||||
|
||||
if( _threadTimer != null && _threadTimer.ManagedThreadId ==
|
||||
System.Threading.Thread.CurrentThread.ManagedThreadId )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while( Enabled )
|
||||
{
|
||||
System.Threading.Thread.SpinWait( 10 );
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationTimer( ref long timerIntervalInMicroSec,
|
||||
ref long ignoreEventIfLateBy,
|
||||
ref bool stopTimer )
|
||||
{
|
||||
int timerCount = 0;
|
||||
long nextNotification = 0;
|
||||
|
||||
MicroStopwatch microStopwatch = new MicroStopwatch();
|
||||
microStopwatch.Start();
|
||||
|
||||
while( !stopTimer )
|
||||
{
|
||||
long callbackFunctionExecutionTime =
|
||||
microStopwatch.ElapsedMicroseconds - nextNotification;
|
||||
|
||||
long timerIntervalInMicroSecCurrent =
|
||||
System.Threading.Interlocked.Read(ref timerIntervalInMicroSec);
|
||||
long ignoreEventIfLateByCurrent =
|
||||
System.Threading.Interlocked.Read(ref ignoreEventIfLateBy);
|
||||
|
||||
nextNotification += timerIntervalInMicroSecCurrent;
|
||||
timerCount++;
|
||||
long elapsedMicroseconds = 0;
|
||||
|
||||
while( ( elapsedMicroseconds = microStopwatch.ElapsedMicroseconds )
|
||||
< nextNotification )
|
||||
{
|
||||
System.Threading.Thread.SpinWait( 10 );
|
||||
}
|
||||
|
||||
long timerLateBy = elapsedMicroseconds - nextNotification;
|
||||
|
||||
if( timerLateBy >= ignoreEventIfLateByCurrent )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MicroTimerEventArgs microTimerEventArgs =
|
||||
new MicroTimerEventArgs(timerCount,
|
||||
elapsedMicroseconds,
|
||||
timerLateBy,
|
||||
callbackFunctionExecutionTime);
|
||||
MicroTimerElapsed( this, microTimerEventArgs );
|
||||
}
|
||||
|
||||
microStopwatch.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MicroTimer Event Argument class
|
||||
/// </summary>
|
||||
public class MicroTimerEventArgs: EventArgs
|
||||
{
|
||||
// Simple counter, number times timed event (callback function) executed
|
||||
public int TimerCount { get; private set; }
|
||||
|
||||
// Time when timed event was called since timer started
|
||||
public long ElapsedMicroseconds { get; private set; }
|
||||
|
||||
// How late the timer was compared to when it should have been called
|
||||
public long TimerLateBy { get; private set; }
|
||||
|
||||
// Time it took to execute previous call to callback function (OnTimedEvent)
|
||||
public long CallbackFunctionExecutionTime { get; private set; }
|
||||
|
||||
public MicroTimerEventArgs( int timerCount,
|
||||
long elapsedMicroseconds,
|
||||
long timerLateBy,
|
||||
long callbackFunctionExecutionTime )
|
||||
{
|
||||
TimerCount = timerCount;
|
||||
ElapsedMicroseconds = elapsedMicroseconds;
|
||||
TimerLateBy = timerLateBy;
|
||||
CallbackFunctionExecutionTime = callbackFunctionExecutionTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Timer
|
||||
{
|
||||
MicroStopwatch m_watch;
|
||||
private long startTime;
|
||||
private long stopTime;
|
||||
private long freq;
|
||||
private long freq_millis;
|
||||
|
||||
public Timer()
|
||||
{
|
||||
m_watch = new MicroStopwatch();
|
||||
//startTime = m_watch.ElapsedMicroseconds;
|
||||
//stopTime = m_watch.ElapsedMicroseconds;
|
||||
freq = 1000 * 1000;
|
||||
freq_millis = freq / 1000;
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
// Start the timer
|
||||
|
||||
public Timer Start()
|
||||
{
|
||||
m_watch.Start();
|
||||
startTime = m_watch.ElapsedMicroseconds;
|
||||
stopTime = m_watch.ElapsedMicroseconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Stop the timer
|
||||
|
||||
public Timer Stop()
|
||||
{
|
||||
m_watch.Stop();
|
||||
stopTime = m_watch.ElapsedMicroseconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double Seconds
|
||||
{
|
||||
get
|
||||
{
|
||||
long current = m_watch.ElapsedMicroseconds;
|
||||
return (double)( current - startTime ) / freq;
|
||||
}
|
||||
}
|
||||
|
||||
public long Current
|
||||
{
|
||||
get
|
||||
{
|
||||
long current = m_watch.ElapsedMicroseconds;
|
||||
return ( current - startTime ) / freq_millis;
|
||||
}
|
||||
}
|
||||
|
||||
public double Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
return (double)( stopTime - startTime ) / (double)freq;
|
||||
}
|
||||
}
|
||||
|
||||
public long DurationMS
|
||||
{
|
||||
get { return ( stopTime - startTime ) / freq_millis; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class TimerWin
|
||||
{
|
||||
[DllImport( "Kernel32.dll" )]
|
||||
private static extern bool QueryPerformanceCounter(
|
||||
out long lpPerformanceCount );
|
||||
|
||||
[DllImport( "Kernel32.dll" )]
|
||||
private static extern bool QueryPerformanceFrequency(
|
||||
out long lpFrequency );
|
||||
|
||||
private long startTime;
|
||||
private long stopTime;
|
||||
private long freq;
|
||||
private long freq_millis;
|
||||
|
||||
// Constructor
|
||||
|
||||
public TimerWin()
|
||||
{
|
||||
startTime = 0;
|
||||
stopTime = 0;
|
||||
|
||||
if( QueryPerformanceFrequency( out freq ) == false )
|
||||
{
|
||||
// high-performance counter not supported
|
||||
throw new Win32Exception();
|
||||
}
|
||||
|
||||
freq_millis = freq / 1000;
|
||||
|
||||
}
|
||||
|
||||
// Start the timer
|
||||
|
||||
public void Start()
|
||||
{
|
||||
// lets do the waiting threads there work
|
||||
|
||||
//Thread.Sleep(0);
|
||||
|
||||
QueryPerformanceCounter( out startTime );
|
||||
}
|
||||
|
||||
// Stop the timer
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
QueryPerformanceCounter( out stopTime );
|
||||
}
|
||||
|
||||
public double Seconds
|
||||
{
|
||||
get
|
||||
{
|
||||
long current;
|
||||
|
||||
QueryPerformanceCounter( out current );
|
||||
|
||||
return (double)( current - startTime ) / freq;
|
||||
}
|
||||
}
|
||||
|
||||
public long Current
|
||||
{
|
||||
get
|
||||
{
|
||||
long current;
|
||||
|
||||
QueryPerformanceCounter( out current );
|
||||
|
||||
return ( current - startTime ) / freq_millis;
|
||||
}
|
||||
}
|
||||
|
||||
public double Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
return (double)( stopTime - startTime ) / (double)freq;
|
||||
}
|
||||
}
|
||||
|
||||
public long DurationMS
|
||||
{
|
||||
get { return ( stopTime - startTime ) / freq_millis; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user