1use super::functions::*;
5use super::functions::{
6 BOUNDARY_ENFORCE_WGSL, BROADPHASE_SORT_SHADER, INTEGRATE_WGSL, LBM_BGK_D2Q9_WGSL,
7 LBM_STREAMING_SHADER, RIGID_INTEGRATE_SHADER, SPH_DENSITY_WGSL, SPH_FORCE_WGSL,
8};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone)]
13pub struct SpirVModule {
14 pub entry_points: Vec<String>,
16 pub binding_count: usize,
18 pub workgroup_size: [u32; 3],
20 pub spirv_bytes: Vec<u8>,
22}
23impl SpirVModule {
24 pub fn from_wgsl(source: &str) -> Self {
26 let mut entry_points = Vec::new();
27 for line in source.lines() {
28 let trimmed = line.trim();
29 if let Some(pos) = trimmed.find("fn ") {
30 let rest = &trimmed[pos + 3..];
31 let name: String = rest
32 .chars()
33 .take_while(|c| c.is_alphanumeric() || *c == '_')
34 .collect();
35 if !name.is_empty() {
36 entry_points.push(name);
37 }
38 }
39 }
40 let binding_count = source.matches("@binding(").count();
41 let workgroup_size = parse_workgroup_size(source);
42 let spirv_bytes = mock_compile_to_spirv(
43 source,
44 entry_points.first().map(|s| s.as_str()).unwrap_or("main"),
45 );
46 Self {
47 entry_points,
48 binding_count,
49 workgroup_size,
50 spirv_bytes,
51 }
52 }
53 pub fn has_entry_point(&self, name: &str) -> bool {
55 self.entry_points.iter().any(|e| e == name)
56 }
57 pub fn byte_size(&self) -> usize {
59 self.spirv_bytes.len()
60 }
61}
62#[derive(Debug, Clone, Copy)]
64pub struct PushConstantRange {
65 pub offset: u32,
67 pub size: u32,
69 pub stage: ShaderStage,
71}
72impl PushConstantRange {
73 pub fn new(offset: u32, size: u32, stage: ShaderStage) -> Self {
75 Self {
76 offset,
77 size,
78 stage,
79 }
80 }
81 pub fn fits_standard_limit(&self) -> bool {
83 self.offset + self.size <= 128
84 }
85}
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum AddressMode {
89 ClampToEdge,
91 Repeat,
93 MirrorRepeat,
95}
96#[derive(Debug, Clone)]
98pub struct DescriptorSetLayout {
99 pub group: u32,
101 pub bindings: Vec<DescriptorBinding>,
103}
104impl DescriptorSetLayout {
105 pub fn new(group: u32) -> Self {
107 Self {
108 group,
109 bindings: Vec::new(),
110 }
111 }
112 pub fn add_storage_buffer(&mut self, binding: u32, stage: ShaderStage, read_only: bool) {
114 self.bindings.push(DescriptorBinding {
115 binding,
116 descriptor_type: DescriptorType::StorageBuffer,
117 stage,
118 read_only,
119 });
120 }
121 pub fn add_uniform_buffer(&mut self, binding: u32, stage: ShaderStage) {
123 self.bindings.push(DescriptorBinding {
124 binding,
125 descriptor_type: DescriptorType::UniformBuffer,
126 stage,
127 read_only: true,
128 });
129 }
130 pub fn add_sampler(&mut self, binding: u32, stage: ShaderStage) {
132 self.bindings.push(DescriptorBinding {
133 binding,
134 descriptor_type: DescriptorType::CombinedImageSampler,
135 stage,
136 read_only: true,
137 });
138 }
139 pub fn len(&self) -> usize {
141 self.bindings.len()
142 }
143 pub fn is_empty(&self) -> bool {
145 self.bindings.is_empty()
146 }
147}
148#[derive(Debug, Clone)]
150pub struct StorageBinding {
151 pub name: String,
153 pub binding: u32,
155 pub read_only: bool,
157}
158#[derive(Debug, Default)]
160pub struct ShaderHotReloadManager {
161 pub(super) sources: HashMap<String, String>,
163 pub(super) hashes: HashMap<String, u64>,
165}
166impl ShaderHotReloadManager {
167 pub fn new() -> Self {
169 Self::default()
170 }
171 pub fn watch(&mut self, name: &str, source: &str) {
173 let hash = simple_hash(source);
174 self.sources.insert(name.to_string(), source.to_string());
175 self.hashes.insert(name.to_string(), hash);
176 }
177 pub fn unwatch(&mut self, name: &str) {
179 self.sources.remove(name);
180 self.hashes.remove(name);
181 }
182 pub fn update(&mut self, name: &str, new_source: &str) -> bool {
184 let new_hash = simple_hash(new_source);
185 if let Some(old_hash) = self.hashes.get(name)
186 && *old_hash == new_hash
187 {
188 return false;
189 }
190 self.sources
191 .insert(name.to_string(), new_source.to_string());
192 self.hashes.insert(name.to_string(), new_hash);
193 true
194 }
195 pub fn is_watched(&self, name: &str) -> bool {
197 self.sources.contains_key(name)
198 }
199 pub fn get_source(&self, name: &str) -> Option<&str> {
201 self.sources.get(name).map(|s| s.as_str())
202 }
203 pub fn watched_names(&self) -> Vec<&str> {
205 self.sources.keys().map(|s| s.as_str()).collect()
206 }
207 pub fn len(&self) -> usize {
209 self.sources.len()
210 }
211 pub fn is_empty(&self) -> bool {
213 self.sources.is_empty()
214 }
215}
216#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub enum DescriptorType {
219 UniformBuffer,
221 StorageBuffer,
223 CombinedImageSampler,
225 StorageImage,
227}
228#[derive(Debug, Default)]
230pub struct ShaderMetaRegistry {
231 pub(super) entries: HashMap<String, ShaderMetadata>,
232}
233impl ShaderMetaRegistry {
234 pub fn new() -> Self {
236 Self::default()
237 }
238 pub fn register(&mut self, name: &str, meta: ShaderMetadata) {
240 self.entries.insert(name.to_string(), meta);
241 }
242 pub fn lookup(&self, name: &str) -> Option<&ShaderMetadata> {
244 self.entries.get(name)
245 }
246 pub fn all_names(&self) -> Vec<&str> {
248 self.entries.keys().map(|s| s.as_str()).collect()
249 }
250 pub fn len(&self) -> usize {
252 self.entries.len()
253 }
254 pub fn is_empty(&self) -> bool {
256 self.entries.is_empty()
257 }
258}
259#[derive(Debug, Clone)]
261pub struct SpecializationConstant {
262 pub name: String,
264 pub default_value: String,
266 pub description: String,
268}
269impl SpecializationConstant {
270 pub fn new(name: &str, default_value: &str, description: &str) -> Self {
272 Self {
273 name: name.to_string(),
274 default_value: default_value.to_string(),
275 description: description.to_string(),
276 }
277 }
278}
279#[derive(Debug, Clone)]
284pub struct ShaderTemplate {
285 pub template: String,
287}
288impl ShaderTemplate {
289 pub fn new(template: impl Into<String>) -> Self {
291 Self {
292 template: template.into(),
293 }
294 }
295 pub fn instantiate(&self, params: &HashMap<&str, &str>) -> String {
304 let mut result = self.template.clone();
305 for (key, value) in params {
306 let placeholder = format!("{{{}}}", key);
307 result = result.replace(&placeholder, value);
308 }
309 result
310 }
311 pub fn placeholders(&self) -> Vec<String> {
317 let mut result = Vec::new();
318 let chars: Vec<char> = self.template.chars().collect();
319 let mut i = 0;
320 while i < chars.len() {
321 if chars[i] == '{' {
322 if i + 1 < chars.len() && chars[i + 1].is_ascii_uppercase() {
323 let start = i + 1;
324 let mut end = start;
325 while end < chars.len() && chars[end] != '}' {
326 end += 1;
327 }
328 if end < chars.len() {
329 let name: String = chars[start..end].iter().collect();
330 let is_valid = name
331 .chars()
332 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_');
333 if is_valid && !result.contains(&name) {
334 result.push(name);
335 }
336 i = end + 1;
337 } else {
338 i += 1;
339 }
340 } else {
341 i += 1;
342 }
343 } else {
344 i += 1;
345 }
346 }
347 result
348 }
349 pub fn all_placeholders_provided(&self, params: &HashMap<&str, &str>) -> bool {
351 for p in self.placeholders() {
352 if !params.contains_key(p.as_str()) {
353 return false;
354 }
355 }
356 true
357 }
358}
359#[derive(Debug, Clone, Copy, PartialEq, Eq)]
361pub enum TextureFormat {
362 Rgba8Unorm,
364 Rgba8Srgb,
366 Rgba16Float,
368 R32Float,
370 Depth32Float,
372 Depth24PlusStencil8,
374}
375#[derive(Debug, Clone, Default)]
377pub struct BindGroupLayout {
378 pub(super) uniforms: Vec<UniformBinding>,
379 pub(super) storages: Vec<StorageBinding>,
380}
381impl BindGroupLayout {
382 pub fn new() -> Self {
384 Self::default()
385 }
386 pub fn add_uniform(&mut self, name: &str, _group: u32, binding: u32, size_bytes: u32) {
388 self.uniforms.push(UniformBinding {
389 name: name.to_string(),
390 binding,
391 size_bytes,
392 });
393 }
394 pub fn add_storage(&mut self, name: &str, _group: u32, binding: u32, read_only: bool) {
396 self.storages.push(StorageBinding {
397 name: name.to_string(),
398 binding,
399 read_only,
400 });
401 }
402 pub fn binding_count(&self) -> usize {
404 self.uniforms.len() + self.storages.len()
405 }
406 pub fn is_empty(&self) -> bool {
408 self.uniforms.is_empty() && self.storages.is_empty()
409 }
410 pub fn to_wgsl_snippet(&self) -> String {
412 let mut out = String::new();
413 for u in &self.uniforms {
414 out.push_str(&format!(
415 "@group(0) @binding({}) var<uniform> {}: {};\n",
416 u.binding,
417 u.name.to_lowercase(),
418 u.name
419 ));
420 }
421 for s in &self.storages {
422 let access = if s.read_only { "read" } else { "read_write" };
423 out.push_str(&format!(
424 "@group(0) @binding({}) var<storage, {}> {}: array<f32>;\n",
425 s.binding,
426 access,
427 s.name.to_lowercase()
428 ));
429 }
430 out
431 }
432}
433#[derive(Debug, Clone)]
435pub struct DepthAttachmentDesc {
436 pub format: TextureFormat,
438 pub load_op: LoadOp,
440 pub store_op: StoreOp,
442 pub clear_depth: f32,
444}
445pub struct ShaderCompilationPipeline {
449 pub(super) includes: HashMap<String, String>,
450 pub(super) cache: ShaderCache,
451}
452impl ShaderCompilationPipeline {
453 pub fn new() -> Self {
455 Self {
456 includes: HashMap::new(),
457 cache: ShaderCache::new(),
458 }
459 }
460 pub fn add_include(&mut self, name: &str, source: &str) {
462 self.includes.insert(name.to_string(), source.to_string());
463 }
464 pub fn compile(
468 &mut self,
469 name: &str,
470 source: &str,
471 spec_map: Option<&SpecializationMap>,
472 ) -> Result<String, String> {
473 if let Some(cached) = self.cache.entries.get(name) {
474 return Ok(cached.clone());
475 }
476 let includes_ref: HashMap<&str, &str> = self
477 .includes
478 .iter()
479 .map(|(k, v)| (k.as_str(), v.as_str()))
480 .collect();
481 let resolved = resolve_includes(source, &includes_ref);
482 let specialized = if let Some(sm) = spec_map {
483 sm.apply(&resolved)
484 } else {
485 resolved
486 };
487 if !validate_wgsl_structure(&specialized) {
488 return Err(format!("shader '{}' failed structural validation", name));
489 }
490 self.cache
491 .entries
492 .insert(name.to_string(), specialized.clone());
493 Ok(specialized)
494 }
495 pub fn cache_size(&self) -> usize {
497 self.cache.len()
498 }
499 pub fn clear_cache(&mut self) {
501 self.cache.clear();
502 }
503}
504#[derive(Debug, Default)]
506pub struct ShaderRegistry {
507 pub(super) shaders: HashMap<String, ComputeShaderDesc>,
508}
509impl ShaderRegistry {
510 pub fn new() -> Self {
512 Self::default()
513 }
514 pub fn register(&mut self, name: impl Into<String>, desc: ComputeShaderDesc) {
516 self.shaders.insert(name.into(), desc);
517 }
518 pub fn get(&self, name: &str) -> Option<&ComputeShaderDesc> {
520 self.shaders.get(name)
521 }
522 pub fn len(&self) -> usize {
524 self.shaders.len()
525 }
526 pub fn is_empty(&self) -> bool {
528 self.shaders.is_empty()
529 }
530 pub fn names(&self) -> impl Iterator<Item = &str> {
532 self.shaders.keys().map(|s| s.as_str())
533 }
534 pub fn unregister(&mut self, name: &str) -> Option<ComputeShaderDesc> {
536 self.shaders.remove(name)
537 }
538 pub fn contains(&self, name: &str) -> bool {
540 self.shaders.contains_key(name)
541 }
542 pub fn with_builtins() -> Self {
544 let mut reg = Self::new();
545 reg.register(
546 "sph_density",
547 ComputeShaderDesc::new("main", [64, 1, 1], SPH_DENSITY_WGSL),
548 );
549 reg.register(
550 "sph_force",
551 ComputeShaderDesc::new("main", [64, 1, 1], SPH_FORCE_WGSL),
552 );
553 reg.register(
554 "integrate",
555 ComputeShaderDesc::new("main", [64, 1, 1], INTEGRATE_WGSL),
556 );
557 reg.register(
558 "lbm_bgk_d2q9",
559 ComputeShaderDesc::new("main", [64, 1, 1], LBM_BGK_D2Q9_WGSL),
560 );
561 reg.register(
562 "lbm_streaming",
563 ComputeShaderDesc::new("main", [64, 1, 1], LBM_STREAMING_SHADER),
564 );
565 reg.register(
566 "rigid_integrate",
567 ComputeShaderDesc::new("main", [64, 1, 1], RIGID_INTEGRATE_SHADER),
568 );
569 reg.register(
570 "broadphase_sort",
571 ComputeShaderDesc::new("main", [64, 1, 1], BROADPHASE_SORT_SHADER),
572 );
573 reg.register(
574 "boundary_enforce",
575 ComputeShaderDesc::new("main", [64, 1, 1], BOUNDARY_ENFORCE_WGSL),
576 );
577 reg
578 }
579}
580#[derive(Debug, Clone)]
582pub struct ComputeShaderDesc {
583 pub entry_point: String,
585 pub workgroup_size: [u32; 3],
587 pub source: String,
589}
590impl ComputeShaderDesc {
591 pub fn new(
593 entry_point: impl Into<String>,
594 workgroup_size: [u32; 3],
595 source: impl Into<String>,
596 ) -> Self {
597 Self {
598 entry_point: entry_point.into(),
599 workgroup_size,
600 source: source.into(),
601 }
602 }
603 pub fn threads_per_workgroup(&self) -> u32 {
605 self.workgroup_size[0] * self.workgroup_size[1] * self.workgroup_size[2]
606 }
607 pub fn binding_count(&self) -> usize {
609 self.source.matches("@binding(").count()
610 }
611}
612#[derive(Debug, Clone)]
614pub struct SamplerDesc {
615 pub filter_min: FilterMode,
617 pub filter_mag: FilterMode,
619 pub address_mode: AddressMode,
621 pub anisotropy: u32,
623 pub lod_bias: f32,
625 pub lod_max: f32,
627}
628impl SamplerDesc {
629 pub fn linear() -> Self {
631 Self {
632 filter_min: FilterMode::Linear,
633 filter_mag: FilterMode::Linear,
634 address_mode: AddressMode::ClampToEdge,
635 anisotropy: 1,
636 lod_bias: 0.0,
637 lod_max: 16.0,
638 }
639 }
640 pub fn nearest() -> Self {
642 Self {
643 filter_min: FilterMode::Nearest,
644 filter_mag: FilterMode::Nearest,
645 address_mode: AddressMode::ClampToEdge,
646 anisotropy: 1,
647 lod_bias: 0.0,
648 lod_max: 0.0,
649 }
650 }
651 pub fn anisotropic(max_anisotropy: u32) -> Self {
653 Self {
654 filter_min: FilterMode::Linear,
655 filter_mag: FilterMode::Linear,
656 address_mode: AddressMode::Repeat,
657 anisotropy: max_anisotropy,
658 lod_bias: 0.0,
659 lod_max: 16.0,
660 }
661 }
662}
663#[derive(Debug, Clone)]
665pub struct RenderPassDesc {
666 pub color_attachments: Vec<ColorAttachmentDesc>,
668 pub depth_attachment: Option<DepthAttachmentDesc>,
670 pub name: String,
672}
673impl RenderPassDesc {
674 pub fn new_simple_color() -> Self {
676 Self {
677 color_attachments: vec![ColorAttachmentDesc {
678 format: TextureFormat::Rgba8Unorm,
679 load_op: LoadOp::Clear,
680 store_op: StoreOp::Store,
681 clear_color: [0.0, 0.0, 0.0, 1.0],
682 }],
683 depth_attachment: None,
684 name: "SimpleColor".to_string(),
685 }
686 }
687 pub fn new_with_depth() -> Self {
689 Self {
690 color_attachments: vec![ColorAttachmentDesc {
691 format: TextureFormat::Rgba16Float,
692 load_op: LoadOp::Clear,
693 store_op: StoreOp::Store,
694 clear_color: [0.0, 0.0, 0.0, 1.0],
695 }],
696 depth_attachment: Some(DepthAttachmentDesc {
697 format: TextureFormat::Depth32Float,
698 load_op: LoadOp::Clear,
699 store_op: StoreOp::Store,
700 clear_depth: 1.0,
701 }),
702 name: "ColorDepth".to_string(),
703 }
704 }
705 pub fn total_attachment_count(&self) -> usize {
707 self.color_attachments.len()
708 + if self.depth_attachment.is_some() {
709 1
710 } else {
711 0
712 }
713 }
714}
715#[derive(Debug, Clone, PartialEq, Eq, Hash)]
717pub enum ShaderVariant {
718 Physics,
720 Collision,
722 Sph,
724 Lbm,
726 RigidBody,
728 NeuralInference,
730}
731#[derive(Debug, Clone)]
733pub struct ShaderMetadata {
734 pub variant: ShaderVariant,
736 pub entry_point: String,
738 pub workgroup_size: [u32; 3],
740 pub bind_group_count: u32,
742}
743impl ShaderMetadata {
744 pub fn new(
746 variant: ShaderVariant,
747 entry_point: impl Into<String>,
748 workgroup_size: [u32; 3],
749 bind_group_count: u32,
750 ) -> Self {
751 Self {
752 variant,
753 entry_point: entry_point.into(),
754 workgroup_size,
755 bind_group_count,
756 }
757 }
758 pub fn threads_per_workgroup(&self) -> u32 {
760 self.workgroup_size[0] * self.workgroup_size[1] * self.workgroup_size[2]
761 }
762}
763#[derive(Debug, Default)]
767pub struct ShaderCache {
768 pub(super) entries: HashMap<String, String>,
769}
770impl ShaderCache {
771 pub fn new() -> Self {
773 Self::default()
774 }
775 pub fn get_or_insert(&mut self, key: &str, compute: impl FnOnce() -> String) -> &str {
777 self.entries.entry(key.to_string()).or_insert_with(compute)
778 }
779 pub fn contains(&self, key: &str) -> bool {
781 self.entries.contains_key(key)
782 }
783 pub fn len(&self) -> usize {
785 self.entries.len()
786 }
787 pub fn is_empty(&self) -> bool {
789 self.entries.is_empty()
790 }
791 pub fn clear(&mut self) {
793 self.entries.clear();
794 }
795 pub fn remove(&mut self, key: &str) -> Option<String> {
797 self.entries.remove(key)
798 }
799}
800#[derive(Debug, Clone, Copy, PartialEq, Eq)]
802pub enum ShaderStage {
803 Vertex,
805 Fragment,
807 Compute,
809 All,
811}
812#[derive(Debug, Clone)]
818pub struct ShaderTemplateV2 {
819 pub source: String,
821 pub defines: HashMap<String, String>,
823}
824impl ShaderTemplateV2 {
825 pub fn new(source: impl Into<String>, defines: HashMap<String, String>) -> Self {
827 Self {
828 source: source.into(),
829 defines,
830 }
831 }
832 pub fn instantiate(&self) -> String {
837 let mut result = self.source.clone();
838 for (key, value) in &self.defines {
839 result = result.replace(key.as_str(), value.as_str());
840 }
841 result
842 }
843}
844#[derive(Debug, Clone)]
846pub struct ColorAttachmentDesc {
847 pub format: TextureFormat,
849 pub load_op: LoadOp,
851 pub store_op: StoreOp,
853 pub clear_color: [f32; 4],
855}
856#[derive(Debug, Clone)]
858pub struct UniformBinding {
859 pub name: String,
861 pub binding: u32,
863 pub size_bytes: u32,
865}
866#[derive(Debug, Clone, Copy, PartialEq, Eq)]
868pub enum StoreOp {
869 Store,
871 Discard,
873}
874#[derive(Debug, Clone)]
876pub struct DescriptorBinding {
877 pub binding: u32,
879 pub descriptor_type: DescriptorType,
881 pub stage: ShaderStage,
883 pub read_only: bool,
885}
886#[derive(Debug, Clone, Copy, PartialEq, Eq)]
888pub enum FilterMode {
889 Nearest,
891 Linear,
893}
894#[derive(Debug, Clone, Copy, PartialEq, Eq)]
896pub enum LoadOp {
897 Load,
899 Clear,
901 DontCare,
903}
904#[derive(Debug, Clone, Default)]
906pub struct SpecializationMap {
907 pub(super) constants: Vec<SpecializationConstant>,
908 pub(super) overrides: HashMap<String, String>,
909}
910impl SpecializationMap {
911 pub fn new() -> Self {
913 Self::default()
914 }
915 pub fn define(&mut self, name: &str, default_value: &str, description: &str) {
917 self.constants.push(SpecializationConstant::new(
918 name,
919 default_value,
920 description,
921 ));
922 }
923 pub fn set(&mut self, name: &str, value: &str) {
925 self.overrides.insert(name.to_string(), value.to_string());
926 }
927 pub fn get(&self, name: &str) -> Option<&str> {
929 if let Some(v) = self.overrides.get(name) {
930 return Some(v.as_str());
931 }
932 for c in &self.constants {
933 if c.name == name {
934 return Some(c.default_value.as_str());
935 }
936 }
937 None
938 }
939 pub fn len(&self) -> usize {
941 self.constants.len()
942 }
943 pub fn is_empty(&self) -> bool {
945 self.constants.is_empty()
946 }
947 pub fn apply(&self, source: &str) -> String {
950 let mut result = source.to_string();
951 for c in &self.constants {
952 let value = self
953 .overrides
954 .get(&c.name)
955 .map(|s| s.as_str())
956 .unwrap_or(&c.default_value);
957 let old = format!("const {} = {};", c.name, c.default_value);
958 let new = format!("const {} = {};", c.name, value);
959 result = result.replace(&old, &new);
960 }
961 result
962 }
963}
964#[derive(Debug, Clone)]
966pub struct UniformBufferDesc {
967 pub name: String,
969 pub group: u32,
971 pub binding: u32,
973 pub size_bytes: u32,
975}
976impl UniformBufferDesc {
977 pub fn new(name: &str, group: u32, binding: u32, size_bytes: u32) -> Self {
979 Self {
980 name: name.to_string(),
981 group,
982 binding,
983 size_bytes,
984 }
985 }
986 pub fn wgsl_annotation(&self) -> String {
988 format!(
989 "@group({}) @binding({}) var<uniform> {}: {};",
990 self.group,
991 self.binding,
992 self.name.to_lowercase(),
993 self.name
994 )
995 }
996}
997#[derive(Debug)]
1002pub struct BytecodeShaderCache {
1003 pub cache: HashMap<String, Vec<u8>>,
1005 pub(super) insertion_order: Vec<String>,
1007 pub max_size: usize,
1009}
1010impl BytecodeShaderCache {
1011 pub fn new(max_size: usize) -> Self {
1013 Self {
1014 cache: HashMap::new(),
1015 insertion_order: Vec::new(),
1016 max_size,
1017 }
1018 }
1019 pub fn insert(&mut self, name: &str, bytecode: Vec<u8>) {
1024 if self.cache.contains_key(name) {
1025 self.insertion_order.retain(|k| k != name);
1026 }
1027 self.cache.insert(name.to_string(), bytecode);
1028 self.insertion_order.push(name.to_string());
1029 while self.total_bytes() > self.max_size && !self.insertion_order.is_empty() {
1030 self.evict_oldest();
1031 }
1032 }
1033 pub fn get(&self, name: &str) -> Option<&Vec<u8>> {
1035 self.cache.get(name)
1036 }
1037 pub fn evict_oldest(&mut self) {
1039 if let Some(oldest) = self.insertion_order.first().cloned() {
1040 self.insertion_order.remove(0);
1041 self.cache.remove(&oldest);
1042 }
1043 }
1044 pub fn total_bytes(&self) -> usize {
1046 self.cache.values().map(|v| v.len()).sum()
1047 }
1048 pub fn len(&self) -> usize {
1050 self.cache.len()
1051 }
1052 pub fn is_empty(&self) -> bool {
1054 self.cache.is_empty()
1055 }
1056}