From 1e3204601ee9af266ddf668da5fd91dd4de09b51 Mon Sep 17 00:00:00 2001 From: Marc Hernandez Date: Fri, 13 Feb 2026 14:08:48 -0800 Subject: [PATCH] Strip out experimental stuff --- ar/AdaptiveArithmeticCompress.cs | 73 -- ar/AdaptiveArithmeticDecompress.cs | 67 -- ar/ArithmeticCoderBase.cs | 185 ----- ar/ArithmeticCompress.cs | 127 ---- ar/ArithmeticDecoder.cs | 152 ---- ar/ArithmeticDecompress.cs | 99 --- ar/ArithmeticEncoder.cs | 118 --- ar/Arrays.cs | 41 -- ar/BitInputStream.cs | 120 --- ar/BitOutputStream.cs | 95 --- ar/CheckedFrequencyTable.cs | 128 ---- ar/FlatFrequencyTable.cs | 145 ---- ar/FrequencyTable.cs | 76 -- ar/PpmCompress.cs | 130 ---- ar/PpmDecompress.cs | 123 ---- ar/PpmModel.cs | 113 --- ar/SimpleFrequencyTable.cs | 260 ------- db/Act.cs | 96 --- db/DB.cs | 228 ------ db/Processor.cs | 128 ---- db/System.cs | 308 -------- exp/Exp.cs | 98 --- fsm/FSM.cs | 64 -- imm/Util.cs | 2 - lib/CodeGen.cs | 182 ----- lib/CodeGenerator.cs | 74 -- lib/SerializableDictionary.cs_bad | 166 ----- lib/VersionFormatter.cs_bad | 677 ----------------- lib/XmlFormatter.cs_bad | 1094 ---------------------------- logging/GC.cs | 516 ------------- logging/Tracing.cs | 675 ----------------- mod/Modules.cs | 87 --- net/Conn.cs | 198 ----- net/FSM.cs | 66 -- net/NetMsg.cs | 103 --- prof/AddressStack.cs | 47 -- prof/Memory.cs | 314 -------- prof/MemoryPipe.cs | 393 ---------- prof/MethodInfo.cs | 68 -- prof/MethodStore.cs | 209 ------ prof/PerProcessProfilingState.cs | 33 - prof/ProcessAllocations.cs | 131 ---- prof/ProcessTypeMapping.cs | 35 - prof/Program.cs | 162 ---- ser/XmlSer.cs | 838 --------------------- ser/XmlSer_Core.cs | 159 ---- ser/XmlSer_Read.cs | 188 ----- ser/XmlSer_Tests.cs | 133 ---- ser/XmlSer_Write.cs | 201 ----- srl/srl.Core.cs | 375 ---------- srl/srl.Debug.cs | 47 -- srl/srl.Xml.cs | 131 ---- task/Task.cs | 16 - tests/Tests.cs | 125 ---- 54 files changed, 10419 deletions(-) delete mode 100644 ar/AdaptiveArithmeticCompress.cs delete mode 100644 ar/AdaptiveArithmeticDecompress.cs delete mode 100644 ar/ArithmeticCoderBase.cs delete mode 100644 ar/ArithmeticCompress.cs delete mode 100644 ar/ArithmeticDecoder.cs delete mode 100644 ar/ArithmeticDecompress.cs delete mode 100644 ar/ArithmeticEncoder.cs delete mode 100644 ar/Arrays.cs delete mode 100644 ar/BitInputStream.cs delete mode 100644 ar/BitOutputStream.cs delete mode 100644 ar/CheckedFrequencyTable.cs delete mode 100644 ar/FlatFrequencyTable.cs delete mode 100644 ar/FrequencyTable.cs delete mode 100644 ar/PpmCompress.cs delete mode 100644 ar/PpmDecompress.cs delete mode 100644 ar/PpmModel.cs delete mode 100644 ar/SimpleFrequencyTable.cs delete mode 100644 db/Act.cs delete mode 100644 db/DB.cs delete mode 100644 db/Processor.cs delete mode 100644 db/System.cs delete mode 100644 exp/Exp.cs delete mode 100644 fsm/FSM.cs delete mode 100644 imm/Util.cs delete mode 100644 lib/CodeGen.cs delete mode 100644 lib/CodeGenerator.cs delete mode 100644 lib/SerializableDictionary.cs_bad delete mode 100644 lib/VersionFormatter.cs_bad delete mode 100644 lib/XmlFormatter.cs_bad delete mode 100644 logging/GC.cs delete mode 100644 logging/Tracing.cs delete mode 100644 mod/Modules.cs delete mode 100644 net/Conn.cs delete mode 100644 net/FSM.cs delete mode 100644 net/NetMsg.cs delete mode 100644 prof/AddressStack.cs delete mode 100644 prof/Memory.cs delete mode 100644 prof/MemoryPipe.cs delete mode 100644 prof/MethodInfo.cs delete mode 100644 prof/MethodStore.cs delete mode 100644 prof/PerProcessProfilingState.cs delete mode 100644 prof/ProcessAllocations.cs delete mode 100644 prof/ProcessTypeMapping.cs delete mode 100644 prof/Program.cs delete mode 100644 ser/XmlSer.cs delete mode 100644 ser/XmlSer_Core.cs delete mode 100644 ser/XmlSer_Read.cs delete mode 100644 ser/XmlSer_Tests.cs delete mode 100644 ser/XmlSer_Write.cs delete mode 100644 srl/srl.Core.cs delete mode 100644 srl/srl.Debug.cs delete mode 100644 srl/srl.Xml.cs delete mode 100644 task/Task.cs delete mode 100644 tests/Tests.cs diff --git a/ar/AdaptiveArithmeticCompress.cs b/ar/AdaptiveArithmeticCompress.cs deleted file mode 100644 index f5f22f7..0000000 --- a/ar/AdaptiveArithmeticCompress.cs +++ /dev/null @@ -1,73 +0,0 @@ -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 - */ - - - -/// -/// Compression application using adaptive arithmetic coding. -/// Usage: java AdaptiveArithmeticCompress InputFile OutputFile -/// Then use the corresponding "AdaptiveArithmeticDecompress" application to recreate the original input file. -/// 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. -/// -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 - } - -} diff --git a/ar/AdaptiveArithmeticDecompress.cs b/ar/AdaptiveArithmeticDecompress.cs deleted file mode 100644 index 6f8837f..0000000 --- a/ar/AdaptiveArithmeticDecompress.cs +++ /dev/null @@ -1,67 +0,0 @@ -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 - */ - - - -/// -/// Decompression application using adaptive arithmetic coding. -/// Usage: java AdaptiveArithmeticDecompress InputFile OutputFile -/// This decompresses files generated by the "AdaptiveArithmeticCompress" application. -/// -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 ); - } - } - -} diff --git a/ar/ArithmeticCoderBase.cs b/ar/ArithmeticCoderBase.cs deleted file mode 100644 index 4c68a68..0000000 --- a/ar/ArithmeticCoderBase.cs +++ /dev/null @@ -1,185 +0,0 @@ -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 - */ - - -/// -/// Provides the state and behaviors that arithmetic coding encoders and decoders share. -/// -/// -public abstract class ArithmeticCoderBase -{ - - /*---- Configuration fields ----*/ - - /// - /// Number of bits for the 'low' and 'high' state variables. Must be in the range [1, 62]. - ///
    - ///
  • 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.
  • - ///
  • 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.
  • - ///
  • Therefore numStateBits=32 is recommended as the most versatile setting - /// because it maximizes maximumTotal (which ends up being slightly over 2^30).
  • - ///
  • 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.
  • - ///
- ///
- protected internal readonly int numStateBits; - - /// - /// Maximum range (high+1-low) during coding (trivial), which is 2^numStateBits = 1000...000. - protected internal readonly long fullRange; - - /// - /// The top bit at width numStateBits, which is 0100...000. - protected internal readonly long halfRange; - - /// - /// The second highest bit at width numStateBits, which is 0010...000. This is zero when numStateBits=1. - protected internal readonly long quarterRange; - - /// - /// Minimum range (high+1-low) during coding (non-trivial), which is 0010...010. - protected internal readonly long minimumRange; - - /// - /// Maximum allowed total from a frequency table at all times during coding. - protected internal readonly long maximumTotal; - - /// - /// Bit mask of numStateBits ones, which is 0111...111. - protected internal readonly long stateMask; - - - - /*---- State fields ----*/ - - /// - /// Low end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 0s. - /// - protected internal long low; - - /// - /// High end of this arithmetic coder's current range. Conceptually has an infinite number of trailing 1s. - /// - protected internal long high; - - - - /*---- Constructor ----*/ - - /// - /// Constructs an arithmetic coder, which initializes the code range. - /// the number of bits for the arithmetic coding range - /// if stateSize is outside the range [1, 62] - 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 ----*/ - - /// - /// Updates the code range (low and high) of this arithmetic coder as a result - /// of processing the specified symbol with the specified frequency table. - /// Invariants that are true before and after encoding/decoding each symbol - /// (letting fullRange = 2numStateBits): - ///
    - ///
  • 0 ≤ low ≤ code ≤ high < fullRange. ('code' exists only in the decoder.) - /// Therefore these variables are unsigned integers of numStateBits bits.
  • - ///
  • low < 1/2 × fullRange ≤ high. - /// In other words, they are in different halves of the full range.
  • - ///
  • (low < 1/4 × fullRange) || (high ≥ 3/4 × fullRange). - /// In other words, they are not both in the middle two quarters.
  • - ///
  • 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.
  • - ///
- /// the frequency table to use - /// the symbol that was processed - /// if the symbol has zero frequency or the frequency table's total is too large - //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; - } - } - - - /// - /// Called to handle the situation when the top bit of {@code low} and {@code high} are equal. - /// if an I/O exception occurred - //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(); - - - /// - /// Called to handle the situation when low=01(...) and high=10(...). - /// if an I/O exception occurred - //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(); - -} diff --git a/ar/ArithmeticCompress.cs b/ar/ArithmeticCompress.cs deleted file mode 100644 index bd73f67..0000000 --- a/ar/ArithmeticCompress.cs +++ /dev/null @@ -1,127 +0,0 @@ -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 - */ - - - -/// -/// Compression application using static arithmetic coding. -/// Usage: java ArithmeticCompress InputFile OutputFile -/// Then use the corresponding "ArithmeticDecompress" application to recreate the original input file. -/// 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. -/// -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 - } - } - -} diff --git a/ar/ArithmeticDecoder.cs b/ar/ArithmeticDecoder.cs deleted file mode 100644 index 55809e6..0000000 --- a/ar/ArithmeticDecoder.cs +++ /dev/null @@ -1,152 +0,0 @@ -/* - * 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; -/// -/// Reads from an arithmetic-coded bit stream and decodes symbols. Not thread-safe. -/// -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 ----*/ - - /// - /// Constructs an arithmetic coding decoder based on the - /// specified bit input stream, and fills the code bits. - /// the number of bits for the arithmetic coding range - /// the bit input stream to read from - /// if the input steam is {@code null} - /// if stateSize is outside the range [1, 62] - /// if an I/O exception occurred - //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 | (long)readCodeBit(); - } - } - - - - /*---- Methods ----*/ - - /// - /// 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. - /// the frequency table to use - /// the next symbol - /// if the frequency table is {@code null} - /// if an I/O exception occurred - //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 ) ); - } - - - /// - /// 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. - /// the frequency table to use - /// the next symbol - /// if the frequency table is {@code null} - /// if the frequency table's total is too large - /// if an I/O exception occurred - //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 ) | (long)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 ) ) ) | (long)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; - } - -} diff --git a/ar/ArithmeticDecompress.cs b/ar/ArithmeticDecompress.cs deleted file mode 100644 index cd04757..0000000 --- a/ar/ArithmeticDecompress.cs +++ /dev/null @@ -1,99 +0,0 @@ -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 - */ - - - -/// -/// Decompression application using static arithmetic coding. -/// Usage: java ArithmeticDecompress InputFile OutputFile -/// This decompresses files generated by the "ArithmeticCompress" application. -/// -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; - } - -} diff --git a/ar/ArithmeticEncoder.cs b/ar/ArithmeticEncoder.cs deleted file mode 100644 index 6449863..0000000 --- a/ar/ArithmeticEncoder.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Reference arithmetic coding - * Copyright (c) Project Nayuki - * - * https://www.nayuki.io/page/reference-arithmetic-coding - * https://github.com/nayuki/Reference-arithmetic-coding - */ - - - -using System; -/// -/// Encodes symbols and writes to an arithmetic-coded bit stream. Not thread-safe. -/// -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 ----*/ - - /// - /// Constructs an arithmetic coding encoder based on the specified bit output stream. - /// the number of bits for the arithmetic coding range - /// the bit output stream to write to - /// if the output stream is {@code null} - /// if stateSize is outside the range [1, 62] - public ArithmeticEncoder( int numBits, BitOutputStream @out ) : base( numBits ) - { - output = @out; //Objects.requireNonNull(@out); - numUnderflow = 0; - } - - - - /*---- Methods ----*/ - - /// - /// Encodes the specified symbol based on the specified frequency table. - /// This updates this arithmetic coder's state and may write out some bits. - /// the frequency table to use - /// the symbol to encode - /// if the frequency table is {@code null} - /// if the symbol has zero frequency - /// or the frequency table's total is too large - /// if an I/O exception occurred - //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 ); - } - - - /// - /// Encodes the specified symbol based on the specified frequency table. - /// Also updates this arithmetic coder's state and may write out some bits. - /// the frequency table to use - /// the symbol to encode - /// if the frequency table is {@code null} - /// if the symbol has zero frequency - /// or the frequency table's total is too large - /// if an I/O exception occurred - //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 ); - } - - - /// - /// 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. - /// Note that this method merely writes data to the underlying output stream but does not close it. - /// if an I/O exception occurred - //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++; - } - -} diff --git a/ar/Arrays.cs b/ar/Arrays.cs deleted file mode 100644 index 2e0d028..0000000 --- a/ar/Arrays.cs +++ /dev/null @@ -1,41 +0,0 @@ -//--------------------------------------------------------------------------------------------------------- -// 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[] original, int newLength ) - { - T[] dest = new T[newLength]; - Array.Copy( original, dest, newLength ); - return dest; - } - - public static T[] CopyOfRange( 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[] array, T value ) - { - for( int i = 0; i < array.Length; i++ ) - { - array[i] = value; - } - } - - public static void Fill( T[] array, int fromIndex, int toIndex, T value ) - { - for( int i = fromIndex; i < toIndex; i++ ) - { - array[i] = value; - } - } -} diff --git a/ar/BitInputStream.cs b/ar/BitInputStream.cs deleted file mode 100644 index 647adf1..0000000 --- a/ar/BitInputStream.cs +++ /dev/null @@ -1,120 +0,0 @@ -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 - */ - - - -/// -/// 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. -/// -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 ----*/ - - /// - /// Constructs a bit input stream based on the specified byte input stream. - /// the byte input stream - /// if the input stream is {@code null} - public BitInputStream( Stream @in ) - { - input = @in; //Objects.requireNonNull(@in); - currentByte = 0; - numBitsRemaining = 0; - } - - - - /*---- Methods ----*/ - - /// - /// 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. - /// the next bit of 0 or 1, or -1 for the end of stream - /// if an I/O exception occurred - //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; - } - - - /// - /// 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. - /// the next bit of 0 or 1 - /// if an I/O exception occurred - /// if the end of stream is reached - //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(); - } - } - - - /// - /// Closes this stream and the underlying input stream. - /// if an I/O exception occurred - //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(); - } -} diff --git a/ar/BitOutputStream.cs b/ar/BitOutputStream.cs deleted file mode 100644 index 3a2f130..0000000 --- a/ar/BitOutputStream.cs +++ /dev/null @@ -1,95 +0,0 @@ -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 - */ - - - -/// -/// 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. -/// -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 ----*/ - - /// - /// Constructs a bit output stream based on the specified byte output stream. - /// the byte output stream - /// if the output stream is {@code null} - public BitOutputStream( Stream @out ) - { - output = @out; //Objects.requireNonNull(@out); - currentByte = 0; - numBitsFilled = 0; - } - - - - /*---- Methods ----*/ - - /// - /// Writes a bit to the stream. The specified bit must be 0 or 1. - /// the bit to write, which must be 0 or 1 - /// if an I/O exception occurred - //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; - } - } - - - /// - /// 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. - /// if an I/O exception occurred - //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(); - } -} diff --git a/ar/CheckedFrequencyTable.cs b/ar/CheckedFrequencyTable.cs deleted file mode 100644 index 0d04c9a..0000000 --- a/ar/CheckedFrequencyTable.cs +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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; -/// -/// 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. -/// -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; - } - -} diff --git a/ar/FlatFrequencyTable.cs b/ar/FlatFrequencyTable.cs deleted file mode 100644 index a2d4bea..0000000 --- a/ar/FlatFrequencyTable.cs +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Reference arithmetic coding - * Copyright (c) Project Nayuki - * - * https://www.nayuki.io/page/reference-arithmetic-coding - * https://github.com/nayuki/Reference-arithmetic-coding - */ - - -/// -/// An immutable frequency table where every symbol has the same frequency of 1. -/// Useful as a fallback model when no statistics are available. -/// -public sealed class FlatFrequencyTable : FrequencyTable -{ - - /*---- Fields ----*/ - - // Total number of symbols, which is at least 1. - private readonly int numSymbols; - - - - /*---- Constructor ----*/ - - /// - /// Constructs a flat frequency table with the specified number of symbols. - /// the number of symbols, which must be at least 1 - /// if the number of symbols is less than 1 - public FlatFrequencyTable( int numSyms ) - { - if( numSyms < 1 ) - { - throw new System.ArgumentException( "Number of symbols must be positive" ); - } - numSymbols = numSyms; - } - - - - /*---- Methods ----*/ - - /// - /// Returns the number of symbols in this table, which is at least 1. - /// the number of symbols in this table - public int SymbolLimit - { - get - { - return numSymbols; - } - } - - - /// - /// Returns the frequency of the specified symbol, which is always 1. - /// the symbol to query - /// the frequency of the symbol, which is 1 - /// if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} - public int get( int symbol ) - { - checkSymbol( symbol ); - return 1; - } - - - /// - /// Returns the total of all symbol frequencies, which is - /// always equal to the number of symbols in this table. - /// the total of all symbol frequencies, which is {@code getSymbolLimit()} - public int Total - { - get - { - return numSymbols; - } - } - - - /// - /// Returns the sum of the frequencies of all the symbols strictly below - /// the specified symbol value. The returned value is equal to {@code symbol}. - /// the symbol to query - /// the sum of the frequencies of all the symbols below {@code symbol}, which is {@code symbol} - /// if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} - public int getLow( int symbol ) - { - checkSymbol( symbol ); - return symbol; - } - - - /// - /// Returns the sum of the frequencies of the specified symbol and all - /// the symbols below. The returned value is equal to {@code symbol + 1}. - /// the symbol to query - /// the sum of the frequencies of {@code symbol} and all symbols below, which is {@code symbol + 1} - /// if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} - 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" ); - } - } - - - /// - /// Returns a string representation of this frequency table. The format is subject to change. - /// a string representation of this frequency table - public override string ToString() - { - return "FlatFrequencyTable=" + numSymbols; - } - - - /// - /// Unsupported operation, because this frequency table is immutable. - /// ignored - /// ignored - /// because this frequency table is immutable - public void set( int symbol, int freq ) - { - throw new System.NotSupportedException(); - } - - - /// - /// Unsupported operation, because this frequency table is immutable. - /// ignored - /// because this frequency table is immutable - public void increment( int symbol ) - { - throw new System.NotSupportedException(); - } - -} diff --git a/ar/FrequencyTable.cs b/ar/FrequencyTable.cs deleted file mode 100644 index 83a52ee..0000000 --- a/ar/FrequencyTable.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Reference arithmetic coding - * Copyright (c) Project Nayuki - * - * https://www.nayuki.io/page/reference-arithmetic-coding - * https://github.com/nayuki/Reference-arithmetic-coding - */ - - -/// -/// 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. -/// 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. -/// -public interface FrequencyTable -{ - - /// - /// Returns the number of symbols in this frequency table, which is a positive number. - /// the number of symbols in this frequency table - int SymbolLimit { get; } - - - /// - /// Returns the frequency of the specified symbol. The returned value is at least 0. - /// the symbol to query - /// the frequency of the symbol - /// if the symbol is out of range - int get( int symbol ); - - - /// - /// Sets the frequency of the specified symbol to the specified value. - /// The frequency value must be at least 0. - /// the symbol to set - /// the frequency value to set - /// if the frequency is negative or the symbol is out of range - /// if an arithmetic overflow occurs - void set( int symbol, int freq ); - - - /// - /// Increments the frequency of the specified symbol. - /// the symbol whose frequency to increment - /// if the symbol is out of range - /// if an arithmetic overflow occurs - void increment( int symbol ); - - - /// - /// Returns the total of all symbol frequencies. The returned value is at - /// least 0 and is always equal to {@code getHigh(getSymbolLimit() - 1)}. - /// the total of all symbol frequencies - int Total { get; } - - - /// - /// Returns the sum of the frequencies of all the symbols strictly - /// below the specified symbol value. The returned value is at least 0. - /// the symbol to query - /// the sum of the frequencies of all the symbols below {@code symbol} - /// if the symbol is out of range - int getLow( int symbol ); - - - /// - /// Returns the sum of the frequencies of the specified symbol - /// and all the symbols below. The returned value is at least 0. - /// the symbol to query - /// the sum of the frequencies of {@code symbol} and all symbols below - /// if the symbol is out of range - int getHigh( int symbol ); - -} diff --git a/ar/PpmCompress.cs b/ar/PpmCompress.cs deleted file mode 100644 index a6d81e8..0000000 --- a/ar/PpmCompress.cs +++ /dev/null @@ -1,130 +0,0 @@ -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 - */ - - - -/// -/// Compression application using prediction by partial matching (PPM) with arithmetic coding. -/// Usage: java PpmCompress InputFile OutputFile -/// Then use the corresponding "PpmDecompress" application to recreate the original input file. -/// 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. -/// -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 ); - } - -} diff --git a/ar/PpmDecompress.cs b/ar/PpmDecompress.cs deleted file mode 100644 index cf80ce8..0000000 --- a/ar/PpmDecompress.cs +++ /dev/null @@ -1,123 +0,0 @@ -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 - */ - - - -/// -/// Decompression application using prediction by partial matching (PPM) with arithmetic coding. -/// Usage: java PpmDecompress InputFile OutputFile -/// This decompresses files generated by the "PpmCompress" application. -/// -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 ); - } - -} diff --git a/ar/PpmModel.cs b/ar/PpmModel.cs deleted file mode 100644 index a1e0286..0000000 --- a/ar/PpmModel.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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; - } - } - - } - -} diff --git a/ar/SimpleFrequencyTable.cs b/ar/SimpleFrequencyTable.cs deleted file mode 100644 index 43702a8..0000000 --- a/ar/SimpleFrequencyTable.cs +++ /dev/null @@ -1,260 +0,0 @@ -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 - */ - - -/// -/// 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. -/// -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 ----*/ - - /// - /// 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}. - /// the array of symbol frequencies - /// if the array is {@code null} - /// if {@code freqs.length} < 1, - /// {@code freqs.length} = {@code Integer.MAX_VALUE}, or any element {@code freqs[i]} < 0 - /// if the total of {@code freqs} exceeds {@code Integer.MAX_VALUE} - 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; - } - - - /// - /// Constructs a frequency table by copying the specified frequency table. - /// the frequency table to copy - /// if {@code freqs} is {@code null} - /// if {@code freqs.getSymbolLimit()} < 1 - /// or any element {@code freqs.get(i)} < 0 - /// if the total of all {@code freqs} elements exceeds {@code Integer.MAX_VALUE} - 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 ----*/ - - /// - /// Returns the number of symbols in this frequency table, which is at least 1. - /// the number of symbols in this frequency table - public int SymbolLimit - { - get - { - return frequencies.Length; - } - } - - - /// - /// Returns the frequency of the specified symbol. The returned value is at least 0. - /// the symbol to query - /// the frequency of the specified symbol - /// if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} - public int get( int symbol ) - { - checkSymbol( symbol ); - return frequencies[symbol]; - } - - - /// - /// 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. - /// the symbol to set - /// the frequency value to set - /// if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} - /// if this set request would cause the total to exceed {@code Integer.MAX_VALUE} - 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; - } - - - /// - /// Increments the frequency of the specified symbol. - /// the symbol whose frequency to increment - /// if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} - public void increment( int symbol ) - { - checkSymbol( symbol ); - Debug.Assert( frequencies[symbol] == int.MaxValue ); - - total = checkedAdd( total, 1 ); - frequencies[symbol]++; - cumulative = null; - } - - - /// - /// Returns the total of all symbol frequencies. The returned value is at - /// least 0 and is always equal to {@code getHigh(getSymbolLimit() - 1)}. - /// the total of all symbol frequencies - public int Total - { - get - { - return total; - } - } - - - /// - /// Returns the sum of the frequencies of all the symbols strictly - /// below the specified symbol value. The returned value is at least 0. - /// the symbol to query - /// the sum of the frequencies of all the symbols below {@code symbol} - /// if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} - public int getLow( int symbol ) - { - checkSymbol( symbol ); - if( cumulative == null ) - { - initCumulative(); - } - return cumulative[symbol]; - } - - - /// - /// Returns the sum of the frequencies of the specified symbol - /// and all the symbols below. The returned value is at least 0. - /// the symbol to query - /// the sum of the frequencies of {@code symbol} and all symbols below - /// if {@code symbol} < 0 or {@code symbol} ≥ {@code getSymbolLimit()} - 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 ); - } - - - /// - /// Returns a string representation of this frequency table, - /// useful for debugging only, and the format is subject to change. - /// a string representation of this frequency table - 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; - } - -} diff --git a/db/Act.cs b/db/Act.cs deleted file mode 100644 index a959544..0000000 --- a/db/Act.cs +++ /dev/null @@ -1,96 +0,0 @@ -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 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 act, string reason = "{unknown_base}", string dbgPath = "", int dbgLine = -1, string dbgMethod = "" ) - { - m_act = act; - - DebugInfo = reason; - Path = dbgPath; - Line = dbgLine; - Member = dbgMethod; - - //ExtractValue( act ); - } - - static public Act create( Func act, string reason = "{unknown}", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "" ) - { - //ExtractValue( act ); - - return new Act( act, reason, dbgPath, dbgLine, dbgMethod ); - } - - public static Act create( Func act, T p0, string reason = "{unknown}", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "" ) - { - //ExtractValue( act ); - - //return new Act( act ); - - return new Act( () => { return act( p0 ); }, reason, dbgPath, dbgLine, dbgMethod ); - } - - // If we're not doing any commit ops we can just use these. - static public Act create( Action act, string reason = "{unknown}", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "" ) - { - //ExtractValue( act ); - - return new Act( () => { act(); return CommitResults.Perfect; }, reason, dbgPath, dbgLine, dbgMethod ); - } - - public static Act create( Action act, T p0, string reason = "{unknown}", [CallerFilePath] string dbgPath = "", [CallerLineNumber] int dbgLine = -1, [CallerMemberName] string dbgMethod = "" ) - { - //ExtractValue( act ); - - //return new Act( act ); - - return new Act( () => { act( p0 ); return CommitResults.Perfect; }, reason, dbgPath, dbgLine, dbgMethod ); - } - - - - 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 m_act; - - } -} diff --git a/db/DB.cs b/db/DB.cs deleted file mode 100644 index 51d3064..0000000 --- a/db/DB.cs +++ /dev/null @@ -1,228 +0,0 @@ -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 id { get; } -} - -public class DB where T : IID -{ - object m_lock = new object(); - - //Current snapshot of the DB. - ImmutableDictionary m_objs = ImmutableDictionary.Empty; - - //List of committed Ids based on when they were committed. - ImmutableList m_committed = ImmutableList.Empty; - - ImmutableDictionary Objects => m_objs; - - public DB() - { - LogGC.RegisterObjectId( m_lock ); - } - - - public Option lookup( TID id ) - { - if( m_objs.TryGetValue( id, out T obj ) ) - { - return obj.Some(); - } - else - { - // LOG - } - - return obj.None(); - } - - public (Tx, Option) checkout( TID id ) - { - var tx = new Tx( m_committed.Count, m_activeTransaction, this ); - - var v = lookup( id ); - - v.Match( t => - { - tx.checkout( id ); - }, () => - { - } ); - - return (tx, v); - } - - public Tx checkout( TID id, out Option tOut ) - { - var (tx, v) = checkout( id ); - - tOut = v; - - return tx; - } - - public Tx checkout() - { - var tx = new Tx( m_committed.Count, m_activeTransaction, this ); - - return tx; - } - - public CommitResults commit( ref Tx co ) - { - co = null; - return commit_internal_single( co ); - } - - public ImmutableDictionary getSnapshot() - { - ImmutableDictionary res = m_objs; - return res; - } - - - internal CommitResults commit_internal_single( Tx 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> m_activeTransaction = Option.None>(); - -} - -public enum TxStates -{ - Invalid, - Running, - Committed, -} - - -//This only works for a single thread -public class Tx : IDisposable where T : IID -{ - internal ImmutableList Checkouts => m_checkouts; - internal TxStates State => m_state; - internal int Start => m_start; - internal ImmutableList Adds => m_adds; - - internal Tx( int start, DB db ) - : - this( start, Option.None>(), db ) - { - } - - internal Tx( int start, Option> parentTx, DB 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 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 m_db; - - //Do we need these? Do we need both? - Option> m_parentTx; - ImmutableList> m_childTx = ImmutableList>.Empty; - - TxStates m_state = TxStates.Invalid; - ImmutableList m_checkouts = ImmutableList.Empty; - - // New objects created this pass - ImmutableList m_adds = ImmutableList.Empty; -} diff --git a/db/Processor.cs b/db/Processor.cs deleted file mode 100644 index 324d11f..0000000 --- a/db/Processor.cs +++ /dev/null @@ -1,128 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// S H A R P L I B -// -/// // (c) 2003..2025 - -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 where T : IID - { - - - public DB DB { get; private set; } - - public System 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 db, System 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; - - - - - - - - - } - - - - - - - - - - - - - - -} diff --git a/db/System.cs b/db/System.cs deleted file mode 100644 index 9408670..0000000 --- a/db/System.cs +++ /dev/null @@ -1,308 +0,0 @@ -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 - { - 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.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 where T : IID - { - //public static System Current => s_system; - - public SemaphoreSlim ActsExist => m_actsExist; - public DB DB { get; private set; } - - public bool Running { get; private set; } - - public System( res.Ref cfg, DB 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[] procs = new Processor[procCount]; - - for( var i = 0; i < procCount; ++i ) - { - var proc = new Processor( 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 getNextAct() - { - if( m_current.TryTake( out Act res ) ) - { - return res.Some(); - } - - m_actsExist.Wait(); - - return Option.None(); - } - - res.Ref m_cfg; - - SemaphoreSlim m_actsExist = new SemaphoreSlim( 0 ); - - Random m_rand = new Random(); - - ConcurrentBag m_current = new ConcurrentBag(); - ConcurrentBag m_next = new ConcurrentBag(); - - // @@ TODO Keep an eye on the timing of this. - ImmutableList m_futureActions = ImmutableList.Empty; - - /* - TimedAction[] m_sortedFutureActions = new TimedAction[16 * 1024]; - int m_sfaStart = 0; - int m_sfaEnd = 0; - */ - - - - ImmutableList> m_processors = ImmutableList>.Empty; - - //private static System s_system; - } - - -} diff --git a/exp/Exp.cs b/exp/Exp.cs deleted file mode 100644 index 6a58bab..0000000 --- a/exp/Exp.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Collections.Immutable; -using System.Runtime.CompilerServices; - -namespace exp; - - -abstract public record class Exp : io.Versioned> -{ - protected Exp() - : - base() - { - - } - - abstract public T exec(); -} - -public record class ConstantExp( T value ) : Exp -{ - public override T exec() => value; -} - -public record class VarExp : Exp -{ - - public T val = default!; - - public VarExp() - { - } - - public override T exec() => val; - - public VarExp set( T newVal ) - { - return this with { val = newVal }; - } -} - -public ref struct RefHolder -{ - public T val = default!; - - public RefHolder() - { - } - - public RefHolder( T initial ) - { - val = initial; - } -} - - -/* - -public record class Op( EntityId id, Func fn, - [CallerMemberName] string dbgMethod = "", - [CallerFilePath] string dbgPath = "", - [CallerLineNumber] int dbgLine = 0, - [CallerArgumentExpression("fn")] - string dbgExp = "" -) : Exp -{ - public override T exec() - { - var ent = ent.Entity.Get( id ); - if( ent == null ) - throw new System.Exception( $"Op<{typeof(T).Name}>: Entity {id} not found" ); - - return fn( ent ); - } -} -*/ - - -public record class StackExp( VarExp BaseVal ) : Exp -{ - public VarExp ModValue = BaseVal; - - public ImmutableArray> Adds = ImmutableArray>.Empty; - public ImmutableArray> Mults = ImmutableArray>.Empty; - - public override T exec() - { - return ModValue.exec(); - } -} - - - - - - -/// // - - diff --git a/fsm/FSM.cs b/fsm/FSM.cs deleted file mode 100644 index 95d51a2..0000000 --- a/fsm/FSM.cs +++ /dev/null @@ -1,64 +0,0 @@ - - -using System; -using System.Runtime.CompilerServices; - - - -namespace fsm; - - - -public class Context -{ - -} - -public class State - where T : State - where CTX : Context -{ - virtual public void onEnter( CTX ctx, State oldState ) - { - } - - virtual public void onExit( CTX ctx, State newState ) - { - } -} - -public class FSM - where T : FSM - where CTX : Context - where ST : State -{ - public CTX Context { get; private set; } - public ST State { get; private set; } - - public FSM( CTX context, ST state ) - { - Context = context; - State = state; - - State.onEnter( Context, state ); - } - - public void transition( ST newState, string reason = "", - [CallerMemberName] string member = "", - [CallerFilePath] string path = "", - [CallerLineNumber] int line = 0 ) - { - log.debug( $"{GetType().Name} switching to {newState.GetType().Name}from {State.GetType().Name} bcs {reason} Code {log.relativePath( path )}:({line}): {member}" ); - - State.onExit( Context, newState ); - newState.onEnter( Context, State ); - State = newState; - } - -} - -/* - -Im going to preface this with, I use FSMs everywhere for quite a few things. - -*/ diff --git a/imm/Util.cs b/imm/Util.cs deleted file mode 100644 index 585827a..0000000 --- a/imm/Util.cs +++ /dev/null @@ -1,2 +0,0 @@ -using System; -using System.Collections.Generic; diff --git a/lib/CodeGen.cs b/lib/CodeGen.cs deleted file mode 100644 index 1488112..0000000 --- a/lib/CodeGen.cs +++ /dev/null @@ -1,182 +0,0 @@ -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// S H A R P L I B -// -/// // (c) 2003..2025 - -using System; -using System.IO; -using System.Xml; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; -using System.Linq; -using System.Collections.Immutable; -using System.Collections.Concurrent; -using System.Collections.Immutable; - -namespace ser; - -public record CodeGenConfig : io.Recorded -{ - // Whitelists (if needed, otherwise rely on attributes/defaults) - public ImmutableDictionary> WLProps { get; init; } = ImmutableDictionary>.Empty; - public ImmutableDictionary> WLFields { get; init; } = ImmutableDictionary>.Empty; - - // Default member types to process - public ser.Types TypesDefault { get; init; } = ser.Types.Fields | ser.Types.Props; - - // How to handle backing fields (might be less relevant for code gen) - public BackingFieldNaming Naming { get; init; } = BackingFieldNaming.Regular; - - public static CodeGenConfig Default { get; } = new CodeGenConfig(); -} - -public record GenMemberMeta( - MemberInfo Info, - Type Type, - string Name, // Name for code generation (usually original) - bool IsPrimitive, - bool IsCollection, - Type? CollectionElementType, - bool HasDo, - bool HasDont -); - -public record TypeStructureInfo( - Type Type, - List Members, - bool IsValueType, - bool IsCollection -); - -public class TypeStructureAnalyzer -{ - private readonly ConcurrentDictionary _cache = new(); - private readonly CodeGenConfig _cfg; - - public TypeStructureAnalyzer( CodeGenConfig cfg ) => _cfg = cfg; - - public TypeStructureInfo Get( Type type ) => _cache.GetOrAdd( type, BuildTypeInfo ); - - private TypeStructureInfo BuildTypeInfo( Type type ) - { - var members = new List(); - var typesTodo = type.GetCustomAttribute( true )?.Types ?? _cfg.TypesDefault; - bool doFields = typesTodo.HasFlag( ser.Types.Fields ); - bool doProps = typesTodo.HasFlag( ser.Types.Props ); - - // Track processed names to avoid duplicates (e.g., field + prop) - var processedNames = new HashSet(); - - // Process Properties First (often preferred interface) - if( doProps ) - { - foreach( var pi in refl.GetAllProperties( type ) ) - { - if( ProcessMember( pi, false, false, new HashSet(), false, members ) ) - { - processedNames.Add( pi.Name ); - } - } - } - - // Process Fields, avoiding those already covered by properties - if( doFields ) - { - foreach( var fi in refl.GetAllFields( type ) ) - { - var (isBacking, propName) = IsBackingField( fi ); - string nameToTest = isBacking ? propName : fi.Name; - - if( !processedNames.Contains( nameToTest ) ) - { - if( ProcessMember( fi, false, false, new HashSet(), false, members ) ) - { - processedNames.Add( nameToTest ); - } - } - } - } - - return new TypeStructureInfo( - type, - members, - type.IsValueType, - typeof( IEnumerable ).IsAssignableFrom( type ) && type != typeof( string ) - ); - } - - private bool ProcessMember( MemberInfo mi, bool filter, bool doImpls, HashSet whitelist, bool isImm, List members ) - { - var (hasDo, hasDont, propName) = GetMemberAttributes( mi, out var actualMiForAtts ); - - if( hasDont ) - return false; - if( mi.GetCustomAttribute( true ) != null ) - return false; - if( mi.Name.Contains( "k__BackingField" ) && !propName.Any() ) - return false; // Skip if backing but no prop found - - string name = string.IsNullOrEmpty( propName ) ? mi.Name : propName; - - // Add filtering logic if needed (based on whitelist, etc.) - - var type = ( mi is FieldInfo fi ) ? fi.FieldType : ( (PropertyInfo)mi ).PropertyType; - bool isCollection = typeof( IEnumerable ).IsAssignableFrom( type ) && type != typeof( string ); - Type? elementType = isCollection ? GetElementType( type ) : null; - bool isPrimitive = Type.GetTypeCode( type ) != TypeCode.Object && !isCollection; - - members.Add( new GenMemberMeta( - mi, type, name, isPrimitive, isCollection, elementType, hasDo, hasDont - ) ); - return true; - } - - private (bool, string) IsBackingField( FieldInfo fi ) - { - if( fi.Name.StartsWith( "<" ) && fi.Name.EndsWith( "BackingField" ) ) - { - var gtIndex = fi.Name.IndexOf( '>' ); - if( gtIndex > 1 ) - { - return (true, fi.Name.Substring( 1, gtIndex - 1 )); - } - } - return (false, ""); - } - - private (bool hasDo, bool hasDont, string propName) GetMemberAttributes( MemberInfo mi, out MemberInfo actualMi ) - { - actualMi = mi; - string propName = ""; - if( mi is FieldInfo fi && IsBackingField( fi ).Item1 ) - { - propName = IsBackingField( fi ).Item2; - var propInfo = mi.DeclaringType?.GetProperty( propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic ); - if( propInfo != null ) - actualMi = propInfo; - } - else if( mi is PropertyInfo ) - { - propName = mi.Name; - } - - return ( - actualMi.GetCustomAttribute() != null, - actualMi.GetCustomAttribute() != null, - propName - ); - } - - private Type GetElementType( Type collectionType ) - { - if( collectionType.IsArray ) - return collectionType.GetElementType()!; - if( collectionType.IsGenericType ) - return collectionType.GetGenericArguments().Last(); // Usually last (e.g., List, Dict) - return typeof( object ); // Fallback - } - - // Add GetFilters and FilterField if needed, or simplify as above -} diff --git a/lib/CodeGenerator.cs b/lib/CodeGenerator.cs deleted file mode 100644 index 2b08984..0000000 --- a/lib/CodeGenerator.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Text; -using System.Collections.Generic; -using System; -using System.Linq; - -namespace ser; - -public abstract class CodeGenerator -{ - protected StringBuilder _sb = new StringBuilder(); - protected int _indent = 0; - protected TypeStructureAnalyzer _analyzer; - protected CodeGenConfig _config; - protected HashSet _generatedTypes = new(); // Track to avoid re-generating - - public CodeGenerator( CodeGenConfig config ) - { - _config = config; - _analyzer = new TypeStructureAnalyzer( config ); - } - - // Main entry point - public string Generate( Type type, string ns = "GeneratedCode" ) - { - _sb.Clear(); - WriteLine( "using System;" ); - WriteLine( "using System.Collections.Generic;" ); - WriteLine( "using System.Linq;" ); - WriteLine( "" ); - WriteLine( $"namespace {ns};" ); - WriteLine( "" ); - GenerateForType( type ); - return _sb.ToString(); - } - - // Core generation logic - needs to be recursive for dependencies - protected virtual void GenerateForType( Type type ) - { - if( type == null || !CanGenerateFor( type ) || _generatedTypes.Contains( type ) ) - return; - - _generatedTypes.Add( type ); - var info = _analyzer.Get( type ); - - // Generate dependencies first - foreach( var member in info.Members ) - { - GenerateForType( member.Type ); - if( member.IsCollection && member.CollectionElementType != null ) - { - GenerateForType( member.CollectionElementType ); - } - } - - // Generate the actual code - GenerateClassHeader( info ); - BeginBlock(); - GenerateClassBody( info ); - EndBlock(); - } - - // Abstract methods to be implemented by specific generators - protected abstract void GenerateClassHeader( TypeStructureInfo info ); - protected abstract void GenerateClassBody( TypeStructureInfo info ); - protected abstract bool CanGenerateFor( Type type ); // Check if we should generate for this type - - // Helper methods - protected void WriteLine( string line = "" ) => _sb.AppendLine( new string( '\t', _indent ) + line ); - protected void BeginBlock() { WriteLine( "{" ); _indent++; } - protected void EndBlock() { _indent--; WriteLine( "}" ); } - protected string GetTypeName( Type t ) => t.IsGenericType - ? $"{t.Name.Split( '`' )[0]}<{string.Join( ", ", t.GetGenericArguments().Select( GetTypeName ) )}>" - : t.Name; // Basic handling, needs improvement for full names/namespaces -} diff --git a/lib/SerializableDictionary.cs_bad b/lib/SerializableDictionary.cs_bad deleted file mode 100644 index a16151a..0000000 --- a/lib/SerializableDictionary.cs_bad +++ /dev/null @@ -1,166 +0,0 @@ -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: Dictionary, 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 dictionary ) - : base( dictionary ) - { - } - - public SerializableDictionary( IEqualityComparer comparer ) - : base( comparer ) - { - } - - public SerializableDictionary( int capacity ) - : base( capacity ) - { - } - - public SerializableDictionary( IDictionary dictionary, IEqualityComparer comparer ) - : base( dictionary, comparer ) - { - } - - public SerializableDictionary( int capacity, IEqualityComparer 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 kvp = (KeyValuePair)info.GetValue(String.Format( $"Item{i}" ), typeof(KeyValuePair)); - 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 kvp in this ) - { - info.AddValue( String.Format( $"Item{itemIdx}" ), kvp, typeof( KeyValuePair ) ); - itemIdx++; - } - } - - #endregion - #region IXmlSerializable Members - - void IXmlSerializable.WriteXml( System.Xml.XmlWriter writer ) - { - //writer.WriteStartElement(DictionaryNodeName); - foreach( KeyValuePair 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 - } -} diff --git a/lib/VersionFormatter.cs_bad b/lib/VersionFormatter.cs_bad deleted file mode 100644 index 815a1fe..0000000 --- a/lib/VersionFormatter.cs_bad +++ /dev/null @@ -1,677 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using System.Collections; -using System.Diagnostics; -//using System.Globalization; -//using System.ComponentModel; -using System.Runtime.Serialization; - -namespace lib -{ - /// - /// - /// - public class VersionFormatter: IFormatter - { - public enum ETypes - { - Array, - Int32, - Ref, - Object, - EndObject, - Single, - Double, - Char, - String, - Boolean, - EndStream, - } - - - public VersionFormatter() - { - // - // TODO: Add constructor logic here - // - } - - - #region Useless - public ISurrogateSelector SurrogateSelector - { - get - { - return null; - } - - set - { - } - } - - public SerializationBinder Binder - { - get - { - return null; - } - - set - { - } - } - - public StreamingContext Context - { - get - { - return new StreamingContext(); - } - - set - { - } - } - #endregion Useless - - Queue m_objectsToBeDeserialized = new Queue(); - Hashtable m_alreadyDeserialzied = new Hashtable(); - //int m_GUID = 0; - - #region Serialize - public void Serialize( Stream stream, object obj ) - { - //Default is 4k - //BufferedStream bufStream = new BufferedStream( stream ); - - BinaryWriter writer = new BinaryWriter( stream ); - - writeObject( writer, obj ); - - while( m_objectsToBeDeserialized.Count != 0 ) - { - object objToDes = m_objectsToBeDeserialized.Dequeue(); - - writeObject( writer, objToDes ); - } - - writer.Write( (char)ETypes.EndStream ); - } - - void writeRefAndSched( BinaryWriter writer, object obj ) - { - //if( m_alreadyDeserialzied[ obj.GetType().GetArrayRank( - - if( obj == null ) - { - writer.Write( 0 ); - return; - } - - - //Now write the address. - //Bad bad. Need to do this correctly. - int objRef = obj.GetHashCode(); - writer.Write( objRef ); - - if( m_alreadyDeserialzied[obj] == null ) - { - m_alreadyDeserialzied[obj] = obj; - m_objectsToBeDeserialized.Enqueue( obj ); - } - } - - void dispatchWrite( BinaryWriter writer, object parentObj, FieldInfo fi ) - { - string typeName = fi.FieldType.Name; - - string name = fi.Name; - - if( fi.IsNotSerialized ) - { - return; - } - - if( fi.FieldType.IsArray ) - { - writer.Write( (char)ETypes.Array ); - writer.Write( name.GetHashCode() ); - - writeArray( writer, (Array)fi.GetValue( parentObj ) ); - } - else if( ( fi.FieldType.IsClass || fi.FieldType.IsInterface ) && typeName != "String" ) - { - writer.Write( (char)ETypes.Ref ); - writer.Write( name.GetHashCode() ); - - writeRefAndSched( writer, fi.GetValue( parentObj ) ); - } - else if( fi.FieldType.IsEnum ) - { - writer.Write( (char)ETypes.Int32 ); - writer.Write( name.GetHashCode() ); - - write( writer, Convert.ToInt32( fi.GetValue( parentObj ) ) ); - } - else - { - switch( typeName ) - { - case "Int32": - writer.Write( (char)ETypes.Int32 ); - writer.Write( name.GetHashCode() ); - - write( writer, Convert.ToInt32( fi.GetValue( parentObj ) ) ); - break; - case "Single": - writer.Write( (char)ETypes.Single ); - writer.Write( name.GetHashCode() ); - - write( writer, Convert.ToSingle( fi.GetValue( parentObj ) ) ); - break; - case "Double": - writer.Write( (char)ETypes.Double ); - writer.Write( name.GetHashCode() ); - - write( writer, Convert.ToDouble( fi.GetValue( parentObj ) ) ); - break; - case "Char": - writer.Write( (char)ETypes.Char ); - writer.Write( name.GetHashCode() ); - - write( writer, Convert.ToChar( fi.GetValue( parentObj ) ) ); - break; - case "String": - writer.Write( (char)ETypes.String ); - writer.Write( name.GetHashCode() ); - - write( writer, Convert.ToString( fi.GetValue( parentObj ) ) ); - break; - case "Boolean": - writer.Write( (char)ETypes.Boolean ); - writer.Write( name.GetHashCode() ); - - writer.Write( Convert.ToBoolean( fi.GetValue( parentObj ) ) ); - break; - default: - Console.WriteLine( "VersionFormatter does not understand type " + typeName ); - break; - } - } - } - - void writeArray( BinaryWriter writer, Array array ) - { - if( array == null ) - { - writer.Write( (int)-1 ); - return; - } - - writer.Write( array.Length ); - - foreach( object obj in array ) - { - writeRefAndSched( writer, obj ); - } - } - - void getAllFields( object obj, ArrayList list ) - { - Type t = obj.GetType(); - - while( t != null ) - { - FieldInfo[] fiArr = t.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly ); - list.AddRange( fiArr ); - - t = t.BaseType; - } - } - - - void writeObject( BinaryWriter writer, object obj ) - { - Type objType = obj.GetType(); - - writer.Write( (char)ETypes.Object ); - writer.Write( objType.FullName ); - - int objRef = obj.GetHashCode(); - writer.Write( objRef ); - - ArrayList list = new ArrayList(); - - getAllFields( obj, list ); - - foreach( FieldInfo fi in list ) - { - dispatchWrite( writer, obj, fi ); - } - - writer.Write( (char)ETypes.EndObject ); - } - - void write( BinaryWriter wr, TType val ) - { - //wr.Write( val ); - } - - /* - void writeInt( BinaryWriter writer, int val ) - { - writer.Write( val ); - } - - void writeSingle( BinaryWriter writer, float val ) - { - writer.Write( val ); - } - - void writeDouble( BinaryWriter writer, double val ) - { - writer.Write( val ); - } - - void writeChar( BinaryWriter writer, char val ) - { - writer.Write( val ); - } - - void writeString( BinaryWriter writer, string val ) - { - writer.Write( val ); - } - - void writeBool( BinaryWriter writer, bool val ) - { - writer.Write( val ); - } - */ - #endregion Serialize - - - #region Deserialize - - class Fixup - { - public Fixup( int guid, object obj, FieldInfo fi ) - { - m_guid = guid; - m_obj = obj; - m_fi = fi; - } - - public Fixup( int guid, object obj, int index ) - { - m_guid = guid; - m_obj = obj; - m_index = index; - } - - public readonly int m_guid = 0; - public readonly object m_obj = null; - - public readonly FieldInfo m_fi = null; - public readonly int m_index= -1; - - } - - Hashtable m_mapGUIDToObject = new Hashtable(); - ArrayList m_fixupList = new ArrayList(); - - ArrayList m_desObjects = new ArrayList(); - - public object Deserialize( Stream stream ) - { - BinaryReader reader = new BinaryReader( stream ); - - object objRoot = null; - - //Read in the first object. - { - ETypes type = (ETypes)reader.ReadChar(); - - Debug.Assert( type == ETypes.Object ); - - objRoot = readObject( reader ); - - m_desObjects.Add( objRoot ); - } - - bool readObjects = true; - - while( readObjects ) - { - ETypes type = (ETypes)reader.ReadChar(); - - Debug.Assert( type == ETypes.Object || type == ETypes.EndStream ); - - if( type == ETypes.Object ) - { - object obj = readObject( reader ); - - m_desObjects.Add( obj ); - } - else - { - Debug.Assert( type == ETypes.EndStream ); - - readObjects = false; - } - } - - foreach( Fixup fu in m_fixupList ) - { - //Fixup fix = m_fixups[ - - object obj = m_mapGUIDToObject[ fu.m_guid ]; - - if( obj != null ) - { - if( fu.m_fi != null ) - { - fu.m_fi.SetValue( fu.m_obj, obj ); - } - else - { - Debug.Assert( fu.m_index >= 0 ); - - object []array = (object [])fu.m_obj; - - array[fu.m_index] = obj; - } - } - else - { - Console.WriteLine( "Obj to ref is null." ); - } - } - - foreach( object obj in m_desObjects ) - { - if( typeof( IDeserializationCallback ).IsAssignableFrom( obj.GetType() ) ) - { - IDeserializationCallback desCB = (IDeserializationCallback)obj; - - if( desCB != null ) - { - desCB.OnDeserialization( this ); - } - } - } - - return objRoot; - } - - - - bool dispatchRead( BinaryReader reader, object obj, Hashtable ht ) - { - - //Read the type - ETypes type = (ETypes)reader.ReadChar(); - - if( type == ETypes.EndObject ) - { - return false; - } - - int nameHash = reader.ReadInt32(); - - FieldInfo fi = (FieldInfo)ht[ nameHash ]; - - if( fi == null ) - { - Console.WriteLine( "Field no longer exists" ); - } - - try - { - switch( type ) - { - case ETypes.Array: - readArray( reader, obj, fi ); - break; - case ETypes.Int32: - readInt( reader, obj, fi ); - break; - case ETypes.Single: - readSingle( reader, obj, fi ); - break; - case ETypes.Double: - readDouble( reader, obj, fi ); - break; - case ETypes.Char: - readChar( reader, obj, fi ); - break; - case ETypes.Boolean: - readBool( reader, obj, fi ); - break; - case ETypes.String: - readString( reader, obj, fi ); - break; - case ETypes.Ref: - readRef( reader, obj, fi ); - break; - case ETypes.Object: - readObject( reader ); - break; - default: - Debug.Fail( "Unknown type on read." ); - break; - } - } - catch( Exception ex ) - { - Console.WriteLine( "Exception: " + ex.Message ); - Console.WriteLine( "Stack: " + ex.StackTrace ); - } - - - return true; - } - - object createObject( string objTypeName ) - { - Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies(); - - foreach( Assembly a in ass ) - { - Type t = a.GetType( objTypeName ); - - if( t != null ) - { - object obj = FormatterServices.GetUninitializedObject( t ); - - if( obj != null ) - { - return obj; - } - } - } - - return null; - } - - - object readObject( BinaryReader reader ) - { - //ETypes type = (ETypes)reader.ReadChar(); - - //Debug.Assert( type == ETypes.Object, "Expecting type Object" ); - - string objTypeName = reader.ReadString(); - int objGUID = reader.ReadInt32(); - - try - { - object obj = createObject( objTypeName ); - - m_mapGUIDToObject[objGUID] = obj; - - ArrayList list = new ArrayList(); - Hashtable ht = new Hashtable(); - - if( obj != null ) - { - getAllFields( obj, list ); - - foreach( FieldInfo fi in list ) - { - ht[fi.Name.GetHashCode()] = fi; - } - } - - while( dispatchRead( reader, obj, ht ) ) - { - } - - return obj; - } - catch( Exception ex ) - { - Console.WriteLine( "Exception: " + ex.Message ); - } - - return null; - } - - void readArray( BinaryReader reader, object obj, FieldInfo fi ) - { - int length = reader.ReadInt32(); - - if( length < 0 ) - { - if( fi == null ) - return; - - fi.SetValue( obj, null ); - - return; - } - - object[] array = new object[length]; - - if( fi != null ) - { - fi.SetValue( obj, array ); - } - - for( int i = 0; i < length; ++i ) - { - int val = reader.ReadInt32(); - - //m_fixups[ val ] = new Fixup( obj, fi ); - - if( fi != null ) - { - m_fixupList.Add( new Fixup( val, array, i ) ); - } - } - } - - void readRef( BinaryReader reader, object obj, FieldInfo fi ) - { - int val = reader.ReadInt32(); - - //m_fixups[ val ] = new Fixup( obj, fi ); - - m_fixupList.Add( new Fixup( val, obj, fi ) ); - } - - void readInt( BinaryReader reader, object obj, FieldInfo fi ) - { - int val = reader.ReadInt32(); - - if( fi == null ) - return; - - if( !fi.FieldType.IsEnum ) - { - fi.SetValue( obj, val ); - } - else - { - object enumVal = Enum.Parse( fi.FieldType, val.ToString() ); - fi.SetValue( obj, Convert.ChangeType( enumVal, fi.FieldType ) ); - } - - } - - void readSingle( BinaryReader reader, object obj, FieldInfo fi ) - { - float val = reader.ReadSingle(); - - if( fi == null ) - return; - - fi.SetValue( obj, val ); - } - - void readDouble( BinaryReader reader, object obj, FieldInfo fi ) - { - double val = reader.ReadDouble(); - - if( fi == null ) - return; - - fi.SetValue( obj, val ); - } - - void readChar( BinaryReader reader, object obj, FieldInfo fi ) - { - char val = reader.ReadChar(); - - if( fi == null ) - return; - - fi.SetValue( obj, val ); - } - - void readString( BinaryReader reader, object obj, FieldInfo fi ) - { - string val = reader.ReadString(); - - if( fi == null ) - return; - - fi.SetValue( obj, val ); - } - - void readBool( BinaryReader reader, object obj, FieldInfo fi ) - { - bool val = reader.ReadBoolean(); - - if( fi == null ) - return; - - fi.SetValue( obj, val ); - } - - #endregion Deserialize - - - } -} - - - - - - - - - - - - - - - - - - - diff --git a/lib/XmlFormatter.cs_bad b/lib/XmlFormatter.cs_bad deleted file mode 100644 index 6573732..0000000 --- a/lib/XmlFormatter.cs_bad +++ /dev/null @@ -1,1094 +0,0 @@ -using System; -using System.IO; -using System.Xml; -using System.Runtime.Serialization; -//using System.Web.Configuration; -using System.Collections; -using System.Collections.Generic; - - -using System.Reflection; -//using System.Collections; -//using System.Diagnostics; -//using System.Globalization; -//using System.ComponentModel; - - -namespace lib -{ - //Old, use 2 now. - class XmlFormatter_BAD: IFormatter - { - StreamingContext m_context; - //SerializationMode m_mode; - //KnownTypeCollection known_types; - //IDataContractSurrogate m_surrogate; - //int m_maxItems; - - public XmlFormatter() - { - } - - /* - public XmlFormatter( SerializationMode mode ) - { - m_mode = mode; - } - - public XmlFormatter( StreamingContext context ) - { - m_context = context; - } - - public XmlFormatter( SerializationMode mode, - StreamingContext context ) - { - m_mode = mode; - m_context = context; - } - */ - - //public XmlFormatter (SerializationMode mode, - // StreamingContext context, KnownTypeCollection knownTypes) - //{ - //} - - SerializationBinder IFormatter.Binder - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - ISurrogateSelector IFormatter.SurrogateSelector - { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } - - public StreamingContext Context - { - get { return m_context; } - set { m_context = value; } - } - - - /* - public KnownTypeCollection KnownTypes { - get { return known_types; } - } - - public int MaxItemsInObjectGraph { - get { return m_maxItems; } - set { m_maxItems= value; } - } - */ - - - object IFormatter.Deserialize( Stream stream ) - { - return Deserialize( stream, null ); - } - - public object Deserialize( Stream stream, Type type ) - { - XmlTextReader reader = new XmlTextReader( stream ); - - return Deserialize( reader, type ); - } - - public object Deserialize( XmlReader reader, Type type ) - { - return Deserialize( reader, type, false ); - } - - public object Deserialize( XmlReader reader, Type type, bool readContentOnly ) - { - reader.Read(); - - XmlDocument doc = new XmlDocument(); - - doc.Load( reader ); - - return Deserialize( doc.DocumentElement ); - } - - ConstructorInfo getNoParamCons( ConstructorInfo[] ciArr ) - { - foreach( ConstructorInfo ci in ciArr ) - { - if( ci.GetParameters().Length == 0 ) - { - return ci; - } - } - - return null; - } - - private static FormatterConverter s_conv = new FormatterConverter(); - - private Dictionary m_alreadySerialized = new Dictionary(); - - public object Deserialize( XmlElement elem ) - { - string strType = elem.GetAttribute( "t" ); - - return Deserialize( elem, strType ); - } - - public object Deserialize( XmlElement elem, string strType ) - { - Type type = Type.GetType( strType ); - - MemberInfo[] miArr = FormatterServices.GetSerializableMembers( type ); - - object obj = Activator.CreateInstance( type ); - - - /* - object obj = FormatterServices.GetUninitializedObject( type ); - - - ConstructorInfo[] ciArr = obj.GetType().GetConstructors(); - - ConstructorInfo ci = getNoParamCons( ciArr ); - - if( ci == null ) - return null; - - obj = ci.Invoke( null ); - */ - - for( int i = 0; i < miArr.Length; ++i ) - { - FieldInfo fi = (FieldInfo)miArr[ i ]; - - XmlNodeList nodeList = elem.GetElementsByTagName( fi.Name ); - - if( nodeList.Count == 1 ) - { - Type t = fi.FieldType; - - TypeCode tc = Type.GetTypeCode( t ); - - XmlElement child = (XmlElement)nodeList[ 0 ]; - - object childObj = null; - - if( tc != TypeCode.Object || fi.FieldType.FullName == "System.String" ) - { - childObj = s_conv.Convert( child.GetAttribute( "v" ), fi.FieldType ); - } - else - { - if( !t.IsArray ) - { - string refStr = child.GetAttribute( "ref" ); - int refInt = Convert.ToInt32( refStr ); - - if( child.HasAttribute( "t" ) ) - { - childObj = Deserialize( child ); - - m_alreadySerialized[refInt] = childObj; - } - else - { - childObj = m_alreadySerialized[refInt]; - } - } - else - { - //FormatterServices.GetUninitializedObject() - - int length = s_conv.ToInt32( child.GetAttribute( "c" ) ); - - string elemType = child.GetAttribute( "t" ); - - Array arr = Array.CreateInstance( t.GetElementType(), length ); - - XmlNodeList arrNodeList = child.ChildNodes; - - for( int iElems = 0; iElems < arr.Length; ++iElems ) - { - XmlElement arrElem = (XmlElement)arrNodeList.Item( iElems ); - - arr.SetValue( Deserialize( arrElem, elemType ), iElems ); - } - } - } - - fi.SetValue( obj, childObj ); - } - else - { - if( nodeList.Count == 0 ) - { - // Should be - - //object obj2 = fi.GetRawConstantValue(); - } - else //More than 1. - { - //log.error( "Too many fields named the same thing" ); - } - } - } - - //FieldInfo fi = (FieldInfo)miArr[0]; - - //ConstructorInfo ci = fi.FieldType.TypeInitializer; - - //ci.Invoke( null ); - - return obj; - } - - - /* - public T Deserialize (Stream stream) - { - return (T) Deserialize (XmlReader.Create (stream), typeof (T)); - } - - public T Deserialize (XmlReader reader) - { - return (T) Deserialize (reader, typeof (T), false); - } - - public T Deserialize (XmlReader reader, bool readContentOnly) - { - return (T) Deserialize (reader, typeof (T), readContentOnly); - } - */ - - public void Serialize( Stream stream, object graph ) - { - /* - XmlWriterSettings settings = new XmlWriterSettings(); - - settings.Indent = true; - - Serialize( XmlWriter.Create( stream, settings ), graph ); - */ - - XmlTextWriter writer = new XmlTextWriter( stream, null ); - - writer.Formatting = Formatting.Indented; - - Serialize( writer, graph ); - - writer.Close(); - } - - public void Serialize( XmlWriter writer, object graph ) - { - Serialize( writer, graph, null, true, false, true ); - } - - public void Serialize( XmlWriter writer, object graph, - Type rootType, bool preserveObjectReferences, - bool writeContentOnly, - bool ignoreUnknownSerializationData ) - { - Type t = graph.GetType(); - - //writer.WriteStartDocument(); - - if( Type.GetTypeCode( t ) == TypeCode.Object ) - { - writer.WriteStartElement( "root" ); - - Assembly assem = t.Assembly; - - string assemName = assem.GetName().Name; - - writer.WriteAttributeString( "t", graph.GetType().FullName + ", " + assemName ); - - FieldInfo[] fiArr = t.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly ); - - foreach( FieldInfo fi in fiArr ) - { - Serialize( writer, fi.Name, fi.GetValue( graph ) ); - - /* - if( fi.FieldType.IsClass ) - { - Serialize( writer, fi.GetValue( graph ), rootType, preserveObjectReferences, writeContentOnly, ignoreUnknownSerializationData ); - } - else - { - SerializePod( writer, fi.GetValue( graph ) ); - } - */ - } - - writer.WriteEndElement(); - } - - //writer.WriteEndDocument(); - } - - private ObjectIDGenerator m_idGenerator = new ObjectIDGenerator(); - private Dictionary m_alreadyDeserialzied = new Dictionary(); - - public void Serialize( XmlWriter writer, string name, object obj ) - { - writer.WriteStartElement( name ); - - if( obj != null ) - { - Type t = obj.GetType(); - - if( Type.GetTypeCode( t ) == TypeCode.Object ) - { - bool first = false; - if( !m_alreadyDeserialzied.ContainsKey( m_idGenerator.GetId( obj, out first ) ) ) - { - m_alreadyDeserialzied[m_idGenerator.GetId( obj, out first )] = obj; - - Assembly assem = t.Assembly; - - string assemName = assem.GetName().Name; - - if( !t.IsArray ) - { - writer.WriteAttributeString( "t", t.FullName + ", " + assemName ); - - writer.WriteAttributeString( "ref", m_idGenerator.GetId( obj, out first ).ToString() ); - - FieldInfo[] fiArr = t.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public ); - - foreach( FieldInfo fi in fiArr ) - { - Serialize( writer, fi.Name, fi.GetValue( obj ) ); - } - } - else - { - Array arr = (Array)obj; - - Type aType = t.GetElementType(); - - string aTypeString = aType.FullName; - - writer.WriteAttributeString( "t", aTypeString + ", " + assemName ); - - writer.WriteAttributeString( "c", arr.Length.ToString() ); - - for( int i = 0; i < arr.Length; ++i ) - { - Serialize( writer, "val", arr.GetValue( i ) ); - } - - //writer.WriteStartElement( "values" ); - - - - //writer.WriteEndElement(); - - } - } - else - { - writer.WriteAttributeString( "ref", m_idGenerator.GetId( obj, out first ).ToString() ); - } - } - else - { - writer.WriteAttributeString( "t", t.FullName ); - - writer.WriteAttributeString( "v", obj.ToString() ); - } - } - else - { - writer.WriteAttributeString( "null", "" ); - } - - writer.WriteEndElement(); - } - - } -} - - - - - - - - - - - - - - - - - - -/* -/// -/// -/// -public class XmlFormatter : IFormatter -{ - public enum ETypes - { - Array, - Int32, - Ref, - Object, - EndObject, - Single, - Double, - Char, - String, - Boolean, - EndStream, - } - - - public XmlFormatter() - { - // - // TODO: Add constructor logic here - // - } - - - #region Useless - public ISurrogateSelector SurrogateSelector - { - get - { - return null; - } - - set - { - } - } - - public SerializationBinder Binder - { - get - { - return null; - } - - set - { - } - } - - public StreamingContext Context - { - get - { - return new StreamingContext(); - } - - set - { - } - } - #endregion Useless - - Queue m_objectsToBeDeserialized = new Queue(); - Hashtable m_alreadyDeserialzied = new Hashtable(); - //int m_GUID = 0; - - #region Serialize - public void Serialize( System.IO.Stream stream, object obj ) - { - //Default is 4k - //BufferedStream bufStream = new BufferedStream( stream ); - - TextWriter writer = new StreamWriter( stream ); - - writeObject( writer, obj ); - - while( m_objectsToBeDeserialized.Count != 0 ) - { - object objToDes = m_objectsToBeDeserialized.Dequeue(); - - writeObject( writer, objToDes ); - } - - writer.Write( (char)ETypes.EndStream ); - } - - void writeRefAndSched( TextWriter writer, object obj ) - { - //if( m_alreadyDeserialzied[ obj.GetType().GetArrayRank( - - if( obj == null ) - { - writer.Write( 0 ); - return; - } - - - //Now write the address. - //Bad bad. Need to do this correctly. - int objRef = obj.GetHashCode(); - writer.Write( objRef ); - - if( m_alreadyDeserialzied[ obj ] == null ) - { - m_alreadyDeserialzied[ obj ] = obj; - m_objectsToBeDeserialized.Enqueue( obj ); - } - } - - void dispatchWrite( TextWriter writer, object parentObj, FieldInfo fi ) - { - string typeName = fi.FieldType.Name; - - string name = fi.Name; - - if( fi.IsNotSerialized ) - { - return; - } - - if( fi.FieldType.IsArray ) - { - writer.Write( (char)ETypes.Array ); - writer.Write( name.GetHashCode() ); - - writeArray( writer, (Array)fi.GetValue( parentObj ) ); - } - else if( ( fi.FieldType.IsClass || fi.FieldType.IsInterface ) && typeName != "String" ) - { - writer.Write( (char)ETypes.Ref ); - writer.Write( name.GetHashCode() ); - - writeRefAndSched( writer, fi.GetValue( parentObj ) ); - } - else if( fi.FieldType.IsEnum ) - { - writer.Write( (char)ETypes.Int32 ); - writer.Write( name.GetHashCode() ); - - writer.Write( Convert.ToInt32( fi.GetValue( parentObj ) ) ); - } - else - { - switch( typeName ) - { - case "Int32": - writer.Write( (char)ETypes.Int32 ); - writer.Write( name.GetHashCode() ); - - writer.Write( Convert.ToInt32( fi.GetValue( parentObj ) ) ); - break; - case "Single": - writer.Write( (char)ETypes.Single ); - writer.Write( name.GetHashCode() ); - - writer.Write( Convert.ToSingle( fi.GetValue( parentObj ) ) ); - break; - case "Double": - writer.Write( (char)ETypes.Double ); - writer.Write( name.GetHashCode() ); - - writer.Write( Convert.ToDouble( fi.GetValue( parentObj ) ) ); - break; - case "Char": - writer.Write( (char)ETypes.Char ); - writer.Write( name.GetHashCode() ); - - writer.Write( Convert.ToChar( fi.GetValue( parentObj ) ) ); - break; - case "String": - writer.Write( (char)ETypes.String ); - writer.Write( name.GetHashCode() ); - - writer.Write( Convert.ToString( fi.GetValue( parentObj ) ) ); - break; - case "Boolean": - writer.Write( (char)ETypes.Boolean ); - writer.Write( name.GetHashCode() ); - - writer.Write( Convert.ToBoolean( fi.GetValue( parentObj ) ) ); - break; - default: - Console.WriteLine( "VersionFormatter does not understand type " + typeName ); - break; - } - } - } - - void writeArray( TextWriter writer, Array array ) - { - if( array == null ) - { - writer.Write( (int)-1 ); - return; - } - - writer.Write( array.Length ); - - foreach( object obj in array ) - { - writeRefAndSched( writer, obj ); - } - } - - void getAllFields( object obj, ArrayList list ) - { - Type t = obj.GetType(); - - while( t != null ) - { - FieldInfo[] fiArr = t.GetFields( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly ); - list.AddRange( fiArr ); - - t = t.BaseType; - } - } - - - void writeObject( TextWriter writer, object obj ) - { - Type objType = obj.GetType(); - - writer.Write( (char)ETypes.Object ); - writer.Write( objType.FullName ); - - int objRef = obj.GetHashCode(); - writer.Write( objRef ); - - ArrayList list = new ArrayList(); - - getAllFields( obj, list ); - - foreach( FieldInfo fi in list ) - { - dispatchWrite( writer, obj, fi ); - } - - writer.Write( (char)ETypes.EndObject ); - } - - void write( TextWriter wr, TType val ) - { - //wr.Write( val ); - } - - /* - void writeInt( TextWriter writer, int val ) - { - writer.Write( val ); - } - - void writeSingle( TextWriter writer, float val ) - { - writer.Write( val ); - } - - void writeDouble( TextWriter writer, double val ) - { - writer.Write( val ); - } - - void writeChar( TextWriter writer, char val ) - { - writer.Write( val ); - } - - void writeString( TextWriter writer, string val ) - { - writer.Write( val ); - } - - void writeBool( TextWriter writer, bool val ) - { - writer.Write( val ); - } - * / - #endregion Serialize - - - #region Deserialize - - class Fixup - { - public Fixup( int guid, object obj, FieldInfo fi ) - { - m_guid= guid; - m_obj = obj; - m_fi = fi; - } - - XmlFormatter - - public Fixup( int guid, object obj, int index ) - { - m_guid = guid; - m_obj = obj; - m_index= index; - } - - public readonly int m_guid = 0; - public readonly object m_obj = null; - - public readonly FieldInfo m_fi = null; - public readonly int m_index= -1; - - } - - Hashtable m_mapGUIDToObject = new Hashtable(); - ArrayList m_fixupList = new ArrayList(); - - ArrayList m_desObjects = new ArrayList(); - - public object Deserialize( System.IO.Stream stream ) - { - StreamReader reader = new StreamReader( stream ); - - object objRoot = null; - - //Read in the first object. - { - ETypes type = (ETypes)reader.ReadChar(); - - Debug.Assert( type == ETypes.Object ); - - objRoot = readObject( reader ); - - m_desObjects.Add( objRoot ); - } - - bool readObjects = true; - - while( readObjects ) - { - ETypes type = (ETypes)reader.ReadChar(); - - Debug.Assert( type == ETypes.Object || type == ETypes.EndStream ); - - if( type == ETypes.Object ) - { - object obj = readObject( reader ); - - m_desObjects.Add( obj ); - } - else - { - Debug.Assert( type == ETypes.EndStream ); - - readObjects = false; - } - } - - foreach( Fixup fu in m_fixupList ) - { - //Fixup fix = m_fixups[ - - object obj = m_mapGUIDToObject[ fu.m_guid ]; - - if( obj != null ) - { - if( fu.m_fi != null ) - { - fu.m_fi.SetValue( fu.m_obj, obj ); - } - else - { - Debug.Assert( fu.m_index >= 0 ); - - object []array = (object [])fu.m_obj; - - array[ fu.m_index ] = obj; - } - } - else - { - Console.WriteLine( "Obj to ref is null." ); - } - } - - foreach( object obj in m_desObjects ) - { - if( typeof( IDeserializationCallback ).IsAssignableFrom( obj.GetType() ) ) - { - IDeserializationCallback desCB = (IDeserializationCallback)obj; - - if( desCB != null ) - { - desCB.OnDeserialization( this ); - } - } - } - - return objRoot; - } - - - - bool dispatchRead( StreamReader reader, object obj, Hashtable ht ) - { - - //Read the type - ETypes type = (ETypes)reader.ReadChar(); - - if( type == ETypes.EndObject ) - { - return false; - } - - int nameHash = reader.ReadInt32(); - - FieldInfo fi = (FieldInfo)ht[ nameHash ]; - - if( fi == null ) - { - Console.WriteLine( "Field no longer exists" ); - } - - try - { - switch( type ) - { - case ETypes.Array: - readArray( reader, obj, fi ); - break; - case ETypes.Int32: - readInt( reader, obj, fi ); - break; - case ETypes.Single: - readSingle( reader, obj, fi ); - break; - case ETypes.Double: - readDouble( reader, obj, fi ); - break; - case ETypes.Char: - readChar( reader, obj, fi ); - break; - case ETypes.Boolean: - readBool( reader, obj, fi ); - break; - case ETypes.String: - readString( reader, obj, fi ); - break; - case ETypes.Ref: - readRef( reader, obj, fi ); - break; - case ETypes.Object: - readObject( reader ); - break; - default: - Debug.Fail( "Unknown type on read." ); - break; - } - } - catch( Exception ex ) - { - Console.WriteLine( "Exception: " + ex.Message ); - Console.WriteLine( "Stack: " + ex.StackTrace ); - } - - - return true; - } - - object createObject( string objTypeName ) - { - Assembly[] ass = AppDomain.CurrentDomain.GetAssemblies(); - - foreach( Assembly a in ass ) - { - Type t = a.GetType( objTypeName ); - - if( t != null ) - { - object obj = FormatterServices.GetUninitializedObject( t ); - - if( obj != null ) - { - return obj; - } - } - } - - return null; - } - - - object readObject( StreamReader reader ) - { - //ETypes type = (ETypes)reader.ReadChar(); - - //Debug.Assert( type == ETypes.Object, "Expecting type Object" ); - - string objTypeName = reader.ReadString(); - int objGUID = reader.ReadInt32(); - - try - { - object obj = createObject( objTypeName ); - - m_mapGUIDToObject[ objGUID ] = obj; - - ArrayList list = new ArrayList(); - Hashtable ht = new Hashtable(); - - if( obj != null ) - { - getAllFields( obj, list ); - - foreach( FieldInfo fi in list ) - { - ht[ fi.Name.GetHashCode() ] = fi; - } - } - - while( dispatchRead( reader, obj, ht ) ) - { - } - - return obj; - } - catch( Exception ex ) - { - Console.WriteLine( "Exception: " + ex.Message ); - } - - return null; - } - - void readArray( StreamReader reader, object obj, FieldInfo fi ) - { - int length = reader.ReadInt32(); - - if( length < 0 ) - { - if( fi == null ) return; - - fi.SetValue( obj, null ); - - return; - } - - object[] array = new object[length]; - - if( fi != null ) - { - fi.SetValue( obj, array ); - } - - for( int i=0; i te.PayloadStringByName( name ) ?? def; - - public static T Get( this TraceEvent te, string name, T def = default ) - { - var index = te.PayloadIndex( name ); - - if( index <= 0 ) - return default; - - var value = te.PayloadValue( index ); - - - if( value.GetType() != typeof( T ) ) - { - log.warn( $"In {te.ID} Payload {name} is type {value.GetType().FriendlyName()} not {typeof( T ).FriendlyName()}" ); - return default; - } - - return (T)value; - } - - -} - - -public class LogGC -{ - static ImmutableHashSet blacklist; - static ImmutableHashSet stacklist; - - static LogGC() - { - blacklist = ImmutableHashSet.Create( - "FAKE_TEST", - "GC/BulkMovedObjectRanges", - - /* - "GC/BulkSurvivingObjectRanges", - "GC/BulkRootStaticVar", - "GC/BulkNode", - "GC/BulkEdge", - "GC/BulkRootEdge", - "GC/FinalizeObject", - "GC/SetGCHandle", - "GC/DestoryGCHandle", - "GC/MarkWithType", - "GC/SuspendEEStart", - "GC/SuspendEEStop", - - "GC/RestartEEStart", - "GC/RestartEEStop", - - "GC/FinalizersStart", - // "GC/FinalizersStop", //Keep this one since it has details - - "GC/GenerationRange", - "GC/FitBucketInfo", - - "TieredCompilation/BackgroundJitStart", - - "Type/BulkType", - "TypeLoad/Start", - - "Method/R2RGetEntryPointStart", - "Method/MethodDetails", - "Method/MemoryAllocatedForJitCode", - "Method/JittingStarted", - "Method/ILToNativeMap", - - "ThreadPoolWorkerThread/Wait", - - "ILStub/StubGenerated" - */ - "FAKE_END_IS_NOTHING" - - ); - stacklist = ImmutableHashSet.Create( "{TEST_ITEM}" ); - - log.endpointForCat( "Method/MemoryAllocatedForJitCode", log.Endpoints.File ); - log.endpointForCat( "TypeLoad/Stop", log.Endpoints.File ); - log.endpointForCat( "GC/BulkRootStaticVar", log.Endpoints.File ); - log.endpointForCat( "GC/BulkNode", log.Endpoints.File ); - log.endpointForCat( "GC/BulkRootStaticVar", log.Endpoints.File ); - - } - - static public void RegisterObjectId( object obj ) - { - /* - var gchWeak = GCHandle.Alloc( obj, GCHandleType.Weak ); - var gchNRML = GCHandle.Alloc( obj, GCHandleType.Normal ); - - var intPtrWeak = GCHandle.ToIntPtr( gchWeak ); - var intPtrNRML = GCHandle.ToIntPtr( gchNRML ); - - //var intPtr = Marshal.GetIUnknownForObject( obj ); - var intPtr = 0; //gchNRML.AddrOfPinnedObject(); - - //Marshal.GetTypedObjectForIUnknown() - - // 0x00000003400111C0 - // 0000000000000000 - - log.info( $"Log ObjectIDs: 0x{intPtrNRML.ToString("X"):0000000000000000} 0x{intPtrWeak.ToString("X"):0000000000000000}" ); - //*/ - } - - - public static Action LogCategoryFunc( string catIn ) - { - return ( TraceEvent te ) => - { - var cat = catIn; - - if( blacklist.Contains( te.EventName ) ) - return; - - { - var methodBeingCompiledNamespace = te.Get( "MethodBeingCompiledNamespace" ); - if( ( methodBeingCompiledNamespace.StartsWith( "Microsoft" ) || methodBeingCompiledNamespace.StartsWith( "System" ) ) ) - return; - } - - { - var methodNamespace = te.PayloadStringByName( "MethodNamespace" ) ?? ""; - if( ( methodNamespace.StartsWith( "Microsoft" ) || methodNamespace.StartsWith( "System" ) ) ) - return; - } - - { - var ns = te.PayloadStringByName( "TypeName" ) ?? ""; - if( ( ns.StartsWith( "Microsoft" ) || ns.StartsWith( "System" ) ) ) - return; - } - - - - { - var ns = te.PayloadStringByName( "TypeLoad/Stop" ) ?? ""; - if( ns.StartsWith( "Godot" ) ) - return; - } - - { - var payloadIndex = te.PayloadIndex( "count" ); - - if( payloadIndex > 0 ) - { - var count = (int)te.PayloadValue( payloadIndex ); - if( count > 16 ) - return; - } - } - - // - // - - // - - if( !te.EventName.StartsWith( "EventID" ) ) - { - var logDetails = ""; - // Custom event displays - if( te.EventName == "Method/LoadVerbose" ) - { - var optTier = te.Get( "OptimizationTier" ); - var methodNamespace = te.Get( "MethodNamespace" ); - - if( optTier == "MinOptJitted" ) - return; - if( methodNamespace.StartsWith( "FastSerialization" ) ) - return; - - log.info( $"{optTier} {LogGCExt.MethodInfo( te )}", cat: te.EventName ); - - return; - //logDetails = "| Details: "; - } - else if( te.EventName.StartsWith( "Method/Inlining" ) ) - { - var methodNamespace = te.Get( "MethodBeingCompiledNamespace" ); - - if( methodNamespace.StartsWith( "FastSerialization" ) ) - return; - - log.info( $"Inlining {te.Get( "FailReason" )} {te.Get( "OptimizationTier" )} {LogGCExt.MethodInfo( te, "MethodBeingCompiled" )}", cat: te.EventName ); - - return; - //logDetails = "| Details: "; - } - else if( te.EventName == "Type/BulkType" ) - { - var val = te.ToString(); - - XmlDocument doc = new(); - - var stream = new MemoryStream(); - var writer = new StreamWriter( stream ); - writer.Write( val ); - writer.Flush(); - stream.Position = 0; - - doc.Load( stream ); - - var root = doc.DocumentElement; - - XmlNodeList children = root.ChildNodes; - - foreach( var child in children ) - { - var node = child as XmlElement; - log.info( $"Child: {node.Name}" ); - } - - return; - } - else if( te.EventName == "TypeLoad/Stop" ) - { - /* - var typeName = te.PayloadStringByName( "TypeName" ); - if( typeName.StartsWith( "Godot." ) ) - return; - - log.info( $"{typeName} Level: {te.PayloadStringByName( "LoadLevel" )}", cat: te.EventName ); - //*/ - - return; - - //logDetails = "| Details: "; - } - else if( te.EventName.StartsWith( "Method/R2RGetEntryPoint" ) ) - { - log.info( $"{LogGCExt.MethodInfo( te )} Entry: {te.PayloadStringByName( "EntryPoint" )}", cat: te.EventName ); - return; - //logDetails = "| Details: "; - } - else if( te.EventName.StartsWith( "Contention/LockCreated" ) ) - { - // "Contention/Start" AssociatedObjectID - // System.Runtime.InteropServices.GCHandle.FromIntPtr( - - var lockId = te.Get( "LockID" ); - var objId = te.Get( "AssociatedObjectID" ); - - var testObj = Marshal.GetIUnknownForObject( objId ); - - var intPtrStr = te.PayloadStringByName( "AssociatedObjectID" ).Substring( 2 ); - - try - { - //var intPtr = Convert.ToUInt64( intPtrStr, 16 ); - - //var gch = System.Runtime.InteropServices.GCHandle.FromIntPtr( intPtr ); - var gch = new GCHandle(); - - - //* - var allocated = gch.IsAllocated; - var obj = gch.Target; - - log.info( $"Lock {lockId} Create {objId.ToString( "X" )} {obj?.GetType()?.Name} {obj}", cat: te.EventName ); - //*/ - } - catch( Exception ex ) - { - log.info( $"Lock {lockId} Create 0x{intPtrStr} (Could not get object) [{ex.Message}]", cat: te.EventName ); - } - - LogGeneric( te, "| Raw: " ); - - return; - //logDetails = "| Details: "; - } - else if( te.EventName.StartsWith( "Contention/Start" ) ) - { - // EventName="Contention/Start" ContentionFlags="Managed" LockID="0x000000011D015CB8" AssociatedObjectID="0x00000003504554C0" LockOwnerThreadID="0" - // EventName="Contention/Stop" ContentionFlags="Managed" DurationNs="26000" - - var lockId = te.Get( "LockID" ); - var objId = te.Get( "AssociatedObjectID" ); - var threadId = te.Get( "LockOwnerThreadID" ); - - log.info( $"Lock {lockId} {te.Get( "ContentionFlags" )} in thread {threadId} on obj 0x{objId.ToString( "X" )} ", cat: te.EventName ); - - LogGeneric( te, "| Raw: " ); - } - else if( te.EventName.StartsWith( "Contention/Stop" ) ) - { - //var lockId = te.Get( "LockID" ); - //var objId = te.Get( "AssociatedObjectID" ); - //var threadId = te.Get( "LockOwnerThreadID" ); - - log.info( $"Lock {{lockId}} {te.Get( "ContentionFlags" )} Duration {te.Get( "DurationNs" )}ns", cat: te.EventName ); - - LogGeneric( te, "| Raw: " ); - } - else if( te.EventName.StartsWith( "AssemblyLoader/" ) || te.EventName.StartsWith( "Loader/" ) ) - { - //AssemblyLoader/Start AssemblyName AssemblyLoadContext RequestingAssemblyLoadContext AssemblyPath RequestingAssembly - //AssemblyLoader/Stop AssemblyName AssemblyLoadContext RequestingAssemblyLoadContext AssemblyPath RequestingAssembly Success ResultAssemblyName ResultAssemblyPath Cached - //Loader/AssemblyLoad AssemblyID AppDomainID AssemblyFlags FullyQualifiedAssemblyName BindingID - //AssemblyLoader/ResolutionAttempted - // AssemblyName AssemblyLoadContext Result ResultAssemblyName ResultAssemblyPath Stage ErrorMessage - - //Loader/ModuleLoad ModuleID AssemblyID ModuleFlags ModuleILPath ModuleNativePath ManagedPdbSignature ManagedPdbAge ManagedPdbBuildPath - //Loader/DomainModuleLoad ModuleID AssemblyID ModuleFlags ModuleILPath ModuleNativePath AppDomainID - - //AssemblyLoader/KnownPathProbed FilePath Source Result - var appDomainId = te.Get( "AppDomainID" ); - - var asId = te.Get( "AssemblyID" ); - var asName = te.Get( "AssemblyName" ); - var asPath = te.Get( "AssemblyPath" ); - var asLC = te.Get( "AssemblyLoadContext" ); - - var reqAs = te.Get( "RequestingAssembly" ); - var reqAsLC = te.Get( "RequestingAssemblyLoadContext" ); - - var reqAsName = te.Get( "ResultAssemblyName" ); - var reqAsPath = te.Get( "ResultAssemblyPath" ); - - var success = te.Get( "Success" ); - var cached = te.Get( "Cached" ); - var errMsg = te.Get( "ErrorMessage" ); - var stage = te.Get( "Stage" ); - var result = te.Get( "Result" ); - var source = te.Get( "Source" ); - - var modId = te.Get( "ModuleID" ); - var modFlags = te.Get( "ModuleFlags" ); - var modILPath = te.Get( "ModuleILPath" ); - var modNativePath = te.Get( "ModuleNativePath" ); - - var manBuildPath = te.Get( "ManagedPdbBuildPath" ); - var fqaName = te.Get( "FullyQualifiedAssemblyName" ); - var bindingId = te.Get( "BindingID" ); - - } - - - - - - //76 CHARACTERS - // 0 ) - { - Encoding enc = new UnicodeEncoding(false, false, true); - var eventDataUtf16 = enc.GetString( eventData ); - log.debug( $"> {eventDataUtf16}" ); - } - */ - } - else - { - /* - var payloadNames = te.PayloadNames; - var channel = te.Channel; - var formattedMsg = te.FormattedMessage; - var keywords = te.Keywords; - var source = te.Source; - var dump = te.Dump(); - var dynMemberNames = te.GetDynamicMemberNames(); - var dataStart = te.DataStart; - */ - - var eventData = te.EventData(); - - //var eventDataStr = eventData.ToString(); - - Encoding enc = new UnicodeEncoding( false, false, true ); - var eventDataUtf16 = enc.GetString( eventData ); - //var safeEventData = eventDataUtf16.Replace( (char)0, '\n' ); - - var arrEventData = eventDataUtf16.Split( (char)0 ); - var joinedEventData = string.Join( " | ", arrEventData ); - - //log.info( $"{te.FormattedMessage}", cat: catIn ); - log.info( $"{joinedEventData}", cat: catIn ); - } - - - }; - } - - private static ConcurrentDictionary> s_validKeywords = new(); - - private static void LogGeneric( TraceEvent te, string logDetails ) - { - var teStr = te.ToString().Replace( "ClrInstanceID=\"0\"", "" ); - - /* - - */ - - var startIndex = teStr.IndexOf( "EventName" ); - - //log.debug( $"{logDetails} {startIndex} {teStr}", cat: te.EventName ); - - var teStrSlice = teStr.AsSpan( startIndex, teStr.Length - ( startIndex + 1 + 1 ) ); - - log.debug( $"{logDetails}{teStrSlice}", cat: te.EventName ); - - /* - try - { - TraceCallStack? callstack = te.CallStack(); - //if( callstack != null) log.trace( $"| Callstack: {callstack?.ToString()}" ); - if( callstack != null ) - { - log.trace( $"| Callstack:" ); - log.trace( $"| {callstack.Depth}" ); - log.trace( $"| {callstack.CodeAddress}" ); - } - } - catch( Exception ex ) - { - log.debug( $"Caught {ex.Message} while getting the callstack" ); - } - //*/ - } - - public static void PrintRuntimeGCEvents( int processId ) - { - var providers = new List() - { - new EventPipeProvider("Microsoft-Windows-DotNETRuntime", - EventLevel.Verbose, (long)( - TraceKeywords.GC | - TraceKeywords.Contention | - TraceKeywords.Debugger | - TraceKeywords.Exception | - TraceKeywords.GCAllObjectAllocation | - TraceKeywords.GCSampledObjectAllocationHigh | - TraceKeywords.GCSampledObjectAllocationLow | - TraceKeywords.Security | - TraceKeywords.Threading | - TraceKeywords.Type | - TraceKeywords.TypeDiagnostic | - TraceKeywords.WaitHandle | - TraceKeywords.All - ) ) - }; - - var client = new DiagnosticsClient( processId ); - using( EventPipeSession session = client.StartEventPipeSession( providers, false ) ) - { - var source = new EventPipeEventSource( session.EventStream ); - - - source.Clr.GCAllocationTick += OnAllocTick; - - //source.Clr.All += LogCategoryFunc( "clr" ); - - //source.Kernel.All += LogCategoryFunc( "kernel" ); - - // Doesnt seem to be too interesting. - //source.Dynamic.All += LogCategoryFunc( "dynamic" ); - - - try - { - source.Process(); - } - catch( Exception e ) - { - Console.WriteLine( "Error encountered while processing events" ); - Console.WriteLine( e.ToString() ); - } - } - } - - private static void OnAllocTick( GCAllocationTickTraceData data ) - { - } -} - diff --git a/logging/Tracing.cs b/logging/Tracing.cs deleted file mode 100644 index 8b797b7..0000000 --- a/logging/Tracing.cs +++ /dev/null @@ -1,675 +0,0 @@ -using Microsoft.Diagnostics.Symbols; -using Microsoft.Diagnostics.Tracing; -using Microsoft.Diagnostics.Tracing.Etlx; -using Microsoft.Diagnostics.Tracing.Parsers; -using Microsoft.Diagnostics.Tracing.Parsers.Clr; -using Microsoft.Diagnostics.Tracing.Parsers.Kernel; -using Microsoft.Diagnostics.Tracing.Session; -using System; -using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Diagnostics.NETCore.Client; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Diagnostics.Tracing; - -namespace Tracing -{ - /// - /// A configurable, real-time EventPipe trace monitor for cross-platform use. - /// This class uses a fluent builder pattern to attach to a .NET process, - /// configure EventPipe providers, and process events in real-time. - /// - public class EventPipeTraceMonitor : IDisposable - { - private readonly int _pid; - private int _durationSec = 10; - private ClrTraceEventParser.Keywords _clrKeywords = ClrTraceEventParser.Keywords.None; - private bool _monitorCpu = false; - private readonly List _customProviders = new(); - - private Action _userCallback; - private EventPipeSession _session; - private TraceEventSource _traceLogSource; /// CHANGED - private Task _processingTask; - - - private Timer _timer; - - public EventPipeTraceMonitor( int processId ) - { - _pid = processId; - } - - // #################################################################### - // Builder Configuration Methods - // #################################################################### - - public EventPipeTraceMonitor WithDuration( int seconds ) - { - _durationSec = seconds; - return this; - } - - /// - /// Adds a custom EventPipeProvider. - /// - public EventPipeTraceMonitor AddProvider( EventPipeProvider provider ) - { - _customProviders.Add( provider ); - return this; - } - - // #################################################################### - // Event Selection Methods - // #################################################################### - - public EventPipeTraceMonitor MonitorExceptions() - { - // EventPipe requires JIT/Loader/Stack keywords to resolve symbols - _clrKeywords |= ClrTraceEventParser.Keywords.Exception | - ClrTraceEventParser.Keywords.Stack | - ClrTraceEventParser.Keywords.Jit | - ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | - ClrTraceEventParser.Keywords.Loader; - return this; - } - - public EventPipeTraceMonitor MonitorModuleLoads() - { - _clrKeywords |= ClrTraceEventParser.Keywords.Loader | - ClrTraceEventParser.Keywords.Stack | - ClrTraceEventParser.Keywords.Jit | - ClrTraceEventParser.Keywords.Threading | - ClrTraceEventParser.Keywords.PerfTrack | - ClrTraceEventParser.Keywords.JittedMethodILToNativeMap; - return this; - } - - public EventPipeTraceMonitor MonitorCpuSamples() - { - _monitorCpu = true; - return this; - } - - // #################################################################### - // Core Execution Methods - // #################################################################### - - public void Start( Action onEventCallback ) - { - _userCallback = onEventCallback; - var providers = new List( _customProviders ); - - - - - - if( _clrKeywords != ClrTraceEventParser.Keywords.None ) - { - // Add the main CLR provider - providers.Add( new EventPipeProvider( - "Microsoft-Windows-DotNETRuntime", - EventLevel.Informational, - (long)_clrKeywords ) ); - } - - if( _monitorCpu ) - { - // Add the CPU sampler provider - providers.Add( new EventPipeProvider( - "Microsoft-DotNETCore-SampleProfiler", - EventLevel.Informational ) ); - } - - - - - - - if( !providers.Any() ) - { - log.warn( "No trace providers configured. Monitor will not start." ); - return; - } - - try - { - var client = new DiagnosticsClient( _pid ); - log.info( $"Starting EventPipe session on PID {_pid} for {_durationSec}s..." ); - - // Start the session. requestRundown=true is critical - // to get symbols for code JIT-compiled before the session started. - _session = client.StartEventPipeSession( providers, requestRundown: true ); - - // Set up the timer to stop the session - _timer = new Timer( delegate - { - log.info( $"Monitoring duration ({_durationSec} sec) elapsed." ); - Stop(); - }, null, _durationSec * 1000, Timeout.Infinite ); - - // Create the TraceLogSource from the session's event stream - _traceLogSource = new EventPipeEventSource( _session.EventStream ); - // KEEP TraceLog.CreateFromTraceEventSession( _session ); - - // Register callbacks based on requested keywords - if( ( _clrKeywords & ClrTraceEventParser.Keywords.Exception ) != 0 ) - _traceLogSource.Clr.ExceptionStart += OnEvent; - if( ( _clrKeywords & ClrTraceEventParser.Keywords.Loader ) != 0 ) - { - _traceLogSource.Clr.LoaderModuleLoad += OnEvent; - _traceLogSource.Clr.All += OnAllEvents; - - } - //if( _monitorCpu ) - // _traceLogSource.CpuSpeedMHz += OnEvent; - - // Start processing the stream on a background thread. - // .Process() is a blocking call that runs until the stream ends. - _processingTask = Task.Run( () => - { - try - {/* _traceLogSource.Pro;*/ } - catch( Exception ex ) { log.error( $"Trace processing error: {ex.Message}" ); } - log.info( "Event processing finished." ); - } ); - } - catch( Exception ex ) - { - log.error( $"Failed to start EventPipe session: {ex.Message}" ); - } - - - - - } - - private void OnAllEvents( TraceEvent evt ) - { - log.info( $"EVENT: [PID:{evt.ProcessID}] -- {evt.EventName}" ); - } - - public void Stop() - { - if( _session != null ) - { - log.info( "Stopping session..." ); - _timer?.Dispose(); - _timer = null; - - // Stopping the session will end the stream, - // which causes traceLogSource.Process() to return. - _session.Stop(); - _session.Dispose(); - _session = null; - - _traceLogSource?.Dispose(); - _traceLogSource = null; - - // Wait for the processing task to complete - _processingTask?.Wait( 2000 ); - _processingTask = null; - } - } - - public void Dispose() - { - Stop(); - } - - /// - /// Internal event handler. - /// - private void OnEvent( TraceEvent data ) - { - // --- Pre-filtering --- - if( data.Opcode == TraceEventOpcode.DataCollectionStart ) - return; - if( data is ExceptionTraceData exd && exd.ExceptionType.Length == 0 ) - return; - - // --- Pass to user --- - _userCallback( data ); - } - } - - /// - /// A configurable, real-time ETW trace monitor. (Windows-Only) - /// This class uses a fluent builder pattern to configure and run a real-time - /// TraceEventSession, processing events through a user-provided callback. - /// - public class RealTimeTraceMonitor : IDisposable - { - private TraceEventSession _session; - private int _durationSec = 10; - private string _processFilter = null; - private KernelTraceEventParser.Keywords _kernelKeywords = KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process; - private ClrTraceEventParser.Keywords _clrKeywords = ClrTraceEventParser.Keywords.None; - private KernelTraceEventParser.Keywords _kernelStackKeywords = KernelTraceEventParser.Keywords.None; - private bool _enableRundown = false; - private bool _resolveNativeSymbols = false; - private readonly List> _eventRegistrars = new(); - private Action _userCallback; - private SymbolReader _symbolReader; - private readonly object _symbolReaderLock = new(); - private TextWriter _symbolLog = new StringWriter(); - private Timer _timer; - - // #################################################################### - // Builder Configuration Methods - // #################################################################### - - /// - /// Sets the monitoring duration in seconds. - /// - public RealTimeTraceMonitor WithDuration( int seconds ) - { - _durationSec = seconds; - return this; - } - - /// - /// Filters events to only include those from a process whose name contains this string. - /// - public RealTimeTraceMonitor FilterByProcess( string processName ) - { - _processFilter = processName; - return this; - } - - /// - /// Provides a TextWriter for detailed symbol lookup messages. - /// - public RealTimeTraceMonitor WithSymbolLog( TextWriter logWriter ) - { - _symbolLog = logWriter; - return this; - } - - // #################################################################### - // Event Selection Methods - // #################################################################### - - /// - /// Enables monitoring for CLR Exceptions (ExceptionStart). - /// Automatically enables JIT, Loader, and Stack keywords required for symbol resolution. - /// - public RealTimeTraceMonitor MonitorExceptions() - { - _clrKeywords |= ClrTraceEventParser.Keywords.Exception | - ClrTraceEventParser.Keywords.Stack | - ClrTraceEventParser.Keywords.Jit | - ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | - ClrTraceEventParser.Keywords.Loader; - _enableRundown = true; - _resolveNativeSymbols = true; // Native symbol resolution is often needed for full exception stacks - _eventRegistrars.Add( source => source.Clr.ExceptionStart += OnEvent ); - return this; - } - - /// - /// Enables monitoring for CLR Module Loads (LoaderModuleLoad). - /// Automatically enables JIT, Loader, and Stack keywords. - /// - public RealTimeTraceMonitor MonitorModuleLoads() - { - _clrKeywords |= ClrTraceEventParser.Keywords.Loader | - ClrTraceEventParser.Keywords.Stack | - ClrTraceEventParser.Keywords.Jit | - ClrTraceEventParser.Keywords.JittedMethodILToNativeMap; - _enableRundown = true; - _eventRegistrars.Add( source => source.Clr.LoaderModuleLoad += OnEvent ); - return this; - } - - /// - /// Enables monitoring for CPU Samples (PerfInfoSample). - /// - public RealTimeTraceMonitor MonitorCpuSamples() - { - _kernelKeywords |= KernelTraceEventParser.Keywords.Profile; - _kernelStackKeywords |= KernelTraceEventParser.Keywords.Profile; - _resolveNativeSymbols = true; // CPU samples require native symbol resolution - _eventRegistrars.Add( source => source.Kernel.PerfInfoSample += OnEvent ); - return this; - } - - /// - /// Enables monitoring for arbitrary Kernel events. - /// - /// Kernel keywords to enable. - /// Keywords to collect stacks for. - public RealTimeTraceMonitor MonitorKernelEvents( KernelTraceEventParser.Keywords events, KernelTraceEventParser.Keywords stackEvents = KernelTraceEventParser.Keywords.None ) - { - _kernelKeywords |= events; - _kernelStackKeywords |= stackEvents; - if( stackEvents != KernelTraceEventParser.Keywords.None ) - { - _resolveNativeSymbols = true; - } - return this; - } - - /// - /// Enables monitoring for arbitrary CLR events. - /// Note: For complex events, prefer the specific Monitor* methods. - /// - /// CLR keywords to enable. - public RealTimeTraceMonitor MonitorClrEvents( ClrTraceEventParser.Keywords events ) - { - _clrKeywords |= events; - if( ( events & ClrTraceEventParser.Keywords.Stack ) != 0 ) - { - _clrKeywords |= ClrTraceEventParser.Keywords.Jit | - ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | - ClrTraceEventParser.Keywords.Loader; - _enableRundown = true; - _resolveNativeSymbols = true; - } - return this; - } - - // #################################################################### - // Core Execution Methods - // #################################################################### - - /// - /// Starts the monitoring session and blocks until the duration expires or Ctrl+C is pressed. - /// - /// The callback to execute for each filtered event. - public void Start( Action onEventCallback ) - { - if( !OperatingSystem.IsWindows() ) - { - log.error( $"{nameof( RealTimeTraceMonitor )} is only supported on Windows (ETW)." ); - return; - } - - _userCallback = onEventCallback; - log.info( $"Starting {nameof( RealTimeTraceMonitor )} for {_durationSec} seconds." ); - - if( TraceEventSession.GetActiveSessionNames().Contains( "TraceLogMonitorSession" ) ) - { - log.warn( "Session 'TraceLogMonitorSession' is already active. Attaching." ); - } - - try - { - using( _session = new TraceEventSession( "TraceLogMonitorSession", TraceEventSessionOptions.Attach ) ) - { - // Set up Ctrl-C to stop the session - Console.CancelKeyPress += ( object sender, ConsoleCancelEventArgs cancelArgs ) => - { - cancelArgs.Cancel = true; - Stop(); - }; - - SetupProviders(); - SetupSymbolReader(); - - using( var traceLogSource = TraceLog.CreateFromTraceEventSession( _session ) ) - { - // Register all requested event callbacks - foreach( var registrar in _eventRegistrars ) - { - registrar( traceLogSource ); - } - - // Set up a timer to stop processing after the duration - _timer = new Timer( delegate ( object state ) - { - log.info( $"Monitoring duration ({_durationSec} sec) elapsed." ); - Stop(); - }, null, _durationSec * 1000, Timeout.Infinite ); - - log.info( "Processing events..." ); - traceLogSource.Process(); - log.info( "Event processing finished." ); - } - } - } - catch( Exception ex ) - { - log.error( $"Failed to start trace session: {ex.Message}" ); - log.error( "This often requires Admin privileges." ); - } - finally - { - _session = null; - } - } - - /// - /// Stops the monitoring session. - /// - public void Stop() - { - if( _session != null ) - { - log.info( "Stopping session..." ); - _session.Dispose(); - _session = null; - } - if( _timer != null ) - { - _timer.Dispose(); - _timer = null; - } - } - - public void Dispose() - { - Stop(); - } - - // #################################################################### - // Private Helper Methods - // #################################################################### - - private void SetupProviders() - { - log.info( $"Enabling Kernel Providers with keywords: {_kernelKeywords}" ); - try - { - _session.EnableKernelProvider( _kernelKeywords, _kernelStackKeywords ); - } - catch( Exception ex ) - { - log.error( $"Failed to enable Kernel provider: {ex}" ); - } - - if( _clrKeywords != ClrTraceEventParser.Keywords.None ) - { - log.info( $"Enabling CLR Provider with keywords: {_clrKeywords}" ); - try - { - _session.EnableProvider( - ClrTraceEventParser.ProviderGuid, - TraceEventLevel.Informational, - (ulong)_clrKeywords ); - } - catch( Exception ex ) - { - log.error( $"Failed to enable CLR provider: {ex}" ); - } - } - - if( _enableRundown ) - { - // Remove keywords not relevant for rundown - var rundownKeywords = ( _clrKeywords & ~( ClrTraceEventParser.Keywords.Exception | ClrTraceEventParser.Keywords.Stack ) ); - rundownKeywords |= ClrTraceEventParser.Keywords.StartEnumeration; - - log.info( $"Enabling CLR Rundown Provider with keywords: {rundownKeywords}" ); - try - { - _session.EnableProvider( - ClrRundownTraceEventParser.ProviderGuid, - TraceEventLevel.Informational, - (ulong)rundownKeywords ); - } - catch( Exception ex ) - { - log.error( $"Failed to enable CLR Rundown provider: {ex}" ); - } - } - } - - private void SetupSymbolReader() - { - if( _resolveNativeSymbols ) - { - log.info( "Setting up SymbolReader for native symbol resolution." ); - var symbolPath = new SymbolPath( SymbolPath.SymbolPathFromEnvironment ).Add( SymbolPath.MicrosoftSymbolServerPath ); - _symbolReader = new SymbolReader( _symbolLog, symbolPath.ToString() ); - // Allow reading PDBs from "unsafe" locations (next to EXE) - _symbolReader.SecurityCheck = ( path => true ); - } - } - - /// - /// Primary event handler. - /// This method is called from multiple threads and must be thread-safe. - /// - private void OnEvent( TraceEvent data ) - { - // --- Pre-filtering --- - if( data.Opcode == TraceEventOpcode.DataCollectionStart ) - return; - if( data is ExceptionTraceData exd && exd.ExceptionType.Length == 0 ) - return; - - // --- Process filter --- - if( _processFilter != null && !data.ProcessName.Contains( _processFilter, StringComparison.OrdinalIgnoreCase ) ) - { - return; - } - - // --- Symbol Resolution --- - var callStack = data.CallStack(); - if( callStack != null && _resolveNativeSymbols ) - { - ResolveNativeStack( callStack ); - } - - // --- Pass to user --- - _userCallback( data ); - } - - /// - /// Resolves native symbols for a given call stack. - /// This method locks the SymbolReader, as it is not thread-safe. - /// - private void ResolveNativeStack( TraceCallStack callStack ) - { - if( _symbolReader == null ) - return; - - lock( _symbolReaderLock ) - { - while( callStack != null ) - { - var codeAddress = callStack.CodeAddress; - if( codeAddress.Method == null ) - { - var moduleFile = codeAddress.ModuleFile; - if( moduleFile != null ) - { - codeAddress.CodeAddresses.LookupSymbolsForModule( _symbolReader, moduleFile ); - } - } - callStack = callStack.Caller; - } - } - } - } - - /// - /// Example implementation of the RealTimeTraceMonitor utility. - /// - internal class TraceLogMonitor - { - public static void Run() - { - log.info( "******************** RealTimeTraceLog DEMO ********************" ); - - // Define a thread-safe callback to process events - Action printCallback = ( TraceEvent data ) => - { - // log.info is assumed to be thread-safe - // Note: EventPipe data does not have ProcessName, as it's scoped to the process. - log.info( $"EVENT: [PID:{data.ProcessID}] -- {data.EventName}" ); - - var stack = data.CallStack(); - if( stack != null ) - { - log.info( $"CALLSTACK: {stack}" ); - } - }; - - // Run an exception generator in the background - Task.Factory.StartNew( delegate - { - Thread.Sleep( 3000 ); - ThrowException(); - } ); - - try - { - if( OperatingSystem.IsWindows() ) - { - log.info( "Windows detected. Using ETW-based RealTimeTraceMonitor." ); - using( var monitor = new RealTimeTraceMonitor() ) - { - monitor.WithDuration( 10 ) - .MonitorExceptions() - .MonitorModuleLoads() - .Start( printCallback ); - } - } - else - { - log.info( "Non-Windows detected. Using EventPipe-based EventPipeTraceMonitor." ); - int currentPid = Process.GetCurrentProcess().Id; - using( var monitor = new EventPipeTraceMonitor( currentPid ) ) - { - monitor.WithDuration( 10 ) - .MonitorExceptions() - .MonitorModuleLoads() - //.MonitorCpuSamples() // Note: EventPipe CPU sampling is very noisy - .Start( printCallback ); - } - } - } - catch( Exception ex ) - { - log.error( $"Monitoring failed: {ex}" ); - } - - log.info( "Finished monitoring." ); - } - - // --- Exception generation helpers --- - - [System.Runtime.CompilerServices.MethodImpl( System.Runtime.CompilerServices.MethodImplOptions.NoInlining )] - private static void ThrowException() - { - ThrowException1(); - } - - [System.Runtime.CompilerServices.MethodImpl( System.Runtime.CompilerServices.MethodImplOptions.NoInlining )] - private static void ThrowException1() - { - log.info( "Causing an exception to happen so a CLR Exception Start event will be generated." ); - try - { - throw new Exception( "This is a test exception thrown to generate a CLR event" ); - } - catch( Exception ) { } - } - } -} diff --git a/mod/Modules.cs b/mod/Modules.cs deleted file mode 100644 index af96caa..0000000 --- a/mod/Modules.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -//using System.Threading.Tasks; -using System.Diagnostics; -using System.Reflection; - -namespace mod -{ - - [Serializable] - public class Config : lib.Config - { - public String name = "Generic"; - } - - public class View - { - } - - public class Base - { - public Config Cfg { get { return m_cfg; } } - - public Base( Config cfg ) - { - m_cfg = cfg; - } - - private Config m_cfg; - } - - - [Serializable] - public class FluidConfig : Config - { - public String type = "none"; - } - - - public class FluidBase : Base - { - public new FluidConfig Cfg { get { return (FluidConfig)base.Cfg; } } - - public FluidBase( FluidConfig cfg ) - : base( cfg ) - { - } - } - - - - - - - - - - - - - - - - - [Serializable] - public class SystemConfig : Config - { - public String type = "none"; - } - - - public class System - { - public SystemConfig Cfg { get { return m_cfg; } } - - public System( SystemConfig cfg ) - { - m_cfg = cfg; - } - - private SystemConfig m_cfg; - } - - -} diff --git a/net/Conn.cs b/net/Conn.cs deleted file mode 100644 index 31d3637..0000000 --- a/net/Conn.cs +++ /dev/null @@ -1,198 +0,0 @@ - -#nullable enable - -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 where T : IFormatter - { - - T getInstance(); - - - - } - - - public class NewEveryCall : ISerDes where T : IFormatter, new() - { - public T getInstance() - { - return new T(); - } - } - - public class Conn - { - public static int BufferSize = 2048; - } - - - public class Conn : Conn - where T : IFormatter, new() - where TInst : ISerDes, 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 receiveObject() - { - return receiveObject( Stream ); - } - - public object receiveObject( Stream stream ) - { - object obj = new object(); - - 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 receive( 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; - } - - - -} diff --git a/net/FSM.cs b/net/FSM.cs deleted file mode 100644 index eb778bc..0000000 --- a/net/FSM.cs +++ /dev/null @@ -1,66 +0,0 @@ - - -using System; - - - -namespace net; - - - -public class Context -{ - -} - -public class State - where T : State - where CTX : Context -{ - virtual public void onEnter( CTX ctx, State oldState ) - { - } - - virtual public void onExit( CTX ctx, State newState ) - { - } -} - -/* -public class PotentialState : State - where T : State - where CTX : Context -{ - virtual public void onEnter(CTX ctx, State oldState) - { - } - - virtual public void onExit(CTX ctx, State newState) - { - } -} -*/ - - -public class FSM - where T : FSM - where CTX : Context - where ST : State -{ - public CTX Context { get; private set; } - public ST State { get; private set; } - - public FSM( CTX context, ST state ) - { - Context = context; - State = state; - } - - public void Transition( ST newState ) - { - } - - - -} - diff --git a/net/NetMsg.cs b/net/NetMsg.cs deleted file mode 100644 index 5e77ecb..0000000 --- a/net/NetMsg.cs +++ /dev/null @@ -1,103 +0,0 @@ -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; - } - - - - - -} diff --git a/prof/AddressStack.cs b/prof/AddressStack.cs deleted file mode 100644 index bf57554..0000000 --- a/prof/AddressStack.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; - -namespace Tracing -{ - public class AddressStack - { - // the first frame is the address of the last called method - private readonly List _stack; - - public AddressStack( int capacity ) - { - _stack = new List( capacity ); - } - - // No need to override GetHashCode because we don't want to use it as a key in a dictionary - public override bool Equals( object obj ) - { - if( obj == null ) - return false; - - var stack = obj as AddressStack; - if( stack == null ) - return false; - - var frameCount = _stack.Count; - if( frameCount != stack._stack.Count ) - return false; - - for( int i = 0; i < frameCount; i++ ) - { - if( _stack[i] != stack._stack[i] ) - return false; - } - - return true; - } - - public override int GetHashCode() => _stack.GetHashCode(); - - public IReadOnlyList Stack => _stack; - - public void AddFrame( ulong address ) - { - _stack.Add( address ); - } - } -} diff --git a/prof/Memory.cs b/prof/Memory.cs deleted file mode 100644 index 0cce0e0..0000000 --- a/prof/Memory.cs +++ /dev/null @@ -1,314 +0,0 @@ -using Microsoft.Diagnostics.Tracing; -using Microsoft.Diagnostics.Tracing.Parsers; -using Microsoft.Diagnostics.Tracing.Parsers.Clr; -using Microsoft.Diagnostics.Tracing.Parsers.Kernel; -using Microsoft.Diagnostics.Tracing.Session; -using ProfilerHelpers; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; - -namespace Tracing -{ - public class Memory - { - private readonly TraceEventSession _session; - private readonly PerProcessProfilingState _processes; - - // because we are not interested in self monitoring - private readonly int _currentPid; - - private int _started = 0; - - public Memory( TraceEventSession session, PerProcessProfilingState processes ) - { - _session = session; - _processes = processes; - _currentPid = Process.GetCurrentProcess().Id; - } - - public async Task StartAsync( bool allAllocations ) - { - if( Interlocked.CompareExchange( ref _started, 1, 0 ) == 1 ) - { - throw new InvalidOperationException( "Impossible to start profiling more than once." ); - } - - await Task.Factory.StartNew( () => - { - using( _session ) - { - log.info( $"SetupProviders" ); - SetupProviders( _session, allAllocations ); - - log.info( $"SetupListeners" ); - SetupListeners( _session.Source ); - - log.info( $"Source.Process()" ); - while( _session.Source.Process() ) - { - Task.Delay( 1 ); - } - - log.info( $"Done" ); - } - } ); - } - - private void SetupProviders( TraceEventSession session, bool noSampling ) - { - // Note: the kernel provider MUST be the first provider to be enabled - // If the kernel provider is not enabled, the callstacks for CLR events are still received - // but the symbols are not found (except for the application itself) - // Maybe a TraceEvent implementation details triggered when a module (image) is loaded - var success = true; - - //* - log.info( $"EnableKernelProvider" ); - success = log.var( session.EnableKernelProvider( KernelTraceEventParser.Keywords.None | - // KernelTraceEventParser.Keywords.ImageLoad | - // KernelTraceEventParser.Keywords.Process - 0, - KernelTraceEventParser.Keywords.None - ) ); - log.info( $"EnableKernelProvider {success}" ); - //*/ - - // The CLR source code indicates that the provider must be set before the monitored application starts - // Note: no real difference between High and Low - ClrTraceEventParser.Keywords eventsKeyword = noSampling - ? ClrTraceEventParser.Keywords.GCSampledObjectAllocationLow | ClrTraceEventParser.Keywords.GCSampledObjectAllocationHigh - : ClrTraceEventParser.Keywords.GCSampledObjectAllocationLow - ; - - log.info( $"EnableProvider" ); - success = log.var( session.EnableProvider( - ClrTraceEventParser.ProviderGuid, - TraceEventLevel.Verbose, // this is needed in order to receive GCSampledObjectAllocation event - (ulong)( - - eventsKeyword | - - // required to receive the BulkType events that allows - // mapping between the type ID received in the allocation events - ClrTraceEventParser.Keywords.GCHeapAndTypeNames | - ClrTraceEventParser.Keywords.Type | - - // events related to JITed methods - ClrTraceEventParser.Keywords.Jit | // Turning on JIT events is necessary to resolve JIT compiled code - ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | // This is needed if you want line number information in the stacks - ClrTraceEventParser.Keywords.Loader | // You must include loader events as well to resolve JIT compiled code. - - // this is mandatory to get the callstacks in each CLR event payload. - //ClrTraceEventParser.Keywords.Stack | - - 0 - ) - ) ); - log.info( $"EnableProvider {success}" ); - - - // Note: ClrRundown is not needed because only new processes will be monitored - } - - private void SetupListeners( ETWTraceEventSource source ) - { - // register for high and low keyword - // if both are set, each allocation will trigger an event (beware performance issues...) - source.Clr.GCSampledObjectAllocation += OnSampleObjectAllocation; - - // required to receive the mapping between type ID (received in GCSampledObjectAllocation) - // and their name (received in TypeBulkType) - source.Clr.TypeBulkType += OnTypeBulkType; - - // messages to get callstacks - // the correlation seems to be as "simple" as taking the last event on the same thread - source.Clr.ClrStackWalk += OnClrStackWalk; - - // needed to get JITed method details - source.Clr.MethodLoadVerbose += OnMethodDetails; - source.Clr.MethodDCStartVerboseV2 += OnMethodDetails; - - source.Clr.ContentionLockCreated += OnLockCreated; - source.Clr.ContentionStart += OnLockStart; - source.Clr.ContentionStop += OnLockStop; - - // get notified when a module is load to map the corresponding symbols - source.Kernel.ImageLoad += OnImageLoad; - } - - private void OnLockCreated( ContentionLockCreatedTraceData data ) - { - } - - private void OnLockStart( ContentionStartTraceData data ) - { - - } - - private void OnLockStop( ContentionStopTraceData data ) - { - } - - private void OnImageLoad( ImageLoadTraceData data ) - { - if( FilterOutEvent( data ) ) - return; - - GetProcessMethods( data.ProcessID ).AddModule( data.FileName, data.ImageBase, data.ImageSize ); - - log.info( $"{data.ProcessID}.{data.ThreadID} --> {data.FileName}" ); - } - - private void OnMethodDetails( MethodLoadUnloadVerboseTraceData data ) - { - if( FilterOutEvent( data ) ) - return; - - // care only about jitted methods - if( !data.IsJitted ) - return; - - var method = GetProcessMethods( data.ProcessID ) - .Add( data.MethodStartAddress, data.MethodSize, data.MethodNamespace, data.MethodName, data.MethodSignature ); - - log.info( $"0x{data.MethodStartAddress.ToString( "x12" )} - {data.MethodSize,6} | {data.MethodName}" ); - } - - private MethodStore GetProcessMethods( int pid ) - { - if( !_processes.Methods.TryGetValue( pid, out var methods ) ) - { - methods = new MethodStore( pid ); - _processes.Methods[pid] = methods; - } - return methods; - } - - - private void OnSampleObjectAllocation( GCSampledObjectAllocationTraceData data ) - { - if( FilterOutEvent( data ) ) - return; - - var typeName = GetProcessTypeName( data.ProcessID, data.TypeID ); - if( data.TotalSizeForTypeSample >= 85000 ) - { - var message = $"{data.ProcessID}.{data.ThreadID} - {data.TimeStampRelativeMSec,12} | Alloc {GetProcessTypeName( data.ProcessID, data.TypeID )} ({data.TotalSizeForTypeSample})"; - log.info( message ); - } - GetProcessAllocations( data.ProcessID ) - .AddAllocation( - data.ThreadID, - (ulong)data.TotalSizeForTypeSample, - (ulong)data.ObjectCountForTypeSample, - typeName - ); - } - - private ProcessAllocations GetProcessAllocations( int pid ) - { - if( !_processes.Allocations.TryGetValue( pid, out var allocations ) ) - { - allocations = new ProcessAllocations( pid ); - _processes.Allocations[pid] = allocations; - } - return allocations; - } - - private void OnClrStackWalk( ClrStackWalkTraceData data ) - { - if( FilterOutEvent( data ) ) - return; - - //var message = $"{data.ProcessID}.{data.ThreadID} - {data.TimeStampRelativeMSec,12} | {data.FrameCount} frames"; - //log.info(message); - - var callstack = BuildCallStack( data ); - GetProcessAllocations( data.ProcessID ).AddStack( data.ThreadID, callstack ); - //DumpStack(data); - } - - private AddressStack BuildCallStack( ClrStackWalkTraceData data ) - { - var length = data.FrameCount; - AddressStack stack = new AddressStack( length ); - - // frame 0 is the last frame of the stack (i.e. last called method) - for( int i = 0; i < length; i++ ) - { - stack.AddFrame( data.InstructionPointer( i ) ); - } - - return stack; - } - - private void DumpStack( ClrStackWalkTraceData data ) - { - var methods = GetProcessMethods( data.ProcessID ); - for( int i = 0; i < data.FrameCount; i++ ) - { - var address = data.InstructionPointer( i ); - log.info( methods.GetFullName( address ) ); - } - log.info( $"" ); - } - - private void OnTypeBulkType( GCBulkTypeTraceData data ) - { - if( FilterOutEvent( data ) ) - return; - - ProcessTypeMapping mapping = GetProcessTypesMapping( data.ProcessID ); - for( int currentType = 0; currentType < data.Count; currentType++ ) - { - GCBulkTypeValues value = data.Values( currentType ); - mapping[value.TypeID] = value.TypeName; - } - } - - private ProcessTypeMapping GetProcessTypesMapping( int pid ) - { - ProcessTypeMapping mapping; - if( !_processes.Types.TryGetValue( pid, out mapping ) ) - { - AssociateProcess( pid ); - - mapping = new ProcessTypeMapping( pid ); - _processes.Types[pid] = mapping; - } - return mapping; - } - - private void AssociateProcess( int pid ) - { - try - { - _processes.Names[pid] = Process.GetProcessById( pid ).ProcessName; - } - catch( Exception ) - { - log.info( $"? {pid}" ); - // we might not have access to the process - } - } - - private string GetProcessTypeName( int pid, ulong typeID ) - { - if( !_processes.Types.TryGetValue( pid, out var mapping ) ) - { - return typeID.ToString(); - } - - var name = mapping[typeID]; - return string.IsNullOrEmpty( name ) ? typeID.ToString() : name; - } - - private bool FilterOutEvent( TraceEvent data ) - { - return ( data.ProcessID == _currentPid ); - } - } -} diff --git a/prof/MemoryPipe.cs b/prof/MemoryPipe.cs deleted file mode 100644 index 01314d7..0000000 --- a/prof/MemoryPipe.cs +++ /dev/null @@ -1,393 +0,0 @@ -using Microsoft.Diagnostics.NETCore.Client; -using Microsoft.Diagnostics.Tracing; -using Microsoft.Diagnostics.Tracing.Etlx; -using Microsoft.Diagnostics.Tracing.Parsers; -using Microsoft.Diagnostics.Tracing.Parsers.Clr; -using Microsoft.Diagnostics.Tracing.Parsers.Kernel; -using Microsoft.Diagnostics.Tracing.Session; -using ProfilerHelpers; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.Tracing; -using System.Threading; -using System.Threading.Tasks; -using TraceKeywords = Microsoft.Diagnostics.Tracing.Parsers.ClrTraceEventParser.Keywords; - -namespace Tracing -{ - public class MemoryPipe - { - private readonly DiagnosticsClient _client; - private readonly PerProcessProfilingState _processes; - - // because we are not interested in self monitoring - private readonly int _currentPid; - - private int _started = 0; - - Thread _thread; - - public MemoryPipe() - { - - _currentPid = Process.GetCurrentProcess().Id; - - _client = new DiagnosticsClient( _currentPid ); - - _processes = new(); - - - var threadStart = new ThreadStart( () => Start( true ) ); - _thread = new Thread( threadStart ); - _thread.Start(); - - - } - - //public async Task StartAsync( bool allAllocations ) - public void Start( bool allAllocations ) - { - if( Interlocked.CompareExchange( ref _started, 1, 0 ) == 1 ) - { - throw new InvalidOperationException( "Impossible to start profiling more than once." ); - } - - log.info( $"Starting MemoryPipe on thread {Thread.CurrentThread.ManagedThreadId}" ); - - var providers = new List() - { - new EventPipeProvider("Microsoft-Windows-DotNETRuntime", - EventLevel.Verbose, (long)( - TraceKeywords.GC | - TraceKeywords.Contention | - TraceKeywords.Debugger | - TraceKeywords.Exception | - TraceKeywords.GCAllObjectAllocation | - TraceKeywords.GCSampledObjectAllocationHigh | - TraceKeywords.GCSampledObjectAllocationLow | - TraceKeywords.Security | - TraceKeywords.Threading | - TraceKeywords.Type | - TraceKeywords.TypeDiagnostic | - TraceKeywords.WaitHandle | - TraceKeywords.All - ) ) - }; - - - //await Task.Factory.StartNew( () => { - using EventPipeSession session = _client.StartEventPipeSession( providers, false ); - - //log.info( $"SetupProviders" ); - //SetupProviders( session, allAllocations ); - var source = new EventPipeEventSource( session.EventStream ); - - - log.info( $"SetupListeners" ); - SetupListeners( source ); - - log.info( $"Run Process" ); - source.Process(); - - log.info( $"Done" ); - //} ); - } - - private void SetupProviders( TraceEventSession session, bool noSampling ) - { - // Note: the kernel provider MUST be the first provider to be enabled - // If the kernel provider is not enabled, the callstacks for CLR events are still received - // but the symbols are not found (except for the application itself) - // Maybe a TraceEvent implementation details triggered when a module (image) is loaded - var success = true; - - //* - log.info( $"EnableKernelProvider" ); - success = log.var( session.EnableKernelProvider( KernelTraceEventParser.Keywords.None | - // KernelTraceEventParser.Keywords.ImageLoad | - // KernelTraceEventParser.Keywords.Process - 0, - KernelTraceEventParser.Keywords.None - ) ); - log.info( $"EnableKernelProvider {success}" ); - //*/ - - // The CLR source code indicates that the provider must be set before the monitored application starts - // Note: no real difference between High and Low - ClrTraceEventParser.Keywords eventsKeyword = noSampling - ? ClrTraceEventParser.Keywords.GCSampledObjectAllocationLow | ClrTraceEventParser.Keywords.GCSampledObjectAllocationHigh - : ClrTraceEventParser.Keywords.GCSampledObjectAllocationLow - ; - - log.info( $"EnableProvider" ); - success = log.var( session.EnableProvider( - ClrTraceEventParser.ProviderGuid, - TraceEventLevel.Verbose, // this is needed in order to receive GCSampledObjectAllocation event - (ulong)( - - eventsKeyword | - - // required to receive the BulkType events that allows - // mapping between the type ID received in the allocation events - ClrTraceEventParser.Keywords.GCHeapAndTypeNames | - ClrTraceEventParser.Keywords.Type | - - // events related to JITed methods - ClrTraceEventParser.Keywords.Jit | // Turning on JIT events is necessary to resolve JIT compiled code - ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | // This is needed if you want line number information in the stacks - ClrTraceEventParser.Keywords.Loader | // You must include loader events as well to resolve JIT compiled code. - - // this is mandatory to get the callstacks in each CLR event payload. - //ClrTraceEventParser.Keywords.Stack | - - 0 - ) - ) ); - log.info( $"EnableProvider {success}" ); - - - // Note: ClrRundown is not needed because only new processes will be monitored - } - - private void SetupListeners( EventPipeEventSource source ) - { - // register for high and low keyword - // if both are set, each allocation will trigger an event (beware performance issues...) - //source.Clr.GCSampledObjectAllocation += OnSampleObjectAllocation; - source.Clr.GCAllocationTick += OnAllocTick; - - // required to receive the mapping between type ID (received in GCSampledObjectAllocation) - // and their name (received in TypeBulkType) - source.Clr.TypeBulkType += OnTypeBulkType; - - // messages to get callstacks - // the correlation seems to be as "simple" as taking the last event on the same thread - source.Clr.ClrStackWalk += OnClrStackWalk; - - // needed to get JITed method details - source.Clr.MethodLoadVerbose += OnMethodDetails; - source.Clr.MethodDCStartVerboseV2 += OnMethodDetails; - - source.Clr.ContentionLockCreated += OnLockCreated; - source.Clr.ContentionStart += OnLockStart; - source.Clr.ContentionStop += OnLockStop; - - // get notified when a module is load to map the corresponding symbols - source.Kernel.ImageLoad += OnImageLoad; - } - - private void OnAllocTick( GCAllocationTickTraceData data ) - { - if( FilterOutEvent( data ) ) - return; - - //log.info( $"*** RAW: {data}" ); - - //var callStack = data.CallStack(); - //var caller = callStack.Caller; - - //log.info( $"Call stack {callStack}" ); - - var thisThreadId = Thread.CurrentThread.ManagedThreadId; - - var typeName = GetProcessTypeName( data.ProcessID, data.TypeID ); - //if( data.TotalSizeForTypeSample >= 85000 ) - { - var message = $"{data.ThreadID}/{thisThreadId} Alloc {data.ObjectSize,8} at 0x{data.Address:0000000000000000} in {data.TypeName} (or {GetProcessTypeName( data.ProcessID, data.TypeID )}) "; - log.info( message ); - } - GetProcessAllocations( data.ProcessID ) - .AddAllocation( - data.ThreadID, - (ulong)data.ObjectSize, - (ulong)1, - typeName - ); - } - - private void OnLockCreated( ContentionLockCreatedTraceData data ) - { - log.info( $"{data}" ); - } - - private void OnLockStart( ContentionStartTraceData data ) - { - log.info( $"{data}" ); - } - - private void OnLockStop( ContentionStopTraceData data ) - { - log.info( $"{data}" ); - } - - private void OnImageLoad( ImageLoadTraceData data ) - { - //if( FilterOutEvent( data ) ) - // return; - - log.info( $"{data}" ); - - //GetProcessMethods( data.ProcessID ).AddModule( data.FileName, data.ImageBase, data.ImageSize ); - - log.info( $"{data.ProcessID}.{data.ThreadID} --> {data.FileName}" ); - } - - private void OnMethodDetails( MethodLoadUnloadVerboseTraceData data ) - { - //if( FilterOutEvent( data ) ) - // return; - - //log.info( $"{data}" ); - - // care only about jitted methods - if( !data.IsJitted ) - return; - - var method = GetProcessMethods( data.ProcessID ) - .Add( data.MethodStartAddress, data.MethodSize, data.MethodNamespace, data.MethodName, data.MethodSignature ); - - //log.info( $"0x{data.MethodStartAddress.ToString( "x12" )} - {data.MethodSize,6} | {data.MethodName}" ); - } - - private MethodStore GetProcessMethods( int pid ) - { - if( !_processes.Methods.TryGetValue( pid, out var methods ) ) - { - methods = new MethodStore( pid ); - _processes.Methods[pid] = methods; - } - return methods; - } - - - private void OnSampleObjectAllocation( GCSampledObjectAllocationTraceData data ) - { - if( FilterOutEvent( data ) ) - return; - - //log.info( $"{data}" ); - - var typeName = GetProcessTypeName( data.ProcessID, data.TypeID ); - //if( data.TotalSizeForTypeSample >= 85000 ) - { - var message = $"{data.ProcessID}.{data.ThreadID} - {data.TimeStampRelativeMSec,12} | Alloc {GetProcessTypeName( data.ProcessID, data.TypeID )} ({data.TotalSizeForTypeSample})"; - log.info( message ); - } - GetProcessAllocations( data.ProcessID ) - .AddAllocation( - data.ThreadID, - (ulong)data.TotalSizeForTypeSample, - (ulong)data.ObjectCountForTypeSample, - typeName - ); - } - - private ProcessAllocations GetProcessAllocations( int pid ) - { - if( !_processes.Allocations.TryGetValue( pid, out var allocations ) ) - { - allocations = new ProcessAllocations( pid ); - _processes.Allocations[pid] = allocations; - } - return allocations; - } - - private void OnClrStackWalk( ClrStackWalkTraceData data ) - { - var message = $"{data.ProcessID}.{data.ThreadID} - {data.TimeStampRelativeMSec,12} | {data.FrameCount} frames"; - log.info( message ); - - var callstack = BuildCallStack( data ); - GetProcessAllocations( data.ProcessID ).AddStack( data.ThreadID, callstack ); - //DumpStack(data); - } - - private AddressStack BuildCallStack( ClrStackWalkTraceData data ) - { - //log.info( $"{data}" ); - - var length = data.FrameCount; - AddressStack stack = new AddressStack( length ); - - // frame 0 is the last frame of the stack (i.e. last called method) - for( int i = 0; i < length; i++ ) - { - stack.AddFrame( data.InstructionPointer( i ) ); - } - - return stack; - } - - private void DumpStack( ClrStackWalkTraceData data ) - { - if( FilterOutEvent( data ) ) - return; - - log.info( $"{data}" ); - - var methods = GetProcessMethods( data.ProcessID ); - for( int i = 0; i < data.FrameCount; i++ ) - { - var address = data.InstructionPointer( i ); - log.info( methods.GetFullName( address ) ); - } - log.info( $"" ); - } - - private void OnTypeBulkType( GCBulkTypeTraceData data ) - { - - ProcessTypeMapping mapping = GetProcessTypesMapping( data.ProcessID ); - for( int currentType = 0; currentType < data.Count; currentType++ ) - { - GCBulkTypeValues value = data.Values( currentType ); - mapping[value.TypeID] = value.TypeName; - //log.info( $"{value}" ); - } - } - - private ProcessTypeMapping GetProcessTypesMapping( int pid ) - { - ProcessTypeMapping mapping; - if( !_processes.Types.TryGetValue( pid, out mapping ) ) - { - AssociateProcess( pid ); - - mapping = new ProcessTypeMapping( pid ); - _processes.Types[pid] = mapping; - } - return mapping; - } - - private void AssociateProcess( int pid ) - { - try - { - _processes.Names[pid] = Process.GetProcessById( pid ).ProcessName; - } - catch( Exception ) - { - log.info( $"? {pid}" ); - // we might not have access to the process - } - } - - private string GetProcessTypeName( int pid, ulong typeID ) - { - if( !_processes.Types.TryGetValue( pid, out var mapping ) ) - { - return typeID.ToString(); - } - - var name = mapping[typeID]; - return string.IsNullOrEmpty( name ) ? typeID.ToString() : name; - } - - private bool FilterOutEvent( TraceEvent data ) - { - return data.ThreadID == Thread.CurrentThread.ManagedThreadId; - //return false; // ( data.ProcessID == _currentPid ); - } - } -} diff --git a/prof/MethodInfo.cs b/prof/MethodInfo.cs deleted file mode 100644 index 0facada..0000000 --- a/prof/MethodInfo.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; - -namespace ProfilerHelpers -{ - public class MethodInfo - { - private readonly ulong _startAddress; - private readonly int _size; - private readonly string _fullName; - - internal MethodInfo( ulong startAddress, int size, string namespaceAndTypeName, string name, string signature ) - { - _startAddress = startAddress; - _size = size; - _fullName = ComputeFullName( startAddress, namespaceAndTypeName, name, signature ); - } - - private string ComputeFullName( ulong startAddress, string namespaceAndTypeName, string name, string signature ) - { - var fullName = signature; - - // constructor case: name = .ctor | namespaceAndTypeName = A.B.typeName | signature = ... (parameters) - // --> A.B.typeName(parameters) - if( name == ".ctor" ) - { - return $"{namespaceAndTypeName}{ExtractParameters( signature )}"; - } - - // general case: name = Foo | namespaceAndTypeName = A.B.typeName | signature = ... (parameters) - // --> A.B.Foo(parameters) - fullName = $"{namespaceAndTypeName}.{name}{ExtractParameters( signature )}"; - return fullName; - } - - private string ExtractTypeName( string namespaceAndTypeName ) - { - var pos = namespaceAndTypeName.LastIndexOf( ".", StringComparison.Ordinal ); - if( pos == -1 ) - { - return namespaceAndTypeName; - } - - // skip the . - pos++; - - return namespaceAndTypeName.Substring( pos ); - } - - private string ExtractParameters( string signature ) - { - var pos = signature.IndexOf( " (" ); - if( pos == -1 ) - { - return "(???)"; - } - - // skip double space - pos += 2; - - var parameters = signature.Substring( pos ); - return parameters; - } - - public ulong StartAddress => _startAddress; - public int Size => _size; - public string FullName => _fullName; - } -} diff --git a/prof/MethodStore.cs b/prof/MethodStore.cs deleted file mode 100644 index 9f09cc0..0000000 --- a/prof/MethodStore.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Text; - -namespace ProfilerHelpers -{ - public class MethodStore : IDisposable - { - // JITed methods information (start address + size + signature) - private readonly List _methods; - - // addresses from callstacks already matching (address -> full name) - private readonly Dictionary _cache; - - // for native methods, rely on dbghelp API - // so the process handle is required - private IntPtr _hProcess; - private Process _process; - private readonly int _pid; - - public MethodStore( int pid, bool loadModules = false ) - { - // it may be possible to open the process - // in that case, _hProcess = IntPtr.Zero - _pid = pid; - - _methods = new List( 1024 ); - _cache = new Dictionary(); - - _hProcess = BindToProcess( pid, loadModules ); - } - - private IntPtr BindToProcess( int pid, bool loadModules ) - { - try - { - _process = Process.GetProcessById( pid ); - - if( !SymInitialize( _process.Handle, loadModules ) ) - return IntPtr.Zero; - - return _process.Handle; - } - catch( Exception x ) - { - Console.WriteLine( $"Error while binding pid #{pid} to DbgHelp:" ); - Console.WriteLine( x.Message ); - return IntPtr.Zero; - } - } - - private bool SymInitialize( IntPtr hProcess, bool loadModules = false ) - { - // read https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetoptions for more details - // maybe SYMOPT_NO_PROMPTS and SYMOPT_FAIL_CRITICAL_ERRORS could be used - NativeDbgHelp.SymSetOptions( - NativeDbgHelp.SYMOPT_DEFERRED_LOADS | // performance optimization - NativeDbgHelp.SYMOPT_UNDNAME // C++ names are not mangled - ); - - // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-syminitialize - // search path for symbols: - // - The current working directory of the application - // - The _NT_SYMBOL_PATH environment variable - // - The _NT_ALTERNATE_SYMBOL_PATH environment variable - // - // passing false as last parameter means that we will need to call SymLoadModule64 - // each time a module is loaded in the process - return NativeDbgHelp.SymInitialize( hProcess, null, loadModules ); - } - - public MethodInfo Add( ulong address, int size, string namespaceAndTypeName, string name, string signature ) - { - var method = new MethodInfo( address, size, namespaceAndTypeName, name, signature ); - _methods.Add( method ); - return method; - } - - public string GetFullName( ulong address ) - { - if( _cache.TryGetValue( address, out var fullName ) ) - return fullName; - - // look for managed methods - for( int i = 0; i < _methods.Count; i++ ) - { - var method = _methods[i]; - - if( ( address >= method.StartAddress ) && ( address < method.StartAddress + (ulong)method.Size ) ) - { - fullName = method.FullName; - _cache[address] = fullName; - return fullName; - } - } - - // look for native methods - fullName = GetNativeMethodName( address ); - _cache[address] = fullName; - - return fullName; - } - - private string GetNativeMethodName( ulong address ) - { - var symbol = new NativeDbgHelp.SYMBOL_INFO(); - symbol.MaxNameLen = 1024; - symbol.SizeOfStruct = (uint)Marshal.SizeOf( symbol ) - 1024; // char buffer is not counted - // the ANSI version of SymFromAddr is called so each character is 1 byte long - - if( NativeDbgHelp.SymFromAddr( _hProcess, address, out var displacement, ref symbol ) ) - { - var buffer = new StringBuilder( symbol.Name.Length ); - - // remove weird "$##" at the end of some symbols - var pos = symbol.Name.LastIndexOf( "$##" ); - if( pos == -1 ) - buffer.Append( symbol.Name ); - else - buffer.Append( symbol.Name, 0, pos ); - - // add offset if any - if( displacement != 0 ) - buffer.Append( $"+0x{displacement}" ); - - return buffer.ToString(); - } - - // default value is just the address in HEX -#if DEBUG - return ( $"0x{address:x} (SymFromAddr failed with 0x{Marshal.GetLastWin32Error():x})" ); -#else - return $"0x{address:x}"; -#endif - - } - - const int ERROR_SUCCESS = 0; - public void AddModule( string filename, ulong baseOfDll, int sizeOfDll ) - { - var baseAddress = NativeDbgHelp.SymLoadModule64( _hProcess, IntPtr.Zero, filename, null, baseOfDll, (uint)sizeOfDll ); - if( baseAddress == 0 ) - { - // should work if the same module is added more than once - if( Marshal.GetLastWin32Error() == ERROR_SUCCESS ) - return; - - Console.WriteLine( $"SymLoadModule64 failed for {filename}" ); - } - } - - public void Dispose() - { - if( _hProcess == IntPtr.Zero ) - return; - _hProcess = IntPtr.Zero; - - _process.Dispose(); - } - } - - internal static class NativeDbgHelp - { - // from C:\Program Files (x86)\Windows Kits\10\Debuggers\inc\dbghelp.h - public const uint SYMOPT_UNDNAME = 0x00000002; - public const uint SYMOPT_DEFERRED_LOADS = 0x00000004; - - [StructLayout( LayoutKind.Sequential )] - public struct SYMBOL_INFO - { - public uint SizeOfStruct; - public uint TypeIndex; // Type Index of symbol - private ulong Reserved1; - private ulong Reserved2; - public uint Index; - public uint Size; - public ulong ModBase; // Base Address of module containing this symbol - public uint Flags; - public ulong Value; // Value of symbol, ValuePresent should be 1 - public ulong Address; // Address of symbol including base address of module - public uint Register; // register holding value or pointer to value - public uint Scope; // scope of the symbol - public uint Tag; // pdb classification - public uint NameLen; // Actual length of name - public uint MaxNameLen; - [MarshalAs( UnmanagedType.ByValTStr, SizeConst = 1024 )] - public string Name; - } - - [DllImport( "dbghelp.dll", SetLastError = true )] - public static extern bool SymInitialize( IntPtr hProcess, string userSearchPath, bool invadeProcess ); - - [DllImport( "dbghelp.dll", SetLastError = true )] - public static extern uint SymSetOptions( uint symOptions ); - - [DllImport( "dbghelp.dll", SetLastError = true, CharSet = CharSet.Ansi )] - public static extern ulong SymLoadModule64( IntPtr hProcess, IntPtr hFile, string imageName, string moduleName, ulong baseOfDll, uint sizeOfDll ); - - // use ANSI version to ensure the right size of the structure - // read https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/ns-dbghelp-symbol_info - [DllImport( "dbghelp.dll", SetLastError = true, CharSet = CharSet.Ansi )] - public static extern bool SymFromAddr( IntPtr hProcess, ulong address, out ulong displacement, ref SYMBOL_INFO symbol ); - - [DllImport( "dbghelp.dll", SetLastError = true )] - public static extern bool SymCleanup( IntPtr hProcess ); - } -} diff --git a/prof/PerProcessProfilingState.cs b/prof/PerProcessProfilingState.cs deleted file mode 100644 index 8a3c2d5..0000000 --- a/prof/PerProcessProfilingState.cs +++ /dev/null @@ -1,33 +0,0 @@ -using ProfilerHelpers; -using System; -using System.Collections.Generic; - -namespace Tracing -{ - public class PerProcessProfilingState : IDisposable - { - private bool _disposed; - - private readonly Dictionary _processNames = new Dictionary(); - private readonly Dictionary _perProcessTypes = new Dictionary(); - private readonly Dictionary _perProcessAllocations = new Dictionary(); - private readonly Dictionary _methods = new Dictionary(); - - public Dictionary Names => _processNames; - public Dictionary Types => _perProcessTypes; - public Dictionary Allocations => _perProcessAllocations; - public Dictionary Methods => _methods; - - public void Dispose() - { - if( _disposed ) - return; - _disposed = true; - - foreach( var methodStore in _methods.Values ) - { - methodStore.Dispose(); - } - } - } -} diff --git a/prof/ProcessAllocations.cs b/prof/ProcessAllocations.cs deleted file mode 100644 index acdc4dd..0000000 --- a/prof/ProcessAllocations.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Tracing -{ - - public class ProcessAllocations - { - private readonly int _pid; - private readonly Dictionary _allocations; - private readonly Dictionary _perThreadLastAllocation; - - public ProcessAllocations( int pid ) - { - _pid = pid; - _allocations = new Dictionary(); - _perThreadLastAllocation = new Dictionary(); - } - - public int Pid => _pid; - - public AllocationInfo GetAllocations( string typeName ) - { - return ( _allocations.TryGetValue( typeName, out var info ) ) ? info : null; - } - - public IEnumerable GetAllAllocations() - { - return _allocations.Values; - } - - public AllocationInfo AddAllocation( int threadID, ulong size, ulong count, string typeName ) - { - if( !_allocations.TryGetValue( typeName, out var info ) ) - { - info = new AllocationInfo( typeName ); - _allocations[typeName] = info; - } - - info.AddAllocation( size, count ); - - // the last allocation is still here without the corresponding stack - if( _perThreadLastAllocation.TryGetValue( threadID, out var lastAlloc ) ) - { - Console.WriteLine( "no stack for the last allocation" ); - } - - // keep track of the allocation for the given thread - // --> will be used when the corresponding call stack event will be received - _perThreadLastAllocation[threadID] = info; - - return info; - } - - public void AddStack( int threadID, AddressStack stack ) - { - if( _perThreadLastAllocation.TryGetValue( threadID, out var lastAlloc ) ) - { - lastAlloc.AddStack( stack ); - _perThreadLastAllocation.Remove( threadID ); - return; - } - - //Console.WriteLine("no last allocation for the stack event"); - } - } - - - public class AllocationInfo - { - private readonly string _typeName; - private ulong _size; - private ulong _count; - private List _stacks; - - internal AllocationInfo( string typeName ) - { - _typeName = typeName; - _stacks = new List(); - } - - public string TypeName => _typeName; - public ulong Count => _count; - public ulong Size => _size; - public IReadOnlyList Stacks => _stacks; - - internal void AddAllocation( ulong size, ulong count ) - { - _count += count; - _size += size; - } - - internal void AddStack( AddressStack stack ) - { - var info = GetInfo( stack ); - if( info == null ) - { - info = new StackInfo( stack ); - _stacks.Add( info ); - } - - info.Count++; - } - - private StackInfo GetInfo( AddressStack stack ) - { - for( int i = 0; i < _stacks.Count; i++ ) - { - var info = _stacks[i]; - if( stack.Equals( info.Stack ) ) - return info; - } - - return null; - } - } - - public class StackInfo - { - private readonly AddressStack _stack; - public ulong Count; - - internal StackInfo( AddressStack stack ) - { - Count = 0; - _stack = stack; - } - - public AddressStack Stack => _stack; - } -} diff --git a/prof/ProcessTypeMapping.cs b/prof/ProcessTypeMapping.cs deleted file mode 100644 index 81574cb..0000000 --- a/prof/ProcessTypeMapping.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; - -namespace Tracing -{ - // Contains the mapping between type ID received by SampleObjectAllocation(Low/High) events - // and their name received by TypeBulkType events - public class ProcessTypeMapping - { - private readonly Dictionary _typesIdToName; - - public ProcessTypeMapping( int processId ) - { - ProcessId = processId; - _typesIdToName = new Dictionary(); - } - - public int ProcessId { get; set; } - - public string this[ulong id] - { - get - { - if( !_typesIdToName.ContainsKey( id ) ) - return null; - - return _typesIdToName[id]; - } - set - { - _typesIdToName[id] = value; - } - } - - } -} diff --git a/prof/Program.cs b/prof/Program.cs deleted file mode 100644 index 1403092..0000000 --- a/prof/Program.cs +++ /dev/null @@ -1,162 +0,0 @@ -using Microsoft.Diagnostics.Tracing.Session; -using ProfilerHelpers; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace Tracing -{ - static class Program - { - - - static public int CreateTracingSession( bool noSampling, bool sortBySize, int topTypesLimit ) - { - ShowHeader(); - - try - { - - TraceEventSession session = new TraceEventSession( - "Tracing", - TraceEventSessionOptions.Create - ); - - log.info( $"Create PerProcessProfilingState" ); - - using( var processes = new PerProcessProfilingState() ) - { - log.info( $"Create Memory profiler for session" ); - var profiler = new Memory( session, processes ); - - log.info( $"Start task" ); - var task = profiler.StartAsync( noSampling ); - - /* - log.info( $"await the Continue" ); - await task.ContinueWith( ( t ) => { - log.info( $"Task is done, Dispose" ); - session.Dispose(); - } ); - */ - - //log.info("Press ENTER to stop memory profiling"); - //Console.ReadLine(); - - /* - try - { - await task; - ShowResults( processes, sortBySize, topTypesLimit ); - - return 0; - } - catch( Exception x ) - { - log.info( x.Message ); - ShowHelp(); - } - */ - } - - return -1; - } - catch( Exception x ) - { - log.info( x.Message ); - ShowHelp(); - } - - return -2; - } - - private static void ShowResults( PerProcessProfilingState processes, bool sortBySize, int topTypesLimit ) - { - foreach( var pid in processes.Allocations.Keys ) - { - // skip processes without symbol resolution - if( !processes.Methods.ContainsKey( pid ) ) - continue; - - // skip processes without allocations - if( !processes.Allocations[pid].GetAllAllocations().Any() ) - continue; - - ShowResults( GetProcessName( pid, processes.Names ), processes.Methods[pid], processes.Allocations[pid], sortBySize, topTypesLimit ); - } - } - - private static string GetProcessName( int pid, Dictionary names ) - { - if( names.TryGetValue( pid, out var name ) ) - return name; - - return pid.ToString(); - } - - private static void ShowResults( string name, MethodStore methods, ProcessAllocations allocations, bool sortBySize, int topTypesLimit ) - { - log.info( $"Memory allocations for {name}" ); - log.info( $"" ); - log.info( "---------------------------------------------------------" ); - log.info( " Count Size Type" ); - log.info( "---------------------------------------------------------" ); - IEnumerable types = ( sortBySize ) - ? allocations.GetAllAllocations().OrderByDescending( a => a.Size ) - : allocations.GetAllAllocations().OrderByDescending( a => a.Count ) - ; - if( topTypesLimit != -1 ) - types = types.Take( topTypesLimit ); - - foreach( var allocation in types ) - { - log.info( $"{allocation.Count,9} {allocation.Size,11} {allocation.TypeName}" ); - - log.info( $"" ); - DumpStacks( allocation, methods ); - log.info( $"" ); - } - log.info( $"" ); - log.info( $"" ); - } - - private static void DumpStacks( AllocationInfo allocation, MethodStore methods ) - { - var stacks = allocation.Stacks.OrderByDescending( s => s.Count ).Take( 10 ); - foreach( var stack in stacks ) - { - log.info( $"{stack.Count,6} allocations" ); - log.info( "----------------------------------" ); - DumpStack( stack.Stack, methods ); - log.info( $"" ); - } - } - - private static void DumpStack( AddressStack stack, MethodStore methods ) - { - var callstack = stack.Stack; - for( int i = 0; i < Math.Min( 10, callstack.Count ); i++ ) - { - log.info( $" {methods.GetFullName( callstack[i] )}" ); - } - } - - private static void ShowHeader() - { - log.info( "Tracing v1.0.0 - Sampled memory profiler for .NET applications" ); - log.info( "by Christophe Nasarre" ); - log.info( $"" ); - } - private static void ShowHelp() - { - log.info( $"" ); - log.info( "Tracing shows sampled allocations of a given .NET application." ); - log.info( "Usage: Tracing [-a (all allocations)] [-c (sort by count instead of default by size)] [-t ]" ); - log.info( " Ex: Tracing -t -1 (all types sampled allocations sorted by size)" ); - log.info( " Ex: Tracing -c -t 10 (allocations for top 10 types sorted by count)" ); - log.info( $"" ); - } - } -} diff --git a/ser/XmlSer.cs b/ser/XmlSer.cs deleted file mode 100644 index 156422e..0000000 --- a/ser/XmlSer.cs +++ /dev/null @@ -1,838 +0,0 @@ -using System; -using System.IO; -using System.Xml; -using System.Runtime.Serialization; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; -using System.Linq; -using System.Collections.Immutable; -using System.Net.Sockets; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Linq.Expressions; - -namespace ser; - -#region Attributes & Enums (Mostly unchanged, ensure these exist) - -/* - -public CoolClass -{ - private string Rare = "Rare Change"; - private float TestF = 1.0f; -} - -public class Bag -{ - public string Owner { get; set; } - public CoolClass Cool { get; set; } = new(); -} -public class BasicTest -{ - public Bag MyBag { get; set; } = new(); -} - - - - - - - - - -public abstract class Item -{ - public int Id { get; set; } -} - -public class Key : Item -{ - public int Code { get; set; } -} - -public class Orb : Item -{ - private float Power = 1.0f; - public bool IsDark = true; -} - - - - -*/ - -public interface I_Serialize -{ - void OnSerialize() { } - object OnDeserialize( object enclosing ) => this; -} - - -[Flags] -public enum Types -{ - Fields = 0b_0001, - Props = 0b_0010, - Implied = 0b_0100, - Explicit = 0b_1000, - - - None = 0b_0000, - Default = Fields, - All = Fields | Props, -} - -public class Ser : Attribute -{ - public Types Types { get; set; } = Types.Default; -} - -public class Do : Attribute -{ -} - -public class Dont : Attribute -{ -} - - -public class ChildAttribute : Attribute -{ - public string[] Values { get; private set; } - - public ChildAttribute( params string[] values ) - { - this.Values = values; - } -} - -public class ChildFieldsAttribute : ChildAttribute -{ - public ChildFieldsAttribute( params string[] values ) : base( values ) { } -} - -public class ChildPropsAttribute : ChildAttribute -{ - public ChildPropsAttribute( params string[] values ) : base( values ) { } -} - - - -public interface ITypeHandler -{ - bool CanHandle( TypeInfo typeInfo, XmlElement? elem = null ); // Elem needed for Deser - void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType ); - object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing ); -} - - - -// --- Enums & Records (Slightly adjusted/renamed) --- -public enum Datastructure { Tree, Graph } -public enum BackingFieldNaming { Short, Regular } -public enum POD { Attributes, Elements } -public record struct TypeProxy( Func fnSer, Func fnDes ); - -public record XmlCfg : io.Recorded -{ - public bool Verbose { get; init; } = false; - public Datastructure Structure { get; init; } = Datastructure.Tree; - public int Version { get; init; } = 2; - public ImmutableDictionary Proxies { get; init; } = ImmutableDictionary.Empty; - public BackingFieldNaming Naming { get; init; } = BackingFieldNaming.Short; - public POD POD { get; init; } = POD.Attributes; - public ser.Types TypesDefault { get; init; } = ser.Types.Fields; - public static XmlCfg Default { get; } = new XmlCfg(); -} - -#endregion - -#region Reflection & Metadata Cache - -public record DependentMember( - string Name, //Prop or field name - TypeInfo Enclosing, - MemberInfo EnclosingMember -); - -public enum MemberMetaType -{ - Invalid, - Simple, - Composite, -} - -public record MemberMeta( - //Base type. - Type Type, - MemberInfo Info, - string XmlName, - Func GetValue, - Action SetValue, - bool IsPodAttribute, - bool HasDo, - bool HasDont -); - -public record TypeInfo( - Type Type, - List Members, - bool IsISerializable, - bool IsImm, - bool IsProxy, - TypeProxy? ProxyDef -); - -public class TypeMetaCache -{ - private readonly ConcurrentDictionary _cache = new(); - private readonly XmlCfg _cfg; - - public TypeMetaCache( XmlCfg cfg ) => _cfg = cfg; - - public TypeInfo Get( Type type ) - { - // Expanded in anticpation of more complex ways to specify saves/loads - - if( _cache.TryGetValue( type, out var ti ) ) - return ti; - - var children = new HashSet(); - - var tiNew = BuildTypeInfo( type, children ); - _cache.AddOrUpdate( type, tiNew, ( t, ti ) => tiNew ); - - return tiNew; - } - - // Helper to create accessors (using standard reflection - can be optimized) - private static Func CreateGetter( MemberInfo mi ) - { - if( mi is FieldInfo fi ) - return fi.GetValue; - if( mi is PropertyInfo pi && pi.CanRead ) - return pi.GetValue; - return _ => null; - } - - private static Action CreateSetter( MemberInfo mi ) - { - if( mi is FieldInfo fi ) - return fi.SetValue; - if( mi is PropertyInfo pi && pi.CanWrite ) - return pi.SetValue; - return ( _, _ ) => { }; - } - - - // Helper to create accessors (using standard reflection - can be optimized) - private static Func CreateGetter( MemberInfo mi, Func getter ) - { - if( mi is FieldInfo fi ) - { - var innerGet = fi.GetValue; - - return obj => innerGet( getter( obj ) ); - } - - if( mi is PropertyInfo pi && pi.CanRead ) - { - Func innerGet = pi.GetValue; - - return obj => innerGet( getter( obj ) ); - } - - // return pi.GetValue; - - - return _ => null; - } - - private static Action CreateSetter( MemberInfo mi, Func getter ) - { - if( mi is FieldInfo fi ) - { - Action innerSet = fi.SetValue; - - return ( obj, value ) => innerSet( getter( obj ), value ); - } - - - if( mi is PropertyInfo pi && pi.CanWrite ) - { - Action innerSet = pi.SetValue; - - //return innerSet; - - //var innerSetType = innerSet.GetType(); - //var isArgs = innerSetType.Metho - - - return ( obj, value ) => - { - var leaf = getter( obj ); - innerSet( leaf, value ); - }; - } - - return ( _, _ ) => { }; - } - - - public void AddType( Type type, params string[] children ) - { - var hashChildren = new HashSet( children ); - - BuildTypeInfo( type, hashChildren ); - } - - private TypeInfo BuildTypeInfo( Type type, HashSet children ) - { - if( _cfg.Verbose ) - log.info( $"Building TypeInfo for {type.Name}" ); - - var members = new List(); - bool doImpls, doFields, doProps; - - GetFilters( _cfg.TypesDefault, type, out doImpls, out doFields, out doProps ); - - - var isImm = typeof( io.Obj ).IsAssignableFrom( type ); - - var typesAtt = type.GetCustomAttribute( true ); - var serTypes = typesAtt?.Types ?? ser.Types.None; - - - if( doFields || doImpls ) - { - foreach( var fi in refl.GetAllFields( type ) ) - { - ProcessMember( fi, serTypes.HasFlag( ser.Types.Fields ), children, doImpls, isImm, members ); - } - - - } - - if( doProps || doImpls ) - { - foreach( var pi in refl.GetAllProperties( type ) ) - { - ProcessMember( pi, serTypes.HasFlag( ser.Types.Props ), children, doImpls, isImm, members ); - } - } - - var (isProxy, proxyDef) = FindProxy( type ); - - //Dont need to sort since we just run through the attributes first, then the nodes - //members.Sort( ( a, b ) => ( a.IsPodAttribute.Int < b.IsPodAttribute.Int ) ? 1 : -1); - - return new TypeInfo( - type, - members, - typeof( ISerializable ).IsAssignableFrom( type ) && !typeof( Delegate ).IsAssignableFrom( type ), // Exclude Delegates - isImm, - isProxy, - proxyDef - ); - - } - - private (bool, TypeProxy?) FindProxy( Type type ) - { - var tryType = type; - while( tryType != null && tryType != typeof( object ) ) - { - if( _cfg.Proxies.TryGetValue( tryType, out var proxy ) ) - { - return (true, proxy); - } - tryType = tryType.BaseType; - } - return (false, null); - } - - - - - - - private void ProcessMember( MemberInfo mi, bool doMemberType, HashSet childrenOverridden, bool doImpls, bool isImm, List members ) - { - List children = new( childrenOverridden ); - var (hasDo, hasDont, hasImpl, propName, serTypess) = GetMemberAttributes( mi, out var actualMiForAtts, children ); - - if( hasDont ) - return; - - // TODO MH Change this to a configurable query(s) - if( isImm && ( mi.Name == "MetaStorage" || mi.Name == "Fn" ) ) - return; - - - if( mi.GetCustomAttribute( true ) != null ) - return; - - if( !( - hasDo | - doMemberType | - ( hasImpl & children.Any() ) - ) ) - return; - - - var miType = ( mi is FieldInfo fi ) ? fi.FieldType : ( (PropertyInfo)mi ).PropertyType; // CHANGED (moved up) - - string name = mi.Name; - string finalName = name; - - if( !string.IsNullOrEmpty( propName ) ) - { - finalName = ( _cfg.Naming == BackingFieldNaming.Short ) ? propName : name; - } - - finalName = refl.TypeToIdentifier( finalName ); // Ensure XML-safe name - - var overiddenName = false; - - var getter = CreateGetter( mi ); - var setter = CreateSetter( mi ); - - var blankHashSet = new HashSet(); - - if( hasImpl && children.Any() ) - { - //List specialMembers = new(); - foreach( var childName in children ) - { - var memberInfoArr = miType.GetMember( childName ); - var miFinal = memberInfoArr?.FirstOrDefault(); - if( miFinal == null ) - continue; - - var dependentType = miFinal is FieldInfo fidd ? fidd.FieldType : ( miFinal as PropertyInfo ).PropertyType; - - - bool isPod = Type.GetTypeCode( dependentType ) != TypeCode.Object; - - //ProcessMember( miFinal, blankHashSet, doImpls, isImm, specialMembers ); - - //First this one. We need the old getter for the setter. - setter = CreateSetter( miFinal, getter ); - - //Now wrap the getter itself - getter = CreateGetter( miFinal, getter ); - - var depName = $"{finalName}.{childName}"; - - var memberMeta = new MemberMeta( - dependentType, - miFinal, - depName, - getter, - setter, - isPod && _cfg.POD == POD.Attributes, - hasDo, - hasDont - ); - - members.Add( memberMeta ); - - if( _cfg.Verbose ) - { - log.info( $"{depName} ({mi.Name}) -> {finalName} ({dependentType.Name}) PodAtt: {isPod && _cfg.POD == POD.Attributes}" ); - } - - } - - - - return; - - /* - foreach( var childName in children ) - { - var memberInfoArr = miType.GetMember( childName ); - var miFinal = memberInfoArr?.FirstOrDefault(); - if( miFinal != null ) - { - realMemberType = ( miFinal is FieldInfo fin ) ? fin.FieldType : ( miFinal as PropertyInfo ).PropertyType; - - getter = CreateGetter( miFinal, getter ); - setter = CreateSetter( miFinal, setter ); - } - } - //*/ - } - - { - // Simplified POD check - bool isPod = Type.GetTypeCode( miType ) != TypeCode.Object && !typeof( IEnumerable ).IsAssignableFrom( miType ) || overiddenName; - - members.Add( new MemberMeta( - miType, - mi, - finalName, - getter, - setter, - isPod && _cfg.POD == POD.Attributes, - hasDo, - hasDont - ) ); - - if( _cfg.Verbose ) - { - log.info( $"{mi.Name} ({miType.Name}) -> {finalName} ({miType.Name}) PodAtt: {isPod && _cfg.POD == POD.Attributes}" ); - } - } - } - - - private void ProcessDepedentMember( Type type, HashSet children, List members ) - { - } - - - private (bool hasDo, bool hasDont, bool hasImpl, string propName, ser.Types serTypess) GetMemberAttributes( MemberInfo mi, out MemberInfo actualMi, List children ) - { - actualMi = mi; - string propName = ""; - bool isBacking = mi.Name.StartsWith( "<" ) && mi.Name.EndsWith( "BackingField" ); - - var typesAtt = mi.DeclaringType.GetCustomAttribute( true ); - - var serTypes = typesAtt?.Types ?? ser.Types.None; - - var doImpls = serTypes.HasFlag( ser.Types.Implied ); - - var attDo = actualMi.GetCustomAttribute() != null; - var attDont = actualMi.GetCustomAttribute() != null; - - - - if( isBacking && mi is FieldInfo ) - { - var gtIndex = mi.Name.IndexOf( '>' ); - propName = mi.Name.Substring( 1, gtIndex - 1 ); - var propInfo = mi.DeclaringType?.GetProperty( propName ); - if( propInfo != null ) - actualMi = propInfo; - } - - var attChildren = actualMi.GetCustomAttribute(); - if( attChildren != null ) - { - children.AddRange( attChildren.Values ); - } - - return ( -attDo, -attDont, - doImpls, - propName, - - serTypes - ); - } - - - // --- These helpers are copied/adapted from XmlFormatter2 --- - - private static void GetFilters( ser.Types typesDefault, Type type, out bool doImpls, out bool doFields, out bool doProps ) - { - var typesTodo = type.GetCustomAttribute( true )?.Types ?? typesDefault; - - doImpls = typesTodo.HasFlag( ser.Types.Implied ); - doFields = typesTodo.HasFlag( ser.Types.Fields ); - doProps = typesTodo.HasFlag( ser.Types.Props ); - } -} - -public class TypeResolver -{ - private readonly ConcurrentDictionary _cache = new(); - private readonly Assembly[] _assemblies; - private static readonly FormatterConverter _conv = new(); - - public TypeResolver() - { - _assemblies = AppDomain.CurrentDomain.GetAssemblies(); - } - - public Type Resolve( XmlElement elem, Type? expectedType ) - { - if( elem.HasAttribute( "_.t" ) ) - { - var typeName = elem.GetAttribute( "_.t" ); - var resolved = FindType( typeName ); - if( resolved != null ) - return resolved; - } - return expectedType ?? typeof( object ); // Fallback needed - } - - public Type? FindType( string typeName ) - { - return _cache.GetOrAdd( typeName, tn => - { - // Try direct lookup first (might work for fully qualified) - var t = Type.GetType( tn ); - if( t != null ) - return t; - - // Then search assemblies - foreach( Assembly a in _assemblies ) - { - t = a.GetType( tn ); - if( t != null ) - return t; - } - log.warn( $"Could not resolve type: {tn}" ); - return null; - } ); - } - - - public object ConvertSimple( string value, Type type ) - { - if( type.IsEnum ) - return Enum.Parse( type, value ); - try - { - return _conv.Convert( value, type ); - } - catch( Exception ex ) - { - object defaultVal = type.IsValueType ? Activator.CreateInstance( type )! : null!; - log.warn( $"Conversion failed for '{value}' to {type.Name}: {ex.Message}. Returning default of {defaultVal}({defaultVal.GetType().Name})." ); - return defaultVal; - } - } -} - -#endregion - -#region XmlSer (Coordinator) - -public class XmlSer // : IFormatter -{ - internal readonly XmlCfg _cfg; - internal readonly TypeMetaCache _meta; - internal readonly TypeResolver _resolver; - private readonly List _handlers; - - // Per-operation state - internal ObjectIDGenerator _idGen = new(); - internal Dictionary _processed = new(); - private string _streamSource = ""; - - public XmlSer( XmlCfg? cfg = null, TypeMetaCache metaCache = null ) - { - var isCustomConfig = cfg != null; - - _cfg = cfg ?? XmlCfg.Default; - - if( _cfg.Verbose ) - { - log.info( $"Config:" ); - log.info( $" {log.var( _cfg.Verbose )}" ); - log.info( $" {log.var( _cfg.Structure )}" ); - log.info( $" {log.var( _cfg.Version )}" ); - log.info( $" {log.var( _cfg.Naming )}" ); - log.info( $" {log.var( _cfg.POD )}" ); - log.info( $" {log.var( _cfg.TypesDefault )}" ); - } - - - _meta = metaCache ?? new TypeMetaCache( _cfg ); - - _resolver = new TypeResolver(); - - _handlers = new List - { - new ProxyHandler(), - new ISerializableHandler(), - new PrimitiveHandler(), - new CollectionHandler(), - new ObjectHandler() // Must be last - }; - - if( _cfg.Verbose ) - { - log.info( $"Handlers in importance..." ); - foreach( var h in _handlers ) - { - log.info( $" {h.GetType().Name}" ); - } - - log.high( "XmlSer Initialized." ); - } - } - - internal ITypeHandler GetHandler( Type t, XmlElement? elem = null ) - { - var ti = _meta.Get( t ); - return _handlers.First( h => h.CanHandle( ti, elem ) ); - } - - // --- Context Helpers --- - internal void WriteTypeAttr( XmlWriter writer, Type memberType, Type actualType ) - { - if( memberType != actualType ) - { - writer.WriteAttributeString( "_.t", actualType.FullName ); - } - } - - internal bool HandleGraphWrite( XmlWriter writer, object obj, out bool first ) - { - first = true; - if( _cfg.Structure == Datastructure.Graph ) - { - long id = _idGen.GetId( obj, out first ); - writer.WriteAttributeString( "ref", id.ToString() ); - if( first ) - _processed[id] = obj; - } - return first || _cfg.Structure == Datastructure.Tree; // Write if first or if Tree - } - - internal long TrackIfGraph( object obj, XmlElement elem ) - { - long id = -1; - bool first; - if( _cfg.Structure == Datastructure.Graph ) - { - id = _idGen.GetId( obj, out first ); - if( elem.HasAttribute( "ref" ) ) - { - id = long.Parse( elem.GetAttribute( "ref" ) ); - } - if( !_processed.ContainsKey( id ) ) - { - _processed[id] = obj; - } - } - return id; - } - - - // --- Deserialization --- - public T? Deserialize( Stream stream ) => (T?)Deserialize( stream, typeof( T ) ); - - public object? Deserialize( Stream stream, Type? type = null ) - { - _streamSource = stream.ToString() ?? "{null}"; // Basic source, improve as needed - _processed.Clear(); - _idGen = new ObjectIDGenerator(); - - using var reader = XmlReader.Create( stream, new XmlReaderSettings { IgnoreWhitespace = true } ); - XmlDocument doc = new XmlDocument(); - try - { doc.Load( reader ); } - catch( Exception ex ) { log.exception( ex, $"XML Load failed: {ex.Message}" ); return null; } - - if( doc.DocumentElement == null ) - return null; - - return ReadNode( doc.DocumentElement, type ?? typeof( object ), null ); - } - - public void DeserializeInto( Stream stream, T obj ) where T : class - { - _streamSource = stream.ToString() ?? "{null}"; - _processed.Clear(); - _idGen = new ObjectIDGenerator(); - - using var reader = XmlReader.Create( stream, new XmlReaderSettings { IgnoreWhitespace = true } ); - XmlDocument doc = new XmlDocument(); - try - { - doc.Load( reader ); - } - catch( Exception ex ) - { - log.exception( ex, $"XML Load failed: {ex.Message}" ); - return; - } - - if( doc.DocumentElement == null ) - return; - - ReadNode( doc.DocumentElement, typeof( T ), obj ); - } - - internal object? ReadNode( XmlElement elem, Type expectedType, object? existing ) - { - if( elem.HasAttribute( "v" ) && elem.GetAttribute( "v" ) == "null" ) - return null; - - // 1. Handle refs (if Graph) - if( _cfg.Structure == Datastructure.Graph && elem.HasAttribute( "ref" ) ) - { - long id = long.Parse( elem.GetAttribute( "ref" ) ); - if( _processed.TryGetValue( id, out var obj ) ) - return obj; - } - - // 2. Determine Type & Select Handler - var actualType = _resolver.Resolve( elem, expectedType ); - var ti = _meta.Get( actualType ); - var handler = _handlers.First( h => h.CanHandle( ti, elem ) ); - - // 3. Delegate - return handler.ReadXml( this, elem, actualType, existing ); - } - - // --- Serialization --- - public void Serialize( Stream stream, object root ) - { - _processed.Clear(); - _idGen = new ObjectIDGenerator(); - - var settings = new XmlWriterSettings - { - Indent = true, - Encoding = System.Text.Encoding.UTF8, // Use UTF8 for better compatibility - OmitXmlDeclaration = true // Often preferred for fragments/storage - }; - - using var writer = XmlWriter.Create( stream, settings ); - - writer.WriteStartDocument(); - WriteNode( writer, root, "root", root?.GetType() ?? typeof( object ), true ); // Force type on root - writer.WriteEndDocument(); - writer.Flush(); - } - - internal void WriteNode( XmlWriter writer, object? obj, string name, Type memberType, bool forceType ) - { - if( _cfg.Verbose ) - log.info( $"Writing {name} ({memberType}) force: {forceType}" ); - - if( obj == null ) - { - writer.WriteStartElement( name ); - writer.WriteAttributeString( "v", "null" ); - writer.WriteEndElement(); - return; - } - - var actualType = obj.GetType(); - var ti = _meta.Get( actualType ); - var handler = _handlers.First( h => h.CanHandle( ti ) ); - - try - { - - handler.WriteXml( this, writer, obj, name, memberType, forceType || memberType != actualType ); - } - catch( Exception ex ) - { - log.exception( ex, $"{name}({memberType.Name}) forceType: {forceType}" ); - } - } -} -#endregion diff --git a/ser/XmlSer_Core.cs b/ser/XmlSer_Core.cs deleted file mode 100644 index 2ec98c3..0000000 --- a/ser/XmlSer_Core.cs +++ /dev/null @@ -1,159 +0,0 @@ - - - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; -using System.Xml; - -namespace ser; - - -// --- Primitive Handler --- -public partial class PrimitiveHandler : ser.ITypeHandler -{ - public bool CanHandle( TypeInfo ti, XmlElement? elem ) - { - var typeCode = Type.GetTypeCode( ti.Type ); - var typeNotObject = Type.GetTypeCode( ti.Type ) != TypeCode.Object; - //var isString = ti.Type == typeof( string ); - return typeNotObject; - } -} - -// --- Proxy Handler --- -public partial class ProxyHandler : ITypeHandler -{ - public bool CanHandle( TypeInfo ti, XmlElement? elem ) => ti.IsProxy || ( elem?.HasAttribute( "proxy" ) ?? false ); -} - -// --- ISerializable Handler --- -public partial class ISerializableHandler : ITypeHandler -{ - public bool CanHandle( TypeInfo ti, XmlElement? elem ) => ti.IsISerializable; -} - -// --- Collection Handler --- -public partial class CollectionHandler : ITypeHandler -{ - public bool CanHandle( TypeInfo ti, XmlElement? elem ) => - typeof( IEnumerable ).IsAssignableFrom( ti.Type ); - - private Type GetElementType( Type collectionType ) - { - if( collectionType.IsArray ) - return collectionType.GetElementType()!; - if( collectionType.IsGenericType ) - { - var args = collectionType.GetGenericArguments(); - if( args.Length == 1 ) - return args[0]; - if( args.Length == 2 ) - return typeof( KeyValuePair<,> ).MakeGenericType( args ); - } - return typeof( object ); // Fallback - } - - private object ConvertToFinalCollection( IList list, Type expectedType, Type elemType ) - { - if( expectedType.IsArray ) - { - var arr = Array.CreateInstance( elemType, list.Count ); - list.CopyTo( arr, 0 ); - return arr; - } - if( expectedType.IsGenericType ) - { - var genDef = expectedType.GetGenericTypeDefinition(); - if( genDef == typeof( ImmutableArray<> ) ) - { - var method = typeof( ImmutableArray ).GetMethods() - .First( m => m.Name == "ToImmutableArray" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) ) - .MakeGenericMethod( elemType ); - return method.Invoke( null, new object[] { list } )!; - } - if( genDef == typeof( ImmutableList<> ) ) - { - var method = typeof( ImmutableList ).GetMethods() - .First( m => m.Name == "ToImmutableList" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.IsGenericType && m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) ) - .MakeGenericMethod( elemType ); - return method.Invoke( null, new object[] { list } )!; - } - // Add more immutable/dictionary handlers here (using MakeImmutableDictionary etc.) - } - return list; // Default to List if no specific match - } -} - - -// --- Object Handler (Default/Complex) --- -public partial class ObjectHandler : ITypeHandler -{ - public bool CanHandle( TypeInfo ti, XmlElement? elem ) => true; // Fallback - - private (XmlNode? source, bool isAttribute) FindValueSource( XmlElement parent, string name ) - { - if( parent.HasAttribute( name ) ) - { - return (parent.Attributes[name], true); - } - foreach( XmlNode node in parent.ChildNodes ) - { - if( node.NodeType == XmlNodeType.Element && node.Name == name ) - { - return (node, false); - } - } - return (null, false); - } - - private bool ShouldSetValue( MemberMeta member, bool isHydrating ) - { - // [Dont] members are filtered out during metadata generation. - // If a member is present in the metadata and a value is found in the XML, - // we should always set it. This handles both new creation and hydration/merge. - return true; - - } - - - private (object? obj, long id) GetOrCreateInstance( XmlSer xml, XmlElement elem, Type type, object? existing ) - { - long id = -1; - bool first = true; - - // Check existing - if( existing != null && type.IsAssignableFrom( existing.GetType() ) ) - { - id = xml._idGen.GetId( existing, out first ); - return (existing, id); - } - - // Create new - object? newObj = null; - try - { - if( type.GetConstructor( Type.EmptyTypes ) != null ) - { - newObj = Activator.CreateInstance( type ); - } - else - { - newObj = FormatterServices.GetUninitializedObject( type ); - } - } - catch( Exception ex ) - { - log.exception( ex, $"Failed to create instance of {type.Name}: {ex.Message}" ); - return (null, -1); - } - - id = xml._idGen.GetId( newObj, out first ); - return (newObj, id); - } -} - diff --git a/ser/XmlSer_Read.cs b/ser/XmlSer_Read.cs deleted file mode 100644 index 2742153..0000000 --- a/ser/XmlSer_Read.cs +++ /dev/null @@ -1,188 +0,0 @@ - - - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; -using System.Xml; - -namespace ser; - - -// --- Primitive Handler --- -public partial class PrimitiveHandler : ser.ITypeHandler -{ - public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing ) - { - string val = elem.HasAttribute( "v" ) ? elem.GetAttribute( "v" ) : elem.InnerText; - if( val == "null" ) - return null; - - // So this is an interesting one. Why not use the expected type? Well, we know we have - // data in the XML, it just wont convert to what we want. - return xml._resolver.ConvertSimple( val, expectedType ); - } -} - -// --- Proxy Handler --- -public partial class ProxyHandler : ITypeHandler -{ - public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing ) - { - var ti = xml._meta.Get( expectedType ); // Re-get to ensure we have proxy info - if( !elem.HasAttribute( "proxy" ) || !ti.ProxyDef.HasValue ) - { - log.warn( $"Proxy read failed for {expectedType.Name}. Fallback needed." ); - return null; // Should fall back or throw - } - var proxyVal = elem.GetAttribute( "proxy" ); - return ti.ProxyDef.Value.fnDes( expectedType.FullName, proxyVal ); - } -} - -// --- ISerializable Handler --- -public partial class ISerializableHandler : ITypeHandler -{ - public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing ) - { - // Create/Get instance (needs FormatterServices for ISerializable) - object obj = existing ?? FormatterServices.GetUninitializedObject( expectedType ); - long id = xml.TrackIfGraph( obj, elem ); // Track it - - var serInfo = new SerializationInfo( expectedType, new FormatterConverter() ); - - foreach( XmlNode objNode in elem.ChildNodes ) - { - if( objNode is XmlElement childElem ) - { - string childName = childElem.Name; - Type? childType = xml._resolver.FindType( childElem.GetAttribute( "_.t" ) ); - if( childType != null ) - { - var desValue = xml.ReadNode( childElem, childType, null ); - serInfo.AddValue( childName, desValue, childType ); - } - } - } - - var context = new StreamingContext( StreamingContextStates.All ); // Or use xml.Context - var cons = expectedType.GetConstructor( - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, - null, new[] { typeof( SerializationInfo ), typeof( StreamingContext ) }, null ); - - if( cons != null ) - { - cons.Invoke( obj, new object[] { serInfo, context } ); - } - else - { - log.error( $"ISerializable type {expectedType.Name} lacks the required constructor." ); - } - - if( obj is IDeserializationCallback cb ) - cb.OnDeserialization( obj ); - - return obj; - } -} - -// --- Collection Handler --- -public partial class CollectionHandler : ITypeHandler -{ - - public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing ) - { - // Determine element type - Type elemType = GetElementType( expectedType ); - - // Create a temporary list - var listType = typeof( List<> ).MakeGenericType( elemType ); - var list = (IList)Activator.CreateInstance( listType )!; - - xml.TrackIfGraph( list, elem ); // Track list if graph - - // Populate the list - foreach( XmlNode node in elem.ChildNodes ) - { - if( node is XmlElement childElem ) - { - list.Add( xml.ReadNode( childElem, elemType, null ) ); - } - } - - // Convert to the final expected type (Array, Immutable*, List) - return ConvertToFinalCollection( list, expectedType, elemType ); - } -} - - -// --- Object Handler (Default/Complex) --- -public partial class ObjectHandler : ITypeHandler -{ - public object? ReadXml( XmlSer xml, XmlElement elem, Type expectedType, object? existing ) - { - var actualType = xml._resolver.Resolve( elem, expectedType ); - var ti = xml._meta.Get( actualType ); - - // 1. Get/Create Instance - var (obj, _) = GetOrCreateInstance( xml, elem, actualType, existing ); - if( obj == null ) - return null; - - // Handle graph refs (if already processed) - if( xml._cfg.Structure == Datastructure.Graph && elem.HasAttribute( "ref" ) ) - { - long id = long.Parse( elem.GetAttribute( "ref" ) ); - if( xml._processed.TryGetValue( id, out var processedObj ) ) - return processedObj; - } - - // Track if it's new - xml.TrackIfGraph( obj, elem ); - - // 2. Hydrate - foreach( var memberMeta in ti.Members ) - { - - - - { - var (valueSource, isAttribute) = FindValueSource( elem, memberMeta.XmlName ); - - if( valueSource != null ) - { - object? memberValue; - object? currentMemberValue = memberMeta.GetValue( obj ); - - if( isAttribute ) - { - memberValue = xml._resolver.ConvertSimple( valueSource.Value!, memberMeta.Type ); - } - else // Child Element - { - memberValue = xml.ReadNode( (XmlElement)valueSource, memberMeta.Type, currentMemberValue ); - } - - // Set value, respecting ser.Do/ser.Dont and pre-hydration - if( ShouldSetValue( memberMeta, existing != null ) ) - { - memberMeta.SetValue( obj, memberValue ); - } - } - } - } - - // 3. Post-processing - if( obj is ser.I_Serialize iSer ) - obj = iSer.OnDeserialize( null ); - if( ti.IsImm && obj is io.Obj immObj ) - return immObj.Record( $"From XML {elem.Name}" ); - - return obj; - } -} - diff --git a/ser/XmlSer_Tests.cs b/ser/XmlSer_Tests.cs deleted file mode 100644 index 428f57b..0000000 --- a/ser/XmlSer_Tests.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Text; -//using Org.BouncyCastle.Crypto.IO; - -namespace ser; - - -public record class SimpleImmutable( string Name, int Age ) : io.Timed; - -static public class Test -{ - - public class ExternalClass - { - public string ActualProperty { get; set; } = "test_ActualProperty_set_inline"; - //public string ActualProperty_NotSerialized { get; set; } = "ActualProperty_NotSerialized"; - } - - [ser.Ser( Types = ser.Types.Implied )] - public partial class LeaveWithExternalClasss - { - - //[ser.Do] - //public bool _cccwp_doBool = true; - - [ser.ChildPropsAttribute( "ActualProperty" )] - public ExternalClass _leaf_external = new(); - - //public string _cccwp_doNotSerialize = "test_do_not_serialize"; - - } - - [ser.Ser] - public class TrunkClass - { - public LeaveWithExternalClasss _trunk_leaf = new(); - //public int _chf_test = 10; - //private string _chf_priv_string = "test_priv_string"; - }; - - public static void Serialization() - { - ser.XmlCfg cfg = new() - { - Verbose = true, - }; - - ser.TypeMetaCache metaCache = new( cfg ); - //metaCache.AddType( typeof( ClassContainsClassWithProp ), "ActualProperty" ); - - - - - TrunkClass trunk = new() - { - _trunk_leaf = new() - { - _leaf_external = new() - { - ActualProperty = "ActualProperty_set_in_cons" - } - } - }; - - Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "ActualProperty_set_in_cons" ); - - - var memStream = new MemoryStream(); - - { - var xml = new ser.XmlSer( cfg, metaCache ); - xml.Serialize( memStream, trunk ); - } - - memStream.Position = 0; - - var strXml = System.Text.Encoding.UTF8.GetString( memStream.ToArray() ); - - var badXml = "\n \n"; - ///* - var badXml_02 = @""" - - - - - - - -"""; - //*/ - Debug.Assert( strXml != badXml ); - - memStream.Position = 0; - - var classHasFields2 = new TrunkClass(); - //classHasFields2._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized = "ActualProperty_NotSerialized_set_in_test_01"; - - Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "test_ActualProperty_set_inline" ); - //Debug.Assert( classHasFields2._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized" ); - - - { - var xml = new ser.XmlSer( cfg, metaCache ); - classHasFields2 = xml.Deserialize( memStream ); - } - - - Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "test_ActualProperty_set_inline" ); - //Debug.Assert( classHasFields2._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" ); - - memStream.Position = 0; - - var classHasFields3 = new TrunkClass(); - - { - var xml = new ser.XmlSer( cfg, metaCache ); - xml.DeserializeInto( memStream, classHasFields3 ); - } - - Debug.Assert( trunk._trunk_leaf._leaf_external.ActualProperty == "test_ActualProperty_set_inline" ); - //Debug.Assert( classHasFields3._chf_prop._cccwp_propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" ); - - - } - - - -} - diff --git a/ser/XmlSer_Write.cs b/ser/XmlSer_Write.cs deleted file mode 100644 index 77758cb..0000000 --- a/ser/XmlSer_Write.cs +++ /dev/null @@ -1,201 +0,0 @@ - - - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; -using System.Xml; - -namespace ser; - - -// --- Primitive Handler --- -public partial class PrimitiveHandler : ser.ITypeHandler -{ - public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType ) - { - if( obj == null ) - { - writer.WriteStartElement( name ); - writer.WriteAttributeString( "v", "null" ); - writer.WriteEndElement(); - return; - } - - bool writeElements = xml._cfg.POD == POD.Elements || forceType || !( writer is XmlTextWriter ); - if( !writeElements && writer is XmlTextWriter tw ) - writeElements = tw.WriteState != WriteState.Element; - - if( writeElements ) - writer.WriteStartElement( name ); - - if( forceType || xml._cfg.POD == POD.Elements ) - { - if( forceType ) - writer.WriteAttributeString( "_.t", obj.GetType().FullName ); - - writer.WriteAttributeString( "v", obj.ToString() ); - } - else - { - writer.WriteAttributeString( name, obj.ToString() ); - } - - if( writeElements ) - writer.WriteEndElement(); - } -} - -// --- Proxy Handler --- -public partial class ProxyHandler : ITypeHandler -{ - public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType ) - { - if( obj == null ) - { xml.GetHandler( typeof( object ) ).WriteXml( xml, writer, null, name, memberType, forceType ); return; } - - var ti = xml._meta.Get( obj.GetType() ); - if( !ti.ProxyDef.HasValue ) - { log.error( "Proxy write called without proxy def!" ); return; } - - writer.WriteStartElement( name ); - var proxyStr = ti.ProxyDef.Value.fnSer( obj ); - - // TODO: Allow arbitrary writing here - writer.WriteAttributeString( "proxy", proxyStr ); - writer.WriteEndElement(); - } -} - -// --- ISerializable Handler --- -public partial class ISerializableHandler : ITypeHandler -{ - public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType ) - { - if( obj == null ) - { /* Write null */ return; } - if( !( obj is ISerializable serObj ) ) - { /* Error */ return; } - - writer.WriteStartElement( name ); - xml.WriteTypeAttr( writer, memberType, obj.GetType() ); - - if( xml.HandleGraphWrite( writer, obj, out bool first ) ) - { - if( first ) - { - var serInfo = new SerializationInfo( obj.GetType(), new FormatterConverter() ); - var context = new StreamingContext( StreamingContextStates.All ); - serObj.GetObjectData( serInfo, context ); - - foreach( var member in serInfo ) - { - xml.WriteNode( writer, member.Value, refl.TypeToIdentifier( member.Name ), member.ObjectType, true ); // Force type for ISer - } - } - } - writer.WriteEndElement(); - } -} - -// --- Collection Handler --- -public partial class CollectionHandler : ITypeHandler -{ - public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType ) - { - if( obj == null ) - { /* Write null */ return; } - if( !( obj is IEnumerable collection ) ) - { /* Error */ return; } - - writer.WriteStartElement( name ); - xml.WriteTypeAttr( writer, memberType, obj.GetType() ); - - if( xml.HandleGraphWrite( writer, obj, out bool first ) ) - { - if( first ) - { - Type elemType = GetElementType( obj.GetType() ); - int i = 0; - foreach( var item in collection ) - { - xml.WriteNode( writer, item, $"i{i++}", elemType, false ); - } - } - } - writer.WriteEndElement(); - } -} - - -// --- Object Handler (Default/Complex) --- -public partial class ObjectHandler : ITypeHandler -{ - public void WriteXml( XmlSer xml, XmlWriter writer, object? obj, string name, Type memberType, bool forceType ) - { - if( obj == null ) - { /* Write null */ return; } - - writer.WriteStartElement( name ); - xml.WriteTypeAttr( writer, memberType, obj.GetType() ); - var ti = xml._meta.Get( obj.GetType() ); - - if( xml.HandleGraphWrite( writer, obj, out bool first ) ) - { - if( first ) - { - foreach( var memberMeta in ti.Members ) - { - if( !memberMeta.IsPodAttribute ) - continue; - - var value = memberMeta.GetValue( obj ); - if( value != null ) - { - try - { - writer.WriteAttributeString( memberMeta.XmlName, value.ToString() ); - } - catch( Exception ex ) - { - log.exception( ex, $"Writing Att {memberMeta.XmlName} = [{value}]" ); - } - } - } - - - foreach( var memberMeta in ti.Members ) - { - if( memberMeta.IsPodAttribute ) - continue; - - var value = memberMeta.GetValue( obj ); - if( value != null ) - { - try - { - xml.WriteNode( writer, value, memberMeta.XmlName, memberMeta.Type, false ); - } - catch( Exception ex ) - { - log.exception( ex, $"Writing Node {memberMeta.XmlName} = [{value}]" ); - } - } - - - } - - - - - } - } - - writer.WriteEndElement(); - } -} - diff --git a/srl/srl.Core.cs b/srl/srl.Core.cs deleted file mode 100644 index 05016fa..0000000 --- a/srl/srl.Core.cs +++ /dev/null @@ -1,375 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace srl; - -// --- INTERFACES --- - -public interface Driver -{ - // Structural - void BeginScope( string name, Type type, int id ); - void EndScope(); - void BeginCollection( string name, int count ); - void EndCollection(); - - // Data - void WriteAttr( string name, string value ); // Primitives & Compact Strings - void WriteRef( string name, int id ); // DAG/Cycle Reference - - // Metadata Hook - void OnProp( MemberInfo member, string name ); -} - -public interface IInput -{ - string? Value { get; } - bool IsLeaf { get; } - IInput? GetAttr( string name ); - IInput? GetChild( string name ); -} - -// --- THE MODEL (Introspection) --- - -public class TypePlan -{ - public string Name; - public bool IsCollection; - public bool IsHybrid; // True if Type has a StringParser registered - public StringParser Parser; // The "Compact" converter - public List Props = new(); -} - -public class PropPlan -{ - public string Name; - public string MemberName; - public Type Type; - public bool IsSmart; // Primitive/Enum/String - public MemberInfo Info; - - // Fast Accessors - public Func Getter; - public Action Setter; -} - -public static class Model -{ - private static Dictionary _cache = new(); - - public static TypePlan Get( Type t ) - { - if( _cache.TryGetValue( t, out var plan ) ) - return plan; - - plan = new TypePlan { Name = t.Name }; - - // 1. Check for Custom Parsers (Hybrid Mode) - if( Parsers.TryGet( t, out var parser ) ) - { - plan.IsHybrid = true; - plan.Parser = parser; - } - - // 2. Check for Collections - if( t != typeof( string ) && typeof( IEnumerable ).IsAssignableFrom( t ) ) - { - plan.IsCollection = true; - _cache[t] = plan; - return plan; - } - - // 3. Scan Properties - foreach( var p in t.GetProperties( BindingFlags.Public | BindingFlags.Instance ) ) - { - if( Attribute.IsDefined( p, typeof( IgnoreAttribute ) ) ) - continue; - - var prop = new PropPlan - { - Name = p.Name, // Default to PascalCase, Drivers can lower if needed - MemberName = p.Name, - Type = p.PropertyType, - Info = p, - Getter = ( o ) => p.GetValue( o ), - Setter = ( o, v ) => p.SetValue( o, v ) - }; - - // Override name - var nameAttr = p.GetCustomAttribute(); - if( nameAttr != null ) - prop.Name = nameAttr.Name; - - // Is "Smart" (Atomic)? - prop.IsSmart = prop.Type.IsPrimitive || prop.Type.IsEnum || - prop.Type == typeof( string ) || prop.Type == typeof( Guid ); - - plan.Props.Add( prop ); - } - - _cache[t] = plan; - return plan; - } -} - -// --- ATTRIBUTES --- -public class IgnoreAttribute : Attribute { } -public class NameAttribute : Attribute { public string Name; public NameAttribute( string n ) => Name = n; } - -// --- UTILITIES --- - -public struct StringParser -{ - public Func To; - public Func From; -} - -public static class Parsers -{ - private static Dictionary _registry = new(); - - public static void Register( Func to, Func from ) - { - _registry[typeof( T )] = new StringParser { To = o => to( (T)o ), From = s => from( s ) }; - } - - public static bool TryGet( Type t, out StringParser p ) - { - if( _registry.TryGetValue( t, out p ) ) - return true; - // Auto-Discovery could go here (Static Parse methods) - return false; - } -} - -public static class Binder -{ - // Handles "Cool.Rare" dot notation - public static void Apply( object root, string path, string value ) - { - var current = root; - var parts = path.Split( '.' ); - - for( int i = 0; i < parts.Length; i++ ) - { - var part = parts[i]; - var isLast = i == parts.Length - 1; - var plan = Model.Get( current.GetType() ); - - // Case-Insensitive Match - var prop = plan.Props.Find( p => p.Name.Equals( part, StringComparison.OrdinalIgnoreCase ) ); - if( prop == null ) - return; - - if( isLast ) - { - var val = ParseUtils.Convert( value, prop.Type ); - prop.Setter( current, val ); - } - else - { - var next = prop.Getter( current ); - if( next == null ) - { - next = Activator.CreateInstance( prop.Type ); - prop.Setter( current, next ); - } - current = next; - } - } - } -} - -public static class ParseUtils -{ - public static object Convert( string raw, Type t ) - { - if( t == typeof( string ) ) - return raw; - if( t == typeof( int ) ) - return int.Parse( raw ); - if( t == typeof( float ) ) - return float.Parse( raw.Replace( "f", "" ) ); - if( t == typeof( bool ) ) - return bool.Parse( raw ); - if( t.IsEnum ) - return Enum.Parse( t, raw ); - // Fallback to TypeConverter - var cv = TypeDescriptor.GetConverter( t ); - if( cv != null && cv.CanConvertFrom( typeof( string ) ) ) - return cv.ConvertFrom( raw ); - return null; - } - - public static void CopyFields( object src, object dst ) - { - foreach( var p in src.GetType().GetProperties() ) - if( p.CanRead && p.CanWrite ) - p.SetValue( dst, p.GetValue( src ) ); - } -} - -// --- THE ENGINE (Walker & Loader) --- - -public static class Walker -{ - public class Context - { - private Dictionary _seen = new( ReferenceEqualityComparer.Instance ); - private int _nextId = 1; - public (int, bool) GetId( object o ) - { - if( _seen.TryGetValue( o, out var id ) ) - return (id, false); - _seen[o] = _nextId; - return (_nextId++, true); - } - } - - public static void Serialize( object root, Driver d ) - { - if( root == null ) - return; - SerializeRecursive( root, d, new Context(), "root", null ); - } - - private static void SerializeRecursive( object obj, Driver d, Context ctx, string name, MemberInfo? member ) - { - if( obj == null ) - return; - - Type type = obj.GetType(); - var plan = Model.Get( type ); - - // STRATEGY 1: COMPACT (Hybrid Parser) - if( plan.IsHybrid ) - { - if( member != null ) - d.OnProp( member, name ); - d.WriteAttr( name, plan.Parser.To( obj ) ); - return; - } - - // STRATEGY 2: DAG CHECK - bool isRef = !type.IsValueType && type != typeof( string ); - if( isRef ) - { - var (id, isNew) = ctx.GetId( obj ); - if( !isNew ) - { d.WriteRef( name, id ); return; } - if( member != null ) - d.OnProp( member, name ); - d.BeginScope( name, type, id ); - } - else - { - if( member != null ) - d.OnProp( member, name ); - d.BeginScope( name, type, 0 ); - } - - // STRATEGY 3: COLLECTIONS - if( plan.IsCollection ) - { - var list = (IEnumerable)obj; - int count = 0; // Simple count (could optimize for ICollection) - foreach( var _ in list ) - count++; - - d.BeginCollection( name, count ); - foreach( var item in list ) - SerializeRecursive( item, d, ctx, "item", null ); - d.EndCollection(); - } - else - { - // STRATEGY 4: STANDARD OBJECT - foreach( var prop in plan.Props ) - { - var val = prop.Getter( obj ); - if( prop.IsSmart ) - { - d.OnProp( prop.Info, prop.Name ); - d.WriteAttr( prop.Name, val?.ToString() ?? "" ); - } - else - { - if( val != null ) - SerializeRecursive( val, d, ctx, prop.Name, prop.Info ); - } - } - } - d.EndScope(); - } -} - -public static class Loader -{ - public static void Load( object target, IInput input ) - { - if( target == null || input == null ) - return; - var plan = Model.Get( target.GetType() ); - - // 1. HYBRID PARSE (Compact String) - // If we have a parser AND input is just a value "1,1" - if( plan.IsHybrid && input.IsLeaf && !string.IsNullOrWhiteSpace( input.Value ) ) - { - try - { - var newObj = plan.Parser.From( input.Value ); - ParseUtils.CopyFields( newObj, target ); - return; - } - catch { } - } - - // 2. STRUCTURAL MAP - foreach( var prop in plan.Props ) - { - // Look for Attribute OR Child Element - var sub = input.GetAttr( prop.Name ) ?? input.GetChild( prop.Name ); - - // Look for Dot Notation (e.g. "Pos.X") in attributes - // (Note: This simple loop doesn't scan ALL attrs for dots, - // it relies on the caller or specific recursive logic. - // For full dot support on root, we need to iterate input attributes if possible. - // But 'Binder' below handles it if we pass the specific attr key). - - if( sub != null ) - { - if( prop.IsSmart ) - { - if( sub.Value != null ) - prop.Setter( target, ParseUtils.Convert( sub.Value, prop.Type ) ); - } - else - { - var child = prop.Getter( target ); - if( child == null ) - { - child = Activator.CreateInstance( prop.Type ); - prop.Setter( target, child ); - } - Load( child, sub ); - } - } - } - } - - // Helper to scan all attributes on an element for "Cool.Rare" patterns - public static void LoadDotNotations( object target, IEnumerable<(string k, string v)> attrs ) - { - foreach( var (k, v) in attrs ) - { - if( k.Contains( '.' ) ) - Binder.Apply( target, k, v ); - } - } -} - diff --git a/srl/srl.Debug.cs b/srl/srl.Debug.cs deleted file mode 100644 index 0012b07..0000000 --- a/srl/srl.Debug.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Reflection; -using System.Text; - -namespace srl.Debug; - -public class DebugDriver : Driver -{ - private StringBuilder _sb = new(); - private int _indent = 0; - - public override string ToString() => _sb.ToString(); - - private void Line( string s ) => _sb.AppendLine( new string( ' ', _indent * 2 ) + s ); - - public void BeginScope( string name, Type type, int id ) - { - var refStr = id > 0 ? $" #{id}" : ""; - Line( $"[{name}] <{type.Name}>{refStr}" ); - _indent++; - } - - public void EndScope() => _indent--; - - public void BeginCollection( string name, int count ) - { - Line( $"[{name}] (Count: {count})" ); - _indent++; - } - - public void EndCollection() => _indent--; - - public void WriteAttr( string name, string value ) - { - Line( $"{name} = {value}" ); - } - - public void WriteRef( string name, int id ) - { - Line( $"{name} -> See #{id}" ); - } - - public void OnProp( MemberInfo m, string name ) - { - // Could log attributes here, e.g. [Tooltip] - } -} diff --git a/srl/srl.Xml.cs b/srl/srl.Xml.cs deleted file mode 100644 index 4101906..0000000 --- a/srl/srl.Xml.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; -using System.Reflection; - -namespace srl.Xml; - -// --- INPUT ADAPTER --- -public class XmlAdapter : IInput -{ - private XElement _e; - private XAttribute _a; - - public XmlAdapter( XElement e ) => _e = e; - public XmlAdapter( XAttribute a ) => _a = a; - - public string? Value => _e?.Value ?? _a?.Value; - public bool IsLeaf => _a != null || ( _e != null && !_e.HasElements ); - - public IInput? GetAttr( string name ) - { - if( _e == null ) - return null; - // Case-Insensitive search - var a = _e.Attribute( name ) ?? _e.Attribute( name.ToLower() ); - return a != null ? new XmlAdapter( a ) : null; - } - - public IInput? GetChild( string name ) - { - if( _e == null ) - return null; - - var c = _e.Element( name ) ?? _e.Element( name.ToLower() ); - - return c != null ? new XmlAdapter( c ) : null; - } - - // Extra helper for Dot Notation scanning - public IEnumerable<(string, string)> GetAllAttrs() - { - if( _e == null ) - yield break; - foreach( var a in _e.Attributes() ) - yield return (a.Name.LocalName, a.Value); - } -} - -// --- OUTPUT DRIVER --- -public class XmlDriver : Driver -{ - private Stack _stack = new(); - private XDocument _doc; - - public XDocument Document => _doc; - - public XmlDriver() - { - _doc = new XDocument(); - } - - public void BeginScope( string name, Type type, int id ) - { - var el = new XElement( name ); - - // Polymorphism Metadata (if needed, e.g. ) - // el.Add(new XAttribute("_type", type.Name)); - - if( id > 0 ) - el.Add( new XAttribute( "_id", id ) ); // DAG ID - - if( _stack.Count > 0 ) - _stack.Peek().Add( el ); - else - _doc.Add( el ); - - _stack.Push( el ); - } - - public void EndScope() => _stack.Pop(); - - public void BeginCollection( string name, int count ) - { - // XML doesn't strictly need array wrappers, but it helps structure - // We use the same BeginScope logic effectively - var el = new XElement( name, new XAttribute( "_count", count ) ); - if( _stack.Count > 0 ) - _stack.Peek().Add( el ); - _stack.Push( el ); - } - - public void EndCollection() => _stack.Pop(); - - public void WriteAttr( string name, string value ) - { - if( _stack.Count == 0 ) - return; - _stack.Peek().Add( new XAttribute( name, value ) ); - } - - public void WriteRef( string name, int id ) - { - var el = new XElement( name, new XAttribute( "_ref", id ) ); - _stack.Peek().Add( el ); - } - - public void OnProp( MemberInfo m, string name ) { /* Optional: Write tooltips/comments */ } -} - -// --- FACADE --- -public static class XmlSerializer -{ - public static string Serialize( object obj ) - { - var driver = new XmlDriver(); - srl.Walker.Serialize( obj, driver ); - return driver.Document.ToString(); - } - - public static void Deserialize( object root, string xml ) - { - var doc = XDocument.Parse( xml ); - var adapter = new XmlAdapter( doc.Root ); - - // 1. Standard Load - srl.Loader.Load( root, adapter ); - - // 2. Dot Notation Pass (MyBag Cool.Rare="Changed") - srl.Loader.LoadDotNotations( root, adapter.GetAllAttrs() ); - } -} diff --git a/task/Task.cs b/task/Task.cs deleted file mode 100644 index f5bfdb7..0000000 --- a/task/Task.cs +++ /dev/null @@ -1,16 +0,0 @@ - -using System.Threading.Tasks; -using System.Threading; -using System.Diagnostics; - - -namespace lib; - - -static public class Task -{ - - - - -} diff --git a/tests/Tests.cs b/tests/Tests.cs deleted file mode 100644 index ca21138..0000000 --- a/tests/Tests.cs +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - -using System.Diagnostics; -using System.IO; - -namespace test; - - -public record class SimpleImmutable( string Name, int Age ) : io.Timed; - -static public class XmlFormatter2 -{ - - public class ClassWithProperties - { - public string ActualProperty { get; set; } = "test_ActualProperty_set_inline"; - public string ActualProperty_NotSerialized { get; set; } = "ActualProperty_NotSerialized"; - } - - [ser.Ser( Types = ser.Types.Implied )] - public partial class ClassContainsClassWithProp - { - - [ser.Do] - public bool doBool = true; - - [ser.ChildPropsAttribute( "ActualProperty" )] - public ClassWithProperties propHolder = new(); - - public string doNotSerialize = "test_do_not_serialize"; - - } - - [ser.Ser] - public class ClassHasFields - { - public ClassContainsClassWithProp prop = new(); - }; - - public static void Serialization() - { - - lib.XmlFormatter2Cfg cfg = new() - { - - }; - - - - - ClassHasFields classHasFields = new() - { - prop = new() - { - propHolder = new() - { - ActualProperty = "ActualProperty_set_in_cons" - } - } - }; - - Debug.Assert( classHasFields.prop.propHolder.ActualProperty == "ActualProperty_set_in_cons" ); - - - var memStream = new MemoryStream(); - - { - var xml = new lib.XmlFormatter2( cfg ); - xml.Serialize( memStream, classHasFields ); - } - - memStream.Position = 0; - - var strXml = System.Text.Encoding.UTF8.GetString( memStream.ToArray() ); - - var badXml = "\n \n"; - /* - - - - -*/ - Debug.Assert( strXml != badXml ); - - memStream.Position = 0; - - var classHasFields2 = new ClassHasFields(); - classHasFields2.prop.propHolder.ActualProperty_NotSerialized = "ActualProperty_NotSerialized_set_in_test_01"; - - Debug.Assert( classHasFields2.prop.propHolder.ActualProperty == "test_ActualProperty_set_inline" ); - Debug.Assert( classHasFields2.prop.propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized" ); - - - { - var xml = new lib.XmlFormatter2( cfg ); - classHasFields2 = xml.Deserialize( memStream ); - } - - - Debug.Assert( classHasFields2.prop.propHolder.ActualProperty == "ActualProperty_set_in_cons" ); - Debug.Assert( classHasFields2.prop.propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" ); - - memStream.Position = 0; - - var classHasFields3 = new ClassHasFields(); - - { - var xml = new lib.XmlFormatter2( cfg ); - xml.DeserializeInto( memStream, classHasFields3 ); - } - - Debug.Assert( classHasFields3.prop.propHolder.ActualProperty == "ActualProperty_set_in_cons" ); - Debug.Assert( classHasFields3.prop.propHolder.ActualProperty_NotSerialized == "ActualProperty_NotSerialized_set_in_test_01" ); - - - } - - - -} -