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