ham_status_t blob_get_datasize(ham_db_t *db, ham_offset_t blobid, ham_offset_t *size) { ham_status_t st; ham_page_t *page; blob_t hdr; /* * in-memory-database: the blobid is actually a pointer to the memory * buffer, in which the blob is stored */ if (env_get_rt_flags(db_get_env(db))&HAM_IN_MEMORY_DB) { blob_t *hdr=(blob_t *)U64_TO_PTR(blobid); *size=blob_get_size(hdr); return (0); } ham_assert(blobid%DB_CHUNKSIZE==0, ("blobid is %llu", blobid)); /* read the blob header */ st=__read_chunk(db_get_env(db), 0, &page, blobid, (ham_u8_t *)&hdr, sizeof(hdr)); if (st) return (st); ham_assert(blob_get_alloc_size(&hdr)%DB_CHUNKSIZE==0, (0)); if (blob_get_self(&hdr)!=blobid) return (HAM_BLOB_NOT_FOUND); *size=blob_get_size(&hdr); return (0); }
static ham_status_t __get_duplicate_table(dupe_table_t **table_ref, ham_page_t **page, ham_env_t *env, ham_u64_t table_id) { ham_status_t st; blob_t hdr; ham_page_t *hdrpage=0; dupe_table_t *table; *page = 0; if (env_get_rt_flags(env)&HAM_IN_MEMORY_DB) { ham_u8_t *p=(ham_u8_t *)U64_TO_PTR(table_id); *table_ref = (dupe_table_t *)(p+sizeof(hdr)); return HAM_SUCCESS; } *table_ref = 0; /* * load the blob header */ st=__read_chunk(env, 0, &hdrpage, table_id, (ham_u8_t *)&hdr, sizeof(hdr)); if (st) { return st; } /* * if the whole table is in a page (and not split between several * pages), just return a pointer directly in the page */ if (page_get_self(hdrpage)+env_get_usable_pagesize(env) >= table_id+blob_get_size(&hdr)) { ham_u8_t *p=page_get_raw_payload(hdrpage); /* yes, table is in the page */ *page=hdrpage; *table_ref = (dupe_table_t *) &p[table_id-page_get_self(hdrpage)+sizeof(hdr)]; return HAM_SUCCESS; } /* * otherwise allocate memory for the table */ table=allocator_alloc(env_get_allocator(env), (ham_size_t)blob_get_size(&hdr)); if (!table) { return HAM_OUT_OF_MEMORY; } /* * then read the rest of the blob */ st=__read_chunk(env, hdrpage, 0, table_id+sizeof(hdr), (ham_u8_t *)table, (ham_size_t)blob_get_size(&hdr)); if (st) { return st; } *table_ref = table; return HAM_SUCCESS; }
void *realloc(void *ptr, size_t size) { if (size == 0) { free(ptr); return NULL; } if (!ptr) return malloc(size); struct bucket_header *header = util_ptr_to_header(ptr); size_t max_avail; if (header->type == BUCKET_TYPE_BLOB) { max_avail = blob_get_size(header); } else { max_avail = bucket_get_record_size(header->type); } if (max_avail >= size) return ptr; void *new_address = malloc(size); if (ptr) memcpy(new_address, ptr, max_avail); free(ptr); dprint("DEBUG: realloc(%p, %zu) = %p\n", ptr, size, new_address); return new_address; }
ham_status_t blob_overwrite(ham_env_t *env, ham_db_t *db, ham_offset_t old_blobid, ham_record_t *record, ham_u32_t flags, ham_offset_t *new_blobid) { ham_status_t st; ham_size_t alloc_size; blob_t old_hdr; blob_t new_hdr; ham_page_t *page; /* * PARTIAL WRITE * * if offset+partial_size equals the full record size, then we won't * have any gaps. In this case we just write the full record and ignore * the partial parameters. */ if (flags&HAM_PARTIAL) { if (record->partial_offset==0 && record->partial_offset+record->partial_size==record->size) flags&=~HAM_PARTIAL; } /* * inmemory-databases: free the old blob, * allocate a new blob (but if both sizes are equal, just overwrite * the data) */ if (env_get_rt_flags(env)&HAM_IN_MEMORY_DB) { blob_t *nhdr, *phdr=(blob_t *)U64_TO_PTR(old_blobid); if (blob_get_size(phdr)==record->size) { ham_u8_t *p=(ham_u8_t *)phdr; if (flags&HAM_PARTIAL) { memmove(p+sizeof(blob_t)+record->partial_offset, record->data, record->partial_size); } else { memmove(p+sizeof(blob_t), record->data, record->size); } *new_blobid=(ham_offset_t)PTR_TO_U64(phdr); } else { st=blob_allocate(env, db, record, flags, new_blobid); if (st) return (st); nhdr=(blob_t *)U64_TO_PTR(*new_blobid); blob_set_flags(nhdr, blob_get_flags(phdr)); allocator_free(env_get_allocator(env), phdr); } return (HAM_SUCCESS); } ham_assert(old_blobid%DB_CHUNKSIZE==0, (0)); /* * blobs are CHUNKSIZE-allocated */ alloc_size=sizeof(blob_t)+record->size; alloc_size += DB_CHUNKSIZE - 1; alloc_size -= alloc_size % DB_CHUNKSIZE; /* * first, read the blob header; if the new blob fits into the * old blob, we overwrite the old blob (and add the remaining * space to the freelist, if there is any) */ st=__read_chunk(env, 0, &page, old_blobid, (ham_u8_t *)&old_hdr, sizeof(old_hdr)); if (st) return (st); ham_assert(blob_get_alloc_size(&old_hdr)%DB_CHUNKSIZE==0, (0)); /* * sanity check */ ham_verify(blob_get_self(&old_hdr)==old_blobid, ("invalid blobid %llu != %llu", blob_get_self(&old_hdr), old_blobid)); if (blob_get_self(&old_hdr)!=old_blobid) return (HAM_BLOB_NOT_FOUND); /* * now compare the sizes; does the new data fit in the old allocated * space? */ if (alloc_size<=blob_get_alloc_size(&old_hdr)) { ham_u8_t *chunk_data[2]; ham_size_t chunk_size[2]; /* * setup the new blob header */ blob_set_self(&new_hdr, blob_get_self(&old_hdr)); blob_set_size(&new_hdr, record->size); blob_set_flags(&new_hdr, blob_get_flags(&old_hdr)); if (blob_get_alloc_size(&old_hdr)-alloc_size>SMALLEST_CHUNK_SIZE) blob_set_alloc_size(&new_hdr, alloc_size); else blob_set_alloc_size(&new_hdr, blob_get_alloc_size(&old_hdr)); /* * PARTIAL WRITE * * if we have a gap at the beginning, then we have to write the * blob header and the blob data in two steps; otherwise we can * write both immediately */ if ((flags&HAM_PARTIAL) && (record->partial_offset)) { chunk_data[0]=(ham_u8_t *)&new_hdr; chunk_size[0]=sizeof(new_hdr); st=__write_chunks(env, page, blob_get_self(&new_hdr), HAM_FALSE, HAM_FALSE, chunk_data, chunk_size, 1); if (st) return (st); chunk_data[0]=record->data; chunk_size[0]=record->partial_size; st=__write_chunks(env, page, blob_get_self(&new_hdr)+sizeof(new_hdr) +record->partial_offset, HAM_FALSE, HAM_FALSE, chunk_data, chunk_size, 1); if (st) return (st); } else { chunk_data[0]=(ham_u8_t *)&new_hdr; chunk_size[0]=sizeof(new_hdr); chunk_data[1]=record->data; chunk_size[1]=(flags&HAM_PARTIAL) ? record->partial_size : record->size; st=__write_chunks(env, page, blob_get_self(&new_hdr), HAM_FALSE, HAM_FALSE, chunk_data, chunk_size, 2); if (st) return (st); } /* * move remaining data to the freelist */ if (blob_get_alloc_size(&old_hdr)!=blob_get_alloc_size(&new_hdr)) { (void)freel_mark_free(env, db, blob_get_self(&new_hdr)+blob_get_alloc_size(&new_hdr), (ham_size_t)(blob_get_alloc_size(&old_hdr)- blob_get_alloc_size(&new_hdr)), HAM_FALSE); } /* * the old rid is the new rid */ *new_blobid=blob_get_self(&new_hdr); return (HAM_SUCCESS); } else { /* * when the new data is larger, allocate a fresh space for it * and discard the old; 'overwrite' has become (delete + insert) now. */ st=blob_allocate(env, db, record, flags, new_blobid); if (st) return (st); (void)freel_mark_free(env, db, old_blobid, (ham_size_t)blob_get_alloc_size(&old_hdr), HAM_FALSE); } return (HAM_SUCCESS); }
ham_status_t blob_read(ham_db_t *db, ham_offset_t blobid, ham_record_t *record, ham_u32_t flags) { ham_status_t st; ham_page_t *page; blob_t hdr; ham_size_t blobsize=0; /* * in-memory-database: the blobid is actually a pointer to the memory * buffer, in which the blob is stored */ if (env_get_rt_flags(db_get_env(db))&HAM_IN_MEMORY_DB) { blob_t *hdr=(blob_t *)U64_TO_PTR(blobid); ham_u8_t *data=(ham_u8_t *)(U64_TO_PTR(blobid))+sizeof(blob_t); /* when the database is closing, the header is already deleted */ if (!hdr) { record->size = 0; return (0); } blobsize = (ham_size_t)blob_get_size(hdr); if (flags&HAM_PARTIAL) { if (record->partial_offset>blobsize) { ham_trace(("partial offset is greater than the total " "record size")); return (HAM_INV_PARAMETER); } if (record->partial_offset+record->partial_size>blobsize) blobsize=blobsize-record->partial_offset; else blobsize=record->partial_size; } if (!blobsize) { /* empty blob? */ record->data = 0; record->size = 0; } else { ham_u8_t *d=data; if (flags&HAM_PARTIAL) d+=record->partial_offset; if ((flags&HAM_DIRECT_ACCESS) && !(record->flags&HAM_RECORD_USER_ALLOC)) { record->size=blobsize; record->data=d; } else { /* resize buffer, if necessary */ if (!(record->flags & HAM_RECORD_USER_ALLOC)) { st=db_resize_record_allocdata(db, blobsize); if (st) return (st); record->data = db_get_record_allocdata(db); } /* and copy the data */ memcpy(record->data, d, blobsize); record->size = blobsize; } } return (0); } ham_assert(blobid%DB_CHUNKSIZE==0, ("blobid is %llu", blobid)); /* * first step: read the blob header */ st=__read_chunk(db_get_env(db), 0, &page, blobid, (ham_u8_t *)&hdr, sizeof(hdr)); if (st) return (st); ham_assert(blob_get_alloc_size(&hdr)%DB_CHUNKSIZE==0, (0)); /* * sanity check */ if (blob_get_self(&hdr)!=blobid) return (HAM_BLOB_NOT_FOUND); blobsize = (ham_size_t)blob_get_size(&hdr); if (flags&HAM_PARTIAL) { if (record->partial_offset>blobsize) { ham_trace(("partial offset+size is greater than the total " "record size")); return (HAM_INV_PARAMETER); } if (record->partial_offset+record->partial_size>blobsize) blobsize=blobsize-record->partial_offset; else blobsize=record->partial_size; } /* * empty blob? */ if (!blobsize) { record->data = 0; record->size = 0; return (0); } /* * second step: resize the blob buffer */ if (!(record->flags & HAM_RECORD_USER_ALLOC)) { st=db_resize_record_allocdata(db, blobsize); if (st) return (st); record->data = db_get_record_allocdata(db); } /* * third step: read the blob data */ st=__read_chunk(db_get_env(db), page, 0, blobid+sizeof(blob_t)+(flags&HAM_PARTIAL ? record->partial_offset : 0), record->data, blobsize); if (st) return (st); record->size = blobsize; return (0); }