gzip.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. var gzip = gzip || {};
  2. var zip = zip || require('./zip');
  3. gzip.Archive = class {
  4. static open(data) {
  5. const stream = data instanceof Uint8Array ? new gzip.BinaryReader(data) : data;
  6. const signature = [ 0x1f, 0x8b ];
  7. if (stream && stream.length > 18 && stream.peek(2).every((value, index) => value === signature[index])) {
  8. return new gzip.Archive(stream);
  9. }
  10. return null;
  11. }
  12. constructor(stream) {
  13. const position = stream.position;
  14. const entry = new gzip.Entry(stream);
  15. this._entries = new Map([ [ entry.name, entry.stream ] ]);
  16. stream.seek(position);
  17. }
  18. get entries() {
  19. return this._entries;
  20. }
  21. };
  22. gzip.Entry = class {
  23. constructor(stream) {
  24. const signature = [ 0x1f, 0x8b ];
  25. if (stream.position + 2 > stream.length ||
  26. !stream.read(2).every((value, index) => value === signature[index])) {
  27. throw new gzip.Error('Invalid gzip signature.');
  28. }
  29. const string = () => {
  30. let content = '';
  31. while (stream.position < stream.length) {
  32. const value = stream.byte();
  33. if (value === 0x00) {
  34. break;
  35. }
  36. content += String.fromCharCode(value);
  37. }
  38. return content;
  39. };
  40. const reader = new gzip.BinaryReader(stream.read(8));
  41. const compressionMethod = reader.byte();
  42. if (compressionMethod != 8) {
  43. throw new gzip.Error("Invalid compression method '" + compressionMethod.toString() + "'.");
  44. }
  45. const flags = reader.byte();
  46. reader.uint32(); // MTIME
  47. reader.byte(); // XFL
  48. reader.byte(); // OS
  49. if ((flags & 4) != 0) { // FEXTRA
  50. const xlen = stream.byte() | (stream.byte() << 8);
  51. stream.skip(xlen);
  52. }
  53. this._name = (flags & 8) != 0 ? string() : ''; // FNAME
  54. if ((flags & 16) != 0) { // FCOMMENT
  55. string();
  56. }
  57. if ((flags & 1) != 0) { // FHCRC
  58. stream.skip(2);
  59. }
  60. this._stream = new gzip.InflaterStream(stream);
  61. }
  62. get name() {
  63. return this._name;
  64. }
  65. get stream() {
  66. return this._stream;
  67. }
  68. };
  69. gzip.InflaterStream = class {
  70. constructor(stream) {
  71. this._stream = stream.stream(stream.length - stream.position - 8);
  72. const reader = new gzip.BinaryReader(stream.read(8));
  73. reader.uint32(); // CRC32
  74. this._length = reader.uint32(); // ISIZE
  75. this._position = 0;
  76. }
  77. get position() {
  78. return this._position;
  79. }
  80. get length() {
  81. return this._length;
  82. }
  83. seek(position) {
  84. if (this._buffer === undefined) {
  85. this._inflate();
  86. }
  87. this._position = position >= 0 ? position : this._length + position;
  88. }
  89. skip(offset) {
  90. if (this._buffer === undefined) {
  91. this._inflate();
  92. }
  93. this._position += offset;
  94. }
  95. stream(length) {
  96. return new gzip.BinaryReader(this.read(length));
  97. }
  98. peek(length) {
  99. const position = this._position;
  100. length = length !== undefined ? length : this._length - this._position;
  101. this.skip(length);
  102. const end = this._position;
  103. this.seek(position);
  104. if (position === 0 && length === this._length) {
  105. return this._buffer;
  106. }
  107. return this._buffer.subarray(position, end);
  108. }
  109. read(length) {
  110. const position = this._position;
  111. length = length !== undefined ? length : this._length - this._position;
  112. this.skip(length);
  113. if (position === 0 && length === this._length) {
  114. return this._buffer;
  115. }
  116. return this._buffer.subarray(position, this._position);
  117. }
  118. byte() {
  119. const position = this._position;
  120. this.skip(1);
  121. return this._buffer[position];
  122. }
  123. _inflate() {
  124. if (this._buffer === undefined) {
  125. const buffer = this._stream.peek();
  126. this._buffer = new zip.Inflater().inflateRaw(buffer, this._length);
  127. delete this._stream;
  128. }
  129. }
  130. };
  131. gzip.BinaryReader = class {
  132. constructor(buffer) {
  133. this._buffer = buffer;
  134. this._length = buffer.length;
  135. this._position = 0;
  136. this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
  137. }
  138. get position() {
  139. return this._position;
  140. }
  141. get length() {
  142. return this._length;
  143. }
  144. create(buffer) {
  145. return new gzip.BinaryReader(buffer);
  146. }
  147. stream(length) {
  148. return this.create(this.read(length));
  149. }
  150. seek(position) {
  151. this._position = position >= 0 ? position : this._length + position;
  152. }
  153. skip(offset) {
  154. this._position += offset;
  155. }
  156. peek(length) {
  157. if (this._position === 0 && length === undefined) {
  158. return this._buffer;
  159. }
  160. const position = this._position;
  161. this.skip(length !== undefined ? length : this._length - this._position);
  162. const end = this._position;
  163. this.seek(position);
  164. return this._buffer.subarray(position, end);
  165. }
  166. read(length) {
  167. if (this._position === 0 && length === undefined) {
  168. this._position = this._length;
  169. return this._buffer;
  170. }
  171. const position = this._position;
  172. this.skip(length !== undefined ? length : this._length - this._position);
  173. return this._buffer.subarray(position, this._position);
  174. }
  175. byte() {
  176. const position = this._position;
  177. this.skip(1);
  178. return this._buffer[position];
  179. }
  180. uint16() {
  181. const position = this._position;
  182. this.skip(2);
  183. return this._view.getUint16(position, true);
  184. }
  185. uint32() {
  186. const position = this._position;
  187. this.skip(4);
  188. return this._view.getUint32(position, true);
  189. }
  190. };
  191. gzip.Error = class extends Error {
  192. constructor(message) {
  193. super(message);
  194. this.name = 'Gzip Error';
  195. }
  196. };
  197. if (typeof module !== 'undefined' && typeof module.exports === 'object') {
  198. module.exports.Archive = gzip.Archive;
  199. }