summaryrefslogtreecommitdiff
path: root/serial/VirtualSerial2/ringbuffer.c
diff options
context:
space:
mode:
authorDave Wilson <[email protected]>2015-04-29 09:48:49 -0700
committerDave Wilson <[email protected]>2015-04-29 10:47:29 -0700
commitd40232638faaab19de557e70a3ee938da7a35295 (patch)
tree14e61c973745949af186b7940e3a53d25322f5de /serial/VirtualSerial2/ringbuffer.c
parent02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff)
samples update for //build
Diffstat (limited to 'serial/VirtualSerial2/ringbuffer.c')
-rw-r--r--serial/VirtualSerial2/ringbuffer.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/serial/VirtualSerial2/ringbuffer.c b/serial/VirtualSerial2/ringbuffer.c
new file mode 100644
index 00000000..0efbe821
--- /dev/null
+++ b/serial/VirtualSerial2/ringbuffer.c
@@ -0,0 +1,338 @@
+/*++
+
+Copyright (c) Microsoft Corporation, All Rights Reserved
+
+Module Name:
+
+ RingBuffer.c
+
+Abstract:
+
+ This file implements the Ring Buffer
+
+Environment:
+
+--*/
+
+#include "internal.h"
+
+VOID
+RingBufferInitialize(
+ _In_ PRING_BUFFER Self,
+ _In_reads_bytes_(BufferSize)
+ BYTE* Buffer,
+ _In_ size_t BufferSize
+ )
+{
+ Self->Size = BufferSize;
+ Self->Base = Buffer;
+ Self->End = Buffer + BufferSize;
+ Self->Head = Buffer;
+ Self->Tail = Buffer;
+}
+
+
+VOID
+RingBufferGetAvailableSpace(
+ _In_ PRING_BUFFER Self,
+ _Out_ size_t *AvailableSpace
+ )
+{
+ BYTE* headSnapshot = NULL;
+ BYTE* tailSnapshot = NULL;
+ BYTE* tailPlusOne = NULL;
+
+ ASSERT(AvailableSpace);
+
+ //
+ // Take a snapshot of the head and tail pointers. We will compute the
+ // available space based on this snapshot. This is safe to do in a
+ // single-producer, single-consumer model, because -
+ // * A producer will call GetAvailableSpace() to determine whether
+ // there is enough space to write the data it is trying to write.
+ // The only other thread that could modify the amount of space
+ // available is the consumer thread, which can only increase the
+ // amount of space available. Hence it is safe for the producer
+ // to write based on this snapshot.
+ // * A consumer thread will call GetAvailableSpace() to determine
+ // whether there is enough data in the buffer for it to read.
+ // (Available data = Buffer size - Available space). The only
+ // other thread that could modify the amount of space available
+ // is the producer thread, which can only decrease the amount of
+ // space available (thereby increasing the amount of data
+ // available. Hence it is safe for the consumer to read based on
+ // this snapshot.
+ //
+ headSnapshot = Self->Head;
+ tailSnapshot = Self->Tail;
+
+ //
+ // In order to distinguish between a full buffer and an empty buffer,
+ // we always leave the last byte of the buffer unused. So, an empty
+ // buffer is denoted by -
+ // tail == head
+ // ... and a full buffer is denoted by -
+ // (tail+1) == head
+ //
+ tailPlusOne = ((tailSnapshot+1) == Self->End) ? Self->Base : (tailSnapshot+1);
+
+ if (tailPlusOne == headSnapshot)
+ {
+ //
+ // Buffer full
+ //
+ *AvailableSpace = 0;
+ }
+ else if (tailSnapshot == headSnapshot)
+ {
+ //
+ // Buffer empty
+ // The -1 in the computation below is to account for the fact that
+ // we always leave the last byte of the ring buffer unused in order
+ // to distinguish between an empty buffer and a full buffer.
+ //
+ *AvailableSpace = Self->Size - 1;
+ }
+ else
+ {
+ if (tailSnapshot > headSnapshot)
+ {
+ //
+ // Data has not wrapped around the end of the buffer
+ // The -1 in the computation below is to account for the fact
+ // that we always leave the last byte of the ring buffer unused
+ // in order to distinguish between an empty buffer and a full
+ // buffer.
+ //
+ *AvailableSpace = Self->Size - (tailSnapshot - headSnapshot) - 1;
+ }
+ else
+ {
+ //
+ // Data has wrapped around the end of the buffer
+ // The -1 in the computation below is to account for the fact
+ // that we always leave the last byte of the ring buffer unused
+ // in order to distinguish between an empty buffer and a full
+ // buffer.
+ //
+ *AvailableSpace = (headSnapshot - tailSnapshot) - 1;
+ }
+ }
+}
+
+
+VOID
+RingBufferGetAvailableData(
+ _In_ PRING_BUFFER Self,
+ _Out_ size_t *AvailableData
+ )
+{
+ size_t availableSpace;
+
+ ASSERT(AvailableData);
+
+ RingBufferGetAvailableSpace(Self, &availableSpace);
+
+ //
+ // The -1 in the arithmetic below accounts for the fact that we always
+ // keep 1 byte of the ring buffer unused in order to distinguish
+ // between a full buffer and an empty buffer.
+ //
+ *AvailableData = Self->Size - availableSpace - 1;
+}
+
+
+NTSTATUS
+RingBufferWrite(
+ _In_ PRING_BUFFER Self,
+ _In_reads_bytes_(DataSize)
+ BYTE* Data,
+ _In_ size_t DataSize
+ )
+{
+ size_t availableSpace;
+ size_t bytesToCopy;
+ size_t spaceFromCurrToEnd;
+
+ ASSERT(Data && (0 != DataSize));
+
+ if (Self->Tail >= Self->End)
+ {
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ //
+ // Get the amount of space available in the buffer
+ //
+ RingBufferGetAvailableSpace(Self, &availableSpace);
+
+ //
+ // If there is not enough space to fit in all the data passed in by the
+ // caller then copy as much as possible and throw away the rest
+ //
+ if (availableSpace < DataSize)
+ {
+ bytesToCopy = availableSpace;
+ }
+ else
+ {
+ bytesToCopy = DataSize;
+ }
+
+ if (bytesToCopy)
+ {
+ //
+ // The buffer has some space at least
+ //
+ if ((Self->Tail + bytesToCopy) > Self->End)
+ {
+ //
+ // The data being written will wrap around the end of the buffer.
+ // So the copy has to be done in two steps -
+ // * X bytes from current position to end of the buffer
+ // * the remaining (bytesToCopy - X) from the start of the buffer
+ //
+
+ //
+ // The first step of the copy ...
+ //
+ spaceFromCurrToEnd = Self->End - Self->Tail;
+
+ RtlCopyMemory(Self->Tail, Data, spaceFromCurrToEnd);
+
+ Data += spaceFromCurrToEnd;
+
+ bytesToCopy -= spaceFromCurrToEnd;
+
+ //
+ // The second step of the copy ...
+ //
+ RtlCopyMemory(Self->Base, Data, bytesToCopy);
+
+ //
+ // Advance the tail pointer
+ //
+ Self->Tail = Self->Base + bytesToCopy;
+ }
+ else
+ {
+ //
+ // Data does NOT wrap around the end of the buffer. Just copy it
+ // over in a single step
+ //
+ RtlCopyMemory(Self->Tail, Data, bytesToCopy);
+
+ //
+ // Advance the tail pointer
+ //
+ Self->Tail += bytesToCopy;
+ if (Self->Tail == Self->End)
+ {
+ //
+ // We have exactly reached the end of the buffer. The next
+ // write should wrap around and start from the beginning.
+ //
+ Self->Tail = Self->Base;
+ }
+ }
+
+ ASSERT(Self->Tail < Self->End);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RingBufferRead(
+ _In_ PRING_BUFFER Self,
+ _Out_writes_bytes_to_(DataSize, *BytesCopied)
+ BYTE* Data,
+ _In_ size_t DataSize,
+ _Out_ size_t *BytesCopied
+ )
+{
+ size_t availableData;
+ size_t dataFromCurrToEnd;
+
+ ASSERT(Data && (DataSize != 0));
+
+ if (Self->Head >= Self->End)
+ {
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ //
+ // Get the amount of data available in the buffer
+ //
+ RingBufferGetAvailableData(Self, &availableData);
+
+ if (availableData == 0)
+ {
+ *BytesCopied = 0;
+ return STATUS_SUCCESS;
+ }
+
+ if (DataSize > availableData)
+ {
+ DataSize = availableData;
+ }
+
+ *BytesCopied = DataSize;
+
+ if ((Self->Head + DataSize) > Self->End)
+ {
+ //
+ // The data requested by the caller is wrapped around the end of the
+ // buffer. So we'll do the copy in two steps -
+ // * Copy X bytes from the current position to the end buffer into
+ // the caller's buffer
+ // * Copy (DataSize - X) bytes from the beginning to the buffer into
+ // the caller's buffer
+ //
+
+ //
+ // The first step of the copy ...
+ //
+ dataFromCurrToEnd = Self->End - Self->Head;
+ RtlCopyMemory(Data, Self->Head, dataFromCurrToEnd);
+ Data += dataFromCurrToEnd;
+ DataSize -= dataFromCurrToEnd;
+
+ //
+ // The second step of the copy ...
+ //
+ RtlCopyMemory(Data, Self->Base, DataSize);
+
+ //
+ // Advance the head pointer
+ //
+ Self->Head = Self->Base + DataSize;
+ }
+ else
+ {
+ //
+ // The data in the buffer is NOT wrapped around the end of the buffer.
+ // Simply copy the data over to the caller's buffer in a single step.
+ //
+ RtlCopyMemory(Data, Self->Head, DataSize);
+
+ //
+ // Advance the head pointer
+ //
+ Self->Head += DataSize;
+ if (Self->Head == Self->End)
+ {
+ //
+ // We have exactly reached the end of the buffer. The next
+ // read should wrap around and start from the beginning.
+ //
+ Self->Head = Self->Base;
+ }
+ }
+
+ ASSERT(Self->Head < Self->End);
+
+ return STATUS_SUCCESS;
+}
+