gzip.js 6.1 KB

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