2011-11-22 16 views
13

Esiste una qualsiasi struttura dati/combinazione di classi .NET che consenta l'aggiunta di dati di byte alla fine di un buffer, ma tutti i peek e le letture sono dall'inizio, accorciando il buffer quando Leggo?Buffer FIFO/coda specializzato in flussi di byte

La classe MemoryStream sembra fare parte di questo, ma ho bisogno di mantenere posizioni separate per la lettura e la scrittura, e non scartare automaticamente i dati all'inizio dopo la lettura.

Una risposta è stata inviata in risposta a this question che è fondamentalmente quello che sto cercando di fare, ma preferirei qualcosa che possa fare l'I/O asincrono in diversi componenti dello stesso processo, proprio come una normale pipa o anche un flusso di rete (ho bisogno di filtrare/elaborare i dati prima).

+1

C'è qualcosa di sbagliato nel saltare avanti e indietro all'interno di un buffer di lettura? – Ryan

+0

Solo ciò che ho detto e che devo tenere traccia di ciò rispetto allo stile NetworkStream di lettura, lettura, lettura, ecc. – Deanna

+0

È necessario leggere e scrivere array di dimensioni diverse? Una coda di 'byte []' non sarebbe abbastanza buona per te? – svick

risposta

10

Pubblicherò una copia spogliata di una logica che ho scritto per un progetto al lavoro una volta. Il vantaggio di questa versione è che funziona con un elenco collegato di dati memorizzati nel buffer e quindi non è necessario memorizzare grandi quantità di memoria e/o copiare memoria durante la lettura. inoltre, il suo thread è sicuro e si comporta come un flusso di rete, ovvero: Quando si legge quando non ci sono dati disponibili: Attendi fino a quando non ci sono dati disponibili o timeout. Inoltre, quando si leggono x quantità di byte e ci sono solo y quantità di byte, si ritorna dopo aver letto tutti i byte. Spero che aiuti!

public class SlidingStream : Stream 
{ 
    #region Other stream member implementations 

    ... 

    #endregion Other stream member implementations 

    public SlidingStream() 
    { 
     ReadTimeout = -1; 
    } 

    private readonly object _writeSyncRoot = new object(); 
    private readonly object _readSyncRoot = new object(); 
    private readonly LinkedList<ArraySegment<byte>> _pendingSegments = new LinkedList<ArraySegment<byte>>(); 
    private readonly ManualResetEventSlim _dataAvailableResetEvent = new ManualResetEventSlim(); 

    public int ReadTimeout { get; set; } 

    public override int Read(byte[] buffer, int offset, int count) 
    { 
     if (_dataAvailableResetEvent.Wait(ReadTimeout)) 
      throw new TimeoutException("No data available"); 

     lock (_readSyncRoot) 
     { 
      int currentCount = 0; 
      int currentOffset = 0; 

      while (currentCount != count) 
      { 
       ArraySegment<byte> segment = _pendingSegments.First.Value; 
       _pendingSegments.RemoveFirst(); 

       int index = segment.Offset; 
       for (; index < segment.Count; index++) 
       { 
        if (currentOffset < offset) 
        { 
         currentOffset++; 
        } 
        else 
        { 
         buffer[currentCount] = segment.Array[index]; 
         currentCount++; 
        } 
       } 

       if (currentCount == count) 
       { 
        if (index < segment.Offset + segment.Count) 
        { 
         _pendingSegments.AddFirst(new ArraySegment<byte>(segment.Array, index, segment.Offset + segment.Count - index)); 
        } 
       } 

       if (_pendingSegments.Count == 0) 
       { 
        _dataAvailableResetEvent.Reset(); 

        return currentCount; 
       } 
      } 

      return currentCount; 
     } 
    } 

    public override void Write(byte[] buffer, int offset, int count) 
    { 
     lock (_writeSyncRoot) 
     { 
      byte[] copy = new byte[count]; 
      Array.Copy(buffer, offset, copy, 0, count); 

      _pendingSegments.AddLast(new ArraySegment<byte>(copy)); 

      _dataAvailableResetEvent.Set(); 
     } 
    } 
} 
+1

Sembra buono, ed era il modo in cui stavo andando. Ci proverò stasera. – Deanna

+0

Mi sembra che questo si bloccherà se si tenta di leggere i dati quando nessuno è disponibile. – svick

+0

@svick - Assolutamente giusto, è solo una bozza, nessuna validazione di argomenti, ecc. Il manuale ResetEvent è lì per quell'unica ragione, ho semplicemente dimenticato di aspettarlo all'inizio del metodo di lettura. Risolto ora. Grazie per l'heads up – Polity

1

Il codice può essere più semplice rispetto alla risposta accettata. Non è necessario utilizzare un ciclo for .:

/// <summary> 
/// This class is a very fast and threadsafe FIFO buffer 
/// </summary> 
public class FastFifo 
{ 
    private List<Byte> mi_FifoData = new List<Byte>(); 

    /// <summary> 
    /// Get the count of bytes in the Fifo buffer 
    /// </summary> 
    public int Count 
    { 
     get 
     { 
      lock (mi_FifoData) 
      { 
       return mi_FifoData.Count; 
      } 
     } 
    } 

    /// <summary> 
    /// Clears the Fifo buffer 
    /// </summary> 
    public void Clear() 
    { 
     lock (mi_FifoData) 
     { 
      mi_FifoData.Clear(); 
     } 
    } 

    /// <summary> 
    /// Append data to the end of the fifo 
    /// </summary> 
    public void Push(Byte[] u8_Data) 
    { 
     lock (mi_FifoData) 
     { 
      // Internally the .NET framework uses Array.Copy() which is extremely fast 
      mi_FifoData.AddRange(u8_Data); 
     } 
    } 

    /// <summary> 
    /// Get data from the beginning of the fifo. 
    /// returns null if s32_Count bytes are not yet available. 
    /// </summary> 
    public Byte[] Pop(int s32_Count) 
    { 
     lock (mi_FifoData) 
     { 
      if (mi_FifoData.Count < s32_Count) 
       return null; 

      // Internally the .NET framework uses Array.Copy() which is extremely fast 
      Byte[] u8_PopData = new Byte[s32_Count]; 
      mi_FifoData.CopyTo(0, u8_PopData, 0, s32_Count); 
      mi_FifoData.RemoveRange(0, s32_Count); 
      return u8_PopData; 
     } 
    } 

    /// <summary> 
    /// Gets a byte without removing it from the Fifo buffer 
    /// returns -1 if the index is invalid 
    /// </summary> 
    public int PeekAt(int s32_Index) 
    { 
     lock (mi_FifoData) 
     { 
      if (s32_Index < 0 || s32_Index >= mi_FifoData.Count) 
       return -1; 

      return mi_FifoData[s32_Index]; 
     } 
    } 
} 
+0

Questo è essenzialmente lo stesso della domanda collegata, che non soddisfa i desideri asincroni o bloccanti. Grazie comunque. – Deanna

+0

OK, ma quel codice non è così elegante e non è thread-safe. Puoi farlo con 6 linee invece di 16 linee. – Elmue

Problemi correlati