8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 08:03:03 +01:00
firebird-mirror/src/jrd/sort_mem.cpp
2003-11-03 23:59:24 +00:00

343 lines
8.1 KiB
C++

/*
* The contents of this file are subject to the Initial
* Developer's Public License Version 1.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.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
*
* Software distributed under the License is distributed AS IS,
* WITHOUT WARRANTY OF ANY KIND, either express or implied.
* See the License for the specific language governing rights
* and limitations under the License.
*
* The Original Code was created by Dmitry Yemanov
* for the Firebird Open Source RDBMS project.
*
* Copyright (c) 2002 Dmitry Yemanov <dimitr@users.sf.net>
* and all contributors signed below.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*/
#include "firebird.h"
#include "../common/config/config.h"
#include "../jrd/gds_proto.h"
#include "../jrd/sort_proto.h"
#include "../jrd/gdsassert.h"
#include "../jrd/sort_mem.h"
bool SortMem::is_initialized = false;
size_t SortMem::mem_block_size;
size_t SortMem::mem_upper_limit;
size_t SortMem::mem_total_size = 0;
/******************************************************************************
*
* Generic storage block implementation
*/
SortMem::Block::Block(Block *tail, size_t length)
: next(0), size(length)
{
// Link block with the chain
if (tail)
{
tail->next = this;
}
prev = tail;
}
/******************************************************************************
*
* Virtual memory block implementation
*/
SortMem::MemoryBlock::MemoryBlock(Block* tail, size_t length)
: Block(tail, length)
{
// Allocate virtual memory block
address = reinterpret_cast<char*>(gds__alloc(size));
}
SortMem::MemoryBlock::~MemoryBlock()
{
// Free virtual memory block
gds__free(address);
}
size_t SortMem::MemoryBlock::read(ISC_STATUS *status, size_t position, char *buffer, size_t length)
{
// Read block from memory
if (position + length > size)
{
length = size - position;
}
memcpy(buffer, address + position, length);
return length;
}
size_t SortMem::MemoryBlock::write(ISC_STATUS *status, size_t position, char *buffer, size_t length)
{
// Write block to memory
if (position + length > size)
{
length = size - position;
}
memcpy(address + position, buffer, length);
return length;
}
/******************************************************************************
*
* File block implementation
*/
SortMem::FileBlock::FileBlock(Block *tail, size_t length, struct sfb *blk, size_t position)
: Block(tail, length), file(blk), offset(position)
{
}
SortMem::FileBlock::~FileBlock()
{
}
size_t SortMem::FileBlock::read(ISC_STATUS *status, size_t position, char *buffer, size_t length)
{
// Read block from file
if (position + length > size)
{
length = size - position;
}
return SORT_read_block(status, file, offset + position,
reinterpret_cast<unsigned char*>(buffer), length) - offset - position;
// _lseek(file->sfb_file, offset + position, SEEK_SET);
// return _read(file->sfb_file, buffer, length);
}
size_t SortMem::FileBlock::write(ISC_STATUS *status, size_t position, char *buffer, size_t length)
{
// Write block to file
if (position + length > size)
{
length = size - position;
}
return SORT_write_block(status, file, offset + position,
reinterpret_cast<unsigned char*>(buffer), length) - offset - position;
// _lseek(file->sfb_file, offset + position, SEEK_SET);
// return _write(file->sfb_file, buffer, length);
}
/******************************************************************************
*
* Virtual scratch file implementation
*/
SortMem::SortMem(struct sfb *blk, size_t size)
: internal(blk), logical_size(0), physical_size(0), file_size(0), head(0), tail(0)
{
// Initialize itself
if (!is_initialized)
{
mem_block_size = Config::getSortMemBlockSize();
mem_upper_limit = Config::getSortMemUpperLimit();
is_initialized = true;
}
// Allocate one block
allocate(size);
}
SortMem::~SortMem()
{
// Free all allocated blocks
while (tail)
{
Block *block = tail->prev;
delete tail;
tail = block;
}
// We've just freed some memory
mem_total_size -= physical_size - file_size;
}
void SortMem::allocate(size_t size)
{
if (size > 0)
{
logical_size += size;
bool mem_allocated = false;
// We've already got enough space to write data of the given size
if (logical_size > physical_size)
{
Block *block;
// Calculate how much virtual memory we should allocate
size_t smart_size = (mem_block_size > size) ? mem_block_size : size;
// Check whether virtual memory should be allocated or file should be used instead
if (mem_total_size + smart_size <= mem_upper_limit)
{
// Allocate block in virtual memory
try
{
block = FB_NEW (*getDefaultMemoryPool())
MemoryBlock(tail, smart_size);
mem_allocated = true;
}
// Not enough memory to allocate block
catch (std::bad_alloc)
{
// Check whether we can try to allocate less memory
if (smart_size > size)
{
// Allocate minimal possible amount of memory once more
smart_size = size;
try
{
block = FB_NEW (*getDefaultMemoryPool())
MemoryBlock(tail, smart_size);
mem_allocated = true;
}
catch (std::bad_alloc) {}
}
}
if (mem_allocated)
{
physical_size += smart_size;
// We've just allocated some virtual memory
mem_total_size += smart_size;
}
}
if (!mem_allocated)
{
// Allocate block on disk
block = FB_NEW (*getDefaultMemoryPool())
FileBlock(tail, size, internal, file_size);
physical_size += size;
// We've just allocated some file storage
file_size += size;
}
// Append new block to the chain
if (!head)
{
head = block;
}
tail = block;
}
}
}
SortMem::Block* SortMem::seek(size_t &position)
{
Block *block = 0;
// Check whether the given offset is valid
if (position < physical_size)
{
if (position < physical_size / 2)
{
// Let's walk forward
block = head;
while (block && position >= block->size)
{
position -= block->size;
block = block->next;
}
}
else
{
// Let's walk backward
block = tail;
while (block && physical_size - position > block->size)
{
position += block->size;
block = block->prev;
}
position += block->size - physical_size;
}
}
return block;
}
size_t SortMem::read(ISC_STATUS *status, size_t position, char *address, size_t length)
{
// If we'are not allowed to use memory, don't waste time
// playing with all these memory blocks - just use scratch file and return
if (!mem_upper_limit)
{
return SORT_read_block(status, internal, position,
reinterpret_cast<unsigned char*>(address), length);
}
size_t copied = 0;
if (length > 0)
{
// Search for the first needed block
size_t pos = position;
Block *block = seek(pos);
fb_assert(block);
// Read data from as many blocks as necessary
for (Block *itr = block; itr && length > 0; itr = itr->next, pos = 0)
{
size_t n = itr->read(status, pos, address, length);
address += n;
copied += n;
length -= n;
}
fb_assert(!length);
}
// New seek value
return position + copied;
}
size_t SortMem::write(ISC_STATUS *status, size_t position, char *address, size_t length)
{
// If we'are not allowed to use memory, don't waste time
// playing with all these memory blocks - just use scratch file and return
if (!mem_upper_limit)
{
return SORT_write_block(status, internal, position,
reinterpret_cast<unsigned char*>(address), length);
}
// There's probably not enough space, try to allocate one more block
if (position + length >= logical_size)
{
allocate(position + length - logical_size);
}
size_t copied = 0;
if (length > 0)
{
// Search for the first needed block
size_t pos = position;
Block *block = seek(pos);
fb_assert(block);
// Write data to as many blocks as necessary
for (Block *itr = block; itr && length > 0; itr = itr->next, pos = 0)
{
size_t n = itr->write(status, pos, address, length);
address += n;
copied += n;
length -= n;
}
fb_assert(!length);
}
// New seek value
return position + copied;
}