AVSystem / Anjay

C implementation of the client-side OMA LwM2M protocol
Other
188 stars 68 forks source link

get string resource length in resource_write callback #48

Closed christopheronco closed 3 years ago

christopheronco commented 3 years ago

Hello,

In resource_write, when resource is a string, how to get resource length? I have a string resource, I don't know its length and I don't want to guess and do multiple malloc/realloc until anjay_get_string does not fail. The goal is to do something like that (I did not compile this code):

static int resource_write(anjay_t *anjay,
                          const anjay_dm_object_def_t *const *obj_ptr,
                          anjay_iid_t iid,
                          anjay_rid_t rid,
                          anjay_riid_t riid,
                          anjay_input_ctx_t *ctx) {
    (void) anjay;

    time_object_t *obj = get_obj(obj_ptr);
    assert(obj);
    time_instance_t *inst = find_instance(obj, iid);
    assert(inst);

    switch (rid) {
    case RID_APPLICATION_TYPE:
        assert(riid == ANJAY_ID_INVALID);
        length = anjay_get_string_length(ctx);
        if (inst->application_type)
            free(inst->application_type);
        inst->application_type = malloc(length + 1);
        return anjay_get_string(ctx, inst->application_type, length+1);

    default:
        return ANJAY_ERR_METHOD_NOT_ALLOWED;
    }
}

But I didn't see any equivalent to "anjay_get_string_length" in Anjay code. Am I missing something obvious?

Best Regards,

Christophe Ronco

kFYatek commented 3 years ago

Hi Christophe,

Unfortunately, there is currently no API that would provide the functionality you describe.

Please also note that there are some corner cases in which the length of the string may not be immediately available - most notably, if a long string is received using CoAP block-wise transfer, its length is unknown.

We might consider refactoring string handling in some future version to make your desired code flow possible, but for now it is not possible.

christopheronco commented 3 years ago

Thanks for your answer. I went for the malloc(MAX_SIZE) approach. That's enough for my test client right now, but that's quite bad for production code.

In case of CoAP block-wise transfer, we are able to read only part of the resource data in resource_write callback? How do we know that's not the complete resource data? Maybe this is explained in advanced topic about firmware update, I did not read it yet.

kFYatek commented 3 years ago

I believe that the best approach for your case with the current API would be to either do malloc(MAX_SIZE) and then realloc() to the final length after reading resource data - or use malloc(MIN_SIZE) and then extend the buffer with realloc() in a loop as necessary - the anjay_get_string() function supports further reads if the initial buffer is too small.

I believe that also explains your question about reading data in part - this is explained in detail in the API documentation (see https://avsystem.github.io/Anjay-doc/api/io_8h.html#a9b2b4670b7b04d43f23cb6e9919e83ca for string resources and https://avsystem.github.io/Anjay-doc/api/io_8h.html#a8f964be4881dd0fd3310be7841993a2b for binary resources). Example usage of anjay_get_bytes() is also provided in the same documentation. There is no example for using anjay_get_string() in a loop, but the idea is something like this (allocation error handling omitted for clarity):

const size_t CHUNK_SIZE = 64;
char *result = malloc(CHUNK_SIZE);
size_t index = 0;
int retval;
while ((retval = anjay_get_string(ctx, result + index, CHUNK_SIZE))
        == ANJAY_BUFFER_TOO_SHORT) {
    assert(strlen(result + index) == CHUNK_SIZE - 1);
    index += CHUNK_SIZE - 1;
    result = realloc(result, index + CHUNK_SIZE);
}

Basically it's a concatenation of strings "returned" each time by the anjay_get_string() function.

christopheronco commented 3 years ago

Thanks, I think we can close this subject.