-
Notifications
You must be signed in to change notification settings - Fork 10
/
magic.go
109 lines (98 loc) · 2.05 KB
/
magic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package mimemagic
import (
"bytes"
)
var utf16beBOM, utf16leBOM, utf8BOM = []byte{0xfe, 0xff}, []byte{0xff, 0xfe}, []byte{0xef, 0xbb, 0xbf}
type magic struct {
mediaType int
matchers []*magicMatch
}
type magicMatch struct {
start, length int
pattern, mask []byte
next []*magicMatch
}
// MatchMagic determines the MIME type of the file in byte slice
// form. For an io.Reader wrapper see MatchReader (blank filename).
func MatchMagic(data []byte) MediaType {
return mediaTypes[matchMagic(data)]
}
func isTextFile(data []byte) bool {
if len(data) > 128 {
data = data[:128]
}
if bytes.HasPrefix(data, utf16beBOM) || bytes.HasPrefix(data, utf16leBOM) || bytes.HasPrefix(data, utf8BOM) {
return true
}
for _, b := range data {
if b < ' ' && b != '\n' && b != '\r' && b != '\t' {
return false
}
}
return true
}
func matchMagic(data []byte) int {
if len(data) == 0 {
return emptyDocument
}
for _, m := range magicSignatures {
if m.match(data) {
return m.mediaType
}
}
if isTextFile(data) {
return plainText
}
return unknownType
}
func (m *magic) match(data []byte) bool {
for _, mm := range m.matchers {
if mm.match(data) {
return true
}
}
return false
}
func (m *magicMatch) match(data []byte) bool {
if m.search(data) {
if m.next == nil {
return true
}
for _, mm := range m.next {
if mm.match(data) {
return true
}
}
}
return false
}
func (m *magicMatch) search(data []byte) bool {
dataLen := len(data)
patternLen := len(m.pattern)
if dataLen < m.start+patternLen {
return false
}
if m.mask == nil {
if m.length == 0 {
return bytes.Equal(data[m.start:m.start+patternLen], m.pattern)
}
return bytes.Contains(data[m.start:min(m.start+m.length+patternLen, dataLen)], m.pattern)
}
searchLen := min(m.start+m.length, dataLen-patternLen)
outer:
for i := m.start; i <= searchLen; i++ {
for k := 0; k < patternLen; k++ {
if m.pattern[k] != data[i+k]&m.mask[k] {
continue outer
}
}
return true
}
return false
}
func min(i, j int) int {
if i < j {
return i
}
return j
}