ludocode / mpack

MPack - A C encoder/decoder for the MessagePack serialization format / msgpack.org[C]
MIT License
521 stars 82 forks source link

How to change the data with node API? Or how to write fast when I know the data offset or giving node? #90

Closed vincentguiling closed 2 years ago

vincentguiling commented 2 years ago

Recently, I found this useful repository to operate msgPack. And I read the msgPack in a giving file with node API. However, when I try to change data, it seems that node API can't change data in current node, and write API can change but only after iterating to current node. How can I change data with giving node? Or to say the least, are there any methods to change from mpack_node_t to mpack_writer_t which in the right position?

This is my msgPack file(after compressing). I make a combo box that storage the operating node (e.g. "run mode") in my embedded device. And when the index is changed, how can I save the new index to file as fast as possible?

Thanks,

vincentguiling commented 2 years ago

I write a converter from mpack_node_t to mpack_writer_t. It works well (except mpack_type_ext which I didn't use) and if currentNode->type is an array or a map, it will write all elements (key, value) by nested operations, which means finish function has been called automatically. It originate from (mpack_writer_t* writer, mpack_tag_t tag). And it only support change number to number.

example:

...//change node data externally
write_data_from_tree(writer, mpack_tree_root(tree));
void write_data_from_tree(mpack_writer_t* writer, mpack_node_t currentNode)
{
    mpack_node_data_t *value = currentNode.data;
    switch (value->type) {
        case mpack_type_missing:
            mpack_break("cannot write a missing value!");
            mpack_writer_flag_error(writer, mpack_error_bug);
            return;

        case mpack_type_nil:    mpack_write_nil   (writer);            return;
        case mpack_type_bool:   mpack_write_bool  (writer, value->value.b); return;
        case mpack_type_int:    mpack_write_int   (writer, value->value.i); return;
        case mpack_type_uint:   mpack_write_uint  (writer, value->value.u); return;

        case mpack_type_float:
            #if MPACK_FLOAT
            mpack_write_float
            #else
            mpack_write_raw_float
            #endif
                (writer, value->value.f);
            return;
        case mpack_type_double:
            #if MPACK_DOUBLE
            mpack_write_double
            #else
            mpack_write_raw_double
            #endif
                (writer, value->value.d);
            return;

        case mpack_type_str: mpack_write_str(writer, tree.data+value->value.offset, value->len); return;
        case mpack_type_bin: mpack_write_bin(writer, tree.data+value->value.offset, value->len); return;

        #if MPACK_EXTENSIONS
        case mpack_type_ext:
        #error data not include ext!
            mpack_start_ext(writer, mpack_tag_ext_exttype(&value), mpack_tag_ext_length(&value));
            return;
        #endif

        case mpack_type_array: 
            mpack_start_array(writer, value->len); 
            for(uint32_t i = 0; i < value->len; i++)
            {
                write_data_from_tree(writer, mpack_node(&tree, mpack_node_child(currentNode, i)));
            }
            mpack_finish_array(writer);
        return;
        case mpack_type_map:   
            mpack_start_map(writer, value->len);
            for(uint32_t i = 0; i < value->len; i++)
            {
                write_data_from_tree(writer, mpack_node(&tree, mpack_node_child(currentNode, i*2)));
                write_data_from_tree(writer, mpack_node(&tree, mpack_node_child(currentNode, i*2+1)));
            }
            mpack_finish_array(writer);
        return;
    }
    mpack_break("unrecognized type %i", (int)value.type);
    mpack_writer_flag_error(writer, mpack_error_bug);
}
ludocode commented 2 years ago

Sorry, the node API parse tree is meant to be immutable. It's heavily optimized for this. I would strongly recommend against changing anything in the node API parse tree.

Making it mutable would drastically alter its efficiency. I've done extensive benchmarking of this in the past. Jansson is an example of a JSON parser that builds a mutable tree and it is the slowest parser on the list.

I don't currently have code to load MessagePack into a mutable tree that can be modified and written out again. If you wanted to implement something like this, it's not very hard. You could make your own mutable tree node struct and use the reader API to build it and the writer API to write it out again. It would only take a hundred or so lines of code.