Hi,

I've refactored Nimble code to use dedicated type for UUID handling instead
of generic byte-array. Full diff is available here (it is split into
several patches for now to separate changes in stack and apps):

https://github.com/andrzej-kaczmarek/incubator-mynewt-core/c
ompare/develop...andrzej-kaczmarek:nimble/uuid

Start by looking at ble_uuid.h which defines new structures and helpers for
UUIDs (or see below where I included essential part):

https://github.com/andrzej-kaczmarek/incubator-mynewt-core/b
lob/1f571c7aeb22eb4e2ae96ca6d01d612afb338fd4/net/nimble/host
/include/host/ble_uuid.h

Now, let me explain what it is all about:

Current implementation handles all UUIDs as long 128-bit values so they are
passed like this (byte-array) in API and stored internally. This creates
overhead for handling short 16-bit UUIDs in terms of both memory and
processing time because:
- short values are stored as long ones anyway,
- they have to be converted back and forth since short UUID cannot be put
as long one in ATT PDU,
- there is no generic code to handle the above so similar patterns are
repeated over and over again.

With new approach there is dedicated type to pass UUID which contains also
its type so this information is available immediately - this is used e.g.
BlueZ and Zephyr.

enum {
    BLE_UUID_TYPE_16 = 16,
    BLE_UUID_TYPE_32 = 32,
    BLE_UUID_TYPE_128 = 128,
};

/* Generic UUID type, to be used only as a pointer */
typedef struct {
    uint8_t type;
} ble_uuid_t;

typedef struct {
    ble_uuid_t u;
    uint16_t value;
} ble_uuid16_t;

typedef struct {
    ble_uuid_t u;
    uint32_t value;
} ble_uuid32_t;

typedef struct {
    ble_uuid_t u;
    uint8_t value[16];
} ble_uuid128_t;

/* Universal UUID type, to be used for any-UUID static allocation */
typedef union {
    ble_uuid_t u;
    ble_uuid16_t u16;
    ble_uuid32_t u32;
    ble_uuid128_t u128;
} ble_uuid_any_t;

This particular approach to handle UUIDs comes from Zephyr which uses neat
trick to have common type for any UUID yet it allows to store 16-bit values
using less memory than for 128-bit values. There are dedicated types for
16- and 128-bit UUIDs so memory for an UUID can be allocated, but the API
uses "stub" type which defines only UUID type and is used as a pointer to
an actual UUID variable. You can use helpers to get UUID value or compare
them quickly without need to convert between 16- and 128-bit values. See
following examples how this works (or see sample apps):

// 16-bit UUID value with initialization
ble_uuid16_t uuid16 = BLE_UUID16_INIT(0x2801);
// 128-bit UUID value with initialization
ble_uuid128_t uuid128 = BLE_UUID128_INIT(0x00, 0x11, ... 0xFF);

// "stub" type is used to pass any type of UUID via API
ble_uuid_t *uuid1 = &uuid16.u;
ble_uuid_t *uuid2 = &uuid128.u;

// comparing UUIDs is really easy now
if (ble_uuid_cmp(uuid1, uuid2) == 0) { /* equal */ )

// since in most cases we use 16-bit UUIDs, it's convenient to easily
retrieve 16-bit value (or 0 if UUID is not 16-bit)
uint16_t u16 = ble_uuid_u16(uuid1);

// the above is equivalent of
u16 = uuid1->type == BLE_UUID_TYPE_16 ? BLE_UUID16(uuid1)->value : 0;

// we can easily define UUID inline as well, e.g. when defining service
static const struct ble_gatt_svc_def ble_svc_gap_defs[] = {
    {
        .type = BLE_GATT_SVC_TYPE_PRIMARY,
        .uuid = BLE_UUID16_DECLARE(BLE_SVC_GAP_UUID16),
(...)

// finally, if we need to store any UUID there is an union defined to help
ble_uuid_any_t uuid;
uuid.u16 = uuid16;

*IMPORTANT NOTE*:
There are two major differences between old and new API:
- Shen defining and registering services, in old API UUID value is copied
to attribute structure so it does not matter where UUID passed as parameter
is defined. In new API only pointer to UUID is stored thus you cannot e.g.
define value on stack since it will be destroyed.
- You need to make sure that 16-bit UUIDs are defined using helpers
dedicated for 16-bit UUIDs only, i.e. you should not create 128-bit UUID
with Bluetooth base since it will not be detected as 16-bit UUID.

Finally, there are of course some savings in both flash and RAM:

before:
<     136     286 *fill*
<   30738    5027 apps_bletiny.a
<   53633    3411 net_nimble_host.a
<     880     290 net_nimble_host_services_ans.a
<     496     289 net_nimble_host_services_gap.a
<     306      86 net_nimble_host_services_gatt.a
<  160884       2784      15828     179496      2bd28
/home/andk/devel/mynewt/bletiny/bin/targets/bletiny/
app/apps/bletiny/bletiny.elf

after:
>     143     364 *fill*
>   30470    5053 apps_bletiny.a
>   53409    3415 net_nimble_host.a
>     880     218 net_nimble_host_services_ans.a
>     496     217 net_nimble_host_services_gap.a
>     306      62 net_nimble_host_services_gatt.a
>  160396       2724      15828     178948      2bb04
/home/andk/devel/mynewt/bletiny/bin/targets/bletiny/
app/apps/bletiny/bletiny.elf

Comments are welcome! :-)

BR,
Andrzej

Reply via email to