UNPKG

7.46 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.isBinaryFileSync = exports.isBinaryFile = void 0;
4const fs = require("fs");
5const util_1 = require("util");
6const statAsync = (0, util_1.promisify)(fs.stat);
7const openAsync = (0, util_1.promisify)(fs.open);
8const closeAsync = (0, util_1.promisify)(fs.close);
9const MAX_BYTES = 512;
10// A very basic non-exception raising reader. Read bytes and
11// at the end use hasError() to check whether this worked.
12class Reader {
13 fileBuffer;
14 size;
15 offset;
16 error;
17 constructor(fileBuffer, size) {
18 this.fileBuffer = fileBuffer;
19 this.size = size;
20 this.offset = 0;
21 this.error = false;
22 }
23 hasError() {
24 return this.error;
25 }
26 nextByte() {
27 if (this.offset === this.size || this.hasError()) {
28 this.error = true;
29 return 0xff;
30 }
31 return this.fileBuffer[this.offset++];
32 }
33 next(len) {
34 const n = new Array();
35 for (let i = 0; i < len; i++) {
36 n[i] = this.nextByte();
37 }
38 return n;
39 }
40}
41// Read a Google Protobuf var(iable)int from the buffer.
42function readProtoVarInt(reader) {
43 let idx = 0;
44 let varInt = 0;
45 while (!reader.hasError()) {
46 const b = reader.nextByte();
47 varInt = varInt | ((b & 0x7f) << (7 * idx));
48 if ((b & 0x80) === 0) {
49 break;
50 }
51 idx++;
52 }
53 return varInt;
54}
55// Attempt to taste a full Google Protobuf message.
56function readProtoMessage(reader) {
57 const varInt = readProtoVarInt(reader);
58 const wireType = varInt & 0x7;
59 switch (wireType) {
60 case 0:
61 readProtoVarInt(reader);
62 return true;
63 case 1:
64 reader.next(8);
65 return true;
66 case 2:
67 const len = readProtoVarInt(reader);
68 reader.next(len);
69 return true;
70 case 5:
71 reader.next(4);
72 return true;
73 }
74 return false;
75}
76// Check whether this seems to be a valid protobuf file.
77function isBinaryProto(fileBuffer, totalBytes) {
78 const reader = new Reader(fileBuffer, totalBytes);
79 let numMessages = 0;
80 while (true) {
81 // Definitely not a valid protobuf
82 if (!readProtoMessage(reader) && !reader.hasError()) {
83 return false;
84 }
85 // Short read?
86 if (reader.hasError()) {
87 break;
88 }
89 numMessages++;
90 }
91 return numMessages > 0;
92}
93async function isBinaryFile(file, size) {
94 if (isString(file)) {
95 const stat = await statAsync(file);
96 isStatFile(stat);
97 const fileDescriptor = await openAsync(file, 'r');
98 const allocBuffer = Buffer.alloc(MAX_BYTES);
99 // Read the file with no encoding for raw buffer access.
100 // NB: something is severely wrong with promisify, had to construct my own Promise
101 return new Promise((fulfill, reject) => {
102 fs.read(fileDescriptor, allocBuffer, 0, MAX_BYTES, 0, (err, bytesRead, _) => {
103 closeAsync(fileDescriptor);
104 if (err) {
105 reject(err);
106 }
107 else {
108 fulfill(isBinaryCheck(allocBuffer, bytesRead));
109 }
110 });
111 });
112 }
113 else {
114 if (size === undefined) {
115 size = file.length;
116 }
117 return isBinaryCheck(file, size);
118 }
119}
120exports.isBinaryFile = isBinaryFile;
121function isBinaryFileSync(file, size) {
122 if (isString(file)) {
123 const stat = fs.statSync(file);
124 isStatFile(stat);
125 const fileDescriptor = fs.openSync(file, 'r');
126 const allocBuffer = Buffer.alloc(MAX_BYTES);
127 const bytesRead = fs.readSync(fileDescriptor, allocBuffer, 0, MAX_BYTES, 0);
128 fs.closeSync(fileDescriptor);
129 return isBinaryCheck(allocBuffer, bytesRead);
130 }
131 else {
132 if (size === undefined) {
133 size = file.length;
134 }
135 return isBinaryCheck(file, size);
136 }
137}
138exports.isBinaryFileSync = isBinaryFileSync;
139function isBinaryCheck(fileBuffer, bytesRead) {
140 // empty file. no clue what it is.
141 if (bytesRead === 0) {
142 return false;
143 }
144 let suspiciousBytes = 0;
145 const totalBytes = Math.min(bytesRead, MAX_BYTES);
146 // UTF-8 BOM
147 if (bytesRead >= 3 && fileBuffer[0] === 0xef && fileBuffer[1] === 0xbb && fileBuffer[2] === 0xbf) {
148 return false;
149 }
150 // UTF-32 BOM
151 if (bytesRead >= 4 &&
152 fileBuffer[0] === 0x00 &&
153 fileBuffer[1] === 0x00 &&
154 fileBuffer[2] === 0xfe &&
155 fileBuffer[3] === 0xff) {
156 return false;
157 }
158 // UTF-32 LE BOM
159 if (bytesRead >= 4 &&
160 fileBuffer[0] === 0xff &&
161 fileBuffer[1] === 0xfe &&
162 fileBuffer[2] === 0x00 &&
163 fileBuffer[3] === 0x00) {
164 return false;
165 }
166 // GB BOM
167 if (bytesRead >= 4 &&
168 fileBuffer[0] === 0x84 &&
169 fileBuffer[1] === 0x31 &&
170 fileBuffer[2] === 0x95 &&
171 fileBuffer[3] === 0x33) {
172 return false;
173 }
174 if (totalBytes >= 5 && fileBuffer.slice(0, 5).toString() === '%PDF-') {
175 /* PDF. This is binary. */
176 return true;
177 }
178 // UTF-16 BE BOM
179 if (bytesRead >= 2 && fileBuffer[0] === 0xfe && fileBuffer[1] === 0xff) {
180 return false;
181 }
182 // UTF-16 LE BOM
183 if (bytesRead >= 2 && fileBuffer[0] === 0xff && fileBuffer[1] === 0xfe) {
184 return false;
185 }
186 for (let i = 0; i < totalBytes; i++) {
187 if (fileBuffer[i] === 0) {
188 // NULL byte--it's binary!
189 return true;
190 }
191 else if ((fileBuffer[i] < 7 || fileBuffer[i] > 14) && (fileBuffer[i] < 32 || fileBuffer[i] > 127)) {
192 // UTF-8 detection
193 if (fileBuffer[i] >= 0xc0 && fileBuffer[i] <= 0xdf && i + 1 < totalBytes) {
194 i++;
195 if (fileBuffer[i] >= 0x80 && fileBuffer[i] <= 0xbf) {
196 continue;
197 }
198 }
199 else if (fileBuffer[i] >= 0xe0 && fileBuffer[i] <= 0xef && i + 2 < totalBytes) {
200 i++;
201 if (fileBuffer[i] >= 0x80 && fileBuffer[i] <= 0xbf && fileBuffer[i + 1] >= 0x80 && fileBuffer[i + 1] <= 0xbf) {
202 i++;
203 continue;
204 }
205 }
206 else if (fileBuffer[i] >= 0xf0 && fileBuffer[i] <= 0xf7 && i + 3 < totalBytes) {
207 i++;
208 if (fileBuffer[i] >= 0x80 &&
209 fileBuffer[i] <= 0xbf &&
210 fileBuffer[i + 1] >= 0x80 &&
211 fileBuffer[i + 1] <= 0xbf &&
212 fileBuffer[i + 2] >= 0x80 &&
213 fileBuffer[i + 2] <= 0xbf) {
214 i += 2;
215 continue;
216 }
217 }
218 suspiciousBytes++;
219 // Read at least 32 fileBuffer before making a decision
220 if (i >= 32 && (suspiciousBytes * 100) / totalBytes > 10) {
221 return true;
222 }
223 }
224 }
225 if ((suspiciousBytes * 100) / totalBytes > 10) {
226 return true;
227 }
228 if (suspiciousBytes > 1 && isBinaryProto(fileBuffer, totalBytes)) {
229 return true;
230 }
231 return false;
232}
233function isString(x) {
234 return typeof x === 'string';
235}
236function isStatFile(stat) {
237 if (!stat.isFile()) {
238 throw new Error(`Path provided was not a file!`);
239 }
240}