Ver Fonte

Update hdf5.js (#589)

Lutz Roeder há 3 anos atrás
pai
commit
db3cc0af2e
1 ficheiros alterados com 216 adições e 115 exclusões
  1. 216 115
      source/hdf5.js

+ 216 - 115
source/hdf5.js

@@ -7,18 +7,19 @@ var zip = zip || require('./zip');
 hdf5.File = class {
 
     static open(data) {
-        if (data) {
-            const buffer = data instanceof Uint8Array ? data : data.peek();
-            const reader = new hdf5.BinaryReader(buffer, 0);
-            if (reader.match('\x89HDF\r\n\x1A\n')) {
-                return new hdf5.File(reader);
+        if (data && data.length >= 8) {
+            const buffer = data instanceof Uint8Array ? data : data.peek(8);
+            const signature = [ 0x89, 0x48, 0x44, 0x46, 0x0D, 0x0A, 0x1A, 0x0A ]; // \x89HDF\r\n\x1A\n
+            if (signature.every((value, index) => value === buffer[index])) {
+                return new hdf5.File(data);
             }
         }
         return null;
     }
 
-    constructor(reader) {
+    constructor(data) {
         // https://support.hdfgroup.org/HDF5/doc/H5.format.html
+        const reader = data instanceof Uint8Array ? new hdf5.BinaryReader(data) : (data.length < 0x10000000 ? new hdf5.BinaryReader(data.peek()) : new hdf5.StreamReader(data));        reader.skip(8);
         this._globalHeap = new hdf5.GlobalHeap(reader);
         const version = reader.byte();
         switch (version) {
@@ -35,7 +36,7 @@ hdf5.File = class {
                 reader.skip(4);
                 if (version > 0) {
                     this._indexedStorageInternalNodeK = reader.uint16();
-                    this.seek(2); // Reserved
+                    this.skip(2); // Reserved
                 }
                 this._baseAddress = reader.offset();
                 reader.offset(); // Address of File Free space Info
@@ -285,15 +286,9 @@ hdf5.Variable = class {
     }
 };
 
-hdf5.BinaryReader = class {
+hdf5.Reader = class {
 
-    constructor(buffer, view, position, offset, offsetSize, lengthSize) {
-        this._buffer = buffer;
-        this._view = view || new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
-        this._position = position || 0;
-        this._offset = offset || 0;
-        this._offsetSize = offsetSize;
-        this._lengthSize = lengthSize;
+    constructor() {
     }
 
     initialize() {
@@ -301,65 +296,44 @@ hdf5.BinaryReader = class {
         this._lengthSize = this.byte();
     }
 
-    skip(offset) {
-        this._offset += offset;
-        if (this._position + this._offset > this._buffer.length) {
-            throw new hdf5.Error('Expected ' + (this._position + this._offset - this._buffer.length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
-        }
-    }
-
-    read(length) {
-        const offset = this._offset;
-        this.skip(length);
-        return this._buffer.subarray(this._position + offset, this._position + this._offset);
-    }
-
     int8() {
-        const offset = this._offset;
-        this.skip(1);
-        return this._view.getInt8(this._position + offset);
+        const position = this.take(1);
+        return this._view.getInt8(position);
     }
 
     byte() {
-        const offset = this._offset;
-        this.skip(1);
-        return this._view.getUint8(this._position + offset);
+        const position = this.take(1);
+        return this._view.getUint8(position);
     }
 
     int16() {
-        const offset = this._position + this._offset;
-        this.skip(2);
-        return this._view.getInt16(offset, true);
+        const position = this.take(2);
+        return this._view.getInt16(position, true);
     }
 
     uint16() {
-        const offset = this._position + this._offset;
-        this.skip(2);
-        return this._view.getUint16(offset, true);
+        const position = this.take(2);
+        return this._view.getUint16(position, true);
     }
 
     int32() {
-        const offset = this._position + this._offset;
-        this.skip(4);
-        return this._view.getInt32(offset, true);
+        const position = this.take(4);
+        return this._view.getInt32(position, true);
     }
 
     uint32() {
-        const offset = this._position + this._offset;
-        this.skip(4);
-        return this._view.getUint32(offset, true);
+        const position = this.take(4);
+        return this._view.getUint32(position, true);
     }
 
     int64() {
-        const offset = this._position + this._offset;
-        this.skip(8);
-        return this._view.getInt64(offset, true).toNumber();
+        const position = this.take(8);
+        return this._view.getInt64(position, true).toNumber();
     }
 
     uint64() {
-        const offset = this._position + this._offset;
-        this.skip(8);
-        return this._view.getUint64(offset, true).toNumber();
+        const position = this.take(8);
+        return this._view.getUint64(position, true).toNumber();
     }
 
     uint(size) {
@@ -373,9 +347,8 @@ hdf5.BinaryReader = class {
     }
 
     float16() {
-        const offset = this._offset;
-        this.skip(2);
-        const value = this._view.getUint16(this._position + offset, true);
+        const position = this.take(2);
+        const value = this._view.getUint16(position, true);
         // decode float16 value
         const s = (value & 0x8000) >> 15;
         const e = (value & 0x7C00) >> 10;
@@ -390,51 +363,19 @@ hdf5.BinaryReader = class {
     }
 
     float32() {
-        const offset = this._position + this._offset;
-        this.skip(4);
-        return this._view.getFloat32(offset, true);
+        const position = this.take(4);
+        return this._view.getFloat32(position, true);
     }
 
     float64() {
-        const offset = this._position + this._offset;
-        this.skip(8);
-        return this._view.getFloat64(offset, true);
-    }
-
-    string(size, encoding) {
-        if (!size || size == -1) {
-            let position = this._position + this._offset;
-            while (this._buffer[position] != 0) {
-                position++;
-            }
-            size = position - this._position - this._offset + 1;
-        }
-        const data = this.read(size);
-        return hdf5.BinaryReader.decode(data, encoding);
-    }
-
-    static decode(data, encoding) {
-        let content = '';
-        if (encoding == 'utf-8') {
-            if (!hdf5.BinaryReader._utf8Decoder) {
-                hdf5.BinaryReader._utf8Decoder = new TextDecoder('utf-8');
-            }
-            content = hdf5.BinaryReader._utf8Decoder.decode(data);
-        }
-        else {
-            if (!hdf5.BinaryReader._asciiDecoder) {
-                hdf5.BinaryReader._asciiDecoder = new TextDecoder('ascii');
-            }
-            content = hdf5.BinaryReader._asciiDecoder.decode(data);
-        }
-        return content.replace(/\0/g, '');
+        const position = this.take(8);
+        return this._view.getFloat64(position, true);
     }
 
     offset() {
         switch (this._offsetSize) {
             case 8: {
-                const position = this._position + this._offset;
-                this.skip(8);
+                const position = this.take(8);
                 const value = this._view.getUint64(position, true);
                 if (value.low === -1 && value.high === -1) {
                     return undefined;
@@ -457,8 +398,7 @@ hdf5.BinaryReader = class {
     length() {
         switch (this._lengthSize) {
             case 8: {
-                const position = this._position + this._offset;
-                this.skip(8);
+                const position = this.take(8);
                 const value = this._view.getUint64(position, true);
                 if (value.low === -1 && value.high === -1) {
                     return undefined;
@@ -478,41 +418,195 @@ hdf5.BinaryReader = class {
         }
     }
 
-    at(position) {
-        return new hdf5.BinaryReader(this._buffer, this._view, position, 0, this._offsetSize, this._lengthSize);
-    }
-
-    clone() {
-        return new hdf5.BinaryReader(this._buffer, this._view, this._position, this._offset, this._offsetSize, this._lengthSize);
-    }
-
-    align(mod) {
-        if (this._offset % mod != 0) {
-            this._offset = (Math.floor(this._offset / mod) + 1) * mod;
+    string(size, encoding) {
+        if (!size || size == -1) {
+            size = this.size(0x00);
+        }
+        const data = this.read(size);
+        if (encoding == 'utf-8') {
+            hdf5.Reader._utf8Decoder = hdf5.Reader._utf8Decoder || new TextDecoder('utf-8');
+            return hdf5.Reader._utf8Decoder.decode(data).replace(/\0/g, '');
         }
+        hdf5.Reader._asciiDecoder = hdf5.Reader._asciiDecoder = new TextDecoder('ascii');
+        return hdf5.Reader._asciiDecoder.decode(data).replace(/\0/g, '');
     }
 
     match(text) {
-        if (this._position + this._offset + text.length > this._buffer.length) {
+        if (this.position + text.length > this._length) {
             return false;
         }
-        const offset = this._offset;
         const buffer = this.read(text.length);
         for (let i = 0; i < text.length; i++) {
             if (text.charCodeAt(i) != buffer[i]) {
-                this._offset = offset;
+                this.skip(-text.length);
                 return false;
             }
         }
         return true;
     }
+};
+
+hdf5.BinaryReader = class extends hdf5.Reader {
+
+    constructor(buffer, view, offset, position, offsetSize, lengthSize) {
+        super();
+        this._buffer = buffer;
+        this._view = view || new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
+        this._offset = offset || 0;
+        this._position = position || 0;
+        this._offsetSize = offsetSize;
+        this._lengthSize = lengthSize;
+    }
 
     get position() {
         return this._position + this._offset;
     }
 
-    get size() {
-        return this._buffer.length;
+    take(offset) {
+        const position = this._offset + this._position;
+        this.skip(offset);
+        return position;
+    }
+
+    skip(offset) {
+        this._position += offset;
+        if (this._offset + this._position > this._buffer.length) {
+            throw new hdf5.Error('Expected ' + (this._offset + this._position - this._buffer.length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
+        }
+    }
+
+    align(mod) {
+        if (this._position % mod != 0) {
+            this._position = (Math.floor(this._position / mod) + 1) * mod;
+        }
+    }
+
+    read(length) {
+        const position = this.take(length);
+        return this._buffer.subarray(position, position + length);
+    }
+
+    size(terminator) {
+        let position = this._offset + this._position;
+        while (this._buffer[position] !== terminator) {
+            position++;
+        }
+        return position - this._offset - this._position + 1;
+    }
+
+    at(offset) {
+        return new hdf5.BinaryReader(this._buffer, this._view, offset, 0, this._offsetSize, this._lengthSize);
+    }
+
+    clone() {
+        return new hdf5.BinaryReader(this._buffer, this._view, this._offset, this._position, this._offsetSize, this._lengthSize);
+    }
+};
+
+hdf5.StreamReader = class extends hdf5.Reader {
+
+    constructor(stream, view, window, offset, position, offsetSize, lengthSize) {
+        super();
+        this._stream = stream;
+        this._length = stream.length;
+        this._view = view;
+        this._window = window || 0;
+        this._offset = offset || 0;
+        this._position = position || 0;
+        this._offsetSize = offsetSize;
+        this._lengthSize = lengthSize;
+    }
+
+    get position() {
+        return this._offset + this._position;
+    }
+
+    skip(offset) {
+        this._position += offset;
+        if (this._position > this._length) {
+            throw new hdf5.Error('Expected ' + (this._position - this._length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
+        }
+    }
+
+    align(mod) {
+        if (this._position % mod != 0) {
+            this._position = (Math.floor(this._position / mod) + 1) * mod;
+        }
+    }
+
+    read(length) {
+        this._stream.seek(this._offset + this._position);
+        this.skip(length);
+        return this._stream.read(length);
+    }
+
+    byte() {
+        const position = this.take(1);
+        return this._view.getUint8(position);
+    }
+
+    uint16() {
+        const position = this.take(2);
+        return this._view.getUint16(position, true);
+    }
+
+    int32() {
+        const position = this.take(4);
+        return this._view.getInt32(position, true);
+    }
+
+    uint32() {
+        const position = this.take(4);
+        return this._view.getUint32(position, true);
+    }
+
+    int64() {
+        const position = this.take(8);
+        return this._view.getInt64(position, true).toNumber();
+    }
+
+    float32() {
+        const position = this.take(4);
+        return this._view.getFloat32(position, true);
+    }
+
+    float64() {
+        const position = this.take(8);
+        return this._view.getFloat64(position, true);
+    }
+
+    at(offset) {
+        return new hdf5.StreamReader(this._stream, this._view, this._window, offset, 0, this._offsetSize, this._lengthSize);
+    }
+
+    clone() {
+        return new hdf5.StreamReader(this._stream, this._view, this._window, this._offset, this._position, this._offsetSize, this._lengthSize);
+    }
+
+    size(terminator) {
+        const position = this._position;
+        let size = 0;
+        while (this.byte() != terminator) {
+            size++;
+        }
+        this._position = position;
+        return size;
+    }
+
+
+    take(length) {
+        const position = this.position;
+        if (position + length > this._length) {
+            throw new Error('Expected ' + (position + length - this._length) + ' more bytes. The file might be corrupted. Unexpected end of file.');
+        }
+        if (!this._buffer || position < this._window || position + length > this._window + this._buffer.length) {
+            this._window = position;
+            this._stream.seek(this._window);
+            this._buffer = this._stream.read(Math.min(0x1000, this._length - this._window));
+            this._view = new DataView(this._buffer.buffer, this._buffer.byteOffset, this._buffer.byteLength);
+        }
+        this._position += length;
+        return position - this._window;
     }
 };
 
@@ -968,9 +1062,9 @@ hdf5.Datatype = class {
             case 3: // string
                 switch ((this._flags >> 8) & 0x0f) { // character set
                     case 0:
-                        return hdf5.BinaryReader.decode(reader.read(this._size), 'ascii');
+                        return reader.string(this._size, 'ascii');
                     case 1:
-                        return hdf5.BinaryReader.decode(reader.read(this._size), 'utf-8');
+                        return reader.string(this._size, 'utf-8');
                     default:
                         throw new hdf5.Error('Unsupported character encoding.');
                 }
@@ -1004,11 +1098,12 @@ hdf5.Datatype = class {
                 const globalHeapObject = globalHeap.get(data.globalHeapID);
                 if (globalHeapObject != null) {
                     const characterSet = (this._flags >> 8) & 0x0f;
+                    const reader = globalHeapObject.reader();
                     switch (characterSet) {
                         case 0:
-                            return hdf5.BinaryReader.decode(globalHeapObject.data, 'ascii');
+                            return reader.string(reader.length(), 'ascii');
                         case 1:
-                            return hdf5.BinaryReader.decode(globalHeapObject.data, 'utf-8');
+                            return reader.string(reader.length(), 'utf-8');
                         default:
                             throw new hdf5.Error('Unsupported character encoding.');
                     }
@@ -1494,8 +1589,14 @@ hdf5.GlobalHeapObject = class {
     constructor(reader) {
         reader.uint16();
         reader.skip(4);
+        this._position = reader.position;
+        this._reader = reader;
         const length = reader.length();
-        this.data = reader.read(length);
+        reader.skip(length);
+    }
+
+    reader() {
+        return this._reader.at(this._position);
     }
 };