mirror of
https://github.com/corda/corda.git
synced 2025-01-06 05:04:20 +00:00
implement minimal, read-only versions of RandomAccessFile and ZipFile
This commit is contained in:
parent
07daa9be51
commit
4c307ae8c6
@ -14,12 +14,14 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "jni.h"
|
||||
#include "jni-util.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# include <windows.h>
|
||||
# include <io.h>
|
||||
|
||||
# define OPEN _open
|
||||
@ -34,6 +36,7 @@
|
||||
# define OPEN_MASK O_BINARY
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include "sys/mman.h"
|
||||
|
||||
# define OPEN open
|
||||
# define CLOSE close
|
||||
@ -47,6 +50,8 @@
|
||||
# define OPEN_MASK 0
|
||||
#endif
|
||||
|
||||
inline void* operator new(size_t, void* p) throw() { return p; }
|
||||
|
||||
namespace {
|
||||
|
||||
inline bool
|
||||
@ -95,9 +100,118 @@ doWrite(JNIEnv* e, jint fd, const jbyte* data, jint length)
|
||||
int r = WRITE(fd, data, length);
|
||||
if (r != length) {
|
||||
throwNew(e, "java/io/IOException", strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
class Mapping {
|
||||
public:
|
||||
Mapping(uint8_t* start, size_t length, HANDLE mapping, HANDLE file):
|
||||
start(start),
|
||||
length(length),
|
||||
mapping(mapping),
|
||||
file(file)
|
||||
{ }
|
||||
|
||||
uint8_t* start;
|
||||
size_t length;
|
||||
HANDLE mapping;
|
||||
HANDLE file;
|
||||
};
|
||||
|
||||
inline Mapping*
|
||||
map(JNIEnv* e, const char* path)
|
||||
{
|
||||
Mapping* result = 0;
|
||||
HANDLE file = CreateFile(path, FILE_READ_DATA, FILE_SHARE_READ, 0,
|
||||
OPEN_EXISTING, 0, 0);
|
||||
if (file != INVALID_HANDLE_VALUE) {
|
||||
unsigned size = GetFileSize(file, 0);
|
||||
if (size != INVALID_FILE_SIZE) {
|
||||
HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, size, 0);
|
||||
if (mapping) {
|
||||
void* data = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
|
||||
if (data) {
|
||||
void* p = allocate(e, sizeof(Mapping));
|
||||
if (not e->ExceptionOccurred()) {
|
||||
result = new (p)
|
||||
Mapping(static_cast<uint8_t*>(data), size, file, mapping);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
CloseHandle(mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
CloseHandle(file);
|
||||
}
|
||||
}
|
||||
if (result == 0 and not e->ExceptionOccurred()) {
|
||||
throwNew(e, "java/io/IOException", "%d", GetLastError());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void
|
||||
unmap(JNIEnv*, Mapping* mapping)
|
||||
{
|
||||
UnmapViewOfFile(mapping->start);
|
||||
CloseHandle(mapping->mapping);
|
||||
CloseHandle(mapping->file);
|
||||
free(mapping);
|
||||
}
|
||||
|
||||
#else // not WIN32
|
||||
|
||||
class Mapping {
|
||||
public:
|
||||
Mapping(uint8_t* start, size_t length):
|
||||
start(start),
|
||||
length(length)
|
||||
{ }
|
||||
|
||||
uint8_t* start;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
inline Mapping*
|
||||
map(JNIEnv* e, const char* path)
|
||||
{
|
||||
Mapping* result = 0;
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd != -1) {
|
||||
struct stat s;
|
||||
int r = fstat(fd, &s);
|
||||
if (r != -1) {
|
||||
void* data = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (data) {
|
||||
void* p = allocate(e, sizeof(Mapping));
|
||||
if (not e->ExceptionOccurred()) {
|
||||
result = new (p) Mapping(static_cast<uint8_t*>(data), s.st_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
if (result == 0 and not e->ExceptionOccurred()) {
|
||||
throwNew(e, "java/io/IOException", strerror(errno));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void
|
||||
unmap(JNIEnv*, Mapping* mapping)
|
||||
{
|
||||
munmap(mapping->start, mapping->length);
|
||||
free(mapping);
|
||||
}
|
||||
|
||||
#endif // not WIN32
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" JNIEXPORT jstring JNICALL
|
||||
@ -333,3 +447,42 @@ Java_java_io_FileOutputStream_close(JNIEnv* e, jclass, jint fd)
|
||||
{
|
||||
doClose(e, fd);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_java_io_RandomAccessFile_open(JNIEnv* e, jclass, jstring path,
|
||||
jlongArray result)
|
||||
{
|
||||
const char* chars = e->GetStringUTFChars(path, 0);
|
||||
if (chars) {
|
||||
Mapping* mapping = map(e, chars);
|
||||
|
||||
jlong peer = reinterpret_cast<jlong>(mapping);
|
||||
e->SetLongArrayRegion(result, 0, 1, &peer);
|
||||
|
||||
jlong length = mapping->length;
|
||||
e->SetLongArrayRegion(result, 1, 1, &length);
|
||||
|
||||
e->ReleaseStringUTFChars(path, chars);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_java_io_RandomAccessFile_copy(JNIEnv* e, jclass, jlong peer,
|
||||
jlong position, jbyteArray buffer,
|
||||
int offset, int length)
|
||||
{
|
||||
uint8_t* dst = reinterpret_cast<uint8_t*>
|
||||
(e->GetPrimitiveArrayCritical(buffer, 0));
|
||||
|
||||
memcpy(dst + offset,
|
||||
reinterpret_cast<Mapping*>(peer)->start + position,
|
||||
length);
|
||||
|
||||
e->ReleasePrimitiveArrayCritical(buffer, dst, 0);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_java_io_RandomAccessFile_close(JNIEnv* e, jclass, jlong peer)
|
||||
{
|
||||
unmap(e, reinterpret_cast<Mapping*>(peer));
|
||||
}
|
||||
|
62
classpath/java/io/RandomAccessFile.java
Normal file
62
classpath/java/io/RandomAccessFile.java
Normal file
@ -0,0 +1,62 @@
|
||||
package java.io;
|
||||
|
||||
public class RandomAccessFile {
|
||||
private long peer;
|
||||
private long length;
|
||||
private long position = 0;
|
||||
|
||||
public RandomAccessFile(String name, String mode)
|
||||
throws FileNotFoundException
|
||||
{
|
||||
if (! mode.equals("r")) throw new IllegalArgumentException();
|
||||
|
||||
long[] result = new long[2];
|
||||
open(name, result);
|
||||
peer = result[0];
|
||||
length = result[1];
|
||||
}
|
||||
|
||||
private static native void open(String name, long[] result)
|
||||
throws FileNotFoundException;
|
||||
|
||||
public long length() throws IOException {
|
||||
return length;
|
||||
}
|
||||
|
||||
public long getFilePointer() throws IOException {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void seek(long position) throws IOException {
|
||||
if (position < 0 || position > length) throw new IOException();
|
||||
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public void readFully(byte[] buffer, int offset, int length)
|
||||
throws IOException
|
||||
{
|
||||
if (peer == 0) throw new IOException();
|
||||
|
||||
if (length == 0) return;
|
||||
|
||||
if (position + length > this.length) throw new EOFException();
|
||||
|
||||
if (offset < 0 || offset + length > buffer.length)
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
|
||||
copy(peer, position, buffer, offset, length);
|
||||
}
|
||||
|
||||
private static native void copy(long peer, long position, byte[] buffer,
|
||||
int offset, int length);
|
||||
|
||||
public void close() throws IOException {
|
||||
if (peer != 0) {
|
||||
close(peer);
|
||||
peer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static native void close(long peer);
|
||||
}
|
6
classpath/java/util/zip/ZipEntry.java
Normal file
6
classpath/java/util/zip/ZipEntry.java
Normal file
@ -0,0 +1,6 @@
|
||||
package java.util.zip;
|
||||
|
||||
public abstract class ZipEntry {
|
||||
public abstract String getName();
|
||||
public abstract int getCompressedSize();
|
||||
}
|
295
classpath/java/util/zip/ZipFile.java
Normal file
295
classpath/java/util/zip/ZipFile.java
Normal file
@ -0,0 +1,295 @@
|
||||
package java.util.zip;
|
||||
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ZipFile {
|
||||
private final RandomAccessFile file;
|
||||
private final Window window;
|
||||
private final Map<String,Integer> index = new HashMap();
|
||||
|
||||
public ZipFile(String name) throws IOException {
|
||||
file = new RandomAccessFile(name, "r");
|
||||
window = new Window(file, 4096);
|
||||
|
||||
int fileLength = (int) file.length();
|
||||
int pointer = fileLength - 22;
|
||||
byte[] magic = new byte[] { 0x50, 0x4B, 0x05, 0x06 };
|
||||
while (pointer > 0) {
|
||||
if (equal(window.data, window.seek(pointer, magic.length),
|
||||
magic, 0, magic.length))
|
||||
{
|
||||
pointer = directoryOffset(window, pointer);
|
||||
|
||||
magic = new byte[] { 0x50, 0x4B, 0x01, 0x02 };
|
||||
while (pointer < fileLength) {
|
||||
if (equal(window.data, window.seek(pointer, magic.length),
|
||||
magic, 0, magic.length))
|
||||
{
|
||||
index.put(entryName(window, pointer), pointer);
|
||||
pointer = entryEnd(window, pointer);
|
||||
} else {
|
||||
pointer = fileLength;
|
||||
}
|
||||
}
|
||||
pointer = 0;
|
||||
} else {
|
||||
-- pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return index.size();
|
||||
}
|
||||
|
||||
public Enumeration<ZipEntry> entries() {
|
||||
return new MyEnumeration(window, index.values().iterator());
|
||||
}
|
||||
|
||||
public ZipEntry getEntry(String name) {
|
||||
Integer pointer = index.get(name);
|
||||
return (pointer == null ? null : new MyZipEntry(window, pointer));
|
||||
}
|
||||
|
||||
public InputStream getInputStream(ZipEntry entry) throws IOException {
|
||||
int pointer = ((MyZipEntry) entry).pointer;
|
||||
int method = compressionMethod(window, pointer);
|
||||
int size = compressedSize(window, pointer);
|
||||
InputStream in = new MyInputStream(file, fileData(window, pointer), size);
|
||||
|
||||
final int Stored = 0;
|
||||
final int Deflated = 8;
|
||||
|
||||
switch (method) {
|
||||
case Stored:
|
||||
return in;
|
||||
|
||||
case Deflated:
|
||||
return new InflaterInputStream(in, new Inflater(true));
|
||||
|
||||
default:
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean equal(byte[] a, int aOffset, byte[] b, int bOffset,
|
||||
int size)
|
||||
{
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (a[aOffset + i] != b[bOffset + i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int get2(Window w, int p) throws IOException {
|
||||
int offset = w.seek(p, 2);
|
||||
return
|
||||
((w.data[offset + 1] & 0xFF) << 8) |
|
||||
((w.data[offset ] & 0xFF) );
|
||||
}
|
||||
|
||||
private static int get4(Window w, int p) throws IOException {
|
||||
int offset = w.seek(p, 4);
|
||||
return
|
||||
((w.data[offset + 3] & 0xFF) << 24) |
|
||||
((w.data[offset + 2] & 0xFF) << 16) |
|
||||
((w.data[offset + 1] & 0xFF) << 8) |
|
||||
((w.data[offset ] & 0xFF) );
|
||||
}
|
||||
|
||||
private static int directoryOffset(Window w, int p) throws IOException {
|
||||
return get4(w, p + 16);
|
||||
}
|
||||
|
||||
private static int entryNameLength(Window w, int p) throws IOException {
|
||||
return get2(w, p + 28);
|
||||
}
|
||||
|
||||
private static String entryName(Window w, int p) throws IOException {
|
||||
int length = entryNameLength(w, p);
|
||||
return new String(w.data, w.seek(p + 46, length), length);
|
||||
}
|
||||
|
||||
private static int compressionMethod(Window w, int p) throws IOException {
|
||||
return get2(w, p + 10);
|
||||
}
|
||||
|
||||
private static int compressedSize(Window w, int p) throws IOException {
|
||||
return get4(w, p + 20);
|
||||
}
|
||||
|
||||
private static int fileNameLength(Window w, int p) throws IOException {
|
||||
return get2(w, p + 28);
|
||||
}
|
||||
|
||||
private static int extraFieldLength(Window w, int p) throws IOException {
|
||||
return get2(w, p + 30);
|
||||
}
|
||||
|
||||
private static int commentFieldLength(Window w, int p) throws IOException {
|
||||
return get2(w, p + 32);
|
||||
}
|
||||
|
||||
private static int entryEnd(Window w, int p) throws IOException {
|
||||
final int HeaderSize = 46;
|
||||
return p + HeaderSize
|
||||
+ fileNameLength(w, p)
|
||||
+ extraFieldLength(w, p)
|
||||
+ commentFieldLength(w, p);
|
||||
}
|
||||
|
||||
private static int fileData(Window w, int p) throws IOException {
|
||||
int localHeader = localHeader(w, p);
|
||||
final int LocalHeaderSize = 30;
|
||||
return localHeader
|
||||
+ LocalHeaderSize
|
||||
+ localFileNameLength(w, localHeader)
|
||||
+ localExtraFieldLength(w, localHeader);
|
||||
}
|
||||
|
||||
private static int localHeader(Window w, int p) throws IOException {
|
||||
return get4(w, p + 42);
|
||||
}
|
||||
|
||||
private static int localFileNameLength(Window w, int p) throws IOException {
|
||||
return get2(w, p + 26);
|
||||
}
|
||||
|
||||
private static int localExtraFieldLength(Window w, int p)
|
||||
throws IOException
|
||||
{
|
||||
return get2(w, p + 28);
|
||||
}
|
||||
|
||||
private static class Window {
|
||||
private final RandomAccessFile file;
|
||||
public final byte[] data;
|
||||
public int start;
|
||||
public int length;
|
||||
|
||||
public Window(RandomAccessFile file, int size) {
|
||||
this.file = file;
|
||||
data = new byte[size];
|
||||
}
|
||||
|
||||
public int seek(int start, int length) throws IOException {
|
||||
int fileLength = (int) file.length();
|
||||
|
||||
if (length > data.length) {
|
||||
throw new IllegalArgumentException
|
||||
("length " + length + " greater than buffer length " + data.length);
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException("negative start " + start);
|
||||
}
|
||||
|
||||
if (start + length > fileLength) {
|
||||
throw new IllegalArgumentException
|
||||
("end " + (start + length) + " greater than file length " +
|
||||
fileLength);
|
||||
}
|
||||
|
||||
if (start < this.start || start + length > this.start + this.length) {
|
||||
this.length = Math.min(data.length, fileLength);
|
||||
this.start = start - ((this.length - length) / 2);
|
||||
if (this.start < 0) {
|
||||
this.start = 0;
|
||||
} else if (this.start + this.length > fileLength) {
|
||||
this.start = fileLength - this.length;
|
||||
}
|
||||
file.seek(this.start);
|
||||
// System.out.println("start " + start + " length " + length + " this start " + this.start + " this.length " + this.length + " file length " + fileLength);
|
||||
file.readFully(data, 0, this.length);
|
||||
}
|
||||
|
||||
return start - this.start;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyZipEntry extends ZipEntry {
|
||||
public final Window window;
|
||||
public final int pointer;
|
||||
|
||||
public MyZipEntry(Window window, int pointer) {
|
||||
this.window = window;
|
||||
this.pointer = pointer;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
try {
|
||||
return entryName(window, pointer);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCompressedSize() {
|
||||
try {
|
||||
return compressedSize(window, pointer);
|
||||
} catch (IOException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyEnumeration implements Enumeration<ZipEntry> {
|
||||
private final Window window;
|
||||
private final Iterator<Integer> iterator;
|
||||
|
||||
public MyEnumeration(Window window, Iterator<Integer> iterator) {
|
||||
this.window = window;
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
public boolean hasMoreElements() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
public ZipEntry nextElement() {
|
||||
return new MyZipEntry(window, iterator.next());
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyInputStream extends InputStream {
|
||||
private RandomAccessFile file;
|
||||
private int offset;
|
||||
private int length;
|
||||
|
||||
public MyInputStream(RandomAccessFile file, int start, int length) {
|
||||
this.file = file;
|
||||
this.offset = start;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
byte[] b = new byte[1];
|
||||
int c = read(b);
|
||||
return (c == -1 ? -1 : b[0] & 0xFF);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int offset, int length) throws IOException {
|
||||
if (this.length == 0) return -1;
|
||||
|
||||
if (length > this.length) length = this.length;
|
||||
|
||||
file.seek(this.offset);
|
||||
file.readFully(b, offset, length);
|
||||
|
||||
this.offset += length;
|
||||
this.length -= length;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
file = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,9 @@
|
||||
#ifndef JNI_UTIL
|
||||
#define JNI_UTIL
|
||||
|
||||
#include "stdio.h"
|
||||
#include "stdlib.h"
|
||||
|
||||
#undef JNIEXPORT
|
||||
#ifdef __MINGW32__
|
||||
# define JNIEXPORT __declspec(dllexport)
|
||||
@ -23,15 +26,37 @@
|
||||
namespace {
|
||||
|
||||
inline void
|
||||
throwNew(JNIEnv* e, const char* class_, const char* message)
|
||||
throwNew(JNIEnv* e, const char* class_, const char* message, ...)
|
||||
{
|
||||
jclass c = e->FindClass(class_);
|
||||
if (c) {
|
||||
e->ThrowNew(c, message);
|
||||
if (message) {
|
||||
static const unsigned BufferSize = 256;
|
||||
char buffer[BufferSize];
|
||||
|
||||
va_list list;
|
||||
va_start(list, message);
|
||||
vsnprintf(buffer, BufferSize - 1, message, list);
|
||||
va_end(list);
|
||||
|
||||
e->ThrowNew(c, buffer);
|
||||
} else {
|
||||
e->ThrowNew(c, 0);
|
||||
}
|
||||
e->DeleteLocalRef(c);
|
||||
}
|
||||
}
|
||||
|
||||
inline void*
|
||||
allocate(JNIEnv* e, unsigned size)
|
||||
{
|
||||
void* p = malloc(size);
|
||||
if (p == 0) {
|
||||
throwNew(e, "java/lang/OutOfMemoryError", 0);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif//JNI_UTIL
|
||||
|
26
test/Zip.java
Normal file
26
test/Zip.java
Normal file
@ -0,0 +1,26 @@
|
||||
import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
public class Zip {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ZipFile file = new ZipFile("build/classpath.jar");
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
for (Enumeration<ZipEntry> e = file.entries(); e.hasMoreElements();) {
|
||||
ZipEntry entry = e.nextElement();
|
||||
InputStream in = file.getInputStream(entry);
|
||||
try {
|
||||
int size = 0;
|
||||
int c; while ((c = in.read(buffer)) != -1) size += c;
|
||||
System.out.println
|
||||
(entry.getName() + " " + entry.getCompressedSize() + " " + size);
|
||||
} finally {
|
||||
in.read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -17,7 +17,7 @@ for test in ${tests}; do
|
||||
printf "%16s" "${test}: "
|
||||
|
||||
case ${mode} in
|
||||
debug|debug-fast|fast )
|
||||
debug|debug-fast|fast|small )
|
||||
${vm} ${flags} ${test} >>${log} 2>&1;;
|
||||
|
||||
stress* )
|
||||
|
Loading…
Reference in New Issue
Block a user