(buf: Buffer, sampleSize = 8000)
| 36 | } |
| 37 | |
| 38 | export function isBinaryBuffer(buf: Buffer, sampleSize = 8000): boolean { |
| 39 | if (buf.length === 0) return false; |
| 40 | |
| 41 | // Fast path: known binary signatures that the byte heuristics would miss. |
| 42 | if (hasBinaryMagic(buf)) return true; |
| 43 | |
| 44 | const sample = buf.subarray(0, Math.min(sampleSize, buf.length)); |
| 45 | |
| 46 | // Heuristic 1: any null byte in the first sample → binary |
| 47 | // (Valid utf-8 text files do not contain null bytes.) |
| 48 | for (let i = 0; i < sample.length; i++) { |
| 49 | if (sample[i] === 0x00) return true; |
| 50 | } |
| 51 | |
| 52 | // Heuristic 2: high proportion of control / non-printable bytes |
| 53 | let nonPrintable = 0; |
| 54 | for (let i = 0; i < sample.length; i++) { |
| 55 | const b = sample[i]!; |
| 56 | // Tab(9), LF(10), CR(13) are printable; everything 0..31 except those is suspect. |
| 57 | // 0x7F (DEL) is also suspect. 0x80+ is fine (utf-8 multi-byte). |
| 58 | if ((b < 0x09) || (b > 0x0d && b < 0x20) || b === 0x7f) { |
| 59 | nonPrintable++; |
| 60 | } |
| 61 | } |
| 62 | return nonPrintable / sample.length > 0.3; |
| 63 | } |
| 64 | |
| 65 | /** Quick check by extension for common binary file types. */ |
| 66 | const BINARY_EXTENSIONS = new Set([ |
no test coverage detected