Repository: paritytech/parity-db (language: rust)

Issue:
Add compressed info to MULTIHEAD
Multipart storage only store the compression bit at the very last chunk.
It could be good to be able to know if compression is applied from the very first chunk.
Can use a new const in this case:
```
const MULTIHEAD_COMPRESSED: &[u8] = &[0xfc, 0xff];
```
or &[0xfd, 0x7f].
This may requires a new db_version for compatibility, or at least to remove dead code when dropping compatibility.


Discussion / hints:

Fix patch:
diff --git a/src/options.rs b/src/options.rs
index f230cdba..83b8a510 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -22,7 +22,7 @@ use crate::{
 use rand::Rng;
 use std::{collections::HashMap, io::Write, path::Path};
 
-pub const CURRENT_VERSION: u32 = 6;
+pub const CURRENT_VERSION: u32 = 7;
 // TODO on last supported 5, remove MULTIHEAD_V4 and MULTIPART_V4
 const LAST_SUPPORTED_VERSION: u32 = 4;
 
diff --git a/src/table.rs b/src/table.rs
index 275a2ff5..ebd57fdc 100644
--- a/src/table.rs
+++ b/src/table.rs
@@ -90,6 +90,7 @@ const MULTIPART_V4: &[u8] = &[0xff, 0xfe];
 const MULTIHEAD_V4: &[u8] = &[0xff, 0xfd];
 const MULTIPART: &[u8] = &[0xfe, 0xff];
 const MULTIHEAD: &[u8] = &[0xfd, 0xff];
+const MULTIHEAD_COMPRESSED: &[u8] = &[0xfd, 0x7f];
 // When a rc reach locked ref, it is locked in db.
 const LOCKED_REF: u32 = u32::MAX;
 
@@ -221,8 +222,12 @@ impl<B: AsRef<[u8]> + AsMut<[u8]>> Entry<B> {
 		self.write_slice(MULTIPART);
 	}
 
+	fn is_multihead_compressed(&self) -> bool {
+		&self.1.as_ref()[0..SIZE_SIZE] == MULTIHEAD_COMPRESSED
+	}
+
 	fn is_multihead(&self) -> bool {
-		&self.1.as_ref()[0..SIZE_SIZE] == MULTIHEAD
+		self.is_multihead_compressed() || &self.1.as_ref()[0..SIZE_SIZE] == MULTIHEAD
 	}
 
 	fn is_multihead_v4(&self) -> bool {
@@ -233,6 +238,10 @@ impl<B: AsRef<[u8]> + AsMut<[u8]>> Entry<B> {
 		self.write_slice(MULTIHEAD);
 	}
 
+	fn write_multihead_compressed(&mut self) {
+		self.write_slice(MULTIHEAD_COMPRESSED);
+	}
+
 	fn is_multi(&self, db_version: u32) -> bool {
 		self.is_multipart() ||
 			self.is_multihead() ||
@@ -426,12 +435,17 @@ impl ValueTable {
 			}
 
 			let (entry_end, next) = if self.multipart && buf.is_multi(self.db_version) {
+				if part == 0 && self.db_version > 6 && buf.is_multihead_compressed() {
+					compressed = true;
+				}
 				buf.skip_size();
 				let next = buf.read_next();
 				(entry_size, next)
 			} else {
 				let (size, read_compressed) = buf.read_size();
-				compressed = read_compressed;
+				if part == 0 || self.db_version <= 6 {
+					compressed = read_compressed;
+				}
 				(buf.offset() + size as usize, 0)
 			};
 
@@ -665,7 +679,11 @@ impl ValueTable {
 					next_index = self.next_free(log)?
 				}
 				if start == 0 {
-					buf.write_multihead();
+					if compressed {
+						buf.write_multihead_compressed();
+					} else {
+						buf.write_multihead();
+					}
 				} else {
 					buf.write_multipart();
 				}
@@ -1400,8 +1418,18 @@ mod test {
 
 	#[test]
 	fn multipart_collision() {
+		use super::MAX_ENTRY_SIZE;
+		let mut entry = super::Entry::new(super::MULTIPART.to_vec());
+		let size = entry.read_size().0 as usize;
+		assert!(size > MAX_ENTRY_SIZE);
+		let mut entry = super::Entry::new(super::MULTIHEAD.to_vec());
+		let size = entry.read_size().0 as usize;
+		assert!(size > MAX_ENTRY_SIZE);
+		let mut entry = super::Entry::new(super::MULTIHEAD_COMPRESSED.to_vec());
+		let size = entry.read_size().0 as usize;
+		assert!(size > MAX_ENTRY_SIZE);
 		let dir = TempDir::new("multipart_collision");
-		let table = dir.table(Some(super::MAX_ENTRY_SIZE as u16), &rc_options());
+		let table = dir.table(Some(MAX_ENTRY_SIZE as u16), &rc_options());
 		let log = dir.log();
 
 		let key = key(1);


Test patch: