summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/utils/CharReader.cpp93
-rw-r--r--src/core/utils/CharReader.hpp37
-rw-r--r--test/core/utils/CharReaderTest.cpp86
3 files changed, 197 insertions, 19 deletions
diff --git a/src/core/utils/CharReader.cpp b/src/core/utils/CharReader.cpp
index 33eab45..bf25a01 100644
--- a/src/core/utils/CharReader.cpp
+++ b/src/core/utils/CharReader.cpp
@@ -16,8 +16,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <iostream>
-
#include <algorithm>
#include <limits>
@@ -204,16 +202,101 @@ void Buffer::deleteCursor(Buffer::CursorId cursor)
size_t Buffer::offset(Buffer::CursorId cursor) const
{
- const Cursor &c = cursors[cursor];
- size_t offs = startOffset + c.bucketOffs;
+ const Cursor &cur = cursors[cursor];
+ size_t offs = startOffset + cur.bucketOffs;
BucketList::const_iterator it = startBucket;
- while (it != c.bucket) {
+ while (it != cur.bucket) {
offs += it->size();
advance(it);
}
return offs;
}
+size_t Buffer::moveForward(CursorId cursor, size_t relativeOffs)
+{
+ size_t offs = relativeOffs;
+ Cursor &cur = cursors[cursor];
+ while (offs > 0) {
+ // Fetch the current bucket of the cursor
+ Bucket &bucket = *(cur.bucket);
+
+ // If there is enough space in the bucket, simply increment the bucket
+ // offset by the given relative offset
+ const size_t space = bucket.size() - cur.bucketOffs;
+ if (space >= offs) {
+ cur.bucketOffs += offs;
+ break;
+ } else {
+ // Go to the end of the current bucket otherwise
+ offs -= space;
+ cur.bucketOffs = bucket.size();
+
+ // Go to the next bucket
+ if (cur.bucket != endBucket) {
+ // Go to the next bucket
+ advance(cur.bucket);
+ cur.bucketIdx++;
+ cur.bucketOffs = 0;
+ } else {
+ // Abort, if there is no more data to stream, otherwise just
+ // load new data
+ if (reachedEnd) {
+ return relativeOffs - offs;
+ }
+ stream();
+ }
+ }
+ }
+ return relativeOffs;
+}
+
+size_t Buffer::moveBackward(CursorId cursor, size_t relativeOffs)
+{
+ size_t offs = relativeOffs;
+ Cursor &cur = cursors[cursor];
+ while (offs > 0) {
+ // If there is enough space in the bucket, simply decrement the bucket
+ // offset by the given relative offset
+ if (cur.bucketOffs >= offs) {
+ cur.bucketOffs -= offs;
+ break;
+ } else {
+ // Go to the beginning of the current bucket otherwise
+ offs -= cur.bucketOffs;
+ cur.bucketOffs = 0;
+
+ // Abort if there is no more bucket to got back to
+ if (cur.bucketIdx == 0) {
+ return relativeOffs - offs;
+ }
+
+ // Go to the previous bucket (wrap around at the beginning of the
+ // list)
+ if (cur.bucket == buckets.begin()) {
+ cur.bucket = buckets.end();
+ }
+ cur.bucket--;
+
+ // Decrement the bucket index, and set the current offset to the
+ // end of the new bucket
+ cur.bucketIdx--;
+ cur.bucketOffs = cur.bucket->size();
+ }
+ }
+ return relativeOffs;
+}
+
+ssize_t Buffer::moveCursor(CursorId cursor, ssize_t relativeOffs)
+{
+ if (relativeOffs > 0) {
+ return moveForward(cursor, relativeOffs);
+ } else if (relativeOffs < 0) {
+ return -moveBackward(cursor, -relativeOffs);
+ } else {
+ return 0;
+ }
+}
+
bool Buffer::atEnd(Buffer::CursorId cursor) const
{
const Cursor &c = cursors[cursor];
diff --git a/src/core/utils/CharReader.hpp b/src/core/utils/CharReader.hpp
index 23e88b7..8d97d39 100644
--- a/src/core/utils/CharReader.hpp
+++ b/src/core/utils/CharReader.hpp
@@ -195,6 +195,16 @@ private:
*/
void stream();
+ /**
+ * Moves the given cursor forward.
+ */
+ size_t moveForward(CursorId cursor, size_t relativeOffs);
+
+ /**
+ * Moves the given cursor backward.
+ */
+ size_t moveBackward(CursorId cursor, size_t relativeOffs);
+
public:
/**
* Intializes the Buffer with a reference to a ReadCallback that is used
@@ -255,6 +265,19 @@ public:
void deleteCursor(CursorId cursor);
/**
+ * Moves a cursor by offs bytes. Note that moving backwards is theoretically
+ * limited by the LOOKBACK_SIZE of the Buffer, practically it will most likely
+ * be limited by the REQUEST_SIZE, so you can got at most 64 KiB backwards.
+ *
+ * @param cursor is the cursor that should be moved.
+ * @param relativeOffs is a positive or negative integer number specifying
+ * the number of bytes the cursor should be moved forward (positive numbers)
+ * or backwards (negative numbers).
+ * @return the actual number of bytes the cursor was moved.
+ */
+ ssize_t moveCursor(CursorId cursor, ssize_t relativeOffs);
+
+ /**
* Returns the current byte offset of the given cursor relative to the
* beginning of the stream.
*
@@ -286,20 +309,6 @@ public:
* been reached.
*/
bool read(CursorId cursor, char &c);
-
-// /**
-// * Reads string from the ring buffer from the given cursor.
-// *
-// * @param cursor specifies the cursor from which the data should be read.
-// * The cursor will be advanced by the specified number of bytes (or to the
-// * end of the stream).
-// * @param res is the vector into which the data should be read. Any already
-// * present data will be overridden.
-// * @param len is number of bytes that should be read from the buffer.
-// * @return true if len bytes were read, false if less the len bytes have
-// * been read because the end of the stream has been reached.
-// */
-// bool read(CursorId cursor, std::vector<char> &res, size_t len);
};
}
diff --git a/test/core/utils/CharReaderTest.cpp b/test/core/utils/CharReaderTest.cpp
index 260b135..1518928 100644
--- a/test/core/utils/CharReaderTest.cpp
+++ b/test/core/utils/CharReaderTest.cpp
@@ -168,6 +168,63 @@ TEST(Buffer, copyCursors)
ASSERT_TRUE(buf.atEnd(cur3));
}
+TEST(Buffer, moveCursor)
+{
+ std::string testStr{"test1 test2 test3"};
+
+ // Create buffer with the test string
+ char c;
+ Buffer buf{testStr};
+ Buffer::CursorId cursor = buf.createCursor();
+
+ // Read the first six characters with cursor one
+ {
+ std::string res;
+ for (int i = 0; i < 6; i++) {
+ if (buf.read(cursor, c)) {
+ res.append(&c, 1);
+ }
+ }
+ ASSERT_EQ("test1 ", res);
+ }
+
+ // Move six bytes backward
+ ASSERT_EQ(-6, buf.moveCursor(cursor, -6));
+ {
+ std::string res;
+ for (int i = 0; i < 6; i++) {
+ if (buf.read(cursor, c)) {
+ res.append(&c, 1);
+ }
+ }
+ ASSERT_EQ("test1 ", res);
+ }
+
+ // Move more than six bytes backward
+ ASSERT_EQ(-6, buf.moveCursor(cursor, -1000));
+ {
+ std::string res;
+ for (int i = 0; i < 6; i++) {
+ if (buf.read(cursor, c)) {
+ res.append(&c, 1);
+ }
+ }
+ ASSERT_EQ("test1 ", res);
+ }
+
+ // Move six bytes forward
+ ASSERT_EQ(6, buf.moveCursor(cursor, 6));
+ {
+ std::string res;
+ for (int i = 0; i < 6; i++) {
+ if (buf.read(cursor, c)) {
+ res.append(&c, 1);
+ }
+ }
+ ASSERT_EQ("test3", res);
+ }
+}
+
// Generates some pseudo-random data
// (inspired by "Numerical Recipes, Third Edition", Chapter 7.17)
static std::vector<char> generateData(size_t len)
@@ -294,6 +351,15 @@ TEST(Buffer, streamTwoCursorsInterleaved)
res2.push_back(c);
}
}
+
+ // Move cur2 120 bytes backward and read the content again
+ res2.resize(res2.size() - 120);
+ ASSERT_EQ(-120, buf.moveCursor(cur2, -120));
+ for (int i = 0; i < 120; i++) {
+ if (buf.read(cur2, c)) {
+ res2.push_back(c);
+ }
+ }
}
ASSERT_EQ(DATA_LENGTH, buf.offset(cur1));
@@ -304,6 +370,26 @@ TEST(Buffer, streamTwoCursorsInterleaved)
ASSERT_EQ(DATA, res2);
}
+TEST(Buffer, streamMoveForward)
+{
+ VectorReadState state(DATA);
+
+ std::vector<char> partialData;
+ partialData.resize(100);
+ std::copy(DATA.end() - partialData.size(), DATA.end(), partialData.begin());
+
+ Buffer buf{readFromVector, &state};
+ Buffer::CursorId cursor = buf.createCursor();
+ ASSERT_EQ(DATA_LENGTH - 100, buf.moveCursor(cursor, DATA_LENGTH - 100));
+
+ char c;
+ std::vector<char> res;
+ while (buf.read(cursor, c)) {
+ res.push_back(c);
+ }
+ ASSERT_EQ(partialData, res);
+}
+
}
}