From 124d3383e8ca85d26cedd259cbeb97a3ca2deeb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:09:02 +0100 Subject: [PATCH] implement custom serialize for Span --- src/base/source_file.rs | 2 +- src/lib.rs | 6 +++ src/serde.rs | 93 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/serde.rs diff --git a/src/base/source_file.rs b/src/base/source_file.rs index 369e210..a19d576 100644 --- a/src/base/source_file.rs +++ b/src/base/source_file.rs @@ -145,7 +145,7 @@ impl SourceFile { } /// Represents a range of characters in a source file. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize))] #[derive(Clone, Getters, CopyGetters)] pub struct Span { /// Get the start byte index of the span. diff --git a/src/lib.rs b/src/lib.rs index 9e530a3..0472ae1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,12 @@ pub mod lexical; pub mod syntax; pub mod transpile; +#[cfg(feature = "serde")] +mod serde; +#[cfg(feature = "serde")] +#[cfg_attr(feature = "serde", doc(inline))] +pub use serde::SerdeWrapper; + use std::path::Path; use base::{source_file::SourceFile, Error, FileProvider, Handler, Result}; diff --git a/src/serde.rs b/src/serde.rs new file mode 100644 index 0000000..d09f070 --- /dev/null +++ b/src/serde.rs @@ -0,0 +1,93 @@ +//! Utilities for (de-)serializing + +use std::{ + collections::HashMap, + sync::{Arc, LazyLock, Mutex, RwLock}, +}; + +use serde::{de::{self, Visitor}, ser::SerializeStruct, Deserialize, Serialize}; + +use crate::base::source_file::{SourceFile, Span}; + +static SERIALIZE_DATA: LazyLock> = + LazyLock::new(|| Mutex::new(SerializeData::default())); + +static DEDUPLICATE_SOURCE_FILES: LazyLock> = LazyLock::new(|| RwLock::new(false)); + +/// Wrapper to remove duplicate source file data during (de-)serialization +#[derive(Debug)] +pub struct SerdeWrapper(pub T); + +impl Serialize for SerdeWrapper +where + T: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + *DEDUPLICATE_SOURCE_FILES.write().unwrap() = true; + SERIALIZE_DATA.lock().unwrap().clear(); + let mut s = serializer.serialize_struct("SourceFileWrapper", 2)?; + s.serialize_field("data", &self.0)?; + *DEDUPLICATE_SOURCE_FILES.write().unwrap() = false; + s.serialize_field( + "source_files", + &SERIALIZE_DATA.lock().unwrap().id_to_source_file, + )?; + s.end() + } +} + +/// Internally used for Serialization +#[derive(Debug, Default)] +struct SerializeData { + id_counter: usize, + ptr_to_id: HashMap, + id_to_source_file: HashMap, +} + +impl SerializeData { + fn clear(&mut self) { + self.id_counter = 0; + self.id_to_source_file.clear(); + self.ptr_to_id.clear(); + } + + /// Get id of already stored [`Arc`] or store it and return new id + pub fn get_id_of(&mut self, source_file: &Arc) -> usize { + let ptr = Arc::as_ptr(source_file); + if let Some(&id) = self.ptr_to_id.get(&(ptr as usize)) { + id + } else { + let id = self.id_counter; + self.id_counter += 1; + + self.ptr_to_id.insert(ptr as usize, id); + self.id_to_source_file + .insert(id, Arc::unwrap_or_clone(source_file.to_owned())); + + id + } + } +} + +impl Serialize for Span { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut s = serializer.serialize_struct("Span", 3)?; + s.serialize_field("start", &self.start())?; + s.serialize_field("end", &self.end())?; + + if *DEDUPLICATE_SOURCE_FILES.read().unwrap() { + let mut data = SERIALIZE_DATA.lock().unwrap(); + s.serialize_field("source_file", &data.get_id_of(self.source_file()))?; + } else { + s.serialize_field("source_file", self.source_file())?; + } + + s.end() + } +}