1#[allow(unused_imports)]
6use super::functions_2::*;
7use std::collections::HashMap;
8
9#[allow(unused_imports)]
10use super::functions::*;
11use super::functions::{
12 BOUNDARY_ENFORCE_WGSL, BROADPHASE_SORT_SHADER, INTEGRATE_WGSL, LBM_BGK_D2Q9_WGSL,
13 LBM_STREAMING_SHADER, RIGID_INTEGRATE_SHADER, SPH_DENSITY_WGSL, SPH_FORCE_WGSL,
14};
15
16#[derive(Debug, Clone)]
18pub struct SpirVModule {
19 pub entry_points: Vec<String>,
21 pub binding_count: usize,
23 pub workgroup_size: [u32; 3],
25 pub spirv_bytes: Vec<u8>,
27}
28impl SpirVModule {
29 pub fn from_wgsl(source: &str) -> Self {
31 let mut entry_points = Vec::new();
32 for line in source.lines() {
33 let trimmed = line.trim();
34 if let Some(pos) = trimmed.find("fn ") {
35 let rest = &trimmed[pos + 3..];
36 let name: String = rest
37 .chars()
38 .take_while(|c| c.is_alphanumeric() || *c == '_')
39 .collect();
40 if !name.is_empty() {
41 entry_points.push(name);
42 }
43 }
44 }
45 let binding_count = source.matches("@binding(").count();
46 let workgroup_size = parse_workgroup_size(source);
47 let spirv_bytes = mock_compile_to_spirv(
48 source,
49 entry_points.first().map(|s| s.as_str()).unwrap_or("main"),
50 );
51 Self {
52 entry_points,
53 binding_count,
54 workgroup_size,
55 spirv_bytes,
56 }
57 }
58 pub fn has_entry_point(&self, name: &str) -> bool {
60 self.entry_points.iter().any(|e| e == name)
61 }
62 pub fn byte_size(&self) -> usize {
64 self.spirv_bytes.len()
65 }
66}
67#[derive(Debug, Clone, Copy)]
69pub struct PushConstantRange {
70 pub offset: u32,
72 pub size: u32,
74 pub stage: ShaderStage,
76}
77impl PushConstantRange {
78 pub fn new(offset: u32, size: u32, stage: ShaderStage) -> Self {
80 Self {
81 offset,
82 size,
83 stage,
84 }
85 }
86 pub fn fits_standard_limit(&self) -> bool {
88 self.offset + self.size <= 128
89 }
90}
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum AddressMode {
94 ClampToEdge,
96 Repeat,
98 MirrorRepeat,
100}
101#[derive(Debug, Clone)]
103pub struct DescriptorSetLayout {
104 pub group: u32,
106 pub bindings: Vec<DescriptorBinding>,
108}
109impl DescriptorSetLayout {
110 pub fn new(group: u32) -> Self {
112 Self {
113 group,
114 bindings: Vec::new(),
115 }
116 }
117 pub fn add_storage_buffer(&mut self, binding: u32, stage: ShaderStage, read_only: bool) {
119 self.bindings.push(DescriptorBinding {
120 binding,
121 descriptor_type: DescriptorType::StorageBuffer,
122 stage,
123 read_only,
124 });
125 }
126 pub fn add_uniform_buffer(&mut self, binding: u32, stage: ShaderStage) {
128 self.bindings.push(DescriptorBinding {
129 binding,
130 descriptor_type: DescriptorType::UniformBuffer,
131 stage,
132 read_only: true,
133 });
134 }
135 pub fn add_sampler(&mut self, binding: u32, stage: ShaderStage) {
137 self.bindings.push(DescriptorBinding {
138 binding,
139 descriptor_type: DescriptorType::CombinedImageSampler,
140 stage,
141 read_only: true,
142 });
143 }
144 pub fn len(&self) -> usize {
146 self.bindings.len()
147 }
148 pub fn is_empty(&self) -> bool {
150 self.bindings.is_empty()
151 }
152}
153#[derive(Debug, Clone)]
155pub struct StorageBinding {
156 pub name: String,
158 pub binding: u32,
160 pub read_only: bool,
162}
163#[derive(Debug, Default)]
165pub struct ShaderHotReloadManager {
166 pub(super) sources: HashMap<String, String>,
168 pub(super) hashes: HashMap<String, u64>,
170}
171impl ShaderHotReloadManager {
172 pub fn new() -> Self {
174 Self::default()
175 }
176 pub fn watch(&mut self, name: &str, source: &str) {
178 let hash = simple_hash(source);
179 self.sources.insert(name.to_string(), source.to_string());
180 self.hashes.insert(name.to_string(), hash);
181 }
182 pub fn unwatch(&mut self, name: &str) {
184 self.sources.remove(name);
185 self.hashes.remove(name);
186 }
187 pub fn update(&mut self, name: &str, new_source: &str) -> bool {
189 let new_hash = simple_hash(new_source);
190 if let Some(old_hash) = self.hashes.get(name)
191 && *old_hash == new_hash
192 {
193 return false;
194 }
195 self.sources
196 .insert(name.to_string(), new_source.to_string());
197 self.hashes.insert(name.to_string(), new_hash);
198 true
199 }
200 pub fn is_watched(&self, name: &str) -> bool {
202 self.sources.contains_key(name)
203 }
204 pub fn get_source(&self, name: &str) -> Option<&str> {
206 self.sources.get(name).map(|s| s.as_str())
207 }
208 pub fn watched_names(&self) -> Vec<&str> {
210 self.sources.keys().map(|s| s.as_str()).collect()
211 }
212 pub fn len(&self) -> usize {
214 self.sources.len()
215 }
216 pub fn is_empty(&self) -> bool {
218 self.sources.is_empty()
219 }
220}
221#[derive(Debug, Clone, Copy, PartialEq, Eq)]
223pub enum DescriptorType {
224 UniformBuffer,
226 StorageBuffer,
228 CombinedImageSampler,
230 StorageImage,
232}
233#[derive(Debug, Default)]
235pub struct ShaderMetaRegistry {
236 pub(super) entries: HashMap<String, ShaderMetadata>,
237}
238impl ShaderMetaRegistry {
239 pub fn new() -> Self {
241 Self::default()
242 }
243 pub fn register(&mut self, name: &str, meta: ShaderMetadata) {
245 self.entries.insert(name.to_string(), meta);
246 }
247 pub fn lookup(&self, name: &str) -> Option<&ShaderMetadata> {
249 self.entries.get(name)
250 }
251 pub fn all_names(&self) -> Vec<&str> {
253 self.entries.keys().map(|s| s.as_str()).collect()
254 }
255 pub fn len(&self) -> usize {
257 self.entries.len()
258 }
259 pub fn is_empty(&self) -> bool {
261 self.entries.is_empty()
262 }
263}
264#[derive(Debug, Clone)]
266pub struct SpecializationConstant {
267 pub name: String,
269 pub default_value: String,
271 pub description: String,
273}
274impl SpecializationConstant {
275 pub fn new(name: &str, default_value: &str, description: &str) -> Self {
277 Self {
278 name: name.to_string(),
279 default_value: default_value.to_string(),
280 description: description.to_string(),
281 }
282 }
283}
284#[derive(Debug, Clone)]
289pub struct ShaderTemplate {
290 pub template: String,
292}
293impl ShaderTemplate {
294 pub fn new(template: impl Into<String>) -> Self {
296 Self {
297 template: template.into(),
298 }
299 }
300 pub fn instantiate(&self, params: &HashMap<&str, &str>) -> String {
309 let mut result = self.template.clone();
310 for (key, value) in params {
311 let placeholder = format!("{{{}}}", key);
312 result = result.replace(&placeholder, value);
313 }
314 result
315 }
316 pub fn placeholders(&self) -> Vec<String> {
322 let mut result = Vec::new();
323 let chars: Vec<char> = self.template.chars().collect();
324 let mut i = 0;
325 while i < chars.len() {
326 if chars[i] == '{' {
327 if i + 1 < chars.len() && chars[i + 1].is_ascii_uppercase() {
328 let start = i + 1;
329 let mut end = start;
330 while end < chars.len() && chars[end] != '}' {
331 end += 1;
332 }
333 if end < chars.len() {
334 let name: String = chars[start..end].iter().collect();
335 let is_valid = name
336 .chars()
337 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_');
338 if is_valid && !result.contains(&name) {
339 result.push(name);
340 }
341 i = end + 1;
342 } else {
343 i += 1;
344 }
345 } else {
346 i += 1;
347 }
348 } else {
349 i += 1;
350 }
351 }
352 result
353 }
354 pub fn all_placeholders_provided(&self, params: &HashMap<&str, &str>) -> bool {
356 for p in self.placeholders() {
357 if !params.contains_key(p.as_str()) {
358 return false;
359 }
360 }
361 true
362 }
363}
364#[derive(Debug, Clone, Copy, PartialEq, Eq)]
366pub enum TextureFormat {
367 Rgba8Unorm,
369 Rgba8Srgb,
371 Rgba16Float,
373 R32Float,
375 Depth32Float,
377 Depth24PlusStencil8,
379}
380#[derive(Debug, Clone, Default)]
382pub struct BindGroupLayout {
383 pub(super) uniforms: Vec<UniformBinding>,
384 pub(super) storages: Vec<StorageBinding>,
385}
386impl BindGroupLayout {
387 pub fn new() -> Self {
389 Self::default()
390 }
391 pub fn add_uniform(&mut self, name: &str, _group: u32, binding: u32, size_bytes: u32) {
393 self.uniforms.push(UniformBinding {
394 name: name.to_string(),
395 binding,
396 size_bytes,
397 });
398 }
399 pub fn add_storage(&mut self, name: &str, _group: u32, binding: u32, read_only: bool) {
401 self.storages.push(StorageBinding {
402 name: name.to_string(),
403 binding,
404 read_only,
405 });
406 }
407 pub fn binding_count(&self) -> usize {
409 self.uniforms.len() + self.storages.len()
410 }
411 pub fn is_empty(&self) -> bool {
413 self.uniforms.is_empty() && self.storages.is_empty()
414 }
415 pub fn to_wgsl_snippet(&self) -> String {
417 let mut out = String::new();
418 for u in &self.uniforms {
419 out.push_str(&format!(
420 "@group(0) @binding({}) var<uniform> {}: {};\n",
421 u.binding,
422 u.name.to_lowercase(),
423 u.name
424 ));
425 }
426 for s in &self.storages {
427 let access = if s.read_only { "read" } else { "read_write" };
428 out.push_str(&format!(
429 "@group(0) @binding({}) var<storage, {}> {}: array<f32>;\n",
430 s.binding,
431 access,
432 s.name.to_lowercase()
433 ));
434 }
435 out
436 }
437}
438#[derive(Debug, Clone)]
440pub struct DepthAttachmentDesc {
441 pub format: TextureFormat,
443 pub load_op: LoadOp,
445 pub store_op: StoreOp,
447 pub clear_depth: f32,
449}
450pub struct ShaderCompilationPipeline {
454 pub(super) includes: HashMap<String, String>,
455 pub(super) cache: ShaderCache,
456}
457impl ShaderCompilationPipeline {
458 pub fn new() -> Self {
460 Self {
461 includes: HashMap::new(),
462 cache: ShaderCache::new(),
463 }
464 }
465 pub fn add_include(&mut self, name: &str, source: &str) {
467 self.includes.insert(name.to_string(), source.to_string());
468 }
469 pub fn compile(
473 &mut self,
474 name: &str,
475 source: &str,
476 spec_map: Option<&SpecializationMap>,
477 ) -> Result<String, String> {
478 if let Some(cached) = self.cache.entries.get(name) {
479 return Ok(cached.clone());
480 }
481 let includes_ref: HashMap<&str, &str> = self
482 .includes
483 .iter()
484 .map(|(k, v)| (k.as_str(), v.as_str()))
485 .collect();
486 let resolved = resolve_includes(source, &includes_ref);
487 let specialized = if let Some(sm) = spec_map {
488 sm.apply(&resolved)
489 } else {
490 resolved
491 };
492 if !validate_wgsl_structure(&specialized) {
493 return Err(format!("shader '{}' failed structural validation", name));
494 }
495 self.cache
496 .entries
497 .insert(name.to_string(), specialized.clone());
498 Ok(specialized)
499 }
500 pub fn cache_size(&self) -> usize {
502 self.cache.len()
503 }
504 pub fn clear_cache(&mut self) {
506 self.cache.clear();
507 }
508}
509#[derive(Debug, Default)]
511pub struct ShaderRegistry {
512 pub(super) shaders: HashMap<String, ComputeShaderDesc>,
513}
514impl ShaderRegistry {
515 pub fn new() -> Self {
517 Self::default()
518 }
519 pub fn register(&mut self, name: impl Into<String>, desc: ComputeShaderDesc) {
521 self.shaders.insert(name.into(), desc);
522 }
523 pub fn get(&self, name: &str) -> Option<&ComputeShaderDesc> {
525 self.shaders.get(name)
526 }
527 pub fn len(&self) -> usize {
529 self.shaders.len()
530 }
531 pub fn is_empty(&self) -> bool {
533 self.shaders.is_empty()
534 }
535 pub fn names(&self) -> impl Iterator<Item = &str> {
537 self.shaders.keys().map(|s| s.as_str())
538 }
539 pub fn unregister(&mut self, name: &str) -> Option<ComputeShaderDesc> {
541 self.shaders.remove(name)
542 }
543 pub fn contains(&self, name: &str) -> bool {
545 self.shaders.contains_key(name)
546 }
547 pub fn with_builtins() -> Self {
549 let mut reg = Self::new();
550 reg.register(
551 "sph_density",
552 ComputeShaderDesc::new("main", [64, 1, 1], SPH_DENSITY_WGSL),
553 );
554 reg.register(
555 "sph_force",
556 ComputeShaderDesc::new("main", [64, 1, 1], SPH_FORCE_WGSL),
557 );
558 reg.register(
559 "integrate",
560 ComputeShaderDesc::new("main", [64, 1, 1], INTEGRATE_WGSL),
561 );
562 reg.register(
563 "lbm_bgk_d2q9",
564 ComputeShaderDesc::new("main", [64, 1, 1], LBM_BGK_D2Q9_WGSL),
565 );
566 reg.register(
567 "lbm_streaming",
568 ComputeShaderDesc::new("main", [64, 1, 1], LBM_STREAMING_SHADER),
569 );
570 reg.register(
571 "rigid_integrate",
572 ComputeShaderDesc::new("main", [64, 1, 1], RIGID_INTEGRATE_SHADER),
573 );
574 reg.register(
575 "broadphase_sort",
576 ComputeShaderDesc::new("main", [64, 1, 1], BROADPHASE_SORT_SHADER),
577 );
578 reg.register(
579 "boundary_enforce",
580 ComputeShaderDesc::new("main", [64, 1, 1], BOUNDARY_ENFORCE_WGSL),
581 );
582 reg
583 }
584}
585#[derive(Debug, Clone)]
587pub struct ComputeShaderDesc {
588 pub entry_point: String,
590 pub workgroup_size: [u32; 3],
592 pub source: String,
594}
595impl ComputeShaderDesc {
596 pub fn new(
598 entry_point: impl Into<String>,
599 workgroup_size: [u32; 3],
600 source: impl Into<String>,
601 ) -> Self {
602 Self {
603 entry_point: entry_point.into(),
604 workgroup_size,
605 source: source.into(),
606 }
607 }
608 pub fn threads_per_workgroup(&self) -> u32 {
610 self.workgroup_size[0] * self.workgroup_size[1] * self.workgroup_size[2]
611 }
612 pub fn binding_count(&self) -> usize {
614 self.source.matches("@binding(").count()
615 }
616}
617#[derive(Debug, Clone)]
619pub struct SamplerDesc {
620 pub filter_min: FilterMode,
622 pub filter_mag: FilterMode,
624 pub address_mode: AddressMode,
626 pub anisotropy: u32,
628 pub lod_bias: f32,
630 pub lod_max: f32,
632}
633impl SamplerDesc {
634 pub fn linear() -> Self {
636 Self {
637 filter_min: FilterMode::Linear,
638 filter_mag: FilterMode::Linear,
639 address_mode: AddressMode::ClampToEdge,
640 anisotropy: 1,
641 lod_bias: 0.0,
642 lod_max: 16.0,
643 }
644 }
645 pub fn nearest() -> Self {
647 Self {
648 filter_min: FilterMode::Nearest,
649 filter_mag: FilterMode::Nearest,
650 address_mode: AddressMode::ClampToEdge,
651 anisotropy: 1,
652 lod_bias: 0.0,
653 lod_max: 0.0,
654 }
655 }
656 pub fn anisotropic(max_anisotropy: u32) -> Self {
658 Self {
659 filter_min: FilterMode::Linear,
660 filter_mag: FilterMode::Linear,
661 address_mode: AddressMode::Repeat,
662 anisotropy: max_anisotropy,
663 lod_bias: 0.0,
664 lod_max: 16.0,
665 }
666 }
667}
668#[derive(Debug, Clone)]
670pub struct RenderPassDesc {
671 pub color_attachments: Vec<ColorAttachmentDesc>,
673 pub depth_attachment: Option<DepthAttachmentDesc>,
675 pub name: String,
677}
678impl RenderPassDesc {
679 pub fn new_simple_color() -> Self {
681 Self {
682 color_attachments: vec![ColorAttachmentDesc {
683 format: TextureFormat::Rgba8Unorm,
684 load_op: LoadOp::Clear,
685 store_op: StoreOp::Store,
686 clear_color: [0.0, 0.0, 0.0, 1.0],
687 }],
688 depth_attachment: None,
689 name: "SimpleColor".to_string(),
690 }
691 }
692 pub fn new_with_depth() -> Self {
694 Self {
695 color_attachments: vec![ColorAttachmentDesc {
696 format: TextureFormat::Rgba16Float,
697 load_op: LoadOp::Clear,
698 store_op: StoreOp::Store,
699 clear_color: [0.0, 0.0, 0.0, 1.0],
700 }],
701 depth_attachment: Some(DepthAttachmentDesc {
702 format: TextureFormat::Depth32Float,
703 load_op: LoadOp::Clear,
704 store_op: StoreOp::Store,
705 clear_depth: 1.0,
706 }),
707 name: "ColorDepth".to_string(),
708 }
709 }
710 pub fn total_attachment_count(&self) -> usize {
712 self.color_attachments.len()
713 + if self.depth_attachment.is_some() {
714 1
715 } else {
716 0
717 }
718 }
719}
720#[derive(Debug, Clone, PartialEq, Eq, Hash)]
722pub enum ShaderVariant {
723 Physics,
725 Collision,
727 Sph,
729 Lbm,
731 RigidBody,
733 NeuralInference,
735}
736#[derive(Debug, Clone)]
738pub struct ShaderMetadata {
739 pub variant: ShaderVariant,
741 pub entry_point: String,
743 pub workgroup_size: [u32; 3],
745 pub bind_group_count: u32,
747}
748impl ShaderMetadata {
749 pub fn new(
751 variant: ShaderVariant,
752 entry_point: impl Into<String>,
753 workgroup_size: [u32; 3],
754 bind_group_count: u32,
755 ) -> Self {
756 Self {
757 variant,
758 entry_point: entry_point.into(),
759 workgroup_size,
760 bind_group_count,
761 }
762 }
763 pub fn threads_per_workgroup(&self) -> u32 {
765 self.workgroup_size[0] * self.workgroup_size[1] * self.workgroup_size[2]
766 }
767}
768#[derive(Debug, Default)]
772pub struct ShaderCache {
773 pub(super) entries: HashMap<String, String>,
774}
775impl ShaderCache {
776 pub fn new() -> Self {
778 Self::default()
779 }
780 pub fn get_or_insert(&mut self, key: &str, compute: impl FnOnce() -> String) -> &str {
782 self.entries.entry(key.to_string()).or_insert_with(compute)
783 }
784 pub fn contains(&self, key: &str) -> bool {
786 self.entries.contains_key(key)
787 }
788 pub fn len(&self) -> usize {
790 self.entries.len()
791 }
792 pub fn is_empty(&self) -> bool {
794 self.entries.is_empty()
795 }
796 pub fn clear(&mut self) {
798 self.entries.clear();
799 }
800 pub fn remove(&mut self, key: &str) -> Option<String> {
802 self.entries.remove(key)
803 }
804}
805#[derive(Debug, Clone, Copy, PartialEq, Eq)]
807pub enum ShaderStage {
808 Vertex,
810 Fragment,
812 Compute,
814 All,
816}
817#[derive(Debug, Clone)]
823pub struct ShaderTemplateV2 {
824 pub source: String,
826 pub defines: HashMap<String, String>,
828}
829impl ShaderTemplateV2 {
830 pub fn new(source: impl Into<String>, defines: HashMap<String, String>) -> Self {
832 Self {
833 source: source.into(),
834 defines,
835 }
836 }
837 pub fn instantiate(&self) -> String {
842 let mut result = self.source.clone();
843 for (key, value) in &self.defines {
844 result = result.replace(key.as_str(), value.as_str());
845 }
846 result
847 }
848}
849#[derive(Debug, Clone)]
851pub struct ColorAttachmentDesc {
852 pub format: TextureFormat,
854 pub load_op: LoadOp,
856 pub store_op: StoreOp,
858 pub clear_color: [f32; 4],
860}
861#[derive(Debug, Clone)]
863pub struct UniformBinding {
864 pub name: String,
866 pub binding: u32,
868 pub size_bytes: u32,
870}
871#[derive(Debug, Clone, Copy, PartialEq, Eq)]
873pub enum StoreOp {
874 Store,
876 Discard,
878}
879#[derive(Debug, Clone)]
881pub struct DescriptorBinding {
882 pub binding: u32,
884 pub descriptor_type: DescriptorType,
886 pub stage: ShaderStage,
888 pub read_only: bool,
890}
891#[derive(Debug, Clone, Copy, PartialEq, Eq)]
893pub enum FilterMode {
894 Nearest,
896 Linear,
898}
899#[derive(Debug, Clone, Copy, PartialEq, Eq)]
901pub enum LoadOp {
902 Load,
904 Clear,
906 DontCare,
908}
909#[derive(Debug, Clone, Default)]
911pub struct SpecializationMap {
912 pub(super) constants: Vec<SpecializationConstant>,
913 pub(super) overrides: HashMap<String, String>,
914}
915impl SpecializationMap {
916 pub fn new() -> Self {
918 Self::default()
919 }
920 pub fn define(&mut self, name: &str, default_value: &str, description: &str) {
922 self.constants.push(SpecializationConstant::new(
923 name,
924 default_value,
925 description,
926 ));
927 }
928 pub fn set(&mut self, name: &str, value: &str) {
930 self.overrides.insert(name.to_string(), value.to_string());
931 }
932 pub fn get(&self, name: &str) -> Option<&str> {
934 if let Some(v) = self.overrides.get(name) {
935 return Some(v.as_str());
936 }
937 for c in &self.constants {
938 if c.name == name {
939 return Some(c.default_value.as_str());
940 }
941 }
942 None
943 }
944 pub fn len(&self) -> usize {
946 self.constants.len()
947 }
948 pub fn is_empty(&self) -> bool {
950 self.constants.is_empty()
951 }
952 pub fn apply(&self, source: &str) -> String {
955 let mut result = source.to_string();
956 for c in &self.constants {
957 let value = self
958 .overrides
959 .get(&c.name)
960 .map(|s| s.as_str())
961 .unwrap_or(&c.default_value);
962 let old = format!("const {} = {};", c.name, c.default_value);
963 let new = format!("const {} = {};", c.name, value);
964 result = result.replace(&old, &new);
965 }
966 result
967 }
968}
969#[derive(Debug, Clone)]
971pub struct UniformBufferDesc {
972 pub name: String,
974 pub group: u32,
976 pub binding: u32,
978 pub size_bytes: u32,
980}
981impl UniformBufferDesc {
982 pub fn new(name: &str, group: u32, binding: u32, size_bytes: u32) -> Self {
984 Self {
985 name: name.to_string(),
986 group,
987 binding,
988 size_bytes,
989 }
990 }
991 pub fn wgsl_annotation(&self) -> String {
993 format!(
994 "@group({}) @binding({}) var<uniform> {}: {};",
995 self.group,
996 self.binding,
997 self.name.to_lowercase(),
998 self.name
999 )
1000 }
1001}
1002#[derive(Debug)]
1007pub struct BytecodeShaderCache {
1008 pub cache: HashMap<String, Vec<u8>>,
1010 pub(super) insertion_order: Vec<String>,
1012 pub max_size: usize,
1014}
1015impl BytecodeShaderCache {
1016 pub fn new(max_size: usize) -> Self {
1018 Self {
1019 cache: HashMap::new(),
1020 insertion_order: Vec::new(),
1021 max_size,
1022 }
1023 }
1024 pub fn insert(&mut self, name: &str, bytecode: Vec<u8>) {
1029 if self.cache.contains_key(name) {
1030 self.insertion_order.retain(|k| k != name);
1031 }
1032 self.cache.insert(name.to_string(), bytecode);
1033 self.insertion_order.push(name.to_string());
1034 while self.total_bytes() > self.max_size && !self.insertion_order.is_empty() {
1035 self.evict_oldest();
1036 }
1037 }
1038 pub fn get(&self, name: &str) -> Option<&Vec<u8>> {
1040 self.cache.get(name)
1041 }
1042 pub fn evict_oldest(&mut self) {
1044 if let Some(oldest) = self.insertion_order.first().cloned() {
1045 self.insertion_order.remove(0);
1046 self.cache.remove(&oldest);
1047 }
1048 }
1049 pub fn total_bytes(&self) -> usize {
1051 self.cache.values().map(|v| v.len()).sum()
1052 }
1053 pub fn len(&self) -> usize {
1055 self.cache.len()
1056 }
1057 pub fn is_empty(&self) -> bool {
1059 self.cache.is_empty()
1060 }
1061}