<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2010 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common.h"
#include "btreeblock.h"
#include "fdb_internal.h"

#include "memleak.h"

#ifdef __DEBUG
#ifndef __DEBUG_BTREEBLOCK
    #undef DBG
    #undef DBGCMD
    #undef DBGSW
    #define DBG(...)
    #define DBGCMD(...)
    #define DBGSW(n, ...)
#endif
#endif

struct btreeblk_addr{
    void *addr;
    struct list_elem le;
};

struct btreeblk_block {
    bid_t bid;
    int sb_no;
    uint32_t pos;
    uint8_t dirty;
    uint8_t age;
    void *addr;
    struct list_elem le;
    struct avl_node avl;
#ifdef __BTREEBLK_BLOCKPOOL
    struct btreeblk_addr *addr_item;
#endif
};

#ifdef __BTREEBLK_READ_TREE
static int _btreeblk_bid_cmp(struct avl_node *a, struct avl_node *b, void *aux)
{
    bid_t aa_bid, bb_bid;
    struct btreeblk_block *aa, *bb;
    aa = _get_entry(a, struct btreeblk_block, avl);
    bb = _get_entry(b, struct btreeblk_block, avl);
    aa_bid = aa->bid;
    bb_bid = bb->bid;

#ifdef __BIT_CMP
    return _CMP_U64(aa_bid, bb_bid);
#else
    if (aa->bid < bb->bid) {
        return -1;
    } else if (aa->bid > bb->bid) {
        return 1;
    } else {
        return 0;
    }
#endif
}
#endif

INLINE void _btreeblk_get_aligned_block(struct btreeblk_handle *handle,
                                        struct btreeblk_block *block)
{
#ifdef __BTREEBLK_BLOCKPOOL
    struct list_elem *e;

    e = list_pop_front(&handle->blockpool);
    if (e) {
        block->addr_item = _get_entry(e, struct btreeblk_addr, le);
        block->addr = block->addr_item->addr;
        return;
    }
    // no free addr .. create
    block->addr_item = (struct btreeblk_addr *)
                       mempool_alloc(sizeof(struct btreeblk_addr));
#endif

    malloc_align(block->addr, FDB_SECTOR_SIZE, handle->file->blocksize);
}

INLINE void _btreeblk_free_aligned_block(struct btreeblk_handle *handle,
                                         struct btreeblk_block *block)
{
#ifdef __BTREEBLK_BLOCKPOOL
    if (!block->addr_item) {
        // TODO: Need to log the corresponding error message.
        return;
    }
    // sync addr & insert into pool
    block->addr_item->addr = block->addr;
    list_push_front(&handle->blockpool, &block->addr_item->le);
    block->addr_item = NULL;
    return;

#endif

    free_align(block->addr);
}

// LCOV_EXCL_START
INLINE int is_subblock(bid_t subbid)
{
    uint8_t flag;
    flag = (subbid >> (8 * (sizeof(bid_t)-2))) & 0x00ff;
    return flag;
}
// LCOV_EXCL_STOP

INLINE void bid2subbid(bid_t bid, size_t subblock_no, size_t idx, bid_t *subbid)
{
    bid_t flag;
    // to distinguish subblock_no==0 to non-subblock
    subblock_no++;
    flag = (subblock_no << 5) | idx;
    *subbid = bid | (flag << (8 * (sizeof(bid_t)-2)));
}
INLINE void subbid2bid(bid_t subbid, size_t *subblock_no, size_t *idx, bid_t *bid)
{
    uint8_t flag;
    flag = (subbid >> (8 * (sizeof(bid_t)-2))) & 0x00ff;
    *subblock_no = flag >> 5;
    // to distinguish subblock_no==0 to non-subblock
    *subblock_no -= 1;
    *idx = flag & (0x20 - 0x01);
    *bid = ((bid_t)(subbid << 16)) >> 16;
}

INLINE void * _btreeblk_alloc(void *voidhandle, bid_t *bid, int sb_no)
{
    struct btreeblk_handle *handle = (struct btreeblk_handle *)voidhandle;
    struct list_elem *e = list_end(&handle->alc_list);
    struct btreeblk_block *block;
    uint32_t curpos;

    if (e) {
        block = _get_entry(e, struct btreeblk_block, le);
        if (block->pos <= (handle->file->blocksize) - (handle->nodesize)) {
            if (filemgr_is_writable(handle->file, block->bid)) {
                curpos = block->pos;
                block->pos += (handle->nodesize);
                *bid = block->bid * handle->nnodeperblock + curpos /
                       (handle->nodesize);
                return ((uint8_t *)block->addr + curpos);
            }
        }
    }

    // allocate new block from file manager
    block = (struct btreeblk_block *)mempool_alloc(sizeof(struct btreeblk_block));
    _btreeblk_get_aligned_block(handle, block);
    if (sb_no != -1) {
        // If this block is used as a sub-block container,
        // fill it with zero bytes for easy identifying
        // which region is allocated and which region is not.
        memset(block->addr, 0x0, handle->nodesize);
    }
    block->sb_no = sb_no;
    block->pos = handle->nodesize;
    block->bid = filemgr_alloc(handle->file, handle->log_callback);
    block->dirty = 1;
    block->age = 0;

#ifdef __CRC32
    memset((uint8_t *)block->addr + handle->nodesize - BLK_MARKER_SIZE,
           BLK_MARKER_BNODE, BLK_MARKER_SIZE);
#endif

    // btree bid differs to filemgr bid
    *bid = block->bid * handle->nnodeperblock;
    list_push_back(&handle->alc_list, &block->le);

    handle->nlivenodes++;
    handle->ndeltanodes++;

    return block->addr;
}
void * btreeblk_alloc(void *voidhandle, bid_t *bid) {
    return _btreeblk_alloc(voidhandle, bid, -1);
}


#ifdef __ENDIAN_SAFE
INLINE void _btreeblk_encode(struct btreeblk_handle *handle,<commit_msg>MB-18567 Insert a dummy block into buffer cache before use it<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2010 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common.h"
#include "btreeblock.h"
#include "fdb_internal.h"

#include "memleak.h"

#ifdef __DEBUG
#ifndef __DEBUG_BTREEBLOCK
    #undef DBG
    #undef DBGCMD
    #undef DBGSW
    #define DBG(...)
    #define DBGCMD(...)
    #define DBGSW(n, ...)
#endif
#endif

struct btreeblk_addr{
    void *addr;
    struct list_elem le;
};

struct btreeblk_block {
    bid_t bid;
    int sb_no;
    uint32_t pos;
    uint8_t dirty;
    uint8_t age;
    void *addr;
    struct list_elem le;
    struct avl_node avl;
#ifdef __BTREEBLK_BLOCKPOOL
    struct btreeblk_addr *addr_item;
#endif
};

#ifdef __BTREEBLK_READ_TREE
static int _btreeblk_bid_cmp(struct avl_node *a, struct avl_node *b, void *aux)
{
    bid_t aa_bid, bb_bid;
    struct btreeblk_block *aa, *bb;
    aa = _get_entry(a, struct btreeblk_block, avl);
    bb = _get_entry(b, struct btreeblk_block, avl);
    aa_bid = aa->bid;
    bb_bid = bb->bid;

#ifdef __BIT_CMP
    return _CMP_U64(aa_bid, bb_bid);
#else
    if (aa->bid < bb->bid) {
        return -1;
    } else if (aa->bid > bb->bid) {
        return 1;
    } else {
        return 0;
    }
#endif
}
#endif

INLINE void _btreeblk_get_aligned_block(struct btreeblk_handle *handle,
                                        struct btreeblk_block *block)
{
#ifdef __BTREEBLK_BLOCKPOOL
    struct list_elem *e;

    e = list_pop_front(&handle->blockpool);
    if (e) {
        block->addr_item = _get_entry(e, struct btreeblk_addr, le);
        block->addr = block->addr_item->addr;
        return;
    }
    // no free addr .. create
    block->addr_item = (struct btreeblk_addr *)
                       mempool_alloc(sizeof(struct btreeblk_addr));
#endif

    malloc_align(block->addr, FDB_SECTOR_SIZE, handle->file->blocksize);
}

INLINE void _btreeblk_free_aligned_block(struct btreeblk_handle *handle,
                                         struct btreeblk_block *block)
{
#ifdef __BTREEBLK_BLOCKPOOL
    if (!block->addr_item) {
        // TODO: Need to log the corresponding error message.
        return;
    }
    // sync addr & insert into pool
    block->addr_item->addr = block->addr;
    list_push_front(&handle->blockpool, &block->addr_item->le);
    block->addr_item = NULL;
    return;

#endif

    free_align(block->addr);
}

// LCOV_EXCL_START
INLINE int is_subblock(bid_t subbid)
{
    uint8_t flag;
    flag = (subbid >> (8 * (sizeof(bid_t)-2))) & 0x00ff;
    return flag;
}
// LCOV_EXCL_STOP

INLINE void bid2subbid(bid_t bid, size_t subblock_no, size_t idx, bid_t *subbid)
{
    bid_t flag;
    // to distinguish subblock_no==0 to non-subblock
    subblock_no++;
    flag = (subblock_no << 5) | idx;
    *subbid = bid | (flag << (8 * (sizeof(bid_t)-2)));
}
INLINE void subbid2bid(bid_t subbid, size_t *subblock_no, size_t *idx, bid_t *bid)
{
    uint8_t flag;
    flag = (subbid >> (8 * (sizeof(bid_t)-2))) & 0x00ff;
    *subblock_no = flag >> 5;
    // to distinguish subblock_no==0 to non-subblock
    *subblock_no -= 1;
    *idx = flag & (0x20 - 0x01);
    *bid = ((bid_t)(subbid << 16)) >> 16;
}

INLINE void * _btreeblk_alloc(void *voidhandle, bid_t *bid, int sb_no)
{
    struct btreeblk_handle *handle = (struct btreeblk_handle *)voidhandle;
    struct list_elem *e = list_end(&handle->alc_list);
    struct btreeblk_block *block;
    uint32_t curpos;

    if (e) {
        block = _get_entry(e, struct btreeblk_block, le);
        if (block->pos <= (handle->file->blocksize) - (handle->nodesize)) {
            if (filemgr_is_writable(handle->file, block->bid)) {
                curpos = block->pos;
                block->pos += (handle->nodesize);
                *bid = block->bid * handle->nnodeperblock + curpos /
                       (handle->nodesize);
                return ((uint8_t *)block->addr + curpos);
            }
        }
    }

    // allocate new block from file manager
    block = (struct btreeblk_block *)mempool_alloc(sizeof(struct btreeblk_block));
    _btreeblk_get_aligned_block(handle, block);
    if (sb_no != -1) {
        // If this block is used as a sub-block container,
        // fill it with zero bytes for easy identifying
        // which region is allocated and which region is not.
        memset(block->addr, 0x0, handle->nodesize);
    }
    block->sb_no = sb_no;
    block->pos = handle->nodesize;
    block->bid = filemgr_alloc(handle->file, handle->log_callback);
    block->dirty = 1;
    block->age = 0;

    // If a block is allocated but not written back into file (due to
    // various reasons), the corresponding byte offset in the file is filled
    // with garbage data so that it causes various unexpected behaviors.
    // To avoid this issue, populate block cache for the given BID before use it.
    uint8_t marker = BLK_MARKER_BNODE;
    filemgr_write_offset(handle->file, block->bid, handle->file->blocksize - 1,
                         1, &marker, false, handle->log_callback);

#ifdef __CRC32
    memset((uint8_t *)block->addr + handle->nodesize - BLK_MARKER_SIZE,
           BLK_MARKER_BNODE, BLK_MARKER_SIZE);
#endif

    // btree bid differs to filemgr bid
    *bid = block->bid * handle->nnodeperblock;
    list_push_back(&handle->alc_list, &block->le);

    handle->nlivenodes++;
    handle->ndeltanodes++;

    return block->addr;
}
void * btreeblk_alloc(void *voidhandle, bid_t *bid) {
    return _btreeblk_alloc(voidhandle, bid, -1);
}


#ifdef __ENDIAN_SAFE
INLINE void _btreeblk_encode(struct btreeblk_handle *handle,
{'fraction_non_alphanumeric': 0.11887122284192125, 'fraction_numerical': 0.010988096229085158, 'mean_word_length': 2.7815549260308465, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 146, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6914153', 'n_tokens_mistral': 4233, 'n_tokens_neox': 4092, 'n_words': 1082}