| 1 | package sftpfs |
| 2 | |
| 3 | import "unicode/utf8" |
| 4 | |
| 5 | // Kind classifies file content for preview purposes. |
| 6 | type Kind int |
| 7 | |
| 8 | const ( |
| 9 | // KindText is UTF-8 (or ASCII) text safe to render and edit. |
| 10 | KindText Kind = iota |
| 11 | // KindBinary contains NUL bytes or invalid UTF-8; not editable as text. |
| 12 | KindBinary |
| 13 | ) |
| 14 | |
| 15 | const ( |
| 16 | // DefaultReadCap bounds a text preview to keep memory and transfer sane. |
| 17 | DefaultReadCap = 4 << 20 // 4 MiB |
| 18 | // sniffLen is how many leading bytes DetectKind inspects. |
| 19 | sniffLen = 8 << 10 // 8 KiB |
| 20 | ) |
| 21 | |
| 22 | // DetectKind classifies a leading sample of file content. A NUL byte marks |
| 23 | // binary immediately; otherwise the sample must be valid UTF-8 (allowing a |
| 24 | // trailing rune truncated by the sample boundary). |
| 25 | func DetectKind(sample []byte) Kind { |
| 26 | if len(sample) > sniffLen { |
| 27 | sample = sample[:sniffLen] |
| 28 | } |
| 29 | for _, b := range sample { |
| 30 | if b == 0 { |
| 31 | return KindBinary |
| 32 | } |
| 33 | } |
| 34 | if utf8.Valid(sample) { |
| 35 | return KindText |
| 36 | } |
| 37 | // The sample may have split a multi-byte rune at the tail; retry without |
| 38 | // the trailing partial rune before declaring binary. |
| 39 | for i := 0; i < utf8.UTFMax-1 && i < len(sample); i++ { |
| 40 | if utf8.Valid(sample[:len(sample)-1-i]) { |
| 41 | return KindText |
| 42 | } |
| 43 | } |
| 44 | return KindBinary |
| 45 | } |
| 46 |