2007-09-19 16:22:19 +00:00
|
|
|
#include "zlib-custom.h"
|
2007-07-20 14:36:31 +00:00
|
|
|
#include "system.h"
|
2007-08-10 23:45:47 +00:00
|
|
|
#include "finder.h"
|
2007-07-20 14:36:31 +00:00
|
|
|
|
|
|
|
using namespace vm;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
const char*
|
2007-08-10 23:45:47 +00:00
|
|
|
append(System* s, const char* a, const char* b, const char* c)
|
2007-07-20 14:36:31 +00:00
|
|
|
{
|
|
|
|
unsigned al = strlen(a);
|
|
|
|
unsigned bl = strlen(b);
|
|
|
|
unsigned cl = strlen(c);
|
2007-08-10 23:45:47 +00:00
|
|
|
char* p = static_cast<char*>(s->allocate(al + bl + cl + 1));
|
2007-07-20 14:36:31 +00:00
|
|
|
memcpy(p, a, al);
|
|
|
|
memcpy(p + al, b, bl);
|
2007-08-10 23:45:47 +00:00
|
|
|
memcpy(p + al + bl, c, cl + 1);
|
2007-07-20 14:36:31 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2007-08-27 13:46:17 +00:00
|
|
|
const char*
|
|
|
|
copy(System* s, const char* a)
|
|
|
|
{
|
|
|
|
unsigned al = strlen(a);
|
|
|
|
char* p = static_cast<char*>(s->allocate(al + 1));
|
|
|
|
memcpy(p, a, al + 1);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2007-09-17 00:13:36 +00:00
|
|
|
bool
|
|
|
|
equal(const void* a, unsigned al, const void* b, unsigned bl)
|
|
|
|
{
|
|
|
|
if (al == bl) {
|
|
|
|
return memcmp(a, b, al) == 0;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class Element {
|
|
|
|
public:
|
|
|
|
Element(): next(0) { }
|
|
|
|
virtual ~Element() { }
|
|
|
|
virtual System::Region* find(const char* name) = 0;
|
|
|
|
virtual bool exists(const char* name) = 0;
|
|
|
|
virtual void dispose() = 0;
|
|
|
|
|
|
|
|
Element* next;
|
|
|
|
};
|
|
|
|
|
|
|
|
class DirectoryElement: public Element {
|
|
|
|
public:
|
|
|
|
DirectoryElement(System* s, const char* name):
|
|
|
|
s(s), name(name)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual System::Region* find(const char* name) {
|
|
|
|
const char* file = append(s, this->name, "/", name);
|
|
|
|
System::Region* region;
|
|
|
|
System::Status status = s->map(®ion, file);
|
|
|
|
s->free(file);
|
|
|
|
|
|
|
|
if (s->success(status)) {
|
|
|
|
return region;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool exists(const char* name) {
|
|
|
|
const char* file = append(s, this->name, "/", name);
|
|
|
|
System::FileType type = s->identify(file);
|
|
|
|
s->free(file);
|
|
|
|
return type != System::DoesNotExist;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void dispose() {
|
|
|
|
s->free(name);
|
|
|
|
s->free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
System* s;
|
|
|
|
const char* name;
|
|
|
|
};
|
|
|
|
|
|
|
|
class PointerRegion: public System::Region {
|
|
|
|
public:
|
|
|
|
PointerRegion(System* s, const uint8_t* start, size_t length):
|
|
|
|
s(s),
|
|
|
|
start_(start),
|
|
|
|
length_(length)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual const uint8_t* start() {
|
|
|
|
return start_;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual size_t length() {
|
|
|
|
return length_;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void dispose() {
|
|
|
|
s->free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
System* s;
|
|
|
|
const uint8_t* start_;
|
|
|
|
size_t length_;
|
|
|
|
};
|
|
|
|
|
|
|
|
class DataRegion: public System::Region {
|
|
|
|
public:
|
|
|
|
DataRegion(System* s, size_t length):
|
|
|
|
s(s),
|
|
|
|
length_(length)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual const uint8_t* start() {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual size_t length() {
|
|
|
|
return length_;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void dispose() {
|
|
|
|
s->free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
System* s;
|
|
|
|
size_t length_;
|
|
|
|
uint8_t data[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
class JarIndex {
|
|
|
|
public:
|
|
|
|
static const unsigned HeaderSize = 30;
|
|
|
|
|
|
|
|
enum CompressionMethod {
|
|
|
|
Stored = 0,
|
|
|
|
Deflated = 8
|
|
|
|
};
|
|
|
|
|
|
|
|
class Node {
|
|
|
|
public:
|
|
|
|
Node(uint32_t hash, const uint8_t* entry, Node* next):
|
|
|
|
hash(hash), entry(entry), next(next)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
uint32_t hash;
|
|
|
|
const uint8_t* entry;
|
|
|
|
Node* next;
|
|
|
|
};
|
|
|
|
|
|
|
|
JarIndex(System* s, unsigned capacity):
|
|
|
|
s(s),
|
|
|
|
capacity(capacity),
|
|
|
|
position(0),
|
2007-09-17 14:11:41 +00:00
|
|
|
nodes(static_cast<Node*>(s->allocate(sizeof(Node) * capacity)))
|
2007-09-17 00:13:36 +00:00
|
|
|
{
|
|
|
|
memset(table, 0, sizeof(Node*) * capacity);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t get2(const uint8_t* p) {
|
|
|
|
return
|
|
|
|
(static_cast<uint16_t>(p[1]) << 8) |
|
|
|
|
(static_cast<uint16_t>(p[0]) );
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t get4(const uint8_t* p) {
|
|
|
|
return
|
|
|
|
(static_cast<uint32_t>(p[3]) << 24) |
|
|
|
|
(static_cast<uint32_t>(p[2]) << 16) |
|
|
|
|
(static_cast<uint32_t>(p[1]) << 8) |
|
|
|
|
(static_cast<uint32_t>(p[0]) );
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t signature(const uint8_t* p) {
|
|
|
|
return get4(p);
|
|
|
|
}
|
|
|
|
|
2007-09-17 14:11:41 +00:00
|
|
|
static uint16_t compressionMethod(const uint8_t* p) {
|
|
|
|
return get2(p + 8);
|
2007-09-17 00:13:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t compressedSize(const uint8_t* p) {
|
|
|
|
return get4(p + 18);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t uncompressedSize(const uint8_t* p) {
|
|
|
|
return get4(p + 22);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t fileNameLength(const uint8_t* p) {
|
|
|
|
return get2(p + 26);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint16_t extraFieldLength(const uint8_t* p) {
|
|
|
|
return get2(p + 28);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const uint8_t* fileName(const uint8_t* p) {
|
|
|
|
return p + 30;
|
|
|
|
}
|
|
|
|
|
2007-09-17 14:11:41 +00:00
|
|
|
static const uint8_t* fileData(const uint8_t* p) {
|
|
|
|
return p + HeaderSize + fileNameLength(p) + extraFieldLength(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const uint8_t* endOfEntry(const uint8_t* p) {
|
|
|
|
return fileData(p) + compressedSize(p);
|
|
|
|
}
|
|
|
|
|
2007-09-17 00:13:36 +00:00
|
|
|
static JarIndex* make(System* s, unsigned capacity) {
|
|
|
|
return new
|
|
|
|
(s->allocate(sizeof(JarIndex) + (sizeof(Node*) * capacity)))
|
|
|
|
JarIndex(s, capacity);
|
|
|
|
}
|
|
|
|
|
|
|
|
static JarIndex* open(System* s, System::Region* region) {
|
|
|
|
JarIndex* index = make(s, 32);
|
|
|
|
|
|
|
|
const uint8_t* p = region->start();
|
|
|
|
const uint8_t* end = p + region->length();
|
|
|
|
while (p < end) {
|
|
|
|
if (signature(p) == 0x04034b50) {
|
|
|
|
index = index->add(hash(fileName(p), fileNameLength(p)), p);
|
|
|
|
|
2007-09-17 14:11:41 +00:00
|
|
|
p = endOfEntry(p);
|
2007-09-17 00:13:36 +00:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
JarIndex* add(uint32_t hash, const uint8_t* entry) {
|
|
|
|
if (position < capacity) {
|
|
|
|
unsigned i = hash & (capacity - 1);
|
|
|
|
table[i] = new (nodes + (position++)) Node(hash, entry, table[i]);
|
|
|
|
return this;
|
|
|
|
} else {
|
|
|
|
JarIndex* index = make(s, capacity * 2);
|
|
|
|
for (unsigned i = 0; i < capacity; ++i) {
|
|
|
|
index->add(nodes[i].hash, nodes[i].entry);
|
|
|
|
}
|
|
|
|
index->add(hash, entry);
|
|
|
|
dispose();
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Node* findNode(const char* name) {
|
|
|
|
unsigned length = strlen(name);
|
|
|
|
unsigned i = hash(name) & (capacity - 1);
|
|
|
|
for (Node* n = table[i]; n; n = n->next) {
|
|
|
|
const uint8_t* p = n->entry;
|
|
|
|
if (equal(name, length, fileName(p), fileNameLength(p))) {
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
System::Region* find(const char* name) {
|
|
|
|
Node* n = findNode(name);
|
|
|
|
if (n) {
|
|
|
|
const uint8_t* p = n->entry;
|
|
|
|
switch (compressionMethod(p)) {
|
|
|
|
case Stored: {
|
|
|
|
return new (s->allocate(sizeof(PointerRegion)))
|
2007-09-17 14:11:41 +00:00
|
|
|
PointerRegion(s, fileData(p), compressedSize(p));
|
2007-09-17 00:13:36 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
case Deflated: {
|
|
|
|
DataRegion* region = new
|
|
|
|
(s->allocate(sizeof(DataRegion) + uncompressedSize(p)))
|
|
|
|
DataRegion(s, uncompressedSize(p));
|
|
|
|
|
2007-09-17 14:11:41 +00:00
|
|
|
z_stream zStream; memset(&zStream, 0, sizeof(z_stream));
|
|
|
|
|
|
|
|
zStream.next_in = const_cast<uint8_t*>(fileData(p));
|
|
|
|
zStream.avail_in = compressedSize(p);
|
|
|
|
zStream.next_out = region->data;
|
|
|
|
zStream.avail_out = region->length();
|
|
|
|
|
|
|
|
// -15 means max window size and raw deflate (no zlib wrapper)
|
|
|
|
int r = inflateInit2(&zStream, -15);
|
|
|
|
expect(s, r == Z_OK);
|
|
|
|
|
|
|
|
r = inflate(&zStream, Z_FINISH);
|
|
|
|
expect(s, r == Z_STREAM_END);
|
|
|
|
|
|
|
|
inflateEnd(&zStream);
|
|
|
|
|
|
|
|
return region;
|
2007-09-17 00:13:36 +00:00
|
|
|
} break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
abort(s);
|
|
|
|
}
|
|
|
|
}
|
2007-09-21 14:15:39 +00:00
|
|
|
|
2007-09-17 00:13:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool exists(const char* name) {
|
|
|
|
return findNode(name) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void dispose() {
|
|
|
|
s->free(nodes);
|
|
|
|
s->free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
System* s;
|
|
|
|
unsigned capacity;
|
|
|
|
unsigned position;
|
|
|
|
Node* nodes;
|
|
|
|
Node* table[0];
|
|
|
|
};
|
|
|
|
|
|
|
|
class JarElement: public Element {
|
|
|
|
public:
|
|
|
|
JarElement(System* s, const char* name):
|
2007-10-24 15:46:09 +00:00
|
|
|
s(s), name(name), region(0), index(0)
|
2007-09-17 00:13:36 +00:00
|
|
|
{ }
|
|
|
|
|
|
|
|
void init() {
|
|
|
|
if (index == 0) {
|
|
|
|
System::Region* r;
|
|
|
|
if (s->success(s->map(&r, this->name))) {
|
|
|
|
region = r;
|
|
|
|
index = JarIndex::open(s, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual System::Region* find(const char* name) {
|
|
|
|
init();
|
2007-09-21 14:15:39 +00:00
|
|
|
|
|
|
|
while (*name == '/') name++;
|
|
|
|
|
2007-09-17 00:13:36 +00:00
|
|
|
return (index ? index->find(name) : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool exists(const char* name) {
|
|
|
|
init();
|
2007-09-21 14:15:39 +00:00
|
|
|
|
|
|
|
while (*name == '/') name++;
|
|
|
|
|
2007-09-17 00:13:36 +00:00
|
|
|
return (index ? index->exists(name) : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void dispose() {
|
|
|
|
s->free(name);
|
|
|
|
if (index) {
|
|
|
|
index->dispose();
|
|
|
|
region->dispose();
|
|
|
|
}
|
|
|
|
s->free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
System* s;
|
|
|
|
const char* name;
|
|
|
|
System::Region* region;
|
|
|
|
JarIndex* index;
|
|
|
|
};
|
|
|
|
|
|
|
|
Element*
|
2007-07-20 14:36:31 +00:00
|
|
|
parsePath(System* s, const char* path)
|
|
|
|
{
|
|
|
|
class Tokenizer {
|
|
|
|
public:
|
|
|
|
class Token {
|
|
|
|
public:
|
|
|
|
Token(const char* s, unsigned length): s(s), length(length) { }
|
|
|
|
|
|
|
|
const char* s;
|
|
|
|
unsigned length;
|
|
|
|
};
|
|
|
|
|
|
|
|
Tokenizer(const char* s, char delimiter): s(s), delimiter(delimiter) { }
|
|
|
|
|
|
|
|
bool hasMore() {
|
|
|
|
while (*s == delimiter) ++s;
|
|
|
|
return *s;
|
|
|
|
}
|
|
|
|
|
|
|
|
Token next() {
|
|
|
|
const char* p = s;
|
|
|
|
while (*s and *s != delimiter) ++s;
|
|
|
|
return Token(p, s - p);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* s;
|
|
|
|
char delimiter;
|
|
|
|
};
|
|
|
|
|
2007-09-17 00:13:36 +00:00
|
|
|
Element* first = 0;
|
|
|
|
Element* prev = 0;
|
2007-10-24 15:46:09 +00:00
|
|
|
for (Tokenizer t(path, s->pathSeparator()); t.hasMore();) {
|
2007-07-20 14:36:31 +00:00
|
|
|
Tokenizer::Token token(t.next());
|
2007-09-17 00:13:36 +00:00
|
|
|
char* name = static_cast<char*>(s->allocate(token.length + 1));
|
|
|
|
memcpy(name, token.s, token.length);
|
|
|
|
name[token.length] = 0;
|
|
|
|
|
|
|
|
Element* e;
|
|
|
|
switch (s->identify(name)) {
|
|
|
|
case System::File: {
|
|
|
|
e = new (s->allocate(sizeof(JarElement)))
|
|
|
|
JarElement(s, name);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case System::Directory: {
|
|
|
|
e = new (s->allocate(sizeof(DirectoryElement)))
|
|
|
|
DirectoryElement(s, name);
|
|
|
|
} break;
|
|
|
|
|
|
|
|
default: {
|
|
|
|
s->free(name);
|
|
|
|
e = 0;
|
|
|
|
} break;
|
|
|
|
}
|
2007-07-20 14:36:31 +00:00
|
|
|
|
2007-09-17 00:13:36 +00:00
|
|
|
if (e) {
|
|
|
|
if (prev) {
|
|
|
|
prev->next = e;
|
|
|
|
} else {
|
|
|
|
first = e;
|
|
|
|
}
|
|
|
|
prev = e;
|
|
|
|
}
|
|
|
|
}
|
2007-07-20 14:36:31 +00:00
|
|
|
|
2007-09-17 00:13:36 +00:00
|
|
|
return first;
|
2007-07-20 14:36:31 +00:00
|
|
|
}
|
|
|
|
|
2007-08-10 23:45:47 +00:00
|
|
|
class MyFinder: public Finder {
|
2007-07-20 14:36:31 +00:00
|
|
|
public:
|
2007-08-10 23:45:47 +00:00
|
|
|
MyFinder(System* system, const char* path):
|
2007-07-20 14:36:31 +00:00
|
|
|
system(system),
|
2007-08-27 13:46:17 +00:00
|
|
|
path_(parsePath(system, path)),
|
|
|
|
pathString(copy(system, path))
|
2007-07-20 14:36:31 +00:00
|
|
|
{ }
|
|
|
|
|
2007-09-17 00:13:36 +00:00
|
|
|
virtual System::Region* find(const char* name) {
|
|
|
|
for (Element* e = path_; e; e = e->next) {
|
|
|
|
System::Region* r = e->find(name);
|
|
|
|
if (r) {
|
|
|
|
return r;
|
2007-07-20 14:36:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-08-10 23:45:47 +00:00
|
|
|
virtual bool exists(const char* name) {
|
2007-09-17 00:13:36 +00:00
|
|
|
for (Element* e = path_; e; e = e->next) {
|
|
|
|
if (e->exists(name)) {
|
2007-08-10 23:45:47 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2007-08-27 13:46:17 +00:00
|
|
|
virtual const char* path() {
|
|
|
|
return pathString;
|
|
|
|
}
|
|
|
|
|
2007-07-20 14:36:31 +00:00
|
|
|
virtual void dispose() {
|
2007-09-17 00:13:36 +00:00
|
|
|
for (Element* e = path_; e;) {
|
|
|
|
Element* t = e;
|
|
|
|
e = e->next;
|
|
|
|
t->dispose();
|
2007-07-20 14:36:31 +00:00
|
|
|
}
|
2007-08-27 13:46:17 +00:00
|
|
|
system->free(pathString);
|
2007-07-20 14:36:31 +00:00
|
|
|
system->free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
System* system;
|
2007-09-17 00:13:36 +00:00
|
|
|
Element* path_;
|
2007-08-27 13:46:17 +00:00
|
|
|
const char* pathString;
|
2007-07-20 14:36:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace vm {
|
|
|
|
|
2007-08-10 23:45:47 +00:00
|
|
|
Finder*
|
|
|
|
makeFinder(System* s, const char* path)
|
2007-07-20 14:36:31 +00:00
|
|
|
{
|
2007-08-10 23:45:47 +00:00
|
|
|
return new (s->allocate(sizeof(MyFinder))) MyFinder(s, path);
|
2007-07-20 14:36:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace vm
|