leapfrog icon indicating copy to clipboard operation
leapfrog copied to clipboard

Inserting fails unpredictably with structs & alignment

Open otisdog8 opened this issue 6 months ago • 2 comments

For some reason, inserting will either freeze or will not properly insert if the struct isn't a multiple of 64 bits in size.

See code below:

If you change b to a u64, the code works as expected.

use std::fmt::{Debug};
use lazy_static::lazy_static;
use leapfrog::{LeapMap, Value};


#[derive(Debug, Copy, Clone, PartialEq)]
struct TestStruct {
    a: u64,
    b: u32,
}

macro_rules! value_impl {
    ($type:ty, $redirect_expr:expr, $null_expr:expr) => {
        impl Value for $type {
            #[inline]
            fn is_redirect(&self) -> bool {
                *self == $redirect_expr
            }
            #[inline]
            fn is_null(&self) -> bool {
                *self == $null_expr
            }
            #[inline]
            fn redirect() -> Self {
                $redirect_expr
            }
            #[inline]
            fn null() -> Self {
                $null_expr
            }
        }
    };
}

value_impl!(TestStruct, TestStruct {a: u64::MAX, b: 0 }, TestStruct {a: u64::MAX - 1, b: 0});

lazy_static! {
    static ref A_MAP: LeapMap<u64, TestStruct> = LeapMap::new();
    static ref B_MAP: LeapMap<u64, u64> = LeapMap::new();
}

fn main() {
    let C_MAP: LeapMap<u64, TestStruct> = LeapMap::new();
    let D_MAP: LeapMap<u64, u64> = LeapMap::new();

    let mut key = 0;
    for i in 0..100 {
        key += 1;
        let result = A_MAP.insert(key, TestStruct { a: key, b: 0});
        println!("Inserting new key {} sz {} res {:?}", key, A_MAP.len(), result);
        let result = C_MAP.insert(key, TestStruct { a: key, b: 0});
        println!("Inserting new key {} sz {} res {:?}", key, C_MAP.len(), result);
        let result = B_MAP.insert(key, key);
        println!("Inserting new key {} sz {} res {:?}", key, B_MAP.len(), result);
        let result = D_MAP.insert(key, key);
        println!("Inserting new key {} sz {} res {:?}", key, D_MAP.len(), result);

    }

    println!("Hello, world!");
}

otisdog8 avatar Dec 18 '23 07:12 otisdog8

Hi,

Thanks for this. I'm not immediately sure what's causing this, but I will try and get to it as soon as possible. Mostly likely this weekend.

robclu avatar Dec 19 '23 04:12 robclu

The problem also exists for keys, and even for those that are a multiple of 64 bits. See this example:

use leapfrog::{LeapMap, Value};

pub type Uuid = u128;
fn main() {
    let map = LeapMap::<Uuid, Token>::new();

    let key = Uuid::MAX / 2;
    let value = Token {
        token_type: TokenType::One,
        index: 0,
    };
    map.insert(key, value);

    let v = map.get(&key);
    println!("{:?}", v.map(|mut r| r.value()));
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Token {
    token_type: TokenType,
    index: usize,
}

#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u32)]
pub enum TokenType {
    One,
    Two,

    Redirect = u32::MAX - 1,
    Null = u32::MAX,
}

impl Value for Token {
    fn null() -> Token {
        Token {
            token_type: TokenType::Null,
            index: usize::MAX,
        }
    }
    fn redirect() -> Self {
        Token {
            token_type: TokenType::Redirect,
            index: usize::MAX - 1,
        }
    }

    fn is_null(&self) -> bool {
        *self == Self::null()
    }

    fn is_redirect(&self) -> bool {
        *self == Self::redirect()
    }
}

This always fails for me (on ARM — a macbook with M2 Max). However, if I switch Uuid to a u64 it works.

edit: Failing in this context means that the get returns None, despite having just inserted

cavemanloverboy avatar Jan 24 '24 23:01 cavemanloverboy