mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-27 16:48:58 +00:00
424 lines
13 KiB
Rust
424 lines
13 KiB
Rust
// Copyright 2024 RustFS Team
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use crate::error::{Error, Result};
|
|
use crate::filemeta::msgp_decode::{prealloc_hint, read_exact_vec};
|
|
use serde::{Deserialize, Serialize};
|
|
use std::io::Cursor;
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
|
pub struct InlineData(Vec<u8>);
|
|
|
|
const INLINE_DATA_VER: u8 = 1;
|
|
|
|
impl InlineData {
|
|
fn contains_key_by<F>(&self, mut should_remove: F) -> Result<bool>
|
|
where
|
|
F: FnMut(&[u8]) -> bool,
|
|
{
|
|
let buf = self.after_version();
|
|
if buf.is_empty() {
|
|
return Ok(false);
|
|
}
|
|
|
|
let mut scan_cur = Cursor::new(buf);
|
|
let mut scan_fields_len = rmp::decode::read_map_len(&mut scan_cur)? as usize;
|
|
|
|
while scan_fields_len > 0 {
|
|
scan_fields_len -= 1;
|
|
|
|
let str_len = rmp::decode::read_str_len(&mut scan_cur)? as usize;
|
|
let key_start = scan_cur.position() as usize;
|
|
let key_end = key_start + str_len;
|
|
let key = buf
|
|
.get(key_start..key_end)
|
|
.ok_or_else(|| Error::other("InlineData key out of range"))?;
|
|
scan_cur.set_position(key_end as u64);
|
|
|
|
let bin_len = rmp::decode::read_bin_len(&mut scan_cur)? as usize;
|
|
let value_start = scan_cur.position() as usize;
|
|
let value_end = value_start + bin_len;
|
|
if value_end > buf.len() {
|
|
return Err(Error::other("InlineData value out of range"));
|
|
}
|
|
scan_cur.set_position(value_end as u64);
|
|
|
|
if should_remove(key) {
|
|
return Ok(true);
|
|
}
|
|
}
|
|
|
|
Ok(false)
|
|
}
|
|
|
|
fn remove_keys_by<F>(&mut self, mut should_remove: F) -> Result<bool>
|
|
where
|
|
F: FnMut(&[u8]) -> bool,
|
|
{
|
|
let buf = self.after_version();
|
|
if buf.is_empty() {
|
|
return Ok(false);
|
|
}
|
|
|
|
let mut cur = Cursor::new(buf);
|
|
let mut fields_len = rmp::decode::read_map_len(&mut cur)? as usize;
|
|
let mut keys = Vec::with_capacity(prealloc_hint(fields_len));
|
|
let mut values = Vec::with_capacity(prealloc_hint(fields_len));
|
|
let mut found = false;
|
|
|
|
while fields_len > 0 {
|
|
fields_len -= 1;
|
|
|
|
let str_len = rmp::decode::read_str_len(&mut cur)? as usize;
|
|
let field_buf = read_exact_vec(&mut cur, str_len)?;
|
|
|
|
let bin_len = rmp::decode::read_bin_len(&mut cur)? as usize;
|
|
let start = cur.position() as usize;
|
|
let end = start + bin_len;
|
|
let value = buf
|
|
.get(start..end)
|
|
.ok_or_else(|| Error::other("InlineData value out of range"))?;
|
|
cur.set_position(end as u64);
|
|
|
|
if should_remove(field_buf.as_slice()) {
|
|
found = true;
|
|
continue;
|
|
}
|
|
|
|
keys.push(String::from_utf8(field_buf)?);
|
|
values.push(value.to_vec());
|
|
}
|
|
|
|
if !found {
|
|
return Ok(false);
|
|
}
|
|
|
|
if keys.is_empty() {
|
|
self.0 = Vec::new();
|
|
return Ok(true);
|
|
}
|
|
|
|
self.serialize(keys, values)?;
|
|
Ok(true)
|
|
}
|
|
|
|
fn remove_two_keys_by_bytes(&mut self, first_key: &[u8], second_key: &[u8]) -> Result<bool> {
|
|
let buf = self.after_version();
|
|
if buf.is_empty() {
|
|
return Ok(false);
|
|
}
|
|
|
|
let same = first_key == second_key;
|
|
let mut cur = Cursor::new(buf);
|
|
let mut fields_len = rmp::decode::read_map_len(&mut cur)? as usize;
|
|
let mut keys = Vec::with_capacity(prealloc_hint(fields_len) + 1);
|
|
let mut values = Vec::with_capacity(prealloc_hint(fields_len) + 1);
|
|
let mut found = false;
|
|
|
|
while fields_len > 0 {
|
|
fields_len -= 1;
|
|
|
|
let str_len = rmp::decode::read_str_len(&mut cur)? as usize;
|
|
let field_buf = read_exact_vec(&mut cur, str_len)?;
|
|
|
|
let bin_len = rmp::decode::read_bin_len(&mut cur)? as usize;
|
|
let start = cur.position() as usize;
|
|
let end = start + bin_len;
|
|
let value = buf
|
|
.get(start..end)
|
|
.ok_or_else(|| Error::other("InlineData value out of range"))?;
|
|
cur.set_position(end as u64);
|
|
|
|
let should_remove = if same {
|
|
field_buf.as_slice() == first_key
|
|
} else {
|
|
field_buf.as_slice() == first_key || field_buf.as_slice() == second_key
|
|
};
|
|
|
|
if should_remove {
|
|
found = true;
|
|
continue;
|
|
}
|
|
|
|
keys.push(String::from_utf8(field_buf)?);
|
|
values.push(value.to_vec());
|
|
}
|
|
|
|
if !found {
|
|
return Ok(false);
|
|
}
|
|
|
|
if keys.is_empty() {
|
|
self.0 = Vec::new();
|
|
return Ok(true);
|
|
}
|
|
|
|
self.serialize(keys, values)?;
|
|
Ok(true)
|
|
}
|
|
|
|
pub fn new() -> Self {
|
|
Self(Vec::new())
|
|
}
|
|
pub fn update(&mut self, buf: &[u8]) {
|
|
self.0 = buf.to_vec()
|
|
}
|
|
pub fn as_slice(&self) -> &[u8] {
|
|
self.0.as_slice()
|
|
}
|
|
pub fn version_ok(&self) -> bool {
|
|
if self.0.is_empty() {
|
|
return true;
|
|
}
|
|
|
|
self.0[0] > 0 && self.0[0] <= INLINE_DATA_VER
|
|
}
|
|
|
|
pub fn after_version(&self) -> &[u8] {
|
|
if self.0.is_empty() { &self.0 } else { &self.0[1..] }
|
|
}
|
|
|
|
pub fn entries(&self) -> Result<usize> {
|
|
if self.0.is_empty() || !self.version_ok() {
|
|
return Ok(0);
|
|
}
|
|
|
|
let buf = self.after_version();
|
|
|
|
let mut cur = Cursor::new(buf);
|
|
|
|
let fields_len = rmp::decode::read_map_len(&mut cur)?;
|
|
|
|
Ok(fields_len as usize)
|
|
}
|
|
|
|
pub fn find(&self, key: &str) -> Result<Option<Vec<u8>>> {
|
|
if self.0.is_empty() || !self.version_ok() {
|
|
return Ok(None);
|
|
}
|
|
|
|
let buf = self.after_version();
|
|
|
|
let mut cur = Cursor::new(buf);
|
|
|
|
let mut fields_len = rmp::decode::read_map_len(&mut cur)?;
|
|
|
|
while fields_len > 0 {
|
|
fields_len -= 1;
|
|
|
|
let str_len = rmp::decode::read_str_len(&mut cur)?;
|
|
|
|
let field_buff = read_exact_vec(&mut cur, str_len as usize)?;
|
|
|
|
let field = String::from_utf8(field_buff)?;
|
|
|
|
let bin_len = rmp::decode::read_bin_len(&mut cur)? as usize;
|
|
let start = cur.position() as usize;
|
|
let end = start + bin_len;
|
|
let value = buf
|
|
.get(start..end)
|
|
.ok_or_else(|| Error::other("InlineData value out of range"))?;
|
|
cur.set_position(end as u64);
|
|
|
|
if field.as_str() == key {
|
|
return Ok(Some(value.to_vec()));
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
|
|
pub fn validate(&self) -> Result<()> {
|
|
if self.0.is_empty() {
|
|
return Ok(());
|
|
}
|
|
|
|
let buf = self.after_version();
|
|
let mut cur = Cursor::new(buf);
|
|
|
|
let mut fields_len = rmp::decode::read_map_len(&mut cur)?;
|
|
|
|
while fields_len > 0 {
|
|
fields_len -= 1;
|
|
|
|
let str_len = rmp::decode::read_str_len(&mut cur)?;
|
|
|
|
let field_buff = read_exact_vec(&mut cur, str_len as usize)?;
|
|
|
|
let field = String::from_utf8(field_buff)?;
|
|
if field.is_empty() {
|
|
return Err(Error::other("InlineData key empty"));
|
|
}
|
|
|
|
let bin_len = rmp::decode::read_bin_len(&mut cur)? as usize;
|
|
let start = cur.position() as usize;
|
|
let end = start + bin_len;
|
|
if end > buf.len() {
|
|
return Err(Error::other("InlineData value out of range"));
|
|
}
|
|
cur.set_position(end as u64);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn replace(&mut self, key: &str, value: Vec<u8>) -> Result<()> {
|
|
if self.after_version().is_empty() {
|
|
let mut keys = Vec::with_capacity(1);
|
|
let mut values = Vec::with_capacity(1);
|
|
|
|
keys.push(key.to_owned());
|
|
values.push(value);
|
|
|
|
return self.serialize(keys, values);
|
|
}
|
|
|
|
let buf = self.after_version();
|
|
let mut cur = Cursor::new(buf);
|
|
|
|
let mut fields_len = rmp::decode::read_map_len(&mut cur)? as usize;
|
|
let mut keys = Vec::with_capacity(prealloc_hint(fields_len) + 1);
|
|
let mut values = Vec::with_capacity(prealloc_hint(fields_len) + 1);
|
|
|
|
let mut replaced = false;
|
|
|
|
while fields_len > 0 {
|
|
fields_len -= 1;
|
|
|
|
let str_len = rmp::decode::read_str_len(&mut cur)?;
|
|
|
|
let field_buff = read_exact_vec(&mut cur, str_len as usize)?;
|
|
|
|
let find_key = String::from_utf8(field_buff)?;
|
|
|
|
let bin_len = rmp::decode::read_bin_len(&mut cur)? as usize;
|
|
let start = cur.position() as usize;
|
|
let end = start + bin_len;
|
|
let find_value = buf
|
|
.get(start..end)
|
|
.ok_or_else(|| Error::other("InlineData value out of range"))?;
|
|
cur.set_position(end as u64);
|
|
|
|
if find_key.as_str() == key {
|
|
values.push(value.clone());
|
|
replaced = true
|
|
} else {
|
|
values.push(find_value.to_vec());
|
|
}
|
|
|
|
keys.push(find_key);
|
|
}
|
|
|
|
if !replaced {
|
|
keys.push(key.to_owned());
|
|
values.push(value);
|
|
}
|
|
|
|
self.serialize(keys, values)
|
|
}
|
|
|
|
pub fn remove_key(&mut self, key: &str) -> Result<bool> {
|
|
let key_bytes = key.as_bytes();
|
|
if !self.contains_key_by(|candidate| candidate == key_bytes)? {
|
|
return Ok(false);
|
|
}
|
|
self.remove_keys_by(|candidate| candidate == key_bytes)
|
|
}
|
|
|
|
pub fn remove(&mut self, remove_keys: Vec<Uuid>) -> Result<bool> {
|
|
let mut encoded_keys = Vec::with_capacity(remove_keys.len());
|
|
for key in remove_keys {
|
|
let mut buf = Uuid::encode_buffer();
|
|
encoded_keys.push(key.hyphenated().encode_lower(&mut buf).to_string().into_bytes());
|
|
}
|
|
|
|
self.remove_keys_by(|candidate| encoded_keys.iter().any(|key| candidate == key.as_slice()))
|
|
}
|
|
|
|
pub fn remove_two(&mut self, first: Uuid, second: Uuid) -> Result<bool> {
|
|
let mut first_buf = Uuid::encode_buffer();
|
|
let mut second_buf = Uuid::encode_buffer();
|
|
let first_key = first.hyphenated().encode_lower(&mut first_buf).as_bytes();
|
|
let second_key = second.hyphenated().encode_lower(&mut second_buf).as_bytes();
|
|
self.remove_two_keys_by_bytes(first_key, second_key)
|
|
}
|
|
fn serialize(&mut self, keys: Vec<String>, values: Vec<Vec<u8>>) -> Result<()> {
|
|
assert_eq!(keys.len(), values.len(), "InlineData serialize: keys/values not match");
|
|
|
|
if keys.is_empty() {
|
|
self.0 = Vec::new();
|
|
return Ok(());
|
|
}
|
|
|
|
let mut wr = Vec::new();
|
|
|
|
wr.push(INLINE_DATA_VER);
|
|
|
|
let map_len = keys.len();
|
|
|
|
rmp::encode::write_map_len(&mut wr, map_len as u32)?;
|
|
|
|
for i in 0..map_len {
|
|
rmp::encode::write_str(&mut wr, keys[i].as_str())?;
|
|
rmp::encode::write_bin(&mut wr, values[i].as_slice())?;
|
|
}
|
|
|
|
self.0 = wr;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn remove_key_miss_keeps_inline_data_unchanged() {
|
|
let mut data = InlineData::new();
|
|
data.replace("keep", b"value".to_vec()).expect("seed inline data");
|
|
let before = data.as_slice().to_vec();
|
|
|
|
let removed = data.remove_key("missing").expect("remove_key should succeed");
|
|
|
|
assert!(!removed);
|
|
assert_eq!(data.as_slice(), before.as_slice());
|
|
}
|
|
|
|
#[test]
|
|
fn remove_two_removes_only_matching_keys() {
|
|
let first = Uuid::new_v4();
|
|
let second = Uuid::new_v4();
|
|
let keep = Uuid::new_v4();
|
|
let mut data = InlineData::new();
|
|
data.replace(first.hyphenated().to_string().as_str(), b"first".to_vec())
|
|
.expect("seed first key");
|
|
data.replace(second.hyphenated().to_string().as_str(), b"second".to_vec())
|
|
.expect("seed second key");
|
|
data.replace(keep.hyphenated().to_string().as_str(), b"keep".to_vec())
|
|
.expect("seed keep key");
|
|
|
|
let removed = data.remove_two(first, second).expect("remove_two should succeed");
|
|
|
|
assert!(removed);
|
|
assert_eq!(data.find(first.hyphenated().to_string().as_str()).expect("find first"), None);
|
|
assert_eq!(data.find(second.hyphenated().to_string().as_str()).expect("find second"), None);
|
|
assert_eq!(
|
|
data.find(keep.hyphenated().to_string().as_str()).expect("find keep"),
|
|
Some(b"keep".to_vec())
|
|
);
|
|
}
|
|
}
|