#include <iostream>
#include <array>
#include <algorithm>
#include <archive.h>
#include <archive_entry.h>
#include <vector>
#include <fcntl.h>
#include <memory>
#include <deque>
#include <assert.h>
#include <cstring>
struct DecoderDataChunk {
uint8_t *data;
size_t size;
DecoderDataChunk(uint8_t *dt = nullptr, size_t sz = 0) :
data( dt ), size( sz )
{}
};
typedef std::deque<DecoderDataChunk> DataChunkList;
struct ArchiveDescriptor {
ArchiveDescriptor(DataChunkList const &_data)
: data { _data }, current { data.begin() }
{
for (auto && chunk : data) {
const_cast<size_t &>(size) += chunk.size; // считаем настоящий размер.
}
}
size_t const size = 0;
DataChunkList const &data;
loff_t offset = 0;
DataChunkList::const_iterator current;
loff_t current_chunk_offset = 0;
char buffer[4096];
};
ssize_t archiveRead(struct archive *a, void *client_data, const void **buff) {
auto *descriptor = static_cast<ArchiveDescriptor *>(client_data);
size_t buffer_offset = 0;
while (buffer_offset < sizeof(ArchiveDescriptor::buffer) && descriptor->current != descriptor->data.end()) {
// вычисляем что мы можем вычитать из текущего чанка
size_t left_in_current_chunk = descriptor->current->size - descriptor->current_chunk_offset;
size_t read_size = std::min(sizeof(ArchiveDescriptor::buffer) - buffer_offset, left_in_current_chunk);
std::memcpy(descriptor->buffer + buffer_offset, descriptor->current->data + descriptor->current_chunk_offset, read_size);
buffer_offset += read_size;
descriptor->offset += read_size;
descriptor->current_chunk_offset += read_size;
// если вычитали весь
if (descriptor->current_chunk_offset == descriptor->current->size) {
++descriptor->current; // уходим на следующий
descriptor->current_chunk_offset = 0;
}
}
if (buffer_offset) {
*buff = descriptor->buffer;
}
return buffer_offset;
}
la_int64_t archiveSeek(struct archive *, void *client_data, la_int64_t offset, int whence) noexcept {
auto descriptor = static_cast<ArchiveDescriptor *>(client_data);
if (whence != SEEK_CUR && whence != SEEK_SET && whence != SEEK_END) {
return ARCHIVE_FATAL;
}
auto current_offset = descriptor->offset;
auto current_chunk_offset = descriptor->current_chunk_offset;
auto current_chunk = descriptor->current;
auto &output_current_chunk_offset = descriptor->current_chunk_offset;
auto &output_current = descriptor->current;
auto &output_offset = descriptor->offset;
if (whence == SEEK_SET) {
current_offset = 0;
current_chunk_offset = 0;
current_chunk = descriptor->data.begin();
} else if (whence == SEEK_END) {
current_offset = descriptor->size;
current_chunk_offset = 0;
current_chunk = descriptor->data.end();
}
if (current_offset + offset < 0 || current_offset + offset >= descriptor->size) {
return ARCHIVE_FATAL;
}
// положительное смещение
if (offset >= 0) {
size_t current_size = current_chunk->size - current_chunk_offset;
std::cout << "SEEK_SET" << std::endl;
if (offset < current_size) { // если мы не выходим за текущий чанк
output_current = current_chunk;
output_current_chunk_offset = current_chunk_offset + offset; // то прото двигаемся в нём
return output_offset;
}
// иначе мы выходим за чанк => вычитаем текущий хвостик и двигаемся на следующий чанк
offset -= current_size;
// применяем тот же алгоритм, что выше для begin
auto it = current_chunk + 1;
while (it != descriptor->data.end() && offset >= it->size) {
offset -= it->size;
++it;
}
output_current = it;
output_current_chunk_offset = offset;
return output_offset;
}
// отрицательное смещение смещение
offset = -offset;
if (offset <= current_chunk_offset) { // если мы не выходим за текущий чанк
output_current = current_chunk;
output_current_chunk_offset = current_chunk_offset - offset; // то проcто двигаемся в нём
return output_offset;
}
offset -= current_chunk_offset;
auto it { current_chunk - 1};
while (it != descriptor->data.begin() && offset >= it->size) {
offset -= it->size;
--it;
}
output_current = it;
output_current_chunk_offset = output_current->size - offset;
return output_offset;
}
int main(int argc, char** argv) {
/*if(argc < 2) {
std::cerr << "Argc < 2\n";
return 0;
}
struct archive_entry *entry;
struct archive *a = archive_read_new();
archive_read_support_format_7zip(a);*/
DataChunkList lst;
lst.emplace_back(reinterpret_cast<uint8_t*>(new int[100]), sizeof(int)*100);
lst.emplace_back(reinterpret_cast<uint8_t*>(new int[200]), sizeof(int)*200);
lst.emplace_back(reinterpret_cast<uint8_t*>(new int[300]), sizeof(int)*300);
lst.emplace_back(reinterpret_cast<uint8_t*>(new int[400]), sizeof(int)*400);
lst.emplace_back(reinterpret_cast<uint8_t*>(new int[500]), sizeof(int)*500);
int i = 0;
for (auto && item : lst) {
for (int j = 0; j < item.size / sizeof(int); ++j) {
((int*)item.data)[j] = i++;
}
}
ArchiveDescriptor info(lst);
const void *buffer = nullptr;
int length = archiveRead(nullptr, &info, &buffer);
for (int i = 0; i < std::min(10, length); ++i) {
std::cout << ((int*)(buffer))[i] << std::endl;
}
//
// 1024
auto ret = archiveSeek(nullptr, &info, 2 * sizeof(int), SEEK_CUR);
std:: cout << "archiveSeek = " << ret << std::endl;
length = archiveRead(nullptr, &info, &buffer);
std::cout << "length = " << length / sizeof(int) << std::endl;
for (int i = 0; i < std::min(10ul, length / sizeof(int)); ++i) {
std::cout << ((int*)(buffer))[i] << std::endl;
}
/*
archive_clear_error(a);
archive_read_set_seek_callback(a, archiveSeek);
int r = archive_read_open(a, &info, nullptr, archiveRead, nullptr);
//int r = archive_read_open_filename(a, argv[1], 10240);
//archive_read_open_memory -- открыть архив из памяти
//archive_read_open_fd -- файловый дескриптор
if (r != ARCHIVE_OK) {
std::cout << "Archive not ok0\n";
exit(1);
}
int res = ARCHIVE_OK;
for(;;) {
fprintf(stderr, "Read next header\n");
res = archive_read_next_header(a, &entry);
if(res != ARCHIVE_OK || res == ARCHIVE_EOF) {
fprintf(stderr, "Break %d; err %s\n", res, archive_error_string(a));
break;
}
printf("%s size %ld\n",archive_entry_pathname(entry), archive_entry_size(entry));
std::vector<uint8_t > data(archive_entry_size(entry));
//archive_read_data_skip(a);
auto len = archive_read_data(a, data.data(), data.size());
printf("Read %ld bytes\n", len);
}
std::cout << archive_read_has_encrypted_entries(a) << " = encrypted entries \n";
if (res == ARCHIVE_FAILED || res == ARCHIVE_FATAL) {
std::cout << "Archive not ok1\n";
exit(1);
}
//TODO scopeguard
r = archive_read_free(a);
if (r != ARCHIVE_OK) {
std::cout << "Archive not ok\n";
exit(1);
}
std::cout << "Archive ok\n";*/
return 0;
}