size_t concat_strings_v(char *dst, size_t size, const char *s, va_list ap) { char *p = dst; size_t ret = 0; g_assert(0 == size || NULL != dst); if (size > 0) { if (!s) *p = '\0'; while (NULL != s) { size_t len; len = g_strlcpy(p, s, size); ret = size_saturate_add(ret, len); s = va_arg(ap, const char *); size = size_saturate_sub(size, len); if (0 == size) break; p += len; } } while (NULL != s) { ret = size_saturate_add(ret, strlen(s)); s = va_arg(ap, const char *); } g_assert(ret < SIZE_MAX); return ret; }
/** * Checks whether appending `len' bytes of data to the header would fit * within the maximum header size requirement in case a continuation * is emitted, and using the configured separator. */ bool header_fmt_value_fits(const header_fmt_t *hf, size_t len) { size_t final_len; size_t maxlen, n; header_fmt_check(hf); if (hf->empty) return FALSE; maxlen = size_saturate_sub(hf->max_size, sizeof("\r\n")); /* * If it fits on the line, no continuation will have to be emitted. * Otherwise, we'll need the stripped version of the separator, * followed by "\r\n\t" (3 chars). */ final_len = size_saturate_add(str_len(hf->header), len); n = size_saturate_add(hf->current_len, size_saturate_add(len, hf->seplen)); if (n <= hf->maxlen) { final_len = size_saturate_add(final_len, hf->seplen); } else { final_len = size_saturate_add(final_len, hf->stripped_seplen); final_len = size_saturate_add(final_len, 3); } return final_len < maxlen; /* Could say "<=" perhaps, but let's be safe */ }
/** * Allocate a single block in the file, without extending it. * * @param db the sdbm database * @param first first block to consider * * @return the block number if found, 0 otherwise. */ static size_t big_falloc(DBM *db, size_t first) { DBMBIG *dbg = db->big; long max_bitmap = dbg->bitmaps; long i; long bmap; size_t first_bit; bmap = first / BIG_BITCOUNT; /* Bitmap handling this block */ first_bit = first & (BIG_BITCOUNT - 1); /* Index within bitmap */ g_assert(first_bit != 0); /* Bit 0 is the bitmap itself */ /* * Loop through all the currently existing bitmaps. */ for (i = bmap; i < max_bitmap; i++) { size_t bno; if (!fetch_bitbuf(db, i)) return 0; bno = bit_field_first_clear(dbg->bitbuf, first_bit, BIG_BITCOUNT - 1); if ((size_t) -1 == bno) continue; /* * Found a free block. */ bit_field_set(dbg->bitbuf, bno); dbg->bitbuf_dirty = TRUE; /* * Correct the block number corresponding to "bno", if we did * not find it in bitmap #0. */ bno = size_saturate_add(bno, size_saturate_mult(BIG_BITCOUNT, i)); /* Make sure we can represent the block number in 32 bits */ g_assert(bno <= MAX_INT_VAL(guint32)); return bno; /* Allocated block number */ } return 0; /* No free block found */ }
/** * Shrink .dat file on disk to remove needlessly allocated blocks. * * @return TRUE if we were able to successfully shrink the file. */ gboolean big_shrink(DBM *db) { DBMBIG *dbg = db->big; long i; filesize_t offset = 0; if (-1 == dbg->fd) { /* * We do not want to call big_open() unless the .dat file already * exists because that would create it and it was not needed so far. */ if (-1 == big_open_lazy(dbg, TRUE)) return 0 == errno; } g_assert(dbg->fd != -1); /* * Loop through all the currently existing bitmaps, starting from the last * one, looking for the last set bit indicating the last used page. */ for (i = dbg->bitmaps - 1; i >= 0; i--) { size_t bno; if (!fetch_bitbuf(db, i)) return FALSE; bno = bit_field_last_set(dbg->bitbuf, 0, BIG_BITCOUNT - 1); if ((size_t) -1 == bno) { g_warning("sdbm: \"%s\": corrupted bitmap #%ld, considered empty", sdbm_name(db), i); } else if (bno != 0) { bno = size_saturate_add(bno, size_saturate_mult(BIG_BITCOUNT, i)); offset = OFF_DAT(bno + 1); break; } } if (-1 == ftruncate(dbg->fd, offset)) return FALSE; dbg->bitmaps = i + 1; /* Possibly reduced the amount of bitmaps */ return TRUE; }
/** * HTTP async callback, invoked when new HTTP data is read. * The EOF condition is indicated by data being NULL. */ static void soap_data_ind(http_async_t *ha, char *data, int len) { soap_rpc_t *sr = http_async_get_opaque(ha); size_t new_length; soap_rpc_check(sr); /* * When data is NULL, we reached EOF and we're done. Time to process * the data we got back. * * The HTTP asynchronous handle is nullified since it is about to be * closed by the HTTP layer upon return. */ if (NULL == data) { sr->ha = NULL; soap_process_reply(sr); return; } /* * Ensure we don't get too much and resize the memory buffer where * we store the reply if needed. */ new_length = size_saturate_add(sr->reply_len, len); if (new_length > sr->content_len) { http_async_error(ha, HTTP_ASYNC_DATA2BIG); return; } if (new_length > sr->reply_size) { size_t new_size = size_saturate_mult(sr->reply_size, 2); sr->reply_data = hrealloc(sr->reply_data, new_size); sr->reply_size = new_size; } /* * Append new data. */ memcpy(&sr->reply_data[sr->reply_len], data, len); sr->reply_len = new_length; }
/** * Extend token buffer. */ static void extend_token(strtok_t *s) { size_t len; strtok_check(s); if (s->len > 0) { if (s->len > STRTOK_GROW) len = size_saturate_add(s->len, STRTOK_GROW); else len = size_saturate_mult(s->len, 2); } else { len = STRTOK_FIRST_LEN; } grow_token(s, len); }
/** * Send datagram. * * @param us the UDP scheduler responsible for sending the datagram * @param mb the message to send * @param to the IP:port destination of the message * @param tx the TX stack sending the message * @param cb callback actions on the datagram * * @return 0 if message was unsent, length of message if sent, queued or * dropped. */ size_t udp_sched_send(udp_sched_t *us, pmsg_t *mb, const gnet_host_t *to, const txdrv_t *tx, const struct tx_dgram_cb *cb) { int len; struct udp_tx_desc *txd; uint prio; len = pmsg_size(mb); /* * Try to send immediately if we have bandwidth. */ if (!us->used_all && udp_sched_mb_sendto(us, mb, to, tx, cb)) return len; /* Message "sent" */ /* * If we already have enough data enqueued, flow-control the upper * layer by acting as if we do not have enough bandwidth. * * However, we now always accept traffic sent with the highest priority * since it is important to send those as soon as possible, i.e. ahead * of any other pending data we would otherwise flush locally before * servicing upper queues. * --RAM, 2012-10-12 */ prio = pmsg_prio(mb); if ( PMSG_P_HIGHEST != prio && us->buffered >= UDP_SCHED_FACTOR * udp_sched_bw_per_second(us) ) { udp_sched_log(1, "%p: flow-controlled", us); us->flow_controlled = TRUE; return 0; /* Flow control upper layers */ } /* * Message is going to be enqueued. * * However, from the upper layers (the message queue in particular), * the message is considered as being sent, and therefore these layers * are going to call pmsg_free() on the message. * * We do not want to pmsg_clone() the message because that would render * uses of pmsg_was_sent() useless in free routines, and upper layers * would think the message was dropped if they installed a free routine * on the message. * * Hence we use pmsg_ref(). */ txd = palloc(us->txpool); txd->magic = UDP_TX_DESC_MAGIC; txd->mb = pmsg_ref(mb); /* Take ownership of message */ txd->to = atom_host_get(to); txd->tx = tx; txd->cb = cb; txd->expire = time_advance(tm_time(), UDP_SCHED_EXPIRE); udp_sched_log(4, "%p: queuing mb=%p (%d bytes) prio=%u", us, mb, pmsg_size(mb), pmsg_prio(mb)); /* * The queue used is a LIFO to avoid buffering delaying all the messages. * Since UDP traffic is unordered, it's better to send the most recent * datagrams first, to reduce the perceived average latency. */ g_assert(prio < N_ITEMS(us->lifo)); eslist_prepend(&us->lifo[prio], txd); us->buffered = size_saturate_add(us->buffered, len); return len; /* Message queued, but tell upper layers it's sent */ }
/** * Allocate "n" consecutive (sequential) blocks in the file, without * attempting to extend it. * * @param db the sdbm database * @param bmap bitmap number from which we need to start looking * @param n amount of consecutive blocks we want * * @return the block number of the first "n" blocks if found, 0 if nothing * was found. */ static size_t big_falloc_seq(DBM *db, int bmap, int n) { DBMBIG *dbg = db->big; long max_bitmap = dbg->bitmaps; long i; g_assert(bmap >= 0); g_assert(n > 0); /* * Loop through all the currently existing bitmaps, starting at the * specified bitmap number. */ for (i = bmap; i < max_bitmap; i++) { size_t first; size_t j; int r; /* Remaining blocks to allocate consecutively */ if (!fetch_bitbuf(db, i)) return 0; /* * We start at bit #1 since bit #0 is the bitmap itself. * * Bit #0 should always be set but in case the file is corrupted, * we don't want to start allocating data in the bitmap itself!. */ first = bit_field_first_clear(dbg->bitbuf, 1, BIG_BITCOUNT - 1); if ((size_t) -1 == first) continue; for (j = first + 1, r = n - 1; r > 0 && j < BIG_BITCOUNT; r--, j++) { if (bit_field_get(dbg->bitbuf, j)) break; } /* * If "r" is 0, we have no remaining page to allocate: we found our * "n" consecutive free blocks. */ if (0 == r) { /* * Mark the "n" consecutive blocks as busy. */ for (j = first, r = n; r > 0; r--, j++) { bit_field_set(dbg->bitbuf, j); } dbg->bitbuf_dirty = TRUE; /* * Correct the block number corresponding to "first", if we did * not find it in bitmap #0. */ first = size_saturate_add(first, size_saturate_mult(BIG_BITCOUNT, i)); /* Make sure we can represent all block numbers in 32 bits */ g_assert(size_saturate_add(first, n - 1) <= MAX_INT_VAL(guint32)); return first; /* "n" consecutive free blocks found */ } } return 0; /* No free block found */ }
/** * Append data `str' to the header line, atomically. * * @param `hf' no brief description. * @param `str' no brief description. * @param `separator' is an optional separator string that will be emitted * BEFORE outputting the data, and only when nothing has been emitted * already. * @param `slen' is the separator length, 0 if empty. * @param `sslen' is the stripped separator length, (size_t)-1 if unknown yet. * * @return TRUE if we were able to fit the string, FALSE if it would have * resulted in the header being larger than the configured max size (the * header line is left in the state it was in upon entry, in that case). */ static bool header_fmt_append_full(header_fmt_t *hf, const char *str, const char *separator, size_t slen, size_t sslen) { size_t len, curlen; gsize gslen; bool success; header_fmt_check(hf); g_assert(size_is_non_negative(slen)); g_assert((size_t)-1 == sslen || size_is_non_negative(sslen)); if (hf->empty) return FALSE; gslen = str_len(hf->header); len = strlen(str); curlen = hf->current_len; g_assert(size_is_non_negative(curlen)); g_assert(len <= INT_MAX); /* Legacy bug */ if ( size_saturate_add(curlen, size_saturate_add(len, slen)) > UNSIGNED(hf->maxlen) ) { /* * Emit sperator, if any and data was already emitted. */ if (separator != NULL && hf->data_emitted) { sslen = (size_t)-1 != sslen ? sslen : stripped_strlen(separator, slen); str_cat_len(hf->header, separator, sslen); } STR_CAT(hf->header, "\r\n\t"); /* Includes continuation */ curlen = 1; /* One tab */ } else if (hf->data_emitted) { str_cat(hf->header, separator); curlen += slen; } str_cat(hf->header, str); /* * Check for overflows, undoing string changes if needed. */ if (str_len(hf->header) + sizeof("\r\n") > hf->max_size) { success = FALSE; str_setlen(hf->header, gslen); /* Undo! */ } else { success = TRUE; hf->data_emitted = TRUE; hf->current_len = curlen + len; } g_assert(str_len(hf->header) + sizeof("\r\n") <= hf->max_size); return success; }