@@ -12,24 +12,48 @@ import (
12
12
)
13
13
14
14
var (
15
- mimeLock sync.RWMutex // guards following 3 maps
16
- mimeTypes map [string ]string // ".Z" => "application/x-compress"
17
- mimeTypesLower map [string ]string // ".z" => "application/x-compress"
15
+ mimeTypes sync.Map // map[string]string; ".Z" => "application/x-compress"
16
+ mimeTypesLower sync.Map // map[string]string; ".z" => "application/x-compress"
18
17
19
18
// extensions maps from MIME type to list of lowercase file
20
19
// extensions: "image/jpeg" => [".jpg", ".jpeg"]
21
- extensions map [string ][]string
20
+ extensionsMu sync.Mutex // Guards stores (but not loads) on extensions.
21
+ extensions sync.Map // map[string][]string; slice values are append-only.
22
22
)
23
23
24
+ func clearSyncMap (m * sync.Map ) {
25
+ m .Range (func (k , _ interface {}) bool {
26
+ m .Delete (k )
27
+ return true
28
+ })
29
+ }
30
+
24
31
// setMimeTypes is used by initMime's non-test path, and by tests.
25
- // The two maps must not be the same, or nil.
26
32
func setMimeTypes (lowerExt , mixExt map [string ]string ) {
27
- if lowerExt == nil || mixExt == nil {
28
- panic ("nil map" )
33
+ clearSyncMap (& mimeTypes )
34
+ clearSyncMap (& mimeTypesLower )
35
+ clearSyncMap (& extensions )
36
+
37
+ for k , v := range lowerExt {
38
+ mimeTypesLower .Store (k , v )
39
+ }
40
+ for k , v := range mixExt {
41
+ mimeTypes .Store (k , v )
42
+ }
43
+
44
+ extensionsMu .Lock ()
45
+ defer extensionsMu .Unlock ()
46
+ for k , v := range lowerExt {
47
+ justType , _ , err := ParseMediaType (v )
48
+ if err != nil {
49
+ panic (err )
50
+ }
51
+ var exts []string
52
+ if ei , ok := extensions .Load (k ); ok {
53
+ exts = ei .([]string )
54
+ }
55
+ extensions .Store (justType , append (exts , k ))
29
56
}
30
- mimeTypesLower = lowerExt
31
- mimeTypes = mixExt
32
- extensions = invert (lowerExt )
33
57
}
34
58
35
59
var builtinTypesLower = map [string ]string {
@@ -45,29 +69,6 @@ var builtinTypesLower = map[string]string{
45
69
".xml" : "text/xml; charset=utf-8" ,
46
70
}
47
71
48
- func clone (m map [string ]string ) map [string ]string {
49
- m2 := make (map [string ]string , len (m ))
50
- for k , v := range m {
51
- m2 [k ] = v
52
- if strings .ToLower (k ) != k {
53
- panic ("keys in builtinTypesLower must be lowercase" )
54
- }
55
- }
56
- return m2
57
- }
58
-
59
- func invert (m map [string ]string ) map [string ][]string {
60
- m2 := make (map [string ][]string , len (m ))
61
- for k , v := range m {
62
- justType , _ , err := ParseMediaType (v )
63
- if err != nil {
64
- panic (err )
65
- }
66
- m2 [justType ] = append (m2 [justType ], k )
67
- }
68
- return m2
69
- }
70
-
71
72
var once sync.Once // guards initMime
72
73
73
74
var testInitMime , osInitMime func ()
@@ -76,7 +77,7 @@ func initMime() {
76
77
if fn := testInitMime ; fn != nil {
77
78
fn ()
78
79
} else {
79
- setMimeTypes (builtinTypesLower , clone ( builtinTypesLower ) )
80
+ setMimeTypes (builtinTypesLower , builtinTypesLower )
80
81
osInitMime ()
81
82
}
82
83
}
@@ -100,12 +101,10 @@ func initMime() {
100
101
// Text types have the charset parameter set to "utf-8" by default.
101
102
func TypeByExtension (ext string ) string {
102
103
once .Do (initMime )
103
- mimeLock .RLock ()
104
- defer mimeLock .RUnlock ()
105
104
106
105
// Case-sensitive lookup.
107
- if v := mimeTypes [ ext ]; v != "" {
108
- return v
106
+ if v , ok := mimeTypes . Load ( ext ); ok {
107
+ return v .( string )
109
108
}
110
109
111
110
// Case-insensitive lookup.
@@ -118,17 +117,19 @@ func TypeByExtension(ext string) string {
118
117
c := ext [i ]
119
118
if c >= utf8RuneSelf {
120
119
// Slow path.
121
- return mimeTypesLower [strings .ToLower (ext )]
120
+ si , _ := mimeTypesLower .Load (strings .ToLower (ext ))
121
+ s , _ := si .(string )
122
+ return s
122
123
}
123
124
if 'A' <= c && c <= 'Z' {
124
125
lower = append (lower , c + ('a' - 'A' ))
125
126
} else {
126
127
lower = append (lower , c )
127
128
}
128
129
}
129
- // The conversion from []byte to string doesn't allocate in
130
- // a map lookup.
131
- return mimeTypesLower [ string ( lower )]
130
+ si , _ := mimeTypesLower . Load ( string ( lower ))
131
+ s , _ := si .( string )
132
+ return s
132
133
}
133
134
134
135
// ExtensionsByType returns the extensions known to be associated with the MIME
@@ -142,13 +143,11 @@ func ExtensionsByType(typ string) ([]string, error) {
142
143
}
143
144
144
145
once .Do (initMime )
145
- mimeLock .RLock ()
146
- defer mimeLock .RUnlock ()
147
- s , ok := extensions [justType ]
146
+ s , ok := extensions .Load (justType )
148
147
if ! ok {
149
148
return nil , nil
150
149
}
151
- return append ([]string {}, s ... ), nil
150
+ return append ([]string {}, s .([] string ) . .. ), nil
152
151
}
153
152
154
153
// AddExtensionType sets the MIME type associated with
@@ -173,15 +172,20 @@ func setExtensionType(extension, mimeType string) error {
173
172
}
174
173
extLower := strings .ToLower (extension )
175
174
176
- mimeLock .Lock ()
177
- defer mimeLock .Unlock ()
178
- mimeTypes [extension ] = mimeType
179
- mimeTypesLower [extLower ] = mimeType
180
- for _ , v := range extensions [justType ] {
175
+ mimeTypes .Store (extension , mimeType )
176
+ mimeTypesLower .Store (extLower , mimeType )
177
+
178
+ extensionsMu .Lock ()
179
+ defer extensionsMu .Unlock ()
180
+ var exts []string
181
+ if ei , ok := extensions .Load (justType ); ok {
182
+ exts = ei .([]string )
183
+ }
184
+ for _ , v := range exts {
181
185
if v == extLower {
182
186
return nil
183
187
}
184
188
}
185
- extensions [ justType ] = append (extensions [ justType ] , extLower )
189
+ extensions . Store ( justType , append (exts , extLower ) )
186
190
return nil
187
191
}
0 commit comments