diff options
| author | Dave Wilson <[email protected]> | 2015-04-29 09:48:49 -0700 |
|---|---|---|
| committer | Dave Wilson <[email protected]> | 2015-04-29 10:47:29 -0700 |
| commit | d40232638faaab19de557e70a3ee938da7a35295 (patch) | |
| tree | 14e61c973745949af186b7940e3a53d25322f5de /serial/VirtualSerial2/ringbuffer.c | |
| parent | 02e5b090a6131a7cc1b90f4a9373117c9e3c7088 (diff) | |
samples update for //build
Diffstat (limited to 'serial/VirtualSerial2/ringbuffer.c')
| -rw-r--r-- | serial/VirtualSerial2/ringbuffer.c | 338 |
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; +} + |
