/// /// Utility classes /// 14 Mar 2009 - D.Bozarth - Sonoma State Engineering Science /// #region Using directives using System; using System.Collections.Generic; using System.IO; #endregion namespace Utility { /* * T, U should be Comparable */ public class Pair { #region Methods of class Pair // public Pair() { } public Pair( T first, U second, string delim ) { this.First = first; this.Second = second; this.DelimString = delim; } public Pair( T first, U second ) { this.First = first; this.Second = second; this.DelimString = ", "; } public bool Equals( Pair other ) { return other.First.Equals( this.First ) && other.Second.Equals(this.Second); } public string ToString( string delimiter ) { return First.ToString() + DelimString + Second.ToString(); } // #endregion #region Properties of class Pair // public T First { get {return first;} set {first = value;} } public U Second { get {return second;} set {second = value;} } public string DelimString { get {return delimString;} set {delimString = value;} } // #endregion #region Data of class Pair // private T first; private U second; private string delimString; // #endregion } // class Pair public class Io { // The following 3 class members must be maintained in sync: public enum Result { Exception = -4, BadFormat, BadIndex, Underflow, OK = 0, Overflow, Mismatch, NoAction }; private static string[] ResultMsg = { "Exception", "Bad Format", "Bad Index", "Underflow", "OK", "Overflow", "Mismatch", "No Action" }; const int ResultOffset = 4; // Translates Result to ResultMsg. // -------------------------------------------------------- public static string GetResultTag( Io.Result ior ) { return ResultMsg[ (int) ior + ResultOffset ]; } } // class Io public static class StreamProcessor { public const int BufSizeDflt = 0x1000; /* * Post: No problem -> return 0; otherwise return nonzero * parameters may have been modified. */ public delegate Io.Result Transform( Stream inStream, Stream outStream, int[] parameters ); /* * If sizes of inStream and h * w don't match, the method will still perform * .. a transpose, but the result may not be useful. */ public static Io.Result TransformTranspose( Stream inStream, Stream outStream, int[] hw ) { int h = hw[0]; int w = hw[1]; byte[,] buf = new byte[h, w]; Io.Result rtn = StreamToArray2D( inStream, buf ); for ( int i = 0; i < w; ++i ) { for ( int j = 0; j < h; ++j ) { outStream.WriteByte( buf[j, i] ); // transposed } } return rtn; } // method TransformTranspose public static Io.Result TransformCopyStream( Stream inStream, Stream outStream, int[] bufSzOrEmpty ) { int bufSize; if ( 0 < bufSzOrEmpty.Length ) { bufSize = bufSzOrEmpty[0]; } else { bufSize = BufSizeDflt; } CopyStream( inStream, outStream, bufSize ); return Io.Result.OK; } /* * Post: outStream has the indicated prefix stripped off, otherwise identical to inStream. */ public static Io.Result CopyStreamMinusPrefix( Stream inStream, Stream outStream, int preSize, int bufSize ) { byte[] nothing = new byte[ preSize ]; Io.Result rtn = StreamProcessor.ReadByteStream( nothing, 0, preSize, inStream ); if ( rtn != Io.Result.BadIndex ) { CopyStream( inStream, outStream, bufSize ); } return rtn; } /* * Adapted from http://stackoverflow.com/questions/129305/ * .. how-to-write-the-content-of-one-stream-into-another-stream-in-net */ public static void CopyStream( Stream inStream, Stream outStream, int bufSize ) { byte[] buffer = new byte[ bufSize ]; int nRead; while ( 0 < (nRead = inStream.Read( buffer, 0, bufSize )) ) { outStream.Write(buffer, 0, nRead); } } public static void MergeStreams( Stream outStream, Stream[] inStreams, int[] counts ) { bool goFlg = true; while ( goFlg ) { goFlg = false; for ( int i = 0; i < counts.Length; ++i ) { Stream source = inStreams[i]; for ( int j = 0; j < counts[i]; ++j ) { if ( source.Position < source.Length ) { int next = source.ReadByte(); outStream.WriteByte( (byte) next ); } } if ( source.Position < source.Length ) goFlg = true; } } } public static Io.Result StreamToArray2D( Stream inStream, byte[,] mat ) { int h = mat.GetLength( 0 ); int w = mat.GetLength( 1 ); long len = inStream.Length; Io.Result rtn = Io.Result.OK; inStream.Position = 0; for ( int i = 0; i < h; ++i ) { for ( int j = 0; j < w; ++j ) { if ( inStream.Position < len ) { mat[i, j] = (byte) inStream.ReadByte(); } else { // Stream has overflown the array. rtn = Io.Result.Overflow; break; } } } if ( inStream.Position + 1 < len ) { // Stream couldn't fill the array. rtn = Io.Result.Underflow; } return rtn; } // method StreamToArray2D /* * outStream will be populated starting at position outStream.Length */ public static void Array2DToStream( Stream outStream, byte[,] mat ) { int h = mat.GetLength( 0 ); int w = mat.GetLength( 1 ); for ( int i = 0; i < h; ++i ) { for ( int j = 0; j < w; ++j ) { outStream.WriteByte( mat[i, j] ); } } } /* * Limitation: Works only with integer-sized arrays and streams. */ public static Io.Result ReadByteStream( byte[] buf, int start, int count, Stream inStream ) { int sum = start + count; if ( start < 0 || count < 0 || buf.Length < sum || (int) inStream.Length < sum ) { return Io.Result.BadIndex; } for ( int n = start, m = 0; n < count; n += m ) { m = inStream.Read( buf, n, count - n ); } return Io.Result.OK; } } // class StreamProcessor public static class FileProcessor { public static void Catenate( string outFile, string[] inFiles, bool addByteCount ) { File.Delete( outFile ); FileStream outStream = File.OpenWrite( outFile ); try { foreach ( string file in inFiles ) { byte[] next = File.ReadAllBytes( file ); if ( addByteCount ) { byte[] nextBytes = BitConverter.GetBytes( next.Length ); outStream.Write( nextBytes, 0, nextBytes.Length ); } outStream.Write( next, 0, next.Length ); } } catch ( Exception ex ) { outStream.Close(); throw ex; } outStream.Close(); } // method Catenate /* * Pre: The content of inFile is partitioned by a set of leading counts, * .. each of which gives the length in bytes of the following subset. * Each count is an Int32. * Post: Each file pointed by outFiles, has the number of bytes indicated * .. by its corresponding count in inFile. * Each count has been discarded. */ public static Io.Result Decatenate( string inFile, string[] outFiles ) { FileStream inStream = File.OpenRead( inFile ); Io.Result rtn = Io.Result.OK; try { foreach ( string file in outFiles ) { int sz = sizeof( Int32 ); byte[] countBytes = new byte[ sz ]; rtn = StreamProcessor.ReadByteStream( countBytes, 0, sz, inStream ); if ( rtn == Io.Result.BadIndex ) return rtn; Int32 count = BitConverter.ToInt32( countBytes, 0 ); byte[] next = new byte[count]; rtn = StreamProcessor.ReadByteStream( next, 0, count, inStream ); if ( rtn == Io.Result.BadIndex ) return rtn; File.Delete( file ); FileStream outStream = File.OpenWrite( file ); try { outStream.Write( next, 0, count ); } catch ( Exception ex ) { outStream.Close(); throw ex; } outStream.Close(); } } catch ( Exception ex ) { inStream.Close(); throw ex; } inStream.Close(); return rtn; } // method Decatenate /* * Post: outFile has the indicated prefix stripped off, otherwise identical to inFile. */ public static Io.Result CopyFileMinusPrefix( string inFile, string outFile, int preSize, int bufSize ) { Io.Result rtn = Io.Result.OK; FileStream inStream = File.OpenRead( inFile ); FileStream outStream = File.OpenWrite( outFile ); try { rtn = StreamProcessor.CopyStreamMinusPrefix( inStream, outStream, preSize, bufSize ); } catch ( Exception ex ) { string msg = "Caught in FileProcessor.CopyFileMinusPrefix(string,string,int,int)"; throw new Exception( msg, ex ); } finally { outStream.Close(); inStream.Close(); } return rtn; } // method CopyFileMinusPrefix } // class FileProcessor public static class ArrayQuery { public static bool Contains( T[] ary, T val ) { foreach ( T test in ary ) { if ( test.Equals(val) ) return true; } return false; } public static void JustifyLimits( ref int tstLo, ref int tstHi, int limLo, int limHi ){ tstLo = limHi < tstLo ? limHi : tstLo; tstHi = limHi < tstHi ? limHi : tstHi; tstLo = tstLo < limLo ? limLo : tstLo; tstHi = tstHi < limLo ? limLo : tstHi; if ( tstHi < tstLo ) { int tmp = tstHi; tstHi = tstLo; tstLo = tmp; } } // method JustifyLimits } // class ArrayQuery public static class ByteArrayManipulator { public static byte[,] Transpose( byte[,] mat ) { int h = mat.GetLength(0); int w = mat.GetLength(1); byte[,] x = new byte[w, h]; for ( int i = 0; i < h; ++i ) { for ( int j = 0; j < w; ++j ) { x[j, i] = mat[i, j]; } } return x; } // method Transpose /* * PickPeriodicValues: * Return the sub-matrix composed of all chosen cells. * X must be a rectangular array. * Periodic specification: m[], n specify the rows; p[], q specify the columns. * If n == 0, then all rows are specified. * If q == 0, then all cols are specified. * If m is empty or p is empty, then an empty array is returned. * The intersection of the row spec & col spec, is chosen. * Example: m=[2,3], n=4 => The 3rd & 4th rows in each group of 4 rows is specified. * p=[1], q=6 => The 2nd col in each group of 6 cols is specified. * The returned matrix contains values with the following indices: * (2, 3), (3, 1), (2, 7), (3, 7)... * (6, 1), (7, 1), (6, 7), (7, 7)... etc. * 7 Feb 2009 - Ported from Matlab. D.Bozarth */ public static byte[,] PickPeriodicValues ( byte[,] X, int[] m, int n, int[] p, int q ){ List block = new List(); int h = 0; int w = 0; if ( 0 < m.Length && 0 < p.Length ) { h = X.GetLength(0); w = X.GetLength(1); } for ( int i = 0; i < h; ++i ) { if ( n == 0 || ArrayQuery.Contains(m, (i+n) % n) ) { // The current row is specified for output. if ( q == 0 ) { // All columns are specified for output. byte[] row = new byte[w]; for ( int k = 0; k < w; ++k ) row[k] = X[i, k]; block.Add( row ); } else { List list = new List(); for ( int j = 0; j < w; ++j ) { if ( ArrayQuery.Contains(p, (j+q) % q) ) { // The current column is specified for output. list.Add( X[i, j] ); } } byte[] row = new byte[list.Count]; list.CopyTo( row ); block.Add( row ); } } } int a = block.Count; if ( a == 0 ) { return new byte[0, 0]; } int b = block[0].Length; byte[,] Y = new byte[a, b]; for ( int i = 0; i < a; ++i ) { for ( int j = 0; j < b; ++j ) { Y[i, j] = block[i][j]; } } return Y; } // method PickPeriodicValues /* * Pre: Dimensions of dest array must be able to accomodate the addition * .. of src at the indicated starting indices. * Post: dest contains all values from src, in addition to its prior contents. */ public static void CopyArrayByColumn( byte[,] dest, int ndxV, int ndxH, byte[,] src ){ int dimV = src.GetLength(0); int dimH = src.GetLength(1); for ( int i = 0; i < dimV; ++i ) { int row = ndxV + i; for ( int j = 0; j < dimH; ++j ) { dest[row, ndxH + j] = src[i, j]; } } } // method CopyArrayByColumn /* * Pre: (1) src1 and src2 must have the same dimensions. * (2) Dimensions of dest array must be able to accomodate the addition * .. of src1 & src2 interleaved at the indicated starting indices. * Post: dest contains interleaved data from src1 & src2, in addition to * .. its prior contents. */ public static void InterleaveTwoArraysByColumn( byte[,] dest, int ndxV, int ndxH, byte[,] src1, byte[,] src2 ){ int dimV = src1.GetLength(0); int dimH = src1.GetLength(1); for ( int i = 0; i < dimV; ++i ) { int row = ndxV + i; for ( int j = 0; j < dimH; ++j ) { dest[row, ndxH + 2*j] = src1[i, j]; dest[row, ndxH + 2*j + 1] = src2[i, j]; } } } // method InterleaveTwoArraysByColumn /* * Pre: All 4 arrays must have the same dimensions. * The width of each array must divide nBytesPerValue. * Each nBytesPerComponent-byte set of array positions represents * .. a single-precision floating-point number. * Post: Using the first pair of arrays as input, the conversion indicated by * ... toPolFlg (0 => polar-to-rectangular, 1 => rectangular-to-polar) * ... appears in the second pair of arrays. */ public static void ConvertCoordinates( byte[,] reOrMa, byte[,] imOrAn, byte[,] maOrRe, byte[,] anOrIm, int nBytesPerValue, bool toPolFlg ){ int h = reOrMa.GetLength(0); int w = reOrMa.GetLength(1) / nBytesPerValue; for ( int i = 0; i < h; ++i ) { for ( int j = 0; j < w; ++j ) { int pos = j * nBytesPerValue; byte[] a = new byte[nBytesPerValue]; byte[] b = new byte[nBytesPerValue]; for ( int k = 0; k < nBytesPerValue; ++k ) { a[k] = reOrMa[i, pos + k]; b[k] = imOrAn[i, pos + k]; } float af = BitConverter.ToSingle( a, 0 ); float bf = BitConverter.ToSingle( b, 0 ); double ad = (double) af, xd; double bd = (double) bf, yd; if ( toPolFlg ) { xd = Math.Sqrt( ad*ad + bd*bd ); // Magnitude yd = Math.Atan( Math.Abs(bd) / Math.Abs(ad) ); // Angle if ( bd < 0 && ad < 0 ) { yd += Math.PI; } else { if ( bd < 0 ) yd = 0 - yd; if ( ad < 0 ) yd = Math.PI - yd; } } else { xd = ad * Math.Cos( bd ); // Real yd = ad * Math.Sin( bd ); // Imaginary } float xf = (float) xd; float yf = (float) yd; a = BitConverter.GetBytes( xf ); b = BitConverter.GetBytes( yf ); for ( int k = 0; k < nBytesPerValue; ++k ) { maOrRe[i, pos + k] = a[k]; anOrIm[i, pos + k] = b[k]; } } } } // method ConvertCoordinates } // class ByteArrayManipulator // // BinaryReader, BinaryWriter, BitConverter were the way to go instead of this!! // .. next time ... // public static class ByteArrayConverter { /* * Pre: (1) inStream: Each comma-separated group of bytes represents a numeric char array. * (2) Each numeric char array represents a single byte value. * (3) inStream may also contain newline separators. * Post: return -1 => inStream is empty, content of myByte is not valid. * return 0 => myByte contains a valid byte converted from inStream. */ public static int ReadByteFromCsvStream( Stream inStream, ref byte myByte ) { Object obj = (Object) myByte; int result = ReadValueFromCsvStream( inStream, ref obj ); myByte = (byte) obj; return result; } // method ReadByteFromCsvStream /* * Pre: (1) inStream: Each comma-separated group of bytes represents a numeric char array. * (2) Each numeric char array represents a single float value. * (3) inStream may also contain newline separators. * Post: return -1 => inStream is empty, content of myFloat is not valid. * return 0 => myFloat contains a valid float converted from inStream. */ public static int ReadFloatFromCsvStream( Stream inStream, ref float myFloat ) { Object obj = (Object) myFloat; int result = ReadValueFromCsvStream( inStream, ref obj ); myFloat = (float) obj; return result; } // method ReadFloatFromCsvStream /* * Pre: (1) inStream: Each comma-separated group of bytes represents a numeric char array. * (2) Each numeric char array represents a single object value. * (3) inStream may also contain newline separators. * Post: return -1 => inStream is empty, content of obj is not valid. * return 0 => obj wraps a valid numeric value converted from inStream. */ private static int ReadValueFromCsvStream( Stream inStream, ref Object obj ) { List scratch = new List(); bool eolFlg = false; int next = 1; while ( next == 1 ) { next = inStream.ReadByte(); if ( next == -1 ) break; char c = Convert.ToChar( next ); if ( c == '\r' ) { eolFlg = true; next = 1; continue; } if ( c == ',' || c == '\n' ) { Type t = obj.GetType(); if ( 0 < scratch.Count ) { byte[] bar = scratch.ToArray(); byte[] buf = new byte[2 * bar.Length]; for ( int i = 0; i < bar.Length; ++i ) { buf[2 * i] = bar[i]; buf[2*i+1] = 0; } int ndx = 0; string str = Utility.ByteArrayConverter.GetString( buf, ref ndx, bar.Length ); if ( t.Name == "Single" ) { float myFloat = Convert.ToSingle( str ); obj = (Object) myFloat; double myDubb = Convert.ToDouble( str ); // The float will be as close as it can, to what you're telling it to be. //byte[] test = new byte[4]; //int zro = 0; //SetFloat( test, ref zro, myFloat ); //zro = 0; } else { if ( t.Name == "Byte" ) { byte myByte = Convert.ToByte( str ); obj = (Object) myByte; } else { next = -1; break; } } scratch.Clear(); } else { if ( eolFlg == false ) { if ( t.Name == "Single" ) { float myFloat = 0; obj = (Object) myFloat; } else { if ( t.Name == "Byte" ) { byte myByte = 0; obj = (Object) myByte; } else { next = -1; break; } } } else { // End of line eolFlg = false; continue; } } // if 0 next = 0; } else { scratch.Add( Convert.ToByte(next) ); next = 1; } // if c } // while return next; } // method ReadValueFromCsvStream // Post: ndx has been incremented by the size of one short. public static void SetShort( byte[] buf, ref int ndx, short toSet ) { byte[] bytes = BitConverter.GetBytes( toSet ); Array.Copy( bytes, 0, buf, ndx, bytes.Length ); ndx += bytes.Length; } // Post: ndx has been incremented by the size of one integer. public static void SetInt( byte[] buf, ref int ndx, int toSet ) { byte[] bytes = BitConverter.GetBytes( toSet ); Array.Copy( bytes, 0, buf, ndx, bytes.Length ); ndx += bytes.Length; } // Post: ndx has been incremented by the size of one long. public static void SetLong( byte[] buf, ref int ndx, long toSet ) { byte[] bytes = BitConverter.GetBytes( toSet ); Array.Copy( bytes, 0, buf, ndx, bytes.Length ); ndx += bytes.Length; } // Post: ndx has been incremented by the size of one float. public static void SetFloat( byte[] buf, ref int ndx, float toSet ) { byte[] bytes = BitConverter.GetBytes( toSet ); Array.Copy( bytes, 0, buf, ndx, bytes.Length ); ndx += bytes.Length; } // Post: ndx has been incremented by the size of one Pair. public static void SetPairFloat( byte[] buf, ref int ndx, Pair toSet ) { SetFloat( buf, ref ndx, toSet.First ); SetFloat( buf, ref ndx, toSet.Second ); } // Post: ndx has been incremented by the size of one Pair. public static void SetPairInt( byte[] buf, ref int ndx, Pair toSet ) { SetInt( buf, ref ndx, toSet.First ); SetInt( buf, ref ndx, toSet.Second ); } // Post: ndx has been incremented by the size of one char. public static void SetChar( byte[] buf, ref int ndx, char toSet ) { byte[] bytes = BitConverter.GetBytes( toSet ); Array.Copy( bytes, 0, buf, ndx, bytes.Length ); ndx += bytes.Length; } // Post: ndx has been incremented by toSet.Length * sizeof(char) public static void SetString( byte[] buf, ref int ndx, string toSet ) { for ( int i = 0; i < toSet.Length; ++i ) { SetChar( buf, ref ndx, toSet[i] ); } } // Post: ndx has been incremented by the size of one short. public static short GetShort( byte[] buf, ref int ndx ) { byte[] bytes = new byte[ sizeof(short) ]; Array.Copy( buf, ndx, bytes, 0, bytes.Length ); ndx += ( bytes.Length ); return BitConverter.ToInt16( bytes, 0 ); } // Post: ndx has been incremented by the size of one integer. public static int GetInt( byte[] buf, ref int ndx ) { byte[] bytes = new byte[ sizeof(int) ]; Array.Copy( buf, ndx, bytes, 0, bytes.Length ); ndx += ( bytes.Length ); return BitConverter.ToInt32( bytes, 0 ); } // Post: ndx has been incremented by the size of one long. public static long GetLong( byte[] buf, ref int ndx ) { byte[] bytes = new byte[ sizeof(long) ]; Array.Copy( buf, ndx, bytes, 0, bytes.Length ); ndx += ( bytes.Length ); return BitConverter.ToInt64( bytes, 0 ); } // Post: ndx has been incremented by the size of one float. ** Why not use BitConverter.ToSingle ...** public static float GetFloat( byte[] buf, ref int ndx ) { byte[] bytes = new byte[ sizeof(float) ]; Array.Copy( buf, ndx, bytes, 0, bytes.Length ); ndx += ( bytes.Length ); return BitConverter.ToSingle( bytes, 0 ); } // Post: ndx has been incremented by the size of one Pair. public static Pair GetPairFloat( byte[] buf, ref int ndx ) { float real = GetFloat( buf, ref ndx ); float imag = GetFloat( buf, ref ndx ); Pair complex = new Pair(); complex.First = real; complex.Second = imag; return complex; } // Post: ndx has been incremented by the size of one Pair. public static Pair GetPairInt( byte[] buf, ref int ndx ) { int real = GetInt( buf, ref ndx ); int imag = GetInt( buf, ref ndx ); Pair complex = new Pair(); complex.First = real; complex.Second = imag; return complex; } // Pre: buf represents a Unicode character array (2 bytes per char). // Post: ndx has been incremented by sizeof(char). public static char GetChar( byte[] buf, ref int ndx ) { byte[] bytes = new byte[ sizeof(char) ]; Array.Copy( buf, ndx, bytes, 0, bytes.Length ); ndx += ( bytes.Length ); return BitConverter.ToChar( bytes, 0 ); } // Pre: buf represents a Unicode character array (2 bytes per char). // Post: ndx has been incremented by strLen * sizeof(char) public static string GetString( byte[] buf, ref int ndx, int strLen ) { string s = ""; for ( int i = 0; i < strLen; ++i ) { s += GetChar( buf, ref ndx ); } return s; } } // class ByteArrayConverter } // namespace Utility