pikelang/Pike

View on GitHub
src/buffer.c

Summary

Maintainability
Test Coverage
/*
|| This file is part of Pike. For copyright information see COPYRIGHT.
|| Pike is distributed under GPL, LGPL and MPL. See the file COPYING
|| for more information.
*/

#include "buffer.h"
#include "stralloc.h"
#include "svalue.h"
#include "pike_error.h"
#include "bignum.h"

PMOD_EXPORT void buffer_free(struct byte_buffer *b) {
    if (!b->dst) return;
    free(buffer_ptr(b));
    buffer_init(b);
}

static void *buffer_realloc(struct byte_buffer *b, size_t len) {
    if (b->flags & BUFFER_GROW_MEXEC) {
        return mexec_realloc(buffer_ptr(b), len);
    } else {
        return realloc(buffer_ptr(b), len);
    }
}

PMOD_EXPORT int buffer_grow_nothrow(struct byte_buffer *b, size_t len) {
    const size_t content_length = buffer_content_length(b);
    size_t new_length, needed;
    unsigned INT16 flags = b->flags;
    void *ptr;

#ifdef PIKE_DEBUG
    if (buffer_space(b) >= len) Pike_fatal("Misuse of buffer_grow.\n");
#endif
    if (DO_SIZE_T_ADD_OVERFLOW(len, content_length, &needed)) goto OUT_OF_MEMORY_FATAL;

    if (!(flags & BUFFER_GROW_EXACT)) {
        new_length = b->length;
        if (new_length < 16) new_length = 16;

        while (new_length < needed) {
            if (DO_SIZE_T_MUL_OVERFLOW(new_length, 2, &new_length)) goto OUT_OF_MEMORY_FATAL;
        }
    } else {
        new_length = needed;
    }

#if 0
    fprintf(stderr, "BUF { left=%llu; length=%llu; dst=%p; }\n", buffer_space(b), b->length, b->dst);
#endif

    ptr = buffer_realloc(b, new_length);

    if (UNLIKELY(!ptr)) return 0;

#if 0
    fprintf(stderr, "realloc(%p, %llu) = %p\n", buffer_ptr(b), new_length, ptr);
    fprintf(stderr, "content_length: %llu\n", content_length);
#endif

    b->dst = (char*)ptr + content_length;
    b->end = (char*)ptr + new_length;
    b->length = new_length;
    return 1;
OUT_OF_MEMORY_FATAL:
    /* This happens on overflow. */
#ifdef PIKE_DEBUG
    Pike_fatal(msg_out_of_mem_2, len);
#else
    return 0;
#endif
}

PMOD_EXPORT void buffer_grow(struct byte_buffer *b, size_t len) {
    if (UNLIKELY(!buffer_grow_nothrow(b, len))) Pike_error(msg_out_of_mem_2, len);
}

PMOD_EXPORT void* buffer_finish(struct byte_buffer *b) {
    void *ret;

    if (buffer_content_length(b) < buffer_length(b)) {
        ret = buffer_realloc(b, buffer_content_length(b));
        /* we cannot shrink, that does not matter */
        if (!ret) ret = buffer_ptr(b);
    } else ret = buffer_ptr(b);

    buffer_init(b);

    return ret;
}

PMOD_EXPORT struct pike_string *buffer_finish_pike_string(struct byte_buffer *b) {
    struct pike_string *ret;
    size_t len = buffer_content_length(b);

    if (!len) {
        buffer_free(b);
        ret = empty_pike_string;
        add_ref(ret);
        return ret;
    }

    /* zero terminate */
    buffer_add_char(b, 0);

    return make_shared_malloc_string(buffer_finish(b), len, eightbit);
}

PMOD_EXPORT const char *buffer_get_string(struct byte_buffer *b) {
    buffer_ensure_space(b, 1);

    /* zero terminate */
    *(char*)buffer_dst(b) = 0;

    return buffer_ptr(b);
}

#ifdef PIKE_DEBUG
PMOD_EXPORT void buffer_check_space(struct byte_buffer *b, size_t len) {
    if (len > buffer_space(b))
        Pike_fatal("buffer_check_space failed.\n");
}
PMOD_EXPORT void buffer_check_position(struct byte_buffer *b, size_t pos) {
    if (pos > b->length)
        Pike_fatal("buffer_check_position failed.\n");
}
PMOD_EXPORT void buffer_remove(struct byte_buffer *b, size_t len) {
    if (len > buffer_content_length(b))
        Pike_fatal("Bad call to buffer_remove.\n");

    b->dst = (char*)b->dst - len;
}
#endif