Writing to something which has been opened from opcContainerOpenMem

Nov 21, 2014 at 12:22 AM
I have a OPC container stored in memory, which I want to modify, and then save out to file.

I can open the container using opcContainerOpenMem, but once I make modifications there doesn't seem to be a way to get the data out once I've made those modifications. If I opcContainerClose it destroys the memory. I need to be able to flush and close the opcZip, access the data to write it out to memory and then free up the memory.

What's the recommended way to do this?
Coordinator
Nov 21, 2014 at 10:05 PM
Hi,

the memory container is currently read-only.
But you can easily create your own "writable" container. Have a look at opcContainerOpenMem. It will init a container and set the IO functions via opcFileInitIOMemory. Just roll your own opcFileInitIOMemory function and use the opcContainerOpenMem as a boiler plate. All you need to do is to implement your own ioread, iowrite, ioclose, ioseek, iotrim and ioflush callbacks. In the ioclose just do NOT delete the mem and you have a writable container.

Hope that helps (if not please let me know),

Florian
Dec 5, 2016 at 6:55 AM
Let me show how I implemented "own writable container".
The base for ioread, iowrite, ioclose, ioseek, iotrim and ioflush callbacks is opcMemRead, opcMemWrite, opcMemClose, opcMemSeek, opcMemTrim and opcMemFlush defined in file.c in libopc sources.
I have changed opcMemWrite, opcMemClose and opcMemTrim callbacks, while opcMemRead, opcMemSeek and opcMemFlush callbacks have been not changed.
From opcMemClose I removed all except return 0; as flr wrote.
opcMemWrite is similar to opcMemRead but we should copy from buffer to memory context:
int opcMemWrite(void *iocontext, const char *buffer, int len)
{
    struct __opcZipMemContext *mem = (struct __opcZipMemContext*)iocontext;

    opc_uint32_t max = mem->data_pos + len <= mem->data_len 
        ? len 
        : mem->data_len - mem->data_pos;

    OPC_ASSERT(max >= 0 && mem->data_pos + max <= mem->data_len);

    memcpy((void *)(mem->data + mem->data_pos), buffer, max);
    mem->data_pos += max;

    return max;
}
opcMemTrim should work similar to opcFileTrim callback but respect to the memory (for WIN32 it's similar to _chsize):
int opcMemTrim(void *iocontext, opc_ofs_t new_size)
{
    struct __opcZipMemContext *mem = (struct __opcZipMemContext*)iocontext;

    unsigned char *new_data = new unsigned char[new_size];
    int savedDataLen = new_size >= mem->data_len ? mem->data_len : new_size;
    memcpy(new_data, mem->data, savedDataLen);
    xmlFree(mem);

    iocontext = opcMemOpen(new_data, new_size);

    return 0;
}
After defining own callbacks I created the container like this:
_memoryContext = opcMemOpen(buffer, size);
_container = opcContainerOpenIO(opcMemRead, opcMemWrite, opcMemClose, 
        opcMemSeek, opcMemTrim, opcMemFlush, _memoryContext, size, 
        OPC_OPEN_READ_WRITE, NULL);
After working with the container one can save its content to file:
opcContainerClose(doc, OPC_CLOSE_TRIM);
FILE *file = fopen(filename, "wb");
struct __opcZipMemContext *mem = (struct __opcZipMemContext*)_memoryContext;
fwrite(mem->data, sizeof(char), mem->data_len, file);
fclose(file);
xmlFree(_memoryContext);