0bfa9300db
- Make original buffer use a disk backed mapping to save memory. - rearrange api function headers for search etc. - fix bugs with iterators. - support uint64_t for all byte and line offsets to allow for much larger files to be opened. - and more minor bug fixes.
214 lines
5.4 KiB
C++
214 lines
5.4 KiB
C++
#include "vase/iterators/chunk.h"
|
|
|
|
ChunkIterator::ChunkIterator(Shard *r, Direction dir)
|
|
: dir(dir), root(r) {
|
|
if (!root)
|
|
return;
|
|
Shard::retain(root);
|
|
}
|
|
|
|
ChunkIterator::~ChunkIterator() {
|
|
Shard::release(root);
|
|
}
|
|
|
|
void ChunkIterator::seek_offset(uint64_t offset) {
|
|
stack.clear();
|
|
petal = nullptr;
|
|
petal_offset = 0;
|
|
global_offset = 0;
|
|
global_line = 0;
|
|
at_end = false;
|
|
if (!root)
|
|
return;
|
|
if (offset >= root->length)
|
|
offset = root->length - 1;
|
|
uint64_t target = offset;
|
|
Shard *curr = root;
|
|
while (curr) {
|
|
if (curr->kind == Shard::ShardKind::Petal) {
|
|
petal = (Petal *)curr;
|
|
petal_offset = target;
|
|
global_offset += target;
|
|
return;
|
|
} else {
|
|
auto *b = (Branch *)curr;
|
|
uint64_t left_len = b->left->length;
|
|
if (target < left_len) {
|
|
if (dir == Direction::Forward)
|
|
stack.push_back(b->right);
|
|
curr = b->left;
|
|
} else {
|
|
target -= left_len;
|
|
if (dir == Direction::Backward)
|
|
stack.push_back(b->left);
|
|
global_offset += left_len;
|
|
global_line += b->left->lines;
|
|
curr = b->right;
|
|
}
|
|
}
|
|
}
|
|
std::unreachable();
|
|
}
|
|
|
|
void ChunkIterator::seek_line(uint64_t line) {
|
|
stack.clear();
|
|
petal = nullptr;
|
|
petal_offset = 0;
|
|
global_offset = 0;
|
|
at_end = false;
|
|
bool last_line = false;
|
|
if (!root)
|
|
return;
|
|
if (dir == Direction::Backward) {
|
|
if (line > root->lines + 1)
|
|
line = root->lines + 1;
|
|
if (line == root->lines + 1)
|
|
last_line = true;
|
|
} else {
|
|
if (line > root->lines)
|
|
line = root->lines;
|
|
if (line == root->lines)
|
|
last_line = true;
|
|
}
|
|
Shard *curr = root;
|
|
while (curr) {
|
|
if (curr->kind == Shard::ShardKind::Petal) {
|
|
petal = (Petal *)curr;
|
|
if (last_line && dir == Direction::Backward) {
|
|
petal_offset = petal->length;
|
|
global_offset += petal->length;
|
|
} else {
|
|
const char *text = nullptr;
|
|
uint64_t remaining = 0;
|
|
uint64_t offset = 0;
|
|
while (line) {
|
|
if (!text) {
|
|
uint64_t got = 0;
|
|
text = petal->source->read(petal->pos + offset, &got);
|
|
remaining = std::min(got, petal->length - offset);
|
|
}
|
|
const char *c = (const char *)memchr(text, '\n', remaining);
|
|
if (!c) {
|
|
offset += remaining;
|
|
text = nullptr;
|
|
continue;
|
|
}
|
|
remaining -= (c - text) + 1;
|
|
offset += (c - text) + 1;
|
|
text = c + 1;
|
|
line--;
|
|
}
|
|
if (dir == Direction::Backward)
|
|
--offset;
|
|
if (last_line && dir == Direction::Forward)
|
|
at_end = true;
|
|
petal_offset = offset;
|
|
global_offset += offset;
|
|
}
|
|
return;
|
|
} else {
|
|
auto *b = (Branch *)curr;
|
|
uint64_t left_lines = b->left->lines;
|
|
if (line <= left_lines) {
|
|
if (dir == Direction::Forward)
|
|
stack.push_back(b->right);
|
|
curr = b->left;
|
|
} else {
|
|
line -= left_lines;
|
|
if (dir == Direction::Backward)
|
|
stack.push_back(b->left);
|
|
global_offset += b->left->length;
|
|
curr = b->right;
|
|
}
|
|
}
|
|
}
|
|
std::unreachable();
|
|
}
|
|
|
|
uint64_t ChunkIterator::byte_offset() {
|
|
return global_offset;
|
|
}
|
|
|
|
uint64_t ChunkIterator::line_offset() {
|
|
return global_line;
|
|
}
|
|
|
|
bool ChunkIterator::next(const char **data, uint64_t *out_len) {
|
|
if (dir == Direction::Forward) {
|
|
while (true) {
|
|
if (petal) {
|
|
if (petal_offset == petal->length) {
|
|
if (at_end) {
|
|
*data = nullptr;
|
|
*out_len = 0;
|
|
at_end = false;
|
|
return true;
|
|
}
|
|
petal = nullptr;
|
|
continue;
|
|
}
|
|
uint64_t remaining = petal->length - petal_offset;
|
|
uint64_t got = 0;
|
|
const char *chunk = petal->source->read(petal->pos + petal_offset, &got);
|
|
if (got == 0) {
|
|
petal = nullptr;
|
|
petal_offset = 0;
|
|
return false;
|
|
}
|
|
uint64_t take = std::min(got, remaining);
|
|
*data = chunk;
|
|
*out_len = take;
|
|
petal_offset += take;
|
|
global_offset += take;
|
|
return true;
|
|
}
|
|
if (stack.empty())
|
|
return false;
|
|
auto s = stack.back();
|
|
stack.pop_back();
|
|
while (s->kind == Shard::ShardKind::Branch) {
|
|
Branch *b = (Branch *)s;
|
|
stack.push_back(b->right);
|
|
s = b->left;
|
|
}
|
|
petal = (Petal *)s;
|
|
petal_offset = 0;
|
|
}
|
|
} else {
|
|
while (true) {
|
|
if (petal) {
|
|
if (petal_offset == 0) {
|
|
petal = nullptr;
|
|
continue;
|
|
}
|
|
uint64_t got = 0;
|
|
*data = petal->source->read(petal->pos, &got);
|
|
if (petal_offset > got) {
|
|
uint64_t got2 = 0;
|
|
*data = petal->source->read(petal->pos + got, &got2);
|
|
*out_len = std::min(got2, petal_offset - got);
|
|
petal_offset = got;
|
|
global_offset -= *out_len;
|
|
return true;
|
|
} else {
|
|
*out_len = std::min(got, petal_offset);
|
|
petal_offset = 0;
|
|
global_offset -= *out_len;
|
|
return true;
|
|
}
|
|
}
|
|
if (stack.empty())
|
|
return false;
|
|
auto s = stack.back();
|
|
stack.pop_back();
|
|
while (s->kind == Shard::ShardKind::Branch) {
|
|
Branch *b = (Branch *)s;
|
|
stack.push_back(b->left);
|
|
s = b->right;
|
|
}
|
|
petal = (Petal *)s;
|
|
petal_offset = petal->length;
|
|
}
|
|
}
|
|
}
|