From 378beb4b6301001ca25ae091fadd8ce690e9a7ee Mon Sep 17 00:00:00 2001 From: David Rose Date: Sun, 13 May 2007 16:05:44 +0000 Subject: [PATCH] forgot commit --- panda/src/gobj/simpleAllocator.I | 149 +++++++++++++++++++ panda/src/gobj/simpleAllocator.cxx | 127 ++++++++++++++++ panda/src/gobj/simpleAllocator.h | 107 ++++++++++++++ panda/src/gobj/vertexDataSaveFile.I | 18 +++ panda/src/gobj/vertexDataSaveFile.cxx | 204 ++++++++++++++++++++++++++ panda/src/gobj/vertexDataSaveFile.h | 61 ++++++++ 6 files changed, 666 insertions(+) create mode 100644 panda/src/gobj/simpleAllocator.I create mode 100644 panda/src/gobj/simpleAllocator.cxx create mode 100644 panda/src/gobj/simpleAllocator.h create mode 100644 panda/src/gobj/vertexDataSaveFile.I create mode 100644 panda/src/gobj/vertexDataSaveFile.cxx create mode 100644 panda/src/gobj/vertexDataSaveFile.h diff --git a/panda/src/gobj/simpleAllocator.I b/panda/src/gobj/simpleAllocator.I new file mode 100644 index 0000000000..46e7ab8d4d --- /dev/null +++ b/panda/src/gobj/simpleAllocator.I @@ -0,0 +1,149 @@ +// Filename: simpleAllocator.I +// Created by: drose (12May07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocator::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +INLINE SimpleAllocator:: +SimpleAllocator(size_t max_size) : + LinkedListNode(true), + _total_size(0), + _max_size(max_size) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocator::get_total_size +// Access: Published +// Description: Returns the total size of allocated objects. +//////////////////////////////////////////////////////////////////// +INLINE size_t SimpleAllocator:: +get_total_size() const { + return _total_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocator::get_max_size +// Access: Published +// Description: Returns the available space for allocated objects. +//////////////////////////////////////////////////////////////////// +INLINE size_t SimpleAllocator:: +get_max_size() const { + return _max_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocator::set_max_size +// Access: Published +// Description: Changes the available space for allocated objects. +// This will not affect any already-allocated objects, +// but will have an effect on future calls to alloc(). +//////////////////////////////////////////////////////////////////// +INLINE void SimpleAllocator:: +set_max_size(size_t max_size) { + _max_size = max_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocatorBlock::Constructor +// Access: Private +// Description: A SimpleAllocatorBlock must be constructed via the +// SimpleAllocator::alloc() call. +//////////////////////////////////////////////////////////////////// +INLINE SimpleAllocatorBlock:: +SimpleAllocatorBlock(SimpleAllocator *alloc, + size_t start, size_t size) : + _allocator(alloc), + _start(start), + _size(size) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocatorBlock::Destructor +// Access: Published +// Description: The block automatically frees itself when it +// destructs. +//////////////////////////////////////////////////////////////////// +INLINE SimpleAllocatorBlock:: +~SimpleAllocatorBlock() { + free(); +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocatorBlock::free +// Access: Published +// Description: Releases the allocated space. +//////////////////////////////////////////////////////////////////// +INLINE void SimpleAllocatorBlock:: +free() { + if (_allocator != (SimpleAllocator *)NULL) { + _allocator->_total_size -= _size; + remove_from_list(); + _allocator = NULL; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocatorBlock::get_allocator +// Access: Published +// Description: Returns the SimpleAllocator object that owns this +// block. Returns NULL if the block has been freed. +//////////////////////////////////////////////////////////////////// +INLINE SimpleAllocator *SimpleAllocatorBlock:: +get_allocator() const { + return _allocator; +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocatorBlock::get_start +// Access: Published +// Description: Returns the starting point of this block. It is an +// error to call this if the block has been freed. +//////////////////////////////////////////////////////////////////// +INLINE size_t SimpleAllocatorBlock:: +get_start() const { + nassertr(_allocator != (SimpleAllocator *)NULL, 0); + return _start; +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocatorBlock::get_size +// Access: Published +// Description: Returns the size of this block. It is an +// error to call this if the block has been freed. +//////////////////////////////////////////////////////////////////// +INLINE size_t SimpleAllocatorBlock:: +get_size() const { + nassertr(_allocator != (SimpleAllocator *)NULL, 0); + return _size; +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocatorBlock::is_free +// Access: Published +// Description: Returns true if the block has been freed, false if it +// is still valid. +//////////////////////////////////////////////////////////////////// +INLINE bool SimpleAllocatorBlock:: +is_free() const { + return (_allocator != (SimpleAllocator *)NULL); +} diff --git a/panda/src/gobj/simpleAllocator.cxx b/panda/src/gobj/simpleAllocator.cxx new file mode 100644 index 0000000000..57eac13a64 --- /dev/null +++ b/panda/src/gobj/simpleAllocator.cxx @@ -0,0 +1,127 @@ +// Filename: simpleAllocator.cxx +// Created by: drose (12May07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "simpleAllocator.h" + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocator::Destructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +SimpleAllocator:: +~SimpleAllocator() { + // We're shutting down. Force-free everything remaining. + while (_next != (LinkedListNode *)this) { + nassertv(_next != (LinkedListNode *)NULL); + ((SimpleAllocatorBlock *)_next)->free(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocator::alloc +// Access: Published +// Description: Allocates a new block. Returns NULL if a block of the +// requested size cannot be allocated. +// +// To free the allocated block, call block->free(), or +// simply delete the block pointer. +//////////////////////////////////////////////////////////////////// +SimpleAllocatorBlock *SimpleAllocator:: +alloc(size_t size) { + // First fit algorithm: walk through all the empty blocks until we + // find one that has enough room. + + SimpleAllocatorBlock *block = NULL; + size_t end = 0; + + if (_next != this) { + // We have at least one allocated block. + block = (SimpleAllocatorBlock *)_next; + end = block->_start + block->_size; + + // Scan until we have reached the last allocated block. + while (block->_next != this) { + SimpleAllocatorBlock *next = (SimpleAllocatorBlock *)block->_next; + size_t free_size = next->_start - end; + if (size <= free_size) { + SimpleAllocatorBlock *new_block = new SimpleAllocatorBlock(this, end, size); + new_block->insert_before(next); + _total_size += size; + return new_block; + } + + block = next; + end = block->_start + block->_size; + } + } + + // No free blocks; check for room at the end. + size_t free_size = _max_size - end; + if (size <= free_size) { + SimpleAllocatorBlock *new_block = new SimpleAllocatorBlock(this, end, size); + new_block->insert_before(this); + _total_size += size; + return new_block; + } + + // No room for this block. + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocator::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void SimpleAllocator:: +output(ostream &out) const { + out << "SimpleAllocator, " << _total_size << " of " << _max_size + << " allocated"; +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocator::write +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void SimpleAllocator:: +write(ostream &out) const { + out << *this << ":\n"; + + SimpleAllocatorBlock *block = (SimpleAllocatorBlock *)_next; + while (block->_next != this) { + SimpleAllocatorBlock *next = (SimpleAllocatorBlock *)block->_next; + + out << " " << *block << "\n"; + block = next; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: SimpleAllocatorBlock::output +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +void SimpleAllocatorBlock:: +output(ostream &out) const { + if (_allocator == (SimpleAllocator *)NULL) { + out << "free block\n"; + } else { + out << "block of size " << _size << " at " << _start; + } +} diff --git a/panda/src/gobj/simpleAllocator.h b/panda/src/gobj/simpleAllocator.h new file mode 100644 index 0000000000..685965ffc4 --- /dev/null +++ b/panda/src/gobj/simpleAllocator.h @@ -0,0 +1,107 @@ +// Filename: simpleAllocator.h +// Created by: drose (12May07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef SIMPLEALLOCATOR_H +#define SIMPLEALLOCATOR_H + +#include "pandabase.h" +#include "linkedListNode.h" + +class SimpleAllocatorBlock; + +//////////////////////////////////////////////////////////////////// +// Class : SimpleAllocator +// Description : An implementation of a very simple block allocator. +// This class can allocate ranges of nonnegative +// integers within a specified upper limit; it uses a +// simple first-fit algorithm to find the next available +// space. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA SimpleAllocator : public LinkedListNode { +PUBLISHED: + INLINE SimpleAllocator(size_t max_size); + ~SimpleAllocator(); + + SimpleAllocatorBlock *alloc(size_t size); + + INLINE size_t get_total_size() const; + INLINE size_t get_max_size() const; + INLINE void set_max_size(size_t max_size); + + void output(ostream &out) const; + void write(ostream &out) const; + +private: + // This is implemented as a linked-list chain of allocated blocks. + // Free blocks are implicit. Blocks are kept in sorted order from + // beginning to end. Allocating a block means creating a new entry + // in the chain wherever it may fit; freeing a block means simply + // removing the allocated block from the chain. With this simple + // approach, there is no need to merge adjacent free blocks to + // straighten out fragmentation, since free blocks are not stored. + // However, it does mean we have to walk through a list of adjacent + // allocated blocks in order to find the free blocks. + size_t _total_size; + size_t _max_size; + + friend class SimpleAllocatorBlock; +}; + +//////////////////////////////////////////////////////////////////// +// Class : SimpleAllocatorBlock +// Description : A single block as returned from +// SimpleAllocator::alloc(). +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA SimpleAllocatorBlock : public LinkedListNode { +private: + INLINE SimpleAllocatorBlock(SimpleAllocator *alloc, + size_t start, size_t size); + +PUBLISHED: + INLINE ~SimpleAllocatorBlock(); + INLINE void free(); + + INLINE SimpleAllocator *get_allocator() const; + + INLINE size_t get_start() const; + INLINE size_t get_size() const; + INLINE bool is_free() const; + + void output(ostream &out) const; + +private: + SimpleAllocator *_allocator; + size_t _start; + size_t _size; + + friend class SimpleAllocator; +}; + +INLINE ostream &operator << (ostream &out, const SimpleAllocator &obj) { + obj.output(out); + return out; +} + +INLINE ostream &operator << (ostream &out, const SimpleAllocatorBlock &obj) { + obj.output(out); + return out; +} + +#include "simpleAllocator.I" + +#endif diff --git a/panda/src/gobj/vertexDataSaveFile.I b/panda/src/gobj/vertexDataSaveFile.I new file mode 100644 index 0000000000..f4b6a31af5 --- /dev/null +++ b/panda/src/gobj/vertexDataSaveFile.I @@ -0,0 +1,18 @@ +// Filename: vertexDataSaveFile.I +// Created by: drose (12May07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + diff --git a/panda/src/gobj/vertexDataSaveFile.cxx b/panda/src/gobj/vertexDataSaveFile.cxx new file mode 100644 index 0000000000..3b596a56d7 --- /dev/null +++ b/panda/src/gobj/vertexDataSaveFile.cxx @@ -0,0 +1,204 @@ +// Filename: vertexDataSaveFile.cxx +// Created by: drose (12May07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "vertexDataSaveFile.h" + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataSaveFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +VertexDataSaveFile:: +VertexDataSaveFile(const Filename &directory, const string &prefix, + size_t max_size) : + _allocator(max_size) +{ + // Try to open and lock a writable temporary filename. + int index = 0; + while (true) { + ++index; + ostringstream strm; + strm << prefix << "_" << index << ".dat"; + string basename = strm.str(); + _filename = Filename(directory, basename); + string os_specific = _filename.to_os_specific(); + +#ifdef _WIN32 + // Windows case. + _handle = CreateFile(os_specific.c_str(), GENERIC_READ | GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_RANDOM_ACCESS, + NULL); + if (_handle == INVALID_HANDLE_VALUE) { + // Couldn't open the file. Either the directory was bad, or the + // file was locked. + DWORD err = GetLastError(); + if (err == ERROR_SHARING_VIOLATION) { + // Try the next file. + break; + } + + // File couldn't be opened; permission problem or bogus directory. + // TODO: handle this + } + +#else + // Posix case. + _fd = open(os_specific.c_str(), O_RDWR | O_CREAT, 0666); + nassertv(_fd != -1); // TODO: handle this. + + // Now try to lock the file, so we can be sure that no other + // process is simultaneously writing to the same save file. + int result = lockf(_fd, F_TLOCK, 0); + if (result == 0) { + // We've got the file. Truncate it first, for good measure, in + // case there's an old version of the file we picked up. + ftruncate(_fd, 0); + + // On Unix, it's safe to unlink (delete) the temporary file after + // it's been opened. The file remains open, but disappears from + // the directory. This ensures it won't accidentally get left + // behind should this process crash without closing and deleting + // the file cleanly. + //unlink(os_specific.c_str()); + //_filename = Filename(); + break; + } + + // Try the next file. + close(_fd); +#endif // _WIN32 + } +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataSaveFile::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +VertexDataSaveFile:: +~VertexDataSaveFile() { +#ifdef _WIN32 + if (_handle != NULL) { + CloseHandle(_handle); + } +#else + if (_fd != -1) { + close(_fd); + } +#endif // _WIN32 + + if (!_filename.empty()) { + _filename.unlink(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataSaveFile::write_data +// Access: Public +// Description: Writes a block of data to the file, and returns a +// handle to the block handle. Returns NULL if the data +// cannot be written (e.g. no remaining space on the +// file). +//////////////////////////////////////////////////////////////////// +SimpleAllocatorBlock *VertexDataSaveFile:: +write_data(const unsigned char *data, size_t size) { + SimpleAllocatorBlock *block = _allocator.alloc(size); + if (block != (SimpleAllocatorBlock *)NULL) { + +#ifdef _WIN32 + if (SetFilePointer(_handle, block->get_start(), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + gobj_cat.error() + << "Error seeking to position " << block->get_start() << " in save file.\n"; + return false; + } + + DWORD bytes_written; + if (!WriteFile(_handle, data, size, &bytes_written, NULL) || + bytes_written != size) { + gobj_cat.error() + << "Error writing " << size << " bytes to save file. Disk full?\n"; + delete block; + return NULL; + } + +#else + // Posix case. + if (lseek(_fd, block->get_start(), SEEK_SET) == -1) { + gobj_cat.error() + << "Error seeking to position " << block->get_start() << " in save file.\n"; + return false; + } + + ssize_t result = write(_fd, data, size); + if (result != (ssize_t)size) { + gobj_cat.error() + << "Error writing " << size << " bytes to save file. Disk full?\n"; + delete block; + return NULL; + } +#endif // _WIN32 + + } + + return block; +} + +//////////////////////////////////////////////////////////////////// +// Function: VertexDataSaveFile::read_data +// Access: Public +// Description: Reads a block of data from the file, and returns true +// on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool VertexDataSaveFile:: +read_data(unsigned char *data, size_t size, SimpleAllocatorBlock *block) { + nassertr(size == block->get_size(), false); + +#ifdef _WIN32 + if (SetFilePointer(_handle, block->get_start(), NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { + gobj_cat.error() + << "Error seeking to position " << block->get_start() << " in save file.\n"; + return false; + } + + DWORD bytes_read; + if (!ReadFile(_handle, data, size, &bytes_read, NULL) || + bytes_read != size) { + gobj_cat.error() + << "Error writing " << size << " bytes to save file. Disk full?\n"; + delete block; + return NULL; + } + +#else + // Posix case. + if (lseek(_fd, block->get_start(), SEEK_SET) == -1) { + gobj_cat.error() + << "Error seeking to position " << block->get_start() << " in save file.\n"; + return false; + } + ssize_t result = read(_fd, data, size); + if (result != (ssize_t)size) { + gobj_cat.error() + << "Error reading " << size << " bytes from save file.\n"; + return false; + } +#endif // _WIN32 + + return true; +} diff --git a/panda/src/gobj/vertexDataSaveFile.h b/panda/src/gobj/vertexDataSaveFile.h new file mode 100644 index 0000000000..43bddf1162 --- /dev/null +++ b/panda/src/gobj/vertexDataSaveFile.h @@ -0,0 +1,61 @@ +// Filename: vertexDataSaveFile.h +// Created by: drose (12May07) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef VERTEXDATASAVEFILE_H +#define VERTEXDATASAVEFILE_H + +#include "pandabase.h" +#include "simpleAllocator.h" +#include "filename.h" + +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#include +#endif + +//////////////////////////////////////////////////////////////////// +// Class : VertexDataSaveFile +// Description : A temporary file to hold the vertex data that has +// been evicted from memory and written to disk. All +// vertex data arrays are written into one large flat +// file. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA VertexDataSaveFile { +public: + VertexDataSaveFile(const Filename &directory, const string &prefix, + size_t max_size); + ~VertexDataSaveFile(); + + SimpleAllocatorBlock *write_data(const unsigned char *data, size_t size); + bool read_data(unsigned char *data, size_t size, + SimpleAllocatorBlock *block); + +private: + SimpleAllocator _allocator; + Filename _filename; + +#ifdef _WIN32 + HANDLE _handle; +#else + int _fd; // Posix file descriptor +#endif // _WIN32 +}; + +#include "vertexDataSaveFile.I" + +#endif