iotivity 0.9.0
[platform/upstream/iotivity.git] / resource / csdk / connectivity / lib / libcoap-4.1.1 / block.c
1 /* block.c -- block transfer
2  *
3  * Copyright (C) 2010--2012 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * This file is part of the CoAP library libcoap. Please see
6  * README for terms of use. 
7  */
8
9 #include "config.h"
10
11 #if defined(HAVE_ASSERT_H) && !defined(assert)
12 # include <assert.h>
13 #endif
14
15 #include "debug.h"
16 #include "block.h"
17
18 #define min(a,b) ((a) < (b) ? (a) : (b))
19
20 #ifndef WITHOUT_BLOCK
21 unsigned int coap_opt_block_num(const coap_opt_t *block_opt)
22 {
23     unsigned int num = 0;
24     unsigned short len;
25
26     len = coap_opt_length(block_opt);
27
28     if (len == 0)
29     {
30         return 0;
31     }
32
33     if (len > 1)
34     {
35         num = coap_decode_var_bytes(COAP_OPT_VALUE(block_opt), COAP_OPT_LENGTH(block_opt) - 1);
36     }
37
38     return (num << 4) | ((*COAP_OPT_BLOCK_LAST(block_opt) & 0xF0) >> 4);
39 }
40
41 int coap_get_block(coap_pdu_t *pdu, unsigned short type, coap_block_t *block)
42 {
43     coap_opt_iterator_t opt_iter;
44     coap_opt_t *option;
45
46     assert(block);
47     memset(block, 0, sizeof(coap_block_t));
48
49     if (pdu && (option = coap_check_option(pdu, type, &opt_iter)))
50     {
51         block->szx = COAP_OPT_BLOCK_SZX(option);
52         if (COAP_OPT_BLOCK_MORE(option))
53             block->m = 1;
54         block->num = coap_opt_block_num(option);
55         return 1;
56     }
57
58     return 0;
59 }
60
61 int coap_write_block_opt(coap_block_t *block, unsigned short type, coap_pdu_t *pdu,
62         size_t data_length)
63 {
64     size_t start, want, avail;
65     unsigned char buf[3];
66
67     assert(pdu);
68
69     /* Block2 */
70     if (type != COAP_OPTION_BLOCK2)
71     {
72         warn("coap_write_block_opt: skipped unknown option\n");
73         return -1;
74     }
75
76     start = block->num << (block->szx + 4);
77     if (data_length <= start)
78     {
79         debug("illegal block requested\n");
80         return -2;
81     }
82
83     avail = pdu->max_size - pdu->length - 4;
84     want = 1 << (block->szx + 4);
85
86     /* check if entire block fits in message */
87     if (want <= avail)
88     {
89         block->m = want < data_length - start;
90     }
91     else
92     {
93         /* Sender has requested a block that is larger than the remaining
94          * space in pdu. This is ok if the remaining data fits into the pdu
95          * anyway. The block size needs to be adjusted only if there is more
96          * data left that cannot be delivered in this message. */
97
98         if (data_length - start <= avail)
99         {
100
101             /* it's the final block and everything fits in the message */
102             block->m = 0;
103         }
104         else
105         {
106             unsigned char szx;
107
108             /* we need to decrease the block size */
109             if (avail < 16)
110             { /* bad luck, this is the smallest block size */
111                 debug("not enough space, even the smallest block does not fit");
112                 return -3;
113             }
114             debug("decrease block size for %d to %d\n", avail, coap_fls(avail) - 5);
115             szx = block->szx;
116             block->szx = coap_fls(avail) - 5;
117             block->m = 1;
118             block->num <<= szx - block->szx;
119         }
120     }
121
122     /* to re-encode the block option */
123     coap_add_option(pdu, type,
124             coap_encode_var_bytes(buf, ((block->num << 4) | (block->m << 3) | block->szx)), buf);
125
126     return 1;
127 }
128
129 int coap_add_block(coap_pdu_t *pdu, unsigned int len, const unsigned char *data,
130         unsigned int block_num, unsigned char block_szx)
131 {
132     size_t start;
133     start = block_num << (block_szx + 4);
134
135     if (len <= start)
136         return 0;
137
138     return coap_add_data(pdu, min(len - start, (unsigned int)(1 << (block_szx + 4))), data + start);
139 }
140 #endif /* WITHOUT_BLOCK  */