Laden...

Random Stream

Erstellt von AyrA vor 11 Jahren Letzter Beitrag vor 11 Jahren 4.798 Views
AyrA Themenstarter:in
60 Beiträge seit 2010
vor 11 Jahren
Random Stream

Beschreibung:

Diese Klasse ist ein Wrapper für die System.Random Klasse und macht diese als Stream verfügbar. Ideal um Dateien zu überschreiben, Dateien zu erstellen oder wenn beim Umleiten und Verketten von Streams auch Zufallsdaten benötigt werden. Dadurch wird das aus Linux bekannte /dev/random emuliert, wobei der Stream in dieser C# Klasse jedoch readonly ist.

**Benchmark unter Windows 7 mit Intel Core 2 Duo E8500 **

Datenrate beim Lesen von einem ganzen Buffer (1024*1024 Bytes):
Dies benötigt kein Array.Copy; Angabe in MB/s

115 MB written
115 MB written
117 MB written
117 MB written
118 MB written
116 MB written
117 MB written
117 MB written
117 MB written
118 MB written

Datenrate beim lesen eines Teilbuffers (1024*1024 Bytes -1):
Benötigt Array.Copy; Angabe in MB/s

102 MB written
107 MB written
109 MB written
108 MB written
109 MB written
108 MB written
107 MB written
109 MB written
106 MB written
109 MB written
110 MB written

Code:
Achtung viele der Methoden und Attribute sind nur da weil dies durch die Vererbung erzwungen wird, z.B. hat der Stream immer eine Länge von 9223372036854775807. (long.MaxValue)


using System;
using System.IO;

namespace AyrA.Random
{
    public class PseudoRandStream : Stream
    {
        private long currPos=0;
        private System.Random r;

        /// <summary>
        /// Returns always true
        /// </summary>
        public override bool CanRead
        {
            get { return true; }
        }

        /// <summary>
        /// eturns always false
        /// </summary>
        public override bool CanSeek
        {
            get { return false; }
        }

        /// <summary>
        /// Returns always false
        /// </summary>
        public override bool CanTimeout
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Returns always false
        /// </summary>
        public override bool CanWrite
        {
            get { return false; }
        }

        /// <summary>
        /// returns long.MaxValue
        /// </summary>
        public override long Length
        {
            get
            {
                return long.MaxValue;
            }
        }

        /// <summary>
        /// gets the number of Bytes read
        /// </summary>
        public override long Position
        {
            get
            {
                return currPos;
            }
            set
            {
                throw new Exception("This Property is readonly");
            }
        }

        /// <summary>
        /// Gets or sets the Read Timeout
        /// </summary>
        public override int ReadTimeout
        { get; set; }

        /// <summary>
        /// Gets or sets the Write Timeout
        /// </summary>
        public override int WriteTimeout
        {get;set;}

        /// <summary>
        /// Initializes a new Random Stream with given Seed
        /// </summary>
        /// <param name="init">Seed Value</param>
        public PseudoRandStream(int init)
        {
            r = new System.Random(init);
        }

        /// <summary>
        /// Generates a new Random Stream with current Time as seed.
        /// </summary>
        public PseudoRandStream()
        {
            r = new System.Random();
        }

        /// <summary>
        /// Does nothing, since this is not writeable.
        /// </summary>
        public override void Flush()
        {
            //Do nothing loop;
        }

        /// <summary>
        /// Seeks to given Position (only Forward seek possible)
        /// </summary>
        /// <param name="offset">Number of Bytes to skip</param>
        /// <param name="origin">Must be SeekOrigin.Current</param>
        /// <returns>new Position</returns>
        public override long Seek(long offset, SeekOrigin origin)
        {
            if (origin != SeekOrigin.Current)
            {
                throw new Exception("Can only seek from Current location");
            }
            else if (offset > 0)
            {
                while(offset>0)
                {
                    if (offset > int.MaxValue)
                    {
                        offset -= Read(new byte[int.MaxValue], 0, int.MaxValue);
                    }
                    else
                    {
                        offset-=Read(new byte[(int)offset], 0, (int)offset);
                    }
                }
                return currPos;
            }
            else
            {
                throw new Exception("Cannot seek backwards");
            }
        }

        /// <summary>
        /// [DO NOT USE] Sets the Length of the Stream
        /// </summary>
        /// <param name="value">New Length</param>
        public override void SetLength(long value)
        {
            throw new Exception(string.Format("This Stream is always {0} long.",long.MaxValue));
        }

        /// <summary>
        /// Reads given number of Bytes from the Stream
        /// </summary>
        /// <param name="buffer">byte Buffer</param>
        /// <param name="offset">Offset in Buffer to start writing</param>
        /// <param name="count">Number of Bytes to write</param>
        /// <returns>number of Bytes actually written to Buffer</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (offset == 0 && count == buffer.Length)
            {
                r.NextBytes(buffer);
                return count;
            }
            else
            {
                byte[] temp = new byte[Math.Abs(count - offset)];
                r.NextBytes(temp);
                Array.Copy(temp, 0, buffer, offset, count);
                return temp.Length;
            }
        }

        /// <summary>
        /// [DO NOT USE] Writes Bytes to the Random Stream
        /// </summary>
        /// <param name="buffer">buffer to read from</param>
        /// <param name="offset">Offset in Buffer to start</param>
        /// <param name="count">number of Bytes to read</param>
        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new Exception("This Stream is readonly");
        }

        /// <summary>
        /// Reads a single Byte
        /// </summary>
        /// <returns>0-255</returns>
        public override int ReadByte()
        {
            return r.Next(256);
        }
    }
}


**:::

Gute Quellen für Zufallszahlen sind zum Beispiel die Ping Werte eines Traceroute nach China oder reale Zufallsreneratoren, wie sie zum Beispiel auf random.org zum Einsatz kommen.

Schlagwörter: Random generator stream zufallsgenerator PRNG

**:::

4.938 Beiträge seit 2008
vor 11 Jahren

Hallo Ayra,

super Idee! (einen eigenen StringStream hatte ich selber auch für meinen EBNF-Parser implementiert)

Da du die Seek-Methode implementiert hast, solltest du CanSeek doch true zurückgeben lassen, oder?

Edit: eine Optimierung bei Read ist mir noch eingefallen:


if (offset == 0 && count == buffer.Length)
  r.ReadBytes(buffer);
else
{
   ...
}

So ersparst du das Umkopieren, falls der gesamte 'buffer' gefüllt werden soll.

AyrA Themenstarter:in
60 Beiträge seit 2010
vor 11 Jahren

Seek ist nur ganz begrenzt möglich. (nur vorwärts und nur relativ zur aktuellen Position), daher habe ich mich für false bei CanSeek entschieden, um Probleme zu vermeiden, falls einer fremden Methode, die ein Stream verlangt der RandomStream übergeben wird und diese auf Seek zurückgreifen will.
Bezüglich des Falls, dass der Ganze Buffer gefüllt wird schaue ich morgen auf meinem Rechner, wie die Performance mit dem zusätzlichen IF ist. Ansonsten baue ich das noch ein.

EDIT: Vorgeschlagene Änderung eingebaut, da Performance Gewinn um bis zu 20 MB/s.
Post ergänzt um Informationen zu Zufallsgeneratoren.

**:::