use aok::Void;
use tempfile::tempdir;
use wedb_embed::{
BitOp, BitfieldEncoding, BitfieldOperation, BitfieldOverflow, BitfieldValue, WeDb,
};
#[ctor::ctor(unsafe)]
fn _log_init() {
log_init::init();
}
#[test]
fn test_bitmap_basic_set_get() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
let offsets = [
0,
123,
1024 * 8,
1024 * 8 + 1,
3 * 1024 * 8,
3 * 1024 * 8 + 1,
];
for &offset in &offsets {
assert_eq!(db.getbit("bkey", offset)?, 0);
assert_eq!(db.setbit("bkey", offset, 1)?, 0);
assert_eq!(db.getbit("bkey", offset)?, 1);
}
for &offset in &offsets {
assert_eq!(db.setbit("bkey", offset, 0)?, 1);
assert_eq!(db.getbit("bkey", offset)?, 0);
}
Ok(())
}
#[test]
fn test_bitmap_bitcount_and_ranges() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
let offsets = [
0,
123,
1024 * 8,
1024 * 8 + 1,
3 * 1024 * 8,
3 * 1024 * 8 + 1,
];
for &offset in &offsets {
db.setbit("bkey", offset, 1)?;
}
assert_eq!(db.bitcount("bkey", Some(0), Some(4 * 1024))?, 6);
assert_eq!(db.bitcount("bkey", Some(0), Some(-1))?, 6);
assert_eq!(db.bitcount("bkey", None, None)?, 6);
let dir2 = tempdir()?;
let db2 = WeDb::open(dir2.path())?;
db2.setbit("neg_k", 0, 1)?;
assert_eq!(db2.bitcount("neg_k", Some(0), Some(4 * 1024))?, 1);
assert_eq!(db2.bitcount("neg_k", Some(0), Some(0))?, 1);
assert_eq!(db2.bitcount("neg_k", Some(0), Some(-1))?, 1);
assert_eq!(db2.bitcount("neg_k", Some(-1), Some(-1))?, 1);
assert_eq!(db2.bitcount("neg_k", Some(1), Some(1))?, 0);
assert_eq!(db2.bitcount("neg_k", Some(-10000), Some(-10000))?, 1);
db2.setbit("neg_k", 5, 1)?;
assert_eq!(db2.bitcount("neg_k", Some(-10000), Some(-10000))?, 2);
db2.setbit("neg_k", 8 * 1024 - 1, 1)?;
db2.setbit("neg_k", 8 * 1024, 1)?;
assert_eq!(db2.bitcount("neg_k", Some(0), Some(1024))?, 4);
assert_eq!(db2.bitcount("neg_k", Some(0), Some(1023))?, 3);
Ok(())
}
#[test]
fn test_bitmap_bitcount_bit_option() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
let offsets = [
0,
100,
1024 * 8,
1024 * 8 + 1,
3 * 1024 * 8,
3 * 1024 * 8 + 1,
];
for &offset in &offsets {
db.setbit("bkey", offset, 1)?;
}
assert_eq!(
db.bitcount_opts("bkey", Some(0), Some(4 * 1024 * 8), true)?,
6
);
assert_eq!(db.bitcount_opts("bkey", Some(0), Some(-1), true)?, 6);
assert_eq!(
db.bitcount_opts("bkey", Some(0), Some(3 * 1024 * 8 + 1), true)?,
6
);
assert_eq!(
db.bitcount_opts("bkey", Some(1), Some(3 * 1024 * 8 + 1), true)?,
5
);
assert_eq!(db.bitcount_opts("bkey", Some(0), Some(0), true)?, 1);
assert_eq!(db.bitcount_opts("bkey", Some(0), Some(100), true)?, 2);
assert_eq!(
db.bitcount_opts("bkey", Some(100), Some(1024 * 8), true)?,
2
);
assert_eq!(
db.bitcount_opts("bkey", Some(100), Some(3 * 1024 * 8), true)?,
4
);
Ok(())
}
#[test]
fn test_bitmap_bitpos() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
let offsets = [
0,
123,
1024 * 8,
1024 * 8 + 16,
3 * 1024 * 8,
3 * 1024 * 8 + 16,
];
for &offset in &offsets {
db.setbit("bkey", offset, 1)?;
}
let start_indexes = [0i64, 1, 124, 1025, 1027, 3 * 1024 + 1];
for (i, &start) in start_indexes.iter().enumerate() {
let pos = db.bitpos_opts("bkey", 1, Some(start), Some(-1), true, false)?;
assert_eq!(pos, offsets[i] as i64);
}
let pos_zero = db.bitpos("bkey", 0, Some(0), None)?;
assert_eq!(pos_zero, 1);
Ok(())
}
#[test]
fn test_bitmap_bitop() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.setbit("k1", 0, 1)?;
db.setbit("k1", 2, 1)?;
db.setbit("k1", 8192, 1)?;
db.setbit("k2", 0, 1)?;
db.setbit("k2", 1, 1)?;
db.setbit("k2", 8192, 1)?;
let len_and = db.bitop(BitOp::And, "k_and", &["k1", "k2"])?;
assert!(len_and > 0);
assert_eq!(db.getbit("k_and", 0)?, 1);
assert_eq!(db.getbit("k_and", 1)?, 0);
assert_eq!(db.getbit("k_and", 2)?, 0);
assert_eq!(db.getbit("k_and", 8192)?, 1);
let len_or = db.bitop(BitOp::Or, "k_or", &["k1", "k2"])?;
assert!(len_or > 0);
assert_eq!(db.getbit("k_or", 0)?, 1);
assert_eq!(db.getbit("k_or", 1)?, 1);
assert_eq!(db.getbit("k_or", 2)?, 1);
assert_eq!(db.getbit("k_or", 8192)?, 1);
let len_xor = db.bitop(BitOp::Xor, "k_xor", &["k1", "k2"])?;
assert!(len_xor > 0);
assert_eq!(db.getbit("k_xor", 0)?, 0);
assert_eq!(db.getbit("k_xor", 1)?, 1);
assert_eq!(db.getbit("k_xor", 2)?, 1);
assert_eq!(db.getbit("k_xor", 8192)?, 0);
let len_not = db.bitop(BitOp::Not, "k_not", &["k1"])?;
assert!(len_not > 0);
assert_eq!(db.getbit("k_not", 0)?, 0);
assert_eq!(db.getbit("k_not", 1)?, 1);
assert_eq!(db.getbit("k_not", 2)?, 0);
assert_eq!(db.getbit("k_not", 8192)?, 0);
Ok(())
}
#[test]
fn test_bitmap_bitfield() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
let magic = 0xdeadbeefu32 as i64;
let set_op = BitfieldOperation::set(
BitfieldEncoding::unsigned(32)?,
114514,
magic,
BitfieldOverflow::Wrap,
);
let rets = db.bitfield("bfkey", &[set_op])?;
assert_eq!(rets.len(), 1);
assert_eq!(rets[0], Some(BitfieldValue::Unsigned(0)));
let get_op = BitfieldOperation::get(BitfieldEncoding::unsigned(32)?, 114514);
let rets = db.bitfield("bfkey", &[get_op])?;
assert_eq!(rets.len(), 1);
assert_eq!(rets[0], Some(BitfieldValue::Unsigned(0xdeadbeef)));
let op_cross = BitfieldOperation::set(
BitfieldEncoding::unsigned(5)?,
8189,
31,
BitfieldOverflow::Wrap,
);
let rets = db.bitfield("bfkey", &[op_cross])?;
assert_eq!(rets.len(), 1);
let get_cross = BitfieldOperation::get(BitfieldEncoding::unsigned(5)?, 8189);
let rets = db.bitfield_read_only("bfkey", &[get_cross])?;
assert_eq!(rets.len(), 1);
assert_eq!(rets[0], Some(BitfieldValue::Unsigned(31)));
let op_sat =
BitfieldOperation::incrby(BitfieldEncoding::signed(6)?, 0, 100, BitfieldOverflow::Sat);
let rets = db.bitfield("satkey", &[op_sat])?;
assert_eq!(rets[0], Some(BitfieldValue::Signed(31)));
let op_fail =
BitfieldOperation::incrby(BitfieldEncoding::signed(5)?, 0, 100, BitfieldOverflow::Fail);
let rets = db.bitfield("failkey", &[op_fail])?;
assert_eq!(rets[0], None);
Ok(())
}
#[test]
fn test_get_bitmap_bytes() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.setbit("bkey", 0, 1)?;
db.setbit("bkey", 7, 1)?;
let bytes = db.get_bitmap_bytes("bkey")?.unwrap();
assert_eq!(bytes.len(), 1);
assert_eq!(bytes[0], 0x81);
Ok(())
}
#[test]
fn test_bitmap_bitfield_all_overflows() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
let op_wrap_pos = BitfieldOperation::incrby(
BitfieldEncoding::signed(8)?,
0,
130, BitfieldOverflow::Wrap,
);
let rets = db.bitfield("bf_overflow", &[op_wrap_pos])?;
assert_eq!(rets[0], Some(BitfieldValue::Signed(-126)));
let op_sat_min = BitfieldOperation::incrby(
BitfieldEncoding::signed(8)?,
0,
-200, BitfieldOverflow::Sat,
);
let rets = db.bitfield("bf_overflow", &[op_sat_min])?;
assert_eq!(rets[0], Some(BitfieldValue::Signed(-128)));
let op_sat_max = BitfieldOperation::incrby(
BitfieldEncoding::signed(8)?,
0,
300, BitfieldOverflow::Sat,
);
let rets = db.bitfield("bf_overflow", &[op_sat_max])?;
assert_eq!(rets[0], Some(BitfieldValue::Signed(127)));
let op_usat_max = BitfieldOperation::incrby(
BitfieldEncoding::unsigned(8)?,
8,
300, BitfieldOverflow::Sat,
);
let rets = db.bitfield("bf_overflow", &[op_usat_max])?;
assert_eq!(rets[0], Some(BitfieldValue::Unsigned(255)));
let op_usat_min = BitfieldOperation::incrby(
BitfieldEncoding::unsigned(8)?,
8,
-500, BitfieldOverflow::Sat,
);
let rets = db.bitfield("bf_overflow", &[op_usat_min])?;
assert_eq!(rets[0], Some(BitfieldValue::Unsigned(0)));
let op_fail = BitfieldOperation::incrby(
BitfieldEncoding::unsigned(8)?,
8,
300, BitfieldOverflow::Fail,
);
let rets = db.bitfield("bf_overflow", &[op_fail])?;
assert_eq!(rets[0], None);
Ok(())
}
#[test]
fn test_bitmap_bitpos_extended() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
assert_eq!(db.bitpos("empty_key", 1, None, None)?, -1);
assert_eq!(db.bitpos("empty_key", 0, None, None)?, 0);
db.setbit("pos_key", 10, 1)?;
assert_eq!(db.bitpos("pos_key", 1, None, None)?, 10);
assert_eq!(
db.bitpos_opts("pos_key", 1, Some(0), Some(1), false, false)?,
10
); assert_eq!(
db.bitpos_opts("pos_key", 1, Some(2), Some(5), true, false)?,
-1
);
assert_eq!(
db.bitpos_opts("pos_key", 1, Some(0), Some(15), true, true)?,
10
);
assert_eq!(
db.bitpos_opts("pos_key", 1, Some(11), Some(20), true, true)?,
-1
);
assert_eq!(
db.bitpos_opts("pos_key", 0, Some(1), Some(2), true, false)?,
8
); assert_eq!(
db.bitpos_opts("pos_key", 0, Some(10), Some(12), true, true)?,
11
);
Ok(())
}
#[test]
fn test_bitmap_bitpos_stop_given() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
for i in 0..8 {
db.setbit("stop_key", i, 1)?;
}
let pos_stop_given = db.bitpos_opts("stop_key", 0, Some(0), Some(0), true, false)?;
assert_eq!(pos_stop_given, -1);
let pos_no_stop = db.bitpos_opts("stop_key", 0, Some(0), Some(0), false, false)?;
assert_eq!(pos_no_stop, 8);
Ok(())
}
#[test]
fn test_bitmap_bitcount_sub_byte() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.setbit("cnt_key", 2, 1)?;
db.setbit("cnt_key", 3, 1)?;
db.setbit("cnt_key", 4, 1)?;
db.setbit("cnt_key", 7, 1)?;
assert_eq!(db.bitcount_opts("cnt_key", Some(2), Some(4), true)?, 3);
assert_eq!(db.bitcount_opts("cnt_key", Some(3), Some(6), true)?, 2);
assert_eq!(db.bitcount_opts("cnt_key", Some(0), Some(1), true)?, 0);
assert_eq!(db.bitcount_opts("cnt_key", Some(7), Some(7), true)?, 1);
Ok(())
}
#[test]
fn test_bitmap_string_mode_comprehensive() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.set("str_bm", "foobar", &[])?;
assert_eq!(db.getbit("str_bm", 0)?, 0);
assert_eq!(db.getbit("str_bm", 1)?, 1);
assert_eq!(db.getbit("str_bm", 2)?, 1);
assert_eq!(db.getbit("str_bm", 3)?, 0);
assert_eq!(db.bitcount("str_bm", None, None)?, 26);
assert_eq!(db.bitcount("str_bm", Some(0), Some(0))?, 4);
assert_eq!(db.bitcount_opts("str_bm", Some(0), Some(7), true)?, 4);
assert_eq!(db.bitcount_opts("str_bm", Some(1), Some(2), true)?, 2);
assert_eq!(db.bitpos("str_bm", 1, None, None)?, 1);
assert_eq!(db.bitpos("str_bm", 0, None, None)?, 0);
assert_eq!(db.bitpos("str_bm", 1, Some(1), Some(1))?, 9);
let old = db.setbit("str_bm", 0, 1)?;
assert_eq!(old, 0);
assert_eq!(db.getbit("str_bm", 0)?, 1);
let get_u8 = BitfieldOperation::get(BitfieldEncoding::unsigned(8)?, 0);
let rets = db.bitfield_read_only("str_bm", &[get_u8])?;
assert_eq!(rets[0], Some(BitfieldValue::Unsigned(0xE6)));
db.setbit("seg_bm", 0, 1)?;
let err = db.bitop(BitOp::And, "dest", &["seg_bm", "str_bm"]);
assert!(err.is_err());
assert!(db.del_bitmap("str_bm")?);
assert_eq!(db.getbit("str_bm", 0)?, 0);
Ok(())
}
#[test]
fn test_bitmap_cleanup_and_shrink() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.setbit("bm_large", 2048 * 8, 1)?;
let bytes = db.get_bitmap_bytes("bm_large")?.unwrap();
assert!(bytes.len() > 2048);
db.setbit("bm_small", 10, 1)?;
let len = db.bitop(BitOp::Or, "bm_large", &["bm_small"])?;
assert!(len < 1024);
let bytes_after = db.get_bitmap_bytes("bm_large")?.unwrap();
assert_eq!(bytes_after.len(), len);
assert_eq!(db.getbit("bm_large", 10)?, 1);
assert_eq!(db.getbit("bm_large", 2048 * 8)?, 0);
assert!(db.del_bitmap("bm_large")?);
assert_eq!(db.get_bitmap_bytes("bm_large")?, None);
Ok(())
}
#[test]
fn test_bitmap_setbit_zero_expands_size() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
let old = db.setbit("zero_expand", 80000, 0)?;
assert_eq!(old, 0);
let bytes = db.get_bitmap_bytes("zero_expand")?.unwrap();
assert_eq!(bytes.len(), 10001);
assert_eq!(db.getbit("zero_expand", 80000)?, 0);
assert_eq!(db.bitcount("zero_expand", None, None)?, 0);
db.setbit("zero_expand", 96000, 0)?;
let bytes_after = db.get_bitmap_bytes("zero_expand")?.unwrap();
assert_eq!(bytes_after.len(), 12001);
Ok(())
}
#[test]
fn test_bitmap_bitop_and_nonexistent_key() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.setbit("k1", 79, 1)?;
let len = db.get_bitmap_bytes("k1")?.unwrap().len();
assert_eq!(len, 10);
let res_len = db.bitop(BitOp::And, "dest_and", &["k1", "k_nonexistent"])?;
assert_eq!(res_len, 10);
let dest_bytes = db.get_bitmap_bytes("dest_and")?.unwrap();
assert_eq!(dest_bytes.len(), 10);
assert_eq!(dest_bytes, vec![0u8; 10]);
assert_eq!(db.getbit("dest_and", 79)?, 0);
Ok(())
}
#[test]
fn test_bitmap_bitfield_arbitrary_widths() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
let set_i1 =
BitfieldOperation::set(BitfieldEncoding::signed(1)?, 0, -1, BitfieldOverflow::Wrap);
let rets = db.bitfield("bf_widths", &[set_i1])?;
assert_eq!(rets[0], Some(BitfieldValue::Signed(0)));
let get_i1 = BitfieldOperation::get(BitfieldEncoding::signed(1)?, 0);
let rets = db.bitfield_read_only("bf_widths", &[get_i1])?;
assert_eq!(rets[0], Some(BitfieldValue::Signed(-1)));
let max_u63 = (1u64 << 63) - 1;
let set_u63 = BitfieldOperation::set(
BitfieldEncoding::unsigned(63)?,
1,
max_u63 as i64,
BitfieldOverflow::Wrap,
);
let rets = db.bitfield("bf_widths", &[set_u63])?;
assert_eq!(rets[0], Some(BitfieldValue::Unsigned(0)));
let get_u63 = BitfieldOperation::get(BitfieldEncoding::unsigned(63)?, 1);
let rets = db.bitfield_read_only("bf_widths", &[get_u63])?;
assert_eq!(rets[0], Some(BitfieldValue::Unsigned(max_u63)));
let min_i64 = i64::MIN;
let set_i64 = BitfieldOperation::set(
BitfieldEncoding::signed(64)?,
100,
min_i64,
BitfieldOverflow::Wrap,
);
let rets = db.bitfield("bf_widths", &[set_i64])?;
assert_eq!(rets[0], Some(BitfieldValue::Signed(0)));
let get_i64 = BitfieldOperation::get(BitfieldEncoding::signed(64)?, 100);
let rets = db.bitfield_read_only("bf_widths", &[get_i64])?;
assert_eq!(rets[0], Some(BitfieldValue::Signed(min_i64)));
Ok(())
}
#[test]
fn test_bitmap_cross_segment_bitpos_and_bitcount() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.setbit("cross_key", 8191, 1)?;
db.setbit("cross_key", 8192, 1)?;
db.setbit("cross_key", 16383, 1)?; db.setbit("cross_key", 16384, 1)?;
assert_eq!(db.bitcount("cross_key", None, None)?, 4);
assert_eq!(db.bitcount("cross_key", Some(0), Some(1023))?, 1); assert_eq!(db.bitcount("cross_key", Some(1024), Some(2047))?, 2); assert_eq!(db.bitcount("cross_key", Some(2048), Some(2048))?, 1);
assert_eq!(db.bitpos("cross_key", 1, None, None)?, 8191);
assert_eq!(
db.bitpos_opts("cross_key", 1, Some(8192), Some(16383), true, true)?,
8192
);
assert_eq!(
db.bitpos_opts("cross_key", 1, Some(8193), Some(16383), true, true)?,
16383
);
assert_eq!(
db.bitpos_opts("cross_key", 1, Some(16384), Some(20000), true, true)?,
16384
);
assert_eq!(db.bitpos("cross_key", 0, None, None)?, 0);
assert_eq!(
db.bitpos_opts("cross_key", 0, Some(8190), Some(8192), true, true)?,
8190
);
Ok(())
}
#[test]
fn test_bitmap_bitfield_multiple_commands_in_sequence() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
let ops = [
BitfieldOperation::set(
BitfieldEncoding::unsigned(8)?,
0,
100,
BitfieldOverflow::Wrap,
),
BitfieldOperation::incrby(
BitfieldEncoding::unsigned(8)?,
0,
50,
BitfieldOverflow::Wrap,
),
BitfieldOperation::get(BitfieldEncoding::unsigned(8)?, 0),
BitfieldOperation::set(
BitfieldEncoding::signed(16)?,
8,
-500,
BitfieldOverflow::Wrap,
),
BitfieldOperation::incrby(
BitfieldEncoding::signed(16)?,
8,
1000,
BitfieldOverflow::Wrap,
),
BitfieldOperation::get(BitfieldEncoding::signed(16)?, 8),
];
let rets = db.bitfield("seq_bf", &ops)?;
assert_eq!(rets.len(), 6);
assert_eq!(rets[0], Some(BitfieldValue::Unsigned(0))); assert_eq!(rets[1], Some(BitfieldValue::Unsigned(150))); assert_eq!(rets[2], Some(BitfieldValue::Unsigned(150)));
assert_eq!(rets[3], Some(BitfieldValue::Signed(0))); assert_eq!(rets[4], Some(BitfieldValue::Signed(500))); assert_eq!(rets[5], Some(BitfieldValue::Signed(500)));
Ok(())
}
#[test]
fn test_bitmap_bitpos_with_unallocated_segment_and_holes() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.setbit("hole_bm", 16384, 1)?;
assert_eq!(
db.bitpos_opts("hole_bm", 0, Some(0), Some(8192), true, true)?,
0
);
assert_eq!(
db.bitpos_opts("hole_bm", 0, Some(100), Some(8192), true, true)?,
100
);
assert_eq!(
db.bitpos_opts("hole_bm", 1, Some(0), Some(8192), true, true)?,
-1
);
assert_eq!(
db.bitpos_opts("hole_bm", 1, Some(10000), Some(20000), true, true)?,
16384
);
assert_eq!(
db.bitpos_opts("hole_bm", 0, Some(16384), Some(16385), true, true)?,
16385
);
Ok(())
}
#[test]
fn test_bitmap_bitop_multi_key_complex() -> Void {
let dir = tempdir()?;
let db = WeDb::open(dir.path())?;
db.setbit("mk1", 0, 1)?;
db.setbit("mk1", 8192, 1)?;
db.setbit("mk2", 1, 1)?;
db.setbit("mk2", 8192, 1)?;
db.setbit("mk3", 2, 1)?;
db.setbit("mk3", 8192, 1)?;
db.setbit("mk4", 3, 1)?;
db.setbit("mk4", 8192, 1)?;
let len_or = db.bitop(BitOp::Or, "mk_or", &["mk1", "mk2", "mk3", "mk4"])?;
assert!(len_or >= 1025);
assert_eq!(db.getbit("mk_or", 0)?, 1);
assert_eq!(db.getbit("mk_or", 1)?, 1);
assert_eq!(db.getbit("mk_or", 2)?, 1);
assert_eq!(db.getbit("mk_or", 3)?, 1);
assert_eq!(db.getbit("mk_or", 4)?, 0);
assert_eq!(db.getbit("mk_or", 8192)?, 1);
let len_and = db.bitop(BitOp::And, "mk_and", &["mk1", "mk2", "mk3", "mk4"])?;
assert!(len_and >= 1025);
assert_eq!(db.getbit("mk_and", 0)?, 0);
assert_eq!(db.getbit("mk_and", 1)?, 0);
assert_eq!(db.getbit("mk_and", 2)?, 0);
assert_eq!(db.getbit("mk_and", 3)?, 0);
assert_eq!(db.getbit("mk_and", 8192)?, 1);
Ok(())
}