@@ -9,11 +9,45 @@ use thiserror::Error;
99
1010use super :: { PermissionMask , Resource , ResourceType } ;
1111
12+ #[ derive( Clone ) ]
13+ enum ResourceContent {
14+ /// A valid utf8 string. Used for text/* mime types or for ResourceType:Template
15+ Text ( String ) ,
16+
17+ /// Raw content in the form of a byte array. Used for other mime types like
18+ /// "image/gif" or "audio/mp3"
19+ Raw ( Vec < u8 > ) ,
20+ }
21+
22+ impl ResourceContent {
23+ fn text_from_base64 ( base64 : & str ) -> Result < Self , AddResourceError > {
24+ let decoded = BASE64_STANDARD . decode ( base64) ?;
25+ Ok ( Self :: Text ( String :: from_utf8 ( decoded) ?) )
26+ }
27+
28+ fn raw_from_base64 ( base64 : & str ) -> Result < Self , AddResourceError > {
29+ let decoded = BASE64_STANDARD . decode ( base64) ?;
30+ Ok ( Self :: Raw ( decoded) )
31+ }
32+ }
33+
34+ #[ derive( Clone ) ]
35+ /// A internal representation of a Resource to store. Stores the content
36+ /// in the decoded form to use less memory.
37+ /// See [Resource] for details
38+ struct ResourceImpl {
39+ name : String ,
40+ kind : ResourceType ,
41+ content : ResourceContent ,
42+ dependencies : Vec < String > ,
43+ permission : PermissionMask ,
44+ }
45+
1246/// Unified resource storage for both redirects and scriptlets.
1347#[ derive( Default ) ]
1448pub struct ResourceStorage {
1549 /// Stores each resource by its canonical name
16- resources : HashMap < String , Resource > ,
50+ resources : HashMap < String , ResourceImpl > ,
1751 /// Stores mappings from aliases to their canonical resource names
1852 aliases : HashMap < String , String > ,
1953}
@@ -130,16 +164,20 @@ impl ResourceStorage {
130164
131165 /// Adds a resource to storage so that it can be retrieved later.
132166 pub fn add_resource ( & mut self , resource : Resource ) -> Result < ( ) , AddResourceError > {
167+ let resource_content: ResourceContent ;
168+
133169 if let ResourceType :: Mime ( content_type) = & resource. kind {
134170 if !resource. dependencies . is_empty ( ) && !content_type. supports_dependencies ( ) {
135171 return Err ( AddResourceError :: ContentTypeDoesNotSupportDependencies ) ;
136172 }
137173
138- // Ensure the resource contents are valid base64 (and utf8 if applicable)
139- let decoded = BASE64_STANDARD . decode ( & resource. content ) ?;
140174 if content_type. is_textual ( ) {
141- let _ = String :: from_utf8 ( decoded) ?;
175+ resource_content = ResourceContent :: text_from_base64 ( & resource. content ) ?;
176+ } else {
177+ resource_content = ResourceContent :: raw_from_base64 ( & resource. content ) ?;
142178 }
179+ } else {
180+ resource_content = ResourceContent :: text_from_base64 ( & resource. content ) ?;
143181 }
144182
145183 for ident in std:: iter:: once ( & resource. name ) . chain ( resource. aliases . iter ( ) ) {
@@ -151,7 +189,14 @@ impl ResourceStorage {
151189 resource. aliases . iter ( ) . for_each ( |alias| {
152190 self . aliases . insert ( alias. clone ( ) , resource. name . clone ( ) ) ;
153191 } ) ;
154- self . resources . insert ( resource. name . clone ( ) , resource) ;
192+ let resource_impl = ResourceImpl {
193+ name : resource. name . clone ( ) ,
194+ kind : resource. kind ,
195+ content : resource_content,
196+ dependencies : resource. dependencies ,
197+ permission : resource. permission ,
198+ } ;
199+ self . resources . insert ( resource. name , resource_impl) ;
155200
156201 Ok ( ( ) )
157202 }
@@ -176,11 +221,9 @@ impl ResourceStorage {
176221 let mut result = String :: new ( ) ;
177222
178223 for dep in deps. iter ( ) {
179- if let Ok ( decoded) = BASE64_STANDARD . decode ( & dep. content ) {
180- if let Ok ( dep) = core:: str:: from_utf8 ( & decoded) {
181- result += dep;
182- result += "\n " ;
183- }
224+ if let ResourceContent :: Text ( content) = & dep. content {
225+ result += content;
226+ result += "\n " ;
184227 }
185228 }
186229
@@ -197,7 +240,7 @@ impl ResourceStorage {
197240 fn recursive_dependencies < ' a : ' b , ' b > (
198241 & ' a self ,
199242 new_dep : & str ,
200- prev_deps : & mut Vec < & ' b Resource > ,
243+ prev_deps : & mut Vec < & ' b ResourceImpl > ,
201244 filter_permission : PermissionMask ,
202245 ) -> Result < ( ) , ScriptletResourceError > {
203246 if prev_deps. iter ( ) . any ( |dep| dep. name == new_dep) {
@@ -221,7 +264,7 @@ impl ResourceStorage {
221264 & ' a self ,
222265 scriptlet_args : & str ,
223266 filter_permission : PermissionMask ,
224- required_deps : & mut Vec < & ' b Resource > ,
267+ required_deps : & mut Vec < & ' b ResourceImpl > ,
225268 ) -> Result < String , ScriptletResourceError > {
226269 // `unwrap` is safe because these are guaranteed valid at filter parsing.
227270 let scriptlet_args = parse_scriptlet_args ( scriptlet_args) . unwrap ( ) ;
@@ -247,7 +290,12 @@ impl ResourceStorage {
247290 self . recursive_dependencies ( dep, required_deps, filter_permission) ?;
248291 }
249292
250- let template = String :: from_utf8 ( BASE64_STANDARD . decode ( & resource. content ) ?) ?;
293+ let template = match & resource. content {
294+ ResourceContent :: Raw ( _content) => {
295+ return Err ( ScriptletResourceError :: ContentTypeNotInjectable ) ;
296+ }
297+ ResourceContent :: Text ( content) => content. clone ( ) ,
298+ } ;
251299
252300 if let Some ( function_name) = extract_function_name ( & template) {
253301 // newer function-style resource: pass args using function call syntax
@@ -284,15 +332,20 @@ impl ResourceStorage {
284332 return None ;
285333 }
286334 if let ResourceType :: Mime ( mime) = & resource. kind {
287- Some ( format ! ( "data:{};base64,{}" , mime, & resource. content) )
335+ let bytes = match & resource. content {
336+ ResourceContent :: Raw ( content) => content,
337+ ResourceContent :: Text ( content) => content. as_bytes ( ) ,
338+ } ;
339+ let encoded = BASE64_STANDARD . encode ( bytes) ;
340+ Some ( format ! ( "data:{};base64,{}" , mime, encoded) )
288341 } else {
289342 None
290343 }
291344 } )
292345 }
293346
294347 /// Gets the resource associated with `resource_ident`, respecting aliases if necessary.
295- fn get_internal_resource ( & self , resource_ident : & str ) -> Option < & Resource > {
348+ fn get_internal_resource ( & self , resource_ident : & str ) -> Option < & ResourceImpl > {
296349 let resource = if let Some ( resource) = self . resources . get ( resource_ident) {
297350 Some ( resource)
298351 } else if let Some ( canonical_name) = self . aliases . get ( resource_ident) {
@@ -308,7 +361,7 @@ impl ResourceStorage {
308361 & self ,
309362 scriptlet_name : & str ,
310363 filter_permission : PermissionMask ,
311- ) -> Result < & Resource , ScriptletResourceError > {
364+ ) -> Result < & ResourceImpl , ScriptletResourceError > {
312365 let resource = self
313366 . get_internal_resource ( scriptlet_name)
314367 . ok_or ( ScriptletResourceError :: NoMatchingScriptlet ) ?;
0 commit comments