/* Copyright (c) 2003, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2019, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file compress_buf.c * * \brief Working with compressed data in buffers. **/ #define BUFFERS_PRIVATE #include "lib/cc/compat_compiler.h" #include "lib/container/buffers.h" #include "lib/compress/compress.h" #include "lib/log/util_bug.h" #ifdef PARANOIA /** Helper: If PARANOIA is defined, assert that the buffer in local variable * buf is well-formed. */ #define check() STMT_BEGIN buf_assert_ok(buf); STMT_END #else #define check() STMT_NIL #endif /* defined(PARANOIA) */ /** Compress or uncompress the data_len bytes in data using the * compression state state, appending the result to buf. If * done is true, flush the data in the state and finish the * compression/uncompression. Return -1 on failure, 0 on success. */ int buf_add_compress(buf_t *buf, tor_compress_state_t *state, const char *data, size_t data_len, const int done) { char *next; size_t old_avail, avail; int over = 0; do { int need_new_chunk = 0; if (!buf->tail || ! CHUNK_REMAINING_CAPACITY(buf->tail)) { size_t cap = data_len / 4; buf_add_chunk_with_capacity(buf, cap, 1); } next = CHUNK_WRITE_PTR(buf->tail); avail = old_avail = CHUNK_REMAINING_CAPACITY(buf->tail); switch (tor_compress_process(state, &next, &avail, &data, &data_len, done)) { case TOR_COMPRESS_DONE: over = 1; break; case TOR_COMPRESS_ERROR: return -1; case TOR_COMPRESS_OK: if (data_len == 0) { tor_assert_nonfatal(!done); over = 1; } break; case TOR_COMPRESS_BUFFER_FULL: if (avail) { /* The compression module says we need more room * (TOR_COMPRESS_BUFFER_FULL). Start a new chunk automatically, * whether were going to or not. */ need_new_chunk = 1; } if (data_len == 0 && !done) { /* We've consumed all the input data, though, so there's no * point in forging ahead right now. */ over = 1; } break; } buf->datalen += old_avail - avail; buf->tail->datalen += old_avail - avail; if (need_new_chunk) { buf_add_chunk_with_capacity(buf, data_len/4, 1); } } while (!over); check(); return 0; }