Question

I'm using leveldb to store key-value pairs of integer and MyClass objects. Actually, a key can contain more then one of theses objects. The problem I have appears when retrieving the data from the database. It compiles, however the values of the MyClass members are not the one I put into the database.

std::string value;
leveldb::Slice keySlice = ANYKEY;
levelDBObj->Get(leveldb::ReadOptions(), keySlice, &value);

The std::string value1 can now contain only one MyClass object or more. So how do I get them?

I already tried the following which didn't work;

1.) directly typecasting and memcpy

std::vector<MyClass> vObjects;
MyClass* obj = (MyClass*)malloc( value.size());
memcpy((void*)obj, (void*) (value.c_str()), value.size());
MyClass dummyObj;
int numValues = value.size()/sizeof(MyClass);
for( int i=0; i<numValues; ++i) {
    dummyObj = *(obj+i);
    vObjects.push_back(dummyObj);
}

2.) reinterpret_cast to void pointer

MyClass* obj = (MyClass*)malloc( value.size());
const void* vobj = reinterpret_cast<const void*>( value.c_str() );
int numValues = value.size()/sizeof(MyClass);
for( int i=0; i<numValues; ++i) {
    const MyClass dummyObj = *(reinterpret_cast<const MyClass*>(vobj)+i);
    vObjects.push_back(dummyObj);
}

MyClass is a collection of several public members, e.g. unsigned int and unsigned char and it has a stable size.

I know that there are similar problems with only one object. But in my case the vector can contain more then one and it comes from the leveldb database.

EDIT: SOLUTION

I wrote (de)serialization method for MyClass which then made it working. Thanks for the hint!

void MyClass::serialize( char* outBuff ) {
    memcpy(outBuff, (const void*) &aVar, sizeof(aVar));
    unsigned int c = sizeof(aVar);
    memcpy(outBuff+c, (const void*) &bVar, sizeof(bVar));
    c += sizeof(bVAr);
    /* and so on */
}

void MyClass::deserialize( const char* inBuff ) {
    memcpy((void*) &aVar, inBuff, sizeof(aVar));
    unsigned int c = sizeof(aVar);
    memcpy((void*) &aVar, inBuff+c, sizeof(aVar));
    c += sizeof(aVar);
    /* and so on */
}

The get method is as follows (put analogously):

int getValues(leveldb::Slice keySlice, std::vector<MyObj>& values) const {
    std::string value;  
    leveldb::Status status = levelDBObj->Get(leveldb::ReadOptions(), keySlice, &value);
    if (!status.ok()) {
        values.clear();
            return -1;
    }

    int nValues = value1.size()/sizeof(CHit);
    MyObj dummyObj;
    for( int i=0; i<nValues; ++i) {
        dummyObj.deserialize(value.c_str()+i*sizeof(MyObj));
        values.push_back(dummyObj);
    }

    return 0;
}               
Was it helpful?

Solution

You have to serialize your class... otherwise, you're just taking some memory and writing it in leveldb. Whatever you get back is not only going to be different, but it will probably be completely useless too. Check out this question for more info on serialization: How do you serialize an object in C++?

LevelDB does support multiple objects under one key, however, try to avoid doing that unless you have a really good reason. I would recommend that you hash each object with a unique hash (see Google's CityHash if you want a hashing function) and store the serialized objects with their corresponding hash. If your objects is a collection in itself, then you have to serialize all of your objects to an array of bytes and have some method that allows you to determine where each object begins/ends.

Update

A serializable class would look something like this:

class MyClass
{
private:
    int _numeric;
    string _text;
public:
    // constructors 

    // mutators
    void SetNumeric(int num);
    void SetText(string text);

    static unsigned int SerializableSize()
    {
        // returns the serializable size of the class with the schema:
        // 4 bytes for the numeric (integer) 
        // 4 bytes for the unsigned int (the size of the text)
        // n bytes for the text (it has a variable size)
        return sizeof(int) + sizeof(unsigned int) + _text.size();
    }

    // serialization
    int Serialize(const char* buffer, const unsigned int bufferLen, const unsigned int position)
    {
        // check if the object can be serialized in the available buffer space
        if(position+SerializableSize()>bufferLen)
        {
            // don't write anything and return -1 signaling that there was an error
            return -1;
        }

        unsigned int finalPosition = position;

        // write the numeric value
        *(int*)(buffer + finalPosition) = _numeric;

        // move the final position past the numeric value
        finalPosition += sizeof(int);

        // write the size of the text
        *(unsigned int*)(buffer + finalPosition) = (unsigned int)_text.size();

        // move the final position past the size of the string
        finalPosition += sizeof(unsigned int);

        // write the string
        memcpy((void*)(buffer+finalPosition), _text.c_str(), (unsigned int)_text.size());

        // move the final position past the end of the string
        finalPosition += (unsigned int)_text.size();

        // return the number of bytes written to the buffer
        return finalPosition-position;
    }

    // deserialization
    static int Deserialize(MyClass& myObject, 
        const char* buffer, 
        const unsigned int buffSize, 
        const unsigned int position)
    {
        insigned int currPosition = position;

        // copy the numeric value
        int numeric = *(int*)(buffer + currentPosition);

        // increment the current position past the numeric value
        currentPosition += sizeof(int);

        // copy the size of the text
        unsigned int textSize = *(unsigned int*)(buffer + currentPosition);

        // increment the current position past the size of the text
        currentPosition += sizeof(unsigned int);

        // copy the text
        string text((buffer+currentPosition), textSize);

        if(currentPosition > buffSize)
        {
            // you decide what to do here
        }

        // Set your object's values
        myObject.SetNumeric(numeric);
        myObject.SetText(text);

        // return the number of bytes deserialized
        return currentPosition - position;
    }
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top