<commit_before>#include "unittest/gtest.hpp"
#include "unittest/server_test_helper.hpp"
#include "unittest/unittest_utils.hpp"
#include "buffer_cache/blob.hpp"

namespace unittest {

class blob_tracker_t {
public:
    blob_tracker_t(size_t maxreflen)
        : buf_(new char[maxreflen]), blob_(buf_, maxreflen) {
        // That's a bit of a hack, because blob_ will only read the
        // first byte or first two bytes if they are zero.
        expected_.clear();
    }

    ~blob_tracker_t() {
        delete[] buf_;
    }

    void check_region(transaction_t *txn, int64_t offset, int64_t size) {
        SCOPED_TRACE("check_region");
        ASSERT_LE(0, offset);
        ASSERT_LE(offset, expected_.size());
        ASSERT_LE(0, size);
        ASSERT_LE(offset + size, expected_.size());

        ASSERT_EQ(expected_.size(), blob_.valuesize());

        buffer_group_t bg;
        blob_acq_t bacq;
        blob_.expose_region(txn, rwi_read, offset, size, &bg, &bacq);

        ASSERT_EQ(size, bg.get_size());

        std::string::iterator p_orig = expected_.begin() + offset, p = p_orig, e = p + size;
        for (size_t i = 0, n = bg.num_buffers(); i < n; ++i) {
            buffer_group_t::buffer_t buffer = bg.get_buffer(i);
            SCOPED_TRACE(strprintf("buffer %zu/%zu (size %zu)", i, n, buffer.size));
            ASSERT_LE(buffer.size, e - p);
            char *data = reinterpret_cast<char *>(buffer.data);
            ASSERT_TRUE(std::equal(data, data + buffer.size, p));
            p += buffer.size;
        }

        ASSERT_EQ(e - p_orig, p - p_orig);
    }

    void check_normalization(transaction_t *txn) {
	int64_t size = expected_.size();
	size_t rs = blob_.refsize(txn->get_cache()->get_block_size());
	if (size < 251) {
	    ASSERT_EQ(1 + size, rs);
	} else if (size <= 4080) {
	    ASSERT_EQ(1 + 8 + 8 + 4, rs);
	} else if (size <= int64_t(4080 * ((250 - 8 - 8) / sizeof(block_id_t)))) {
	    if (rs != 1 + 8 + 8 + 4) {
		ASSERT_LE(1 + 8 + 8 + 4 * ceil_divide(size, 4080), rs);
		ASSERT_GE(1 + 8 + 8 + 4 * (1 + ceil_divide(size - 1, 4080)), rs);
	    } else {
		ASSERT_GT(size, 4080 * ((250 - 8 - 8) / sizeof(block_id_t)) - 4080 + 1);
	    }
	} else if (size <= int64_t(4080 * (4080 / 4) * ((250 - 8 - 8) / sizeof(block_id_t)))) {
	    ASSERT_LE(1 + 8 + 8 + 4 * ceil_divide(size, 4080 * (4080 / 4)), rs);
	    ASSERT_GE(1 + 8 + 8 + 4 * (1 + ceil_divide(size - 1, 4080 * (4080 / 4))), rs);
	} else {
	    ASSERT_GT(0, size);
	}
    }

    void check(transaction_t *txn) {
        check_region(txn, 0, expected_.size());
	check_normalization(txn);
    }

    void append(transaction_t *txn, const std::string& x) {
        SCOPED_TRACE("append " + x);
        int64_t n = x.size();

        blob_.append_region(txn, n);

        ASSERT_EQ(expected_.size() + n, blob_.valuesize());

        {
            buffer_group_t bg;
            blob_acq_t bacq;
            blob_.expose_region(txn, rwi_write, expected_.size(), n, &bg, &bacq);

            ASSERT_EQ(n, bg.get_size());

            const_buffer_group_t copyee;
            copyee.add_buffer(x.size(), x.data());

            buffer_group_copy_data(&bg, &copyee);
<commit_msg>Fixed uninitialized buffer bug in blob_tracker_t.<commit_after>#include "unittest/gtest.hpp"
#include "unittest/server_test_helper.hpp"
#include "unittest/unittest_utils.hpp"
#include "buffer_cache/blob.hpp"

namespace unittest {

class blob_tracker_t {
public:
    static char *alloc_emptybuf(size_t n) {
        char *ret = new char[n];
        memset(ret, 0, n);
        return ret;
    }

    blob_tracker_t(size_t maxreflen)
        : buf_(alloc_emptybuf(maxreflen)), blob_(buf_, maxreflen) { }

    ~blob_tracker_t() {
        delete[] buf_;
    }

    void check_region(transaction_t *txn, int64_t offset, int64_t size) {
        SCOPED_TRACE("check_region");
        ASSERT_LE(0, offset);
        ASSERT_LE(offset, expected_.size());
        ASSERT_LE(0, size);
        ASSERT_LE(offset + size, expected_.size());

        ASSERT_EQ(expected_.size(), blob_.valuesize());

        buffer_group_t bg;
        blob_acq_t bacq;
        blob_.expose_region(txn, rwi_read, offset, size, &bg, &bacq);

        ASSERT_EQ(size, bg.get_size());

        std::string::iterator p_orig = expected_.begin() + offset, p = p_orig, e = p + size;
        for (size_t i = 0, n = bg.num_buffers(); i < n; ++i) {
            buffer_group_t::buffer_t buffer = bg.get_buffer(i);
            SCOPED_TRACE(strprintf("buffer %zu/%zu (size %zu)", i, n, buffer.size));
            ASSERT_LE(buffer.size, e - p);
            char *data = reinterpret_cast<char *>(buffer.data);
            ASSERT_TRUE(std::equal(data, data + buffer.size, p));
            p += buffer.size;
        }

        ASSERT_EQ(e - p_orig, p - p_orig);
    }

    void check_normalization(transaction_t *txn) {
	int64_t size = expected_.size();
	size_t rs = blob_.refsize(txn->get_cache()->get_block_size());
	if (size < 251) {
	    ASSERT_EQ(1 + size, rs);
	} else if (size <= 4080) {
	    ASSERT_EQ(1 + 8 + 8 + 4, rs);
	} else if (size <= int64_t(4080 * ((250 - 8 - 8) / sizeof(block_id_t)))) {
	    if (rs != 1 + 8 + 8 + 4) {
		ASSERT_LE(1 + 8 + 8 + 4 * ceil_divide(size, 4080), rs);
		ASSERT_GE(1 + 8 + 8 + 4 * (1 + ceil_divide(size - 1, 4080)), rs);
	    } else {
		ASSERT_GT(size, 4080 * ((250 - 8 - 8) / sizeof(block_id_t)) - 4080 + 1);
	    }
	} else if (size <= int64_t(4080 * (4080 / 4) * ((250 - 8 - 8) / sizeof(block_id_t)))) {
	    ASSERT_LE(1 + 8 + 8 + 4 * ceil_divide(size, 4080 * (4080 / 4)), rs);
	    ASSERT_GE(1 + 8 + 8 + 4 * (1 + ceil_divide(size - 1, 4080 * (4080 / 4))), rs);
	} else {
	    ASSERT_GT(0, size);
	}
    }

    void check(transaction_t *txn) {
        check_region(txn, 0, expected_.size());
	check_normalization(txn);
    }

    void append(transaction_t *txn, const std::string& x) {
        SCOPED_TRACE(strprintf("append (%zu) ", x.size()) + std::string(x.begin(), x.begin() + std::min<size_t>(x.size(), 50)));
        int64_t n = x.size();

        blob_.append_region(txn, n);

        ASSERT_EQ(expected_.size() + n, blob_.valuesize());

        {
            buffer_group_t bg;
            blob_acq_t bacq;
            blob_.expose_region(txn, rwi_write, expected_.size(), n, &bg, &bacq);

            ASSERT_EQ(n, bg.get_size());

            const_buffer_group_t copyee;
            copyee.add_buffer(x.size(), x.data());

            buffer_group_copy_data(&bg, &copyee);

{'fraction_non_alphanumeric': 0.15643933509398789, 'fraction_numerical': 0.03650768991766351, 'mean_word_length': 2.564784053156146, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1430102', 'n_tokens_mistral': 2574, 'n_tokens_neox': 2339, 'n_words': 515}