Add support for borsh
diff --git a/Cargo.toml b/Cargo.toml
index c1e34e7..7dd7a5f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@
 
 [dependencies]
 serde = { version = "1.0", optional = true, default-features = false }
+borsh = { version = "1.4.0", optional = true, default-features = false }
 arbitrary = { version = "1.3", optional = true }
 
 [dev-dependencies]
@@ -22,4 +23,4 @@
 
 [features]
 default = ["std"]
-std = ["serde?/std"]
+std = ["serde?/std", "borsh?/std"]
diff --git a/src/borsh.rs b/src/borsh.rs
new file mode 100644
index 0000000..12580cb
--- /dev/null
+++ b/src/borsh.rs
@@ -0,0 +1,58 @@
+use crate::{Repr, SmolStr, INLINE_CAP};
+use alloc::string::{String, ToString};
+use borsh::io::{Error, ErrorKind, Read, Write};
+use borsh::{BorshDeserialize, BorshSerialize};
+use core::intrinsics::transmute;
+
+impl BorshSerialize for SmolStr {
+    fn serialize<W: Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
+        self.as_str().serialize(writer)
+    }
+}
+
+impl BorshDeserialize for SmolStr {
+    #[inline]
+    fn deserialize_reader<R: Read>(reader: &mut R) -> borsh::io::Result<Self> {
+        let len = u32::deserialize_reader(reader)?;
+        if (len as usize) < INLINE_CAP {
+            let mut buf = [0u8; INLINE_CAP];
+            reader.read_exact(&mut buf[..len as usize])?;
+            _ = core::str::from_utf8(&buf[..len as usize]).map_err(|err| {
+                let msg = err.to_string();
+                Error::new(ErrorKind::InvalidData, msg)
+            })?;
+            Ok(SmolStr(Repr::Inline {
+                len: unsafe { transmute(len as u8) },
+                buf,
+            }))
+        } else {
+            // u8::vec_from_reader always returns Some on success in current implementation
+            let vec = u8::vec_from_reader(len, reader)?.ok_or_else(|| {
+                Error::new(
+                    ErrorKind::Other,
+                    "u8::vec_from_reader unexpectedly returned None".to_string(),
+                )
+            })?;
+            Ok(SmolStr::from(String::from_utf8(vec).map_err(|err| {
+                let msg = err.to_string();
+                Error::new(ErrorKind::InvalidData, msg)
+            })?))
+        }
+    }
+}
+
+#[cfg(feature = "borsh/unstable__schema")]
+mod schema {
+    use alloc::collections::BTreeMap;
+    use borsh::schema::{Declaration, Definition};
+    use borsh::BorshSchema;
+    impl BorshSchema for SmolStr {
+        fn add_definitions_recursively(definitions: &mut BTreeMap<Declaration, Definition>) {
+            str::add_definitions_recursively(definitions)
+        }
+
+        fn declaration() -> Declaration {
+            str::declaration()
+        }
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 448315c..cc8612e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -795,5 +795,7 @@
     }
 }
 
+#[cfg(feature = "borsh")]
+mod borsh;
 #[cfg(feature = "serde")]
 mod serde;
diff --git a/tests/test.rs b/tests/test.rs
index 0d553ca..22b9df2 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -348,3 +348,55 @@
         assert!(!result.is_heap_allocated());
     }
 }
+#[cfg(feature = "borsh")]
+
+mod borsh_tests {
+    use borsh::BorshDeserialize;
+    use smol_str::{SmolStr, ToSmolStr};
+    use std::io::Cursor;
+
+    #[test]
+    fn borsh_serialize_stack() {
+        let smolstr_on_stack = "aßΔCaßδc".to_smolstr();
+        let mut buffer = Vec::new();
+        borsh::BorshSerialize::serialize(&smolstr_on_stack, &mut buffer).unwrap();
+        let mut cursor = Cursor::new(buffer);
+        let decoded: SmolStr = borsh::BorshDeserialize::deserialize_reader(&mut cursor).unwrap();
+        assert_eq!(smolstr_on_stack, decoded);
+    }
+    #[test]
+    fn borsh_serialize_heap() {
+        let smolstr_on_heap = "aßΔCaßδcaßΔCaßδcaßΔCaßδcaßΔCaßδcaßΔCaßδcaßΔCaßδcaßΔCaßδcaßΔCaßδcaßΔCaßδcaßΔCaßδcaßΔCaßδc".to_smolstr();
+        let mut buffer = Vec::new();
+        borsh::BorshSerialize::serialize(&smolstr_on_heap, &mut buffer).unwrap();
+        let mut cursor = Cursor::new(buffer);
+        let decoded: SmolStr = borsh::BorshDeserialize::deserialize_reader(&mut cursor).unwrap();
+        assert_eq!(smolstr_on_heap, decoded);
+    }
+    #[test]
+    fn borsh_non_utf8_stack() {
+        let invalid_utf8: Vec<u8> = vec![0xF0, 0x9F, 0x8F]; // Incomplete UTF-8 sequence
+
+        let wrong_utf8 = SmolStr::from(unsafe { String::from_utf8_unchecked(invalid_utf8) });
+        let mut buffer = Vec::new();
+        borsh::BorshSerialize::serialize(&wrong_utf8, &mut buffer).unwrap();
+        let mut cursor = Cursor::new(buffer);
+        let result = SmolStr::deserialize_reader(&mut cursor);
+        assert!(result.is_err());
+    }
+
+    #[test]
+    fn borsh_non_utf8_heap() {
+        let invalid_utf8: Vec<u8> = vec![
+            0xC1, 0x8A, 0x5F, 0xE2, 0x3A, 0x9E, 0x3B, 0xAA, 0x01, 0x08, 0x6F, 0x2F, 0xC0, 0x32,
+            0xAB, 0xE1, 0x9A, 0x2F, 0x4A, 0x3F, 0x25, 0x0D, 0x8A, 0x2A, 0x19, 0x11, 0xF0, 0x7F,
+            0x0E, 0x80,
+        ];
+        let wrong_utf8 = SmolStr::from(unsafe { String::from_utf8_unchecked(invalid_utf8) });
+        let mut buffer = Vec::new();
+        borsh::BorshSerialize::serialize(&wrong_utf8, &mut buffer).unwrap();
+        let mut cursor = Cursor::new(buffer);
+        let result = SmolStr::deserialize_reader(&mut cursor);
+        assert!(result.is_err());
+    }
+}