-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Wraps any reader in a generic way.
- Loading branch information
Showing
5 changed files
with
259 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// | ||
// Copyright 2023 Lexi Mayfield | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
#pragma once | ||
|
||
#include "./core.hpp" | ||
|
||
namespace LexIO | ||
{ | ||
|
||
template <typename T> | ||
class GenericBufReader | ||
{ | ||
T m_wrapped; | ||
uint8_t *m_buffer = nullptr; | ||
size_t m_allocSize = 0; | ||
size_t m_size = 0; | ||
|
||
public: | ||
GenericBufReader(T &&wrapped) : m_wrapped(wrapped) {} | ||
|
||
size_t LexRead(uint8_t *outDest, const size_t count) | ||
{ | ||
BufferView data = LexFillBuffer(count); | ||
std::copy(data.first, data.first + data.second, outDest); | ||
LexConsumeBuffer(data.second); | ||
return data.second; | ||
} | ||
|
||
BufferView LexFillBuffer(const size_t count) | ||
{ | ||
if (count <= m_size) | ||
{ | ||
// We already have enough data buffered. | ||
return BufferView{m_buffer, m_size}; | ||
} | ||
|
||
if (count > m_allocSize) | ||
{ | ||
// Reallocate our buffer with any existing data. | ||
uint8_t *buffer = ::new uint8_t[count]; | ||
std::copy(&m_buffer[0], &m_buffer[m_size], buffer); | ||
::delete[] m_buffer; | ||
m_buffer = buffer; | ||
m_allocSize = count; | ||
} | ||
|
||
// Read into the buffer. | ||
const size_t wanted = count - m_size; | ||
const size_t actual = LexIO::Read<T>(&m_buffer[m_size], wanted, m_wrapped); | ||
m_size += actual; | ||
return BufferView{m_buffer, m_size}; | ||
} | ||
|
||
void LexConsumeBuffer(const size_t count) | ||
{ | ||
if (count > m_size) | ||
{ | ||
throw new std::runtime_error("can't consume more bytes than buffer size"); | ||
} | ||
std::copy(&m_buffer[count], &m_buffer[m_size], &m_buffer[0]); | ||
m_size -= count; | ||
} | ||
}; | ||
|
||
} // namespace LexIO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
// | ||
// Copyright 2023 Lexi Mayfield | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
#include "./test.h" | ||
#include "catch2/catch_all.hpp" | ||
|
||
//****************************************************************************** | ||
|
||
template <typename T, std::size_t N> | ||
constexpr std::size_t CountOf(T const (&)[N]) noexcept | ||
{ | ||
return N; | ||
} | ||
|
||
constexpr const uint8_t TEXT_BUFFER[] = "The quick brown fox\njumps over the lazy dog.\n"; | ||
constexpr const size_t TEXT_BUFFER_SIZE = CountOf(TEXT_BUFFER) - 1; | ||
|
||
static LexIO::VectorStream GetStream() | ||
{ | ||
LexIO::VectorStream rvo; | ||
rvo.LexWrite(&TEXT_BUFFER[0], TEXT_BUFFER_SIZE); | ||
rvo.LexSeek(LexIO::SeekPos(0, LexIO::seek::start)); | ||
return rvo; | ||
} | ||
|
||
TEST_CASE("FillBuffer, single call") | ||
{ | ||
auto stream = GetStream(); | ||
auto bufReader = LexIO::GenericBufReader(std::move(stream)); | ||
|
||
auto test = LexIO::FillBuffer(bufReader, 8); | ||
REQUIRE(test.first[0] == 'T'); | ||
REQUIRE(test.first[7] == 'c'); | ||
REQUIRE(test.second == 8); | ||
} | ||
|
||
TEST_CASE("FillBuffer, multiple calls") | ||
{ | ||
auto stream = GetStream(); | ||
auto bufReader = LexIO::GenericBufReader(std::move(stream)); | ||
|
||
// Buffer initial four bytes. | ||
auto test = LexIO::FillBuffer(bufReader, 4); | ||
REQUIRE(test.first[0] == 'T'); | ||
REQUIRE(test.first[3] == ' '); | ||
REQUIRE(test.second == 4); | ||
|
||
// Buffer less than what we had before, should read nothing. | ||
test = LexIO::FillBuffer(bufReader, 2); | ||
REQUIRE(test.first[0] == 'T'); | ||
REQUIRE(test.first[3] == ' '); | ||
REQUIRE(test.second == 4); | ||
|
||
// Buffer more than what we had before. | ||
test = LexIO::FillBuffer(bufReader, 8); | ||
REQUIRE(test.first[0] == 'T'); | ||
REQUIRE(test.first[3] == ' '); | ||
REQUIRE(test.first[4] == 'q'); | ||
REQUIRE(test.first[7] == 'c'); | ||
REQUIRE(test.second == 8); | ||
} | ||
|
||
TEST_CASE("FillBuffer, EOF") | ||
{ | ||
auto stream = GetStream(); | ||
auto bufReader = LexIO::GenericBufReader(std::move(stream)); | ||
|
||
// Buffer everything. | ||
auto test = LexIO::FillBuffer(bufReader, 64); | ||
REQUIRE(test.first[0] == 'T'); | ||
REQUIRE(test.first[TEXT_BUFFER_SIZE - 1] == '\n'); | ||
REQUIRE(test.second == TEXT_BUFFER_SIZE); | ||
|
||
// Buffer more than everything. | ||
test = LexIO::FillBuffer(bufReader, 96); | ||
REQUIRE(test.first[0] == 'T'); | ||
REQUIRE(test.first[TEXT_BUFFER_SIZE - 1] == '\n'); | ||
REQUIRE(test.second == TEXT_BUFFER_SIZE); | ||
} | ||
|
||
TEST_CASE("FillBuffer, EOF with initial buffer") | ||
{ | ||
auto stream = GetStream(); | ||
auto bufReader = LexIO::GenericBufReader(std::move(stream)); | ||
|
||
auto test = LexIO::FillBuffer(bufReader, 4); | ||
REQUIRE(test.first[0] == 'T'); | ||
REQUIRE(test.first[3] == ' '); | ||
REQUIRE(test.second == 4); | ||
|
||
// Buffer everything. | ||
test = LexIO::FillBuffer(bufReader, 64); | ||
REQUIRE(test.first[0] == 'T'); | ||
REQUIRE(test.first[3] == ' '); | ||
REQUIRE(test.first[4] == 'q'); | ||
REQUIRE(test.first[TEXT_BUFFER_SIZE - 1] == '\n'); | ||
REQUIRE(test.second == TEXT_BUFFER_SIZE); | ||
} | ||
|
||
TEST_CASE("FillBuffer, zero sized read") | ||
{ | ||
auto stream = GetStream(); | ||
auto bufReader = LexIO::GenericBufReader(std::move(stream)); | ||
|
||
auto test = LexIO::FillBuffer(bufReader, 0); | ||
REQUIRE(test.second == 0); | ||
} | ||
|
||
TEST_CASE("ConsumeBuffer, single call") | ||
{ | ||
auto stream = GetStream(); | ||
auto bufReader = LexIO::GenericBufReader(std::move(stream)); | ||
|
||
// Fill, then consume whole buffer. | ||
LexIO::FillBuffer(bufReader, 8); | ||
REQUIRE_NOTHROW(LexIO::ConsumeBuffer(bufReader, 8)); | ||
|
||
auto test = LexIO::GetBuffer(bufReader); | ||
REQUIRE(test.second == 0); | ||
|
||
// Subsequent read should pick up where we left off. | ||
test = LexIO::FillBuffer(bufReader, 8); | ||
REQUIRE(test.first[0] == 'k'); | ||
REQUIRE(test.first[7] == ' '); | ||
REQUIRE(test.second == 8); | ||
} | ||
|
||
TEST_CASE("ConsumeBuffer, multiple calls") | ||
{ | ||
auto stream = GetStream(); | ||
auto bufReader = LexIO::GenericBufReader(std::move(stream)); | ||
|
||
// Consume half the buffer. | ||
LexIO::FillBuffer(bufReader, 8); | ||
REQUIRE_NOTHROW(LexIO::ConsumeBuffer(bufReader, 4)); | ||
auto test = LexIO::GetBuffer(bufReader); | ||
REQUIRE(test.first[0] == 'q'); | ||
REQUIRE(test.first[3] == 'c'); | ||
REQUIRE(test.second == 4); | ||
|
||
// Consume the other half of the buffer. | ||
REQUIRE_NOTHROW(LexIO::ConsumeBuffer(bufReader, 4)); | ||
test = LexIO::GetBuffer(bufReader); | ||
REQUIRE(test.second == 0); | ||
} | ||
|
||
TEST_CASE("ConsumeBuffer, EOF") | ||
{ | ||
auto stream = GetStream(); | ||
auto bufReader = LexIO::GenericBufReader(std::move(stream)); | ||
|
||
// Fill to EOF, consume part of it. | ||
auto test = LexIO::FillBuffer(bufReader, 64); | ||
REQUIRE_NOTHROW(LexIO::ConsumeBuffer(bufReader, 4)); | ||
test = LexIO::GetBuffer(bufReader); | ||
REQUIRE(test.first[0] == 'q'); | ||
REQUIRE(test.first[3] == 'c'); | ||
REQUIRE(test.second == TEXT_BUFFER_SIZE - 4); | ||
|
||
// Consume the rest of it. | ||
REQUIRE_NOTHROW(LexIO::ConsumeBuffer(bufReader, TEXT_BUFFER_SIZE - 4)); | ||
test = LexIO::GetBuffer(bufReader); | ||
REQUIRE(test.second == 0); | ||
} |