first check in seems to work

This commit is contained in:
vasiloglou
2007-05-11 18:54:42 +00:00
parent fdf132eb4e
commit ffc0f8cdcd
104 changed files with 32649 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: ami.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/19/94
//
// $Id: ami.h,v 1.19 2003/04/17 11:59:40 jan Exp $
//
#ifndef _AMI_H
#define _AMI_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get a stream implementation.
#include <ami_stream.h>
// Get templates for ami_scan().
#include <ami_scan.h>
// Get templates for ami_merge().
#include <ami_merge.h>
// Get templates for ami_sort().
#include <ami_sort.h>
// Get templates for general permutation.
#include <ami_gen_perm.h>
// Get templates for bit permuting.
#include <ami_bit_permute.h>
// Get a collection implementation.
#include <ami_coll.h>
// Get a block implementation.
#include <ami_block.h>
// Get templates for AMI_btree.
#include <ami_btree.h>
#endif // _AMI_H
+35
View File
@@ -0,0 +1,35 @@
// Copyright (c) 1995 Darren Vengroff
//
// File: ami_bit_permute.cpp
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 1/9/95
//
#include <iostream>
#include <versions.h>
VERSION(ami_bit_permute_cpp,"$Id: ami_bit_permute.cpp,v 1.4 2003/04/20 06:44:01 tavi Exp $");
#include <ami_bit_permute.h>
AMI_bit_perm_object::AMI_bit_perm_object(const bit_matrix &A,
const bit_matrix &c) :
mA(A), mc(c)
{
}
AMI_bit_perm_object::~AMI_bit_perm_object(void)
{
}
bit_matrix AMI_bit_perm_object::A(void)
{
return mA;
}
bit_matrix AMI_bit_perm_object::c(void)
{
return mc;
}
+126
View File
@@ -0,0 +1,126 @@
// Copyright (c) 1995 Darren Vengroff
//
// File: ami_bit_permute.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 1/9/95
//
// $Id: ami_bit_permute.h,v 1.8 2004/08/12 12:35:29 jan Exp $
//
// For the moment this is done in terms of general permutations.
// This will obviously change in the future.
//
#ifndef _AMI_BIT_PERMUTE_H
#define _AMI_BIT_PERMUTE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get bit_matrix.
#include <bit_matrix.h>
// Get AMI_gen_perm_object.
#include <ami_gen_perm_object.h>
// Get the AMI_general_permute().
#include <ami_gen_perm.h>
class AMI_bit_perm_object {
private:
// The matrices that define the permutation.
bit_matrix mA;
bit_matrix mc;
public:
AMI_bit_perm_object(const bit_matrix &A,
const bit_matrix &c);
~AMI_bit_perm_object(void);
bit_matrix A(void);
bit_matrix c(void);
};
template<class T>
class bmmc_as_gen_po : public AMI_gen_perm_object {
private:
bit_matrix *src_bits;
bit_matrix A;
bit_matrix c;
public:
bmmc_as_gen_po(AMI_bit_perm_object &bpo) :
A(bpo.A()), c(bpo.c())
{
tp_assert(A.rows() == A.cols(), "A is not square.");
tp_assert(c.cols() == 1, "c is not a column vector.");
tp_assert(c.rows() == A.cols(), "A and c dimensions do not match.");
src_bits = new bit_matrix(c.rows(),1);
};
AMI_err initialize(TPIE_OS_OFFSET /*stream_len*/) {
return AMI_ERROR_NO_ERROR;
}
TPIE_OS_OFFSET destination(TPIE_OS_OFFSET input_offset) {
*src_bits = input_offset;
bit_matrix r1 = A * *src_bits;
bit_matrix res = r1 + c;
return TPIE_OS_OFFSET(res);
}
};
#ifndef TPIE_LIBRARY
template<class T>
AMI_err AMI_BMMC_permute(AMI_STREAM<T> *instream, AMI_STREAM<T> *outstream,
AMI_bit_perm_object *bpo)
{
TPIE_OS_OFFSET sz_len = instream->stream_len();
TPIE_OS_OFFSET sz_pow2;
unsigned int bits;
// Make sure the length of the input stream is a power of two.
for (sz_pow2 = 1, bits = 0; sz_pow2 < sz_len; sz_pow2 += sz_pow2) {
bits++;
}
if (sz_pow2 != sz_len) {
return AMI_ERROR_NOT_POWER_OF_2;
}
// Make sure the number of bits in the permutation matrix matches
// the log of the number of items in the input stream.
{
bit_matrix A = bpo->A();
bit_matrix c = bpo->c();
if (A.rows() != bits) {
return AMI_ERROR_BIT_MATRIX_BOUNDS;
}
if (A.cols() != bits) {
return AMI_ERROR_BIT_MATRIX_BOUNDS;
}
if (c.rows() != bits) {
return AMI_ERROR_BIT_MATRIX_BOUNDS;
}
if (c.cols() != 1) {
return AMI_ERROR_BIT_MATRIX_BOUNDS;
}
}
// Create the general permutation object.
bmmc_as_gen_po<T> gpo(*bpo);
// Do the permutation.
return AMI_general_permute(instream, outstream,
(AMI_gen_perm_object *)&gpo);
}
#endif // ndef TPIE_LIBRARY
#endif // _AMI_BIT_PERMUTE_H
+98
View File
@@ -0,0 +1,98 @@
// Copyright (C) 2001 Octavian Procopiuc
//
// File: ami_block.h
// Authors: Octavian Procopiuc <tavi@cs.duke.edu>
//
// Definition and implementation of the AMI_block class.
//
// $Id: ami_block.h,v 1.9 2005/01/21 17:29:26 tavi Exp $
//
#ifndef _AMI_BLOCK_H
#define _AMI_BLOCK_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// The AMI_block_base class.
#include <ami_block_base.h>
// The b_vector class.
#include <b_vector.h>
template<class E, class I, class BTECOLL = BTE_COLLECTION >
class AMI_block: public AMI_block_base<BTECOLL> {
protected:
using AMI_block_base<BTECOLL>::bid_;
using AMI_block_base<BTECOLL>::dirty_;
using AMI_block_base<BTECOLL>::pdata_;
using AMI_block_base<BTECOLL>::per_;
using AMI_block_base<BTECOLL>::pcoll_;
public:
using AMI_block_base<BTECOLL>::bid;
// typedef typename BTECOLL::block_id_t id_t;
// The array of links.
b_vector<AMI_bid> lk;
// The array of elements.
b_vector<E> el;
typedef typename b_vector<AMI_bid>::iterator lk_iterator;
typedef typename b_vector<E>::iterator el_iterator;
public:
// Compute the capacity of the el vector statically (but you have to
// give the correct block size and number of links!). It can also be
// used to figure out how many elements would fit into a block with
// a given size and a given number of links.
static size_t el_capacity(size_t block_size, size_t links);
// Constructor. Read and initialize a block with a given ID. If the
// ID is missing or 0, a new block is created.
AMI_block(AMI_collection_single<BTECOLL>* pacoll, size_t links, AMI_bid bid = 0);
// Get a reference to the info field.
I* info();
const I* info() const;
};
////////// ***Implementation*** ///////////
////////////////////////////////////
////////// **AMI_block** ///////////
////////////////////////////////////
template<class E, class I, class BTECOLL>
size_t AMI_block<E,I,BTECOLL>::el_capacity(size_t block_size, size_t links) {
return (size_t) ((block_size - sizeof(I) - links * sizeof(AMI_bid)) / sizeof(E));
}
template<class E, class I, class BTECOLL>
AMI_block<E,I,BTECOLL>::AMI_block(AMI_collection_single<BTECOLL>* pacoll,
size_t links, AMI_bid _bid):
AMI_block_base<BTECOLL>(pacoll, _bid),
lk((AMI_bid*)pdata_, links),
el((E*) ((char*) pdata_ + links * sizeof(AMI_bid)),
el_capacity(pcoll_->block_size(), links))
{
}
template<class E, class I, class BTECOLL>
I* AMI_block<E,I,BTECOLL>::info() {
return (I*) (((char*) pdata_ + (lk.capacity()*sizeof(AMI_bid) +
el.capacity()*sizeof(E))));
}
template<class E, class I, class BTECOLL>
const I* AMI_block<E,I,BTECOLL>::info() const {
return (I*) (((char*) pdata_ + (lk.capacity()*sizeof(AMI_bid) +
el.capacity()*sizeof(E))));
}
#endif // _AMI_BLOCK_H
+136
View File
@@ -0,0 +1,136 @@
// Copyright (C) 2001 Octavian Procopiuc
//
// File: ami_block_base.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: ami_block_base.h,v 1.14 2004/08/17 16:47:38 jan Exp $
//
// Definition of the AMI_block_base class and supporting types:
// AMI_bid, AMI_block_status.
//
#ifndef _AMI_BLOCK_BASE_H
#define _AMI_BLOCK_BASE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// The AMI error codes.
#include <ami_err.h>
// The AMI_COLLECTION class.
#include <ami_coll.h>
// AMI block id type.
typedef TPIE_BLOCK_ID_TYPE AMI_bid;
// Block status type.
enum AMI_block_status {
AMI_BLOCK_STATUS_VALID = 0,
AMI_BLOCK_STATUS_INVALID = 1
};
template<class BTECOLL>
class AMI_block_base {
public:
// typedef typename BTECOLL::block_id_t id_t;
protected:
// Pointer to the block collection.
BTECOLL * pcoll_;
// Unique ID. Represents the offset of the block in the blocks file.
AMI_bid bid_;
// Dirty bit. If set, the block needs to be written back.
char dirty_;
// Pointer to the actual data.
void * pdata_;
// Persistence flag.
persistence per_;
public:
// Constructor.
// Read and initialize a block with a given ID.
// When bid is missing or 0, a new block is created.
AMI_block_base(AMI_collection_single<BTECOLL>* pacoll, AMI_bid bid = 0)
: bid_(bid), dirty_(0), per_(PERSIST_PERSISTENT) {
pcoll_ = pacoll->bte();
if (bid != 0) {
// Get an existing block from disk.
if (pcoll_->get_block(bid_, pdata_) != BTE_ERROR_NO_ERROR)
pdata_ = NULL;
} else {
// Create a new block in the collection.
if (pcoll_->new_block(bid_, pdata_) != BTE_ERROR_NO_ERROR)
pdata_ = NULL;
}
}
AMI_err sync() {
if (pcoll_->sync_block(bid_, pdata_) != BTE_ERROR_NO_ERROR)
return AMI_ERROR_BTE_ERROR;
else
return AMI_ERROR_NO_ERROR;
}
// Get the block id.
AMI_bid bid() const { return bid_; }
// Get a reference to the dirty bit.
char& dirty() { return dirty_; };
char dirty() const { return dirty_; }
// Copy block rhs into this block.
AMI_block_base<BTECOLL>& operator=(const AMI_block_base<BTECOLL>& rhs) {
if (pcoll_ == rhs.pcoll_) {
memcpy(pdata_, rhs.pdata_, pcoll_->block_size());
dirty_ = 1;
} else
pdata_ = NULL;
return *this;
}
// Get the block's status.
AMI_block_status status() const {
return (pdata_ == NULL) ?
AMI_BLOCK_STATUS_INVALID: AMI_BLOCK_STATUS_VALID;
}
// Return true if the block is valid.
bool is_valid() const {
return (pdata_ != NULL);
}
// Return true if the block is invalid.
bool operator!() const {
return (pdata_ == NULL);
}
void persist(persistence per) { per_ = per; }
persistence persist() const { return per_; }
size_t block_size() const { return pcoll_->block_size(); }
// Destructor.
~AMI_block_base() {
// Check first the status of the collection.
if (pdata_ != NULL){
if (per_ == PERSIST_PERSISTENT) {
// Write back the block.
pcoll_->put_block(bid_, pdata_);
} else {
// Delete the block from the collection.
pcoll_->delete_block(bid_, pdata_);
}
}
}
};
#endif //_AMI_BLOCK_BASE_H
File diff suppressed because it is too large Load Diff
+238
View File
@@ -0,0 +1,238 @@
// Copyright (C) 2001 Octavian Procopiuc
//
// File: ami_cache.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: ami_cache.h,v 1.10 2004/08/12 12:35:30 jan Exp $
//
// Declaration and definition of AMI_CACHE_MANAGER
// implementation(s).
//
#ifndef _AMI_CACHE_H
#define _AMI_CACHE_H
// Get the STL pair class.
#include <utility>
// Get the logging macros.
#include <tpie_log.h>
// Get the b_vector class.
#include <b_vector.h>
// The only implementation is AMI_cache_manager_lru.
#define AMI_CACHE_MANAGER AMI_cache_manager_lru
// Base class for all implementations.
class AMI_cache_manager_base {
protected:
// Max size.
TPIE_OS_SIZE_T capacity_;
// Associativity.
TPIE_OS_SIZE_T assoc_;
// Behavior.
int behavior_;
// Constructor. Protected to prevent instantiation of this class.
AMI_cache_manager_base(TPIE_OS_SIZE_T capacity, TPIE_OS_SIZE_T assoc):
capacity_(capacity), assoc_(assoc), behavior_(0) {}
public:
// Set behavior. TODO: Expand.
int behavior(int b) { behavior_ = b; return behavior_; }
// Inquire behavior.
int behavior() const { return behavior_; }
};
// Implementation using an LRU replacement policy.
template<class T, class W>
class AMI_cache_manager_lru: public AMI_cache_manager_base {
protected:
typedef pair<TPIE_OS_OFFSET,T> item_type_;
// The array of items.
item_type_ * pdata_;
// The number of sets (equals capacity / associativity).
TPIE_OS_SIZE_T sets_;
// The writeout function object.
W writeout_;
public:
AMI_cache_manager_lru(TPIE_OS_SIZE_T capacity, TPIE_OS_SIZE_T assoc = 0);
// Read an item from the cache based on the key k. The item is
// passed to the user and *removed* from the cache (but not written
// out).
bool read(TPIE_OS_OFFSET k, T& item);
// Write an item to the cache based on the key k. If the set where
// the item should go is full, the last item (ie, the l.r.u. item)
// is written out.
bool write(TPIE_OS_OFFSET k, const T& item);
// Erase an item from the cache based on the key k. The item is
// written out first.
bool erase(TPIE_OS_OFFSET k);
// Write out all items in the cache.
void flush();
~AMI_cache_manager_lru();
};
template<class T, class W>
AMI_cache_manager_lru<T,W>::AMI_cache_manager_lru(size_t capacity, size_t assoc):
AMI_cache_manager_base(capacity, assoc == 0 ? capacity: assoc), writeout_() {
size_t i;
if (capacity_ != 0) {
if (assoc_ > capacity_) {
TP_LOG_WARNING_ID("Associativity too big.");
TP_LOG_WARNING_ID("Associativity reduced to capacity.");
assoc_ = capacity_;
}
if (capacity_ % assoc_ != 0) {
TP_LOG_WARNING_ID("Capacity is not multiple of associativity.");
TP_LOG_WARNING_ID("Capacity reduced.");
capacity_ = (capacity_ / assoc_) * assoc_;
}
// The number of cache lines.
sets_ = capacity_ / assoc_;
// Initialize the array (mark all positions empty).
pdata_ = new item_type_[capacity_];
for (i = 0; i < capacity_; i++) {
pdata_[i].first = 0;
}
} else {
pdata_ = NULL;
sets_ = 0;
}
}
template<class T, class W>
inline bool AMI_cache_manager_lru<T,W>::read(TPIE_OS_OFFSET k, T& item) {
TPIE_OS_SIZE_T i;
if (capacity_ == 0)
return false;
assert(k != 0);
// The cache line, based on the key k.
b_vector<item_type_> set(&pdata_[(k % sets_) * assoc_], assoc_);
// Find the item using the key.
for (i = 0; i < assoc_; i++) {
if (set[i].first == k)
break;
}
if (i == assoc_)
return false;
// memcpy(&item, &set[i].second, sizeof(T));
item = set[i].second;
// Erase the item from the cache.
// NB: We don't write it out because we pass it up to the user.
if (assoc_ > 1)
set.erase(i);
// Mark the last item empty.
set[assoc_ - 1].first = 0;
return true;
}
template<class T, class W>
inline bool AMI_cache_manager_lru<T,W>::write(TPIE_OS_OFFSET k, const T& item) {
assert(k != 0);
if (capacity_ == 0) {
writeout_(item);
} else {
// The cache line, based on the key k.
b_vector<item_type_> set(&pdata_[(k % sets_) * assoc_], assoc_);
// Write out the item in the last position.
if (set[assoc_ - 1].first != 0) {
writeout_(set[assoc_ - 1].second);
}
// Insert in the first position.
if (assoc_ > 1)
set.insert(item_type_(k, item), 0);
else {
set[0] = item_type_(k, item);
}
}
return true;
}
template<class T, class W>
bool AMI_cache_manager_lru<T,W>::erase(TPIE_OS_OFFSET k) {
TPIE_OS_SIZE_T i;
assert(k != 0);
// The cache line, based on the key k.
b_vector<item_type_> set(&pdata_[(k % sets_) * assoc_], assoc_);
// Find the item using the key.
for (i = 0; i < set.capacity(); i++) {
if (set[i].first == k)
break;
}
// If not found, return false.
if (i == set.capacity())
return false;
// Write out the item in position i;
writeout_(set[i].second);
// Erase the item from the cache.
set.erase(i);
// Mark last item in the set as empty.
set[set.capacity() - 1].first = 0;
return true;
}
template<class T, class W>
void AMI_cache_manager_lru<T,W>::flush() {
TPIE_OS_SIZE_T i;
for (i = 0; i < capacity_; i++) {
if (pdata_[i].first != 0) {
writeout_(pdata_[i].second);
pdata_[i].first = 0;
}
}
}
template<class T, class W>
AMI_cache_manager_lru<T,W>::~AMI_cache_manager_lru() {
flush();
if (capacity_ > 0) {
delete [] pdata_;
}
}
#endif // _AMI_CACHE_H
+27
View File
@@ -0,0 +1,27 @@
//
// File: ami_coll.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: ami_coll.h,v 1.8 2003/05/08 22:12:21 tavi Exp $
//
// Front end for the AMI_COLLECTION implementations.
//
#ifndef _AMI_COLL_H
#define _AMI_COLL_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <ami_coll_base.h>
#include <ami_coll_single.h>
// AMI_collection_single is the only implementation, so make it easy
// to get to.
#define AMI_collection AMI_collection_single
#ifdef BTE_COLLECTION
# define AMI_COLLECTION AMI_collection_single< BTE_COLLECTION >
#endif
#endif // _AMI_COLL_H
+29
View File
@@ -0,0 +1,29 @@
// Copyright (C) 2001 Octavian Procopiuc
//
// File: ami_coll_base.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: ami_coll_base.h,v 1.4 2004/08/17 16:47:45 jan Exp $
//
// Basic definitions for all AMI_COLLECTION implementations.
//
#ifndef _AMI_COLL_BASE_H
#define _AMI_COLL_BASE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// AMI collection types passed to constructors
enum AMI_collection_type {
AMI_READ_COLLECTION = 1, // Open existing collection for reading
AMI_WRITE_COLLECTION, // Open for writing. Create if non-existent
AMI_READ_WRITE_COLLECTION // Open to read and write.
};
// AMI collection status.
enum AMI_collection_status {
AMI_COLLECTION_STATUS_VALID = 0,
AMI_COLLECTION_STATUS_INVALID = 1
};
#endif // _AMI_COLL_BASE_H
+117
View File
@@ -0,0 +1,117 @@
// Copyright (C) 2001 Octavian Procopiuc
//
// File: ami_coll_single.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: ami_coll_single.h,v 1.14 2004/08/12 12:35:30 jan Exp $
//
// AMI collection entry points implemented on top of a single BTE.
//
#ifndef _AMI_COLL_SINGLE_H
#define _AMI_COLL_SINGLE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// For persist type.
#include <persist.h>
// Get an appropriate BTE collection.
#include <bte_coll.h>
// For AMI_collection_type and AMI_collection_status.
#include <ami_coll_base.h>
// The tpie_tempnam() function.
#include <tpie_tempnam.h>
// Get the tpie_stats_coll class for collection statistics.
#include <tpie_stats_coll.h>
template < class BTECOLL = BTE_COLLECTION >
class AMI_collection_single {
public:
// Initialize a temporary collection.
AMI_collection_single(TPIE_OS_SIZE_T logical_block_factor = 1);
// Initialize a named collection.
AMI_collection_single(char* path_name,
AMI_collection_type ct = AMI_READ_WRITE_COLLECTION,
TPIE_OS_SIZE_T logical_block_factor = 1);
// Return the total number of used blocks.
TPIE_OS_OFFSET size() const { return btec_->size(); }
// Return the logical block size in bytes.
TPIE_OS_SIZE_T block_size() const { return btec_->block_size(); }
// Return the logical block factor.
TPIE_OS_SIZE_T block_factor() const { return btec_->block_factor(); }
// Set the persistence flag.
void persist(persistence p) { btec_->persist(p); }
// Inquire the persistence status.
persistence persist() const { return btec_->persist(); }
// Inquire the status.
AMI_collection_status status() const { return status_; }
bool is_valid() const { return status_ == AMI_COLLECTION_STATUS_VALID; }
bool operator!() const { return !is_valid(); }
// User data to be stored in the header.
void *user_data() { return btec_->user_data(); }
// Destructor.
~AMI_collection_single() { delete btec_; }
BTECOLL* bte() { return btec_; }
const tpie_stats_collection& stats() const { return btec_->stats(); }
static const tpie_stats_collection& gstats()
{ return BTECOLL::gstats(); }
private:
BTECOLL *btec_;
AMI_collection_status status_;
// Allow AMI_block base direct access to the BTE_COLLECTION.
// friend class AMI_block_base;
};
template <class BTECOLL>
AMI_collection_single<BTECOLL>::AMI_collection_single(TPIE_OS_SIZE_T lbf) {
char *temp_path = tpie_tempnam("AMI");
btec_ = new BTECOLL(temp_path, BTE_WRITE_COLLECTION, lbf);
tp_assert(btec_ != NULL, "new failed to create a new BTE_COLLECTION.");
btec_->persist(PERSIST_DELETE);
if (btec_->status() == BTE_COLLECTION_STATUS_VALID)
status_ = AMI_COLLECTION_STATUS_VALID;
else
status_ = AMI_COLLECTION_STATUS_INVALID;
}
template <class BTECOLL>
AMI_collection_single<BTECOLL>::AMI_collection_single(char* path_name,
AMI_collection_type ct, TPIE_OS_SIZE_T lbf) {
BTE_collection_type btect;
if (ct == AMI_READ_COLLECTION)
btect = BTE_READ_COLLECTION;
else
btect = BTE_WRITE_COLLECTION;
btec_ = new BTECOLL(path_name, btect, lbf);
tp_assert(btec_ != NULL, "new failed to create a new BTE_COLLECTION.");
btec_->persist(PERSIST_PERSISTENT);
if (btec_->status() == BTE_COLLECTION_STATUS_VALID)
status_ = AMI_COLLECTION_STATUS_VALID;
else
status_ = AMI_COLLECTION_STATUS_INVALID;
}
#endif // _AMI_COLL_SINGLE_H
+146
View File
@@ -0,0 +1,146 @@
//
// File: ami_device.cpp
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 8/22/93
//
#include "versions.h"
VERSION(ami_device_cpp,"$Id: ami_device.cpp,v 1.14 2004/08/12 12:53:42 jan Exp $");
#include "lib_config.h"
#include <stdlib.h>
#include <string.h>
#include <ami_err.h>
#include <ami_device.h>
AMI_device::AMI_device(void)
{
TP_LOG_DEBUG_ID("In AMI_device(void).");
}
AMI_device::AMI_device(unsigned int count, char **strings)
{
char *s, *t;
argc = count;
if (argc) {
argv = new char*[argc];
while (count--) {
argv[count] = new char[strlen(strings[count]) + 1];
// for (s = strings[count], t = argv[count]; *t++ = *s++; )
// [tavi] modified to avoid warning.
for (s = strings[count], t = argv[count]; *t; *t++ = *s++)
;
}
} else {
argv = NULL;
}
}
AMI_device::~AMI_device(void)
{
dispose_contents();
}
const char * AMI_device::operator[](unsigned int index)
{
return argv[index];
}
unsigned int AMI_device::arity()
{
return argc;
}
void AMI_device::dispose_contents(void)
{
if (argc) {
while (argc--) {
delete argv[argc];
}
tp_assert((argv != NULL), "Nonzero argc and NULL argv.");
delete argv;
}
}
AMI_err AMI_device::set_to_path(const char *path)
{
const char *s, *t;
unsigned int ii;
dispose_contents();
// Count the components
for (argc = 1, s = path; *s; s++) {
if (*s == '|') {
argc++;
}
}
argv = new char*[argc];
// copy the components one by one. t points to the start of the
// current component and s is used to scan to the end of it.
for (ii = 0, s = t = path; ii < argc; ii++, t = ++s) {
// Move past the current component.
while (*s && (*s != '|'))
s++;
tp_assert(((*s == '|') || (ii == argc - 1)),
"Path ended before all components found.");
// Copy the current component.
argv[ii] = new char[s - t + 1];
strncpy(argv[ii], t, s - t);
argv[ii][s - t] = '\0';
// make sure there is no trailing /
for(TPIE_OS_LONGLONG i=s-t-1; i && (argv[ii][i]) == '/'; i--) {
argv[ii][i] = '\0';
}
tp_assert(strlen(argv[ii]) > 0, "non-null path specified");
tp_assert(strlen(argv[ii]) == 1 ||
argv[ii][strlen(argv[ii])] != '/', "no / suffix");
}
return AMI_ERROR_NO_ERROR;
}
AMI_err AMI_device::read_environment(const char *name)
{
char *env_value = getenv(name);
if (env_value == NULL) {
return AMI_ERROR_ENV_UNDEFINED;
}
return set_to_path(env_value);
}
// Output of a device description:
ostream &operator<<(ostream &os, const AMI_device &dev)
{
unsigned int ii;
for (ii = 0; ii < dev.argc; ii++) {
os << dev.argv[ii];
if (ii < dev.argc - 1) {
os << '|';
}
}
return os;
}
+38
View File
@@ -0,0 +1,38 @@
// Copyright (c) 1993 Darren Erik Vengroff
//
// File: ami_device.h
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 8/22/93
//
// $Id: ami_device.h,v 1.4 2003/09/12 01:44:29 jan Exp $
//
#ifndef _AMI_DEVICE_H
#define _AMI_DEVICE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <iostream>
class AMI_device {
friend ostream &operator<<(ostream &os, const AMI_device &dev);
private:
void dispose_contents(void);
protected:
unsigned int argc;
char **argv;
public:
AMI_device(void);
AMI_device(unsigned int count, char **strings);
~AMI_device(void);
AMI_err set_to_path(const char *path);
AMI_err read_environment(const char *name);
const char * operator[](unsigned int index);
unsigned int arity(void);
};
// Output operator
ostream &operator<<(ostream &os, const AMI_device &dev);
#endif // _AMI_DEVICE_H
+55
View File
@@ -0,0 +1,55 @@
//
// File: ami_err.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
// (from Darren's ami_base.h)
// Created: 12/29/01
// $Id: ami_err.h,v 1.4 2005/07/07 20:38:39 adanner Exp $
//
// AMI error codes, moved here from ami_base.h
//
#ifndef _AMI_ERR_H
#define _AMI_ERR_H
// AMI error codes are returned using the AMI_err type.
enum AMI_err {
AMI_ERROR_NO_ERROR = 0,
AMI_ERROR_IO_ERROR,
AMI_ERROR_END_OF_STREAM,
AMI_ERROR_READ_ONLY,
AMI_ERROR_OS_ERROR,
AMI_ERROR_BASE_METHOD,
AMI_ERROR_BTE_ERROR,
AMI_ERROR_MM_ERROR,
AMI_ERROR_OBJECT_INITIALIZATION,
AMI_ERROR_OBJECT_INVALID,
AMI_ERROR_PERMISSION_DENIED,
AMI_ERROR_INSUFFICIENT_MAIN_MEMORY,
AMI_ERROR_INSUFFICIENT_AVAILABLE_STREAMS,
AMI_ERROR_ENV_UNDEFINED,
AMI_ERROR_NO_MAIN_MEMORY_OPERATION,
AMI_ERROR_BIT_MATRIX_BOUNDS,
AMI_ERROR_NOT_POWER_OF_2,
AMI_ERROR_NULL_POINTER,
AMI_ERROR_GENERIC_ERROR = 0xfff,
// Values returned by scan objects.
AMI_SCAN_DONE = 0x1000,
AMI_SCAN_CONTINUE,
// Values returned by merge objects.
AMI_MERGE_DONE = 0x2000,
AMI_MERGE_CONTINUE,
AMI_MERGE_OUTPUT,
AMI_MERGE_READ_MULTIPLE,
// Matrix related errors
AMI_MATRIX_BOUNDS = 0x3000,
// Values returned by sort routines.
AMI_SORT_ALREADY_SORTED = 0x4000
};
#endif // _AMI_ERR_H
+141
View File
@@ -0,0 +1,141 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: ami_gen_perm.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 11/1/94
//
// $Id: ami_gen_perm.h,v 1.14 2004/08/12 12:35:30 jan Exp $
//
// General permutation.
//
#ifndef _AMI_GEN_PERM_H
#define _AMI_GEN_PERM_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get AMI_scan_object.
#include <ami_scan.h>
// Get AMI_sort
#include <ami_sort.h>
#include <ami_gen_perm_object.h>
// (tavi) moved dest_obj definition down due to error in gcc 2.8.1
template<class T> class dest_obj;
// A comparison operator that simply compares destinations (for sorting).
template<class T>
int operator<(const dest_obj<T> &s, const dest_obj<T> &t)
{
return s.dest < t.dest;
}
template<class T>
int operator>(const dest_obj<T> &s, const dest_obj<T> &t)
{
return s.dest > t.dest;
}
template<class T>
class gen_perm_add_dest : AMI_scan_object {
private:
AMI_gen_perm_object *pgp;
off_t input_offset;
public:
gen_perm_add_dest(AMI_gen_perm_object *gpo) : pgp(gpo) {};
virtual ~gen_perm_add_dest(void) {};
AMI_err initialize(void) { input_offset = 0; return AMI_ERROR_NO_ERROR; };
AMI_err operate(const T &in, AMI_SCAN_FLAG *sfin, dest_obj<T> *out,
AMI_SCAN_FLAG *sfout)
{
if (!(*sfout = *sfin)) {
return AMI_SCAN_DONE;
}
*out = dest_obj<T>(in, pgp->destination(input_offset++));
return AMI_SCAN_CONTINUE;
}
};
template<class T>
class gen_perm_strip_dest : AMI_scan_object {
public:
AMI_err initialize(void) { return AMI_ERROR_NO_ERROR; };
AMI_err operate(const dest_obj<T> &in, AMI_SCAN_FLAG *sfin, T *out,
AMI_SCAN_FLAG *sfout)
{
if (!(*sfout = *sfin)) {
return AMI_SCAN_DONE;
}
*out = in.t;
return AMI_SCAN_CONTINUE;
}
};
template<class T>
class dest_obj {
private:
T t;
TPIE_OS_OFFSET dest;
public:
dest_obj(void) {};
dest_obj(T t_in, TPIE_OS_OFFSET d) : t(t_in), dest(d) {};
~dest_obj(void) {};
// The second alternative caused problems on Win32 (jv)
//#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
friend int operator< <> (const dest_obj<T> &s, const dest_obj<T> &t);
friend int operator> <> (const dest_obj<T> &s, const dest_obj<T> &t);
//#else
// friend int operator< (const dest_obj<T> &s, const dest_obj<T> &t);
// friend int operator> (const dest_obj<T> &s, const dest_obj<T> &t);
//#endif
friend AMI_err gen_perm_strip_dest<T>::operate(const dest_obj<T> &in,
AMI_SCAN_FLAG *sfin, T *out,
AMI_SCAN_FLAG *sfout);
};
template<class T>
AMI_err AMI_general_permute(AMI_STREAM<T> *instream, AMI_STREAM<T> *outstream,
AMI_gen_perm_object *gpo) {
AMI_err ae;
gen_perm_add_dest<T> gpad(gpo);
gen_perm_strip_dest<T> gpsd;
AMI_STREAM< dest_obj<T> > sdo_in;
AMI_STREAM< dest_obj<T> > sdo_out;
// Initialize
ae = gpo->initialize(instream->stream_len());
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Scan the stream, producing an output stream that labels each
// item with its destination.
ae = AMI_scan((AMI_STREAM<T> *)instream, &gpad,
(AMI_STREAM< dest_obj<T> > *)&sdo_in);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Sort by destination.
ae = AMI_sort(&sdo_in, &sdo_out);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Scan to strip off the destinations.
ae = AMI_scan((AMI_STREAM< dest_obj<T> > *)&sdo_out, &gpsd,
(AMI_STREAM<T> *)outstream);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
return AMI_ERROR_NO_ERROR;
}
#endif // _AMI_GEN_PERM_H
@@ -0,0 +1,24 @@
//
// File: ami_gen_perm_object.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 12/15/94
//
// $Id: ami_gen_perm_object.h,v 1.4 2003/04/17 12:22:15 jan Exp $
//
#ifndef _AMI_GEN_PERM_OBJECT_H
#define _AMI_GEN_PERM_OBJECT_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// For AMI_err.
#include <ami_err.h>
// A class of object that computes permutation destinations.
class AMI_gen_perm_object {
public:
virtual AMI_err initialize(TPIE_OS_OFFSET len) = 0;
virtual TPIE_OS_OFFSET destination(TPIE_OS_OFFSET src) = 0;
};
#endif // _AMI_GEN_PERM_OBJECT_H
+242
View File
@@ -0,0 +1,242 @@
// Copyright (c) 1995 Darren Erik Vengroff
//
// File: ami_kb_dist.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 3/11/95
//
// $Id: ami_kb_dist.h,v 1.10 2004/08/12 12:35:30 jan Exp $
//
// Radix based distribution for single or striped AMI layers.
//
// Get definitions for working with Unix and Windows
#include <portability.h>
// If we have not already seen this file with KB_KEY undefined or
// KB_KEY is defined, we will process the file.
#if !(defined(_AMI_KB_DIST_H)) || defined(KB_KEY)
#ifdef KB_KEY
#define _KB_CONCAT(a,b) a ## b
#define _AMI_KB_DIST(kbk) _KB_CONCAT(AMI_kb_dist_,kbk)
#else
// KB_KEY is not defined, so set the flag so we won't come through
// this file again with KB_KEY unset, and set KB_KEY to kb_key
// temporarily. We also Set the macro for the name of the function
// defined in this file.
#define _AMI_KB_DIST_H
#define KB_KEY kb_key
#ifdef _HAVE_TEMP_KB_KEY_DEFINITION_
#error _HAVE_TEMP_KB_KEY_DEFINITION_ already defined.
#else
#define _HAVE_TEMP_KB_KEY_DEFINITION_
#endif
#define _AMI_KB_DIST(kbk) AMI_kb_dist
#endif
#include <ami_stream.h>
#include <ami_key.h>
// This is a hack. The reason it is here is that if AMI_STREAM<char>
// is used directly in the template for AMI_kb_dist() a parse error is
// generated at compile time. I suspect this may be a bug in the
// template instantiation code in g++ 2.6.3.
#ifndef _DEFINED_TYPE_AMISC_
#define _DEFINED_TYPE_AMISC_
typedef AMI_STREAM<char> type_amisc;
#endif
template<class T>
AMI_err _AMI_KB_DIST(KB_KEY)(AMI_STREAM<T> &instream,
type_amisc &name_stream,
const key_range &range, TPIE_OS_OFFSET &max_size)
{
AMI_err ae;
size_t sz_avail;
size_t single_stream_usage;
// How many ouput streams will there be?
unsigned int output_streams;
unsigned int ii;
// How much main memory do we have?
sz_avail = MM_manager.memory_available ();
// How much memory does a single stream need in the worst case?
if ((ae = instream.main_memory_usage(&single_stream_usage,
MM_STREAM_USAGE_MAXIMUM)) !=
AMI_ERROR_NO_ERROR) {
return ae;
}
// How many output streams can we buffer in that amount of space?
// Recall that we also need a pointer and a range for each stream.
output_streams = (unsigned int)((sz_avail - 2 * single_stream_usage) /
(single_stream_usage + sizeof(AMI_STREAM<T> *) + sizeof(range)));
// We need at least two output streams.
if (output_streams < 2) {
return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
}
// Make sure we don't use more streams than are available.
{
unsigned available_streams = instream.available_streams();
if ((available_streams != (unsigned)-1) &&
(available_streams < output_streams)) {
output_streams = available_streams;
}
}
#ifdef AMI_RADIX_POWER_OF_TWO
// Adjust the number of output streams so that it is a power of two.
#endif
// Create the output streams and initialize the ranges they cover to
// be empty.
AMI_STREAM<T> **out_streams = new AMI_STREAM<T> *[output_streams];
key_range *out_ranges = new key_range[output_streams];
for (ii = 0; ii < output_streams; ii++) {
// This needs to be fixed to eliminate the max size parameter.
// This should be done system-wide.
out_streams[ii] = new AMI_STREAM<T>;
out_ranges[ii].min = KEY_MAX;
out_ranges[ii].max = KEY_MIN;
}
// Scan the input putting each item in the right output stream.
instream.seek(0);
unsigned int index_denom = (((range.max - range.min) / output_streams)
+ 1);
while (1) {
T *in;
kb_key k;
ae = instream.read_item(&in);
if (ae == AMI_ERROR_END_OF_STREAM) {
break;
} else if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
k = (unsigned int)KB_KEY(*in);
#ifdef AMI_RADIX_POWER_OF_TWO
// Do it with shifting and masking.
#else
ii = (k - range.min) / index_denom;
#endif
ae = out_streams[ii]->write_item(*in);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
if (k < out_ranges[ii].min) {
out_ranges[ii].min = k;
}
if (k > out_ranges[ii].max) {
out_ranges[ii].max = k;
}
}
// Write the names and ranges of all non-empty output streams.
for (ii = 0, max_size = 0; ii < output_streams; ii++) {
char *stream_name;
TPIE_OS_OFFSET stream_len;
if ((stream_len = out_streams[ii]->stream_len()) > 0) {
// cerr << stream_len << '\n';
// Is it the biggest one so far?
if (stream_len > max_size) {
max_size = stream_len;
}
// Get the and write the name of the stream.
ae = out_streams[ii]->name(&stream_name);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
ae = name_stream.write_array(stream_name,
strlen(stream_name) + 1);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// For purposes of efficiency, and to avoid having another
// stream, we are going to cast the new range to an array of
// characters and tack it onto the stream name stream.
// We then do the same thing with the length of the stream.
ae = name_stream.write_array((const char *)(out_ranges+ii),
sizeof(key_range));
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
ae = name_stream.write_array((const char *)&stream_len,
sizeof(stream_len));
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// The semantics of name() are to allocate space for the
// buffer it returns. We are responsible for giving it back.
delete [] stream_name;
// Make this stream persist on disk since it is not empty.
out_streams[ii]->persist(PERSIST_PERSISTENT);
}
// Delete the stream.
delete out_streams[ii];
}
delete [] out_streams;
// We're done.
return AMI_ERROR_NO_ERROR;
}
#ifdef _HAVE_TEMP_KB_KEY_DEFINITION_
#undef _HAVE_TEMP_KB_KEY_DEFINITION_
#undef KB_KEY
#endif
#ifdef _KB_CONCAT
#undef _KB_CONCAT
#endif
#undef _AMI_KB_DIST
#endif // !(defined(_AMI_KB_DIST_H)) || defined(KB_KEY)
+519
View File
@@ -0,0 +1,519 @@
// Copyright (c) 1995 Darren Erik Vengroff
//
// File: ami_kb_sort.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 3/12/95
//
// $Id: ami_kb_sort.h,v 1.10 2004/08/12 12:35:30 jan Exp $
//
// This header file can be included in one of two ways, either with a
// KB_KEY macro defined, in which case it is assumed to be the name of
// a function (typically inline) for extracting a key from an object
// of type T, or undefined, in which case the conversion operator
// kb_key() will be used by default. This file can be included
// multiple times in the former case, but only once in the latter.
// If we have not already seen this file with KB_KEY undefined or
// KB_KEY is defined, we will process the file.
#if !(defined(_AMI_KB_SORT_H)) || defined(KB_KEY)
#include <iostream>
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <ami_key.h>
#include <ami_kb_dist.h>
#ifdef KB_KEY
#define _KB_CONCAT(a,b) a ## b
#define _AMI_KB_SORT(kbk) _KB_CONCAT(AMI_kb_sort_,kbk)
#define _AMI_MM_KB_SORT(kbk) _KB_CONCAT(AMI_mm_kb_sort_,kbk)
#define _AMI_KB_DIST(kbk) _KB_CONCAT(AMI_kb_dist_,kbk)
#else
// KB_KEY is not defined, so set the flag so we won't come through
// this file again with KB_KEY unset, and set KB_KEY to kb_key
// temporarily. We also Set the macro for the name of the function
// defined in this file.
#define _AMI_KB_SORT_H
#define KB_KEY kb_key
#ifdef _HAVE_TEMP_KB_KEY_DEFINITION_
#error _HAVE_TEMP_KB_KEY_DEFINITION_ already defined.
#else
#define _HAVE_TEMP_KB_KEY_DEFINITION_
#endif
#define _AMI_KB_SORT(kbk) AMI_kb_sort
#define _AMI_MM_KB_SORT(kbk) AMI_mm_kb_sort
#define _AMI_KB_DIST(kbk) AMI_kb_dist
#endif
#ifndef _DEFINED_STATIC_KEY_MIN_MAX
#define _DEFINED_STATIC_KEY_MIN_MAX
static key_range min_max(KEY_MIN, KEY_MAX);
#endif // _DEFINED_STATIC_KEY_MIN_MAX
#ifndef _AMI_BUCKET_LIST_ELEM
#define _AMI_BUCKET_LIST_ELEM
template<class T>
class AMI_bucket_list_elem
{
public:
T data;
AMI_bucket_list_elem<T> *next;
AMI_bucket_list_elem() : next(0) {};
~AMI_bucket_list_elem() {};
};
#endif
template<class T>
AMI_err _AMI_MM_KB_SORT(KB_KEY)(AMI_STREAM<T> &instream,
AMI_STREAM<T> &outstream,
const key_range &range);
template<class T>
AMI_err _AMI_KB_SORT(KB_KEY)(AMI_STREAM<T> &instream,
AMI_STREAM<T> &outstream,
const key_range &range)
{
AMI_err ae;
// Memory sizes.
size_t sz_avail, sz_stream;
// Stream sizes.
TPIE_OS_OFFSET max_size, stream_len;
// Check whether the problem fits in main memory.
sz_avail = MM_manager.memory_available ();
instream.main_memory_usage(&sz_stream, MM_STREAM_USAGE_MAXIMUM);
if (sz_avail < 4 * sz_stream) {
return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
}
// Account for the two name streams.
sz_avail -= 2*sz_stream;
// If it fits, simply read it, sort it, and write it.
stream_len = instream.stream_len();
if (sz_avail >= stream_len * (sizeof(T) +
sizeof(AMI_bucket_list_elem<T> *) +
sizeof(AMI_bucket_list_elem<T>))) {
return _AMI_MM_KB_SORT(KB_KEY)(instream, outstream, range);
}
// It did not fit, so we have to distribute it.
// Create streams of temporary file names.
AMI_STREAM<char> *name_stream, *name_stream2 = NULL;
name_stream = new AMI_STREAM<char>;
// Do the first level distribution.
ae = _AMI_KB_DIST(KB_KEY)(instream, *name_stream, range, max_size);
// Do the rest of the levels of distribution, continuing until all
// streams are small or have a single key and then, in a final
// iteration, sorting the streams internally and concatenating
// them. In some bad cases we will end up with one or more large
// streams of all the same key, but these are detected and treated
// as small streams.
bool some_stream_is_large = ((max_size *
(sizeof(T) + 4 +
sizeof(AMI_bucket_list_elem<T> *) +
sizeof(AMI_bucket_list_elem<T>))) >
sz_avail);
while (1) {
// Create a new stream of temporary stream names.
tp_assert(name_stream2 == NULL, "Non-null target name stream.");
name_stream2 = new AMI_STREAM<char>;
// Is this the last (special) iteration.
bool last_iteration = !some_stream_is_large;
// We have not seen a large stream yet.
some_stream_is_large = false;
// Iterate over the streams by reading their names out of
// stream_name and recursing on each one.
name_stream->seek(0);
while (1) {
// The range of keys in the stream being read.
key_range stream_range;
char *pc_read;
char *pc;
char stream_name[255];
// Read the next stream name. We start by reading the
// first character and checking for an EOS condition
// indicating there are no more streams.
pc = stream_name;
ae = name_stream->read_item(&pc_read);
if (ae == AMI_ERROR_END_OF_STREAM) {
// We hit the end of the stream name stream, so we
// should break out of the while loop over the streams
// that are named.
break;
} else if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
*pc = *pc_read;
// Now loop to read the rest of the name.
while (*pc != '\0') {
ae = name_stream->read_item(&pc_read);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
*(++pc) = *pc_read;
tp_assert(pc < stream_name+254, "Read too far.");
}
// Read its range.
{
TPIE_OS_OFFSET range_size = sizeof(stream_range);
ae = name_stream->read_array((char *)&stream_range,
&range_size);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
// Read its length.
{
TPIE_OS_OFFSET length_size = sizeof(stream_len);
ae = name_stream->read_array((char *)&stream_len,
&length_size);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
if (!last_iteration) {
if ((stream_len * (sizeof(T) + 4 +
sizeof(AMI_bucket_list_elem<T> *) +
sizeof(AMI_bucket_list_elem<T>)) >
sz_avail) &&
(stream_range.max > stream_range.min + 1)) {
// If it is too big but does not contain all the same key,
// distribute it again.
some_stream_is_large = true;
AMI_STREAM<T> curr_stream(stream_name);
// We only need to read the intermediate stream once.
curr_stream.persist(PERSIST_READ_ONCE);
ae = _AMI_KB_DIST(KB_KEY)(curr_stream, *name_stream2,
stream_range, max_size);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
} else {
// It is either small or contains only a single
// key, so just pass it without further
// processing.
// Write the name.
ae = name_stream2->write_array(stream_name,
strlen(stream_name) + 1);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Write the range.
ae = name_stream2->write_array((const char *)&stream_range,
sizeof(stream_range));
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Write the length.
ae = name_stream2->write_array((const char *)&stream_len,
sizeof(stream_len));
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
} else {
// This is the last iteration.
// Open the stream.
AMI_STREAM<T> curr_stream(stream_name);
// We only need to read the intermediate stream once.
curr_stream.persist(PERSIST_READ_ONCE);
// Check whether it is truly small or just all one key.
if (stream_range.min == stream_range.max) {
// If it is all one key, simply concatenate it
// onto the output.
T *pt;
while(1) {
ae = curr_stream.read_item(&pt);
if (ae == AMI_ERROR_END_OF_STREAM) {
break;
} else if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
ae = outstream.write_item(*pt);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
} else {
// Read the contents into main memory; sort them,
// and write them out.
ae = _AMI_MM_KB_SORT(KB_KEY)(curr_stream, outstream,
stream_range);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
}
}
// We just finished reading all named streams at the current
// level of recursion. Now we want to set up for the next
// level. To do this, we get rid of the names we just
// processed and replace them by the names we need to process
// next. Of course, this is only done if we are not in the last
// iteration.
delete name_stream;
if (!last_iteration) {
name_stream = name_stream2;
name_stream2 = NULL;
} else {
delete name_stream2;
break;
}
}
return AMI_ERROR_NO_ERROR;
}
// Main memory key bucket sorting.
template<class T>
AMI_err _AMI_MM_KB_SORT(KB_KEY)(AMI_STREAM<T> &instream,
AMI_STREAM<T> &outstream,
const key_range &range)
{
AMI_err ae;
size_t sz_avail;
TPIE_OS_OFFSET stream_len;
TPIE_OS_OFFSET ii,jj;
// Check available main memory.
sz_avail = MM_manager.memory_available ();
// How long is the input stream?
stream_len = instream.stream_len();
// Verify that we have enough memory.
if (sz_avail < stream_len * (sizeof(T) +
sizeof(AMI_bucket_list_elem<T> *) +
sizeof(AMI_bucket_list_elem<T>))) {
cerr << '\n' << (TPIE_OS_LONGLONG)sz_avail << ' ' << (TPIE_OS_LONGLONG)stream_len << '\n';
cerr << sizeof(T) << ' ' << sizeof(AMI_bucket_list_elem<T> *) <<
' ' << sizeof(AMI_bucket_list_elem<T>);
return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
}
// Allocate the space for the data and for the bucket list elements.
// We know that stream_len items fit in main memory, so it is safe to cast.
T *indata = new T[(TPIE_OS_SIZE_T)stream_len];
AMI_bucket_list_elem<T> **buckets =
new AMI_bucket_list_elem<T>*[(TPIE_OS_SIZE_T)stream_len];
AMI_bucket_list_elem<T> *list_space =
new AMI_bucket_list_elem<T>[(TPIE_OS_SIZE_T)stream_len];
// Read the input stream.
instream.seek(0);
{
TPIE_OS_OFFSET sl2 = stream_len;
ae = instream.read_array(indata, &sl2);
if (ae != AMI_ERROR_NO_ERROR) {
delete[] indata;
delete[] buckets;
delete[] list_space;
return ae;
}
}
// Empty out the buckets.
for (ii = stream_len; ii--; ) {
buckets[ii] = NULL;
}
// Scan the input, assigning each item to the appropriate bucket.
AMI_bucket_list_elem<T> *list_elem;
unsigned int bucket_index_denom = (unsigned int)(((range.max - range.min) /
stream_len) + 1);
if (!bucket_index_denom) {
bucket_index_denom = 1;
}
for (ii = stream_len, list_elem = list_space;
ii--; list_elem++ ) {
unsigned int bucket_index;
bucket_index = ((unsigned int)((KB_KEY)(indata[ii])) - range.min) /
bucket_index_denom;
tp_assert(bucket_index < (unsigned long)stream_len, "Bucket index too large.");
list_elem->data = indata[ii];
list_elem->next = buckets[bucket_index];
buckets[bucket_index] = list_elem;
}
tp_assert(list_elem == list_space + stream_len,
"Didn't use the right number of list elements.");
// Scan the buckets to put the data back in the input array. In
// order to make the sort stable, we have to be sure to read the
// lists corresponding to the buckets in the correct order. The
// elements that appeared earlier in the orginal data were written
// later (i.e. towards the front of the lists) so we should
// rebuild the data array from the 0th element upwards.
#define VERIFY_OCCUPANCY 0
#if VERIFY_OCCUPANCY
unsigned int max_occupancy = 0;
unsigned int buckets_occupied = 0;
#endif
for (ii = 0, jj = 0; ii < (unsigned)stream_len; ii++) {
#if VERIFY_OCCUPANCY
unsigned int cur_occupancy = 0;
#endif
for (list_elem = buckets[ii]; list_elem != NULL;
list_elem = list_elem->next) {
#if VERIFY_OCCUPANCY
cur_occupancy++;
#endif
indata[jj++] = list_elem->data;
}
#if VERIFY_OCCUPANCY
if (cur_occupancy > max_occupancy) {
max_occupancy = cur_occupancy;
}
if (cur_occupancy != 0) {
buckets_occupied++;
}
#endif
}
#if VERIFY_OCCUPANCY
cerr << "Max occupancy = " << max_occupancy << '\n';
cerr << "Buckets occupied = " << buckets_occupied << '\n';
cerr << "Stream length = " << stream_len << '\n';
#endif
// Do an insertion sort across the whole data set.
{
T *p, *q, test;
for (p = indata + 1; p < indata + stream_len; p++) {
for (q = p - 1, test = *p;
(q >= indata) && (KB_KEY(*q) > KB_KEY(test)); q--) {
*(q+1) = *q;
}
*(q+1) = test;
}
}
// Write the results.
ae = outstream.write_array(indata, stream_len);
if (ae != AMI_ERROR_NO_ERROR) {
delete [] indata;
delete [] buckets;
delete [] list_space;
return ae;
}
delete [] indata;
delete [] buckets;
delete [] list_space;
return AMI_ERROR_NO_ERROR;
}
#ifdef _HAVE_TEMP_KB_KEY_DEFINITION_
#undef _HAVE_TEMP_KB_KEY_DEFINITION_
#undef KB_KEY
#endif
#ifdef _KB_CONCAT
#undef _KB_CONCAT
#endif
#undef _AMI_KB_SORT
#undef _AMI_KB_MM_SORT
#undef _AMI_KB_MM_DIST
#endif // !(defined(_AMI_KB_SORT_H)) || defined(KB_KEY)
+556
View File
@@ -0,0 +1,556 @@
// Copyright (C) 2001 Octavian Procopiuc
//
// File: ami_kdtree_base.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// Supporting types for AMI_kdtree and AMI_kdbtree:
// AMI_kdtree_status, link_type_t,
// AMI_kdtree_params, Bin_node_default,
// AMI_kdbtree_status, AMI_kdbtree_params,
// region_t, kdb_item_t, path_stack_item_t.
//
// $Id: ami_kd_base.h,v 1.9 2005/02/12 20:29:10 tavi Exp $
//
#ifndef _AMI_KD_BASE_H
#define _AMI_KD_BASE_H
// For ostream.
#include <iostream>
// For min, max.
#include <algorithm>
#include <ami_block_base.h>
#include <ami_point.h>
// AMI_KDTREE_STORE_WEIGHTS determines whether weights are stored in all
// binary kd-tree nodes (when set to 1), or just in block nodes (when set
// to 0). Setting to 1 results in bigger binary nodes and, consequently,
// smaller fanout. The weights are used to dramatically improve the
// performance of range *counting* queries (not range reporting
// queries). Caveat emptor: avoid often change of this parameter. Trying to
// open an existing kd-tree with the wrong value for this parameter will
// generate an invalid kd-tree.
#ifndef AMI_KDTREE_STORE_WEIGHTS
# define AMI_KDTREE_STORE_WEIGHTS 0
#endif
// AMI_KDTREE_USE_EXACT_SPLIT determines how points on the median line are
// distributed. If set to 1, some of the points go into the left child,
// some into the right child; the search procedure should look into both
// children. If set to 0, only the left child contains those points. In
// theory, this is a tradeoff between space utilization and query
// performance. However, query performance is rarely affected, so a value
// of 1 is appropriate for most instances. Trying to open an existing
// kd-tree with the wrong value for this parameter will generate an invalid
// kd-tree.
#ifndef AMI_KDTREE_USE_EXACT_SPLIT
# define AMI_KDTREE_USE_EXACT_SPLIT 1
#endif
// AMI_KDTREE_USE_KDBTREE_LEAF determines what the info field of a leaf
// contains. Setting to 1 gives a three-element info field, similar to
// the one used by the K-D-B-tree. This allows a kd-tree to be
// transformed into a K-D-B-tree without touching the leaves, but it
// wastes 4 bytes in every leaf. If set to 0, the info field of a leaf
// contains only two 4-byte elements. Caveat emptor: avoid often
// change of this parameter. Trying to open an existing kd-tree with
// the wrong value for this parameter will generate an invalid kd-tree.
#ifndef AMI_KDTREE_USE_KDBTREE_LEAF
# define AMI_KDTREE_USE_KDBTREE_LEAF 1
#endif
// AMI_KDTREE_USE_REAL_MEDIAN determines the kd-tree splitting method. If
// set to 1, medians are used. If set to 0, the weight of the left branch
// is always a power of 2. This allows kd-trees to be very compact in terms
// of storage utilization. The only place this value is checked is the
// median() method of the kd-tree.
#ifndef AMI_KDTREE_USE_REAL_MEDIAN
# define AMI_KDTREE_USE_REAL_MEDIAN 0
#endif
// The default grid size (on each dimension) for the grid bulk loading.
#ifndef AMI_KDTREE_GRID_SIZE
# define AMI_KDTREE_GRID_SIZE 256
#endif
// Loading methods. These bits can be combined, but not all
// combinations are valid.
#define AMI_KDTREE_LOAD_SORT 0x1
#define AMI_KDTREE_LOAD_SAMPLE 0x2
#define AMI_KDTREE_LOAD_BINARY 0x4
#define AMI_KDTREE_LOAD_GRID 0x8
// AMI_kdtree status type.
enum AMI_kdtree_status {
AMI_KDTREE_STATUS_VALID = 0,
AMI_KDTREE_STATUS_INVALID = 1,
};
// Node type type.
typedef unsigned short int link_type_t;
#define BLOCK_NODE 0u
#define BLOCK_LEAF 1u
#define BIN_NODE 2u
#define GRID_INDEX 3u
// AMI_kdtree run-time parameters.
class AMI_kdtree_params {
public:
// Max number of Value's in a leaf. 0 means use all available capacity.
TPIE_OS_SIZE_T leaf_size_max;
// Max number of Key's in a node. 0 means use all available capacity.
TPIE_OS_SIZE_T node_size_max;
// How much bigger is the leaf logical block than the system block.
TPIE_OS_SIZE_T leaf_block_factor;
// How much bigger is the node logical block than the system block.
TPIE_OS_SIZE_T node_block_factor;
// The max number of leaves cached.
TPIE_OS_SIZE_T leaf_cache_size;
// The max number of nodes cached.
TPIE_OS_SIZE_T node_cache_size;
// Max height of a binary node inside a block node (other than
// root). The root binary node has height 0. A default value, based
// on node capacity, is used if set to 0.
TPIE_OS_SIZE_T max_intranode_height;
// Max height of a binary node inside the root block node. The root
// binary node has height 0. A default value, based on node
// capacity, is used if set to 0.
TPIE_OS_SIZE_T max_intraroot_height;
// The grid size on each dimension, for grid bulk loading.
TPIE_OS_SIZE_T grid_size;
// The default parameter values.
AMI_kdtree_params():
leaf_size_max(0), node_size_max(0),
leaf_block_factor(1), node_block_factor(1),
leaf_cache_size(8), node_cache_size(8),
max_intranode_height(0), max_intraroot_height(0),
grid_size(AMI_KDTREE_GRID_SIZE) {}
};
// A base class for all binary node implementations. This is not a complete
// implementation of a kd-tree binary node!
template<class coord_t, TPIE_OS_SIZE_T dim>
class AMI_kdtree_bin_node_base {
public:
void initialize(const AMI_point<coord_t, dim> &p, TPIE_OS_SIZE_T d) {
assert(d < dim);
discr_val_ = p[d];
discr_dim_ = d;
}
TPIE_OS_SIZE_T get_discriminator_dim() {
return discr_dim_;
}
coord_t get_discriminator_val() {
return discr_val_;
}
int discriminate(const AMI_point<coord_t, dim> &p) const {
return (p[discr_dim_] < discr_val_) ? -1: (p[discr_dim_] > discr_val_) ? 1: 0;
}
// int discriminate(const AMI_point<coord_t, dim> &p) const {
// return (p[discr_dim_] <= discr_val_) ? -1: 1;
// }
#if AMI_KDTREE_STORE_WEIGHTS
TPIE_OS_OFFSET &low_weight() {
return lo_weight_;
}
TPIE_OS_OFFSET &high_weight() {
return hi_weight_;
}
TPIE_OS_OFFSET low_weight() const {
return lo_weight_;
}
TPIE_OS_SIZE_T high_weight() const {
return hi_weight_;
}
private:
TPIE_OS_OFFSET lo_weight_;
TPIE_OS_OFFSET hi_weight_;
#endif
private:
// The split coordinate (the split hyperplane crosses the orthogonal
// axis in this value).
coord_t discr_val_;
// The dimension orthogonal to the split hyperplane. Should be less than dim.
TPIE_OS_SIZE_T discr_dim_;
};
// The default binary node implementation.
// (All binary node implementations should have the same public interface).
template<class coord_t, TPIE_OS_SIZE_T dim>
class AMI_kdtree_bin_node_default: public AMI_kdtree_bin_node_base<coord_t, dim> {
public:
void set_low_child(TPIE_OS_SIZE_T idx, link_type_t idx_type) {
lo_child_ = idx;
lo_type_ = idx_type;
}
void set_high_child(TPIE_OS_SIZE_T idx, link_type_t idx_type) {
hi_child_ = idx;
hi_type_ = idx_type;
}
void get_low_child(TPIE_OS_SIZE_T &idx, link_type_t &idx_type) const {
idx = lo_child_;
idx_type = lo_type_;
}
void get_high_child(TPIE_OS_SIZE_T &idx, link_type_t &idx_type) const {
idx = hi_child_;
idx_type = hi_type_;
}
private:
// The low child (i.e., its position in the block node).
TPIE_OS_SIZE_T lo_child_;
link_type_t lo_type_;
// The high child (i.e., its position in the block node).
TPIE_OS_SIZE_T hi_child_;
link_type_t hi_type_;
};
// Another binary node implementation, smaller than the default (uses short
// int instead of TPIE_OS_SIZE_T and link_type_t).
template<class coord_t, TPIE_OS_SIZE_T dim>
class AMI_kdtree_bin_node_short: public AMI_kdtree_bin_node_base<coord_t, dim> {
public:
void set_low_child(TPIE_OS_SIZE_T idx, link_type_t idx_type) {
lo_child_ = (unsigned short) idx;
lo_type_ = (unsigned short) idx_type;
}
void set_high_child(TPIE_OS_SIZE_T idx, link_type_t idx_type) {
hi_child_ = (unsigned short) idx;
hi_type_ = (unsigned short) idx_type;
}
void get_low_child(TPIE_OS_SIZE_T &idx, link_type_t &idx_type) const {
idx = (TPIE_OS_SIZE_T) lo_child_;
idx_type = (link_type_t) lo_type_;
}
void get_high_child(TPIE_OS_SIZE_T &idx, link_type_t &idx_type) const {
idx = (TPIE_OS_SIZE_T) hi_child_;
idx_type = (link_type_t) hi_type_;
}
private:
// The low child (i.e., its position in the block node).
unsigned short lo_child_;
unsigned short lo_type_;
// The high child (i.e., its position in the block node).
unsigned short hi_child_;
unsigned short hi_type_;
};
// Yet another binary node type. Same functionality as the default
// type, but much more compact.
template<class coord_t, TPIE_OS_SIZE_T dim>
class AMI_kdtree_bin_node_small: public AMI_kdtree_bin_node_base<coord_t, dim> {
public:
void set_low_child(TPIE_OS_SIZE_T idx, link_type_t idx_type) {
lo_child_ = ((unsigned short) idx << 2) | ((unsigned short) idx_type & 0x3);
}
void set_high_child(TPIE_OS_SIZE_T idx, link_type_t idx_type) {
hi_child_ = ((unsigned short) idx << 2) | ((unsigned short) idx_type & 0x3);
}
void get_low_child(TPIE_OS_SIZE_T &idx, link_type_t &idx_type) const {
idx = lo_child_ >> 2;
idx_type = (link_type_t) (lo_child_ & 0x3);
}
void get_high_child(TPIE_OS_SIZE_T &idx, link_type_t &idx_type) const {
idx = hi_child_ >> 2;
idx_type = (link_type_t) (hi_child_ & 0x3);
}
private:
// The low child and type together.
unsigned short lo_child_;
// The high child and type together.
unsigned short hi_child_;
};
// A binary node larger than the default. Stores an entire point as a
// discriminator, instead of just one value. Does not inherit from the base
// class.
template<class coord_t, TPIE_OS_SIZE_T dim>
class AMI_kdtree_bin_node_large {
public:
void initialize(const AMI_point<coord_t, dim> &p, TPIE_OS_SIZE_T d) {
discr_val_ = p;
discr_dim_ = d;
}
void set_low_child(TPIE_OS_SIZE_T idx, link_type_t idx_type) {
lo_child_ = idx;
lo_type_ = idx_type;
}
void set_high_child(TPIE_OS_SIZE_T idx, link_type_t idx_type) {
hi_child_ = idx;
hi_type_ = idx_type;
}
int discriminate(const AMI_point<coord_t, dim> &p) const {
return (p[discr_dim_] < discr_val_[discr_dim_]) ? -1:
(p[discr_dim_] > discr_val_[discr_dim_]) ? 1:
(p[(discr_dim_+1)%dim] < discr_val_[(discr_dim_+1)%dim]) ? -1 :
(p[(discr_dim_+1)%dim] == discr_val_[(discr_dim_+1)%dim]) ? 0: 1;
}
void get_low_child(TPIE_OS_SIZE_T &idx, link_type_t &idx_type) const {
idx = lo_child_;
idx_type = lo_type_;
}
void get_high_child(TPIE_OS_SIZE_T &idx, link_type_t &idx_type) const {
idx = hi_child_;
idx_type = hi_type_;
}
#if AMI_KDTREE_STORE_WEIGHTS
TPIE_OS_OFFSET &low_weight() {
return lo_weight_;
}
TPIE_OS_OFFSET &high_weight() {
return hi_weight_;
}
TPIE_OS_OFFSET low_weight() const {
return lo_weight_;
}
TPIE_OS_OFFSET high_weight() const {
return hi_weight_;
}
private:
TPIE_OS_OFFSET lo_weight_;
TPIE_OS_OFFSET hi_weight_;
#endif
private:
// The split point.
AMI_point<coord_t, dim> discr_val_;
// The dimension orthogonal to the split hyperplane. Should be less than dim.
TPIE_OS_SIZE_T discr_dim_;
// The low child (i.e., its position in the block node).
TPIE_OS_SIZE_T lo_child_;
link_type_t lo_type_;
// The high child (i.e., its position in the block node).
TPIE_OS_SIZE_T hi_child_;
link_type_t hi_type_;
};
///// AMI_kdbtree stuff /////
template<class coord_t, TPIE_OS_SIZE_T dim>
class region_t {
protected:
// The low and high coordinates. The boolean bit is true iff the
// box is bounded on that dimension.
// pair<coord_t, bool> lo_[dim];
// pair<coord_t, bool> hi_[dim];
coord_t lo_[dim];
coord_t hi_[dim];
// Contains the bounded bits: least significant bit is for low
// boundary, and second least significant bit is for upper
// boundary.
unsigned char bd_[dim];
#define LO_BD_MASK ((unsigned char) 1)
#define HI_BD_MASK ((unsigned char) 2)
// char lo_bd_[dim];
// char hi_bd_[dim];
public:
region_t() {
for (int i = 0; i < dim; i++)
bd_[i] = 0;
// lo_bd_[i] = hi_bd_[i] = 0; //false;
}
// Initialize this box with the values stored in points p1 and p2.
region_t(const AMI_point<coord_t, dim>& p1, const AMI_point<coord_t, dim>& p2) {
for (TPIE_OS_SIZE_T i = 0; i < dim; i++) {
lo_[i] = min(p1[i], p2[i]);
// lo_bd_[i] = 1;//true;
bd_[i] |= LO_BD_MASK; // true on low bd.
hi_[i] = max(p1[i], p2[i]);
// hi_bd_[i] = 1;//true;
bd_[i] |= HI_BD_MASK; // true on high bd.
if (p1[i] == p2[i])
TP_LOG_WARNING_ID(" region_t: points have one identical coordinate.");
}
}
coord_t lo(TPIE_OS_SIZE_T d) const { return lo_[d]; }
coord_t& lo(TPIE_OS_SIZE_T d) { return lo_[d]; }
coord_t hi(TPIE_OS_SIZE_T d) const { return hi_[d]; }
coord_t& hi(TPIE_OS_SIZE_T d) { return hi_[d]; }
bool is_bounded_lo(TPIE_OS_SIZE_T d) const { return (bd_[d] & LO_BD_MASK) != 0; }
bool is_bounded_hi(TPIE_OS_SIZE_T d) const { return (bd_[d] & HI_BD_MASK) != 0; }
bool is_bounded(TPIE_OS_SIZE_T d) const
{ return is_bounded_lo(d) && is_bounded_hi(d); }
bool is_bounded() const {
TPIE_OS_SIZE_T i;
for (i = 0; i < dim; i++)
if (!is_bounded(i))
break;
return (i == dim);
}
void set_bounded_lo(TPIE_OS_SIZE_T d, bool b) { bd_[d] |= (b ? LO_BD_MASK: 0); }
void set_bounded_hi(TPIE_OS_SIZE_T d, bool b) { bd_[d] |= (b ? HI_BD_MASK: 0); }
coord_t span(TPIE_OS_SIZE_T d) const { return hi(d) - lo(d); }
AMI_point<coord_t, dim> point_lo() const {
AMI_point<coord_t, dim> p;
for (TPIE_OS_SIZE_T i = 0; i < dim; i++)
p[i] = lo_[i];
return p;
}
AMI_point<coord_t, dim> point_hi() const {
AMI_point<coord_t, dim> p;
for (TPIE_OS_SIZE_T i = 0; i < dim; i++)
p[i] = hi_[i];
return p;
}
// Cutout the portion of the region that's higher than the given
// coordinate.
void cutout_hi(coord_t v, TPIE_OS_SIZE_T d) {
hi_[d] = v;
// hi_bd_[d] = 1;//true;
bd_[d] |= HI_BD_MASK;
}
// Cutout the portion of the region that's lower than the given
// coordinate.
void cutout_lo(coord_t v, TPIE_OS_SIZE_T d) {
lo_[d] = v;
// lo_bd_[d] = 1;//true;
bd_[d] |= LO_BD_MASK;
}
// Return true if this box contains point p.
bool contains(const AMI_point<coord_t, dim>& p) const {
TPIE_OS_SIZE_T i;
for (i = 0; i < dim; i++) {
if ((is_bounded_lo(i) && p[i] < lo_[i]) ||
(is_bounded_hi(i) && p[i] > hi_[i]))
break;
}
return (i == dim);
}
// Return true if this box intersects box r.
bool intersects(const region_t<coord_t, dim>& r) const {
TPIE_OS_SIZE_T i;
for (i = 0; i < dim; i++) {
if ((r.is_bounded_lo(i) && relative_to_plane(r.lo_[i], i) == -1) ||
(r.is_bounded_hi(i) && relative_to_plane(r.hi_[i], i) == 1))
break;
}
return (i == dim);
}
// Return the position of this box relative to the hyperplane
// orthogonal to dimension d and passing through sp: -1 if left of
// the hyperplane, 1 if right of the hyperplane, and 0 if it
// intersects the hyperplane.
int relative_to_plane(coord_t sp, TPIE_OS_SIZE_T d) const {
if (is_bounded_hi(d) && !(sp < hi_[d]))
return -1;
if (is_bounded_lo(d) && !(lo_[d] < sp))
return 1;
return 0;
}
#undef LO_BD_MASK
#undef HI_BD_MASK
}
#if !defined(_WIN32)
__attribute__((packed))
#endif
;
template<class coord_t, TPIE_OS_SIZE_T dim>
class kdb_item_t {
public:
// For this purpose, every interval in region is considered open on
// the left and closed on the right.
region_t<coord_t, dim> region;
link_type_t type;
AMI_bid bid;
kdb_item_t(const region_t<coord_t, dim>& r, AMI_bid b, link_type_t t):
region(r), bid(b), type(t) {}
kdb_item_t() {}
}
#if !defined(_WIN32)
__attribute__((packed))
#endif
;
template<class coord_t, TPIE_OS_SIZE_T dim>
ostream &operator<<(ostream& s, const kdb_item_t<coord_t, dim>& ki) {
s << "[";
for (TPIE_OS_SIZE_T i = 0; i < dim; i++) {
if (ki.region.is_bounded_lo(i))
s << ki.region.lo(i);
else
s << "-INF";
s << " ";
}
for (TPIE_OS_SIZE_T i = 0; i < dim; i++) {
if (ki.region.is_bounded_hi(i))
s << ki.region.hi(i);
else
s << "INF";
s << " ";
}
s << (ki.type == BLOCK_NODE ? 'N': 'L') << ki.bid;
s << "]";
return s;
}
template<class coord_t, TPIE_OS_SIZE_T dim>
struct path_stack_item_t {
kdb_item_t<coord_t, dim> item;
TPIE_OS_SIZE_T d;
TPIE_OS_SIZE_T el_idx; //
path_stack_item_t(const kdb_item_t<coord_t, dim>& ki, TPIE_OS_SIZE_T di,
TPIE_OS_SIZE_T idx = 0): item(ki), d(di), el_idx(idx) {}
path_stack_item_t() {}
};
// Kdtree status type.
enum AMI_kdbtree_status {
AMI_KDBTREE_STATUS_VALID = 0,
AMI_KDBTREE_STATUS_INVALID = 1,
AMI_KDBTREE_STATUS_KDTREE = 2, // For opening the kdb-tree as a kd-tree.
};
// Split heuristics
enum split_heuristic_t {
CYCLICAL,
LONGEST_SPAN,
RANDOM,
};
class AMI_kdbtree_params: public AMI_kdtree_params {
public:
AMI_kdbtree_params(): AMI_kdtree_params(), split_heuristic(LONGEST_SPAN) {}
AMI_kdbtree_params(AMI_kdtree_params p): AMI_kdtree_params(p), split_heuristic(LONGEST_SPAN) {}
split_heuristic_t split_heuristic;
};
#endif // _AMI_KD_BASE_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 1995 Darren Erik Vengroff
//
// File: ami_key.cpp
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 3/12/95
//
#include "versions.h"
#include "ami_key.h"
VERSION(ami_key_cpp,"$Id: ami_key.cpp,v 1.3 2003/04/17 20:42:24 jan Exp $");
key_range::key_range(kb_key min_key, kb_key max_key) {
this->min = min_key;
this->max = max_key;
}
key_range::key_range(void) {
this->min = 0;
this->max = 0;
}
+36
View File
@@ -0,0 +1,36 @@
// Copyright (c) 1995 Darren Erik Vengroff
//
// File: ami_key.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 3/12/95
//
// $Id: ami_key.h,v 1.2 2003/04/17 12:34:44 jan Exp $
//
#ifndef _AMI_KEY_H
#define _AMI_KEY_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Temporary until the configuration script is edited to determine word
// size.
#define UINT32 unsigned long
// Radix keys are unsigned 32 bit integers.
typedef UINT32 kb_key;
#define KEY_MAX 0x80000000
#define KEY_MIN 0
// A range of keys. A stream having this range of keys is guarantted
// to have no keys < min and no keys >= max.
class key_range {
public:
kb_key min;
kb_key max;
key_range(void);
key_range(kb_key min_key, kb_key max_key);
};
#endif // _AMI_KEY_H
+545
View File
@@ -0,0 +1,545 @@
// Copyright (c) 2001 Octavian Procopiuc
//
// File: ami_logmethod.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: ami_logmethod.h,v 1.9 2005/01/21 16:55:48 tavi Exp $
//
// Logmethod_base, Logmethod2 and LogmethodB declarations and
// definitions.
//
#ifndef _LOGMETHOD_H
#define _LOGMETHOD_H
#include <portability.h>
// For vector
#include <vector>
// For pair
#include <utility>
// TPIE stuff.
#include <ami_stream.h>
#include <ami_coll.h>
#include <tpie_stats_tree.h>
#define LM_PATH_NAME_LENGTH 128
template<class Tp, class T0p=Tp>
class Logmethod_params {
public:
size_t cached_blocks;
Tp tree_params;
T0p tree0_params;
Logmethod_params(): cached_blocks(16), tree_params(), tree0_params() {}
};
// Requirements common to T and T0:
// key_t [key type]
// size_t size();
// bool erase(const Value&);
// bool find(const Value&);
// size_t window_query(const Value&, const Value&, AMI_STREAM<Value>*);
// void persist(persistence);
// void unload(AMI_STREAM<Value>*);
//
// Requirements specific to T:
// const Tp& params();
// void load(AMI_STREAM<Value>*);
// T(char*, AMI_collection_type, Tp);
// ~T();
//
// Requirements specific to T0:
// const T0p& params();
// size_t os_block_count();
// void insert(const Value&);
// T0(char*, AMI_cllection_type, T0p);
// ~T0();
template<class Key, class Value, class T, class Tp, class T0 = T, class T0p = Tp>
class Logmethod_base {
public:
typedef AMI_STREAM<Value> stream_t;
typedef Logmethod_params<Tp, T0p> params_t;
// Delete a point.
bool erase(const Value& p);
// Point query.
bool find(const Value& p);
// Window query. Report results in stream os.
TPIE_OS_OFFSET window_query(const Key &lop, const Key &hip, stream_t* os);
void persist(persistence per);
// Inquire the mbr.
const pair<Value, Value> &mbr();
// Inquire the size.
TPIE_OS_OFFSET size() const { return header_.size; }
// Inquire the run-time parameters.
const Logmethod_params<Tp, T0p>& params() const { return params_; }
// Inquire the statistics.
const tpie_stats_tree &stats();
// Destructor. Delete all trees.
~Logmethod_base();
protected:
// Constructor. Create a new struct. with the given base name for
// all its files. Protected to avoid instantiation of this base
// class.
Logmethod_base(const char *base_file_name, const Logmethod_params<Tp, T0p>& params);
class header_type {
public:
TPIE_OS_OFFSET size; // The total number of elements stored in the structure.
TPIE_OS_SIZE_T last_tree; // the index of the last tree in the trees_ vector
header_type(): size(0), last_tree(0) {}
};
// Run-time parameters.
Logmethod_params<Tp, T0p> params_;
// Critical information (will be written in the header of the first tree)
header_type header_;
// The first tree.
T0 *tree0_;
// The vector of trees, in increasing size.
vector< T* > trees_;
// The base name of all trees.
char base_file_name_[LM_PATH_NAME_LENGTH];
// String used for constructing tree names.
char temp_name_[LM_PATH_NAME_LENGTH];
// Minimum bounding rectangle.
pair<Value, Value> mbr_;
bool mbr_is_set_;
// Persistence flag.
persistence per_;
// Statistics.
tpie_stats_tree stats_;
// Create a tree name in temp_name_ from base_file_name_ and the
// given index.
void create_tree(size_t idx);
};
template<class Key, class Value, class T, class Tp, class T0, class T0p>
class Logmethod2: public Logmethod_base<Key, Value, T, Tp, T0, T0p> {
protected:
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::tree0_;
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::trees_;
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::params_;
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::stats_;
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::header_;
public:
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::create_tree;
Logmethod2(const char *base_file_name, const Logmethod_params<Tp, T0p> &params);
bool insert(const Value& p);
};
template<class Key, class Value, class T, class Tp, class T0, class T0p>
class LogmethodB: public Logmethod_base<Key, Value, T, Tp, T0, T0p> {
protected:
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::tree0_;
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::trees_;
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::params_;
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::stats_;
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::header_;
public:
using Logmethod_base<Key, Value, T, Tp, T0, T0p>::create_tree;
LogmethodB(const char *base_file_name, const Logmethod_params<Tp, T0p> &params);
bool insert(const Value& p);
static size_t B;
};
////////////////////////////////////////////
/////////// ***Implementation*** ///////////
////////////////////////////////////////////
#define LOGMETHOD_BASE Logmethod_base<Key, Value, T, Tp, T0, T0p>
#define LOGMETHOD2 Logmethod2<Key, Value, T, Tp, T0, T0p>
#define LOGMETHODB LogmethodB<Key, Value, T, Tp, T0, T0p>
///////////////////////////////////////
///////// **Logmethod_base** //////////
///////////////////////////////////////
//// *Logmethod_base::Logmethod_base* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
LOGMETHOD_BASE::Logmethod_base(const char *base_file_name,
const Logmethod_params<Tp, T0p> &params):
header_(), params_(params), tree0_(NULL), trees_(0), per_(PERSIST_PERSISTENT), stats_() {
// Copy the name and make sure it has two free positions, to be
// filled later with a unique number for each tree.
strncpy(base_file_name_, base_file_name, LM_PATH_NAME_LENGTH - 4);
mbr_is_set_ = false;
strcpy(temp_name_, base_file_name_);
TPIE_OS_FILE_DESCRIPTOR fd; // file descriptor for the header file.
// Try to open header file read-only.
if (TPIE_OS_IS_VALID_FILE_DESCRIPTOR(fd = TPIE_OS_OPEN_ORDONLY(base_file_name_))) {
if (TPIE_OS_READ(fd, &header_, sizeof(header_)) != sizeof(header_)) {
TP_LOG_WARNING_ID("Corrupt header file.");
assert(0);
}
TPIE_OS_CLOSE(fd);
assert(header_.last_tree < 100);
size_t i;
// Initialize trees.
for (i = 0; i <= header_.last_tree; i++) {
trees_.insert(trees_.end(), NULL);
create_tree(i);
}
// Get the real params.
params_.tree0_params = tree0_->params();
if (i >= 1)
params_.tree_params = trees_[1]->params();
} else {
TP_LOG_APP_DEBUG_ID("Creating new logmethod structure.");
// Bogus entry in the trees_ vector.
trees_.insert(trees_.end(), NULL);
// Create tree0_.
create_tree(0);
}
}
//// *Logmethod_base::erase* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
bool LOGMETHOD_BASE::erase(const Value& p) {
bool ans = false;
if (tree0_->size() > 0 && tree0_->erase(p)) {
ans = true;
} else {
for (size_t i = 1; i < trees_.size(); i++) {
if (trees_[i]->size() > 0 && trees_[i]->erase(p)) {
ans = true;
break;
}
}
}
if (ans)
header_.size--;
return ans;
}
//// *Logmethod_base::find* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
bool LOGMETHOD_BASE::find(const Value& p) {
bool ans = false;
// Search in all nonempty trees_.
if (tree0_->size() > 0 && tree0_->find(p)) {
ans = true;
} else {
for (size_t i = 1; i < trees_.size(); i++) {
// Order is important! Short circuit evaluation.
if (trees_[i]->size() > 0 && trees_[i]->find(p)) {
ans = true;
break;
}
}
}
return ans;
}
//// *Logmethod_base::window_query* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
TPIE_OS_OFFSET LOGMETHOD_BASE::window_query(const Key &lop, const Key &hip,
AMI_STREAM<Value>* stream) {
TPIE_OS_OFFSET result = 0;
TPIE_OS_SIZE_T i;
if (tree0_->size() > 0)
result += tree0_->window_query(lop, hip, stream);
for (i = 1; i < trees_.size(); i++) {
if (trees_[i]->size() > 0)
result += trees_[i]->window_query(lop, hip, stream);
}
return result;
}
//// *Logmethod_base::persist* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
void LOGMETHOD_BASE::persist(persistence per) {
per_ = per;
}
//// *Logmethod_base::~Logmethod_base* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
LOGMETHOD_BASE::~Logmethod_base() {
TPIE_OS_FILE_DESCRIPTOR fd;
if (per_ == PERSIST_PERSISTENT) {
header_.last_tree = trees_.size() - 1;
// Open the header file (create if not present).
if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(fd = TPIE_OS_OPEN_OEXCL(base_file_name_, TPIE_OS_FLAG_USE_MAPPING_FALSE))) {
// if ((fd = open(base_file_name_, O_RDWR | O_CREAT | O_EXCL,
// S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == -1) {
// Try again, hoping it exists.
if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(fd = TPIE_OS_OPEN_ORDWR(base_file_name_, TPIE_OS_FLAG_USE_MAPPING_FALSE))) {
// if ((fd = open(base_file_name_, O_RDWR)) == -1) {
TP_LOG_WARNING_ID("Error creating header file.");
TP_LOG_WARNING_ID(strerror(errno));
assert(0);
}
}
TPIE_OS_WRITE(fd, &header_, sizeof(header_));
}
if (TPIE_OS_CLOSE(fd)) {
TP_LOG_FATAL_ID("Failed to close() ");
TP_LOG_FATAL_ID(base_file_name_);
}
if (per_ == PERSIST_DELETE) {
if (TPIE_OS_UNLINK(base_file_name_)) {
TP_LOG_FATAL_ID("Failed to unlink() ");
TP_LOG_FATAL_ID(base_file_name_);
}
}
tree0_->persist(per_);
delete tree0_;
tree0_ = NULL;
for (size_t i = 1; i < trees_.size(); i++) {
trees_[i]->persist(per_);
delete trees_[i];
trees_[i] = NULL;
}
}
//// *Logmethod_base::mbr* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
const pair<Value, Value>& LOGMETHOD_BASE::mbr() {
size_t i;
if (!mbr_is_set_) {
if (tree0_->size() > 0) {
if (mbr_is_set_) {
mbr_.first.set_min(tree0_->mbr().first);
mbr_.second.set_max(tree0_->mbr().second);
} else {
mbr_.first = tree0_->mbr().first;
mbr_.second = tree0_->mbr().second;
}
}
for (i = 0; i < trees_.size(); i++) {
if (trees_[i]->size() > 0) {
if (mbr_is_set_) {
mbr_.first.set_min(trees_[i]->mbr().first);
mbr_.second.set_max(trees_[i]->mbr().second);
} else {
mbr_.first = trees_[i]->mbr().first;
mbr_.second = trees_[i]->mbr().second;
}
}
}
mbr_is_set_ = true;
}
return mbr_;
}
//// *Logmethod_base::create_tree* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
void LOGMETHOD_BASE::create_tree(TPIE_OS_SIZE_T idx) {
TPIE_OS_SIZE_T i = strlen(base_file_name_);
temp_name_[i ] = '0' + (char)((idx/10) % 10);
temp_name_[i+1] = '0' + (char)(idx % 10);
temp_name_[i+2] = '\0';
if (idx == 0) {
if (sizeof(T0p) == 0)
tree0_ = new T0(temp_name_, AMI_WRITE_COLLECTION);
else
tree0_ = new T0(temp_name_, AMI_WRITE_COLLECTION,
params_.tree0_params);
} else {
if (sizeof(Tp) == 0)
trees_[idx] = new T(temp_name_, AMI_WRITE_COLLECTION);
else
trees_[idx] = new T(temp_name_, AMI_WRITE_COLLECTION,
params_.tree_params);
}
}
//// *Logmethod_base::stats* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
const tpie_stats_tree &LOGMETHOD_BASE::stats() {
for (int i = 1; i < trees_.size(); i++) {
if (trees_[i]->size() > 0)
stats_.record(trees_[i]->stats());
}
return stats_;
}
///////////////////////////////////////
/////////// **Logmethod2** ////////////
///////////////////////////////////////
//// *Logmethod2::Logmethod2* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
LOGMETHOD2::Logmethod2(const char* base_file_name,
const Logmethod_params<Tp, T0p> &params):
Logmethod_base<Key, Value, T, Tp, T0, T0p>(base_file_name, params) {
}
//// *Logmethod2::insert* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
bool LOGMETHOD2::insert(const Value& p) {
assert(tree0_ != NULL);
if (tree0_->os_block_count() < params_.cached_blocks) {
// tree0_ must have insert capabilities, ie, the T0 class
// must have insert capabilities.
tree0_->insert(p);
} else {
// cout << "trees_[0]->os_block_count(): " << trees_[0]->os_block_count() << endl;
// cout << " leaf_count: " << trees_[0]->leaf_count()
// << " node_count: " << trees_[0]->node_count() << endl;
// cout << "trees_[0]->size(): " << trees_[0]->size() << endl;
// cout << " node_cache_size: " << trees_[0]->params().node_cache_size
// << " leaf_cache_size: " << trees_[0]->params().leaf_cache_size << endl;
// First unload all relevant trees to a stream.
typename LOGMETHOD_BASE::stream_t *stream = new typename LOGMETHOD_BASE::stream_t;
stream->persist(PERSIST_DELETE);
tree0_->unload(stream);
tree0_->persist(PERSIST_DELETE);
delete tree0_;
/// create_tree(0);
// Free index. The index of the first empty tree.
size_t fi = 1;
while (fi < trees_.size() && trees_[fi]->size() > 0) {
trees_[fi]->unload(stream);
trees_[fi]->persist(PERSIST_DELETE);
stats_.record(trees_[fi]->stats());
delete trees_[fi];
/// create_tree(fi);
fi++;
}
// Add a new tree position if necessary (ie, no empty tree found).
if (fi == trees_.size()) {
trees_.insert(trees_.end(), NULL);
create_tree(fi);
}
assert(trees_[fi]->size() == 0);
// Write the new guy.
stream->write_item(p);
// Create a new tree from stream.
trees_[fi]->load(stream);
delete stream;
// Create empty trees in positions 0 to fi-1.
for (int ii = 0; ii < fi; ii++)
create_tree(ii);
}
header_.size++;
return true;
}
////////////////////////////////////////
/////////// **LogmethodB** /////////////
////////////////////////////////////////
//// *LogmethodB::LogmethodB* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
LOGMETHODB::LogmethodB(const char* base_file_name,
const Logmethod_params<Tp, T0p> &params):
Logmethod_base<Key, Value, T, Tp, T0, T0p>(base_file_name, params) {
}
//// *LogmethodB::insert* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
bool LOGMETHODB::insert(const Value& p) {
assert(tree0_ != NULL);
// Check whether the cached block is full.
if (tree0_->os_block_count() < params_.cached_blocks) {
tree0_->insert(p);
} else {
size_t fi;
typename LOGMETHOD_BASE::stream_t *stream = new typename LOGMETHOD_BASE::stream_t;
stream->persist(PERSIST_DELETE);
tree0_->unload(stream);
tree0_->persist(PERSIST_DELETE);
delete tree0_;
// Write the new guy.
stream->write_item(p);
// Now unload all relevant trees to stream.
fi = 0;
TPIE_OS_OFFSET b_to_fi = stream->stream_len();
while (stream->stream_len() >= b_to_fi) {
fi++;
b_to_fi *= B;
if (fi == trees_.size())
break;
if (trees_[fi]->size() > 0) {
trees_[fi]->unload(stream);
trees_[fi]->persist(PERSIST_DELETE);
stats_.record(trees_[fi]->stats());
delete trees_[fi];
}
}
// Add a new tree position if necessary.
if (fi == trees_.size()) {
trees_.insert(trees_.end(), NULL);
}
create_tree(fi);
assert(trees_[fi] != NULL);
trees_[fi]->load(stream);
for (int ii = 0; ii < fi; ii++)
create_tree(ii);
delete stream;
}
header_.size++;
return true;
}
//// *LogmethodB::B* ////
template<class Key, class Value, class T, class Tp, class T0, class T0p>
size_t LOGMETHODB::B = 100;
#endif // _LOGMETHOD_H
+519
View File
@@ -0,0 +1,519 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: ami_matrix.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 12/9/94
//
// $Id: ami_matrix.h,v 1.14 2004/08/12 12:35:30 jan Exp $
//
#ifndef _AMI_MATRIX_H
#define _AMI_MATRIX_H
// Get definitions for working with Unix and Windows
#include <portability.h>
//#define QUICK_MATRIX_MULT 1
#define AGGARWAL_MATRIX_MULT 1
#define INTERNAL_TIMING 1
#ifdef INTERNAL_TIMING
# include <cpu_timer.h>
# include <iostream>
#endif
#include <matrix.h>
#include <ami_matrix_pad.h>
#include <ami_matrix_blocks.h>
#include <ami_stream_arith.h>
#include <ami_gen_perm.h>
template<class T>
class AMI_matrix : public AMI_STREAM<T> {
private:
TPIE_OS_OFFSET r,c;
public:
AMI_matrix(TPIE_OS_OFFSET row, TPIE_OS_OFFSET col);
~AMI_matrix(void);
TPIE_OS_OFFSET rows();
TPIE_OS_OFFSET cols();
};
template<class T>
AMI_matrix<T>::AMI_matrix(TPIE_OS_OFFSET row, TPIE_OS_OFFSET col) :
r(row), c(col), AMI_STREAM<T>()
{
}
template<class T>
AMI_matrix<T>::~AMI_matrix(void)
{
}
template<class T>
TPIE_OS_OFFSET AMI_matrix<T>::rows(void)
{
return r;
}
template<class T>
TPIE_OS_OFFSET AMI_matrix<T>::cols(void)
{
return c;
}
// Add two matrices.
template<class T>
AMI_err AMI_matrix_add(AMI_matrix<T> &op1, AMI_matrix<T> &op2,
AMI_matrix<T> &res)
{
AMI_scan_add<T> sa;
// We should do some bound checking here.
return AMI_scan((AMI_STREAM<T> *)&op1, (AMI_STREAM<T> *)&op2,
&sa, (AMI_STREAM<T> *)&res);
}
// Subtract.
template<class T>
AMI_err AMI_matrix_sub(AMI_matrix<T> &op1, AMI_matrix<T> &op2,
AMI_matrix<T> &res)
{
AMI_scan_sub<T> ss;
// We should do some bound checking here.
return AMI_scan((AMI_STREAM<T> *)&op1, (AMI_STREAM<T> *)&op2,
&ss, (AMI_STREAM<T> *)&res);
}
// Matrix multiply.
// For standard (non-Strassen) matrix multiply, there are at least two
// algorithms with the same asymptotic complexity. There is the 4 way
// divide and conquer algorithms of Vitter and Shriver and there is
// the technique which divides the matrix into blocks of size
// $sqrt(M/B) \times sqrt(M/B)$. The latter has a smaller constant and is
// simpler to implement, so we chose to use it.
template<class T>
AMI_err AMI_matrix_mult(AMI_matrix<T> &op1, AMI_matrix<T> &op2,
AMI_matrix<T> &res)
{
AMI_err ae;
TPIE_OS_SIZE_T sz_avail;
TPIE_OS_SIZE_T mm_matrix_extent;
TPIE_OS_SIZE_T single_stream_usage;
// Check bounds on the matrices to make sure they match up.
if ((op1.cols() != op2.rows()) || (res.rows() != op1.rows()) ||
(res.cols() != op2.cols())) {
return AMI_MATRIX_BOUNDS;
}
// Check available main memory.
sz_avail = MM_manager.memory_available ();
// How much memory does a single streamneed in the worst case?
if ((ae = op1.main_memory_usage(&single_stream_usage,
MM_STREAM_USAGE_MAXIMUM)) !=
AMI_ERROR_NO_ERROR) {
return ae;
}
// Will the problem fit in main memory?
{
TPIE_OS_OFFSET sz_op1 = op1.rows() * op1.cols() * sizeof(T);
TPIE_OS_OFFSET sz_op2 = op2.rows() * op2.cols() * sizeof(T);
TPIE_OS_OFFSET sz_res = res.rows() * res.cols() * sizeof(T);
if (sz_avail > sz_op1 + sz_op2 + sz_res + 3 * single_stream_usage +
3 * sizeof(matrix<T>)) {
TPIE_OS_SIZE_T ii,jj;
T *tmp_read;
// Main memory copies of the matrices.
matrix<T> mm_op1((TPIE_OS_SIZE_T)op1.rows(), (TPIE_OS_SIZE_T)op1.cols());
matrix<T> mm_op2((TPIE_OS_SIZE_T)op2.rows(), (TPIE_OS_SIZE_T)op2.cols());
matrix<T> mm_res((TPIE_OS_SIZE_T)res.rows(), (TPIE_OS_SIZE_T)res.cols());
// Read in the matrices and solve in main memory.
op1.seek(0);
for (ii = 0; ii < op1.rows(); ii++ ) {
for (jj = 0; jj < op1.cols(); jj++ ) {
ae = op1.read_item(&tmp_read);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
mm_op1[ii][jj] = *tmp_read;
}
}
op2.seek(0);
for (ii = 0; ii < op2.rows(); ii++ ) {
for (jj = 0; jj < op2.cols(); jj++ ) {
ae = op2.read_item(&tmp_read);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
mm_op2[ii][jj] = *tmp_read;
}
}
#if QUICK_MATRIX_MULT
quick_matrix_mult_in_place(mm_op1, mm_op2, mm_res);
#elif defined(AGGARWAL_MATRIX_MULT)
aggarwal_matrix_mult_in_place(mm_op1, mm_op2, mm_res);
#else
perform_mult_in_place((matrix_base<T> &)mm_op1,
(matrix_base<T> &)mm_op2,
(matrix_base<T> &)mm_res);
#endif
// Write out the result.
res.seek(0);
for (ii = 0; ii < res.rows(); ii++ ) {
for (jj = 0; jj < res.cols(); jj++ ) {
ae = res.write_item(mm_res[ii][jj]);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
}
return AMI_ERROR_NO_ERROR;
}
}
// We now know the problem does not fit in main memory.
{
TPIE_OS_SIZE_T num_active_streams = 4 + 4;
TPIE_OS_SIZE_T mm_matrix_space;
TPIE_OS_SIZE_T single_stream_usage;
// What is the maximum extent of any matrix we will try to
// load into memory? We may have up to four in memory at any
// given time. To be safe, let each one have a stream behind
// it and let there be some additional active streams such as
// ....
if ((ae = op1.main_memory_usage(&single_stream_usage,
MM_STREAM_USAGE_MAXIMUM)) !=
AMI_ERROR_NO_ERROR) {
return ae;
}
mm_matrix_space = sz_avail - num_active_streams * single_stream_usage;
mm_matrix_space /= 3;
#ifdef AGGARWAL_MATRIX_MULT_IN_PLACE
// Recall that a temporary vector is used, so we solve x^2 + x = m
// for x, instead of the usual x^2 = m.
mm_matrix_extent = (TPIE_OS_SIZE_T)(sqrt(1.0 +
4 * (double)mm_matrix_space /
sizeof(T)) / 2) - 1;
#else
mm_matrix_extent = (TPIE_OS_SIZE_T)sqrt((double)mm_matrix_space /
sizeof(T));
#endif
// How many rows and columns of chunks in each matrix?
TPIE_OS_OFFSET chunkrows1 = ((op1.rows() - 1) /
mm_matrix_extent) + 1;
TPIE_OS_OFFSET chunkcols1 = ((op1.cols() - 1) /
mm_matrix_extent) + 1;
TPIE_OS_OFFSET chunkrows2 = ((op2.rows() - 1) /
mm_matrix_extent) + 1;
TPIE_OS_OFFSET chunkcols2 = ((op2.cols() - 1) /
mm_matrix_extent) + 1;
// Now shrink the main memory matrix extent as much as possible
// given the constraint that the number of chunk rows and cols
// in each matrix cannot decrease.
TPIE_OS_SIZE_T min_rows_per_chunk1 = (TPIE_OS_SIZE_T)((op1.rows() + chunkrows1 - 1) /
chunkrows1);
TPIE_OS_SIZE_T min_cols_per_chunk1 = (TPIE_OS_SIZE_T)((op1.cols() + chunkcols1 - 1) /
chunkcols1);
TPIE_OS_SIZE_T min_rows_per_chunk2 = (TPIE_OS_SIZE_T)((op2.rows() + chunkrows2 - 1) /
chunkrows2);
TPIE_OS_SIZE_T min_cols_per_chunk2 = (TPIE_OS_SIZE_T)((op2.cols() + chunkcols2 - 1) /
chunkcols2);
// Adjust the main memory matrix extent so that an integral
// multiple of it is just a little bit larger than the inputs.
// Note that we are still assuming square chunks. We can do
// better than this in some cases if we are willing to allow
// non-square matrices.
mm_matrix_extent = min_rows_per_chunk1;
if (mm_matrix_extent < min_rows_per_chunk2)
mm_matrix_extent = min_rows_per_chunk2;
if (mm_matrix_extent < min_cols_per_chunk1)
mm_matrix_extent = min_cols_per_chunk1;
if (mm_matrix_extent < min_cols_per_chunk2)
mm_matrix_extent = min_cols_per_chunk2;
// How many rows and cols in padded matrices.
TPIE_OS_OFFSET rowsp1 = mm_matrix_extent * (((op1.rows() - 1) /
mm_matrix_extent) + 1);
TPIE_OS_OFFSET colsp1 = mm_matrix_extent * (((op1.cols() - 1) /
mm_matrix_extent) + 1);
TPIE_OS_OFFSET rowsp2 = mm_matrix_extent * (((op2.rows() - 1) /
mm_matrix_extent) + 1);
TPIE_OS_OFFSET colsp2 = mm_matrix_extent * (((op2.cols() - 1) /
mm_matrix_extent) + 1);
// Padded matrices.
AMI_matrix<T> *op1p = new AMI_matrix<T>(rowsp1, colsp1);
AMI_matrix<T> *op2p = new AMI_matrix<T>(rowsp2, colsp2);
// Scan each matrix to pad it out with zeroes as needed.
{
AMI_matrix_pad<T> smp1(op1.rows(), op1.cols(), mm_matrix_extent);
AMI_matrix_pad<T> smp2(op2.rows(), op2.cols(), mm_matrix_extent);
ae = AMI_scan((AMI_STREAM<T> *)&op1, &smp1,
(AMI_STREAM<T> *)op1p);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
ae = AMI_scan((AMI_STREAM<T> *)&op2, &smp2,
(AMI_STREAM<T> *)op2p);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
// Permuted padded matrices.
AMI_matrix<T> *op1pp = new AMI_matrix<T>(rowsp1, colsp1);
AMI_matrix<T> *op2pp = new AMI_matrix<T>(rowsp2, colsp2);
AMI_matrix<T> *respp = new AMI_matrix<T>(rowsp1, colsp2);
// Permute each padded matrix into block order. The blocks
// are in row major order and the elements within the blocks
// are in row major order.
{
perm_matrix_into_blocks pmib1(rowsp1, colsp1, mm_matrix_extent);
perm_matrix_into_blocks pmib2(rowsp2, colsp2, mm_matrix_extent);
ae = AMI_general_permute((AMI_STREAM<T> *)op1p,
(AMI_STREAM<T> *)op1pp,
(AMI_gen_perm_object *)&pmib1);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
ae = AMI_general_permute((AMI_STREAM<T> *)op2p,
(AMI_STREAM<T> *)op2pp,
(AMI_gen_perm_object *)&pmib2);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
// We are done with the padded but unpermuted matrices.
delete op1p;
delete op2p;
#ifdef INTERNAL_TIMING
cpu_timer cput_internal;
cput_internal.reset();
cput_internal.start();
#endif
// Now run the standard matrix multiplication algorithm over
// the blocks. To multiply two blocks, we read them into main
// memory. The blocks of the result are accumulated one by
// one in main memory and then written out.
{
//Sometimes the mm_matrix_extent value is such that
//it is too large for the available amount of memory
//to permit the allocation for the matrices below. I suspect
//that there is a bug in the way mm_matrix_extent is assigned
//a value; sz_avail needs to be correctly taken into account.
//But the bug doesn't always take place. Need looking at.
//--Rakesh on mm_matrix_extent.
matrix<T> mm_op1(mm_matrix_extent, mm_matrix_extent);
matrix<T> mm_op2(mm_matrix_extent, mm_matrix_extent);
matrix<T> mm_accum(mm_matrix_extent, mm_matrix_extent);
TPIE_OS_OFFSET ii,jj,kk;
T *tmp_read;
respp->seek(0);
// ii loops over block rows of op1pp.
for (ii = 0; ii < rowsp1 / mm_matrix_extent; ii++ ) {
// jj loops over block cols of op2pp.
for (jj = 0; jj < colsp2 / mm_matrix_extent; jj++ ) {
// These are for looping over rows and cols of MM
// matrices.
TPIE_OS_SIZE_T ii1,jj1;
// Clear the temporary result.
for (ii1 = 0; ii1 < mm_matrix_extent; ii1++ ) {
for (jj1 = 0; jj1 < mm_matrix_extent; jj1++ ) {
mm_accum[ii1][jj1] = 0;
}
}
// kk loops over the cols of op1pp and rows of
// op2pp at the same time.
tp_assert(rowsp2 == colsp1, "Matrix extent mismatch.");
for (kk = 0; kk < rowsp2 / mm_matrix_extent; kk++ ) {
// Read a block from op1pp.
op1pp->seek(ii * colsp1 * mm_matrix_extent +
kk * mm_matrix_extent * mm_matrix_extent);
for (ii1 = 0; ii1 < mm_matrix_extent; ii1++ ) {
for (jj1 = 0; jj1 < mm_matrix_extent; jj1++ ) {
ae = op1pp->read_item(&tmp_read);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
mm_op1[ii1][jj1] = *tmp_read;
}
}
// Read a block from op2pp.
op2pp->seek(kk * colsp2 * mm_matrix_extent +
jj * mm_matrix_extent * mm_matrix_extent);
for (ii1 = 0; ii1 < mm_matrix_extent; ii1++ ) {
for (jj1 = 0; jj1 < mm_matrix_extent; jj1++ ) {
ae = op2pp->read_item(&tmp_read);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
mm_op2[ii1][jj1] = *tmp_read;
}
}
// Multiply in MM and add to the running sum.
#if QUICK_MATRIX_MULT
quick_matrix_mult_add_in_place(mm_op1, mm_op2,
mm_accum);
#elif defined(AGGARWAL_MATRIX_MULT)
aggarwal_matrix_mult_add_in_place(mm_op1, mm_op2,
mm_accum);
#else
perform_mult_add_in_place((matrix_base<T> &)mm_op1,
(matrix_base<T> &)mm_op2,
(matrix_base<T> &)mm_accum);
#endif
}
// We now have the complete result for a block of
// respp, so write it out.
for (ii1 = 0; ii1 < mm_matrix_extent; ii1++ ) {
for (jj1 = 0; jj1 < mm_matrix_extent; jj1++ ) {
ae = respp->write_item(mm_accum[ii1][jj1]);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
}
}
}
}
#ifdef INTERNAL_TIMING
cput_internal.stop();
cout << cput_internal << ' ';
#endif
// We are done with the padded and permuted operators.
delete op1pp;
delete op2pp;
// Permute the result from scan block order back into row
// major order.
AMI_matrix<T> *resp = new AMI_matrix<T>(rowsp1, colsp2);
{
perm_matrix_outof_blocks pmob(rowsp1, colsp1, mm_matrix_extent);
ae = AMI_general_permute((AMI_STREAM<T> *)respp,
(AMI_STREAM<T> *)resp,
(AMI_gen_perm_object *)&pmob);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
// We are done with the padded and permuted result.
delete respp;
// Scan to strip the padding from the output matrix.
{
AMI_matrix_unpad<T> smup(op1.rows(), op2.cols(),
mm_matrix_extent);
ae = AMI_scan((AMI_STREAM<T> *)resp, &smup,
(AMI_STREAM<T> *)&res);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
// We are done with the padded but unpermuted result.
delete resp;
}
return AMI_ERROR_NO_ERROR;
}
#endif // _AMI_MATRIX_H
+134
View File
@@ -0,0 +1,134 @@
//
// File: ami_matrix_blocks.cpp
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 12/11/94
//
#include <versions.h>
VERSION(ami_matrix_blocks_cpp,"$Id: ami_matrix_blocks.cpp,v 1.6 2004/08/12 12:53:42 jan Exp $");
#include "lib_config.h"
#include <sys/types.h>
#include <ami_err.h>
#include <ami_gen_perm_object.h>
#include <ami_matrix_blocks.h>
perm_matrix_into_blocks::perm_matrix_into_blocks(TPIE_OS_OFFSET rows,
TPIE_OS_OFFSET cols,
TPIE_OS_OFFSET block_extent) :
r(rows),
c(cols),
be(block_extent)
{
}
perm_matrix_into_blocks::~perm_matrix_into_blocks()
{
}
AMI_err perm_matrix_into_blocks::initialize(TPIE_OS_OFFSET len)
{
return static_cast<TPIE_OS_OUTPUT_SIZE_T>( (r * c) == len) ? AMI_ERROR_NO_ERROR : AMI_MATRIX_BOUNDS;
}
TPIE_OS_OFFSET perm_matrix_into_blocks::destination(TPIE_OS_OFFSET source)
{
tp_assert(r % be == 0, "Rows not a multiple of block extent.");
tp_assert(c % be == 0, "Cols not a multiple of block extent.");
// What row and column are the source in?
TPIE_OS_OFFSET src_row = source / c;
TPIE_OS_OFFSET src_col = source % c;
// How many rows of blocks come before the one the source is in?
TPIE_OS_OFFSET src_brow = src_row / be;
// How many blocks in the row of blocks that the source is in come
// before the block the source is in?
TPIE_OS_OFFSET src_bcol = src_col / be;
// Number of objects in block rows above.
TPIE_OS_OFFSET obj_b_above = src_brow * be * c;
// Number of objects in blocks in the same block row before it.
TPIE_OS_OFFSET obj_b_left = src_bcol * be * be;
// Position in block
TPIE_OS_OFFSET bpos = (src_row - be * src_brow) * be +
(src_col - be * src_bcol);
return obj_b_above + obj_b_left + bpos;
}
perm_matrix_outof_blocks::perm_matrix_outof_blocks(TPIE_OS_OFFSET rows,
TPIE_OS_OFFSET cols,
TPIE_OS_OFFSET block_extent) :
r(rows),
c(cols),
be(block_extent)
{
}
perm_matrix_outof_blocks::~perm_matrix_outof_blocks()
{
}
AMI_err perm_matrix_outof_blocks::initialize(TPIE_OS_OFFSET len)
{
return static_cast<TPIE_OS_OUTPUT_SIZE_T>( (r * c) == len) ? AMI_ERROR_NO_ERROR : AMI_MATRIX_BOUNDS;
}
TPIE_OS_OFFSET perm_matrix_outof_blocks::destination(TPIE_OS_OFFSET source)
{
tp_assert(r % be == 0, "Rows not a multiple of block extent.");
tp_assert(c % be == 0, "Cols not a multiple of block extent.");
// How many full blocks come before source?
TPIE_OS_OFFSET src_blocks_before = source / (be * be);
// How many rows of blocks are above the block source is in?
TPIE_OS_OFFSET src_brow = src_blocks_before / (c / be);
// How many blocks in the current row are before the block src is in?
TPIE_OS_OFFSET src_bleft = src_blocks_before % (c / be);
// What is the position of source in its block?
TPIE_OS_OFFSET src_pos_in_block = source % (be * be);
// What is the row of the source in its block?
TPIE_OS_OFFSET src_row_in_block = src_pos_in_block / be;
// What is the col of the source in its block?
TPIE_OS_OFFSET src_col_in_block = src_pos_in_block % be;
// Number of items in block rows above src.
TPIE_OS_OFFSET items_brow_above = src_brow * c * be;
// Number of items in the current block row above src.
TPIE_OS_OFFSET items_curr_brow_above = src_row_in_block * c;
// Number of items in item row to left of source.
TPIE_OS_OFFSET items_left_in_row = (src_bleft * be) + src_col_in_block;
// Add up everything before it.
return items_brow_above + items_curr_brow_above + items_left_in_row;
}
+43
View File
@@ -0,0 +1,43 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: ami_matrix_blocks.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 12/11/94
//
// $Id: ami_matrix_blocks.h,v 1.4 2004/08/12 12:35:30 jan Exp $
//
#ifndef _AMI_MATRIX_BLOCKS_H
#define _AMI_MATRIX_BLOCKS_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get AMI_gen_perm_object.
#include <ami_gen_perm_object.h>
class perm_matrix_into_blocks : public AMI_gen_perm_object {
private:
TPIE_OS_OFFSET r,c,be;
public:
perm_matrix_into_blocks(TPIE_OS_OFFSET rows, TPIE_OS_OFFSET cols,
TPIE_OS_OFFSET block_extent);
virtual ~perm_matrix_into_blocks();
AMI_err initialize(TPIE_OS_OFFSET len);
TPIE_OS_OFFSET destination(TPIE_OS_OFFSET source);
};
class perm_matrix_outof_blocks : public AMI_gen_perm_object {
private:
TPIE_OS_OFFSET r,c,be;
public:
perm_matrix_outof_blocks(TPIE_OS_OFFSET rows, TPIE_OS_OFFSET cols,
TPIE_OS_OFFSET block_extent);
virtual ~perm_matrix_outof_blocks();
AMI_err initialize(TPIE_OS_OFFSET len);
TPIE_OS_OFFSET destination(TPIE_OS_OFFSET source);
};
#endif // _AMI_MATRIX_BLOCKS_H
+71
View File
@@ -0,0 +1,71 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: ami_matrix_fill.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 12/12/94
//
// $Id: ami_matrix_fill.h,v 1.7 2004/08/12 12:35:30 jan Exp $
//
#ifndef _AMI_MATRIX_FILL_H
#define _AMI_MATRIX_FILL_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get the AMI_scan_object definition.
#include <ami_scan.h>
template<class T>
class AMI_matrix_filler {
public:
virtual AMI_err initialize(TPIE_OS_OFFSET rows, TPIE_OS_OFFSET cols) = 0;
virtual T element(TPIE_OS_OFFSET row, TPIE_OS_OFFSET col) = 0;
};
template<class T>
class AMI_matrix_fill_scan : AMI_scan_object {
private:
TPIE_OS_OFFSET r, c;
TPIE_OS_OFFSET cur_row, cur_col;
AMI_matrix_filler<T> *pemf;
public:
AMI_matrix_fill_scan(AMI_matrix_filler<T> *pem_filler,
TPIE_OS_OFFSET rows, TPIE_OS_OFFSET cols) :
r(rows), c(cols),
pemf(pem_filler)
{
};
AMI_err initialize(void)
{
cur_row = cur_col = 0;
return AMI_ERROR_NO_ERROR;
};
AMI_err operate(T *out, AMI_SCAN_FLAG *sf)
{
if ((*sf = (cur_row < r))) {
*out = pemf->element(cur_row,cur_col);
if (!(cur_col = (cur_col+1) % c)) {
cur_row++;
}
return AMI_SCAN_CONTINUE;
} else {
return AMI_SCAN_DONE;
}
};
};
template<class T>
AMI_err AMI_matrix_fill(AMI_matrix<T> *pem, AMI_matrix_filler<T> *pemf)
{
AMI_err ae;
ae = pemf->initialize(pem->rows(), pem->cols());
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
AMI_matrix_fill_scan<T> emfs(pemf, pem->rows(), pem->cols());
return AMI_scan(&emfs, (AMI_STREAM<T> *)pem);
};
#endif // _AMI_MATRIX_FILL_H
+174
View File
@@ -0,0 +1,174 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: ami_matrix_pad.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 12/11/94
//
// $Id: ami_matrix_pad.h,v 1.8 2004/08/12 12:35:30 jan Exp $
//
#ifndef _AMI_MATRIX_PAD_H
#define _AMI_MATRIX_PAD_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get definition of AMI_scan_object class.
#include <ami_scan.h>
// This is a scan management object designed to pad a rows by cols
// matrix with zeroes so that is becomes an (i * block_extent) by (j *
// block_extent) matrix where i and j are integers as small as
// possible.
template<class T>
class AMI_matrix_pad : AMI_scan_object {
private:
TPIE_OS_OFFSET cur_row, cur_col;
TPIE_OS_OFFSET orig_rows, orig_cols;
TPIE_OS_OFFSET final_rows, final_cols;
public:
AMI_matrix_pad(TPIE_OS_OFFSET rows, TPIE_OS_OFFSET cols,
TPIE_OS_OFFSET block_extent);
virtual ~AMI_matrix_pad();
AMI_err initialize(void);
AMI_err operate(const T &in, AMI_SCAN_FLAG *sfin,
T *out, AMI_SCAN_FLAG *sfout);
};
template<class T>
AMI_matrix_pad<T>::AMI_matrix_pad(TPIE_OS_OFFSET rows, TPIE_OS_OFFSET cols,
TPIE_OS_OFFSET block_extent)
{
orig_rows = rows;
orig_cols = cols;
final_rows = block_extent * (((orig_rows - 1) / block_extent) + 1);
final_cols = block_extent * (((orig_cols - 1) / block_extent) + 1);
}
template<class T>
AMI_matrix_pad<T>::~AMI_matrix_pad()
{
}
template<class T>
AMI_err AMI_matrix_pad<T>::initialize(void)
{
cur_col = cur_row = 0;
return AMI_ERROR_NO_ERROR;
}
template<class T>
AMI_err AMI_matrix_pad<T>::operate(const T &in, AMI_SCAN_FLAG *sfin,
T *out, AMI_SCAN_FLAG *sfout)
{
AMI_err ae;
// If we are within the bounds of the original matrix, simply copy.
if ((cur_col < orig_cols) && (cur_row < orig_rows)) {
*out = in;
*sfout = true;
ae = AMI_SCAN_CONTINUE;
} else {
// Don't take the input.
*sfin = false;
// If we are not completely done then write padding.
if ((*sfout = (cur_row < final_rows))) {
*out = (T)0;
ae = AMI_SCAN_CONTINUE;
} else {
tp_assert(cur_row == final_rows, "Too many rows.");
ae = AMI_SCAN_DONE;
}
}
// Increment the column.
cur_col = (cur_col + 1) % final_cols;
// Increment the row if needed.
if (!cur_col) {
cur_row++;
}
return ae;
}
// This is a scan management object designed to unpad a rows by cols
// matrix that was padded by a an object of type scan_matrix_pad.
template<class T>
class AMI_matrix_unpad : AMI_scan_object {
private:
TPIE_OS_OFFSET cur_row, cur_col;
TPIE_OS_OFFSET orig_rows, orig_cols;
TPIE_OS_OFFSET final_rows, final_cols;
public:
AMI_matrix_unpad(TPIE_OS_OFFSET rows, TPIE_OS_OFFSET cols,
TPIE_OS_OFFSET block_extent);
virtual ~AMI_matrix_unpad();
AMI_err initialize(void);
AMI_err operate(const T &in, AMI_SCAN_FLAG *sfin,
T *out, AMI_SCAN_FLAG *sfout);
};
template<class T>
AMI_matrix_unpad<T>::AMI_matrix_unpad(TPIE_OS_OFFSET rows, TPIE_OS_OFFSET cols,
TPIE_OS_OFFSET block_extent)
{
orig_rows = rows;
orig_cols = cols;
final_rows = block_extent * (((orig_rows - 1) / block_extent) + 1);
final_cols = block_extent * (((orig_cols - 1) / block_extent) + 1);
}
template<class T>
AMI_matrix_unpad<T>::~AMI_matrix_unpad()
{
}
template<class T>
AMI_err AMI_matrix_unpad<T>::initialize(void)
{
cur_col = cur_row = 0;
return AMI_ERROR_NO_ERROR;
}
template<class T>
AMI_err AMI_matrix_unpad<T>::operate(const T &in, AMI_SCAN_FLAG *sfin,
T *out, AMI_SCAN_FLAG *sfout)
{
AMI_err ae;
// If we are within the bounds of the original matrix, simply copy.
if ((cur_col < orig_cols) && (cur_row < orig_rows)) {
*out = in;
*sfout = true;
ae = AMI_SCAN_CONTINUE;
} else {
// Don't write anything.
*sfout = false;
// If we are not completely done then skip padding.
if ((*sfin = (cur_row < final_rows))) {
ae = AMI_SCAN_CONTINUE;
} else {
tp_assert(cur_row == final_rows, "Too many rows.");
ae = AMI_SCAN_DONE;
}
}
// Increment the column.
cur_col = (cur_col + 1) % final_cols;
// Increment the row if needed.
if (!cur_col) {
cur_row++;
}
return ae;
}
#endif // _AMI_MATRIX_PAD_H
+813
View File
@@ -0,0 +1,813 @@
//
// File: ami_merge.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/31/94
//
// First cut at a merger. Obviously missing is code to verify that
// lower level streams will use appropriate levels of buffering. This
// will be more critical for parallel disk implementations.
//
// $Id: ami_merge.h,v 1.38 2005/07/07 20:43:36 adanner Exp $
//
#ifndef _AMI_MERGE_H
#define _AMI_MERGE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// For log() and such as needed to compute tree heights.
#include <math.h>
#include <ami_stream.h>
enum AMI_merge_output_type {
AMI_MERGE_OUTPUT_OVERWRITE = 1,
AMI_MERGE_OUTPUT_APPEND
};
typedef int AMI_merge_flag;
typedef int arity_t;
#define CONST const
// CLASSES AND FUNCTIONS DEFINED IN THIS MODULE
//------------------------------------------------------------
//A superclass for merge management objects
template<class T> class AMI_generalized_merge_base;
//merge <arity> streams using a merge management object and write
//result into <outstream>; it is assumed that the available memory can
//fit the <arity> streams, the output stream and also the space
//required by the merge management object; AMI_generalized_merge() checks this and
//then calls AMI_generalized_single_merge();
template<class T, class M>
AMI_err AMI_generalized_merge(AMI_STREAM<T> **instreams, arity_t arity,
AMI_STREAM<T> *outstream, M *m_obj);
// divide the input stream in substreams, merge each substream
// recursively, and merge them together using AMI_generalized_single_merge()
template<class T, class M>
AMI_err AMI_generalized_partition_and_merge(AMI_STREAM<T> *instream,
AMI_STREAM<T> *outstream, M *m_obj);
//merge <arity> streams in memory using a merge management object and
//write result into <outstream>;
template<class T, class M>
AMI_err AMI_generalized_single_merge(AMI_STREAM<T> **instreams, arity_t arity,
AMI_STREAM<T> *outstream, M *m_obj);
//read <instream> in memory and merge it using
//m_obj->main_mem_operate(); if <instream> does not fit in main memory
//return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
template<class T, class M>
AMI_err AMI_main_mem_merge(AMI_STREAM<T> *instream,
AMI_STREAM<T> *outstream, M *m_obj);
//------------------------------------------------------------
//------------------------------------------------------------
// A superclass for merge management objects
//------------------------------------------------------------
template<class T>
class AMI_generalized_merge_base {
public:
#if AMI_VIRTUAL_BASE
virtual AMI_err initialize(arity_t arity,
CONST T * CONST * in,
AMI_merge_flag *taken_flags,
int &taken_index) = 0;
virtual AMI_err operate(CONST T * CONST *in,
AMI_merge_flag *taken_flags,
int &taken_index,
T *out) = 0;
virtual AMI_err main_mem_operate(T* mm_stream, TPIE_OS_SIZE_T len) = 0;
virtual TPIE_OS_SIZE_T space_usage_overhead(void) = 0;
virtual TPIE_OS_SIZE_T space_usage_per_stream(void) = 0;
#endif // AMI_VIRTUAL_BASE
};
//------------------------------------------------------------
//merge <arity> streams using a merge management object and write
//result into <outstream>; it is assumed that the available memory can
//fit the <arity> streams, the output stream and also the space
//required by the merge management object; AMI_generalized_merge() checks this and
//then calls AMI_generalized_single_merge();
//------------------------------------------------------------
template<class T, class M>
AMI_err
AMI_generalized_merge(AMI_STREAM<T> **instreams, arity_t arity,
AMI_STREAM<T> *outstream, M *m_obj) {
TPIE_OS_SIZE_T sz_avail;
TPIE_OS_OFFSET sz_stream, sz_needed = 0;
// How much main memory is available?
sz_avail = MM_manager.memory_available ();
// Iterate through the streams, finding out how much additional
// memory each stream will need in the worst case (the streams are
// in memory, but their memory usage could be smaller then the
// maximum one; one scenario is when the streams have been loaded
// from disk with no subsequent read_item/write_item operation, in
// which case their current memory usage is just the header block);
// count also the output stream
for (unsigned int ii = 0; ii < arity + 1; ii++) {
instreams[ii]->main_memory_usage(&sz_stream, MM_STREAM_USAGE_MAXIMUM);
sz_needed += sz_stream;
instreams[ii]->main_memory_usage(&sz_stream, MM_STREAM_USAGE_CURRENT);
sz_needed -= sz_stream;
}
//count the space used by the merge_management object (include
//overhead added to a stream)
sz_needed += m_obj->space_usage_overhead() +
arity * m_obj->space_usage_per_stream();
//streams and m_obj must fit in memory!
if (sz_needed >= (TPIE_OS_OFFSET)sz_avail) {
TP_LOG_WARNING("Insuficent main memory to perform a merge.\n");
return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
}
assert(sz_needed < sz_avail);
//merge streams in memory
return AMI_generalized_single_merge(instreams, arity, outstream, m_obj);
};
//------------------------------------------------------------
//merge <arity> streams in memory using a merge management object and
//write result into <outstream>;
//------------------------------------------------------------
template<class T, class M>
AMI_err
AMI_generalized_single_merge(AMI_STREAM<T> **instreams, arity_t arity,
AMI_STREAM<T> *outstream, M *m_obj) {
unsigned int ii;
AMI_err ami_err;
// Create an array of pointers for the input.
T* *in_objects = new T*[arity];
// Create an array of flags the merge object can use to ask for more
// input from specific streams.
AMI_merge_flag* taken_flags = new AMI_merge_flag[arity];
// An index to speed things up when the merge object takes only from
// one index.
int taken_index;
//Output of the merge object.
T merge_out;
#if DEBUG_PERFECT_MERGE
unsigned int input_count = 0, output_count = 0;
#endif
// Rewind and read the first item from every stream; count the
// number of non-null items read
for (ii = arity; ii--; ) {
if ((ami_err = instreams[ii]->seek(0)) != AMI_ERROR_NO_ERROR) {
delete[] in_objects;
delete[] taken_flags;
return ami_err;
}
if ((ami_err = instreams[ii]->read_item(&(in_objects[ii]))) !=
AMI_ERROR_NO_ERROR) {
//error on read
if (ami_err == AMI_ERROR_END_OF_STREAM) {
in_objects[ii] = NULL;
} else {
delete[] in_objects;
delete[] taken_flags;
return ami_err;
}
// Set the taken flags to 0 before we call intialize()
taken_flags[ii] = 0;
} else {
//item read succesfully
#if DEBUG_PERFECT_MERGE
input_count++;
#endif
}
}
// Initialize the merge object.
if (((ami_err = m_obj->initialize(arity, in_objects, taken_flags,
taken_index)) != AMI_ERROR_NO_ERROR) &&
(ami_err != AMI_MERGE_READ_MULTIPLE)) {
return AMI_ERROR_OBJECT_INITIALIZATION;
}
// Now simply call the merge object repeatedly until it claims to
// be done or generates an error.
while (1) {
if (ami_err == AMI_MERGE_READ_MULTIPLE) {
for (ii = arity; ii--; ) {
if (taken_flags[ii]) {
ami_err = instreams[ii]->read_item(&(in_objects[ii]));
if (ami_err != AMI_ERROR_NO_ERROR) {
if (ami_err == AMI_ERROR_END_OF_STREAM) {
in_objects[ii] = NULL;
} else {
delete[] in_objects;
delete[] taken_flags;
return ami_err;
}
} else {
#if DEBUG_PERFECT_MERGE
input_count++;
#endif
}
}
// Clear all flags before operate is called.
taken_flags[ii] = 0;
}
} else {
// The last call took at most one item.
if (taken_index >= 0) {
ami_err = instreams[taken_index]->
read_item(&(in_objects[taken_index]));
if (ami_err != AMI_ERROR_NO_ERROR) {
if (ami_err == AMI_ERROR_END_OF_STREAM) {
in_objects[taken_index] = NULL;
} else {
delete[] in_objects;
delete[] taken_flags;
return ami_err;
}
} else {
#if DEBUG_PERFECT_MERGE
input_count++;
#endif
}
taken_flags[taken_index] = 0;
}
}
ami_err = m_obj->operate(in_objects, taken_flags, taken_index,
&merge_out);
if (ami_err == AMI_MERGE_DONE) {
break;
} else if (ami_err == AMI_MERGE_OUTPUT) {
#if DEBUG_PERFECT_MERGE
output_count++;
#endif
if ((ami_err = outstream->write_item(merge_out)) !=
AMI_ERROR_NO_ERROR) {
delete[] in_objects;
delete[] taken_flags;
return ami_err;
}
} else if ((ami_err != AMI_MERGE_CONTINUE) &&
(ami_err != AMI_MERGE_READ_MULTIPLE)) {
delete[] in_objects;
delete[] taken_flags;
return ami_err;
}
}
#if DEBUG_PERFECT_MERGE
tp_assert(input_count == output_count,
"Merge done, input_count = " << input_count <<
", output_count = " << output_count << '.');
#endif
delete[] in_objects;
delete[] taken_flags;
return AMI_ERROR_NO_ERROR;
};
//------------------------------------------------------------
//read <instream> in memory and merge it using
//m_obj->main_mem_operate(); if <instream> does not fit in main memory
//return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
//------------------------------------------------------------
template<class T, class M>
AMI_err AMI_main_mem_merge(AMI_STREAM<T> *instream,
AMI_STREAM<T> *outstream, M *m_obj) {
AMI_err ae;
TPIE_OS_OFFSET len;
TPIE_OS_SIZE_T sz_avail;
// How much memory is available?
sz_avail = MM_manager.memory_available ();
len = instream->stream_len();
if ((len * sizeof(T)) <= (TPIE_OS_OFFSET)sz_avail) {
// If the whole input can fit in main memory just call
// m_obj->main_mem_operate
ae = instream->seek(0);
assert(ae == AMI_ERROR_NO_ERROR);
// This code is sloppy and has to be rewritten correctly for
// parallel buffer allocation. It will not work with anything
// other than a registration based memory manager.
T *mm_stream;
TPIE_OS_OFFSET len1;
//allocate and read input stream in memory we know it fits, so we may cast.
if ((mm_stream = new T[(TPIE_OS_SIZE_T)len]) == NULL) {
return AMI_ERROR_MM_ERROR;
};
len1 = len;
if ((ae = instream->read_array(mm_stream, &len1)) !=
AMI_ERROR_NO_ERROR) {
return ae;
}
tp_assert(len1 == len, "Did not read the right amount; "
"Allocated space for " << len << ", read " << len1 << '.');
//just call m_obj->main_mem_operate. We know that len items fit into
//main memory, so we may cast to TPIE_OS_SIZE_T
if ((ae = m_obj->main_mem_operate(mm_stream, (TPIE_OS_SIZE_T)len)) !=
AMI_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("main_mem_operate failed");
return ae;
}
//write array back to stream
if ((ae = outstream->write_array(mm_stream, (TPIE_OS_SIZE_T)len)) !=
AMI_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("write array failed");
return ae;
}
delete [] mm_stream;
return AMI_ERROR_NO_ERROR;
} else {
// Something went wrong. We should not have called this
// function, since we don't have enough main memory.
TP_LOG_WARNING_ID("out of memory");
return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
}
};
//------------------------------------------------------------
// divide the input stream in substreams, merge each substream
// recursively, and merge them together using AMI_generalized_single_merge()
//------------------------------------------------------------
template<class T, class M>
AMI_err AMI_generalized_partition_and_merge(AMI_STREAM<T> *instream,
AMI_STREAM<T> *outstream, M *m_obj) {
AMI_err ae;
TPIE_OS_OFFSET len;
TPIE_OS_SIZE_T sz_avail, sz_stream;
unsigned int ii;
int jj;
//How much memory is available?
sz_avail = MM_manager.memory_available ();
// If the whole input can fit in main memory then just call
// AMI_main_mem_merge() to deal with it by loading it once and
// processing it.
len = instream->stream_len();
if ((len * sizeof(T)) <= (TPIE_OS_OFFSET)sz_avail) {
return AMI_main_mem_merge(instream, outstream, m_obj);
}
//else {
// The number of substreams that can be merged together at once; i
// this many substreams (at most) we are dividing the input stream
arity_t merge_arity;
//nb of substreams the original input stream will be split into
arity_t nb_orig_substr;
// length (nb obj of type T) of the original substreams of the input
// stream. The last one may be shorter than this.
TPIE_OS_OFFSET sz_orig_substr;
// The initial temporary stream, to which substreams of the
// original input stream are written.
AMI_STREAM<T> *initial_tmp_stream;
// A pointer to the buffer in main memory to read a memory load into.
T *mm_stream;
// Loop variables:
// The stream being read at the current level.
AMI_STREAM<T> *current_input;
// The output stream for the current level if it is not outstream.
AMI_STREAM<T> *intermediate_tmp_stream;
// The size of substreams of *current_input that are being
// merged. The last one may be smaller. This value should be
// sz_orig_substr * (merge_arity ** k) where k is the
// number of iterations the loop has gone through.
TPIE_OS_OFFSET current_substream_len;
// The exponenent used to verify that current_substream_len is
// correct.
unsigned int k;
TPIE_OS_OFFSET sub_start, sub_end;
// How many substreams will there be? The main memory
// available to us is the total amount available, minus what
// is needed for the input stream and the temporary stream.
if ((ae = instream->main_memory_usage(&sz_stream, MM_STREAM_USAGE_MAXIMUM))
!= AMI_ERROR_NO_ERROR) {
return ae;
}
if (sz_avail <= 2 * sz_stream + sizeof(T)) {
return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
}
sz_avail -= 2 * sz_stream;
// number of elements that will fit in memory (M) -R
sz_orig_substr = sz_avail / sizeof(T);
// Round the original substream length off to an integral number of
// chunks. This is for systems like HP-UX that cannot map in
// overlapping regions. It is also required for BTE's that are
// capable of freeing chunks as they are read.
{
TPIE_OS_OFFSET sz_chunk_size = instream->chunk_size();
sz_orig_substr = sz_chunk_size *
((sz_orig_substr + sz_chunk_size - 1) /sz_chunk_size);
// WARNING sz_orig_substr now may not fit in memory!!! -R
}
// number of memoryloads in input ceil(N/M) -R
nb_orig_substr = (arity_t)((len + sz_orig_substr - 1) / sz_orig_substr);
// Account for the space that a merge object will use.
{
TPIE_OS_SIZE_T sz_avail_during_merge = sz_avail - m_obj->space_usage_overhead();
TPIE_OS_SIZE_T sz_stream_during_merge = sz_stream +m_obj->space_usage_per_stream();
merge_arity = (arity_t)((sz_avail_during_merge +
sz_stream_during_merge - 1) / sz_stream_during_merge);
}
// Make sure that the AMI is willing to provide us with the number
// of substreams we want. It may not be able to due to operating
// system restrictions, such as on the number of regions that can be
// mmap()ed in.
{
int ami_available_streams = instream->available_streams();
if (ami_available_streams != -1) {
if (ami_available_streams <= 4) {
return AMI_ERROR_INSUFFICIENT_AVAILABLE_STREAMS;
}
if (merge_arity > (arity_t)ami_available_streams - 2) {
merge_arity = ami_available_streams - 2;
TP_LOG_DEBUG_ID("Reduced merge arity due to AMI restrictions.");
}
}
}
TP_LOG_DEBUG_ID("AMI_generalized_partition_and_merge(): merge arity = "<< merge_arity);
if (merge_arity < 2) {
return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
}
//#define MINIMIZE_INITIAL_SUBSTREAM_LENGTH
#ifdef MINIMIZE_INITIAL_SUBSTREAM_LENGTH
// Make the substreams as small as possible without increasing the
// height of the merge tree.
{
// The tree height is the ceiling of the log base merge_arity
// of the number of original substreams.
double tree_height = log((double)nb_orig_substr)/ log((double)merge_arity);
tp_assert(tree_height > 0, "Negative or zero tree height!");
tree_height = ceil(tree_height);
// See how many substreams we could possibly fit in the tree
// without increasing the height.
double max_original_substreams = pow((double)merge_arity, tree_height);
tp_assert(max_original_substreams >= nb_orig_substr,
"Number of permitted substreams was reduced.");
// How big will such substreams be?
double new_sz_original_substream = ceil((double)len /
max_original_substreams);
tp_assert(new_sz_original_substream <= sz_orig_substr,
"Size of original streams increased.");
sz_orig_substr = (size_t)new_sz_original_substream;
TP_LOG_DEBUG_ID("Memory constraints set original substreams = " << nb_orig_substr);
nb_orig_substr = (len + sz_orig_substr - 1) / sz_orig_substr;
TP_LOG_DEBUG_ID("Tree height constraints set original substreams = " << nb_orig_substr);
}
#endif // MINIMIZE_INITIAL_SUBSTREAM_LENGTH
// Create a temporary stream, then iterate through the substreams,
// processing each one and writing it to the corresponding substream
// of the temporary stream.
initial_tmp_stream = new AMI_STREAM<T>;
mm_stream = new T[(TPIE_OS_SIZE_T)sz_orig_substr];
tp_assert(mm_stream != NULL, "Misjudged available main memory.");
if (mm_stream == NULL) {
return AMI_ERROR_INSUFFICIENT_MAIN_MEMORY;
}
instream->seek(0);
assert(ae == AMI_ERROR_NO_ERROR);
tp_assert(nb_orig_substr * sz_orig_substr - len < sz_orig_substr,
"Total substream length too long or too many.");
tp_assert(len - (nb_orig_substr - 1) * sz_orig_substr <= sz_orig_substr,
"Total substream length too short or too few.");
for (ii = 0; ii++ < nb_orig_substr; ) {
TPIE_OS_OFFSET mm_len;
if (ii == nb_orig_substr) {
mm_len = len % sz_orig_substr;
// If it is an exact multiple, then the mod will come out 0,
// which is wrong.
if (!mm_len) {
mm_len = sz_orig_substr;
}
} else {
mm_len = sz_orig_substr;
}
#if DEBUG_ASSERTIONS
TPIE_OS_OFFSET mm_len_bak = mm_len;
#endif
// Read a memory load out of the input stream.
ae = instream->read_array(mm_stream, &mm_len);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
tp_assert(mm_len == mm_len_bak,
"Did not read the requested number of objects." <<
"\n\tmm_len = " << mm_len <<
"\n\tmm_len_bak = " << mm_len_bak << '.');
// Solve in main memory. We know it fits, so cast to TPIE_OS_SIZE_T
m_obj->main_mem_operate(mm_stream, (TPIE_OS_SIZE_T)mm_len);
// Write the result out to the temporary stream.
ae = initial_tmp_stream->write_array(mm_stream, mm_len);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
} //for
delete [] mm_stream;
// Make sure the total length of the temporary stream is the same as
// the total length of the original input stream.
tp_assert(instream->stream_len() == initial_tmp_stream->stream_len(),
"Stream lengths do not match:" <<
"\n\tinstream->stream_len() = " << instream->stream_len() <<
"\n\tinitial_tmp_stream->stream_len() = " <<
initial_tmp_stream->stream_len() << ".\n");
// Set up the loop invariants for the first iteration of hte main
// loop.
current_input = initial_tmp_stream;
current_substream_len = sz_orig_substr;
// Pointers to the substreams that will be merged.
AMI_STREAM<T>* *the_substreams = new AMI_STREAM<T>*[merge_arity];
//Monitoring prints.
TP_LOG_DEBUG_ID("Number of runs from run formation is "
<< nb_orig_substr);
TP_LOG_DEBUG_ID("Merge arity is " << merge_arity);
k = 0;
// The main loop. At the outermost level we are looping over levels
// of the merge tree. Typically this will be very small, e.g. 1-3.
for( ; current_substream_len < (size_t)len;
current_substream_len *= merge_arity) {
// The number of substreams to be processed at this level.
arity_t substream_count;
// Set up to process a given level.
tp_assert(len == current_input->stream_len(),
"Current level stream not same length as input." <<
"\n\tlen = " << len <<
"\n\tcurrent_input->stream_len() = " <<
current_input->stream_len() << ".\n");
// Do we have enough main memory to merge all the substreams on
// the current level into the output stream? If so, then we will
// do so, if not then we need an additional level of iteration to
// process the substreams in groups.
substream_count = (arity_t)((len + current_substream_len - 1) /
current_substream_len);
if (substream_count <= merge_arity) {
TP_LOG_DEBUG_ID("Merging substreams directly to the output stream.");
// Create all the substreams
for (sub_start = 0, ii = 0 ;
ii < substream_count;
sub_start += current_substream_len, ii++) {
sub_end = sub_start + current_substream_len - 1;
if (sub_end >= len) {
sub_end = len - 1;
}
current_input->new_substream(AMI_READ_STREAM, sub_start, sub_end,
(AMI_stream_base<T> **)
(the_substreams + ii));
// The substreams are read-once.
the_substreams[ii]->persist(PERSIST_READ_ONCE);
}
tp_assert(((int) sub_start >= (int) len) &&
((int) sub_start < (int) len + (int) current_substream_len),
"Loop ended in wrong location.");
// Fool the OS into unmapping the current block of the input
// stream so that blocks of the substreams can be mapped in
// without overlapping it. This is needed for correct execution
// on HP-UX.
//this needs to be cleaned up..Laura
current_input->seek(0);
assert(ae == AMI_ERROR_NO_ERROR);
// Merge them into the output stream.
ae = AMI_generalized_single_merge(the_substreams, substream_count, outstream, m_obj);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Delete the substreams.
for (ii = 0; ii < substream_count; ii++) {
delete the_substreams[ii];
}
// And the current input, which is an intermediate stream of
// some kind.
delete current_input;
} else {
//substream_count is > merge_arity
TP_LOG_DEBUG_ID("Merging substreams to an intermediate stream.");
// Create the next intermediate stream.
intermediate_tmp_stream = new AMI_STREAM<T>;
// Fool the OS into unmapping the current block of the input
// stream so that blocks of the substreams can be mapped in
// without overlapping it. This is needed for correct execution
// on HU-UX.
//this needs to be cleaned up..Laura
current_input->seek(0);
assert(ae == AMI_ERROR_NO_ERROR);
// Loop through the substreams of the current stream, merging as
// many as we can at a time until all are done with.
for (sub_start = 0, ii = 0, jj = 0;
ii < substream_count;
sub_start += current_substream_len, ii++, jj++) {
sub_end = sub_start + current_substream_len - 1;
if (sub_end >= len) {
sub_end = len - 1;
}
current_input->new_substream(AMI_READ_STREAM, sub_start, sub_end,
(AMI_stream_base<T> **)
(the_substreams + jj));
// The substreams are read-once.
the_substreams[jj]->persist(PERSIST_READ_ONCE);
// If we've got all we can handle or we've seen them all, then
// merge them.
if ((jj >= (int) merge_arity - 1) || (ii == substream_count - 1)) {
tp_assert(jj <= (int) merge_arity - 1,
"Index got too large.");
#if DEBUG_ASSERTIONS
// Check the lengths before the merge.
TPIE_OS_OFFSET sz_output, sz_output_after_merge;
TPIE_OS_OFFSET sz_substream_total;
{
unsigned int kk;
sz_output = intermediate_tmp_stream->stream_len();
sz_substream_total = 0;
for (kk = jj+1; kk--; ) {
sz_substream_total += the_substreams[kk]->stream_len();
}
}
#endif
// This should append to the stream, since
// AMI_generalized_single_merge() does not rewind the output before
// merging.
ae = AMI_generalized_single_merge(the_substreams, jj+1,
intermediate_tmp_stream, m_obj);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
#if DEBUG_ASSERTIONS
// Verify the total lengths after the merge.
sz_output_after_merge = intermediate_tmp_stream->stream_len();
tp_assert(sz_output_after_merge - sz_output ==
sz_substream_total,
"Stream lengths do not add up: " <<
sz_output_after_merge - sz_output <<
" written when " <<
sz_substream_total <<
" were to have been read.");
#endif
// Delete the substreams. jj is currently the index of the
// largest, so we want to bump it up before the idiomatic
// loop.
for (jj++; jj--; ) {
delete the_substreams[jj];
}
// Now jj should be -1 so that it gets bumped back up to 0
// before the next iteration of the outer loop.
tp_assert((jj == -1), "Index not reduced to -1.");
} // if
} //for
// Get rid of the current input stream and use the next one.
delete current_input;
current_input = intermediate_tmp_stream;
}
k++;
}
//Monitoring prints.
TP_LOG_DEBUG_ID("Number of passes incl run formation is " << k+1);
delete [] the_substreams;
return AMI_ERROR_NO_ERROR;
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,38 @@
//
// File: ami_optimized_sort.h
// $Id: ami_optimized_sort.h,v 1.4 2003/04/17 13:10:02 jan Exp $
//
// Optimized merge sorting.
//
#ifndef _AMI_SORT_OPTIMIZED_H
#define _AMI_SORT_OPTIMIZED_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#ifndef AMI_STREAM_IMP_SINGLE
# warning Including __FILE__ when AMI_STREAM_IMP_SINGLE undefined.
#endif
#include <ami_merge.h>
#include <ami_optimized_merge.h>
//------------------------------------------------------------
template<class T>
AMI_err
AMI_optimized_sort(AMI_STREAM<T> *instream, AMI_STREAM<T> *outstream) {
return AMI_partition_and_merge(instream, outstream);
}
//------------------------------------------------------------
template<class T, class KEY>
AMI_err
AMI_optimized_sort(AMI_STREAM<T> *instream, AMI_STREAM<T> *outstream,
int keyoffset, KEY dummykey) {
return AMI_partition_and_merge(instream, outstream, keyoffset, dummykey);
}
#endif
+440
View File
@@ -0,0 +1,440 @@
// Copyright (C) 2002 Octavian Procopiuc
//
// File: ami_point.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// The AMI_point and AMI_record classes.
//
// $Id: ami_point.h,v 1.9 2005/01/21 16:55:29 tavi Exp $
//
#ifndef AMI_POINT_H_
#define AMI_POINT_H_
// For ostream.
#include <iostream>
// This is a hack. It works for integer types only.
template<class coord_t>
class infinity_t {
static coord_t minf;// = (1 << (8*sizeof(coord_t) - 1));
static coord_t pinf;// = ~minf;
public:
int operator+() const { return pinf; }
int operator-() const { return minf; }
};
template<class coord_t>
coord_t infinity_t<coord_t>::minf = (1 << (8*sizeof(coord_t) - 1));
template<class coord_t>
coord_t infinity_t<coord_t>::pinf = ~(1 << (8*sizeof(coord_t) - 1));
//int infinity_t<int>::minf = (1 << (8*sizeof(int) - 1));
//int infinity_t<int>::pinf = ~(1 << (8*sizeof(int) - 1));
// The base class for AMI_point.
template<class coord_t, size_t dim>
class AMI_point_base {
protected:
coord_t coords_[dim];
public:
static infinity_t<coord_t> Inf;
// The default constructor.
AMI_point_base() {}
// The array operators for accessing/setting the coordinates.
coord_t& operator[](size_t i) { return coords_[i]; }
const coord_t& operator[](size_t i) const { return coords_[i]; }
// Operator < for window queries. It's actually more like <=.
bool operator<(const AMI_point_base<coord_t, dim>& p) const {
size_t i;
for (i = 0; i < dim; i++)
if (p[i] < coords_[i])
break;
return (i == dim);
}
void set_min(const AMI_point_base<coord_t, dim>& p) {
for (size_t j = 0; j < dim; j++)
coords_[j] = min(coords_[j], p[j]);
}
void set_max(const AMI_point_base<coord_t, dim>& p) {
for (size_t j = 0; j < dim; j++)
coords_[j] = max(coords_[j], p[j]);
}
// Scalar product.
coord_t operator*(const AMI_point_base<coord_t,dim>& p) const {
coord_t ans = coords_[0]*p[0];
for (size_t i = 1; i < dim; i++)
ans += coords_[i]*p[i];
return ans;
}
};
template<class coord_t, size_t dim>
infinity_t<coord_t> AMI_point_base<coord_t, dim>::Inf = infinity_t<coord_t>();
// The AMI_point class.
template <class coord_t, size_t dim>
class AMI_point: public AMI_point_base<coord_t, dim> {
protected:
using AMI_point_base<coord_t, dim>::coords_;
public:
bool operator==(const AMI_point<coord_t, dim>& p) const {
size_t i = 0;
while (i < dim) {
if (coords_[i] != p[i])
break;
i++;
}
return (i == dim);
}
// The comparison class. For sorting on each of the dim dimensions.
class cmp {
// The dimension on which to compare. It should be less than dim.
size_t d_;
public:
cmp(size_t d = 0): d_(d) {}
inline int compare(const AMI_point<coord_t,dim>& p1,
const AMI_point<coord_t,dim>& p2) const {
// Lexicographic order starting with dimension d_.
if (p1[d_] < p2[d_])
return -1;
else if (p1[d_] > p2[d_])
return 1;
else
return _compare(p1, p2);
}
// This operator is used by STL sort().
bool operator()(const AMI_point<coord_t, dim>& p1,
const AMI_point<coord_t, dim>& p2) const {
return (compare(p1, p2) == -1);
}
private:
int _compare(const AMI_point<coord_t,dim>& p1,
const AMI_point<coord_t,dim>& p2) const {
size_t j = 0;
// Cycle once through all dimensions, starting with d_.
while (j < dim && p1[(j+d_)%dim] == p2[(j+d_)%dim])
j++;
if (j == dim)
return 0;
else
return (p1[(j+d_)%dim] < p2[(j+d_)%dim]) ? -1: 1;
}
};
};
template<class coord_t, size_t dim>
ostream& operator<<(ostream& s, const AMI_point<coord_t, dim>& p) {
for (size_t i = 0; i < dim-1; i++)
s << p[i] << " ";
return s << p[dim-1];
}
#ifdef _WIN32
#else
// Partial specialization of AMI_point for 2 dimensions.
template <class coord_t>
class AMI_point<coord_t, 2>: public AMI_point_base<coord_t, 2> {
protected:
using AMI_point_base<coord_t, 2>::coords_;
public:
AMI_point() {}
AMI_point(coord_t _x, coord_t _y) { coords_[0] = _x; coords_[1] = _y; }
coord_t x() const { return coords_[0]; }
coord_t& x() { return coords_[0]; }
coord_t y() const { return coords_[1]; }
coord_t& y() { return coords_[1]; }
bool operator==(const AMI_point<coord_t, 2>& p) const {
return (coords_[0] == p.coords_[0]) &&
(coords_[1] == p.coords_[1]);
}
bool operator!=(const AMI_point<coord_t, 2>& p) const {
return (coords_[0] != p.coords_[0]) ||
(coords_[1] != p.coords_[1]);
}
bool less_x(const AMI_point<coord_t, 2>& b) const {
return (coords_[0] < b.coords_[0]) ||
((coords_[0] == b.coords_[0]) && (coords_[1] < b.coords_[1]));
}
bool less_y(const AMI_point<coord_t, 2>& b) const {
return (coords_[1] < b.coords_[1]) ||
((coords_[1] == b.coords_[1]) && (coords_[0] < b.coords_[0]));
}
struct less_X {
bool operator()(const AMI_point<coord_t, 2>& a,
const AMI_point<coord_t, 2>& b) const
{ return (a[0] < b[0]) || ((a[0] == b[0]) && (a[1] < b[1])); }
};
struct less_Y {
bool operator()(const AMI_point<coord_t, 2>& a,
const AMI_point<coord_t, 2>& b) const
{ return (a[1] < b[1]) || ((a[1] == b[1]) && (a[0] < b[0])); }
};
// The comparison class. For sorting on each of the dim dimensions.
class cmp {
// The dimension on which to compare. It should be less than 2.
size_t d_;
public:
cmp(size_t d = 0): d_(d) {}
inline int compare(const AMI_point<coord_t, 2>& p1,
const AMI_point<coord_t, 2>& p2) const {
// Lexicographic order starting with dimension d_.
if (p1[d_] < p2[d_])
return -1;
else if (p1[d_] > p2[d_])
return 1;
else if (p1[(d_+1)%2] < p2[(d_+1)%2])
return -1;
else if (p2[(d_+1)%2] < p1[(d_+1)%2])
return 1;
else
return 0;
}
// This operator is used by STL sort().
bool operator()(const AMI_point<coord_t, 2>& p1,
const AMI_point<coord_t, 2>& p2) const {
return (compare(p1, p2) == -1);
}
};
};
template<class coord_t>
ostream& operator<<(ostream& s, const AMI_point<coord_t, 2>& p) {
return s << p[0] << " " << p[1];
}
#endif // !_WIN32
template<class coord_t, class data_t, size_t dim>
class AMI_record_base {
public:
typedef AMI_point<coord_t, dim> point_t;
point_t key;
data_t data;
// AMI_record() {}
AMI_record_base(const point_t& p, data_t _data = data_t(0)):
key(p), data(_data) {}
AMI_record_base(data_t _data = data_t(0)): data(_data) {}
data_t& id() { return data; }
const data_t& id() const { return data; }
bool operator==(const AMI_record_base<coord_t, data_t, dim>& r) const
{ return key == r.key; }
// The array operators for accessing/setting the coordinates.
coord_t& operator[](size_t i) { return key[i]; }
const coord_t& operator[](size_t i) const { return key[i]; }
// Operator < for window queries. It's actually more like <=.
bool operator<(const AMI_record_base<coord_t, data_t, dim>& p) const {
size_t i;
for (i = 0; i < dim; i++)
if (p[i] < key[i])
break;
return (i == dim);
}
void set_min(const AMI_record_base<coord_t, data_t, dim>& p) {
for (size_t j = 0; j < dim; j++)
key[j] = min(key[j], p[j]);
}
void set_max(const AMI_record_base<coord_t, data_t, dim>& p) {
for (size_t j = 0; j < dim; j++)
key[j] = max(key[j], p[j]);
}
// Scalar product.
coord_t operator*(const AMI_record_base<coord_t, data_t, dim>& p) const {
coord_t ans = key[0] * p[0];
for (size_t i = 1; i < dim; i++)
ans += key[i] * p[i];
return ans;
}
};
template<class coord_t, class data_t, size_t dim>
class AMI_record: public AMI_record_base<coord_t, data_t, dim> {
public:
AMI_record(const typename AMI_record_base<coord_t, data_t, dim>::point_t& p, data_t b = data_t(0)):
AMI_record_base<coord_t, data_t, dim>(p, b) {}
AMI_record(data_t b = data_t(0)): AMI_record_base<coord_t, data_t, dim>(b) {}
// The comparison class. For sorting on each of the dim dimensions.
class cmp {
// The dimension on which to compare. It should be less than dim.
size_t d_;
public:
cmp(size_t d = 0): d_(d) {}
inline int compare(const AMI_record<coord_t, data_t, dim>& p1,
const AMI_record<coord_t, data_t, dim>& p2) const {
// Lexicographic order starting with dimension d_.
if (p1[d_] < p2[d_])
return -1;
else if (p2[d_] < p1[d_])
return 1;
else
return _compare(p1, p2);
}
// This operator is used by STL sort().
bool operator()(const AMI_record<coord_t, data_t, dim>& p1,
const AMI_record<coord_t, data_t, dim>& p2) const {
return (compare(p1, p2) == -1);
}
private:
int _compare(const AMI_record<coord_t, data_t, dim>& p1,
const AMI_record<coord_t, data_t, dim>& p2) const {
size_t j = 0;
// Cycle once through all dimensions, starting with d_.
// TODO: change equality expression to an expression containing only <.
while (j < dim && p1[(j+d_)%dim] == p2[(j+d_)%dim])
j++;
if (j == dim)
return 0;
else
return (p1[(j+d_)%dim] < p2[(j+d_)%dim]) ? -1: 1;
}
};
};
template<class coord_t, class data_t, size_t dim>
ostream& operator<<(ostream& s, const AMI_record<coord_t, data_t, dim>& p) {
for (TPIE_OS_TIME_T i = 0; i < dim; i++)
s << p[i] << " ";
return s << (TPIE_OS_OFFSET)p.id();
}
#ifdef _WIN32
#else
// A record consists of a key (two-dimensional point) and a data
// item. Used by the EPStree.
template<class coord_t, class data_t>
struct AMI_record<coord_t, data_t, 2>: public AMI_record_base<coord_t, data_t, 2> {
public:
AMI_record(const typename AMI_record_base<coord_t, data_t, 2>::point_t& p, data_t b = data_t(0)):
AMI_record_base<coord_t, data_t, 2>(p, b) {}
AMI_record(const coord_t& x, const coord_t& y, const data_t& b):
AMI_record_base<coord_t, data_t, 2>(point_t(x, y), b) {}
AMI_record(data_t b = data_t(0)): AMI_record_base<coord_t, data_t, 2>(b) {}
struct less_X_point {
bool operator()(const AMI_record<coord_t, data_t, 2>& r,
const typename AMI_record_base<coord_t, data_t, 2>::point_t& p) const {
// return point_t::less_X()(r.key, p);
return r.key.less_x(p);
}
};
struct less_X {
bool operator()(const AMI_record<coord_t, data_t, 2>& r1,
const AMI_record<coord_t, data_t, 2>& r2) const {
// return point_t::less_X()(r1.key, r2.key);
return r1.key.less_x(r2.key);
}
int compare(const AMI_record<coord_t, data_t, 2>& r1,
const AMI_record<coord_t, data_t, 2>& r2) const {
if (r1.key.less_x(r2.key))
return -1;
else if (r2.key.less_x(r1.key))
return 1;
else
return 0;
}
};
struct less_Y {
bool operator()(const AMI_record<coord_t, data_t, 2>& r1,
const AMI_record<coord_t, data_t, 2>& r2) const {
// return point_t::less_Y()(r1.key, r2.key);
return r1.key.less_y(r2.key);
}
int compare(const AMI_record<coord_t, data_t, 2>& r1,
const AMI_record<coord_t, data_t, 2>& r2) const {
if (r1.key.less_y(r2.key))
return -1;
else if (r2.key.less_y(r1.key))
return 1;
else
return 0;
}
};
// The comparison class. For sorting on each of the dim dimensions.
class cmp {
// The dimension on which to compare. It should be less than 2.
size_t d_;
public:
cmp(size_t d = 0): d_(d) {}
inline int compare(const AMI_record<coord_t, data_t, 2>& p1,
const AMI_record<coord_t, data_t, 2>& p2) const {
// Lexicographic order starting with dimension d_.
if (p1[d_] < p2[d_])
return -1;
else if (p2[d_] < p1[d_])
return 1;
else if (p1[(d_+1)%2] < p2[(d_+1)%2])
return -1;
else if (p2[(d_+1)%2] < p1[(d_+1)%2])
return 1;
else
return 0;
}
// This operator is used by STL sort().
bool operator()(const AMI_record<coord_t, data_t, 2>& p1,
const AMI_record<coord_t, data_t, 2>& p2) const {
return (compare(p1, p2) == -1);
}
};
};
template<class coord_t, class data_t>
ostream& operator<<(ostream& s, const AMI_record<coord_t, data_t, 2>& p) {
return s << p[0] << " " << p[1] << " " << p.id();
}
#endif // !_WIN32
// Function object to extract the key from a record.
template<class coord_t, class data_t, size_t dim>
class AMI_record_key {
public:
AMI_point<coord_t, dim> operator()(const AMI_record<coord_t, data_t, dim>& r) const
{ return r.key; }
};
#endif // AMI_POINT_H_
+122
View File
@@ -0,0 +1,122 @@
// Copyright (c) 2005 Andrew Danner
//
// File: ami_queue.h
// Author: Andrew Danner <adanner@cs.duke.edu>
// Created: 2/22/05
//
// $Id: ami_queue.h,v 1.1 2005/04/25 19:08:06 adanner Exp $
//
#ifndef _AMI_QUEUE_H
#define _AMI_QUEUE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get the AMI_STREAM definition.
#include <ami_stream.h>
#include <ami_stack.h>
// Basic Implementation of I/O Efficient FIFO queue.
// Uses two stacks
template<class T>
class AMI_queue {
public:
bool empty();
TPIE_OS_OFFSET size(){return Qsize;}
AMI_queue();
AMI_queue(const char* basename);
~AMI_queue(void);
AMI_err enqueue(const T &t);
AMI_err dequeue(T **t);
void persist(persistence p);
private:
AMI_stack<T>* enQstack;
AMI_stack<T>* deQstack;
TPIE_OS_OFFSET Qsize;
};
//Constructor for Temporary Queue
template<class T>
AMI_queue<T>::AMI_queue() {
enQstack = new AMI_stack<T>();
deQstack = new AMI_stack<T>();
enQstack->persist(PERSIST_DELETE);
deQstack->persist(PERSIST_DELETE);
Qsize=0;
}
//Constructor for Queue with filename
template<class T>
AMI_queue<T>::AMI_queue(const char* basename)
{
char fname[BTE_STREAM_PATH_NAME_LEN];
strncpy(fname, basename, BTE_STREAM_PATH_NAME_LEN-4);
strcat(fname,".nq");
enQstack = new AMI_stack<T>(fname);
strncpy(fname, basename, BTE_STREAM_PATH_NAME_LEN-4);
strcat(fname,".dq");
deQstack = new AMI_stack<T>(fname);
enQstack->persist(PERSIST_PERSISTENT);
deQstack->persist(PERSIST_PERSISTENT);
Qsize=enQstack->stream_len()+deQstack->stream_len();
}
template<class T>
AMI_queue<T>::~AMI_queue(void)
{
delete enQstack;
delete deQstack;
}
template<class T>
void AMI_queue<T>::persist(persistence p) {
enQstack->persist(p);
deQstack->persist(p);
}
template<class T>
AMI_err AMI_queue<T>::enqueue(const T &t)
{
//Elements are pushed onto an Enqueue stack
AMI_err ae=enQstack->push(t);
if(ae == AMI_ERROR_NO_ERROR){
Qsize++;
}
return ae;
}
template<class T>
AMI_err AMI_queue<T>::dequeue(T **t)
{
AMI_err ae;
T* tmp;
//Elements popped from Dequeue stack
if(deQstack->stream_len()>0){
ae=deQstack->pop(t);
if(ae == AMI_ERROR_NO_ERROR){
Qsize--;
}
return ae;
}
else if(Qsize == 0){
return AMI_ERROR_END_OF_STREAM;
}
else{
//move elements from Enqueue stack to Dequeue stack
while((ae=enQstack->pop(&tmp)) == AMI_ERROR_NO_ERROR){
ae=deQstack->push(*tmp);
if(ae != AMI_ERROR_NO_ERROR){ return ae; }
}
if(ae != AMI_ERROR_BTE_ERROR){
return ae;
}
ae=deQstack->pop(t);
if(ae == AMI_ERROR_NO_ERROR){
Qsize--;
}
return ae;
}
}
#endif // _AMI_QUEUE_H
+359
View File
@@ -0,0 +1,359 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: ami_scan.H
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Header Created: 5/25/94
//
// This file was created mechanically by make, as specified in Makefile.
// There is no reason it should ever be edited by hand.
//
// The following Id string applies to the header used in the processs of
// generating this file. The header, stored in ami_scan.H.head, may be
// edited if necessary.
//
// $Id: ami_scan.H,v 1.1 2004/02/05 17:26:52 jan Exp $
//
//
#ifndef _AMI_SCAN_H
#define _AMI_SCAN_H
#include <ami_err.h>
#include <ami_stream.h>
typedef int AMI_SCAN_FLAG;
// The base class for scan objects.
class AMI_scan_object {
public:
virtual AMI_err initialize(void) = 0;
};
// BEGIN MECHANICALLY GENERATED CODE.
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: ami_scan_mac.H
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/24/94
//
// $Id: ami_scan.H,v 1.1 2004/02/05 17:26:52 jan Exp $
//
#ifndef _AMI_SCAN_MAC_H
#define _AMI_SCAN_MAC_H
// Macros for defining parameters to AMI_scan()
#define __SPARM_BASE(T,io,n) AMI_STREAM< T ## n > *io ## n
#define __SPARM_1(T,io) __SPARM_BASE(T,io,1)
#define __SPARM_2(T,io) __SPARM_1(T,io), __SPARM_BASE(T,io,2)
#define __SPARM_3(T,io) __SPARM_2(T,io), __SPARM_BASE(T,io,3)
#define __SPARM_4(T,io) __SPARM_3(T,io), __SPARM_BASE(T,io,4)
// Macros for defining types in a template for AMI_scan()
#define __STEMP_BASE(T,n) class T ## n
#define __STEMP_1(T) __STEMP_BASE(T,1)
#define __STEMP_2(T) __STEMP_1(T), __STEMP_BASE(T,2)
#define __STEMP_3(T) __STEMP_2(T), __STEMP_BASE(T,3)
#define __STEMP_4(T) __STEMP_3(T), __STEMP_BASE(T,4)
// Temporary space used within AMI_scan
#define __STS_BASE(T,t,n) T ## n t ## n
#define __STSPACE_1(T,t) __STS_BASE(T,t,1)
#define __STSPACE_2(T,t) __STSPACE_1(T,t) ; __STS_BASE(T,t,2)
#define __STSPACE_3(T,t) __STSPACE_2(T,t) ; __STS_BASE(T,t,3)
#define __STSPACE_4(T,t) __STSPACE_3(T,t) ; __STS_BASE(T,t,4)
// An array of flags.
#define __FSPACE(f,n) AMI_SCAN_FLAG f[n]
// Check stream validity.
#define __CHK_BASE(T,n) { \
if (T ## n == NULL || T ## n -> status() != AMI_STREAM_STATUS_VALID) {\
return AMI_ERROR_GENERIC_ERROR; \
} \
}
#define __CHKSTR_1(T) __CHK_BASE(T,1)
#define __CHKSTR_2(T) __CHKSTR_1(T) __CHK_BASE(T,2)
#define __CHKSTR_3(T) __CHKSTR_2(T) __CHK_BASE(T,3)
#define __CHKSTR_4(T) __CHKSTR_3(T) __CHK_BASE(T,4)
// Rewind the input streams prior to performing the scan.
#define __REW_BASE(T,n) { \
if ((_ami_err_ = T ## n -> seek(0)) != AMI_ERROR_NO_ERROR) { \
return _ami_err_; \
} \
}
#define __REWIND_1(T) __REW_BASE(T,1)
#define __REWIND_2(T) __REWIND_1(T) __REW_BASE(T,2)
#define __REWIND_3(T) __REWIND_2(T) __REW_BASE(T,3)
#define __REWIND_4(T) __REWIND_3(T) __REW_BASE(T,4)
// Set the input flags to true before entering the do loop so that the
// initial values will be read.
#define __SET_IF_BASE(f,n) f[n-1] = 1
#define __SET_IF_1(f) __SET_IF_BASE(f,1)
#define __SET_IF_2(f) __SET_IF_1(f); __SET_IF_BASE(f,2)
#define __SET_IF_3(f) __SET_IF_2(f); __SET_IF_BASE(f,3)
#define __SET_IF_4(f) __SET_IF_3(f); __SET_IF_BASE(f,4)
// If the flag is set, then read inputs into temporary space. Set the
// flag based on whether the read was succesful or not. If it was
// unsuccessful for any reason other than EOS, then break out of the
// scan loop. If the flag is not currently set, then either the scan
// management object did not take the last input or the last time we
// tried to read from this file we failed. If we read successfully
// last time, then reset the flag.
#define __STSR_BASE(t,ts,f,g,e,n) \
if (f[n-1]) { \
if (!(f[n-1] = g[n-1] = \
((e = ts ## n->read_item(&t ## n)) == AMI_ERROR_NO_ERROR))) { \
if (e != AMI_ERROR_END_OF_STREAM) { \
break; \
} \
} \
} else { \
f[n-1] = g[n-1]; \
}
#define __STS_READ_1(t,ts,f,g,e) __STSR_BASE(t,ts,f,g,e,1)
#define __STS_READ_2(t,ts,f,g,e) __STS_READ_1(t,ts,f,g,e) \
__STSR_BASE(t,ts,f,g,e,2)
#define __STS_READ_3(t,ts,f,g,e) __STS_READ_2(t,ts,f,g,e) \
__STSR_BASE(t,ts,f,g,e,3)
#define __STS_READ_4(t,ts,f,g,e) __STS_READ_3(t,ts,f,g,e) \
__STSR_BASE(t,ts,f,g,e,4)
// Write outputs. Only write if the flag is set. If there is an
// error during the write, then break out of the scan loop.
#define __STSW_BASE(u,us,f,e,n) \
if (f[n-1] && (e = us ## n -> write_item(u ## n)) != AMI_ERROR_NO_ERROR) { \
break; \
}
#define __STS_WRITE_1(u,us,f,e) __STSW_BASE(u,us,f,e,1)
#define __STS_WRITE_2(u,us,f,e) __STS_WRITE_1(u,us,f,e) __STSW_BASE(u,us,f,e,2)
#define __STS_WRITE_3(u,us,f,e) __STS_WRITE_2(u,us,f,e) __STSW_BASE(u,us,f,e,3)
#define __STS_WRITE_4(u,us,f,e) __STS_WRITE_3(u,us,f,e) __STSW_BASE(u,us,f,e,4)
// Arguments to the operate() call
#define __SCA_BASE(t,n) t ## n
#define __SCALL_ARGS_1(t) __SCA_BASE(t,1)
#define __SCALL_ARGS_2(t) __SCALL_ARGS_1(t), __SCA_BASE(t,2)
#define __SCALL_ARGS_3(t) __SCALL_ARGS_2(t), __SCA_BASE(t,3)
#define __SCALL_ARGS_4(t) __SCALL_ARGS_3(t), __SCA_BASE(t,4)
// Operate on the inputs to produce the outputs.
#define __SCALL_BASE(t,nt,if,sop,u,nu,of) \
sop->operate(__SCALL_ARGS_ ## nt (*t), if, __SCALL_ARGS_ ## nu (&u), of)
#define __SCALL_OP_1_1(t,if,sop,u,of) __SCALL_BASE(t,1,if,sop,u,1,of)
#define __SCALL_OP_1_2(t,if,sop,u,of) __SCALL_BASE(t,1,if,sop,u,2,of)
#define __SCALL_OP_1_3(t,if,sop,u,of) __SCALL_BASE(t,1,if,sop,u,3,of)
#define __SCALL_OP_1_4(t,if,sop,u,of) __SCALL_BASE(t,1,if,sop,u,4,of)
#define __SCALL_OP_2_1(t,if,sop,u,of) __SCALL_BASE(t,2,if,sop,u,1,of)
#define __SCALL_OP_2_2(t,if,sop,u,of) __SCALL_BASE(t,2,if,sop,u,2,of)
#define __SCALL_OP_2_3(t,if,sop,u,of) __SCALL_BASE(t,2,if,sop,u,3,of)
#define __SCALL_OP_2_4(t,if,sop,u,of) __SCALL_BASE(t,2,if,sop,u,4,of)
#define __SCALL_OP_3_1(t,if,sop,u,of) __SCALL_BASE(t,3,if,sop,u,1,of)
#define __SCALL_OP_3_2(t,if,sop,u,of) __SCALL_BASE(t,3,if,sop,u,2,of)
#define __SCALL_OP_3_3(t,if,sop,u,of) __SCALL_BASE(t,3,if,sop,u,3,of)
#define __SCALL_OP_3_4(t,if,sop,u,of) __SCALL_BASE(t,3,if,sop,u,4,of)
#define __SCALL_OP_4_1(t,if,sop,u,of) __SCALL_BASE(t,4,if,sop,u,1,of)
#define __SCALL_OP_4_2(t,if,sop,u,of) __SCALL_BASE(t,4,if,sop,u,2,of)
#define __SCALL_OP_4_3(t,if,sop,u,of) __SCALL_BASE(t,4,if,sop,u,3,of)
#define __SCALL_OP_4_4(t,if,sop,u,of) __SCALL_BASE(t,4,if,sop,u,4,of)
// Handle the no input case.
#define __SCALL_BASE_O(sop,u,nu,of) \
sop->operate(__SCALL_ARGS_ ## nu (&u), of)
#define __SCALL_OP_O_1(sop,u,of) __SCALL_BASE_O(sop,u,1,of)
#define __SCALL_OP_O_2(sop,u,of) __SCALL_BASE_O(sop,u,2,of)
#define __SCALL_OP_O_3(sop,u,of) __SCALL_BASE_O(sop,u,3,of)
#define __SCALL_OP_O_4(sop,u,of) __SCALL_BASE_O(sop,u,4,of)
// Handle the no output case.
#define __SCALL_BASE_I(t,nt,if,sop) \
sop->operate(__SCALL_ARGS_ ## nt (*t), if)
#define __SCALL_OP_I_1(t,if,sop) __SCALL_BASE_I(t,1,if,sop)
#define __SCALL_OP_I_2(t,if,sop) __SCALL_BASE_I(t,2,if,sop)
#define __SCALL_OP_I_3(t,if,sop) __SCALL_BASE_I(t,3,if,sop)
#define __SCALL_OP_I_4(t,if,sop) __SCALL_BASE_I(t,4,if,sop)
// The template for the whole AMI_scan(), with inputs and outputs.
#define __STEMPLATE(in_arity, out_arity) \
template< __STEMP_ ## in_arity (T), class SC, __STEMP_ ## out_arity (U) > \
AMI_err AMI_scan( __SPARM_ ## in_arity (T,_ts_), \
SC *soper, __SPARM_ ## out_arity (U,_us_)) \
{ \
__STSPACE_ ## in_arity (T,*_t_); \
__STSPACE_ ## out_arity (U,_u_); \
\
__FSPACE(_if_,in_arity); \
__FSPACE(_lif_,in_arity); \
__FSPACE(_of_,out_arity); \
\
AMI_err _op_err_, _ami_err_; \
\
__CHKSTR_ ## in_arity (_ts_) \
__CHKSTR_ ## out_arity (_us_) \
__REWIND_ ## in_arity (_ts_) \
soper->initialize(); \
\
__SET_IF_ ## in_arity (_if_); \
\
do { \
\
__STS_READ_ ## in_arity (_t_,_ts_,_if_,_lif_,_ami_err_) \
\
_op_err_ = __SCALL_OP_ ## in_arity ## _ ## \
out_arity(_t_,_if_,soper,_u_,_of_); \
\
__STS_WRITE_ ## out_arity(_u_,_us_,_of_,_ami_err_) \
\
} while (_op_err_ == AMI_SCAN_CONTINUE); \
\
if ((_ami_err_ != AMI_ERROR_NO_ERROR) && \
(_ami_err_ != AMI_ERROR_END_OF_STREAM)) { \
return _ami_err_; \
} \
\
return AMI_ERROR_NO_ERROR; \
}
// The template for the whole AMI_scan(), with no inputs. This is
// based on __STEMPLATE_() and could be merged into one big macro at
// the expense of having to define multiple versions of __STEMP_N()
// and __SPARM_N() to handle the case N = 0.
#define __STEMPLATE_O(out_arity) \
template< class SC, __STEMP_ ## out_arity (U) > \
AMI_err AMI_scan( SC *soper, __SPARM_ ## out_arity (U,_us_)) \
{ \
__STSPACE_ ## out_arity (U,_u_); \
\
__FSPACE(_of_,out_arity); \
\
AMI_err _op_err_, _ami_err_; \
\
__CHKSTR_ ## out_arity (_us_) \
soper->initialize(); \
\
do { \
\
_op_err_ = __SCALL_OP_O_ ## out_arity(soper,_u_,_of_); \
\
__STS_WRITE_ ## out_arity(_u_,_us_,_of_,_ami_err_) \
\
} while (_op_err_ == AMI_SCAN_CONTINUE); \
\
if ((_ami_err_ != AMI_ERROR_NO_ERROR) && \
(_ami_err_ != AMI_ERROR_END_OF_STREAM)) { \
return _ami_err_; \
} \
\
return AMI_ERROR_NO_ERROR; \
}
// The template for the whole AMI_scan(), with no outputs.
#define __STEMPLATE_I(in_arity) \
template< __STEMP_ ## in_arity (T), class SC > \
AMI_err AMI_scan( __SPARM_ ## in_arity (T,_ts_), SC *soper) \
{ \
__STSPACE_ ## in_arity (T,*_t_); \
\
__FSPACE(_if_,in_arity); \
__FSPACE(_lif_,in_arity); \
\
AMI_err _op_err_, _ami_err_; \
\
__CHKSTR_ ## in_arity (_ts_) \
__REWIND_ ## in_arity (_ts_); \
\
soper->initialize(); \
\
__SET_IF_ ## in_arity (_if_); \
\
do { \
\
__STS_READ_ ## in_arity (_t_,_ts_,_if_,_lif_,_ami_err_) \
\
_op_err_ = __SCALL_OP_I_ ## in_arity (_t_,_if_,soper); \
\
} while (_op_err_ == AMI_SCAN_CONTINUE); \
\
if ((_ami_err_ != AMI_ERROR_NO_ERROR) && \
(_ami_err_ != AMI_ERROR_END_OF_STREAM)) { \
return _ami_err_; \
} \
\
return AMI_ERROR_NO_ERROR; \
}
// Finally, the templates themsleves.
__STEMPLATE(1,1); __STEMPLATE(1,2); __STEMPLATE(1,3); __STEMPLATE(1,4);
__STEMPLATE(2,1); __STEMPLATE(2,2); __STEMPLATE(2,3); __STEMPLATE(2,4);
__STEMPLATE(3,1); __STEMPLATE(3,2); __STEMPLATE(3,3); __STEMPLATE(3,4);
__STEMPLATE(4,1); __STEMPLATE(4,2); __STEMPLATE(4,3); __STEMPLATE(4,4);
__STEMPLATE_O(1); __STEMPLATE_O(2); __STEMPLATE_O(3); __STEMPLATE_O(4);
__STEMPLATE_I(1); __STEMPLATE_I(2); __STEMPLATE_I(3); __STEMPLATE_I(4);
#endif // _AMI_SCAN_MAC_H
// END MECHANICALLY GENERATED CODE.
// The following Id string applies to the tail used in the processs of
// generating this file. The tail, stored in ami_scan.H.tail, may be
// edited if necessary.
//
// $Id: ami_scan.H,v 1.1 2004/02/05 17:26:52 jan Exp $
// A class template for copying streams by scanning.
template<class T>
class AMI_identity_scan : public AMI_scan_object {
public:
AMI_err initialize(void) { return AMI_ERROR_NO_ERROR; }
AMI_err operate(const T &in, AMI_SCAN_FLAG *sfin,
T *out, AMI_SCAN_FLAG *sfout);
};
template<class T>
AMI_err AMI_identity_scan<T>::operate(const T &in, AMI_SCAN_FLAG *sfin,
T *out, AMI_SCAN_FLAG *sfout)
{
if (*sfout = *sfin) {
*out = in;
return AMI_SCAN_CONTINUE;
} else {
return AMI_SCAN_DONE;
}
};
// A copy function for streams
template<class T>
AMI_err AMI_copy_stream(AMI_stream_base<T> *t, AMI_stream_base<T> *s)
{
AMI_identity_scan<T> id;
return AMI_scan(t, &id, s);
}
#endif // _AMI_SCAN_H
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: ami_scan.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Header Created: 5/25/94
//
// This file was created mechanically by make, as specified in Makefile.
// There is no reason it should ever be edited by hand.
//
// The following Id string applies to the header used in the processs of
// generating this file. The header, stored in ami_scan.h.head, may be
// edited if necessary.
//
// $Id: ami_scan.h.head,v 1.5 2003/04/20 23:12:42 tavi Exp $
//
//
#ifndef _AMI_SCAN_H
#define _AMI_SCAN_H
#include <ami_err.h>
#include <ami_stream.h>
typedef int AMI_SCAN_FLAG;
// The base class for scan objects.
class AMI_scan_object {
public:
virtual AMI_err initialize(void) = 0;
};
// BEGIN MECHANICALLY GENERATED CODE.
+42
View File
@@ -0,0 +1,42 @@
// END MECHANICALLY GENERATED CODE.
// The following Id string applies to the tail used in the processs of
// generating this file. The tail, stored in ami_scan.h.tail, may be
// edited if necessary.
//
// $Id: ami_scan.h.tail,v 1.3 2002/01/14 16:02:43 tavi Exp $
// A class template for copying streams by scanning.
template<class T>
class AMI_identity_scan : public AMI_scan_object {
public:
AMI_err initialize(void) { return AMI_ERROR_NO_ERROR; }
AMI_err operate(const T &in, AMI_SCAN_FLAG *sfin,
T *out, AMI_SCAN_FLAG *sfout);
};
template<class T>
AMI_err AMI_identity_scan<T>::operate(const T &in, AMI_SCAN_FLAG *sfin,
T *out, AMI_SCAN_FLAG *sfout)
{
if (*sfout = *sfin) {
*out = in;
return AMI_SCAN_CONTINUE;
} else {
return AMI_SCAN_DONE;
}
};
// A copy function for streams
template<class T>
AMI_err AMI_copy_stream(AMI_stream_base<T> *t, AMI_stream_base<T> *s)
{
AMI_identity_scan<T> id;
return AMI_scan(t, &id, s);
}
#endif // _AMI_SCAN_H
+17
View File
@@ -0,0 +1,17 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: ami_scan_mac.cpp
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/25/94
//
// This file exists only for the purpose of building ami_scan.h.
// The Makefile will invoke the C preprocessor on it in order to
// generate ami_scan.h, which is theactual header file that TPIE
// programs will include.
//
// $Id: ami_scan_mac.cpp,v 1.1 1994/05/25 19:35:06 dev Exp $
//
#include "ami_scan_mac.h"
+283
View File
@@ -0,0 +1,283 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: ami_scan_mac.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/24/94
//
// $Id: ami_scan_mac.h,v 1.10 2003/04/25 00:06:56 tavi Exp $
//
#ifndef _AMI_SCAN_MAC_H
#define _AMI_SCAN_MAC_H
// Macros for defining parameters to AMI_scan()
#define __SPARM_BASE(T,io,n) AMI_STREAM< T ## n > *io ## n
#define __SPARM_1(T,io) __SPARM_BASE(T,io,1)
#define __SPARM_2(T,io) __SPARM_1(T,io), __SPARM_BASE(T,io,2)
#define __SPARM_3(T,io) __SPARM_2(T,io), __SPARM_BASE(T,io,3)
#define __SPARM_4(T,io) __SPARM_3(T,io), __SPARM_BASE(T,io,4)
// Macros for defining types in a template for AMI_scan()
#define __STEMP_BASE(T,n) class T ## n
#define __STEMP_1(T) __STEMP_BASE(T,1)
#define __STEMP_2(T) __STEMP_1(T), __STEMP_BASE(T,2)
#define __STEMP_3(T) __STEMP_2(T), __STEMP_BASE(T,3)
#define __STEMP_4(T) __STEMP_3(T), __STEMP_BASE(T,4)
// Temporary space used within AMI_scan
#define __STS_BASE(T,t,n) T ## n t ## n
#define __STSPACE_1(T,t) __STS_BASE(T,t,1)
#define __STSPACE_2(T,t) __STSPACE_1(T,t) ; __STS_BASE(T,t,2)
#define __STSPACE_3(T,t) __STSPACE_2(T,t) ; __STS_BASE(T,t,3)
#define __STSPACE_4(T,t) __STSPACE_3(T,t) ; __STS_BASE(T,t,4)
// An array of flags.
#define __FSPACE(f,n) AMI_SCAN_FLAG f[n]
// Check stream validity.
#define __CHK_BASE(T,n) { \
if (T ## n == NULL || T ## n -> status() != AMI_STREAM_STATUS_VALID) {\
return AMI_ERROR_GENERIC_ERROR; \
} \
}
#define __CHKSTR_1(T) __CHK_BASE(T,1)
#define __CHKSTR_2(T) __CHKSTR_1(T) __CHK_BASE(T,2)
#define __CHKSTR_3(T) __CHKSTR_2(T) __CHK_BASE(T,3)
#define __CHKSTR_4(T) __CHKSTR_3(T) __CHK_BASE(T,4)
// Rewind the input streams prior to performing the scan.
#define __REW_BASE(T,n) { \
if ((_ami_err_ = T ## n -> seek(0)) != AMI_ERROR_NO_ERROR) { \
return _ami_err_; \
} \
}
#define __REWIND_1(T) __REW_BASE(T,1)
#define __REWIND_2(T) __REWIND_1(T) __REW_BASE(T,2)
#define __REWIND_3(T) __REWIND_2(T) __REW_BASE(T,3)
#define __REWIND_4(T) __REWIND_3(T) __REW_BASE(T,4)
// Set the input flags to true before entering the do loop so that the
// initial values will be read.
#define __SET_IF_BASE(f,n) f[n-1] = 1
#define __SET_IF_1(f) __SET_IF_BASE(f,1)
#define __SET_IF_2(f) __SET_IF_1(f); __SET_IF_BASE(f,2)
#define __SET_IF_3(f) __SET_IF_2(f); __SET_IF_BASE(f,3)
#define __SET_IF_4(f) __SET_IF_3(f); __SET_IF_BASE(f,4)
// If the flag is set, then read inputs into temporary space. Set the
// flag based on whether the read was succesful or not. If it was
// unsuccessful for any reason other than EOS, then break out of the
// scan loop. If the flag is not currently set, then either the scan
// management object did not take the last input or the last time we
// tried to read from this file we failed. If we read successfully
// last time, then reset the flag.
#define __STSR_BASE(t,ts,f,g,e,n) \
if (f[n-1]) { \
if (!(f[n-1] = g[n-1] = \
((e = ts ## n->read_item(&t ## n)) == AMI_ERROR_NO_ERROR))) { \
if (e != AMI_ERROR_END_OF_STREAM) { \
break; \
} \
} \
} else { \
f[n-1] = g[n-1]; \
}
#define __STS_READ_1(t,ts,f,g,e) __STSR_BASE(t,ts,f,g,e,1)
#define __STS_READ_2(t,ts,f,g,e) __STS_READ_1(t,ts,f,g,e) \
__STSR_BASE(t,ts,f,g,e,2)
#define __STS_READ_3(t,ts,f,g,e) __STS_READ_2(t,ts,f,g,e) \
__STSR_BASE(t,ts,f,g,e,3)
#define __STS_READ_4(t,ts,f,g,e) __STS_READ_3(t,ts,f,g,e) \
__STSR_BASE(t,ts,f,g,e,4)
// Write outputs. Only write if the flag is set. If there is an
// error during the write, then break out of the scan loop.
#define __STSW_BASE(u,us,f,e,n) \
if (f[n-1] && (e = us ## n -> write_item(u ## n)) != AMI_ERROR_NO_ERROR) { \
break; \
}
#define __STS_WRITE_1(u,us,f,e) __STSW_BASE(u,us,f,e,1)
#define __STS_WRITE_2(u,us,f,e) __STS_WRITE_1(u,us,f,e) __STSW_BASE(u,us,f,e,2)
#define __STS_WRITE_3(u,us,f,e) __STS_WRITE_2(u,us,f,e) __STSW_BASE(u,us,f,e,3)
#define __STS_WRITE_4(u,us,f,e) __STS_WRITE_3(u,us,f,e) __STSW_BASE(u,us,f,e,4)
// Arguments to the operate() call
#define __SCA_BASE(t,n) t ## n
#define __SCALL_ARGS_1(t) __SCA_BASE(t,1)
#define __SCALL_ARGS_2(t) __SCALL_ARGS_1(t), __SCA_BASE(t,2)
#define __SCALL_ARGS_3(t) __SCALL_ARGS_2(t), __SCA_BASE(t,3)
#define __SCALL_ARGS_4(t) __SCALL_ARGS_3(t), __SCA_BASE(t,4)
// Operate on the inputs to produce the outputs.
#define __SCALL_BASE(t,nt,if,sop,u,nu,of) \
sop->operate(__SCALL_ARGS_ ## nt (*t), if, __SCALL_ARGS_ ## nu (&u), of)
#define __SCALL_OP_1_1(t,if,sop,u,of) __SCALL_BASE(t,1,if,sop,u,1,of)
#define __SCALL_OP_1_2(t,if,sop,u,of) __SCALL_BASE(t,1,if,sop,u,2,of)
#define __SCALL_OP_1_3(t,if,sop,u,of) __SCALL_BASE(t,1,if,sop,u,3,of)
#define __SCALL_OP_1_4(t,if,sop,u,of) __SCALL_BASE(t,1,if,sop,u,4,of)
#define __SCALL_OP_2_1(t,if,sop,u,of) __SCALL_BASE(t,2,if,sop,u,1,of)
#define __SCALL_OP_2_2(t,if,sop,u,of) __SCALL_BASE(t,2,if,sop,u,2,of)
#define __SCALL_OP_2_3(t,if,sop,u,of) __SCALL_BASE(t,2,if,sop,u,3,of)
#define __SCALL_OP_2_4(t,if,sop,u,of) __SCALL_BASE(t,2,if,sop,u,4,of)
#define __SCALL_OP_3_1(t,if,sop,u,of) __SCALL_BASE(t,3,if,sop,u,1,of)
#define __SCALL_OP_3_2(t,if,sop,u,of) __SCALL_BASE(t,3,if,sop,u,2,of)
#define __SCALL_OP_3_3(t,if,sop,u,of) __SCALL_BASE(t,3,if,sop,u,3,of)
#define __SCALL_OP_3_4(t,if,sop,u,of) __SCALL_BASE(t,3,if,sop,u,4,of)
#define __SCALL_OP_4_1(t,if,sop,u,of) __SCALL_BASE(t,4,if,sop,u,1,of)
#define __SCALL_OP_4_2(t,if,sop,u,of) __SCALL_BASE(t,4,if,sop,u,2,of)
#define __SCALL_OP_4_3(t,if,sop,u,of) __SCALL_BASE(t,4,if,sop,u,3,of)
#define __SCALL_OP_4_4(t,if,sop,u,of) __SCALL_BASE(t,4,if,sop,u,4,of)
// Handle the no input case.
#define __SCALL_BASE_O(sop,u,nu,of) \
sop->operate(__SCALL_ARGS_ ## nu (&u), of)
#define __SCALL_OP_O_1(sop,u,of) __SCALL_BASE_O(sop,u,1,of)
#define __SCALL_OP_O_2(sop,u,of) __SCALL_BASE_O(sop,u,2,of)
#define __SCALL_OP_O_3(sop,u,of) __SCALL_BASE_O(sop,u,3,of)
#define __SCALL_OP_O_4(sop,u,of) __SCALL_BASE_O(sop,u,4,of)
// Handle the no output case.
#define __SCALL_BASE_I(t,nt,if,sop) \
sop->operate(__SCALL_ARGS_ ## nt (*t), if)
#define __SCALL_OP_I_1(t,if,sop) __SCALL_BASE_I(t,1,if,sop)
#define __SCALL_OP_I_2(t,if,sop) __SCALL_BASE_I(t,2,if,sop)
#define __SCALL_OP_I_3(t,if,sop) __SCALL_BASE_I(t,3,if,sop)
#define __SCALL_OP_I_4(t,if,sop) __SCALL_BASE_I(t,4,if,sop)
// The template for the whole AMI_scan(), with inputs and outputs.
#define __STEMPLATE(in_arity, out_arity) \
template< __STEMP_ ## in_arity (T), class SC, __STEMP_ ## out_arity (U) > \
AMI_err AMI_scan( __SPARM_ ## in_arity (T,_ts_), \
SC *soper, __SPARM_ ## out_arity (U,_us_)) \
{ \
__STSPACE_ ## in_arity (T,*_t_); \
__STSPACE_ ## out_arity (U,_u_); \
\
__FSPACE(_if_,in_arity); \
__FSPACE(_lif_,in_arity); \
__FSPACE(_of_,out_arity); \
\
AMI_err _op_err_, _ami_err_; \
\
__CHKSTR_ ## in_arity (_ts_) \
__CHKSTR_ ## out_arity (_us_) \
__REWIND_ ## in_arity (_ts_) \
soper->initialize(); \
\
__SET_IF_ ## in_arity (_if_); \
\
do { \
\
__STS_READ_ ## in_arity (_t_,_ts_,_if_,_lif_,_ami_err_) \
\
_op_err_ = __SCALL_OP_ ## in_arity ## _ ## \
out_arity(_t_,_if_,soper,_u_,_of_); \
\
__STS_WRITE_ ## out_arity(_u_,_us_,_of_,_ami_err_) \
\
} while (_op_err_ == AMI_SCAN_CONTINUE); \
\
if ((_ami_err_ != AMI_ERROR_NO_ERROR) && \
(_ami_err_ != AMI_ERROR_END_OF_STREAM)) { \
return _ami_err_; \
} \
\
return AMI_ERROR_NO_ERROR; \
}
// The template for the whole AMI_scan(), with no inputs. This is
// based on __STEMPLATE_() and could be merged into one big macro at
// the expense of having to define multiple versions of __STEMP_N()
// and __SPARM_N() to handle the case N = 0.
#define __STEMPLATE_O(out_arity) \
template< class SC, __STEMP_ ## out_arity (U) > \
AMI_err AMI_scan( SC *soper, __SPARM_ ## out_arity (U,_us_)) \
{ \
__STSPACE_ ## out_arity (U,_u_); \
\
__FSPACE(_of_,out_arity); \
\
AMI_err _op_err_, _ami_err_; \
\
__CHKSTR_ ## out_arity (_us_) \
soper->initialize(); \
\
do { \
\
_op_err_ = __SCALL_OP_O_ ## out_arity(soper,_u_,_of_); \
\
__STS_WRITE_ ## out_arity(_u_,_us_,_of_,_ami_err_) \
\
} while (_op_err_ == AMI_SCAN_CONTINUE); \
\
if ((_ami_err_ != AMI_ERROR_NO_ERROR) && \
(_ami_err_ != AMI_ERROR_END_OF_STREAM)) { \
return _ami_err_; \
} \
\
return AMI_ERROR_NO_ERROR; \
}
// The template for the whole AMI_scan(), with no outputs.
#define __STEMPLATE_I(in_arity) \
template< __STEMP_ ## in_arity (T), class SC > \
AMI_err AMI_scan( __SPARM_ ## in_arity (T,_ts_), SC *soper) \
{ \
__STSPACE_ ## in_arity (T,*_t_); \
\
__FSPACE(_if_,in_arity); \
__FSPACE(_lif_,in_arity); \
\
AMI_err _op_err_, _ami_err_; \
\
__CHKSTR_ ## in_arity (_ts_) \
__REWIND_ ## in_arity (_ts_); \
\
soper->initialize(); \
\
__SET_IF_ ## in_arity (_if_); \
\
do { \
\
__STS_READ_ ## in_arity (_t_,_ts_,_if_,_lif_,_ami_err_) \
\
_op_err_ = __SCALL_OP_I_ ## in_arity (_t_,_if_,soper); \
\
} while (_op_err_ == AMI_SCAN_CONTINUE); \
\
if ((_ami_err_ != AMI_ERROR_NO_ERROR) && \
(_ami_err_ != AMI_ERROR_END_OF_STREAM)) { \
return _ami_err_; \
} \
\
return AMI_ERROR_NO_ERROR; \
}
// Finally, the templates themsleves.
__STEMPLATE(1,1); __STEMPLATE(1,2); __STEMPLATE(1,3); __STEMPLATE(1,4);
__STEMPLATE(2,1); __STEMPLATE(2,2); __STEMPLATE(2,3); __STEMPLATE(2,4);
__STEMPLATE(3,1); __STEMPLATE(3,2); __STEMPLATE(3,3); __STEMPLATE(3,4);
__STEMPLATE(4,1); __STEMPLATE(4,2); __STEMPLATE(4,3); __STEMPLATE(4,4);
__STEMPLATE_O(1); __STEMPLATE_O(2); __STEMPLATE_O(3); __STEMPLATE_O(4);
__STEMPLATE_I(1); __STEMPLATE_I(2); __STEMPLATE_I(3); __STEMPLATE_I(4);
#endif // _AMI_SCAN_MAC_H
+94
View File
@@ -0,0 +1,94 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: ami_scan_utils.h
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 8/31/94
//
// $Id: ami_scan_utils.h,v 1.10 2003/09/12 01:46:38 jan Exp $
//
#ifndef _AMI_SCAN_UTILS_H
#define _AMI_SCAN_UTILS_H
#include <iostream>
// Get definitions for working with Unix and Windows.
#include <portability.h>
// Get the AMI_scan_object definition.
#include <ami_scan.h>
// A scan object class template for reading the contents of an
// ordinary C++ input stream into a TPIE stream. It works with
// streams of any type for which an >> operator is defined for C++
// stream input.
template<class T> class cxx_istream_scan : AMI_scan_object {
private:
istream *is;
public:
cxx_istream_scan(istream *instr = &cin);
AMI_err initialize(void);
AMI_err operate(T *out, AMI_SCAN_FLAG *sfout);
};
template<class T>
cxx_istream_scan<T>::cxx_istream_scan(istream *instr) : is(instr)
{
};
template<class T>
AMI_err cxx_istream_scan<T>::initialize(void)
{
return AMI_ERROR_NO_ERROR;
};
template<class T>
AMI_err cxx_istream_scan<T>::operate(T *out, AMI_SCAN_FLAG *sfout)
{
if (*is >> *out) {
*sfout = true;
return AMI_SCAN_CONTINUE;
} else {
*sfout = false;
return AMI_SCAN_DONE;
}
};
// A scan object to print the contents of a TPIE stream to a C++
// output stream. One item per line is written. It works with
// streams of any type for which an << operator is defined for C++
// stream output.
template<class T> class cxx_ostream_scan : AMI_scan_object {
private:
ostream *os;
public:
cxx_ostream_scan(ostream *outstr = &cout);
AMI_err initialize(void);
AMI_err operate(const T &in, AMI_SCAN_FLAG *sfin);
};
template<class T>
cxx_ostream_scan<T>::cxx_ostream_scan(ostream *outstr) : os(outstr)
{
};
template<class T>
AMI_err cxx_ostream_scan<T>::initialize(void)
{
return AMI_ERROR_NO_ERROR;
};
template<class T>
AMI_err cxx_ostream_scan<T>::operate(const T &in, AMI_SCAN_FLAG *sfin)
{
if (*sfin) {
*os << in << '\n';
return AMI_SCAN_CONTINUE;
} else {
return AMI_SCAN_DONE;
}
};
#endif // _AMI_SCAN_UTILS_H
+24
View File
@@ -0,0 +1,24 @@
//
// File: ami_sort.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 6/10/94
//
// $Id: ami_sort.h,v 1.11 2003/09/27 07:00:48 tavi Exp $
//
#ifndef _AMI_SORT_H
#define _AMI_SORT_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#define CONST const
#include <ami_stream.h>
#ifdef AMI_STREAM_IMP_SINGLE
#include <ami_sort_single.h>
#include <ami_optimized_sort.h>
#include <ami_sort_single_dh.h>
#endif
#endif // _AMI_SORT_H
+467
View File
@@ -0,0 +1,467 @@
//
// File: ami_sort_single.h
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 9/28/94
//
// $Id: ami_sort_single.h,v 1.22 2005/01/14 18:40:35 tavi Exp $
//
// Merge sorting for the AMI_STREAM_IMP_SINGLE implementation.
//
#ifndef _AMI_SORT_SINGLE_H
#define _AMI_SORT_SINGLE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#ifndef AMI_STREAM_IMP_SINGLE
# warning Including __FILE__ when AMI_STREAM_IMP_SINGLE undefined.
#endif
// For use in core by main_mem_operate().
#include <quicksort.h>
#include <pqueue_heap.h>
#include <ami_merge.h>
#include <ami_optimized_merge.h>
// A class of merge objects for merge sorting objects of type T. We
// will actually use one of three subclasses of this class which use
// either a comparison function, a comparison object, or the binary
// comparison operator <.
template <class T, class Q>
class merge_sort_manager /*: public AMI_merge_base<T> */{
private:
bool use_operator;
protected:
Q *pq;
arity_t input_arity;
#if DEBUG_ASSERTIONS
unsigned int input_count, output_count;
#endif
public:
merge_sort_manager(void);
virtual ~merge_sort_manager(void);
inline AMI_err operate(CONST T * CONST *in, AMI_merge_flag *taken_flags,
int &taken_index, T *out);
TPIE_OS_SIZE_T space_usage_per_stream(void);
};
template<class T, class Q>
merge_sort_manager<T,Q>::merge_sort_manager(void)
{
}
template<class T, class Q>
merge_sort_manager<T,Q>::~merge_sort_manager(void)
{
if (pq != NULL) {
delete pq;
}
}
template<class T, class Q>
TPIE_OS_SIZE_T merge_sort_manager<T,Q>::space_usage_per_stream(void)
{
return sizeof(arity_t) + sizeof(T);
}
template<class T, class Q>
AMI_err merge_sort_manager<T,Q>::operate(CONST T * CONST *in,
AMI_merge_flag * /*taken_flags*/,
int &taken_index,
T *out)
{
bool pqret;
// If the queue is empty, we are done. There should be no more
// inputs.
if (!pq->num_elts()) {
#if DEBUG_ASSERTIONS
arity_t ii;
for (ii = input_arity; ii--; ) {
tp_assert(in[ii] == NULL, "Empty queue but more input.");
}
tp_assert(input_count == output_count,
"Merge done, input_count = " << input_count <<
", output_count = " << output_count << '.');
#endif
// Delete the queue, which may take up a lot of main memory.
tp_assert(pq != NULL, "pq == NULL");
delete pq;
pq = NULL;
return AMI_MERGE_DONE;
} else {
arity_t min_source;
T min_t;
pqret = pq->extract_min(min_source,min_t);
tp_assert(pqret, "pq->extract_min() failed.");
*out = min_t;
if (in[min_source] != NULL) {
pqret = pq->insert(min_source,*in[min_source]);
tp_assert(pqret, "pq->insert() failed.");
taken_index = min_source;
//taken_flags[min_source] = 1;
#if DEBUG_ASSERTIONS
input_count++;
#endif
} else {
taken_index = -1;
}
#if DEBUG_ASSERTIONS
output_count++;
#endif
return AMI_MERGE_OUTPUT;
}
}
// Operator based merge sort manager.
template <class T, class Q>
class merge_sort_manager_op : public merge_sort_manager<T,Q> {
private:
Q *new_pqueue(arity_t arity);
protected:
using merge_sort_manager<T,Q>::pq;
using merge_sort_manager<T,Q>::input_arity;
#if DEBUG_ASSERTIONS
using merge_sort_manager<T,Q>::input_count;
using merge_sort_manager<T,Q>::output_count;
#endif
public:
merge_sort_manager_op(void);
virtual ~merge_sort_manager_op(void);
AMI_err main_mem_operate(T* mm_stream, TPIE_OS_SIZE_T len);
TPIE_OS_SIZE_T space_usage_overhead(void);
AMI_err initialize(arity_t arity, CONST T * CONST *in,
AMI_merge_flag *taken_flags,
int &taken_index);
};
template<class T,class Q>
merge_sort_manager_op<T,Q>::merge_sort_manager_op(void)
{
pq = NULL;
}
template<class T, class Q>
Q *merge_sort_manager_op<T,Q>::new_pqueue(arity_t arity)
{
return pq = new Q (arity);
}
template<class T,class Q>
merge_sort_manager_op<T,Q>::~merge_sort_manager_op(void)
{
}
template<class T,class Q>
AMI_err merge_sort_manager_op<T,Q>::main_mem_operate(T* mm_stream, TPIE_OS_SIZE_T len)
{
quick_sort_op(mm_stream, len);
return AMI_ERROR_NO_ERROR;
}
template<class T,class Q>
TPIE_OS_SIZE_T merge_sort_manager_op<T,Q>::space_usage_overhead(void)
{
return sizeof(Q);
}
template<class T, class Q>
AMI_err merge_sort_manager_op<T,Q>::initialize(arity_t arity, CONST T * CONST *in,
AMI_merge_flag *taken_flags,
int &taken_index)
{
arity_t ii;
input_arity = arity;
bool pqret;
tp_assert(arity > 0, "Input arity is 0.");
if (pq != NULL) {
delete pq;
pq = NULL;
}
new_pqueue(arity);
#if DEBUG_ASSERTIONS
input_count = output_count = 0;
#endif
for (ii = arity; ii--; ) {
if (in[ii] != NULL) {
taken_flags[ii] = 1;
pqret = pq->insert(ii,*in[ii]);
tp_assert(pqret, "pq->insert() failed.");
#if DEBUG_ASSERTIONS
input_count++;
#endif
} else {
taken_flags[ii] = 0;
}
}
taken_index = -1;
return AMI_MERGE_READ_MULTIPLE;
}
// Comparison object based merge sort manager.
template <class T, class Q, class CMPR>
class merge_sort_manager_obj : public merge_sort_manager<T,Q> {
private:
CMPR *cmp_o;
Q *new_pqueue(arity_t arity);
protected:
using merge_sort_manager<T,Q>::pq;
using merge_sort_manager<T,Q>::input_arity;
#if DEBUG_ASSERTIONS
using merge_sort_manager<T,Q>::input_count;
using merge_sort_manager<T,Q>::output_count;
#endif
public:
merge_sort_manager_obj(CMPR *cmp);
virtual ~merge_sort_manager_obj(void);
AMI_err main_mem_operate(T* mm_stream, TPIE_OS_SIZE_T len);
TPIE_OS_SIZE_T space_usage_overhead(void);
AMI_err initialize(arity_t arity, CONST T * CONST *in,
AMI_merge_flag *taken_flags,
int &taken_index);
};
template<class T, class Q, class CMPR>
merge_sort_manager_obj<T,Q,CMPR>::merge_sort_manager_obj(CMPR *cmp)
{
cmp_o = cmp;
pq = NULL;
}
template<class T, class Q, class CMPR>
Q *merge_sort_manager_obj<T,Q,CMPR>::new_pqueue(arity_t arity)
{
return pq = new Q (arity,cmp_o);
}
template<class T, class Q, class CMPR>
merge_sort_manager_obj<T,Q,CMPR>::~merge_sort_manager_obj(void)
{
}
template<class T, class Q, class CMPR>
AMI_err merge_sort_manager_obj<T,Q,CMPR>::main_mem_operate(T* mm_stream, TPIE_OS_SIZE_T len)
{
quick_sort_obj(mm_stream, len, cmp_o);
return AMI_ERROR_NO_ERROR;
}
template<class T, class Q, class CMPR>
TPIE_OS_SIZE_T merge_sort_manager_obj<T,Q,CMPR>::space_usage_overhead(void)
{
return sizeof(Q);
}
template<class T, class Q, class CMPR>
AMI_err merge_sort_manager_obj<T,Q,CMPR>::initialize(arity_t arity, CONST T * CONST *in,
AMI_merge_flag *taken_flags,
int &taken_index)
{
arity_t ii;
input_arity = arity;
bool pqret;
tp_assert(arity > 0, "Input arity is 0.");
if (pq != NULL) {
delete pq;
pq = NULL;
}
new_pqueue(arity);
#if DEBUG_ASSERTIONS
input_count = output_count = 0;
#endif
for (ii = arity; ii--; ) {
if (in[ii] != NULL) {
taken_flags[ii] = 1;
pqret = pq->insert(ii,*in[ii]);
tp_assert(pqret, "pq->insert() failed.");
#if DEBUG_ASSERTIONS
input_count++;
#endif
} else {
taken_flags[ii] = 0;
}
}
taken_index = -1;
return AMI_MERGE_READ_MULTIPLE;
}
// Comparison function based merge sort manager.
template <class T, class Q>
class merge_sort_manager_cmp : public merge_sort_manager<T,Q> {
private:
int (*cmp_f)(CONST T&, CONST T&);
Q *new_pqueue(arity_t arity);
protected:
using merge_sort_manager<T,Q>::pq;
using merge_sort_manager<T,Q>::input_arity;
#if DEBUG_ASSERTIONS
using merge_sort_manager<T,Q>::input_count;
using merge_sort_manager<T,Q>::output_count;
#endif
public:
merge_sort_manager_cmp(int (*cmp)(CONST T&, CONST T&));
virtual ~merge_sort_manager_cmp(void);
AMI_err main_mem_operate(T* mm_stream, TPIE_OS_SIZE_T len);
TPIE_OS_SIZE_T space_usage_overhead(void);
AMI_err initialize(arity_t arity, CONST T * CONST *in,
AMI_merge_flag *taken_flags,
int &taken_index);
};
template<class T,class Q>
merge_sort_manager_cmp<T,Q>::merge_sort_manager_cmp(int (*cmp)(CONST T&,
CONST T&))
{
cmp_f = cmp;
pq = NULL;
}
template<class T, class Q>
Q *merge_sort_manager_cmp<T,Q>::new_pqueue(arity_t arity)
{
return pq = new Q (arity,cmp_f);
}
template<class T, class Q>
merge_sort_manager_cmp<T,Q>::~merge_sort_manager_cmp(void)
{
}
template<class T,class Q>
AMI_err merge_sort_manager_cmp<T,Q>::main_mem_operate(T* mm_stream, TPIE_OS_SIZE_T len)
{
quick_sort_cmp(mm_stream, len, cmp_f);
return AMI_ERROR_NO_ERROR;
}
template<class T,class Q>
TPIE_OS_SIZE_T merge_sort_manager_cmp<T,Q>::space_usage_overhead(void)
{
return sizeof(Q);
}
template<class T, class Q>
AMI_err merge_sort_manager_cmp<T,Q>::initialize(arity_t arity, CONST T * CONST *in,
AMI_merge_flag *taken_flags,
int &taken_index)
{
arity_t ii;
input_arity = arity;
bool pqret;
tp_assert(arity > 0, "Input arity is 0.");
if (pq != NULL) {
delete pq;
pq = NULL;
}
new_pqueue(arity);
#if DEBUG_ASSERTIONS
input_count = output_count = 0;
#endif
for (ii = arity; ii--; ) {
if (in[ii] != NULL) {
taken_flags[ii] = 1;
pqret = pq->insert(ii,*in[ii]);
tp_assert(pqret, "pq->insert() failed.");
#if DEBUG_ASSERTIONS
input_count++;
#endif
} else {
taken_flags[ii] = 0;
}
}
taken_index = -1;
return AMI_MERGE_READ_MULTIPLE;
}
// *******************************************************************
// * *
// * The actual AMI_sort calls *
// * *
// *******************************************************************
// A version of AMI_sort that takes an input stream of elements of
// type T, an output stream, and a user-specified comparison function
template<class T>
AMI_err AMI_sort_V1(AMI_STREAM<T> *instream, AMI_STREAM<T> *outstream,
int (*cmp)(CONST T&, CONST T&))
{
merge_sort_manager_cmp<T,pqueue_heap_cmp<arity_t,T> > msm(cmp);
return AMI_generalized_partition_and_merge(instream, outstream,
(merge_sort_manager_cmp<T, pqueue_heap_cmp<arity_t,T> > *)&msm);
}
// A version of AMI_sort that takes an input streamof elements of type
// T, and an output stream, and and uses the < operator to sort
template<class T>
AMI_err AMI_sort_V1(AMI_STREAM<T> *instream, AMI_STREAM<T> *outstream)
{
merge_sort_manager_op<T,pqueue_heap_op<arity_t,T> > msm;
return AMI_generalized_partition_and_merge(instream, outstream,
(merge_sort_manager_op<T,pqueue_heap_op<arity_t,T> > *)&msm);
}
// A version of AMI_sort that takes an input stream of elements of
// type T, an output stream, and a user-specified comparison
// object. The comparison object "cmp", of (user-defined) class
// represented by CMPR, must have a member function called "compare"
// which is used for sorting the input stream.
template<class T, class CMPR>
AMI_err AMI_sort_V1(AMI_STREAM<T> *instream, AMI_STREAM<T> *outstream,
CMPR *cmp)
{
merge_sort_manager_obj<T,pqueue_heap_obj<arity_t,T,CMPR>,CMPR > msm(cmp);
return AMI_generalized_partition_and_merge (instream, outstream,
(merge_sort_manager_obj<T,pqueue_heap_obj<arity_t,T,CMPR>,CMPR> *)&msm);
}
#endif // _AMI_SORT_SINGLE_H
File diff suppressed because it is too large Load Diff
+376
View File
@@ -0,0 +1,376 @@
// Copyright (c) 1995 Darren Vengroff
//
// File: ami_sparse_matrix.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 3/2/95
//
// $Id: ami_sparse_matrix.h,v 1.14 2005/07/07 20:45:37 adanner Exp $
//
#ifndef AMI_SPARSE_MATRIX_H
#define AMI_SPARSE_MATRIX_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <iostream>
// We need dense matrices to support some sparse/dense interactions.
#include <ami_matrix.h>
// A spares matrix element is labeled with a row er and a column ec.
// A sparse matrix is simply represented by a colletion of these.
template <class T>
class AMI_sm_elem {
public:
TPIE_OS_OFFSET er;
TPIE_OS_OFFSET ec;
T val;
};
template <class T>
ostream &operator<<(ostream& s, const AMI_sm_elem<T> &a)
{
return s << a.er << ' ' << a.ec << ' ' << a.val;
};
template <class T>
istream &operator>>(istream& s, AMI_sm_elem<T> &a)
{
return s >> a.er >> a.ec >> a.val;
};
template<class T>
class AMI_sparse_matrix : public AMI_STREAM< AMI_sm_elem<T> > {
private:
// How many rows and columns.
TPIE_OS_OFFSET r,c;
public:
AMI_sparse_matrix(TPIE_OS_OFFSET row, TPIE_OS_OFFSET col);
~AMI_sparse_matrix(void);
TPIE_OS_OFFSET rows();
TPIE_OS_OFFSET cols();
};
template<class T>
AMI_sparse_matrix<T>::AMI_sparse_matrix(TPIE_OS_OFFSET row, TPIE_OS_OFFSET col) :
r(row), c(col), AMI_STREAM< AMI_sm_elem<T> >()
{
}
template<class T>
AMI_sparse_matrix<T>::~AMI_sparse_matrix(void)
{
}
template<class T>
TPIE_OS_OFFSET AMI_sparse_matrix<T>::rows(void)
{
return r;
}
template<class T>
TPIE_OS_OFFSET AMI_sparse_matrix<T>::cols(void)
{
return c;
}
//
// A class of comparison object designed to facilitate sorting of
// elements of the spase matrix into bands.
//
template<class T>
class sm_band_comparator
{
private:
TPIE_OS_SIZE_T rpb;
public:
sm_band_comparator(TPIE_OS_SIZE_T rows_per_band) :
rpb(rows_per_band) {};
virtual ~sm_band_comparator(void) {};
// If they are in the same band, compare columns, otherwise,
// compare rows.
int compare(const AMI_sm_elem<T> &t1, const AMI_sm_elem<T> &t2) {
if ((t1.er / rpb) == (t2.er / rpb)) {
return int(t1.ec) - int(t2.ec);
} else {
return int(t1.er) - int(t2.er);
}
}
};
// A function to bandify a sparse matrix.
template<class T>
AMI_err AMI_sparse_bandify(AMI_sparse_matrix<T> &sm,
AMI_sparse_matrix<T> &bsm,
TPIE_OS_SIZE_T rows_per_band)
{
AMI_err ae;
sm_band_comparator<T> cmp(rows_per_band);
ae = AMI_sort_V1((AMI_STREAM< AMI_sm_elem<T> > *)&sm,
(AMI_STREAM< AMI_sm_elem<T> > *)&bsm,
(sm_band_comparator<T> *)&cmp);
return ae;
}
// Get all band information for the given matrix and the current
// runtime environment.
template<class T>
AMI_err AMI_sparse_band_info(AMI_sparse_matrix<T> &opm,
TPIE_OS_SIZE_T &rows_per_band,
TPIE_OS_OFFSET &total_bands)
{
TPIE_OS_SIZE_T sz_avail, single_stream_usage;
TPIE_OS_OFFSET rows = opm.rows();
AMI_err ae;
// Check available main memory.
sz_avail = MM_manager.memory_available ();
// How much memory does a single stream need in the worst case?
if ((ae = opm.main_memory_usage(&single_stream_usage,
MM_STREAM_USAGE_MAXIMUM)) !=
AMI_ERROR_NO_ERROR) {
return ae;
}
// Figure out how many elements of the output can fit in main
// memory at a time. This will determine the number of rows of
// the sparse matrix that go into a band.
rows_per_band = (sz_avail - single_stream_usage * 5) / sizeof(T);
if (rows_per_band > rows) {
rows_per_band = (TPIE_OS_SIZE_T)rows;
}
total_bands = (rows + rows_per_band - 1) / rows_per_band;
return AMI_ERROR_NO_ERROR;
}
//
//
//
//
template<class T>
AMI_err AMI_sparse_mult_scan_banded(AMI_sparse_matrix<T> &banded_opm,
AMI_matrix<T> &opv, AMI_matrix<T> &res,
TPIE_OS_OFFSET rows, TPIE_OS_OFFSET /*cols*/,
TPIE_OS_SIZE_T rows_per_band)
{
AMI_err ae;
AMI_sm_elem<T> *sparse_current;
T *vec_current;
TPIE_OS_OFFSET vec_row;
banded_opm.seek(0);
ae = banded_opm.read_item(&sparse_current);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
opv.seek(0);
ae = opv.read_item(&vec_current);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
vec_row = 0;
res.seek(0);
TPIE_OS_OFFSET next_band_start = rows_per_band;
TPIE_OS_OFFSET ii;
TPIE_OS_OFFSET curr_band_start = 0;
TPIE_OS_SIZE_T rows_in_current_band = rows_per_band;
bool sparse_done = false;
T *output_subvector = new T[rows_per_band];
for (ii = rows_per_band; ii--; ) {
output_subvector[ii] = 0;
}
while (1) {
//
// Each time we enter this loop, we have the following invariants:
//
// vec_current = an element of the vector.
//
// vec_row = row vec_current came from.
//
// sparse_current = current element from the banded sparse mat.
//
// curr_band_start = row beginning current band.
//
// next_band_start = row beginning next band.
//
// rows_in_current_band = as name implies.
//
if (sparse_done || (sparse_current->er >= next_band_start)) {
// If we are out of sparse elements or the sparse element
// row is in the next band then we have to write the
// current results, reset the output buffer, and rewind
// the vector.
ae = res.write_array(output_subvector, rows_in_current_band);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
if (sparse_done) {
// Write more zeores if necessary before breaking out
// of the loop. We have to do this in some cases if
// there are one or more empty bands at the bottom of
// the sparse matrix. It is unlikely this sort of
// thing would ever really happen, but we should be
// careful anyway.
T tmp = 0;
for (ii = rows - next_band_start; ii--; ) {
ae = res.write_item(tmp);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
break;
}
for (ii = rows_in_current_band; ii--; ) {
output_subvector[ii] = 0;
}
opv.seek(0);
curr_band_start = next_band_start;
next_band_start += rows_per_band;
if (next_band_start > rows) {
// The final band may not have exactly rows_per_band
// rows due to roundoff. We make the appropropriate
// adjustments here.
rows_in_current_band = (TPIE_OS_SIZE_T)(rows - curr_band_start);
next_band_start = rows;
} else {
rows_in_current_band = rows_per_band;
}
} else if (sparse_current->ec == vec_row) {
// If the column of the sparse matrix and the row of the
// vector that the current inputs come from are the same,
// then multiply them, add the result to the appropriate
// output element, and advance past the sparse element.
output_subvector[sparse_current->er - curr_band_start] +=
sparse_current->val * *vec_current;
ae = banded_opm.read_item(&sparse_current);
if (ae == AMI_ERROR_END_OF_STREAM) {
sparse_done = true;
} else if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
} else {
// If the sparse element column is past the current
// row in the vector, then advance the vector.
tp_assert(sparse_current->ec > vec_row,
"Sparse column fell behind current row.");
ae = opv.read_item(&vec_current);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
vec_row++;
}
}
delete [] output_subvector;
return AMI_ERROR_NO_ERROR;
}
// Multiply a sparse (n,m)-matrix by a dense m-vector to get a dense
// n-vector.
template<class T>
AMI_err AMI_sparse_mult(AMI_sparse_matrix<T> &opm, AMI_matrix<T> &opv,
AMI_matrix<T> &res)
{
TPIE_OS_SIZE_T rows_per_band;
TPIE_OS_OFFSET total_bands;
TPIE_OS_OFFSET rows;
TPIE_OS_OFFSET cols;
// size_t sz_avail, single_stream_usage;
AMI_err ae;
// Make sure the sizes of the matrix and vectors match up.
rows = opm.rows();
cols = opm.cols();
if ((cols != opv.rows()) || (rows != res.rows()) ||
(opv.cols() != 1) || (res.cols() != 1)) {
return AMI_MATRIX_BOUNDS;
}
// Get band information.
ae = AMI_sparse_band_info(opm, rows_per_band, total_bands);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Partition the sparse matrix into bands with the elements within
// a band sorted by column. This is all done by a single sort
// operation.
//
// Note that if our goal is to multiply a large number of
// different vectors by a single matrix we should seperate this
// step out into a preprocessing phacse so that the sort is only
// done once.
AMI_sparse_matrix<T> banded_opm(rows, cols);
ae = AMI_sparse_bandify(opm, banded_opm, rows_per_band);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Scan the contents of the bands and the vector to produce output.
ae = AMI_sparse_mult_scan_banded(banded_opm, opv, res,
rows, cols, rows_per_band);
return ae;
}
#endif // _AMI_SPARSE_MATRIX_H
+91
View File
@@ -0,0 +1,91 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: ami_stack.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 12/15/94
//
// $Id: ami_stack.h,v 1.9 2005/01/21 16:52:39 tavi Exp $
//
#ifndef _AMI_STACK_H
#define _AMI_STACK_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get the AMI_STREAM definition.
#include <ami_stream.h>
template<class T>
class AMI_stack : public AMI_STREAM<T> {
public:
using AMI_STREAM<T>::seek;
using AMI_STREAM<T>::truncate;
using AMI_STREAM<T>::stream_len;
AMI_stack();
AMI_stack(const char* path,
AMI_stream_type type = AMI_READ_WRITE_STREAM);
~AMI_stack(void);
AMI_err push(const T &t);
AMI_err pop(T **t);
};
template<class T>
AMI_stack<T>::AMI_stack() :
AMI_STREAM<T>()
{
}
template<class T>
AMI_stack<T>::AMI_stack(const char* path, AMI_stream_type type):
AMI_STREAM<T>(path, type)
{
}
template<class T>
AMI_stack<T>::~AMI_stack(void)
{
}
template<class T>
AMI_err AMI_stack<T>::push(const T &t)
{
AMI_err ae;
TPIE_OS_OFFSET slen;
ae = truncate((slen = stream_len())+1);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
ae = seek(slen);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
return write_item(t);
}
template<class T>
AMI_err AMI_stack<T>::pop(T **t)
{
AMI_err ae;
TPIE_OS_OFFSET slen;
slen = stream_len();
ae = seek(slen-1);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
ae = read_item(t);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
return truncate(slen-1);
}
#endif // _AMI_STACK_H
+99
View File
@@ -0,0 +1,99 @@
//
// File: ami_stream.h (formerly part of ami.h and ami_imps.h)
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
//
// $Id: ami_stream.h,v 1.6 2003/05/08 22:30:48 tavi Exp $
//
#ifndef _AMI_STREAM_H
#define _AMI_STREAM_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#ifndef AMI_VIRTUAL_BASE
# define AMI_VIRTUAL_BASE 0
#endif
// include definition of VERSION macro
#include <versions.h>
// Include the configuration header.
#include <config.h>
// Get the base class, enums, etc...
#include <ami_err.h>
#include <ami_stream_base.h>
// Get the device description class
#include <ami_device.h>
// Get an implementation definition
#if defined(AMI_IMP_SINGLE)
TPIE_OS_UNIX_ONLY_WARNING_AMI_IMP_SINGLE
#else
# define AMI_STREAM_IMP_SINGLE
#endif
#if defined(AMI_IMP_USER_DEFINED)
# warning The AMI_IMP_USER_DEFINED flag is obsolete. \
Please use AMI_STREAM_IMP_USER_DEFINED.
# warning Implicitly defining AMI_STREAM_IMP_USER_DEFINED.
# define AMI_STREAM_IMP_USER_DEFINED
#endif
// The number of implementations to be defined.
#define _AMI_STREAM_IMP_COUNT (defined(AMI_STREAM_IMP_USER_DEFINED) + \
defined(AMI_STREAM_IMP_SINGLE))
// Multiple implementations are allowed to coexist, with some
// restrictions. Declarations of streams must use explicit subclasses
// of AMI_stream_base to specify what type of streams they are.
// If the including module did not explicitly ask for multiple
// implementations but requested more than one implementation, issue a
// warning.
#ifndef AMI_STREAM_IMP_MULTI_IMP
# if (_AMI_STREAM_IMP_COUNT > 1)
# warning Multiple AMI_STREAM_IMP_* defined, \
but AMI_STREAM_IMP_MULTI_IMP undefined.
# warning Implicitly defining AMI_STREAM_IMP_MULTI_IMP.
# define AMI_STREAM_IMP_MULTI_IMP
# endif // (_AMI_STREAM_IMP_COUNT > 1)
#endif // AMI_STREAM_IMP_MULTI_IMP
// If we have multiple implementations, set AMI_STREAM to be the base
// class.
#ifdef AMI_STREAM_IMP_MULTI_IMP
# define AMI_STREAM AMI_stream_base
# define AMI_stream AMI_stream_base
#endif
// Now include the definitions of each implementation that will be
// used.
// Make sure at least one implementation was chosen. If none was,
// then choose one by default, but warn the user. [tavi] NO, don't
// bother. Since the IMP_SINGLE is the only existing implementation,
// just tacitly make it the default.
#if (_AMI_STREAM_IMP_COUNT < 1)
//# warning No implementation defined. Using AMI_STREAM_IMP_SINGLE by default.
# define AMI_STREAM_IMP_SINGLE
#endif // (_AMI_STREAM_IMP_COUNT < 1)
// User defined implementation.
#if defined(AMI_STREAM_IMP_USER_DEFINED)
// Do nothing. The user will provide a definition of AMI_STREAM.
#endif // defined(AMI_STREAM_IMP_USER_DEFINED)
// Single BTE stream implementation.
#if defined(AMI_STREAM_IMP_SINGLE)
# include <ami_stream_single.h>
// If this is the only implementation, then make it easier to get to.
# ifndef AMI_STREAM_IMP_MULTI_IMP
# define AMI_STREAM AMI_stream_single
# define AMI_stream AMI_stream_single
# endif // AMI_STREAM_IMP_MULTI_IMP
#endif // defined(AMI_STREAM_IMP_SINGLE)
#endif // _AMI_STREAM_H
+106
View File
@@ -0,0 +1,106 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: ami_stream_arith.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 12/10/94
//
// $Id: ami_stream_arith.h,v 1.6 2003/04/20 23:56:38 tavi Exp $
//
#ifndef _AMI_STREAM_ARITH_H
#define _AMI_STREAM_ARITH_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get the definition of the AMI_scan_object class.
#include <ami_scan.h>
#define SCAN_OPERATOR_DECLARATION(NAME,OP) \
\
template<class T> class AMI_scan_ ## NAME : AMI_scan_object { \
public: \
AMI_err initialize(void); \
AMI_err operate(const T &op1, const T &op2, AMI_SCAN_FLAG *sfin, \
T *res, AMI_SCAN_FLAG *sfout); \
}; \
\
template<class T> \
AMI_err AMI_scan_ ## NAME<T>::initialize(void) \
{ \
return AMI_ERROR_NO_ERROR; \
} \
\
\
template<class T> \
AMI_err AMI_scan_ ## NAME<T>::operate(const T &op1, const T &op2, \
AMI_SCAN_FLAG *sfin, \
T *res, AMI_SCAN_FLAG *sfout) \
{ \
if ((*sfout = (sfin[0] && sfin[1]))) { \
*res = op1 OP op2; \
return AMI_SCAN_CONTINUE; \
} else { \
return AMI_SCAN_DONE; \
} \
}
SCAN_OPERATOR_DECLARATION(add,+)
SCAN_OPERATOR_DECLARATION(sub,-)
SCAN_OPERATOR_DECLARATION(mult,*)
SCAN_OPERATOR_DECLARATION(div,/)
#define SCAN_SCALAR_OPERATOR_DECLARATION(NAME,OP) \
\
template<class T> class AMI_scan_scalar_ ## NAME : AMI_scan_object { \
private: \
T scalar; \
public: \
AMI_scan_scalar_ ## NAME(const T &s); \
virtual ~AMI_scan_scalar_ ## NAME(void); \
AMI_err initialize(void); \
AMI_err operate(const T &op, AMI_SCAN_FLAG *sfin, \
T *res, AMI_SCAN_FLAG *sfout); \
}; \
\
\
template<class T> \
AMI_scan_scalar_ ## NAME<T>:: \
AMI_scan_scalar_ ## NAME(const T &s) : \
scalar(s) \
{ \
} \
\
\
template<class T> \
AMI_scan_scalar_ ## NAME<T>::~AMI_scan_scalar_ ## NAME() \
{ \
} \
\
\
template<class T> \
AMI_err AMI_scan_scalar_ ## NAME<T>::initialize(void) \
{ \
return AMI_ERROR_NO_ERROR; \
} \
\
\
template<class T> \
AMI_err AMI_scan_scalar_ ## NAME<T>::operate(const T &op, \
AMI_SCAN_FLAG *sfin, \
T *res, AMI_SCAN_FLAG *sfout) \
{ \
if ((*sfout = *sfin)) { \
*res = op OP scalar; \
return AMI_SCAN_CONTINUE; \
} else { \
return AMI_SCAN_DONE; \
} \
}
SCAN_SCALAR_OPERATOR_DECLARATION(add,+)
SCAN_SCALAR_OPERATOR_DECLARATION(sub,-)
SCAN_SCALAR_OPERATOR_DECLARATION(mult,*)
SCAN_SCALAR_OPERATOR_DECLARATION(div,/)
#endif // _AMI_STREAM_ARITH_H
+93
View File
@@ -0,0 +1,93 @@
//
// File: ami_stream_base.h (formerly ami_base.h)
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/19/94
//
// $Id: ami_stream_base.h,v 1.5 2004/08/17 16:47:58 jan Exp $
//
#ifndef _AMI_STREAM_BASE_H
#define _AMI_STREAM_BASE_H
#define A_INLINE inline
#include <tpie_assert.h>
#include <ami_err.h>
#include <persist.h>
// Get definitions for working with Unix and Windows
#include <portability.h>
// AMI stream types passed to constructors
enum AMI_stream_type {
AMI_READ_STREAM = 1, // Open existing stream for reading
AMI_WRITE_STREAM, // Open for writing. Create if non-existent
AMI_APPEND_STREAM, // Open for writing at end. Create if needed.
AMI_READ_WRITE_STREAM // Open to read and write.
};
// AMI stream status.
enum AMI_stream_status {
AMI_STREAM_STATUS_VALID = 0,
AMI_STREAM_STATUS_INVALID = 1
};
// An abstract class template which implements a stream of objects
// of type T within the AMI. This is the superclass of all actual
// implementations of streams of T within the AMI (e.g. single device
// streams, single CPU/many disk streams, and distributed streams).
template<class T> class AMI_stream_base {
protected:
AMI_stream_status status_;
public:
AMI_stream_base(void) { status_ = AMI_STREAM_STATUS_INVALID; }
// Inquire the status.
AMI_stream_status status() const { return status_; }
bool is_valid() const { return status_ == AMI_STREAM_STATUS_VALID; }
bool operator!() const { return !is_valid(); }
// TODO: Does this need to be virtual?
virtual ~AMI_stream_base(void) {}
#if AMI_VIRTUAL_BASE
// A virtual psuedo-constructor for substreams.
virtual AMI_err new_substream(AMI_stream_type st,
TPIE_OS_OFFSET sub_begin,
TPIE_OS_OFFSET sub_end,
AMI_stream_base<T> **sub_stream) = 0;
// Access methods.
virtual A_INLINE AMI_err write_item(const T &tin) = 0;
virtual A_INLINE AMI_err read_item(T **tout) = 0;
virtual A_INLINE AMI_err read_array(T *mm_space, TPIE_OS_OFFSET *len) = 0;
virtual A_INLINE AMI_err write_array(const T *mm_space, TPIE_OS_OFFSET len) = 0;
// Misc.
virtual AMI_err main_memory_usage(size_t *usage,
MM_stream_usage usage_type) = 0;
virtual TPIE_OS_OFFSET stream_len(void) = 0;
virtual AMI_err name(char **stream_name) = 0;
virtual AMI_err seek(TPIE_OS_OFFSET offset) = 0;
virtual AMI_err truncate(TPIE_OS_OFFSET offset) = 0;
virtual int available_streams(void) = 0;
virtual TPIE_OS_OFFSET chunk_size(void) = 0;
virtual void persist(persistence) = 0;
#endif // AMI_VIRTUAL_BASE
};
#endif // _AMI_STREAM_BASE_H
@@ -0,0 +1,72 @@
//
// File: ami_stream_single.cpp (formerly ami_single.cpp)
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 8/24/93
//
#include <versions.h>
VERSION(ami_single_cpp,"$Id: ami_stream_single.cpp,v 1.4 2004/08/12 12:53:42 jan Exp $");
#include "lib_config.h"
// Don't bother defining any BTE implementation, since this is library
// code for the AMI.
#define BTE_STREAM_IMP_USER_DEFINED
#define BTE_STREAM BTE_stream_base
#define AMI_STREAM_IMP_SINGLE
#include <ami_stream.h>
// The default device description for AMI single streams.
AMI_device AMI_stream_single_base::default_device;
// The device index of the most recently created stream.
unsigned int AMI_stream_single_base::device_index;
// Initializer
unsigned int AMI_stream_single_base_device_initializer::count;
AMI_stream_single_base_device_initializer::
AMI_stream_single_base_device_initializer(void)
{
AMI_err ae;
if (!count++) {
// Try to initialize from the environment.
ae = AMI_stream_single_base::
default_device.read_environment(AMI_SINGLE_DEVICE_ENV);
if (ae == AMI_ERROR_NO_ERROR) {
return;
}
// Try to initialize from TMP_DIR
ae = AMI_stream_single_base::
default_device.read_environment(TMPDIR_ENV);
if (ae == AMI_ERROR_NO_ERROR) {
return;
}
// Try to initialize to a default path
ae = AMI_stream_single_base::
default_device.set_to_path(TMP_DIR "|" TMP_DIR);
if (ae != AMI_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("Unable to initialize the default device description for AMI single streams.");
}
TP_LOG_DEBUG_ID("Default device description for AMI single streams:");
TP_LOG_DEBUG_ID(AMI_stream_single_base::default_device);
// Set the last device index used to the last device index, so
// that the first stream will wrap around to go on device 0.
AMI_stream_single_base::device_index =
AMI_stream_single_base::default_device.arity() - 1;
}
}
AMI_stream_single_base_device_initializer::
~AMI_stream_single_base_device_initializer(void)
{
}
+512
View File
@@ -0,0 +1,512 @@
//
// File: ami_stream_single.h (formerly ami_single.h)
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/19/94
//
// $Id: ami_stream_single.h,v 1.13 2005/07/07 20:43:06 adanner Exp $
//
// AMI entry points implemented on top of a single BTE. This is useful
// for single CPU, single disk machines.
//
#ifndef _AMI_STREAM_SINGLE_H
#define _AMI_STREAM_SINGLE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// [tavi] for UINT_MAX
#include <limits.h>
#include <sys/types.h>
// Use tempnam() instead of mktemp().
// no - tempnam uses environment in way we dont like
#include <stdio.h>
// For free()
#include <stdlib.h>
// To make assertions.
#include <tpie_assert.h>
#include <assert.h>
// Get an appropriate BTE. Flags may have been set to determine
// exactly what BTE implementaion will be used, but that should be of
// little concern to us. bte_stream.h and recursively included files
// will worry about parsing the appropriate flags and getting us an
// implementation.
#include <bte_stream.h>
// Get the AMI_stream_base class.
#include <ami_stream_base.h>
// Get the base memory manager. Normally the BTE will have already
// gotten this, but in library code where we have no BTE defined, it
// may not.
#include <mm_base.h>
#include <ami_device.h>
#include <tpie_tempnam.h>
// An initializer class to set the default device for the
// AMI_stream_single_base class.
class AMI_stream_single_base_device_initializer {
private:
static unsigned int count;
public:
AMI_stream_single_base_device_initializer(void);
~AMI_stream_single_base_device_initializer(void);
};
// A base class for AMI single streams that is used to hold the
// default device description for AMI single streams regardless of the
// particular type of object in the stream.
class AMI_stream_single_base {
friend AMI_stream_single_base_device_initializer::
AMI_stream_single_base_device_initializer(void);
public:
// The default device description for AMI streams.
static AMI_device default_device;
// The index into the device list for the next stream.
static unsigned int device_index;
static const tpie_stats_stream& gstats()
{ return BTE_stream_base_generic::gstats(); }
};
// This is a trick to make sure that at least one initializer is declared.
// The constructor for this initializer will make sure that the default
// device is set up properly.
static AMI_stream_single_base_device_initializer one_ssbd_initializer_per_source_file;
// The single stream class.
template<class T> class AMI_stream_single : public AMI_stream_base<T>,
public AMI_stream_single_base {
private:
using AMI_stream_base<T>::status_;
// Point to a base stream, since the particular type of BTE
// stream we are using may vary.
BTE_STREAM<T> *btes;
int r_only;
// Non-zero if we should destroy the bte stream when we the
// AMI stream is destroyed.
int destruct_bte;
public:
// Read and write elements.
A_INLINE AMI_err read_item(T **elt);
A_INLINE AMI_err write_item(const T &elt);
A_INLINE AMI_err read_array(T *mm_space, TPIE_OS_OFFSET *len);
A_INLINE AMI_err write_array(const T *mm_space, TPIE_OS_OFFSET len);
// We have a variety of constructors for different uses.
// A temporary AMI_stream using the default BTE stream type and
// a temporary space on the disk or in some file system.
AMI_stream_single(unsigned int device = UINT_MAX);
// An AMI stream based on a specific path name.
AMI_stream_single(const char *path_name,
AMI_stream_type st = AMI_READ_WRITE_STREAM);
// An AMI stream based on a specific existing BTE stream. Note
// that in this case the BTE stream will not be detroyed when the
// destructor is called.
AMI_stream_single(BTE_STREAM<T> *bs);
// A psuedo-constructor for substreams.
AMI_err new_substream(AMI_stream_type st, TPIE_OS_OFFSET sub_begin,
TPIE_OS_OFFSET sub_end,
AMI_stream_base<T> **sub_stream);
// Return the number of items in the stream.
TPIE_OS_OFFSET stream_len(void) const { return btes->stream_len(); }
// Return the path name of this stream in newly allocated space.
AMI_err name(char **stream_name);
// Move to a specific position in the stream.
AMI_err seek(TPIE_OS_OFFSET offset);
// Return the current position in the stream.
TPIE_OS_OFFSET tell() const { return btes->tell(); }
// Truncate
AMI_err truncate(TPIE_OS_OFFSET offset);
// Query memory usage
AMI_err main_memory_usage(TPIE_OS_SIZE_T *usage,
MM_stream_usage usage_type);
// Destructor
~AMI_stream_single(void);
const tpie_stats_stream& stats() const { return btes->stats(); }
int available_streams(void);
TPIE_OS_OFFSET chunk_size(void) const { return btes->chunk_size(); }
void persist(persistence p);
persistence persist() const { return btes->persist(); }
char *sprint();
};
// Create a temporary AMI stream on one of the devices in the default
// device description. Persistence is PERSIST_DELETE by default. We
// are given the index of the string describing the desired device.
template<class T>
AMI_stream_single<T>::AMI_stream_single(unsigned int device) {
// [tavi] Hack to fix an error that appears in gcc 2.8.1
if (device == UINT_MAX) {
device = (device_index = ((device_index + 1) % default_device.arity()));
}
r_only = 0;
destruct_bte = 1;
// Get a unique name.
char *path = tpie_tempnam("AMI", default_device[device]);
TP_LOG_DEBUG_ID("Temporary stream in file: ");
TP_LOG_DEBUG_ID(path);
// Create the BTE stream.
btes = new BTE_STREAM<T>(path, BTE_WRITE_STREAM);
// (Short circuit evaluation...)
if (btes == NULL || btes->status() == BTE_STREAM_STATUS_INVALID) {
TP_LOG_FATAL_ID("BTE returned invalid or NULL stream.");
status_ = AMI_STREAM_STATUS_INVALID;
return;
}
btes->persist(PERSIST_DELETE);
if (seek(0) != AMI_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID("seek(0) returned error.");
status_ = AMI_STREAM_STATUS_INVALID;
return;
}
status_ = AMI_STREAM_STATUS_VALID;
};
// A stream created with this constructor will persist on disk at the
// location specified by the path name.
template<class T>
AMI_stream_single<T>::AMI_stream_single(const char *path_name,
AMI_stream_type st) {
// Decide BTE stream type
BTE_stream_type bst;
switch (st) {
case AMI_READ_STREAM:
bst = BTE_READ_STREAM;
break;
case AMI_APPEND_STREAM:
bst = BTE_APPEND_STREAM;
break;
case AMI_WRITE_STREAM:
case AMI_READ_WRITE_STREAM:
bst = BTE_WRITE_STREAM; //BTE_WRITE_STREAM means both read and
//write; this is inconsistent and should be modified..
break;
default:
TP_LOG_WARNING_ID("Unknown stream type passed to constructor;");
TP_LOG_WARNING_ID("Defaulting to AMI_READ_WRITE_STREAM.");
bst = BTE_WRITE_STREAM;
break;
}
r_only = ((st == AMI_READ_STREAM)? 1 : 0);
destruct_bte = 1;
// Create the BTE stream.
btes = new BTE_STREAM<T>(path_name, bst);
// (Short circuit evaluation...)
if (btes == NULL || btes->status() == BTE_STREAM_STATUS_INVALID) {
TP_LOG_FATAL_ID("BTE returned invalid or NULL stream.");
status_ = AMI_STREAM_STATUS_INVALID;
return;
}
btes->persist(PERSIST_PERSISTENT);
// If an APPEND stream, the BTE constructor seeks to its end;
if (st != AMI_APPEND_STREAM) {
if (seek(0) != AMI_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID("seek(0) returned error.");
status_ = AMI_STREAM_STATUS_INVALID;
return;
}
}
status_ = AMI_STREAM_STATUS_VALID;
};
template<class T>
AMI_stream_single<T>::AMI_stream_single(BTE_STREAM<T> *bs) {
destruct_bte = 0;
btes = bs;
if (btes == NULL || btes->status() == BTE_STREAM_STATUS_INVALID) {
TP_LOG_FATAL_ID("BTE returned invalid or NULL stream.");
status_ = AMI_STREAM_STATUS_INVALID;
return;
}
r_only = bs->read_only();
status_ = AMI_STREAM_STATUS_VALID;
};
template<class T>
AMI_err AMI_stream_single<T>::new_substream(AMI_stream_type st,
TPIE_OS_OFFSET sub_begin,
TPIE_OS_OFFSET sub_end,
AMI_stream_base<T> **sub_stream)
{
AMI_err ae = AMI_ERROR_NO_ERROR;
// Check permissions. Only READ and WRITE are allowed, and only READ is
// allowed if r_only is set.
if ((st != AMI_READ_STREAM) && ((st != AMI_WRITE_STREAM) || r_only)) {
*sub_stream = NULL;
TP_LOG_DEBUG_ID("permission denied");
return AMI_ERROR_PERMISSION_DENIED;
}
BTE_stream_base<T> *bte_ss;
if (btes->new_substream(((st == AMI_READ_STREAM) ? BTE_READ_STREAM :
BTE_WRITE_STREAM),
sub_begin, sub_end,
&bte_ss) != BTE_ERROR_NO_ERROR) {
TP_LOG_DEBUG_ID("new_substream failed");
*sub_stream = NULL;
return AMI_ERROR_BTE_ERROR;
}
AMI_stream_single<T> *ami_ss;
// This is a potentially dangerous downcast. It is being done for
// the sake of efficiency, so that calls to the BTE can be
// inlined. If multiple implementations of BTE streams are
// present it could be very dangerous.
// this is to avoid compiler warnings
// hack! XXX
#if(0)
BTE_STREAM<T> *bte_ss_b=0;
assert(sizeof(BTE_STREAM<T>*) == sizeof(BTE_stream_base<T>*));
memcpy(bte_ss_b, bte_ss, sizeof(BTE_STREAM<T>*));
ami_ss = new AMI_stream_single<T>(bte_ss_b);
#endif
ami_ss = new AMI_stream_single<T>((BTE_STREAM<T>*)bte_ss);
ami_ss->destruct_bte = 1;
ae = ami_ss->seek(0);
assert(ae == AMI_ERROR_NO_ERROR); // sanity check
*sub_stream = (AMI_stream_base<T> *)ami_ss;
return ae;
}
template<class T>
AMI_err AMI_stream_single<T>::name(char **stream_name)
{
BTE_err be = btes->name(stream_name);
if (be != BTE_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("bte error");
return AMI_ERROR_BTE_ERROR;
} else {
return AMI_ERROR_NO_ERROR;
}
}
// Move to a specific offset.
template<class T>
AMI_err AMI_stream_single<T>::seek(TPIE_OS_OFFSET offset)
{
if (btes->seek(offset) != BTE_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("bte error");
return AMI_ERROR_BTE_ERROR;
}
return AMI_ERROR_NO_ERROR;
}
// Truncate
template<class T>
AMI_err AMI_stream_single<T>::truncate(TPIE_OS_OFFSET offset)
{
if (btes->truncate(offset) != BTE_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("bte error");
return AMI_ERROR_BTE_ERROR;
}
return AMI_ERROR_NO_ERROR;
}
// Query memory usage
template<class T>
AMI_err AMI_stream_single<T>::main_memory_usage(TPIE_OS_SIZE_T *usage,
MM_stream_usage usage_type)
{
if (btes->main_memory_usage(usage, usage_type) != BTE_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("bte error");
return AMI_ERROR_BTE_ERROR;
}
switch (usage_type) {
case MM_STREAM_USAGE_OVERHEAD:
case MM_STREAM_USAGE_CURRENT:
case MM_STREAM_USAGE_MAXIMUM:
case MM_STREAM_USAGE_SUBSTREAM:
*usage += sizeof(*this);
break;
case MM_STREAM_USAGE_BUFFER:
break;
default:
tp_assert(0, "Unknown MM_stream_usage type added.");
break;
}
return AMI_ERROR_NO_ERROR;
}
template<class T>
AMI_stream_single<T>::~AMI_stream_single(void)
{
if (destruct_bte) {
delete btes;
}
}
template<class T>
A_INLINE AMI_err AMI_stream_single<T>::read_item(T **elt)
{
BTE_err bte_err;
AMI_err ae;
bte_err = btes->read_item(elt);
switch(bte_err) {
case BTE_ERROR_NO_ERROR:
ae = AMI_ERROR_NO_ERROR;
break;
case BTE_ERROR_END_OF_STREAM:
TP_LOG_DEBUG_ID("eos in read_item");
ae = AMI_ERROR_END_OF_STREAM;
break;
default:
TP_LOG_DEBUG_ID("bte error in read_item");
ae = AMI_ERROR_BTE_ERROR;
break;
}
return ae;
}
template<class T>
A_INLINE AMI_err AMI_stream_single<T>::write_item(const T &elt)
{
if (btes->write_item(elt) != BTE_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("bte error");
return AMI_ERROR_BTE_ERROR;
}
return AMI_ERROR_NO_ERROR;
}
template<class T>
A_INLINE AMI_err AMI_stream_single<T>::read_array(T *mm_space, TPIE_OS_OFFSET *len)
{
BTE_err be;
T *read;
TPIE_OS_OFFSET ii;
// How long is it.
TPIE_OS_OFFSET str_len = *len;
// Read them all.
for (ii = str_len; ii--; ) {
if ((be = btes->read_item(&read)) != BTE_ERROR_NO_ERROR) {
if (be == BTE_ERROR_END_OF_STREAM) {
return AMI_ERROR_END_OF_STREAM;
} else {
return AMI_ERROR_BTE_ERROR;
}
}
*mm_space++ = *read;
}
*len = str_len;
return AMI_ERROR_NO_ERROR;
}
template<class T>
A_INLINE AMI_err AMI_stream_single<T>::write_array(const T *mm_space, TPIE_OS_OFFSET len)
{
BTE_err be;
TPIE_OS_OFFSET ii;
for (ii = len; ii--; ) {
if ((be = btes->write_item(*mm_space++)) != BTE_ERROR_NO_ERROR) {
if (be == BTE_ERROR_END_OF_STREAM) {
return AMI_ERROR_END_OF_STREAM;
} else {
return AMI_ERROR_BTE_ERROR;
}
}
}
return AMI_ERROR_NO_ERROR;
}
template<class T>
int AMI_stream_single<T>::available_streams(void)
{
return btes->available_streams();
}
template<class T>
void AMI_stream_single<T>::persist(persistence p)
{
btes->persist(p);
}
// sprint()
// Return a string describing the stream
//
// This function gives easy access to the file name, length.
// It is not reentrant, but this should not be too much of a problem
// if you are careful.
template<class T>
char *AMI_stream_single<T>::sprint()
{
static char buf[BUFSIZ];
char *s;
name(&s);
sprintf(buf, "[AMI_STREAM %s %ld]", s, (long)stream_len());
delete s;
return buf;
}
#endif // _AMI_STREAM_SINGLE_H
+212
View File
@@ -0,0 +1,212 @@
// DK Summer 05 TODO:
// get rid of dh extension, convert to ami_merge
// perhaps grab merge calls from ami_sort_single_dh
// check mem usage thoroughly
// get rid of varArrays where not needed
// change "substream" to "run" where appropriate
// get rid of User defined BTE junk
// Document better
// File: apm_dh.h
#ifndef _APM_DH_H
#define _APM_DH_H
// **************************************************************************
// * *
// * This include file contains the routine *
// * AMI_single_merge *
// * used in several of TPIE's merge variants *
// * *
// **************************************************************************
// $Id: apm_dh.h,v 1.18 2005/08/24 19:32:38 adanner Exp $
// Get definitions for working with Unix and Windows
#include <portability.h>
// Includes needed from TPIE
#include <ami_stream.h>
#include <tpie_tempnam.h>
#include <mergeheap_dh.h> //For templated heaps
#include <quicksort.h> //For templated qsort_items
typedef int arity_t;
// **************************************************************************
// * *
// * A M I _ s i n g l e _ m e r g e _ d h *
// * *
// * This is a common merge routine for all of the AMI_merge, AMI_ptr_merge *
// * and AMI_key_merge entry points. It is also used by the sort entry *
// * points AMI_sort, AMI_ptr_sort and AMI_key_sort and by the routine *
// * AMI_partition_and_merge. Differences are encapsulated within the *
// * merge heap object 'MergeHeap'. It is assumed that MergeHeap.allocate *
// * was called before entering AMI_single_merge_dh. *
// * *
// **************************************************************************
template < class T, class M >
AMI_err
AMI_single_merge_dh (AMI_STREAM < T > **inStreams, arity_t arity,
AMI_STREAM < T > *outStream, M MergeHeap,
TPIE_OS_OFFSET cutoff=-1 )
{
TPIE_OS_SIZE_T i;
AMI_err ami_err;
TPIE_OS_OFFSET* nread = new TPIE_OS_OFFSET[arity];
//Pointers to current leading elements of streams
T** in_objects = new T*[arity];
// **************************************************************
// * Read first element from stream. Do not rewind! We may read *
// * more elements from the same stream later. *
// **************************************************************
for (i = 0; i < arity; i++) {
if ((ami_err = inStreams[i]->read_item (&(in_objects[i]))) !=
AMI_ERROR_NO_ERROR) {
if (ami_err == AMI_ERROR_END_OF_STREAM) {
in_objects[i] = NULL;
} else {
delete[] in_objects;
return ami_err;
}
} else {
MergeHeap.insert( in_objects[i], i );
}
nread[i]=1;
}
// *********************************************************
// * Build a heap from the smallest items of each stream *
// *********************************************************
MergeHeap.initialize ( );
// *********************************************************
// * Perform the merge until the inputs are exhausted. *
// *********************************************************
while (MergeHeap.sizeofheap() > 0) {
i = MergeHeap.get_min_run_id ();
if ((ami_err = outStream->write_item (*in_objects[i]))
!= AMI_ERROR_NO_ERROR) {
delete[] in_objects;
return ami_err;
}
//Check if we read as many elements as we are allowed to
if( (cutoff != -1) && (nread[i]>=cutoff)){
ami_err=AMI_ERROR_END_OF_STREAM;
}
else {
if ((ami_err = inStreams[i]->read_item (&(in_objects[i])))
!= AMI_ERROR_NO_ERROR) {
if (ami_err != AMI_ERROR_END_OF_STREAM) {
delete[] in_objects;
return ami_err;
}
}
}
if (ami_err == AMI_ERROR_END_OF_STREAM) {
MergeHeap.delete_min_and_insert ((T *) NULL);
} else {
nread[i]++;
MergeHeap.delete_min_and_insert (in_objects[i]);
}
}//while
//cleanup
delete [] in_objects;
delete [] nread;
return AMI_ERROR_NO_ERROR;
}
/*******************************************************************
* *
* The actual AMI_merge calls *
* *
* These are the AMI_merge entry points for merging without the *
* 'merge_management_object' used by AMI_generalized_merge. *
* These routines perform the special case of merging when the *
* the required output is the original records interleaved *
* according to a comparison operator or function. *
*******************************************************************/
/*
Merging with a heap that contains the records to be merged: CMPR is
the class of the comparison object, and must contain the method
'compare' which is called from within the merge.
TODO:
1) check that memory management is done right
2) add comparison operator and comparison function versions
3) watch out for conflicts with AMI_merge versions in
*/
template < class T, class CMPR >
AMI_err AMI_merge (AMI_STREAM < T > **inStreams, arity_t arity,
AMI_STREAM < T > *outStream, CMPR *cmp)
{
// make a merge heap which uses the user's comparison object
// and initialize it
merge_heap_dh_obj<T,CMPR> mrgheap (cmp);
mrgheap.allocate (arity);
//Rewind all the input streams
for(int i=0; i<arity; i++){ inStreams[i]->seek(0); }
return AMI_single_merge_dh ( inStreams, arity, outStream, mrgheap);
}
// Merging with a heap that keeps a pointer to the records rather than
// the records themselves: CMPR is the class of the comparison object,
// and must contain the method 'compare' which is called from within
// the merge.
// TODO:
// 1) check that memory management is done right
template < class T, class CMPR >
AMI_err AMI_ptr_merge (AMI_STREAM < T > **inStreams, arity_t arity,
AMI_STREAM < T > *outStream, CMPR *cmp)
{
// make a merge heap of pointers which uses the user's comparison
// object and initialize it
merge_heap_pdh_obj<T,CMPR> mrgheap (cmp);
mrgheap.allocate (arity);
//Rewind all the input streams
for(int i=0; i<arity; i++){ inStreams[i]->seek(0); }
return AMI_single_merge_dh ( inStreams, arity, outStream, mrgheap);
}
// Merging with a heap that contains copies of the keys from the
// records being merged, rather than the records themselves:
// The comparison object "cmp", of (user-defined) class represented by
// CMPR, must have a member function called "compare" which is used
// for merging the input streams, and a member function called "copy"
// which is used for copying the key (of type KEY) from a record of
// type T (the type to be sorted).
// TODO:
// 1) check that memory management is done right
template<class T, class KEY, class CMPR>
AMI_err AMI_key_merge (AMI_STREAM < T > **inStreams, arity_t arity,
AMI_STREAM < T > *outStream, CMPR *cmp)
{
// make a key merge heap which uses the user's comparison object
// and initialize it
merge_heap_dh_kobj<T,KEY,CMPR> mrgheap (cmp);
mrgheap.allocate (arity);
//Rewind all the input streams
for(int i=0; i<arity; i++){ inStreams[i]->seek(0); }
return AMI_single_merge_dh ( inStreams, arity, outStream, mrgheap);
}
#endif //_APM_DH_H
+124
View File
@@ -0,0 +1,124 @@
//
// File: b_vector.h
// Authors: Octavian Procopiuc <tavi@cs.duke.edu>
//
// Definition of the b_vector class.
//
// $Id: b_vector.h,v 1.7 2003/04/17 14:40:34 jan Exp $
//
#ifndef _B_VECTOR_H
#define _B_VECTOR_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <string.h>
template<class T>
class b_vector {
protected:
T* p_;
size_t capacity_;
public:
typedef T value_type;
typedef value_type* iterator;
typedef const value_type* const_iterator;
b_vector(T* p, size_t cap): p_(p), capacity_(cap) {}
iterator begin() { return p_; }
const_iterator begin() const { return p_; }
iterator end() { return p_ + capacity_; }
const_iterator end() const { return p_ + capacity_; }
// Get a reference to the i'th element.
T& operator[](size_t i) { return *(p_ + i); }
// Get a const reference to the i'th element.
const T& operator[](size_t i) const { return *(p_ + i); }
size_t capacity() const { return capacity_; }
// Copy length elements from the source vector, starting with
// element s_start, to this block, starting with element start.
// Return the number of elements copied. Source can be *this.
size_t copy(size_t start, size_t length,
const b_vector<T>& source, size_t s_start = 0);
// Copy from an array of elements.
size_t copy(size_t start, size_t length, const T* source);
// Insert item t in position pos; all items from position pos onward
// are shifted one position higher; the last item is lost.
void insert(const T& t, size_t pos);
// Erase the item in position pos and shift all items from position
// pos+1 onward one position lower; the last item becomes identical
// with the next to last item.
void erase(size_t pos);
};
////////////////////////////////
///////// **b_vector** /////////
////////////////////////////////
//// *b_vector::copy* ////
template<class T>
size_t b_vector<T>::copy(size_t start, size_t length,
const b_vector<T>& source, size_t s_start) {
// copy_length will store the actual number of items that can be copied.
size_t copy_length = length;
if (start < capacity_ && s_start < source.capacity()) {
// Check how much of length we can copy.
copy_length = (copy_length > capacity_ - start) ?
capacity_ - start: copy_length;
copy_length = (copy_length > source.capacity() - s_start) ?
source.capacity() - s_start: copy_length;
memmove(&(*this)[start], &source[s_start], copy_length * sizeof(T));
} else {
// start is too big. No copying.
copy_length = 0;
}
return copy_length;
}
//// *b_vector::copy* ////
template<class T>
size_t b_vector<T>::copy(size_t start, size_t length, const T* source) {
size_t copy_length = length;
if (start < capacity_) {
// Check how much of length we can copy.
copy_length = (copy_length > capacity_ - start) ?
capacity_ - start: copy_length;
memmove(&(*this)[start], source, copy_length * sizeof(T));
} else
copy_length = 0;
return copy_length;
}
//// *b_vector::insert* ////
template<class T>
void b_vector<T>::insert(const T& t, size_t pos) {
copy(pos + 1, capacity_ - pos - 1, *this, pos);
copy(pos, 1, &t);
}
//// *b_vector::erase* ////
template<class T>
void b_vector<T>::erase(size_t pos) {
copy(pos, capacity_ - pos - 1, *this, pos + 1);
}
#endif // _B_VECTOR_H
+80
View File
@@ -0,0 +1,80 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: bit.cpp
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 11/4/94
//
#include <versions.h>
VERSION(bit_cpp,"$Id: bit.cpp,v 1.5 2003/09/12 18:46:44 jan Exp $");
#include <bit.h>
bit::bit(void)
{
}
bit::bit(bool b)
{
data = (b == true);
}
bit::bit(int i)
{
data = (i != 0);
}
bit::bit(long int i)
{
data = (i != 0);
}
bit::operator bool(void)
{
return (data != 0);
}
bit::operator int(void)
{
return data;
}
bit::operator long int(void)
{
return data;
}
bit::~bit(void)
{
}
bit bit::operator+=(bit rhs)
{
return *this = *this + rhs;
}
bit bit::operator*=(bit rhs)
{
return *this = *this + rhs;
}
bit operator+(bit op1, bit op2)
{
return bit(op1.data ^ op2.data);
}
bit operator*(bit op1, bit op2)
{
return bit(op1.data & op2.data);
}
ostream &operator<<(ostream &s, bit b)
{
return s << int(b.data);
}
+42
View File
@@ -0,0 +1,42 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: bit.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 11/4/94
//
// $Id: bit.h,v 1.5 2003/09/12 01:46:38 jan Exp $
//
#ifndef _BIT_H
#define _BIT_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <iostream>
// A bit with two operarators, addition (= XOR) and multiplication (=
// AND).
class bit {
private:
char data;
public:
bit(void);
bit(bool);
bit(int);
bit(long int);
~bit(void);
operator bool(void);
operator int(void);
operator long int(void);
bit operator+=(bit rhs);
bit operator*=(bit rhs);
friend bit operator+(bit op1, bit op2);
friend bit operator*(bit op1, bit op2);
friend ostream &operator<<(ostream &s, bit b);
};
#endif // _BIT_H
+91
View File
@@ -0,0 +1,91 @@
// Copyright (c) 1995 Darren Vengroff
//
// File: bit_matrix.cpp
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 1/9/95
//
#include <versions.h>
VERSION(bit_matrix_cpp,"$Id: bit_matrix.cpp,v 1.15 2005/01/14 18:42:24 tavi Exp $");
#include <bit_matrix.h>
bit_matrix::bit_matrix(TPIE_OS_SIZE_T arows, TPIE_OS_SIZE_T acols) :
matrix<bit>(arows, acols)
{
}
bit_matrix::bit_matrix(matrix<bit> &mb) :
matrix<bit>(mb)
{
}
bit_matrix::~bit_matrix(void)
{
}
bit_matrix bit_matrix::operator=(const bit_matrix &rhs) {
return this->matrix<bit>::operator=((matrix<bit> &)rhs);
}
bit_matrix & bit_matrix::operator=(const TPIE_OS_OFFSET &rhs)
{
TPIE_OS_SIZE_T rows = this->rows();
TPIE_OS_SIZE_T ii;
if (this->cols() != 1) {
#if HANDLE_EXCEPTIONS
throw matrix_base<bit>::range();
#else
tp_assert(0, "Range error.");
#endif
}
for (ii = 0; ii < rows; ii++) {
(*this)[ii][0] = (long int)(rhs & (1 << ii)) >> ii;
}
return *this;
}
bit_matrix::operator TPIE_OS_OFFSET(void)
{
TPIE_OS_OFFSET res;
TPIE_OS_SIZE_T rows = this->rows();
TPIE_OS_SIZE_T ii;
if (this->cols() != 1) {
#if HANDLE_EXCEPTIONS
throw matrix_base<bit>::range();
#else
tp_assert(0, "Range error.");
#endif
}
for (res = 0, ii = 0; ii < rows; ii++) {
res |= (long int)((*this)[ii][0]) << ii;
}
return res;
}
bit_matrix operator+(const bit_matrix &op1, const bit_matrix &op2)
{
matrix<bit> sum = ((matrix<bit> &)op1) + ((matrix<bit> &)op2);
return sum;
}
bit_matrix operator*(const bit_matrix &op1, const bit_matrix &op2)
{
matrix<bit> prod = ((matrix<bit> &)op1) * ((matrix<bit> &)op2);
return prod;
}
ostream &operator<<(ostream &s, bit_matrix &bm)
{
return s << (matrix<bit> &)bm;
}
+49
View File
@@ -0,0 +1,49 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: bit_matrix.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 11/4/94
//
// $Id: bit_matrix.h,v 1.14 2005/01/14 18:35:00 tavi Exp $
//
#ifndef _BIT_MATRIX_H
#define _BIT_MATRIX_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <bit.h>
#include <matrix.h>
#include <sys/types.h>
// typedef matrix<bit> bit_matrix_0;
class bit_matrix : public matrix<bit> {
public:
using matrix<bit>::rows;
using matrix<bit>::cols;
bit_matrix(matrix<bit> &mb);
bit_matrix(TPIE_OS_SIZE_T rows, TPIE_OS_SIZE_T cols);
virtual ~bit_matrix(void);
bit_matrix operator=(const bit_matrix &rhs);
// We can assign from an offset, which is typically a source
// address for a BMMC permutation.
bit_matrix &operator=(const TPIE_OS_OFFSET &rhs);
operator TPIE_OS_OFFSET(void);
friend bit_matrix operator+(const bit_matrix &op1, const bit_matrix &op2);
friend bit_matrix operator*(const bit_matrix &op1, const bit_matrix &op2);
};
bit_matrix operator+(const bit_matrix &op1, const bit_matrix &op2);
bit_matrix operator*(const bit_matrix &op1, const bit_matrix &op2);
ostream &operator<<(ostream &s, bit_matrix &bm);
#endif // _BIT_MATRIX_H
+55
View File
@@ -0,0 +1,55 @@
//
// File: bte_coll.h
// Authors: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: bte_coll.h,v 1.4 2003/04/29 05:29:42 tavi Exp $
//
// Front end for the BTE collection classes.
//
#ifndef _BTE_COLL_H
#define _BTE_COLL_H
// Get the base class and various definitions.
#include <bte_coll_base.h>
// The MMAP implementation.
#include <bte_coll_mmap.h>
// The UFS implementation.
#include <bte_coll_ufs.h>
// Get definitions for working with Unix and Windows
#include <portability.h>
#if defined(BTE_COLLECTION_IMP_MMB)
// TPIE_OS_UNIX_ONLY_WARNING_BTE_COLLECTION_IMP_MMB_UNIX_ONLY
# define BTE_COLLECTION_IMP_MMAP
#endif
#define _BTE_COLL_IMP_COUNT (defined(BTE_COLLECTION_IMP_UFS) + \
defined(BTE_COLLECTION_IMP_MMAP) + \
defined(BTE_COLLECTION_IMP_USER_DEFINED))
// Multiple implem. are included, but we have to choose a default one.
#if (_BTE_COLL_IMP_COUNT > 1)
// TPIE_OS_UNIX_ONLY_WARNING_MULTIPLE_BTE_COLLECTION_IMP_DEFINED
# define BTE_COLLECTION_IMP_MMAP
#elif (_BTE_COLL_IMP_COUNT == 0)
// TPIE_OS_UNIX_ONLY_WARNING_NO_DEFAULT_BTE_COLLECTION
# define BTE_COLLECTION_IMP_MMAP
#endif
#define BTE_COLLECTION_MMAP BTE_collection_mmap<TPIE_BLOCK_ID_TYPE>
#define BTE_COLLECTION_UFS BTE_collection_ufs<TPIE_BLOCK_ID_TYPE>
#if defined(BTE_COLLECTION_IMP_MMAP)
# define BTE_COLLECTION BTE_COLLECTION_MMAP
#elif defined(BTE_COLLECTION_IMP_UFS)
# define BTE_COLLECTION BTE_COLLECTION_UFS
#elif defined(BTE_COLLECTION_IMP_USER_DEFINED)
// Do not define BTE_COLLECTION. The user will define it.
#endif
#endif // _BTE_COLL_H
+624
View File
@@ -0,0 +1,624 @@
// Copyright (c) 2001 Octavian Procopiuc
//
// File: bte_coll_base.h
// Authors: Octavian Procopiuc <tavi@cs.duke.edu>
// (using some code by Rakesh Barve)
//
// $Id: bte_coll_base.h,v 1.27 2004/08/17 16:48:06 jan Exp $
//
// BTE_collection_base class and various basic definitions.
#ifndef _BTE_COLL_BASE_H
#define _BTE_COLL_BASE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Include the registration based memory manager.
#define MM_IMP_REGISTER
#include <mm.h>
// For persist.
#include <persist.h>
// For BTE_stack_ufs
#include <bte_stack_ufs.h>
// For BTE_err.
#include <bte_err.h>
// For class tpie_stats_collection.
#include <tpie_stats_coll.h>
// BTE_COLLECTION types passed to constructors.
enum BTE_collection_type {
BTE_READ_COLLECTION = 1, // Open existing stream read only.
BTE_WRITE_COLLECTION, // Open for read/write. Create if non-existent.
BTE_WRITE_NEW_COLLECTION // Open for read/write a new collection,
// even if a nonempty file with that name exists.
};
// BTE collection status.
enum BTE_collection_status {
BTE_COLLECTION_STATUS_VALID = 0,
BTE_COLLECTION_STATUS_INVALID = 1
};
// Maximum length of the file names.
#define BTE_COLLECTION_PATH_NAME_LEN 128
// Number of bytes in the header's user_data_ field.
#define BTE_COLLECTION_USER_DATA_LEN 512
// The magic number of the files storing blocks.
// (in network byteorder, it spells "TPBC": TPie Block Collection)
#define BTE_COLLECTION_HEADER_MAGIC_NUMBER 0x54504243
// Default file name suffixes
#define BTE_COLLECTION_BLK_SUFFIX ".blk"
#define BTE_COLLECTION_STK_SUFFIX ".stk"
// Setting this to 1 causes the use of ftruncate(2) for extending
// files, which, in conjunction with mmap(2), results in more
// fragmented files and, consequently, slower I/O. See mmap(2) on
// FreeBSD for an explanation. When set to 0, lseek(2) and write(2)
// are used to extend the files. This should be set to 1 for WIN32
// (see portability.h)
#ifndef BTE_COLLECTION_USE_FTRUNCATE
#define BTE_COLLECTION_USE_FTRUNCATE 0
#endif
// The in-memory representation of the BTE_COLLECTION header.
// This data structure is read from/written to the first
// (physical) page of the blocks file.
class BTE_collection_header {
public:
// Unique header identifier. Set to BTE_COLLECTION_HEADER_MAGIC_NUMBER
unsigned int magic_number;
// Should be 1 for current version.
unsigned int version;
// The type of BTE_COLLECTION that created this header. Setting this
// field is optional and is mostly for information purposes and
// similarity with stream header. The current implementations all
// use the same file format; it's not important to differentiate
// among them, since they can all read each other's collections. If
// used, it should be set to a non-zero value (zero is reserved for
// the base class).
unsigned int type;
// The number of bytes in this structure.
TPIE_OS_SIZE_T header_length;
// The number of blocks consumed by this collection, plus 1.
TPIE_OS_OFFSET total_blocks;
// The highest bid any block of this block collection has, PLUS 1
// (always <= total_blocks).
TPIE_OS_OFFSET last_block;
// The number of valid blocks in this block collection.
TPIE_OS_OFFSET used_blocks;
// The size of a physical block on the device this stream resides.
TPIE_OS_SIZE_T os_block_size;
// Size in bytes of each logical block.
TPIE_OS_SIZE_T block_size;
// Some data to be filled by the user of the collection.
char user_data[BTE_COLLECTION_USER_DATA_LEN];
// Default constructor.
BTE_collection_header():
magic_number(BTE_COLLECTION_HEADER_MAGIC_NUMBER),
version(1),
type(0),
header_length(sizeof(BTE_collection_header)),
total_blocks(1),
last_block(1),
used_blocks(0) {
os_block_size = TPIE_OS_BLOCKSIZE();
}
};
// A base class for all implementations of block collection classes.
template <class BIDT>
class BTE_collection_base {
protected:
// Various parameters (will be stored into the file header block).
BTE_collection_header header_;
// A stack of TPIE_OS_OFFSET's.
BTE_stack_ufs<BIDT> *freeblock_stack_;
// File descriptor for the file backing the block collection.
TPIE_OS_FILE_DESCRIPTOR bcc_fd_;
char base_file_name_[BTE_COLLECTION_PATH_NAME_LEN];
TPIE_OS_SIZE_T os_block_size_;
// Persistency flag. Set during construction and using the persist()
// method.
persistence per_;
// Status of the collection. Set during construction.
BTE_collection_status status_;
// Read-only flag. Set during construction.
bool read_only_;
// Number of blocks from this collection that are currently in memory
TPIE_OS_SIZE_T in_memory_blocks_;
// File pointer position. A value of -1 signals unknown position.
TPIE_OS_OFFSET file_pointer;
// Statistics for this object.
tpie_stats_collection stats_;
// Global collection statistics.
static tpie_stats_collection gstats_;
private:
// Helper functions. We don't want them inherited.
// Initialization common to all constructors.
void shared_init(BTE_collection_type type, size_t logical_block_factor, TPIE_OS_MAPPING_FLAG mapping);
// Read header from disk.
BTE_err read_header(char *bcc_name);
// Write header to disk.
BTE_err write_header(char* bcc_name);
void remove_stack_file();
protected:
// Needs to be inlined!
BTE_err register_memory_allocation(TPIE_OS_SIZE_T sz) {
if (MM_manager.register_allocation(sz) != MM_ERROR_NO_ERROR) {
status_ = BTE_COLLECTION_STATUS_INVALID;
TP_LOG_FATAL_ID("Memory manager error in allocation.");
return BTE_ERROR_MEMORY_ERROR;
}
return BTE_ERROR_NO_ERROR;
}
// Needs to be inlined!
BTE_err register_memory_deallocation(TPIE_OS_SIZE_T sz) {
if (MM_manager.register_deallocation(sz) != MM_ERROR_NO_ERROR) {
status_ = BTE_COLLECTION_STATUS_INVALID;
TP_LOG_FATAL_ID("Memory manager error in deallocation.");
return BTE_ERROR_MEMORY_ERROR;
}
return BTE_ERROR_NO_ERROR;
}
TPIE_OS_OFFSET bid_to_file_offset(BIDT bid) const
{ return header_.os_block_size + header_.block_size * (bid-1); }
void create_stack();
// Common code for all new_block implementations. Inlined.
BTE_err new_block_getid(BIDT& bid) {
// We try getting a free bid from the stack first. If there aren't
// any there, we will try to get one after last_block; if there are
// no blocks past last_block, we will ftruncate() some more blocks
// to the tail of the BCC and then get a free bid.
BIDT *lbn;
BTE_err err;
if (header_.used_blocks < header_.last_block - 1) {
tp_assert(freeblock_stack_ != NULL,
"BTE_collection_ufs internal error: NULL stack pointer");
// TODO: this is a costly operation. improve!
TPIE_OS_OFFSET slen = freeblock_stack_->stream_len();
tp_assert(slen > 0, "BTE_collection_ufs internal error: empty stack");
if ((err = freeblock_stack_->pop(&lbn)) != BTE_ERROR_NO_ERROR)
return err;
bid = *lbn;
} else {
tp_assert(header_.last_block <= header_.total_blocks,
"BTE_collection_ufs internal error: last_block>total_blocks");
if (header_.last_block == header_.total_blocks) {
// Increase the capacity for storing blocks in the stream by
// 16 (only by 2 the first time around to be gentle with very
// small coll's).
if (header_.total_blocks == 1)
header_.total_blocks += 2;
else if (header_.total_blocks <= 161)
header_.total_blocks += 8;
else
header_.total_blocks += 64;
#if BTE_COLLECTION_USE_FTRUNCATE
if (TPIE_OS_FTRUNCATE(bcc_fd_, bid_to_file_offset(header_.total_blocks))) {
TP_LOG_FATAL_ID("Failed to truncate to the new end of file.");
//LOG_FATAL_ID(strerror(errno));
return BTE_ERROR_OS_ERROR;
}
#else
char* tbuf = new char[header_.os_block_size];
TPIE_OS_OFFSET curr_off;
if ((curr_off = TPIE_OS_LSEEK(bcc_fd_, 0, TPIE_OS_FLAG_SEEK_END)) == (TPIE_OS_OFFSET)(-1)) {
TP_LOG_FATAL_ID("Failed to seek to the end of file.");
//LOG_FATAL_ID(strerror(errno));
return BTE_ERROR_OS_ERROR;
}
while (curr_off < bid_to_file_offset(header_.total_blocks)) {
TPIE_OS_WRITE(bcc_fd_, tbuf, header_.os_block_size);
curr_off += header_.os_block_size;
}
delete [] tbuf;
file_pointer = curr_off;
#endif
}
bid = header_.last_block++;
}
return BTE_ERROR_NO_ERROR;
}
// Common code for all delete_block implementations. Inlined.
BTE_err delete_block_shared(BIDT bid) {
if (bid == header_.last_block - 1)
header_.last_block--;
else {
if (freeblock_stack_ == NULL)
create_stack();
//tp_assert(freeblock_stack_ != NULL,
// "BTE_collection_ufs internal error: NULL stack pointer");
return freeblock_stack_->push(bid);
}
return BTE_ERROR_NO_ERROR;
}
public:
typedef BIDT block_id_t;
BTE_collection_base(const char *base_name, BTE_collection_type ct,
size_t logical_block_factor, TPIE_OS_MAPPING_FLAG mapping = TPIE_OS_FLAG_USE_MAPPING_FALSE);
// Return the total number of used blocks.
TPIE_OS_OFFSET size() const { return header_.used_blocks; }
// Return the total number of blocks consumed by the block collection.
TPIE_OS_OFFSET file_size() const { return header_.total_blocks - 1; }
// Return the logical block size in bytes.
TPIE_OS_SIZE_T block_size() const { return header_.block_size; }
// Return the logical block factor.
TPIE_OS_SIZE_T block_factor() const
{ return header_.block_size / header_.os_block_size; }
// Return the status of the collection.
BTE_collection_status status() const { return status_; }
// Set the persistence flag.
void persist(persistence p) { per_ = p; }
// Inquire the persistence status.
persistence persist() const { return per_; }
const char *base_file_name() const { return base_file_name_; }
void *user_data() { return (void *) header_.user_data; }
// Local statistics (for this object).
const tpie_stats_collection& stats() const { return stats_; }
// Global statistics (for all collections).
static const tpie_stats_collection& gstats() { return gstats_; }
// Destructor.
~BTE_collection_base();
#if defined(__sun__)
static bool direct_io;
#endif
};
template<class BIDT>
tpie_stats_collection BTE_collection_base<BIDT>::gstats_;
template<class BIDT>
void BTE_collection_base<BIDT>::create_stack() {
// Fill in the stack file name.
char stack_name[BTE_COLLECTION_PATH_NAME_LEN];
strncpy((char *) stack_name, base_file_name_, BTE_COLLECTION_PATH_NAME_LEN - 4);
strcat((char *) stack_name, BTE_COLLECTION_STK_SUFFIX);
// Construct the pre-existing freeblock_stack.
freeblock_stack_ = new BTE_stack_ufs<BIDT>((char *) stack_name,
read_only_? BTE_READ_STREAM: BTE_WRITE_STREAM);
}
template<class BIDT>
void BTE_collection_base<BIDT>::remove_stack_file() {
// Fill in the stack file name.
char stack_name[BTE_COLLECTION_PATH_NAME_LEN];
strncpy((char *) stack_name, base_file_name_, BTE_COLLECTION_PATH_NAME_LEN - 4);
strcat((char *) stack_name, BTE_COLLECTION_STK_SUFFIX);
TPIE_OS_UNLINK(stack_name);
}
template<class BIDT>
BTE_collection_base<BIDT>::BTE_collection_base(const char *base_name,
BTE_collection_type type, size_t logical_block_factor, TPIE_OS_MAPPING_FLAG mapping):
header_(), freeblock_stack_(NULL) {
if (base_name == NULL) {
status_ = BTE_COLLECTION_STATUS_INVALID;
TP_LOG_FATAL_ID("NULL file name passed to constructor");
return;
}
strncpy((char*) base_file_name_, base_name, BTE_COLLECTION_PATH_NAME_LEN - 4);
// A collection with a given name is not deleted upon destruction.
per_ = PERSIST_PERSISTENT;
shared_init(type, logical_block_factor, mapping);
}
template<class BIDT>
void BTE_collection_base<BIDT>::shared_init(BTE_collection_type type,
TPIE_OS_SIZE_T logical_block_factor, TPIE_OS_MAPPING_FLAG mapping) {
read_only_ = (type == BTE_READ_COLLECTION);
status_ = BTE_COLLECTION_STATUS_VALID;
in_memory_blocks_ = 0;
file_pointer = -1;
os_block_size_ = TPIE_OS_BLOCKSIZE();
// Fill in the blocks file name.
char bcc_name[BTE_COLLECTION_PATH_NAME_LEN];
strncpy((char *) bcc_name, base_file_name_, BTE_COLLECTION_PATH_NAME_LEN - 4);
strcat((char *) bcc_name, BTE_COLLECTION_BLK_SUFFIX);
if (read_only_) {
if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(bcc_fd_ = TPIE_OS_OPEN_ORDONLY(bcc_name, mapping))) {
status_ = BTE_COLLECTION_STATUS_INVALID;
TP_LOG_FATAL_ID("open() failed to open read-only file: ");
TP_LOG_FATAL_ID(bcc_name);
return;
}
if (read_header(bcc_name) != BTE_ERROR_NO_ERROR) {
status_ = BTE_COLLECTION_STATUS_INVALID;
return;
}
// Check whether we need a stack.
if (header_.used_blocks < header_.last_block - 1) {
create_stack();
if (freeblock_stack_->status() == BTE_STREAM_STATUS_INVALID) {
status_ = BTE_COLLECTION_STATUS_INVALID;
return;
}
} else
freeblock_stack_ = NULL;
} else { // Writeable bcc.
// If a new collection, remove any existing files with the same names.
if (type == BTE_WRITE_NEW_COLLECTION) {
TPIE_OS_UNLINK(bcc_name);
remove_stack_file();
}
// Open the file for writing. First we will try to open
// it with the O_EXCL flag set. This will fail if the file
// already exists. If this is the case, we will call open()
// again without it and read in the header block.
if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(bcc_fd_ = TPIE_OS_OPEN_OEXCL(bcc_name,mapping))) {
// Try again, hoping the file already exists.
if (!TPIE_OS_IS_VALID_FILE_DESCRIPTOR(bcc_fd_ = TPIE_OS_OPEN_ORDWR(bcc_name,mapping))) {
status_ = BTE_COLLECTION_STATUS_INVALID;
TP_LOG_FATAL_ID("open() failed to open file:");
TP_LOG_FATAL_ID(bcc_name);
return;
}
if (read_header(bcc_name) != BTE_ERROR_NO_ERROR) {
status_ = BTE_COLLECTION_STATUS_INVALID;
return;
}
// Check whether we need a stack.
if (header_.used_blocks < header_.last_block - 1) {
create_stack();
if (freeblock_stack_->status() == BTE_STREAM_STATUS_INVALID) {
status_ = BTE_COLLECTION_STATUS_INVALID;
return;
}
} else
freeblock_stack_ = NULL;
} else { // The file was just created.
tp_assert(header_.magic_number == BTE_COLLECTION_HEADER_MAGIC_NUMBER, "Header magic number mismatch.");
tp_assert(header_.os_block_size == os_block_size_, "Header os_block_size mismatch.");
header_.block_size = logical_block_factor * header_.os_block_size;
if (write_header(bcc_name) != BTE_ERROR_NO_ERROR) {
status_ = BTE_COLLECTION_STATUS_INVALID;
return;
}
// No stack (yet). Will be created by delete if needed.
freeblock_stack_ = NULL;
gstats_.record(COLLECTION_CREATE);
stats_.record(COLLECTION_CREATE);
}
}
#if defined(__sun__)
if (direct_io)
directio(bcc_fd_, DIRECTIO_ON);
else
directio(bcc_fd_, DIRECTIO_OFF);
#endif
gstats_.record(COLLECTION_OPEN);
stats_.record(COLLECTION_OPEN);
}
#if defined(__sun__)
template<class BIDT>
bool BTE_collection_base<BIDT>::direct_io = false;
#endif
template<class BIDT>
BTE_err BTE_collection_base<BIDT>::read_header(char* bcc_name) {
char * tmp_buffer = new char[os_block_size_];
if (TPIE_OS_LSEEK(bcc_fd_, 0, TPIE_OS_FLAG_SEEK_SET) != 0) {
TP_LOG_FATAL_ID("Failed to lseek in file:");
TP_LOG_FATAL_ID(bcc_name);
return BTE_ERROR_IO_ERROR;
}
if (TPIE_OS_READ(bcc_fd_, (char *)tmp_buffer, os_block_size_) != (int)os_block_size_) {
TP_LOG_FATAL_ID("Failed to read() in file:");
TP_LOG_FATAL_ID(bcc_name);
return BTE_ERROR_IO_ERROR;
}
file_pointer = os_block_size_;
memcpy((void *) &header_, (const void *) tmp_buffer,
sizeof(BTE_collection_header));
delete [] tmp_buffer;
// Do some error checking on the header, such as to make sure that
// it has the correct header version, block size etc.
if (header_.magic_number != BTE_COLLECTION_HEADER_MAGIC_NUMBER ||
header_.os_block_size != os_block_size_) {
TP_LOG_FATAL_ID("Invalid header in file: ");
TP_LOG_FATAL_ID(bcc_name);
return BTE_ERROR_BAD_HEADER;
}
TPIE_OS_OFFSET lseek_retval;
// Some more error checking.
if ((lseek_retval = TPIE_OS_LSEEK(bcc_fd_, 0, TPIE_OS_FLAG_SEEK_END)) != bid_to_file_offset(header_.total_blocks)) {
TP_LOG_FATAL_ID("File length mismatch for:");
TP_LOG_FATAL_ID(bcc_name);
TP_LOG_FATAL("\tReturn value of seek (to end): ");
TP_LOG_FATAL(lseek_retval);
TP_LOG_FATAL("\n\tReturn value of bid_to_file_offset(header_.total_blocks): ");
TP_LOG_FATAL(bid_to_file_offset(header_.total_blocks));
TP_LOG_FATAL("\n\theader_.total_blocks: ");
TP_LOG_FATAL(header_.total_blocks);
TP_LOG_FATAL("\n");
return BTE_ERROR_BAD_HEADER;
}
file_pointer = lseek_retval;
return BTE_ERROR_NO_ERROR;
}
template<class BIDT>
BTE_err BTE_collection_base<BIDT>::write_header(char *bcc_name) {
char * tmp_buffer = new char[os_block_size_];
memcpy((void *) tmp_buffer, (const void *) &header_,
sizeof(BTE_collection_header));
if (TPIE_OS_LSEEK(bcc_fd_, 0, TPIE_OS_FLAG_SEEK_SET) != 0) {
TP_LOG_FATAL_ID("Failed to lseek() in file:");
TP_LOG_FATAL_ID(bcc_name);
return BTE_ERROR_IO_ERROR;
}
if (TPIE_OS_WRITE(bcc_fd_, tmp_buffer, os_block_size_) != (int)os_block_size_) {
TP_LOG_FATAL_ID("Failed to write() in file:");
TP_LOG_FATAL_ID(bcc_name);
return BTE_ERROR_IO_ERROR;
}
file_pointer = os_block_size_;
// TP_LOG_APP_DEBUG_ID("header_.total_blocks: ");
// TP_LOG_APP_DEBUG_ID(header_.total_blocks);
delete [] tmp_buffer;
return BTE_ERROR_NO_ERROR;
}
template<class BIDT>
BTE_collection_base<BIDT>::~BTE_collection_base() {
char bcc_name[BTE_COLLECTION_PATH_NAME_LEN];
strncpy((char *) bcc_name, base_file_name_,
BTE_COLLECTION_PATH_NAME_LEN - 4);
strcat((char *) bcc_name, BTE_COLLECTION_BLK_SUFFIX);
// No block should be in memory at the time of destruction.
if (in_memory_blocks_) {
TP_LOG_WARNING_ID("In memory blocks when closing collection in:");
TP_LOG_WARNING_ID(base_file_name_);
}
#if defined(__sun__)
if (direct_io)
directio(bcc_fd_, DIRECTIO_OFF);
#endif
// Write the header.
if (!read_only_)
write_header(bcc_name);
// Delete the stack.
if (freeblock_stack_ != NULL) {
freeblock_stack_->persist(per_);
delete freeblock_stack_;
}
// Close the blocks file.
if (TPIE_OS_CLOSE(bcc_fd_)) {
TP_LOG_FATAL_ID("Failed to close() ");
TP_LOG_FATAL_ID(bcc_name);
return;
}
// If necessary, remove the blocks file.
if (per_ == PERSIST_DELETE) {
if (read_only_) {
TP_LOG_WARNING_ID("Read-only collection is PERSIST_DELETE");
TP_LOG_WARNING_ID(bcc_name);
return;
}
if (TPIE_OS_UNLINK(bcc_name)) {
TP_LOG_FATAL_ID("Failed to unlink() ");
TP_LOG_FATAL_ID(bcc_name);
return;
} else {
gstats_.record(COLLECTION_DELETE);
stats_.record(COLLECTION_DELETE);
}
}
gstats_.record(COLLECTION_CLOSE);
stats_.record(COLLECTION_CLOSE);
}
#endif //_BTE_COLL_BASE_H
+221
View File
@@ -0,0 +1,221 @@
//
// File: bte_coll_mmap.h (formerly bte_coll_mmb.h)
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: bte_coll_mmap.h,v 1.12 2005/01/14 18:58:32 tavi Exp $
//
// BTE_collection_mmap class definition.
//
#ifndef _BTE_COLL_MMAP_H
#define _BTE_COLL_MMAP_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get the base class.
#include <bte_coll_base.h>
// For header's type field (77 == 'M').
#define BTE_COLLECTION_MMAP_ID 77
// Define write behavior, if not already defined by the user.
// Allowed values:
// 0 (synchronous writes)
// 1 (asynchronous writes using MS_ASYNC - see msync(2))
// 2 (asynchronous bulk writes) [default]
#ifndef BTE_COLLECTION_MMAP_LAZY_WRITE
# define BTE_COLLECTION_MMAP_LAZY_WRITE 2
#endif
template<class BIDT = TPIE_BLOCK_ID_TYPE>
class BTE_collection_mmap: public BTE_collection_base<BIDT> {
protected:
using BTE_collection_base<BIDT>::header_;
using BTE_collection_base<BIDT>::freeblock_stack_;
using BTE_collection_base<BIDT>::bcc_fd_;
using BTE_collection_base<BIDT>::per_;
using BTE_collection_base<BIDT>::os_block_size_;
using BTE_collection_base<BIDT>::base_file_name_;
using BTE_collection_base<BIDT>::status_;
using BTE_collection_base<BIDT>::read_only_;
using BTE_collection_base<BIDT>::in_memory_blocks_;
using BTE_collection_base<BIDT>::file_pointer;
using BTE_collection_base<BIDT>::stats_;
using BTE_collection_base<BIDT>::gstats_;
using BTE_collection_base<BIDT>::register_memory_allocation;
using BTE_collection_base<BIDT>::register_memory_deallocation;
using BTE_collection_base<BIDT>::bid_to_file_offset;
using BTE_collection_base<BIDT>::create_stack;
using BTE_collection_base<BIDT>::new_block_getid;
using BTE_collection_base<BIDT>::delete_block_shared;
public:
// Constructor. Read and verify the header of the
// collection. Implemented in the base class.
BTE_collection_mmap(const char *base_file_name,
BTE_collection_type type = BTE_WRITE_COLLECTION,
size_t logical_block_factor = 1):
BTE_collection_base<BIDT>(base_file_name, type, logical_block_factor, TPIE_OS_FLAG_USE_MAPPING_TRUE) {
header_.type = BTE_COLLECTION_MMAP_ID;
}
// Allocate a new block in block collection and then map that block
// into memory, allocating and returning an appropriately
// initialized Block. Main memory usage increases.
BTE_err new_block(BIDT &bid, void * &place) {
BTE_err err;
// Get a block id.
if ((err = new_block_getid(bid)) != BTE_ERROR_NO_ERROR)
return err;
// We have a bid, so we can call the get_block routine.
if ((err = get_block_internals(bid, place)) != BTE_ERROR_NO_ERROR)
return err;
header_.used_blocks++;
stats_.record(BLOCK_NEW);
gstats_.record(BLOCK_NEW);
return BTE_ERROR_NO_ERROR;
}
// Delete a previously created, currently mapped-in BLOCK. This causes the
// number of free blocks in the collection to increase by 1, the bid is
// entered into the stdio_stack. NOTE that it is the onus of the user of
// this class to ensure that the bid of this placeholder is correct. No
// check is made if the bid is an invalid or previously unallocated bid,
// which will introduce erroneous entries in the stdio_stack of free
// blocks. Main memory usage goes down.
BTE_err delete_block(BIDT bid, void * place) {
BTE_err err;
if ((err = put_block_internals(bid, place, 1)) != BTE_ERROR_NO_ERROR)
return err;
if ((err = delete_block_shared(bid)) != BTE_ERROR_NO_ERROR)
return err;
header_.used_blocks--;
stats_.record(BLOCK_DELETE);
gstats_.record(BLOCK_DELETE);
return BTE_ERROR_NO_ERROR;
}
// Map in the block with the indicated bid and allocate and initialize a
// corresponding placeholder. NOTE once more that it is the user's onus
// to ensure that the bid requested corresponds to a valid block and so
// on; no checks made here to ensure that that is indeed the case. Main
// memory usage increases.
BTE_err get_block(BIDT bid, void * &place) {
BTE_err err;
if ((err = get_block_internals(bid, place)) != BTE_ERROR_NO_ERROR)
return err;
stats_.record(BLOCK_GET);
gstats_.record(BLOCK_GET);
return BTE_ERROR_NO_ERROR;
}
// Unmap a currently mapped in block. NOTE once more that it is the user's
// onus to ensure that the bid is correct and so on; no checks made here
// to ensure that that is indeed the case. Main memory usage decreases.
BTE_err put_block(BIDT bid, void * place, char dirty = 1) {
BTE_err err;
if ((err = put_block_internals(bid, place, dirty)) != BTE_ERROR_NO_ERROR)
return err;
stats_.record(BLOCK_PUT);
gstats_.record(BLOCK_PUT);
return BTE_ERROR_NO_ERROR;
}
// Synchronize the in-memory block with the on-disk block.
BTE_err sync_block(BIDT bid, void* place, char dirty = 1);
protected:
BTE_err get_block_internals(BIDT bid, void *&place);
BTE_err put_block_internals(BIDT bid, void* place, char dirty);
};
template<class BIDT>
BTE_err BTE_collection_mmap<BIDT>::get_block_internals(BIDT bid, void * &place) {
place = TPIE_OS_MMAP(NULL, header_.block_size,
read_only_ ? TPIE_OS_FLAG_PROT_READ :
TPIE_OS_FLAG_PROT_READ | TPIE_OS_FLAG_PROT_WRITE,
#ifdef SYSTYPE_BSD
MAP_FILE | MAP_VARIABLE | MAP_NOSYNC |
#endif
TPIE_OS_FLAG_MAP_SHARED, bcc_fd_, bid_to_file_offset(bid));
if (place == (void *)(-1)) {
TP_LOG_FATAL_ID("mmap() failed to map in a block from file.");
TP_LOG_FATAL_ID(strerror(errno));
return BTE_ERROR_MEMORY_ERROR;
}
// madvise(place, header_.block_size, MADV_RANDOM);
// Register the memory allocation since mmapped memory is
// not accounted for otherwise.
register_memory_allocation(header_.block_size);
in_memory_blocks_++;
return BTE_ERROR_NO_ERROR;
}
template<class BIDT>
BTE_err BTE_collection_mmap<BIDT>::put_block_internals(BIDT bid, void* place, char dirty) {
// The dirty parameter is not used in this implemetation.
if ((bid <= 0) || (bid >= header_.last_block)) {
TP_LOG_FATAL_ID("Incorrect bid in placeholder.");
return BTE_ERROR_INVALID_PLACEHOLDER;
}
#if (BTE_COLLECTION_MMAP_LAZY_WRITE < 2)
if (!read_only_) {
if (TPIE_OS_MSYNC((char*)place, header_.block_size,
# if (BTE_COLLECTION_MMAP_LAZY_WRITE == 1)
TPIE_OS_FLAG_MS_ASYNC
# else
TPIE_OS_FLAG_MS_SYNC
# endif
) == -1) {
TP_LOG_FATAL_ID("Failed to msync() block to file.");
TP_LOG_FATAL_ID(strerror(errno));
return BTE_ERROR_IO_ERROR;
}
}
#endif
if (TPIE_OS_MUNMAP((char*)place, header_.block_size) == -1) {
TP_LOG_FATAL_ID("Failed to unmap() block of file.");
TP_LOG_FATAL_ID(strerror(errno));
return BTE_ERROR_IO_ERROR;
}
register_memory_deallocation(header_.block_size);
in_memory_blocks_--;
return BTE_ERROR_NO_ERROR;
}
template<class BIDT>
BTE_err BTE_collection_mmap<BIDT>::sync_block(BIDT bid, void* place, char dirty) {
if ((bid <= 0) || (bid >= header_.last_block)) {
TP_LOG_FATAL_ID("Incorrect bid in placeholder.");
return BTE_ERROR_INVALID_PLACEHOLDER;
}
if (!read_only_) {
if (TPIE_OS_MSYNC((char*)place, header_.block_size, TPIE_OS_FLAG_MS_SYNC)) {
TP_LOG_FATAL_ID("Failed to msync() block to file.");
TP_LOG_FATAL_ID(strerror(errno));
return BTE_ERROR_IO_ERROR;
}
}
stats_.record(BLOCK_SYNC);
gstats_.record(BLOCK_SYNC);
return BTE_ERROR_NO_ERROR;
}
#endif //_BTE_COLL_MMAP_H
+299
View File
@@ -0,0 +1,299 @@
//
// File: bte_coll_ufs.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: bte_coll_ufs.h,v 1.12 2005/01/14 18:58:32 tavi Exp $
//
// BTE_collection_ufs class definition.
//
#ifndef _BTE_COLL_UFS_H
#define _BTE_COLL_UFS_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get the base class.
#include <bte_coll_base.h>
// For header's type field (85 == 'U').
#define BTE_COLLECTION_UFS_ID 85
template<class BIDT = TPIE_BLOCK_ID_TYPE>
class BTE_collection_ufs: public BTE_collection_base<BIDT> {
protected:
using BTE_collection_base<BIDT>::header_;
using BTE_collection_base<BIDT>::freeblock_stack_;
using BTE_collection_base<BIDT>::bcc_fd_;
using BTE_collection_base<BIDT>::per_;
using BTE_collection_base<BIDT>::os_block_size_;
using BTE_collection_base<BIDT>::base_file_name_;
using BTE_collection_base<BIDT>::status_;
using BTE_collection_base<BIDT>::read_only_;
using BTE_collection_base<BIDT>::in_memory_blocks_;
using BTE_collection_base<BIDT>::file_pointer;
using BTE_collection_base<BIDT>::stats_;
using BTE_collection_base<BIDT>::gstats_;
using BTE_collection_base<BIDT>::register_memory_allocation;
using BTE_collection_base<BIDT>::register_memory_deallocation;
using BTE_collection_base<BIDT>::bid_to_file_offset;
using BTE_collection_base<BIDT>::create_stack;
using BTE_collection_base<BIDT>::new_block_getid;
using BTE_collection_base<BIDT>::delete_block_shared;
public:
// Constructors.
BTE_collection_ufs(const char *base_file_name,
BTE_collection_type type = BTE_WRITE_COLLECTION,
size_t logical_block_factor = 1):
BTE_collection_base<BIDT>(base_file_name, type, logical_block_factor) {
header_.type = BTE_COLLECTION_UFS_ID;
}
// Allocate a new block in block collection and then read that block into
// memory, allocating and returning an appropriately initialized
// Block. Main memory usage increases.
BTE_err new_block(BIDT &bid, void * &place) {
BTE_err err;
// Get a block id.
if ((err = new_block_getid(bid)) != BTE_ERROR_NO_ERROR)
return err;
// We have a bid, so we can call the get_block routine.
if ((err = new_block_internals(bid, place)) != BTE_ERROR_NO_ERROR)
return err;
header_.used_blocks++;
stats_.record(BLOCK_NEW);
gstats_.record(BLOCK_NEW);
return BTE_ERROR_NO_ERROR;
}
// Delete a previously created, currently in-memory BLOCK. This causes
// the number of free blocks in the collection to increase by 1, the bid
// is entered into the stdio_stack. NOTE that it is the onus of the user
// of this class to ensure that the bid of this placeholder is
// correct. No check is made if the bid is an invalid or previously
// unallocated bid, which will introduce erroneous entries in the
// stdio_stack of free blocks. Main memory usage goes down.
BTE_err delete_block(BIDT bid, void * place) {
BTE_err err;
if ((err = put_block_internals(bid, place, 1)) != BTE_ERROR_NO_ERROR)
return err;
if ((err = delete_block_shared(bid)) != BTE_ERROR_NO_ERROR)
return err;
header_.used_blocks--;
stats_.record(BLOCK_DELETE);
gstats_.record(BLOCK_DELETE);
return BTE_ERROR_NO_ERROR;
}
// Read the block with the indicated bid and allocate and initialize a
// corresponding placeholder. NOTE once more that it is the user's onus
// to ensure that the bid requested corresponds to a valid block and so
// on; no checks made here to ensure that that is indeed the case. Main
// memory usage increases.
BTE_err get_block(BIDT bid, void * &place) {
BTE_err err;
if ((err = get_block_internals(bid, place)) != BTE_ERROR_NO_ERROR)
return err;
stats_.record(BLOCK_GET);
gstats_.record(BLOCK_GET);
return BTE_ERROR_NO_ERROR;
}
// Write a currently in-memory block. NOTE once more that it is the
// user's onus to ensure that the bid is correct and so on; no checks
// made here to ensure that that is indeed the case. Main memory usage
// decreases.
BTE_err put_block(BIDT bid, void * place, char dirty = 1) {
BTE_err err;
if ((err = put_block_internals(bid, place, dirty)) != BTE_ERROR_NO_ERROR)
return err;
stats_.record(BLOCK_PUT);
gstats_.record(BLOCK_PUT);
return BTE_ERROR_NO_ERROR;
}
// Synchronize the in-memory block with the on-disk block.
BTE_err sync_block(BIDT bid, void* place, char dirty = 1);
protected:
BTE_err new_block_internals(BIDT bid, void *&place);
// BTE_err new_block_getid_specific(BIDT& bid);
BTE_err get_block_internals(BIDT bid, void *&place);
BTE_err put_block_internals(BIDT bid, void* place, char dirty);
};
template<class BIDT>
BTE_err BTE_collection_ufs<BIDT>::new_block_internals(BIDT bid, void* &place) {
if ((place = new char[header_.block_size]) == NULL) {
TP_LOG_FATAL_ID("new() failed to alloc space for a block from file.");
return BTE_ERROR_MEMORY_ERROR;
}
in_memory_blocks_++;
return BTE_ERROR_NO_ERROR;
}
#if 0
template<class BIDT>
BTE_err BTE_collection_ufs<BIDT>::new_block_getid_specific(BIDT& bid) {
BIDT *lbn;
BTE_err err;
if (header_.used_blocks < header_.last_block - 1) {
tp_assert(freeblock_stack_ != NULL,
"BTE_collection_ufs internal error: NULL stack pointer");
size_t slen = freeblock_stack_->stream_len();
tp_assert(slen > 0, "BTE_collection_ufs internal error: empty stack");
if ((err = freeblock_stack_->pop(&lbn)) != BTE_ERROR_NO_ERROR)
return err;
bid = *lbn;
} else {
tp_assert(header_.last_block <= header_.total_blocks,
"BTE_collection_ufs internal error: last_block>total_blocks");
if (header_.last_block == header_.total_blocks) {
// Increase the capacity for storing blocks in the stream by
// 16 (only by 1 the first time around to be gentle with very
// small coll's).
if (header_.total_blocks == 1)
header_.total_blocks += 2;
else if (header_.total_blocks <= 161)
header_.total_blocks += 8;
else
header_.total_blocks += 64;
#define USE_FTRUNCATE_FOR_UFS 1
#if USE_FTRUNCATE_FOR_UFS
if (TPIE_OS_FTRUNCATE(bcc_fd_, bid_to_file_offset(header_.total_blocks))) {
TP_LOG_FATAL_ID("Failed to ftruncate() to the new end of file.");
TP_LOG_FATAL_ID(strerror(errno));
return BTE_ERROR_OS_ERROR;
}
#else
TPIE_OS_OFFSET curr_off;
char* tbuf = new char[header_.os_block_size];
if ((curr_off = TPIE_OS_LSEEK(bcc_fd_, 0, TPIE_OS_FLAG_SEEK_END)) == (TPIE_OS_OFFSET)-1) {
TP_LOG_FATAL_ID("Failed to lseek() to the end of file.");
TP_LOG_FATAL_ID(strerror(errno));
return BTE_ERROR_OS_ERROR;
}
while (curr_off < bid_to_file_offset(header_.total_blocks)) {
TPIE_OS_WRITE(bcc_fd_, tbuf, header_.os_block_size);
curr_off += header_.os_block_size;
}
file_pointer = curr_off;
delete [] tbuf;
#endif
}
bid = header_.last_block++;
}
return BTE_ERROR_NO_ERROR;
}
#endif
template<class BIDT>
BTE_err BTE_collection_ufs<BIDT>::get_block_internals(BIDT bid, void * &place) {
if ((place = new char[header_.block_size]) == NULL) {
TP_LOG_FATAL_ID("new() failed to alloc space for a block from file.");
return BTE_ERROR_MEMORY_ERROR;
}
if (file_pointer != bid_to_file_offset(bid)) {
if (TPIE_OS_LSEEK(bcc_fd_, bid_to_file_offset(bid), TPIE_OS_FLAG_SEEK_SET) !=
bid_to_file_offset(bid)) {
TP_LOG_FATAL_ID("lseek failed in file.");
return BTE_ERROR_IO_ERROR;
}
}
if (TPIE_OS_READ(bcc_fd_, (char *) place, header_.block_size) !=
(TPIE_OS_SSIZE_T)header_.block_size) {
TP_LOG_FATAL_ID("Failed to read() from file.");
return BTE_ERROR_IO_ERROR;
}
file_pointer = bid_to_file_offset(bid) + header_.block_size;
in_memory_blocks_++;
return BTE_ERROR_NO_ERROR;
}
template<class BIDT>
BTE_err BTE_collection_ufs<BIDT>::put_block_internals(BIDT bid, void * place, char dirty) {
if ((bid < 0) || (bid >= header_.last_block)) {
TP_LOG_FATAL_ID("Incorrect bid in placeholder.");
return BTE_ERROR_INVALID_PLACEHOLDER;
}
if (place == NULL) {
TP_LOG_FATAL_ID("Null block ptr field in placeholder.");
return BTE_ERROR_INVALID_PLACEHOLDER;
}
// if (place->dirty)
if (!read_only_) {
if (file_pointer != bid_to_file_offset(bid)) {
if (TPIE_OS_LSEEK(bcc_fd_, bid_to_file_offset(bid), TPIE_OS_FLAG_SEEK_SET)
!= bid_to_file_offset(bid)) {
TP_LOG_FATAL_ID("Failed to lseek() in file.");
return BTE_ERROR_IO_ERROR;
}
}
if (TPIE_OS_WRITE(bcc_fd_, place, header_.block_size) != (TPIE_OS_SSIZE_T)header_.block_size) {
TP_LOG_FATAL_ID("Failed to write() block to file.");
return BTE_ERROR_IO_ERROR;
}
file_pointer = bid_to_file_offset(bid) + header_.block_size;
}
delete [] (char *) place;
in_memory_blocks_--;
return BTE_ERROR_NO_ERROR;
}
template<class BIDT>
BTE_err BTE_collection_ufs<BIDT>::sync_block(BIDT bid, void* place, char dirty) {
if ((bid < 0) || (bid >= header_.last_block)) {
TP_LOG_FATAL_ID("Incorrect bid in placeholder.");
return BTE_ERROR_INVALID_PLACEHOLDER;
}
if (place == NULL) {
TP_LOG_FATAL_ID("Null block pointer.");
return BTE_ERROR_INVALID_PLACEHOLDER;
}
if (!read_only_) {
if (TPIE_OS_LSEEK(bcc_fd_, bid_to_file_offset(bid), TPIE_OS_FLAG_SEEK_SET)
!= bid_to_file_offset(bid)) {
TP_LOG_FATAL_ID("Failed to lseek() in file.");
return BTE_ERROR_IO_ERROR;
}
if (TPIE_OS_WRITE(bcc_fd_, place, header_.block_size) != (TPIE_OS_SSIZE_T)header_.block_size) {
TP_LOG_FATAL_ID("Failed to write() block to file.");
return BTE_ERROR_IO_ERROR;
}
file_pointer = bid_to_file_offset(bid) + header_.block_size;
}
stats_.record(BLOCK_SYNC);
gstats_.record(BLOCK_SYNC);
return BTE_ERROR_NO_ERROR;
}
#endif // _BTE_COLL_UFS_H
+37
View File
@@ -0,0 +1,37 @@
//
// File: bte_err.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
// (from Darren's bte_base_stream.h)
// Created: 12/29/01
// $Id: bte_err.h,v 1.2 2003/04/17 14:56:26 jan Exp $
//
// BTE error codes, moved here from bte_base_stream.h
//
#ifndef _BTE_ERR_H
#define _BTE_ERR_H
// Get definitions for working with Unix and Windows
#include <portability.h>
//
// BTE error codes are returned using the BTE_err type.
//
enum BTE_err {
BTE_ERROR_NO_ERROR = 0,
BTE_ERROR_IO_ERROR,
BTE_ERROR_END_OF_STREAM,
BTE_ERROR_READ_ONLY,
BTE_ERROR_OS_ERROR,
BTE_ERROR_BASE_METHOD,
BTE_ERROR_MEMORY_ERROR,
BTE_ERROR_PERMISSION_DENIED,
BTE_ERROR_OFFSET_OUT_OF_RANGE,
BTE_ERROR_OUT_OF_SPACE,
BTE_ERROR_STREAM_IS_SUBSTREAM,
BTE_ERROR_WRITE_ONLY,
BTE_ERROR_BAD_HEADER,
BTE_ERROR_INVALID_PLACEHOLDER
};
#endif // _BTE_ERR_H
+91
View File
@@ -0,0 +1,91 @@
//
// File: bte_stack_ufs.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
// Created: 09/15/03
//
// A stack implemented using BTE_stream_ufs. It is used by
// BTE_collection_base to implement deletions.
//
// $Id: bte_stack_ufs.h,v 1.2 2005/01/14 18:47:22 tavi Exp $
//
#ifndef _BTE_STACK_UFS_H
#define _BTE_STACK_UFS_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <bte_stream_ufs.h>
template<class T>
class BTE_stack_ufs : public BTE_stream_ufs<T> {
public:
using BTE_stream_ufs<T>::stream_len;
using BTE_stream_ufs<T>::seek;
using BTE_stream_ufs<T>::truncate;
// Construct a new stack with the given name and access type.
BTE_stack_ufs(char *path, BTE_stream_type type = BTE_WRITE_STREAM);
// Destroy this object.
~BTE_stack_ufs(void);
// Push an element on top of the stack.
BTE_err push(const T &t);
// Pop an element from the top of the stack.
BTE_err pop(T **t);
};
template<class T>
BTE_stack_ufs<T>::BTE_stack_ufs(char *path,
BTE_stream_type type) :
BTE_stream_ufs<T>(path, type, 1)
{
}
template<class T>
BTE_stack_ufs<T>::~BTE_stack_ufs(void)
{
}
template<class T>
BTE_err BTE_stack_ufs<T>::push(const T &t)
{
BTE_err ae;
TPIE_OS_OFFSET slen;
ae = truncate((slen = stream_len())+1);
if (ae != BTE_ERROR_NO_ERROR) {
return ae;
}
ae = seek(slen);
if (ae != BTE_ERROR_NO_ERROR) {
return ae;
}
return write_item(t);
}
template<class T>
BTE_err BTE_stack_ufs<T>::pop(T **t)
{
BTE_err ae;
TPIE_OS_OFFSET slen;
slen = stream_len();
ae = seek(slen-1);
if (ae != BTE_ERROR_NO_ERROR) {
return ae;
}
ae = read_item(t);
if (ae != BTE_ERROR_NO_ERROR) {
return ae;
}
return truncate(slen-1);
}
#endif // _BTE_STACK_UFS_H
+117
View File
@@ -0,0 +1,117 @@
//
// File: bte_stream.h (formerly bte.h)
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/9/94
//
// $Id: bte_stream.h,v 1.3 2003/04/17 14:59:16 jan Exp $
//
#ifndef _BTE_STREAM_H
#define _BTE_STREAM_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#ifndef BTE_VIRTUAL_BASE
# define BTE_VIRTUAL_BASE 0
#endif
// Get the base class, enums, etc...
#include <bte_stream_base.h>
#ifdef BTE_IMP_UFS
// TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_UFS
# define BTE_STREAM_IMP_UFS
#endif
#ifdef BTE_IMP_MMB
// TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_MMAP
# define BTE_STREAM_IMP_MMAP
#endif
#ifdef BTE_IMP_STDIO
// TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_STDIO
# define BTE_STREAM_IMP_STDIO
#endif
#ifdef BTE_IMP_USER_DEFINED
// TPIE_OS_UNIX_ONLY_WARNING_USE_BTE_STREAM_IMP_USER_DEFINED
# define BTE_STREAM_IMP_USER_DEFINED
#endif
// The number of implementations to be defined.
#define _BTE_STREAM_IMP_COUNT (defined(BTE_STREAM_IMP_USER_DEFINED) + \
defined(BTE_STREAM_IMP_STDIO) + \
defined(BTE_STREAM_IMP_MMAP) + \
defined(BTE_STREAM_IMP_UFS) )
// Multiple implementations are allowed to coexist, with some
// restrictions.
// If the including module did not explicitly ask for multiple
// implementations but requested more than one implementation, issue a
// warning.
#ifndef BTE_STREAM_IMP_MULTI_IMP
# if (_BTE_STREAM_IMP_COUNT > 1)
// TPIE_OS_UNIX_ONLY_WARNING_MULTIPLE_BTE_STREAM_IMP_DEFINED
# define BTE_STREAM_IMP_MULTI_IMP
# endif // (_BTE_STREAM_IMP_COUNT > 1)
#endif // BTE_STREAM_IMP_MULTI_IMP
// Make sure at least one implementation was chosen. If none was, then
// choose one by default, but warn the user.
#if (_BTE_STREAM_IMP_COUNT < 1)
// TPIE_OS_UNIX_ONLY_WARNING_NO_IMPLEMENTATION_USING_BTE_STREAM_IMP_UFS
# define BTE_STREAM_IMP_STDIO
#endif // (_BTE_STREAM_IMP_COUNT < 1)
// Now include the definitions of each implementation
// that will be used.
#ifdef BTE_STREAM_IMP_MULTI_IMP
// If we have multiple implem., set BTE_STREAM to be the base class.
# define BTE_STREAM BTE_stream_base
#endif
// User defined implementation.
#if defined(BTE_STREAM_IMP_USER_DEFINED)
// Do nothing. The user will provide a definition of BTE_STREAM.
#endif
// stdio implementation.
#if defined(BTE_STREAM_IMP_STDIO)
# include <bte_stream_stdio.h>
// If this is the only implementation, then make it easier to get to.
# ifndef BTE_STREAM_IMP_MULTI_IMP
#ifdef BTE_STREAM
#undef BTE_STREAM
#endif
# define BTE_STREAM BTE_stream_stdio
# endif
#endif
// mmap implementation.
#if defined(BTE_STREAM_IMP_MMAP)
# include <bte_stream_mmap.h>
// If this is the only implementation, then make it easier to get to.
# ifndef BTE_STREAM_IMP_MULTI_IMP
#ifdef BTE_STREAM
#undef BTE_STREAM
#endif
# define BTE_STREAM BTE_stream_mmap
# endif
#endif
// ufs implementation.
#if defined(BTE_STREAM_IMP_UFS)
# include <bte_stream_ufs.h>
// If this is the only implementation, then make it easier to get to.
# ifndef BTE_STREAM_IMP_MULTI_IMP
#ifdef BTE_STREAM
#undef BTE_STREAM
#endif
# define BTE_STREAM BTE_stream_ufs
# endif
#endif
#endif // _BTE_STREAM_H
+21
View File
@@ -0,0 +1,21 @@
//
// File: bte_stream_base.cpp
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
// (using some code by Darren Erik Vengroff)
// Created: 01/08/02
//
#include "lib_config.h"
#include <versions.h>
VERSION(bte_stream_base_cpp,"$Id: bte_stream_base.cpp,v 1.3 2003/04/23 07:32:15 tavi Exp $");
#include <bte_stream_base.h>
static unsigned long get_remaining_streams() {
TPIE_OS_SET_LIMITS_BODY;
}
tpie_stats_stream BTE_stream_base_generic::gstats_;
int BTE_stream_base_generic::remaining_streams = get_remaining_streams();
+265
View File
@@ -0,0 +1,265 @@
//
// File: bte_stream_base.h (formerly bte_base_stream.h)
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/11/94
//
// $Id: bte_stream_base.h,v 1.9 2005/01/26 20:12:53 tavi Exp $
//
#ifndef _BTE_STREAM_BASE_H
#define _BTE_STREAM_BASE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <persist.h>
// Get the BTE error codes.
#include <bte_err.h>
// Get statistics definitions.
#include <tpie_stats_stream.h>
// Include the registration based memory manager.
#define MM_IMP_REGISTER
#include <mm.h>
// Inline commonly called functions.
//#define B_INLINE
#define B_INLINE inline
// Max length of a stream file name.
#define BTE_STREAM_PATH_NAME_LEN 128
// The magic number of the file storing the stream.
// (in network byteorder, it spells "TPST": TPie STream)
#define BTE_STREAM_HEADER_MAGIC_NUMBER 0x54505354
// BTE stream types passed to constructors.
enum BTE_stream_type {
BTE_READ_STREAM = 1, // Open existing stream for reading.
BTE_WRITE_STREAM, // Open for read/writing. Create if non-existent.
BTE_APPEND_STREAM, // Open for writing at end. Create if needed.
BTE_WRITEONLY_STREAM // Open only for writing (allows mmb optimization)
// (must be sequential write through whole file)
};
// BTE stream status.
enum BTE_stream_status {
BTE_STREAM_STATUS_NO_STATUS = 0,
BTE_STREAM_STATUS_INVALID = 1,
BTE_STREAM_STATUS_EOS_ON_NEXT_CALL,
BTE_STREAM_STATUS_END_OF_STREAM
};
// BTE stream header info.
class BTE_stream_header {
public:
// Unique header identifier. Set to BTE_STREAM_HEADER_MAGIC_NUMBER.
unsigned int magic_number;
// Should be 2 for current version (version 1 has been deprecated).
unsigned int version;
// The type of BTE_STREAM that created this header. Not all types of
// BTE's are readable by all BTE implementations. For example,
// BTE_STREAM_STDIO streams are not readable by either
// BTE_STREAM_UFS or BTE_STREAM_MMAP implementations. The value 0 is
// reserved for the base class. Use numbers bigger than 0 for the
// various implementations.
unsigned int type;
// The number of bytes in this structure.
TPIE_OS_SIZE_T header_length;
// The size of each item in the stream.
TPIE_OS_SIZE_T item_size;
// The size of a physical block on the device this stream resides.
TPIE_OS_SIZE_T os_block_size;
// Size in bytes of each logical block, if applicable.
TPIE_OS_SIZE_T block_size;
// For all intents and purposes, the length of the stream in number
// of items.
TPIE_OS_OFFSET item_logical_eof;
};
// A base class for the base class :). The role of this class is to
// provide global variables, accessible by all streams, regardless of
// template.
class BTE_stream_base_generic {
protected:
static tpie_stats_stream gstats_;
static int remaining_streams;
public:
// The number of globally available streams.
static int available_streams() { return remaining_streams; }
// The global stats.
static const tpie_stats_stream& gstats() { return gstats_; }
};
// An abstract class template which implements a single stream of objects
// of type T within the BTE. This is the superclass of all actual
// implementations of streams of T within the BTE (e.g. mmap() streams,
// UN*X file system streams, and kernel streams).
template<class T> class BTE_stream_base: public BTE_stream_base_generic {
protected:
using BTE_stream_base_generic::remaining_streams;
using BTE_stream_base_generic::gstats_;
// The persistence status of this stream.
persistence per;
// The status (integrity) of this stream.
BTE_stream_status status_;
// How deeply is this stream nested.
unsigned int substream_level;
// Non-zero if this stream was opened for reading only.
int r_only;
// Statistics for this stream only.
tpie_stats_stream stats_;
// Check the given header for reasonable values.
int check_header(BTE_stream_header* ph);
// Initialize the header with as much information as is known here.
void init_header(BTE_stream_header* ph);
inline BTE_err register_memory_allocation (TPIE_OS_SIZE_T sz);
inline BTE_err register_memory_deallocation (TPIE_OS_SIZE_T sz);
public:
BTE_stream_base() {};
// Tell the stream whether to leave its data on the disk or not
// when it is destructed.
void persist (persistence p) { per = p; }
// Inquire the persistence status of this BTE stream.
persistence persist() const { return per; }
// Return true if a read-only stream.
bool read_only () const { return (r_only != 0); }
// Inquire the status.
BTE_stream_status status() const { return status_; }
// Inquire the OS block size.
TPIE_OS_SIZE_T os_block_size () const;
const tpie_stats_stream& stats() const { return stats_; }
#if BTE_VIRTUAL_BASE
// A virtual psuedo-constructor for substreams.
virtual BTE_err new_substream(BTE_stream_type st,
TPIE_OS_OFFSET sub_begin, TPIE_OS_OFFSET sub_end,
BTE_stream_base<T> **sub_stream) = 0;
virtual B_INLINE BTE_err read_item(T **elt) = 0;
virtual B_INLINE BTE_err write_item(const T &elt) = 0;
// Query memory usage
virtual BTE_err main_memory_usage(TPIE_OS_SIZE_T *usage,
MM_stream_usage usage_type) = 0;
virtual TPIE_OS_OFFSET stream_len(void) = 0;
virtual BTE_err name(char **stream_name) = 0;
virtual BTE_err seek(TPIE_OS_OFFSET offset) = 0;
virtual BTE_err truncate(TPIE_OS_OFFSET offset) = 0;
virtual ~BTE_stream_base(void) {};
virtual int available_streams(void) = 0;
virtual TPIE_OS_OFFSET chunk_size(void) = 0;
#endif // BTE_VIRTUAL_BASE
};
template<class T>
int BTE_stream_base<T>::check_header(BTE_stream_header* ph) {
if (ph == NULL) {
TP_LOG_FATAL_ID ("Could not map header.");
return -1;
}
if (ph->magic_number != BTE_STREAM_HEADER_MAGIC_NUMBER) {
TP_LOG_FATAL_ID ("header: magic number mismatch (expected/obtained):");
TP_LOG_FATAL_ID (BTE_STREAM_HEADER_MAGIC_NUMBER);
TP_LOG_FATAL_ID (ph->magic_number);
return -1;
}
if (ph->header_length != sizeof (*ph)) {
TP_LOG_FATAL_ID ("header: incorrect header length; (expected/obtained):");
TP_LOG_FATAL_ID (sizeof (BTE_stream_header));
TP_LOG_FATAL_ID (ph->header_length);
TP_LOG_FATAL_ID ("This could be due to a stream written without 64-bit support.");
return -1;
}
if (ph->version != 2) {
TP_LOG_FATAL_ID ("header: incorrect version (expected/obtained):");
TP_LOG_FATAL_ID (2);
TP_LOG_FATAL_ID (ph->version);
return -1;
}
if (ph->type == 0) {
TP_LOG_FATAL_ID ("header: type is 0 (reserved for base class).");
return -1;
}
if (ph->item_size != sizeof (T)) {
TP_LOG_FATAL_ID ("header: incorrect item size (expected/obtained):");
TP_LOG_FATAL_ID (sizeof(T));
TP_LOG_FATAL_ID ((TPIE_OS_LONGLONG)ph->item_size);
return -1;
}
if (ph->os_block_size != os_block_size()) {
TP_LOG_FATAL_ID ("header: incorrect OS block size (expected/obtained):");
TP_LOG_FATAL_ID ((TPIE_OS_LONGLONG)os_block_size());
TP_LOG_FATAL_ID ((TPIE_OS_LONGLONG)ph->os_block_size);
return -1;
}
return 0;
}
template<class T>
void BTE_stream_base<T>::init_header (BTE_stream_header* ph) {
tp_assert(ph != NULL, "NULL header pointer");
ph->magic_number = BTE_STREAM_HEADER_MAGIC_NUMBER;
ph->version = 2;
ph->type = 0; // Not known here.
ph->header_length = sizeof(*ph);
ph->item_size = sizeof(T);
ph->os_block_size = os_block_size();
ph->block_size = 0; // Not known here.
ph->item_logical_eof = 0;
}
template<class T>
BTE_err BTE_stream_base<T>::register_memory_allocation (TPIE_OS_SIZE_T sz) {
if (MM_manager.register_allocation(sz) != MM_ERROR_NO_ERROR) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_FATAL_ID("Memory manager error in allocation.");
return BTE_ERROR_MEMORY_ERROR;
}
return BTE_ERROR_NO_ERROR;
}
template<class T>
BTE_err BTE_stream_base<T>::register_memory_deallocation (TPIE_OS_SIZE_T sz) {
if (MM_manager.register_deallocation (sz) != MM_ERROR_NO_ERROR) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_FATAL_ID("Memory manager error in deallocation.");
return BTE_ERROR_MEMORY_ERROR;
}
return BTE_ERROR_NO_ERROR;
}
template<class T>
TPIE_OS_SIZE_T BTE_stream_base<T>::os_block_size () const {
return TPIE_OS_BLOCKSIZE();
}
#endif // _BTE_STREAM_BASE_H
+233
View File
@@ -0,0 +1,233 @@
//
// File: bte_stream_cache.h (formerly bte_cache.h)
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 9/19/94
//
// $Id: bte_stream_cache.h,v 1.4 2004/08/12 12:35:31 jan Exp $
//
// BTE streams for main memory caches.
//
#ifndef _BTE_STREAM_CACHE_H
#define _BTE_STREAM_CACHE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Include the registration based memory manager.
#define MM_IMP_REGISTER
#include <mm.h>
#include <bte_stream_base.h>
// This code makes assertions and logs errors.
#include <tpie_assert.h>
#include <tpie_log.h>
#define BTE_STREAM_CACHE_DEFAULT_MAX_LEN (1024 * 256)
#ifndef BTE_STREAM_CACHE_LINE_SIZE
#define BTE_STREAM_CACHE_LINE_SIZE 64
#endif // BTE_STREAM_CACHE_LINE_SIZE
//
// The cache stream class.
//
template <class T>
class BTE_stream_cache : public BTE_stream_base<T> {
private:
T *data;
T *current;
T *data_max;
T *data_hard_end;
unsigned int substream_level;
unsigned int valid;
unsigned int r_only;
BTE_stream_cache(void);
public:
// Constructors
BTE_stream_cache(const char *path, BTE_stream_type st, TPIE_OS_OFFSET max_len);
// A psuedo-constructor for substreams.
BTE_err new_substream(BTE_stream_type st, TPIE_OS_OFFSET sub_begin,
TPIE_OS_OFFSET sub_end, BTE_stream_base<T> **sub_stream);
// Query memory usage
BTE_err main_memory_usage(size_t *usage,
MM_stream_usage usage_type);
// Return the number of items in the stream.
TPIE_OS_OFFSET stream_len(void);
// Move to a specific position in the stream.
BTE_err seek(TPIE_OS_OFFSET offset);
// Destructor
~BTE_stream_cache(void);
BTE_err read_item(T **elt);
BTE_err write_item(const T &elt);
int read_only(void) { return r_only; };
int available_streams(void) { return -1; };
TPIE_OS_OFFSET chunk_size(void);
};
template<class T>
BTE_stream_cache<T>::BTE_stream_cache(void)
{
};
template<class T>
BTE_stream_cache<T>::BTE_stream_cache(const char *path, BTE_stream_type st,
TPIE_OS_OFFSET max_len) {
// A stream being created out of the blue must be writable, so we
// return an error if it is not.
switch (st) {
case BTE_READ_STREAM:
case BTE_APPEND_STREAM:
valid = 0;
break;
case BTE_WRITE_STREAM:
r_only = 0;
if (!max_len) {
max_len = BTE_STREAM_CACHE_DEFAULT_MAX_LEN;
}
// Use malloc() directly rather than new becasue this is
// in "secondary memory" and will not necessarily go into
// the cache.
data = (T*)malloc(max_len*sizeof(T));
if (data == NULL) {
valid = 0;
TP_LOG_FATAL_ID("Out of \"secondary memory.\"");
return;
}
current = data_max = data;
data_hard_end = data + max_len;
valid = 1;
}
};
template<class T>
BTE_err BTE_stream_cache<T>::new_substream(BTE_stream_type st, TPIE_OS_OFFSET sub_begin,
TPIE_OS_OFFSET sub_end,
BTE_stream_base<T> **sub_stream)
{
BTE_stream_cache *ss;
if (st == BTE_APPEND_STREAM) {
return BTE_ERROR_PERMISSION_DENIED;
} else {
if ((sub_begin >= data_hard_end - data) ||
(sub_end >= data_hard_end - data) ||
(sub_begin >= data_max - data) ||
(sub_end >= data_max - data) ||
(sub_end < sub_begin)) {
return BTE_ERROR_OFFSET_OUT_OF_RANGE;
}
ss = new BTE_stream_cache;
ss->r_only = (st == BTE_READ_STREAM);
ss->substream_level = substream_level + 1;
ss->current = ss->data = data + sub_begin;
ss->data_max = ss->data_hard_end = data + sub_end + 1;
*sub_stream = (BTE_stream_base<T> *)ss;
return BTE_ERROR_NO_ERROR;
}
};
template<class T>
BTE_err BTE_stream_cache<T>::main_memory_usage(size_t *usage,
MM_stream_usage usage_type)
{
switch (usage_type) {
case MM_STREAM_USAGE_CURRENT:
case MM_STREAM_USAGE_MAXIMUM:
case MM_STREAM_USAGE_SUBSTREAM:
*usage = sizeof(*this) + BTE_STREAM_CACHE_LINE_SIZE;
break;
case MM_STREAM_USAGE_BUFFER:
*usage = BTE_STREAM_CACHE_LINE_SIZE;
break;
case MM_STREAM_USAGE_OVERHEAD:
*usage = sizeof(this);
break;
}
return BTE_ERROR_NO_ERROR;
};
template<class T>
TPIE_OS_OFFSET BTE_stream_cache<T>::stream_len(void)
{
return data_max - data;
};
template<class T>
BTE_err BTE_stream_cache<T>::seek(TPIE_OS_OFFSET offset)
{
if (offset > data_hard_end - data) {
return BTE_ERROR_OFFSET_OUT_OF_RANGE;
} else {
current = data + offset;
return BTE_ERROR_NO_ERROR;
}
};
template<class T>
BTE_stream_cache<T>::~BTE_stream_cache(void)
{
if (!substream_level) {
delete data;
}
};
template<class T>
BTE_err BTE_stream_cache<T>::read_item(T **elt)
{
if (current >= data_max) {
return BTE_ERROR_END_OF_STREAM;
} else {
*elt = current++;
return BTE_ERROR_NO_ERROR;
}
};
template<class T>
BTE_err BTE_stream_cache<T>::write_item(const T &elt)
{
if (r_only) {
return BTE_ERROR_PERMISSION_DENIED;
}
if (current >= data_hard_end) {
return BTE_ERROR_OUT_OF_SPACE;
} else {
*current++ = elt;
if (current > data_max) {
data_max = current;
}
return BTE_ERROR_NO_ERROR;
}
};
template<class T>
TPIE_OS_OFFSET BTE_stream_cache<T>::chunk_size(void)
{
return BTE_STREAM_CACHE_LINE_SIZE / sizeof(T);
}
#endif // _BTE_STREAM_CACHE_H
File diff suppressed because it is too large Load Diff
+635
View File
@@ -0,0 +1,635 @@
//
// File: bte_stream_stdio.h (formerly bte_stdio.h)
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/11/94
//
// $Id: bte_stream_stdio.h,v 1.14 2005/07/07 20:36:12 adanner Exp $
//
#ifndef _BTE_STREAM_STDIO_H
#define _BTE_STREAM_STDIO_H
// For header's type field (83 == 'S').
#define BTE_STREAM_STDIO 83
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <string.h>
#include <tpie_log.h>
#include <tpie_assert.h>
#include <stddef.h>
#include <stdio.h>
#include <errno.h>
#include <bte_stream_base.h>
// File system streams are streams in a special format that is designed
// to be stored in an ordinary file in a UN*X file system. They are
// predominatly designed to be used to store streams in a persistent way.
// They may disappear in later versions as persistence becomes an integral
// part of TPIE.
//
// For simplicity, we work through the standard C I/O library (stdio).
//
// A class of BTE streams implemented using ordinary stdio
// semantics.
template < class T >
class BTE_stream_stdio: public BTE_stream_base < T > {
private:
FILE * file;
BTE_stream_header header;
size_t os_block_size_;
int os_errno; // A place to cache OS error values. It is normally
// set after each call to the OS.
char path[BTE_STREAM_PATH_NAME_LEN];
// If this stream is actually a substream, these will be set to
// indicate the portion of the file that is part of this stream.
// If the stream is the whole file, they will be set to -1.
TPIE_OS_OFFSET logical_bos;
TPIE_OS_OFFSET logical_eos;
// Offset of the current item in the file.
TPIE_OS_OFFSET f_offset;
// Offset past the last item in the file.
TPIE_OS_OFFSET f_eof;
// Read and check the header; used by constructors
int readcheck_header ();
inline TPIE_OS_OFFSET file_off_to_item_off (TPIE_OS_OFFSET file_off) const;
inline TPIE_OS_OFFSET item_off_to_file_off (TPIE_OS_OFFSET item_off) const;
protected:
using BTE_stream_base<T>::remaining_streams;
using BTE_stream_base<T>::gstats_;
using BTE_stream_base<T>::status_;
using BTE_stream_base<T>::stats_;
using BTE_stream_base<T>::substream_level;
using BTE_stream_base<T>::per;
using BTE_stream_base<T>::r_only;
public:
using BTE_stream_base<T>::os_block_size;
using BTE_stream_base<T>::check_header;
using BTE_stream_base<T>::init_header;
using BTE_stream_base<T>::register_memory_allocation;
using BTE_stream_base<T>::register_memory_deallocation;
T read_tmp;
// Constructors
BTE_stream_stdio (const char *dev_path, const BTE_stream_type st,
size_t lbf = 1);
// A psuedo-constructor for substreams.
BTE_err new_substream (BTE_stream_type st, TPIE_OS_OFFSET sub_begin,
TPIE_OS_OFFSET sub_end,
BTE_stream_base < T > **sub_stream);
~BTE_stream_stdio (void);
BTE_err read_item (T ** elt);
BTE_err write_item (const T & elt);
// Query memory usage
BTE_err main_memory_usage (size_t * usage, MM_stream_usage usage_type);
// Return the number of items in the stream.
TPIE_OS_OFFSET stream_len (void) const;
// Return the path name in newly allocated space.
BTE_err name (char **stream_name);
// Move to a specific position in the stream.
BTE_err seek (TPIE_OS_OFFSET offset);
// Return the current position in the stream.
TPIE_OS_OFFSET tell () const;
// Truncate the stream.
BTE_err truncate (TPIE_OS_OFFSET offset);
TPIE_OS_OFFSET chunk_size (void) const;
};
template < class T >
BTE_stream_stdio < T >::BTE_stream_stdio (const char *dev_path,
const BTE_stream_type st,
size_t lbf) {
BTE_err berr;
// Reduce the number of streams avaialble.
if (remaining_streams <= 0) {
status_ = BTE_STREAM_STATUS_INVALID;
return;
}
// Cache the path name
if (strlen (dev_path) > BTE_STREAM_PATH_NAME_LEN - 1) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_FATAL_ID("Path name too long:");
TP_LOG_FATAL_ID(dev_path);
return;
}
strncpy (path, dev_path, BTE_STREAM_PATH_NAME_LEN);
status_ = BTE_STREAM_STATUS_NO_STATUS;
os_block_size_ = os_block_size ();
// Not a substream.
substream_level = 0;
logical_bos = logical_eos = -1;
// By default, all streams are deleted at destruction time. (the
// comment above is misleading. the AMI level stream is controlling
// the persistency of this stream)
per = PERSIST_DELETE;
remaining_streams--;
switch (st) {
case BTE_READ_STREAM:
// Open the file for reading.
r_only = 1;
if ((file = TPIE_OS_FOPEN(dev_path, "rb")) == NULL) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_FATAL_ID("Failed to open file:");
TP_LOG_FATAL_ID(dev_path);
return;
}
// Read and check header
if (readcheck_header () == -1) {
TP_LOG_FATAL_ID("Bad header.");
return;
}
// Seek past the end of the first block.
//if (TPIE_OS_FSEEK (file, os_block_size_, 0) == -1) {
//status_ = BTE_STREAM_STATUS_INVALID;
//LOG_FATAL_ID("fseek failed.");
//return;
//}
if ((berr = this->seek (0)) != BTE_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID("Cannot seek in file:");
TP_LOG_FATAL_ID(dev_path);
return;
}
break;
case BTE_WRITE_STREAM:
case BTE_WRITEONLY_STREAM:
case BTE_APPEND_STREAM:
// Open the file for appending.
r_only = 0;
if ((file = TPIE_OS_FOPEN(dev_path, "rb+")) == NULL) {
//file does not exist - create it
if ((file = TPIE_OS_FOPEN (dev_path, "wb+")) == NULL) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_FATAL_ID("Failed to open file:");
TP_LOG_FATAL_ID(dev_path);
return;
}
// Create and write the header
init_header(&header);
header.type = BTE_STREAM_STDIO;
if (TPIE_OS_FWRITE ((char *) &header, sizeof (header), 1, file) != 1) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_FATAL_ID("Failed to write header to file:");
TP_LOG_FATAL_ID(dev_path);
return;
}
// Truncate the file to header block
if ((berr = this->truncate (0)) != BTE_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID("Cannot truncate in file:");
TP_LOG_FATAL_ID(dev_path);
return;
}
if ((berr = this->seek (0)) != BTE_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID("Cannot seek in file:");
TP_LOG_FATAL_ID(dev_path);
return;
}
gstats_.record(STREAM_CREATE);
stats_.record(STREAM_CREATE);
} else {
// File exists - read and check header
if (readcheck_header () == -1) {
TP_LOG_FATAL_ID("Bad header in file:");
TP_LOG_FATAL_ID(dev_path);
return;
}
// Seek to the end of the stream if BTE_APPEND_STREAM
if (st == BTE_APPEND_STREAM) {
if (TPIE_OS_FSEEK (file, 0, TPIE_OS_FLAG_SEEK_END)) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_FATAL_ID("Failed to go to EOF of file:");
TP_LOG_FATAL_ID(dev_path);
return;
}
// Make sure there was at least a full block there to pass.
if (TPIE_OS_FTELL (file) < (long)os_block_size_) {
TP_LOG_FATAL_ID("File too short:");
TP_LOG_FATAL_ID(dev_path);
status_ = BTE_STREAM_STATUS_INVALID;
}
} else {
// seek to 0 if BTE_WRITE_STREAM
if ((berr = this->seek (0)) != BTE_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID("Cannot seek in file:");
TP_LOG_FATAL_ID(dev_path);
return;
}
}
}
break;
default:
// Either a bad value or a case that has not been implemented
// yet.
TP_LOG_WARNING_ID("Bad or unimplemented case.");
status_ = BTE_STREAM_STATUS_INVALID;
break;
}
f_eof = item_off_to_file_off(header.item_logical_eof);
// Register memory usage before returning.
// A quick and dirty guess. One block in the buffer cache, one in
// user space. This is not accounted for by a "new" call, so we have to
// register it ourselves. TODO: is 2*block_size accurate?
register_memory_allocation (os_block_size_ * 2);
gstats_.record(STREAM_OPEN);
stats_.record(STREAM_OPEN);
}
// A psuedo-constructor for substreams. This allows us to get around
// the fact that one cannot have virtual constructors.
template < class T >
BTE_err BTE_stream_stdio < T >::new_substream (BTE_stream_type st,
TPIE_OS_OFFSET sub_begin,
TPIE_OS_OFFSET sub_end,
BTE_stream_base < T >
**sub_stream)
{
// Check permissions.
if ((st != BTE_READ_STREAM) && ((st != BTE_WRITE_STREAM) || r_only)) {
*sub_stream = NULL;
return BTE_ERROR_PERMISSION_DENIED;
}
tp_assert (((st == BTE_WRITE_STREAM) && !r_only) ||
(st == BTE_READ_STREAM),
"Bad things got through the permisssion checks.");
if (substream_level) {
if ((sub_begin * (TPIE_OS_OFFSET)sizeof (T) >=
(logical_eos - logical_bos))
|| (sub_end * (TPIE_OS_OFFSET)sizeof (T) >
(logical_eos - logical_bos))) {
*sub_stream = NULL;
return BTE_ERROR_OFFSET_OUT_OF_RANGE;
}
}
// We actually have to completely reopen the file in order to get
// another seek pointer into it. We'll do this by constructing
// the stream that will end up being the substream.
BTE_stream_stdio *sub = new BTE_stream_stdio (path, st);
// Set up the beginning and end positions.
if (substream_level) {
sub->logical_bos = logical_bos + sub_begin * sizeof (T);
sub->logical_eos = logical_bos + (sub_end + 1) * sizeof (T);
} else {
sub->logical_bos = sub->os_block_size_ + sub_begin * sizeof (T);
sub->logical_eos =
sub->os_block_size_ + (sub_end + 1) * sizeof (T);
}
// Set the current position.
TPIE_OS_FSEEK (sub->file, sub->logical_bos, 0);
sub->substream_level = substream_level + 1;
sub->per =
(per == PERSIST_READ_ONCE) ? PERSIST_READ_ONCE : PERSIST_PERSISTENT;
*sub_stream = (BTE_stream_base < T > *)sub;
gstats_.record(SUBSTREAM_CREATE);
stats_.record(SUBSTREAM_CREATE);
return BTE_ERROR_NO_ERROR;
}
template < class T > BTE_stream_stdio < T >::~BTE_stream_stdio (void) {
if (!r_only) {
header.item_logical_eof = file_off_to_item_off(f_eof);
if (TPIE_OS_FSEEK (file, 0, TPIE_OS_FLAG_SEEK_SET) == -1) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_WARNING_ID("Failed to seek in file:");
TP_LOG_WARNING_ID(path);
} else if (TPIE_OS_FWRITE ((char *) &header, sizeof (header), 1, file) != 1) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_WARNING_ID("Failed to write header to file:");
TP_LOG_WARNING_ID(path);
// return;
}
}
if (TPIE_OS_FCLOSE (file) != 0) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_WARNING_ID("Failed to close file:");
TP_LOG_WARNING_ID(path);
}
// Get rid of the file if not persistent and if not substream.
if (!substream_level) {
if (per == PERSIST_DELETE) {
if (r_only) {
TP_LOG_WARNING_ID("Read only stream is PERSIST_DELETE:");
TP_LOG_WARNING_ID(path);
TP_LOG_WARNING_ID("Ignoring persistency request.");
} else if (unlink (path)) {
os_errno = errno;
TP_LOG_WARNING_ID("Failed to unlink() file:");
TP_LOG_WARNING_ID(path);
TP_LOG_WARNING_ID(strerror(os_errno));
} else {
gstats_.record(STREAM_DELETE);
stats_.record(STREAM_DELETE);
}
}
} else {
gstats_.record(SUBSTREAM_DELETE);
stats_.record(SUBSTREAM_DELETE);
}
// Register memory deallocation before returning.
// A quick and dirty guess. One block in the buffer cache, one in
// user space. TODO.
register_memory_deallocation (os_block_size_ * 2);
if (remaining_streams >= 0) {
remaining_streams++;
}
gstats_.record(STREAM_CLOSE);
stats_.record(STREAM_CLOSE);
}
template < class T > BTE_err BTE_stream_stdio < T >::read_item (T ** elt)
{
TPIE_OS_SIZE_T stdio_ret;
BTE_err ret;
if ((logical_eos >= 0) && (TPIE_OS_FTELL (file) >= logical_eos)) {
tp_assert ((logical_bos >= 0), "eos set but bos not.");
status_ = BTE_STREAM_STATUS_END_OF_STREAM;
ret = BTE_ERROR_END_OF_STREAM;
} else {
stdio_ret = TPIE_OS_FREAD ((char *) (&read_tmp), sizeof (T), 1, file);
if (stdio_ret == 1) {
f_offset += sizeof(T);
*elt = &read_tmp;
ret = BTE_ERROR_NO_ERROR;
} else {
// Assume EOF. Fix this later.
status_ = BTE_STREAM_STATUS_END_OF_STREAM;
ret = BTE_ERROR_END_OF_STREAM;
}
}
gstats_.record(ITEM_READ);
stats_.record(ITEM_READ);
return ret;
}
template < class T >
BTE_err BTE_stream_stdio < T >::write_item (const T & elt) {
TPIE_OS_SIZE_T stdio_ret;
BTE_err ret;
if ((logical_eos >= 0) && (TPIE_OS_FTELL (file) > logical_eos)) {
tp_assert ((logical_bos >= 0), "eos set but bos not.");
status_ = BTE_STREAM_STATUS_END_OF_STREAM;
ret = BTE_ERROR_END_OF_STREAM;
} else {
stdio_ret = TPIE_OS_FWRITE ((char *) &elt, sizeof (T), 1, file);
if (stdio_ret == 1) {
if (f_eof == f_offset)
f_eof += sizeof(T);
f_offset += sizeof(T);
ret = BTE_ERROR_NO_ERROR;
} else {
TP_LOG_FATAL_ID("write_item failed.");
status_ = BTE_STREAM_STATUS_INVALID;
ret = BTE_ERROR_IO_ERROR;
}
}
gstats_.record(ITEM_WRITE);
stats_.record(ITEM_WRITE);
return ret;
}
template < class T >
BTE_err BTE_stream_stdio < T >::main_memory_usage (size_t * usage,
MM_stream_usage
usage_type) {
switch (usage_type) {
case MM_STREAM_USAGE_OVERHEAD:
//Fixed overhead per object. *this includes base class.
//Need to include 2*overhead per "new" that sizeof doesn't
//know about.
*usage = sizeof(*this)+2*MM_manager.space_overhead();
break;
case MM_STREAM_USAGE_BUFFER:
//Amount used by stdio buffers. No "new" calls => no overhead
*usage = 2 * os_block_size_;
break;
case MM_STREAM_USAGE_CURRENT:
case MM_STREAM_USAGE_MAXIMUM:
case MM_STREAM_USAGE_SUBSTREAM:
*usage = sizeof(*this) + 2*os_block_size_ +
2*MM_manager.space_overhead();
break;
}
return BTE_ERROR_NO_ERROR;
}
// Return the number of items in the stream.
template < class T >
TPIE_OS_OFFSET BTE_stream_stdio < T >::stream_len (void) const {
if (substream_level) { // We are in a substream.
/// return (logical_eos - logical_bos) / sizeof (T);
// [tavi 01/25/02] Commented out the above and replaced it with the following:
return file_off_to_item_off(logical_eos) - file_off_to_item_off(logical_bos);
} else {
// There must be a way to get this information directly,
// instead of fseeking around.
// Where are we now?
/// TPIE_OS_OFFSET current = ftell (file);
// Go to the end and see where we are.
/// TPIE_OS_FSEEK (file, 0, TPIE_OS_FLAG_SEEK_END);
/// TPIE_OS_OFFSET end = ftell (file);
// Go back.
/// TPIE_OS_FSEEK (file, current, TPIE_OS_FLAG_SEEK_SET);
// Lars May 22, 1997: This is a quick hack to fix a problem
// with headers of length less than a block. That shouldn't
// be possible but there is a bug somewhere
// - Look at it later (seems to have something to do with
// substreams. Header block is not truncated).
/// if (end < (int) os_block_size_) {
//printf("shouldnt be here! possible bug\n);
/// return 0;
/// } else {
/// return (end - os_block_size_) / sizeof (T);
/// }
// [tavi 01/25/02] Commented out the above and replaced it with the following:
return file_off_to_item_off(f_eof);
}
}
// Return the path name in newly allocated space.
template < class T >
BTE_err BTE_stream_stdio < T >::name (char **stream_name) {
TPIE_OS_SIZE_T len = strlen (path);
tp_assert (len < BTE_STREAM_PATH_NAME_LEN, "Path length is too long.");
// Return the path name in newly allocated space.
char *new_path = new char[len + 1];
strncpy (new_path, path, len + 1);
*stream_name = new_path;
return BTE_ERROR_NO_ERROR;
}
// Move to a specific position.
template < class T > BTE_err BTE_stream_stdio < T >::seek (TPIE_OS_OFFSET offset) {
TPIE_OS_OFFSET file_position;
if (substream_level) {
if (offset * (TPIE_OS_OFFSET)sizeof (T) > (TPIE_OS_OFFSET)(logical_eos - logical_bos)) {
return BTE_ERROR_OFFSET_OUT_OF_RANGE;
} else {
file_position = offset * sizeof (T) + logical_bos;
}
} else {
file_position = offset * sizeof (T) + os_block_size_;
}
if (TPIE_OS_FSEEK (file, file_position, TPIE_OS_FLAG_SEEK_SET)) {
TP_LOG_FATAL("fseek failed to go to position " << file_position <<
" of \"" << "\"\n");
TP_LOG_FLUSH_LOG;
return BTE_ERROR_OS_ERROR;
}
f_offset = file_position;
gstats_.record(ITEM_SEEK);
stats_.record(ITEM_SEEK);
return BTE_ERROR_NO_ERROR;
}
template < class T >
TPIE_OS_OFFSET BTE_stream_stdio < T >::tell() const {
return file_off_to_item_off(f_offset);
}
// Truncate the stream.
template < class T >
BTE_err BTE_stream_stdio < T >::truncate (TPIE_OS_OFFSET offset) {
// TPIE_OS_TRUNCATE_STREAM_TEMPLATE_CLASS_BODY;
TPIE_OS_OFFSET file_position;
if (substream_level) {
return BTE_ERROR_STREAM_IS_SUBSTREAM;
}
if (offset < 0) {
return BTE_ERROR_OFFSET_OUT_OF_RANGE;
}
file_position = offset * sizeof (T) + os_block_size_;
// if (::truncate (path, file_position)) {
if (TPIE_OS_TRUNCATE(file, path, file_position) == -1) {
os_errno = errno;
TP_LOG_FATAL_ID("Failed to truncate() to the new end of file:");
TP_LOG_FATAL_ID(path);
TP_LOG_FATAL_ID(strerror (os_errno));
return BTE_ERROR_OS_ERROR;
}
if (TPIE_OS_FSEEK(file, file_position, SEEK_SET) == -1) {
TP_LOG_FATAL("fseek failed to go to position " << file_position << " of \"" << "\"\n");
TP_LOG_FLUSH_LOG;
return BTE_ERROR_OS_ERROR;
}
f_offset = file_position;
f_eof = file_position;
return BTE_ERROR_NO_ERROR;
}
template < class T >
TPIE_OS_OFFSET BTE_stream_stdio < T >::chunk_size (void) const {
// Quick and dirty guess.
return (os_block_size_ * 2) / sizeof (T);
}
// Return -1 if error, 0 otherwise.
template<class T> int BTE_stream_stdio < T >::readcheck_header ()
{
// Read the header.
if ((TPIE_OS_FREAD ((char *) &header, sizeof (header), 1, file)) != 1) {
status_ = BTE_STREAM_STATUS_INVALID;
TP_LOG_FATAL_ID("Failed to read header from file:");
TP_LOG_FATAL_ID(path);
return -1;
}
if (check_header(&header) < 0) {
return -1;
}
//everything's fine
return 0;
}
template<class T>
TPIE_OS_OFFSET BTE_stream_stdio < T >::file_off_to_item_off (TPIE_OS_OFFSET file_off) const {
return (file_off - os_block_size_) / sizeof (T);
}
template<class T>
TPIE_OS_OFFSET BTE_stream_stdio < T >::item_off_to_file_off (TPIE_OS_OFFSET item_off) const {
return (os_block_size_ + item_off * sizeof (T));
}
#endif // _BTE_STREAM_STDIO_H
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
librule(
name="tpie",
cflags = "-I. -DHAVE_CONFIG_H -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE",
sources=["ami_bit_permute.cc",
"ami_device.cc",
"ami_key.cc",
"ami_matrix_blocks.cc",
#"ami_scan_mac.cc",
"ami_stream_single.cc",
"bit.cc",
"bit_matrix.cc",
"bte_stream_base.cc",
"cpu_timer.cc",
"cpu_timer.cc",
"logstream.cc",
"mm_base.cc",
"mm_register.cc",
"portability.cc",
"tpie_log.cc",
"tpie_tempnam.cc"],
headers=lglob("*.h")
);
+73
View File
@@ -0,0 +1,73 @@
// Copyright (c) 2005 Andrew Danner
//
// File: comparator.h
// Author: Andrew Danner
// Created: 28 Jun 2005
//
// Mappings/Wrappers for converting between different comparison types
//
// $Id: comparator.h,v 1.4 2005/07/07 20:39:22 adanner Exp $
//
#ifndef _COMPARATOR_H
#define _COMPARATOR_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// In is unlikely that users will need to directly need these classes
// except to maybe upgrade old code with minimal changes. In the future it
// may be better to make TPIE's compare() method match the syntax of the
// STL operator ()
//Convert STL comparison object with operator() to a TPIE comparison
//object with a compare() function.
template<class T, class STLCMP>
class STL2TPIE_cmp{
private:
STLCMP *isLess; //Class with STL comparison operator()
public:
STL2TPIE_cmp(STLCMP* _cmp) {isLess=_cmp; }
//Do not use with applications that test if compare returns +1
//Because it never does so.
inline int compare(const T& left, const T& right){
if( (*isLess)(left, right) ){ return -1; }
else { return 0; }
}
};
//Convert a TPIE comparison object with a compare() function.
//to STL comparison object with operator()
template<class T, class TPCMP>
class TPIE2STL_cmp{
private:
TPCMP* cmpobj; //Class with TPIE comparison compare()
public:
TPIE2STL_cmp(TPCMP* cmp) {cmpobj=cmp;}
inline bool operator()(const T& left, const T& right) const{
return (cmpobj->compare(left, right) < 0);
}
};
//Convert a class with a comparison operator <
//to a TPIE comparison object with a compare() function.
template<class T>
class op2TPIE_cmp{
public:
op2TPIE_cmp(){};
//Do not use with applications that test if compare returns +1
//Because it never does so.
inline int compare(const T& left, const T& right){
if( left < right ){ return -1; }
else { return 0; }
}
};
//Convert a class with a comparison operator <
//to an STL comparison object with a comparison operator ().
//Not implemented here. It is called less in <functional>, part of STL
#endif // _COMPARATOR_H
+67
View File
@@ -0,0 +1,67 @@
/* include/config.h. Generated by configure. */
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: config.h.in
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 10/13/94
//
// $Id: config.h.in,v 1.8 2003/04/17 18:51:20 jan Exp $
//
#ifndef _CONFIG_H
#define _CONFIG_H
// Define if you have getrusage()
#define HAVE_GETRUSAGE 1
// Define if you have mmap()
#define HAVE_MMAP 1
// Define if asyncronous I/O is avaialable.
#define HAVE_LIBAIO 0
// Define if you have a prototype for mmap()
#define HAVE_PROTOTYPE_MMAP 1
// Define if you have a prototype for munmap()
#define HAVE_PROTOTYPE_MUNMAP 1
// Define if you have a prototype for ftruncate()
#define HAVE_PROTOTYPE_FTRUNCATE 1
// Do we have string.h or strings.h?
#define HAVE_STRING_H 1
#define HAVE_STRINGS_H 1
// Where is unistd.h?
#ifdef _WIN32
#define HAVE_UNISTD_H 1
#define HAVE_SYS_UNISTD_H 1
#else
#define HAVE_UNISTD_H 1
#define HAVE_SYS_UNISTD_H 1
#endif
// Are we on a hacked kernel that supports zero()?
#define HAVE_ZERO 0
#if HAVE_UNISTD_H
#include <unistd.h>
#elif HAVE_SYS_UNISTD_H
#include <sys/unistd.h>
#endif
// On Solaris, _SC_PAGE_SIZE is called _SC_PAGE_SIZE. Here's a quick
// fix.
#if !defined(_SC_PAGE_SIZE) && defined(_SC_PAGESIZE)
#define _SC_PAGE_SIZE _SC_PAGESIZE
#endif
// Flags to enable or disable various features of the system.
#define TP_ASSERT_APPS 1
#define TP_ASSERT_LIB 1
#define TP_LOG_APPS 1
#define TP_LOG_LIB 1
#endif // _CONFIG_H
+66
View File
@@ -0,0 +1,66 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: config.h.in
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 10/13/94
//
// $Id: config.h.in,v 1.8 2003/04/17 18:51:20 jan Exp $
//
#ifndef _CONFIG_H
#define _CONFIG_H
// Define if you have getrusage()
#define HAVE_GETRUSAGE 0
// Define if you have mmap()
#define HAVE_MMAP 0
// Define if asyncronous I/O is avaialable.
#define HAVE_LIBAIO 0
// Define if you have a prototype for mmap()
#define HAVE_PROTOTYPE_MMAP 0
// Define if you have a prototype for munmap()
#define HAVE_PROTOTYPE_MUNMAP 0
// Define if you have a prototype for ftruncate()
#define HAVE_PROTOTYPE_FTRUNCATE 0
// Do we have string.h or strings.h?
#define HAVE_STRING_H 0
#define HAVE_STRINGS_H 0
// Where is unistd.h?
#ifdef _WIN32
#define HAVE_UNISTD_H 0
#define HAVE_SYS_UNISTD_H 0
#else
#define HAVE_UNISTD_H 1
#define HAVE_SYS_UNISTD_H 1
#endif
// Are we on a hacked kernel that supports zero()?
#define HAVE_ZERO 0
#if HAVE_UNISTD_H
#include <unistd.h>
#elif HAVE_SYS_UNISTD_H
#include <sys/unistd.h>
#endif
// On Solaris, _SC_PAGE_SIZE is called _SC_PAGE_SIZE. Here's a quick
// fix.
#if !defined(_SC_PAGE_SIZE) && defined(_SC_PAGESIZE)
#define _SC_PAGE_SIZE _SC_PAGESIZE
#endif
// Flags to enable or disable various features of the system.
#define TP_ASSERT_APPS 1
#define TP_ASSERT_LIB 1
#define TP_LOG_APPS 1
#define TP_LOG_LIB 1
#endif // _CONFIG_H
+90
View File
@@ -0,0 +1,90 @@
// Copyright (c) 1995 Darren Vengroff
//
// File: cpu_timer.cpp
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 1/11/95
//
#include <versions.h>
VERSION(cpu_timer_cpp,"$Id: cpu_timer.cpp,v 1.9 2004/08/17 16:48:50 jan Exp $");
#include <cpu_timer.h>
cpu_timer::cpu_timer() :
running(false)
{
TPIE_OS_SET_CLOCK_TICK;
elapsed_real = 0;
}
cpu_timer::~cpu_timer()
{
}
void cpu_timer::sync()
{
clock_t current_real;
TPIE_OS_TMS current;
TPIE_OS_SET_CURRENT_TIME(current);
TPIE_OS_UNIX_ONLY_SET_ELAPSED_TIME(current);
elapsed_real += current_real - last_sync_real;
last_sync = current;
last_sync_real = current_real;
}
void cpu_timer::start()
{
if (!running) {
TPIE_OS_LAST_SYNC_REAL_DECLARATION;
running = true;
}
}
void cpu_timer::stop()
{
if (running) {
sync();
running = false;
}
}
void cpu_timer::reset()
{
if (running) {
TPIE_OS_LAST_SYNC_REAL_DECLARATION;
}
TPIE_OS_SET_CLOCK_TICK;
elapsed_real = 0;
}
double cpu_timer::user_time() {
if (running) sync();
TPIE_OS_USER_TIME_BODY;
}
double cpu_timer::system_time() {
if (running) sync();
TPIE_OS_USER_TIME_BODY;
}
double cpu_timer::wall_time() {
if (running) sync();
return double(elapsed_real) / double(clock_tick);
}
ostream &operator<<(ostream &s, cpu_timer &wt)
{
if (wt.running) {
wt.sync();
}
TPIE_OS_OPERATOR_OVERLOAD;
}
+49
View File
@@ -0,0 +1,49 @@
//
// File: cpu_timer.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 1/11/95
//
// $Id: cpu_timer.h,v 1.8 2004/08/17 16:48:11 jan Exp $
//
// A timer measuring user time, system time and wall clock time. The
// timer can be start()'ed, stop()'ed, and queried. Querying can be
// done without stopping the timer, to report intermediate values.
//
#ifndef _CPU_TIMER_H
#define _CPU_TIMER_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <iostream>
#include <timer.h>
class cpu_timer : public timer {
private:
long clock_tick;
TPIE_OS_TMS last_sync;
TPIE_OS_TMS elapsed;
clock_t last_sync_real;
clock_t elapsed_real;
bool running;
public:
cpu_timer();
virtual ~cpu_timer();
void start();
void stop();
void sync();
void reset();
double user_time();
double system_time();
double wall_time();
friend ostream &operator<<(ostream &s, cpu_timer &ct);
};
ostream &operator<<(ostream &s, cpu_timer &ct);
#endif // _CPU_TIMER_H
+469
View File
@@ -0,0 +1,469 @@
// Copyright (c) 2005 Andrew Danner
//
// File: internal_sorter.h
// Author: Andrew Danner <adanner@cs.duke.edu>
// Created: 28 Jun 2005
//
// Internal sorter class that can be used within AMI_sort() on small
// streams/substreams
//
// $Id: internal_sort.h,v 1.1 2005/08/24 19:32:38 adanner Exp $
//
#ifndef _INTERNAL_SORT_H
#define _INTERNAL_SORT_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <quicksort.h>
// Use our quicksort, or the sort from STL
#ifdef TPIE_USE_STL_SORT
// portability.h includes <algorithm> for us in the case of STL sort
#include <comparator.h> //to convert TPIE comparisons to STL
#endif
// The base class. This class does not have a sort() function, so it
// cannot be used directly
template<class T>
class Internal_Sorter_Base{
protected:
T* ItemArray; //Array that holds items to be sorted
TPIE_OS_OFFSET len; //length of ItemArray
public:
//Constructor
Internal_Sorter_Base(void): len(0) { ItemArray=NULL; }
~Internal_Sorter_Base(void); //Destructor
//Allocate array that can hold nItems
void allocate(TPIE_OS_OFFSET nItems);
void deallocate(void); //Clean up internal array
// Maximum number of Items that can be sorted using memSize bytes
TPIE_OS_OFFSET MaxItemCount(TPIE_OS_OFFSET memSize);
// Memory usage per sort item
TPIE_OS_SIZE_T space_per_item();
// Fixed memory usage overhead per class instantiation
TPIE_OS_SIZE_T space_overhead();
};
template<class T>
Internal_Sorter_Base<T>::~Internal_Sorter_Base(void){
//In case someone forgot to call deallocate()
if(ItemArray){
delete [] ItemArray;
ItemArray=NULL;
}
}
template<class T>
inline void Internal_Sorter_Base<T>::allocate(TPIE_OS_OFFSET nitems){
len=nitems;
ItemArray = new T[len];
}
template<class T>
inline void Internal_Sorter_Base<T>::deallocate(void){
if(ItemArray){
delete [] ItemArray;
ItemArray=NULL;
len=0;
}
}
template<class T>
inline TPIE_OS_OFFSET Internal_Sorter_Base<T>::MaxItemCount(
TPIE_OS_OFFSET memSize)
{
//Space available for items
TPIE_OS_OFFSET memAvail=memSize-space_overhead();
if(memAvail < space_per_item() ){ return -1; }
else{ return memAvail/space_per_item(); }
}
template<class T>
inline TPIE_OS_SIZE_T Internal_Sorter_Base<T>::space_overhead(void)
{
// Space usage independent of space_per_item
// accounts MM_manager space overhead on "new" call
return MM_manager.space_overhead();
}
template<class T>
inline TPIE_OS_SIZE_T Internal_Sorter_Base<T>::space_per_item(void)
{
return sizeof(T);
}
// *********************************************************************
// * *
// * Operator based Internal Sorter. *
// * *
// *********************************************************************
template<class T>
class Internal_Sorter_Op: public Internal_Sorter_Base<T>{
protected:
using Internal_Sorter_Base<T>::len;
using Internal_Sorter_Base<T>::ItemArray;
public:
//Constructor/Destructor
Internal_Sorter_Op(){};
~Internal_Sorter_Op(){};
using Internal_Sorter_Base<T>::space_overhead;
//Sort nItems from input stream and write to output stream
AMI_err sort(AMI_STREAM<T>* InStr, AMI_STREAM<T>* OutStr,
TPIE_OS_OFFSET nItems);
};
// Read nItems sequentially from InStr, starting at the current file
// position. Write the sorted output to OutStr, starting from the current
// file position.
template<class T>
AMI_err Internal_Sorter_Op<T>::sort(AMI_STREAM<T>* InStr,
AMI_STREAM<T>* OutStr, TPIE_OS_OFFSET nItems){
AMI_err ae;
T *next_item;
TPIE_OS_OFFSET i = 0;
TP_LOG_DEBUG_ID("Sorting internal run of " << nItems
<< " items using \"<\" operator.");
tp_assert ( nItems <= len, "nItems more than interal buffer size.");
// Read a memory load out of the input stream one item at a time,
for (i = 0; i < nItems; i++) {
if ((ae=InStr->read_item (&next_item)) != AMI_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID ("Internal sort: AMI read error " << ae);
return ae;
}
ItemArray[i] = *next_item;
}
//Sort the array.
#ifdef TPIE_USE_STL_SORT
TP_LOG_DEBUG_ID("calling STL sort for " << nItems << " items");
std::sort(ItemArray, ItemArray+nItems);
#else
TP_LOG_DEBUG_ID("calling quick_sort_op for " << nItems << " items");
quick_sort_op<T> (ItemArray, nItems);
#endif
if(InStr==OutStr){ //Do the right thing if we are doing 2x sort
//Internal sort objects should probably be re-written so that
//the interface is cleaner and they don't have to worry about I/O
InStr->truncate(0); //delete original items
InStr->seek(0); //rewind
}
//Write sorted array to OutStr
for (i = 0; i < nItems; i++) {
if ((ae = OutStr->write_item(ItemArray[i])) != AMI_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID ("Internal Sorter: AMI write error" << ae );
return ae;
}
}
TP_LOG_DEBUG_ID("returning from Internal_Sorter_Op");
return AMI_ERROR_NO_ERROR;
}
// *********************************************************************
// * *
// * Comparison object based Internal Sorter. *
// * *
// *********************************************************************
template<class T, class CMPR>
class Internal_Sorter_Obj: public Internal_Sorter_Base<T>{
protected:
using Internal_Sorter_Base<T>::ItemArray;
using Internal_Sorter_Base<T>::len;
CMPR *cmp_o; //Comparison object used for sorting
public:
//Constructor/Destructor
Internal_Sorter_Obj(CMPR* cmp){cmp_o=cmp;};
~Internal_Sorter_Obj(){};
using Internal_Sorter_Base<T>::space_overhead;
//Sort nItems from input stream and write to output stream
AMI_err sort(AMI_STREAM<T>* InStr, AMI_STREAM<T>* OutStr,
TPIE_OS_OFFSET nItems);
};
// Read nItems sequentially from InStr, starting at the current file
// position. Write the sorted output to OutStr, starting from the current
// file position.
template<class T, class CMPR>
AMI_err Internal_Sorter_Obj<T, CMPR>::sort(AMI_STREAM<T>* InStr,
AMI_STREAM<T>* OutStr, TPIE_OS_OFFSET nItems){
AMI_err ae;
T *next_item;
TPIE_OS_OFFSET i = 0;
TP_LOG_DEBUG_ID("Sorting internal run of " << nItems
<< " items using TPIE comparison object.");
tp_assert ( nItems <= len, "nItems more than interal buffer size.");
// Read a memory load out of the input stream one item at a time,
for (i = 0; i < nItems; i++) {
if ((ae=InStr->read_item (&next_item)) != AMI_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID ("Internal sort: AMI read error " << ae);
return ae;
}
ItemArray[i] = *next_item;
}
//Sort the array.
#ifdef TPIE_USE_STL_SORT
TP_LOG_DEBUG_ID("calling STL sort for " << nItems << " items");
TP_LOG_DEBUG_ID("converting TPIE comparison object to STL");
std::sort(ItemArray, ItemArray+nItems, TPIE2STL_cmp<T,CMPR>(cmp_o));
#else
TP_LOG_DEBUG_ID("calling quick_sort_obj for " << nItems << " items");
quick_sort_obj<T> (ItemArray, nItems, cmp_o);
#endif
if(InStr==OutStr){ //Do the right thing if we are doing 2x sort
//Internal sort objects should probably be re-written so that
//the interface is cleaner and they don't have to worry about I/O
InStr->truncate(0); //delete original items
InStr->seek(0); //rewind
}
//Write sorted array to OutStr
for (i = 0; i < nItems; i++) {
if ((ae = OutStr->write_item(ItemArray[i])) != AMI_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID ("Internal Sorter: AMI write error" << ae );
return ae;
}
}
TP_LOG_DEBUG_ID("returning from Internal_Sorter_Op");
return AMI_ERROR_NO_ERROR;
}
// *********************************************************************
// * *
// * Key + Object based Internal Sorter *
// * *
// *********************************************************************
template<class T, class KEY, class CMPR>
class Internal_Sorter_KObj{
protected:
T* ItemArray; //Array that holds original items
qsort_item<KEY>* sortItemArray; //Holds keys to be sorted
CMPR *UsrObject; //Copy,compare keys
TPIE_OS_OFFSET len; //length of ItemArray
public:
//Constructor:
Internal_Sorter_KObj(CMPR* cmp): len(0) {
ItemArray=NULL;
sortItemArray=NULL;
UsrObject=cmp;
}
~Internal_Sorter_KObj(void); //Destructor
//Allocate array that can hold nItems
void allocate(TPIE_OS_OFFSET nItems);
//Sort nItems from input stream and write to output stream
AMI_err sort(AMI_STREAM<T>* InStr, AMI_STREAM<T>* OutStr,
TPIE_OS_OFFSET nItems);
void deallocate(void); //Clean up internal array
// Maximum number of Items that can be sorted using memSize bytes
TPIE_OS_OFFSET MaxItemCount(TPIE_OS_OFFSET memSize);
// Memory usage per sort item
TPIE_OS_SIZE_T space_per_item();
// Fixed memory usage overhead per class instantiation
TPIE_OS_SIZE_T space_overhead();
};
template<class T, class KEY, class CMPR>
Internal_Sorter_KObj<T, KEY, CMPR>::~Internal_Sorter_KObj(void){
//In case someone forgot to call deallocate()
if(ItemArray){
delete [] ItemArray;
ItemArray=NULL;
}
if(sortItemArray){
delete [] sortItemArray;
sortItemArray=NULL;
}
}
template<class T, class KEY, class CMPR>
inline void Internal_Sorter_KObj<T, KEY, CMPR>::allocate(TPIE_OS_OFFSET nitems){
len=nitems;
ItemArray = new T[len];
sortItemArray = new qsort_item<KEY>[len];
}
// A helper class to quick sort qsort_item<KEY> types
// given a comparison object for comparing keys
template<class KEY, class KCMP>
class QsortKeyCmp{
private:
KCMP *isLess; //Class with function compare that compares 2 keys
public:
QsortKeyCmp(KCMP* kcmp) {isLess=kcmp; }
inline int compare(const qsort_item<KEY>& left,
const qsort_item<KEY>& right){
return isLess->compare(left.keyval, right.keyval);
}
};
template<class T, class KEY, class CMPR>
inline AMI_err Internal_Sorter_KObj<T, KEY, CMPR>::sort(AMI_STREAM<T>* InStr,
AMI_STREAM<T>* OutStr, TPIE_OS_OFFSET nItems){
AMI_err ae;
T *next_item;
TPIE_OS_OFFSET i = 0;
TP_LOG_DEBUG_ID("Sorting internal run of " << nItems
<< " items using \"<\" operator.");
tp_assert ( nItems <= len, "nItems more than interal buffer size.");
// Read a memory load out of the input stream one item at a time,
for (i = 0; i < nItems; i++) {
if ((ae=InStr->read_item (&next_item)) != AMI_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID ("Internal sort: AMI read error " << ae);
return ae;
}
ItemArray[i] = *next_item;
UsrObject->copy(&sortItemArray[i].keyval, *next_item);
sortItemArray[i].source=i;
}
//Sort the array.
#ifdef TPIE_USE_STL_SORT
TP_LOG_DEBUG_ID("calling STL sort for " << nItems << " items");
TP_LOG_DEBUG_ID("converting TPIE Key comparison object to STL");
std::sort(sortItemArray, ItemArray+nItems,
TPIE2STL_cmp<qsort_item<KEY>,QsortKeyCmp<KEY,CMPR> >
(QsortKeyCmp<KEY, CMPR>(UsrObject)));
#else
QsortKeyCmp<KEY, CMPR> qcmp(UsrObject);
TP_LOG_DEBUG_ID("calling quick_sort_obj for " << nItems << " items");
quick_sort_obj< qsort_item<KEY> > (sortItemArray, nItems, &qcmp);
#endif
if(InStr==OutStr){ //Do the right thing if we are doing 2x sort
//Internal sort objects should probably be re-written so that
//the interface is cleaner and they don't have to worry about I/O
InStr->truncate(0); //delete original items
InStr->seek(0); //rewind
}
//Write sorted array to OutStr
for (i = 0; i < nItems; i++) {
if ((ae = OutStr->write_item(ItemArray[sortItemArray[i].source]))
!= AMI_ERROR_NO_ERROR) {
TP_LOG_FATAL_ID ("Internal Sorter: AMI write error" << ae );
return ae;
}
}
TP_LOG_DEBUG_ID("returning from Internal_Sorter_Op");
return AMI_ERROR_NO_ERROR;
}
template<class T, class KEY, class CMPR>
inline void Internal_Sorter_KObj<T, KEY, CMPR>::deallocate(void){
len=0;
if(ItemArray){
delete [] ItemArray;
ItemArray=NULL;
}
if(sortItemArray){
delete [] sortItemArray;
sortItemArray=NULL;
}
}
template<class T, class KEY, class CMPR>
inline TPIE_OS_OFFSET Internal_Sorter_KObj<T, KEY, CMPR>::MaxItemCount(
TPIE_OS_OFFSET memSize)
{
//Space available for items
TPIE_OS_OFFSET memAvail=memSize-space_overhead();
if(memAvail < space_per_item() ){ return -1; }
else{ return memAvail/space_per_item(); }
}
template<class T, class KEY, class CMPR>
inline TPIE_OS_SIZE_T Internal_Sorter_KObj<T, KEY, CMPR>::space_overhead(void)
{
// Space usage independent of space_per_item
// accounts MM_manager space overhead on "new" call
return 2*MM_manager.space_overhead();
}
template<class T, class KEY, class CMPR>
inline TPIE_OS_SIZE_T Internal_Sorter_KObj<T, KEY, CMPR>::space_per_item(void)
{
return sizeof(T) + sizeof(qsort_item<KEY>);
}
#endif // _INTERNAL_SORT_H
+27
View File
@@ -0,0 +1,27 @@
//
// File: lib_config.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 10/31/94
//
// $Id: lib_config.h,v 1.4 2003/04/17 21:00:01 jan Exp $
//
#ifndef _LIB_CONFIG_H
#define _LIB_CONFIG_H
#include <config.h>
// Use logs if requested.
#if TP_LOG_LIB
#define TPL_LOGGING 1
#endif
#include <tpie_log.h>
// Enable assertions if requested.
#if TP_ASSERT_LIB
#define DEBUG_ASSERTIONS 1
#endif
#include <tpie_assert.h>
#endif // _LIB_CONFIG_H
+91
View File
@@ -0,0 +1,91 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: logstream.cpp
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/12/94
//
// The logstream class, for writing to the log.
//
#include <versions.h>
VERSION(logstream_cpp,"$Id: logstream.cpp,v 1.20 2004/08/17 16:48:53 jan Exp $");
#include <logstream.h>
// Constructor.
logstream::logstream(const char *fname,
unsigned int p,
unsigned int tp)
#ifdef UNIFIED_LOGGING
: ofstream(2), priority(p), threshold(tp) { log_initialized = true; }
#else
: ofstream(fname), priority(p), threshold(tp) { log_initialized = true; }
#endif
bool logstream::log_initialized = false;
// Destructor.
logstream::~logstream() {
log_initialized = false;
}
// Output operators.
// A macro to define a log stream output operator for a given type.
// The type can be any type that has an ofstream output operator.
#define _DEFINE_LOGSTREAM_OUTPUT_OPERATOR(T) \
logstream& logstream::operator<<(const T x) \
{ \
if (priority <= threshold) { \
ofstream::operator<<(x); \
} \
return *this; \
}
logstream& logstream::operator<<(const char *x)
{
if (priority <= threshold) {
std::operator<<((*this), x);
}
return *this;
}
//_DEFINE_LOGSTREAM_OUTPUT_OPERATOR(char *)
_DEFINE_LOGSTREAM_OUTPUT_OPERATOR(char)
_DEFINE_LOGSTREAM_OUTPUT_OPERATOR(int)
_DEFINE_LOGSTREAM_OUTPUT_OPERATOR(unsigned int)
_DEFINE_LOGSTREAM_OUTPUT_OPERATOR(long int)
_DEFINE_LOGSTREAM_OUTPUT_OPERATOR(unsigned long)
_DEFINE_LOGSTREAM_OUTPUT_OPERATOR(float)
_DEFINE_LOGSTREAM_OUTPUT_OPERATOR(double)
//"LONGLONG" and "long long" are different in Win32/Unix
TPIE_OS_DEFINE_LOGSTREAM_LONGLONG
// Setting priority and threshold on the fly with manipulators.
logstream& manip_priority(logstream& tpl, unsigned long p)
{
tpl.priority = p;
return tpl;
}
logmanip<unsigned long> setpriority(unsigned long p)
{
return logmanip<unsigned long>(&manip_priority, p);
}
logstream& manip_threshold(logstream& tpl, unsigned long p)
{
tpl.threshold = p;
return tpl;
}
logmanip<unsigned long> setthreshold(unsigned long p)
{
return logmanip<unsigned long>(&manip_threshold, p);
}
+73
View File
@@ -0,0 +1,73 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: logstream.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/12/94
//
// $Id: logstream.h,v 1.20 2004/08/17 16:48:14 jan Exp $
//
#ifndef _LOGSTREAM_H
#define _LOGSTREAM_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// For size_t
#include <sys/types.h>
// A macro for declaring output operators for log streams.
#define _DECLARE_LOGSTREAM_OUTPUT_OPERATOR(T) logstream& operator<<(T)
// A log is like a regular output stream, but it also supports messages
// at different priorities. If a message's priority is at least as high
// as the current priority threshold, then it appears in the log.
// Otherwise, it does not. Lower numbers have higher priority; 0 is
// the highest. 1 is the default if not
class logstream : public ofstream {
public:
static bool log_initialized;
unsigned int priority;
unsigned int threshold;
logstream(const char *fname, unsigned int p = 0, unsigned int tp = 0);
~logstream();
// Output operators
_DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const char *);
_DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const char);
_DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const int);
_DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const unsigned int);
_DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const long int);
_DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const long unsigned int);
_DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const float);
_DECLARE_LOGSTREAM_OUTPUT_OPERATOR(const double);
// Unix "long long", Win32 "LONGLONG".
TPIE_OS_DECLARE_LOGSTREAM_LONGLONG
};
// The logmanip template is based on the omanip template from iomanip.h
// in the libg++ sources.
template <class TP> class logmanip {
logstream& (*_f)(logstream&, TP);
TP _a;
public:
logmanip(logstream& (*f)(logstream&, TP), TP a) : _f(f), _a(a) {}
friend logstream& operator<< (logstream& o, const logmanip<TP>& m) {
(*m._f)(o, m._a);
return o;
}
};
logmanip<unsigned long> setpriority(unsigned long p);
logmanip<unsigned long> setthreshold(unsigned long p);
#endif // _LOGSTREAM_H
+922
View File
@@ -0,0 +1,922 @@
// Copyright (c) 1994 Darren Vengroff
//
// File: matrix.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 11/4/94
//
// $Id: matrix.h,v 1.11 2005/01/14 18:35:00 tavi Exp $
//
#ifndef MATRIX_H
#define MATRIX_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <iostream>
#include <tpie_assert.h>
// Enable exceptions if the compiler supports them.
#ifndef HANDLE_EXCEPTIONS
#define HANDLE_EXCEPTIONS 0
#endif
// References to rows and colums and submatrices.
template<class T> class rowref;
template<class T> class colref;
// Matrices and submatrices.
template<class T> class matrix_base;
template<class T> class matrix;
template<class T> class submatrix;
// A base class for matrices and submatrices.
template<class T> class matrix_base
{
protected:
TPIE_OS_SIZE_T r,c;
public:
#if HANDLE_EXCEPTIONS
// Exception class.
class range { };
#endif
matrix_base(TPIE_OS_SIZE_T rows, TPIE_OS_SIZE_T cols);
virtual ~matrix_base(void);
// What is the size of the matrix?
TPIE_OS_SIZE_T rows(void) const;
TPIE_OS_SIZE_T cols(void) const;
// Access to the contents of the matrix.
virtual T &elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const = 0;
rowref<T> row(TPIE_OS_SIZE_T row) ;
colref<T> col(TPIE_OS_SIZE_T col) ;
rowref<T> operator[](TPIE_OS_SIZE_T row) ;
// Assignement.
matrix_base<T> &operator=(const matrix_base<T> &rhs);
matrix_base<T> &operator=(const rowref<T> &rhs);
matrix_base<T> &operator=(const colref<T> &rhs);
// Addition in place.
matrix_base<T> &operator+=(const matrix_base<T> &rhs);
};
// References to rows and columns.
template<class T>
class rowref
{
private:
matrix_base<T> &m;
TPIE_OS_SIZE_T r;
public:
rowref(matrix_base<T> &amatrix, TPIE_OS_SIZE_T row);
~rowref(void);
T &operator[](const TPIE_OS_SIZE_T col) const;
friend class matrix_base<T>;
friend class matrix<T>;
};
template<class T>
class colref
{
private:
matrix_base<T> &m;
TPIE_OS_SIZE_T c;
public:
colref(matrix_base<T> &amatrix, TPIE_OS_SIZE_T col);
~colref(void);
T &operator[](const TPIE_OS_SIZE_T col) const;
friend class matrix_base<T>;
friend class matrix<T>;
};
template<class T>
matrix_base<T>::matrix_base(TPIE_OS_SIZE_T rows, TPIE_OS_SIZE_T cols) :
r(rows),
c(cols)
{
}
template<class T>
matrix_base<T>::~matrix_base(void)
{
}
template<class T>
TPIE_OS_SIZE_T matrix_base<T>::rows(void) const
{
return r;
}
template<class T>
TPIE_OS_SIZE_T matrix_base<T>::cols(void) const
{
return c;
}
template<class T>
rowref<T> matrix_base<T>::row(TPIE_OS_SIZE_T row)
{
if (row >= r) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
return rowref<T>(*this, row);
}
template<class T>
colref<T> matrix_base<T>::col(TPIE_OS_SIZE_T col)
{
if (col >= c) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
return colref<T>(*this, col);
}
template<class T>
rowref<T> matrix_base<T>::operator[](TPIE_OS_SIZE_T row)
{
return this->row(row);
}
template<class T>
matrix_base<T> &matrix_base<T>::operator=(const matrix_base<T> &rhs)
{
if ((rows() != rhs.rows()) || (cols() != rhs.cols())) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj;
for (ii = rows(); ii--; ) {
for (jj = cols(); jj--; ) {
elt(ii,jj) = rhs.elt(ii,jj);
}
}
return *this;
}
template<class T>
matrix_base<T> &matrix_base<T>::operator=(const rowref<T> &rhs)
{
if ((rows() != 1) || (cols() != rhs.m.cols())) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii;
for (ii = cols(); ii--; ) {
elt(0,ii) = rhs[ii];
}
return *this;
}
template<class T>
matrix_base<T> &matrix_base<T>::operator=(const colref<T> &rhs)
{
if ((cols() != 1) || (rows() != rhs.m.rows())) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii;
T t;
for (ii = rows(); ii--; ) {
t = rhs[ii];
elt(ii,0) = t;
}
return *this;
}
template<class T>
matrix_base<T> &matrix_base<T>::
operator+=(const matrix_base<T> &rhs)
{
if ((rows() != rhs.rows()) || (cols() != rhs.cols())) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj;
for (ii = rows(); ii--; ) {
for (jj = cols(); jj--; ) {
elt(ii,jj) = elt(ii,jj) + rhs.elt(ii,jj);
}
}
return *this;
}
template<class T>
matrix<T> operator+(const matrix_base<T> &op1,
const matrix_base<T> &op2)
{
if ((op1.rows() != op2.rows()) || (op1.cols() != op2.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
matrix<T> temp(op1);
return temp += op2;
}
template<class T>
void perform_mult_in_place(const matrix_base<T> &op1,
const matrix_base<T> &op2,
matrix_base<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
T t;
// Iterate over rows of op1.
for (ii = op1.rows(); ii--; ) {
// Iterate over colums of op2.
for (jj = op2.cols(); jj--; ) {
// Iterate through the row of r1 and the column of r2.
t = op1.elt(ii,op1.cols()-1) * op2.elt(op2.rows()-1,jj);
for (kk = op2.rows() - 1; kk--; ) {
t += op1.elt(ii,kk) * op2.elt(kk,jj);
}
// Assign into the result.
res.elt(ii,jj) = t;
}
}
}
template<class T>
void perform_mult_add_in_place(matrix_base<T> &op1,
matrix_base<T> &op2,
matrix_base<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
T t;
// Iterate over rows of op1.
for (ii = op1.rows(); ii--; ) {
// Iterate over colums of op2.
for (jj = op2.cols(); jj--; ) {
// Iterate through the row of r1 and the column of r2.
t = op1.elt(ii,op1.cols()-1) * op2.elt(op2.rows()-1,jj);
for (kk = op2.rows() - 1; kk--; ) {
t += op1.elt(ii,kk) * op2.elt(kk,jj);
}
// Add into the result.
res.elt(ii,jj) += t;
}
}
}
template<class T>
matrix<T> operator*(const matrix_base<T> &op1,
const matrix_base<T> &op2)
{
if (op1.cols() != op2.rows()) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
matrix<T> temp(op1.rows(),op2.cols());
perform_mult_in_place(op1, op2, (matrix_base<T> &)temp);
return temp;
}
template<class T>
ostream &operator<<(ostream &s, matrix_base<T> &m)
{
TPIE_OS_SIZE_T ii,jj;
// Iterate over rows
for (ii = 0; ii < m.rows(); ii++) {
// Iterate over cols
s << m.elt(ii,0);
for (jj = 1; jj < m.cols(); jj++) {
if (jj) (s << ' ');
s << m.elt(ii,jj);
}
s << '\n';
}
return s;
}
// Member functions for row and column reference classes.
template<class T>
rowref<T>::rowref(matrix_base<T> &amatrix, TPIE_OS_SIZE_T row) :
m(amatrix),
r(row)
{
}
template<class T>
rowref<T>::~rowref(void)
{
}
template<class T>
T &rowref<T>::operator[](const TPIE_OS_SIZE_T col) const
{
return m.elt(r,col);
}
template<class T>
colref<T>::colref(matrix_base<T> &amatrix, TPIE_OS_SIZE_T col) :
m(amatrix),
c(col)
{
}
template<class T>
colref<T>::~colref(void)
{
}
template<class T>
T &colref<T>::operator[](const TPIE_OS_SIZE_T row) const
{
return m.elt(row,c);
}
// A submatrix class.
template<class T>
class submatrix : public matrix_base<T>
{
private:
matrix_base<T> &m;
TPIE_OS_SIZE_T r1,r2,c1,c2;
public:
using matrix_base<T>::rows;
using matrix_base<T>::cols;
// Construction/destruction.
submatrix(matrix_base<T> &amatrix,
TPIE_OS_SIZE_T row1, TPIE_OS_SIZE_T row2,
TPIE_OS_SIZE_T col1, TPIE_OS_SIZE_T col2);
virtual ~submatrix(void);
// We need an assignement operator that copies data by explicitly
// calling the base class's assignment operator to do elementwise
// copying. Otherwise, m, r1, r2, c1, and c2 are just copied.
submatrix<T> &operator=(const submatrix<T> &rhs);
// We also want to be able to assign from matrices.
submatrix<T> &operator=(const matrix<T> &rhs);
// Access to elements.
T& elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const;
};
template<class T>
submatrix<T>::submatrix(matrix_base<T> &amatrix,
TPIE_OS_SIZE_T row1, TPIE_OS_SIZE_T row2,
TPIE_OS_SIZE_T col1, TPIE_OS_SIZE_T col2) :
matrix_base<T>(row2 - row1 + 1,
col2 - col1 + 1),
m(amatrix),
r1(row1), r2(row2),
c1(col1), c2(col2)
{
}
template<class T>
submatrix<T>::~submatrix(void)
{
}
template<class T>
submatrix<T> &submatrix<T>::operator=(const submatrix<T> &rhs)
{
// Call the assignement operator from the base class to do range
// checking and elementwise assignment.
(matrix_base<T> &)(*this) = (matrix_base<T> &)rhs;
return *this;
}
template<class T>
submatrix<T> &submatrix<T>::operator=(const matrix<T> &rhs)
{
// Call the assignement operator from the base class to do range
// checking and elementwise assignment.
(matrix_base<T> &)(*this) = (matrix_base<T> &)rhs;
return *this;
}
template<class T>
T& submatrix<T>::elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const
{
if ((row >= rows()) || (col >= cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
return m.elt(row + r1, col + c1);
}
// The matrix class itself.
template<class T>
class matrix : public matrix_base<T> {
private:
using matrix_base<T>::r;
using matrix_base<T>::c;
T *data;
public:
using matrix_base<T>::rows;
using matrix_base<T>::cols;
// Construction/destruction.
matrix(TPIE_OS_SIZE_T arows, TPIE_OS_SIZE_T acols);
matrix(const matrix<T> &rhs);
matrix(const matrix_base<T> &rhs);
matrix(const submatrix<T> &rhs);
matrix(const rowref<T> umrr);
matrix(const colref<T> umcr);
virtual ~matrix(void);
// We need an assignement operator that copies data by explicitly
// calling the base class's assignment operator to do elementwise
// copying. Otherwise, the data pointer is just copied.
matrix<T> &operator=(const matrix<T> &rhs);
// We also want to be able to assign from submatrices.
matrix<T> &operator=(const submatrix<T> &rhs);
// Access to elements.
T &elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const;
// Friends that need direct access to data for fast multiplication.
// friend void quick_matrix_mult_in_place(const matrix<T> &op1,
// const matrix<T> &op2,
// matrix<T> &res);
// friend void quick_matrix_mult_add_in_place(const matrix<T> &op1,
// const matrix<T> &op2,
// matrix<T> &res);
// friend void aggarwal_matrix_mult_in_place(const matrix<T> &op1,
// const matrix<T> &op2,
// matrix<T> &res);
// friend void aggarwal_matrix_mult_add_in_place(const matrix<T> &op1,
// const matrix<T> &op2,
// matrix<T> &res);
};
template<class T>
matrix<T>::matrix(TPIE_OS_SIZE_T arows, TPIE_OS_SIZE_T acols) :
matrix_base<T>(arows, acols)
{
data = new T[arows * acols];
// Initialize the contents of the matrix.
memset(data, 0, arows * acols * sizeof(T));
}
template<class T>
matrix<T>::matrix(const matrix<T> &rhs) :
matrix_base<T>(rhs.rows(), rhs.cols())
{
TPIE_OS_SIZE_T ii;
data = new T[r*c];
for (ii = r*c; ii--; ) {
data[ii] = rhs.data[ii];
}
}
template<class T>
matrix<T>::matrix(const matrix_base<T> &rhs) :
matrix_base<T>(rhs.rows(), rhs.cols())
{
TPIE_OS_SIZE_T ii,jj;
data = new T[r*c];
for (ii = r; ii--; ) {
for (jj = c; jj--; ) {
data[c*ii+jj] = ((matrix_base<T> &)rhs).elt(ii,jj);
}
}
}
template<class T>
matrix<T>::matrix(const submatrix<T> &rhs) :
matrix_base<T>(rhs.rows(), rhs.cols())
{
TPIE_OS_SIZE_T ii,jj;
data = new T[r*c];
for (ii = r; ii--; ) {
for (jj = c; jj--; ) {
data[c*ii+jj] = ((submatrix<T> &)rhs).elt(ii,jj);
}
}
}
template<class T>
matrix<T>::matrix(const rowref<T> umrr) :
matrix_base<T>(1, umrr.m.cols())
{
data = new T[c];
matrix_base<T>::operator=(umrr);
}
template<class T>
matrix<T>::matrix(const colref<T> umcr) :
matrix_base<T>(umcr.m.rows(),1)
{
data = new T[r];
matrix_base<T>::operator=(umcr);
}
template<class T>
matrix<T>::~matrix(void) {
delete[] data;
}
template<class T>
matrix<T> &matrix<T>::operator=(const matrix<T> &rhs)
{
// Call the assignement operator from the base class to do range
// checking and elementwise assignment.
(matrix_base<T> &)(*this) = (matrix_base<T> &)rhs;
return *this;
}
template<class T>
matrix<T> &matrix<T>::operator=(const submatrix<T> &rhs)
{
// Call the assignement operator from the base class to do range
// checking and elementwise assignment.
(matrix_base<T> &)(*this) = (matrix_base<T> &)rhs;
return *this;
}
template<class T>
T& matrix<T>::elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const
{
if ((row >= rows()) || (col >= cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
return data[row*cols()+col];
}
// These are needed since template functions accept only exact argument
// type matches. Base class promotion is not done as it is for
// ordinary functions.
#define MAT_DUMMY_OP(TM1,TM2,OP) \
template<class T> \
matrix<T> operator OP (const TM1 &op1, \
const TM2 &op2) \
{ \
return ((matrix_base<T> &)op1) OP \
((matrix_base<T> &)op2); \
}
MAT_DUMMY_OP(matrix<T>,matrix<T>,+)
MAT_DUMMY_OP(matrix<T>,submatrix<T>,+)
MAT_DUMMY_OP(submatrix<T>,matrix<T>,+)
MAT_DUMMY_OP(submatrix<T>,submatrix<T>,+)
MAT_DUMMY_OP(matrix<T>,matrix<T>,*)
MAT_DUMMY_OP(matrix<T>,submatrix<T>,*)
MAT_DUMMY_OP(submatrix<T>,matrix<T>,*)
MAT_DUMMY_OP(submatrix<T>,submatrix<T>,*)
template<class T>
ostream &operator<<(ostream &s, const matrix<T> &m)
{
return s << (matrix_base<T> &)m;
}
template<class T>
ostream &operator<<(ostream &s, const submatrix<T> &m)
{
return s << (matrix_base<T> &)m;
}
// Speedups for multiplying matrices. This is only for use with the
// specific implementation of matrices above. General purpose
// multiplication still has to be done with perform_mult_in_place or
// perform_mult_add_in_place.
template<class T>
void quick_matrix_mult_in_place(const matrix<T> &op1,
const matrix<T> &op2,
matrix<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
TPIE_OS_SIZE_T r1,r2,c1,c2,cres;
T t;
r1 = op1.rows();
r2 = op2.rows();
c1 = op1.cols();
c2 = op2.cols();
cres = res.cols();
// Iterate over rows of op1.
for (ii = r1; ii--; ) {
// Iterate over colums of op2.
for (jj = c2; jj--; ) {
// Iterate through the row of r1 and the column of r2.
// t = op1.data[ii*c1+c1-1] * op2.data[(r2-1)*c2+jj];
// // op1.elt(ii,op1.cols()-1) * op2.elt(op2.rows()-1,jj);
t = op1.elt(ii,c1-1) * op2.elt(r2-1,jj);
for (kk = r2 - 1; kk--; ) {
// t += op1.data[ii*c1+kk] * op2.data[kk*c2+jj];
// // op1.elt(ii,kk) * op2.elt(kk,jj);
t += op1.elt(ii,kk) * op2.elt(kk,jj);
}
// Assign into the result.
// res.data[ii*cres+jj] = t;
res.elt(ii,jj) = t;
}
}
}
template<class T>
void quick_matrix_mult_add_in_place(const matrix<T> &op1,
const matrix<T> &op2,
matrix<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
TPIE_OS_SIZE_T r1,r2,c1,c2,cres;
T t;
r1 = op1.rows();
r2 = op2.rows();
c1 = op1.cols();
c2 = op2.cols();
cres = res.cols();
// Iterate over rows of op1.
for (ii = r1; ii--; ) {
// Iterate over colums of op2.
for (jj = c2; jj--; ) {
// Iterate through the row of r1 and the column of r2.
// t = op1.data[ii*c1+c1-1] * op2.data[(r2-1)*c2+jj];
// // op1.elt(ii,op1.cols()-1) * op2.elt(op2.rows()-1,jj);
t = op1.elt(ii,c1-1) * op2.elt(r2-1,jj);
for (kk = r2 - 1; kk--; ) {
// t += op1.data[ii*c1+kk] * op2.data[kk*c2+jj];
t += op1.elt(ii,kk) * op2.elt(kk,jj);
}
// Assign into the result.
// res.data[ii*cres+jj] += t;
res.elt(ii,jj) += t;
}
}
}
// Aggarwal et. al.'s algorithm.
template<class T>
void aggarwal_matrix_mult_in_place(const matrix<T> &op1,
const matrix<T> &op2,
matrix<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
TPIE_OS_SIZE_T r1,r2,c1,c2,cres;
r1 = op1.rows();
r2 = op2.rows();
c1 = op1.cols();
c2 = op2.cols();
cres = res.cols();
// Temporary results.
T *temp = new T[c2];
T op1elt;
// Iterate over rows of op1.
for (ii = r1; ii--; ) {
// Clear out the temporary sums.
for (jj = c2; jj--; ) {
temp[jj] = 0;
}
// Iterate through the row of r1 and the column of r2.
for (kk = r2; kk--; ) {
// Iterate over columns of op2.
// op1elt = op1.data[ii*c1+kk];
op1elt = op1.elt(ii,kk);
for (jj = c2; jj--; ) {
// temp[jj] += op1elt * op2.data[kk*c2+jj];
temp[jj] += op1elt * op2.elt(kk,jj);
}
}
// Set the results.
for (jj = c2; jj--; ) {
// res.data[ii*cres+jj] = temp[jj];
res.elt(ii,jj) = temp[jj];
}
}
delete [] temp;
}
template<class T>
void aggarwal_matrix_mult_add_in_place(const matrix<T> &op1,
const matrix<T> &op2,
matrix<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
TPIE_OS_SIZE_T r1,r2,c1,c2,cres;
r1 = op1.rows();
r2 = op2.rows();
c1 = op1.cols();
c2 = op2.cols();
cres = res.cols();
// Temporary results.
T *temp = new T[c2];
T op1elt;
// Iterate over rows of op1.
for (ii = r1; ii--; ) {
// Clear out the temporary sums.
for (jj = c2; jj--; ) {
temp[jj] = 0;
}
// Iterate through the row of r1 and the column of r2.
for (kk = r2; kk--; ) {
// Iterate over columns of op2.
// op1elt = op1.data[ii*c1+kk];
op1elt = op1.elt(ii,kk);
for (jj = c2; jj--; ) {
// temp[jj] += op1elt * op2.data[kk*c2+jj];
temp[jj] += op1elt * op2.elt(kk,jj);
}
}
// Set the results.
for (jj = c2; jj--; ) {
// res.data[ii*cres+jj] += temp[jj];
res.elt(ii,jj) += temp[jj];
}
}
delete [] temp;
}
#endif // MATRIX_H
+254
View File
@@ -0,0 +1,254 @@
//
// File: mergeheap.h
// Author: Rakesh Barve <rbarve@cs.duke.edu>
//
// Id: mergeheap.h,v 1.6 1999/11/23 16:49:10 tavi Exp tavi $
// A template useful during merge operations. Basically a heap can be
// maintained on keys; so that the log n comparisons per item involve
// touching an array of keys; not items. The heap is basically the
// heap from CLR except that there is provision to exploit the fact
// that when you are merging you know you will be inserting a new
// element whenever you are doing a delete_min.
//
#ifndef _MERGE_HEAP_H
#define _MERGE_HEAP_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Macros for left and right.
#define Left(i) 2*i
#define Right(i) 2*i+1
#define Parent(i) i/2
//This is a heap element. Meant to encapsulate the key, along with
//the label run_id indicating the run the key originates from.
template<class Key>
class merge_heap_element {
public:
Key key;
unsigned short run_id;
};
/*
TPIE_OS_WIN_ONLY_TEMPLATE_MERGE_HEAP_ELEMENT_COMPILER_FOOLER
*/
//This is the actual heap; there is a constructor, destructor and
//various useful public member functions.
template<class Key>
class merge_heap{
merge_heap_element<Key> *Heaparray;
TPIE_OS_SIZE_T Heapsize;
void Exchange(TPIE_OS_SIZE_T i, TPIE_OS_SIZE_T j) {
Key tmpkey;
unsigned short tmpid;
tmpkey = Heaparray[i].key;
tmpid = Heaparray[i].run_id;
Heaparray[i].key = Heaparray[j].key;
Heaparray[i].run_id = Heaparray[j].run_id;
Heaparray[j].key = tmpkey;
Heaparray[j].run_id = tmpid;
};
inline void Heapify(TPIE_OS_SIZE_T i);
public:
// Constructor
merge_heap(merge_heap_element<Key> *array_of_elements,
TPIE_OS_SIZE_T array_size);
// Destructor
~merge_heap(void) {if (Heaparray) {delete Heaparray; Heaparray = NULL;}};
// Report size of Heap (number of elements)
TPIE_OS_SIZE_T sizeofheap(void) {return Heapsize;};
//Delete the current minimum and insert the new item from
//the same source / run.
void delete_min_and_insert(Key *nextelement_same_run){
if (nextelement_same_run == NULL) {
Heaparray[1].key = Heaparray[Heapsize].key;
Heaparray[1].run_id = Heaparray[Heapsize].run_id;
Heapsize--;
} else
Heaparray[1].key = *nextelement_same_run;
this->Heapify(1);
};
// Return the minimum key.
Key get_min_key(void) {return Heaparray[1].key;};
//Return the run with the minimum key.
unsigned short get_min_run_id(void) {return Heaparray[1].run_id;};
};
// This is the primary function; note that we have unfolded the
// recursion.
template<class Key>
inline void merge_heap<Key>::Heapify(TPIE_OS_SIZE_T i) {
TPIE_OS_SIZE_T l,r, smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) &&
(Heaparray[l].key < Heaparray[i].key)) ? l : i;
smallest = ((r <= Heapsize) &&
(Heaparray[r].key < Heaparray[smallest].key))? r : smallest;
while (smallest != i) {
this->Exchange(i,smallest);
i = smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) &&
(Heaparray[l].key < Heaparray[i].key))? l : i;
smallest = ((r <= Heapsize) &&
(Heaparray[r].key < Heaparray[smallest].key))? r : smallest;
}
}
//Constructor
template<class Key>
merge_heap<Key>::merge_heap(merge_heap_element<Key> *array_of_elements,
TPIE_OS_SIZE_T size_of_array) {
TPIE_OS_SIZE_T i;
Heapsize = size_of_array;
Heaparray = array_of_elements;
for ( i = Heapsize/2; i >= 1; i--)
this->Heapify(i);
}
template<class Key>
class merge_heap_cmp {
class merge_heap_element<Key> *Heaparray;
int (*cmp)(const Key&, const Key&);
TPIE_OS_SIZE_T Heapsize;
void Exchange(TPIE_OS_SIZE_T i, TPIE_OS_SIZE_T j){
Key tmpkey;
unsigned short tmpid;
tmpkey = Heaparray[i].key;
tmpid = Heaparray[i].run_id;
Heaparray[i].key = Heaparray[j].key;
Heaparray[i].run_id = Heaparray[j].run_id;
Heaparray[j].key = tmpkey;
Heaparray[j].run_id = tmpid;
};
inline void Heapify(TPIE_OS_SIZE_T i);
public:
// Constructor
merge_heap_cmp(class merge_heap_element<Key> *array_of_elements,
TPIE_OS_SIZE_T array_size, int (*comp_fun)(const Key&, const Key&));
// Destructor
~merge_heap_cmp(void) {if (Heaparray) {delete Heaparray; Heaparray = NULL;}};
// Report size of Heap (number of elements)
TPIE_OS_SIZE_T sizeofheap(void) {return Heapsize;};
// Delete the current minimum and insert the new item from
// the same source / run.
void delete_min_and_insert(Key *nextelement_same_run) {
if (nextelement_same_run == NULL) {
Heaparray[1].key = Heaparray[Heapsize].key;
Heaparray[1].run_id = Heaparray[Heapsize].run_id;
Heapsize--;
} else Heaparray[1].key = *nextelement_same_run;
this->Heapify(1);
};
//Return the minimum key.
Key get_min_key(void) {return Heaparray[1].key;};
//Return the run with the minimum key.
unsigned short get_min_run_id(void) {return Heaparray[1].run_id;};
};
// This is the primary function; note that we have unfolded the
// recursion.
template<class Key>
inline void merge_heap_cmp<Key>::Heapify(TPIE_OS_SIZE_T i) {
TPIE_OS_SIZE_T l,r, smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) && (cmp(Heaparray[l].key,Heaparray[i].key)< 0)) ? l : i;
smallest = ((r <= Heapsize) &&
(cmp(Heaparray[r].key,Heaparray[smallest].key)<0))? r : smallest;
while (smallest != i) {
this->Exchange(i,smallest);
i = smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) &&
(cmp(Heaparray[l].key,Heaparray[i].key)<0))? l : i;
smallest = ((r <= Heapsize) &&
(cmp(Heaparray[r].key,Heaparray[smallest].key)<0))? r : smallest;
}
}
// Constructor
template<class Key>
merge_heap_cmp<Key>::merge_heap_cmp(class merge_heap_element<Key> *array_of_elements,
TPIE_OS_SIZE_T size_of_array,
int (*comp_fun)(const Key&, const Key&)) {
TPIE_OS_SIZE_T i;
Heapsize = size_of_array;
Heaparray = array_of_elements;
cmp = comp_fun;
for ( i = Heapsize/2; i >= 1; i--)
this->Heapify(i);
}
#undef Left
#undef Right
#undef Parent
#endif // _MERGE_HEAP_H
+827
View File
@@ -0,0 +1,827 @@
//
// File: mergeheap_dh.h
//
// $Id: mergeheap_dh.h,v 1.9 2005/08/24 19:32:38 adanner Exp $
// This file contains several merge heap templates.
// Originally written by Rakesh Barve.
// The heap is basically the heap from CLR except that there is
// provision to exploit the fact that when you are merging you know
// you will be inserting a new element whenever you are doing a
// delete_min.
// Modified by David Hutchinson 2000 03 02
// - main purpose of the mods is to allow the merge heap to be
// part of a sort management object. The sort management object
// contains several procedures and data structures needed for
// sorting, but the precise versions of these procedures and data
// structures depend on the sorting approach used (this permits
// parameterization of the sorting procedure via the sort
// management object, and avoids having multiple versions of large
// pieces of code that are highly redundant and difficult to
// maintain).
// - move initialization from constructor to an explicit
// "initialize" member function
// - add a "comparison object" version of the merge heap
// object. This allows a comparison object with a "compare" member
// function to be specified for comparing keys. "Comparison
// operator" and "comparison function" versions previously
// existed.
// - add a set of three (comparison object, operator and function)
// versions of the merge heap that maintain pointers to the
// current records at the head of the streams being merged. The
// previous versions kept the entire corresponding record in the heap.
#ifndef _MERGE_HEAP_DH_H
#define _MERGE_HEAP_DH_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Macros for left and right.
#define Left(i) 2*(i)
#define Right(i) 2*(i)+1
#define Parent(i) (i)/2
// This is a heap element. Encapsulates the key, along with
// the label run_id indicating the run the key originates from.
template<class KEY>
class heap_element {
public:
heap_element(){};
KEY key;
TPIE_OS_SIZE_T run_id;
};
// This is a record pointer element. Encapsulates the record pointer,
// along with the label run_id indicating the run the record
// originates from.
template<class REC>
class heap_ptr {
public:
heap_ptr(){};
~heap_ptr(){};
REC *recptr;
TPIE_OS_SIZE_T run_id;
};
// ********************************************************************
// * A record pointer heap base class - also serves as the full *
// * implementation for objects with a < comparison operator *
// ********************************************************************
template<class REC>
class merge_heap_pdh_op{
protected:
heap_ptr<REC> *Heaparray;
TPIE_OS_SIZE_T Heapsize;
TPIE_OS_SIZE_T maxHeapsize;
inline void Exchange(TPIE_OS_SIZE_T i, TPIE_OS_SIZE_T j);
//These functions will typically be overridden by subclasses
inline TPIE_OS_SIZE_T get_smallest(TPIE_OS_SIZE_T i);
inline void Heapify(TPIE_OS_SIZE_T i);
public:
// Constructor/Destructor
merge_heap_pdh_op() { Heaparray=NULL; };
~merge_heap_pdh_op() {
//Cleanup if someone forgot de-allocate
//(abd) This seems to cause double free errors, but I don't know why
//This was just a safeguard anyways, turn off for now
//if(Heaparray != NULL){delete [] Heaparray;}
}
// Report size of Heap (number of elements)
TPIE_OS_SIZE_T sizeofheap(void) {return Heapsize;};
// Return the run with the minimum key.
inline TPIE_OS_SIZE_T get_min_run_id(void) {return Heaparray[1].run_id;};
void allocate (TPIE_OS_SIZE_T size);
void insert (REC *ptr, TPIE_OS_SIZE_T run_id);
void deallocate (void);
// heapify's an initial array of elements
// typically overridden in sub class.
void initialize (void);
// Delete the current minimum and insert the new item from the same
// source / run.
inline void delete_min_and_insert(REC *nextelement_same_run);
// Return main memory space usage per item
inline TPIE_OS_SIZE_T space_per_item(void) { return sizeof(heap_ptr<REC>); }
// Return fixed main memory space overhead, regardless of item count
inline TPIE_OS_SIZE_T space_overhead(void) {
// One extra array item is defined to make heap indexing easier
return sizeof(heap_ptr<REC>)+MM_manager.space_overhead();
}
};
template<class REC>
inline void merge_heap_pdh_op<REC>::Exchange(TPIE_OS_SIZE_T i,
TPIE_OS_SIZE_T j)
{
REC* tmpptr;
TPIE_OS_SIZE_T tmpid;
tmpptr = Heaparray[i].recptr;
tmpid = Heaparray[i].run_id;
Heaparray[i].recptr = Heaparray[j].recptr;
Heaparray[i].run_id = Heaparray[j].run_id;
Heaparray[j].recptr = tmpptr;
Heaparray[j].run_id = tmpid;
}
//Returns the index of the smallest element out of
//i, the left child of i, and the right child of i
template<class REC>
inline TPIE_OS_SIZE_T merge_heap_pdh_op<REC>::get_smallest(
TPIE_OS_SIZE_T i)
{
TPIE_OS_SIZE_T l,r, smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) &&
(*Heaparray[l].recptr < *Heaparray[i].recptr)) ? l : i;
smallest = ((r <= Heapsize) &&
(*Heaparray[r].recptr < *Heaparray[smallest].recptr))? r : smallest;
return smallest;
}
// This is the primary function; note that we have unfolded the
// recursion.
template<class REC>
inline void merge_heap_pdh_op<REC>::Heapify(TPIE_OS_SIZE_T i) {
TPIE_OS_SIZE_T smallest = get_smallest(i);
while (smallest != i) {
this->Exchange(i,smallest);
i = smallest;
smallest = get_smallest(i);
}
}
template<class REC>
inline void merge_heap_pdh_op<REC>::delete_min_and_insert
(REC *nextelement_same_run)
{
if (nextelement_same_run == NULL) {
Heaparray[1].recptr = Heaparray[Heapsize].recptr;
Heaparray[1].run_id = Heaparray[Heapsize].run_id;
Heapsize--;
} else {
Heaparray[1].recptr = nextelement_same_run;
}
Heapify(1);
}
// Allocate space for the heap
template<class REC>
inline void merge_heap_pdh_op<REC>::allocate ( TPIE_OS_SIZE_T size ) {
Heaparray = new heap_ptr<REC> [size+1];
Heapsize = 0;
maxHeapsize = size;
}
// Copy an (initial) element into the heap array
template<class REC>
inline void merge_heap_pdh_op<REC>::insert (REC *ptr, TPIE_OS_SIZE_T run_id)
{
Heaparray[Heapsize+1].recptr = ptr;
Heaparray[Heapsize+1].run_id = run_id;
Heapsize++;
}
// Deallocate the space used by the heap
template<class REC>
inline void merge_heap_pdh_op<REC>::deallocate () {
if (Heaparray){
delete [] Heaparray;
Heaparray=NULL;
}
Heapsize = 0;
maxHeapsize = 0;
}
template<class REC>
void merge_heap_pdh_op<REC>::initialize () {
for ( TPIE_OS_SIZE_T i = Heapsize/2; i >= 1; i--){ Heapify(i); }
}
// ********************************************************************
// * A record pointer heap that uses a comparison object *
// ********************************************************************
template<class REC, class CMPR>
class merge_heap_pdh_obj: public merge_heap_pdh_op<REC>{
protected:
using merge_heap_pdh_op<REC>::Heapsize;
using merge_heap_pdh_op<REC>::Heaparray;
using merge_heap_pdh_op<REC>::maxHeapsize;
CMPR* cmp;
inline TPIE_OS_SIZE_T get_smallest(TPIE_OS_SIZE_T i);
inline void Heapify(TPIE_OS_SIZE_T i);
public:
using merge_heap_pdh_op<REC>::sizeofheap;
// Constructor initializes a pointer to the user's comparison object
// The object may contain dynamic data although the 'compare' method is const
// and therefore inline'able.
merge_heap_pdh_obj ( CMPR *cmptr ) : cmp(cmptr) {};
~merge_heap_pdh_obj(){};
void initialize (void);
// Delete the current minimum and insert the new item from the same
// source / run.
inline void delete_min_and_insert(REC *nextelement_same_run);
};
//Returns the index of the smallest element out of
//i, the left child of i, and the right child of i
template<class REC, class CMPR>
inline TPIE_OS_SIZE_T merge_heap_pdh_obj<REC,CMPR>::get_smallest(
TPIE_OS_SIZE_T i)
{
TPIE_OS_SIZE_T l,r, smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) &&
(cmp->compare(*Heaparray[l].recptr,*Heaparray[i].recptr)< 0)) ? l : i;
smallest = ((r <= Heapsize) &&
(cmp->compare(*Heaparray[r].recptr,*Heaparray[smallest].recptr)<0))?
r : smallest;
return smallest;
}
template<class REC, class CMPR>
inline void merge_heap_pdh_obj<REC, CMPR>::Heapify(TPIE_OS_SIZE_T i) {
TPIE_OS_SIZE_T smallest = get_smallest(i);
while (smallest != i) {
this->Exchange(i,smallest);
i = smallest;
smallest = get_smallest(i);
}
}
template<class REC, class CMPR>
inline void merge_heap_pdh_obj<REC, CMPR>::delete_min_and_insert
(REC *nextelement_same_run)
{
if (nextelement_same_run == NULL) {
Heaparray[1].recptr = Heaparray[Heapsize].recptr;
Heaparray[1].run_id = Heaparray[Heapsize].run_id;
Heapsize--;
} else {
Heaparray[1].recptr = nextelement_same_run;
}
Heapify(1);
}
template<class REC, class CMPR>
void merge_heap_pdh_obj<REC, CMPR>::initialize () {
for ( TPIE_OS_SIZE_T i = Heapsize/2; i >= 1; i--){ Heapify(i); }
}
// ********************************************************************
// * A merge heap object base class - also serves as the full *
// * implementation for objects with a < comparison operator *
// ********************************************************************
template<class REC>
class merge_heap_dh_op{
protected:
heap_element<REC> *Heaparray;
TPIE_OS_SIZE_T Heapsize;
TPIE_OS_SIZE_T maxHeapsize;
inline void Exchange(TPIE_OS_SIZE_T i, TPIE_OS_SIZE_T j);
inline void Heapify(TPIE_OS_SIZE_T i);
//This function will typically be overridden by subclasses
inline TPIE_OS_SIZE_T get_smallest(TPIE_OS_SIZE_T i);
public:
// Constructor/Destructor
merge_heap_dh_op() { Heaparray=NULL; };
~merge_heap_dh_op() {
//Cleanup if someone forgot de-allocate
//(abd) This seems to cause double free errors, but I don't know why
//This was just a safeguard anyways, turn off for now
//if(Heaparray != NULL){delete [] Heaparray;}
}
// Report size of Heap (number of elements)
TPIE_OS_SIZE_T sizeofheap(void) {return Heapsize;};
// Return the run with the minimum key.
inline TPIE_OS_SIZE_T get_min_run_id(void) {return Heaparray[1].run_id;};
void allocate (TPIE_OS_SIZE_T size);
void insert (REC *ptr, TPIE_OS_SIZE_T run_id);
void deallocate (void);
// heapify's an initial array of elements
void initialize (void);
// Delete the current minimum and insert the new item from the same
// source / run.
inline void delete_min_and_insert(REC *nextelement_same_run);
// Return main memory space usage per item
inline TPIE_OS_SIZE_T space_per_item(void) {
return sizeof(heap_element<REC>);
}
// Return fixed main memory space overhead, regardless of item count
inline TPIE_OS_SIZE_T space_overhead(void) {
// One extra array item is defined to make heap indexing easier
return sizeof(heap_element<REC>)+MM_manager.space_overhead();
}
};
template<class REC>
inline void merge_heap_dh_op<REC>::Exchange(TPIE_OS_SIZE_T i,
TPIE_OS_SIZE_T j)
{
REC tmpkey;
TPIE_OS_SIZE_T tmpid;
tmpkey = Heaparray[i].key;
tmpid = Heaparray[i].run_id;
Heaparray[i].key = Heaparray[j].key;
Heaparray[i].run_id = Heaparray[j].run_id;
Heaparray[j].key = tmpkey;
Heaparray[j].run_id = tmpid;
}
//Returns the index of the smallest element out of
//i, the left child of i, and the right child of i
template<class REC>
inline TPIE_OS_SIZE_T merge_heap_dh_op<REC>::get_smallest(
TPIE_OS_SIZE_T i)
{
TPIE_OS_SIZE_T l,r, smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) &&
(Heaparray[l].key < Heaparray[i].key)) ? l : i;
smallest = ((r <= Heapsize) &&
(Heaparray[r].key < Heaparray[smallest].key))? r : smallest;
return smallest;
}
// This is the primary function; note that we have unfolded the
// recursion.
template<class REC>
inline void merge_heap_dh_op<REC>::Heapify(TPIE_OS_SIZE_T i) {
TPIE_OS_SIZE_T smallest = get_smallest(i);
while (smallest != i) {
this->Exchange(i,smallest);
i = smallest;
smallest = get_smallest(i);
}
}
template<class REC>
inline void merge_heap_dh_op<REC>::delete_min_and_insert
(REC *nextelement_same_run)
{
if (nextelement_same_run == NULL) {
Heaparray[1].key = Heaparray[Heapsize].key;
Heaparray[1].run_id = Heaparray[Heapsize].run_id;
Heapsize--;
} else {
Heaparray[1].key = *nextelement_same_run;
}
Heapify(1);
}
// Allocate space for the heap
template<class REC>
inline void merge_heap_dh_op<REC>::allocate ( TPIE_OS_SIZE_T size ) {
Heaparray = new heap_element<REC> [size+1];
Heapsize = 0;
maxHeapsize = size;
}
// Copy an (initial) element into the heap array
template<class REC>
inline void merge_heap_dh_op<REC>::insert (REC *ptr, TPIE_OS_SIZE_T run_id)
{
Heaparray[Heapsize+1].key = *ptr;
Heaparray[Heapsize+1].run_id = run_id;
Heapsize++;
}
// Deallocate the space used by the heap
template<class REC>
inline void merge_heap_dh_op<REC>::deallocate () {
if (Heaparray){
delete [] Heaparray;
Heaparray=NULL;
}
Heapsize = 0;
maxHeapsize = 0;
};
template<class REC>
void merge_heap_dh_op<REC>::initialize () {
for ( TPIE_OS_SIZE_T i = Heapsize/2; i >= 1; i--){ Heapify(i); }
}
// ********************************************************************
// * A merge heap that uses a comparison object *
// ********************************************************************
template<class REC, class CMPR>
class merge_heap_dh_obj: public merge_heap_dh_op<REC>{
protected:
using merge_heap_dh_op<REC>::Heapsize;
using merge_heap_dh_op<REC>::Heaparray;
using merge_heap_dh_op<REC>::maxHeapsize;
CMPR* cmp;
inline TPIE_OS_SIZE_T get_smallest(TPIE_OS_SIZE_T i);
inline void Heapify(TPIE_OS_SIZE_T i);
public:
using merge_heap_dh_op<REC>::sizeofheap;
// Constructor initializes a pointer to the user's comparison object
// The object may contain dynamic data although the 'compare' method is const
// and therefore inline'able.
merge_heap_dh_obj ( CMPR *cmptr ) : cmp(cmptr) {};
~merge_heap_dh_obj(){};
// heapify's an initial array of elements
void initialize (void);
// Delete the current minimum and insert the new item from the same
// source / run.
inline void delete_min_and_insert(REC *nextelement_same_run);
};
//Returns the index of the smallest element out of
//i, the left child of i, and the right child of i
template<class REC, class CMPR>
inline TPIE_OS_SIZE_T merge_heap_dh_obj<REC,CMPR>::get_smallest(
TPIE_OS_SIZE_T i)
{
TPIE_OS_SIZE_T l,r, smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) &&
(cmp->compare(Heaparray[l].key,Heaparray[i].key)< 0)) ? l : i;
smallest = ((r <= Heapsize) &&
(cmp->compare(Heaparray[r].key,Heaparray[smallest].key)<0))?
r : smallest;
return smallest;
}
template<class REC, class CMPR>
inline void merge_heap_dh_obj<REC, CMPR>::Heapify(TPIE_OS_SIZE_T i) {
TPIE_OS_SIZE_T smallest = get_smallest(i);
while (smallest != i) {
this->Exchange(i,smallest);
i = smallest;
smallest = get_smallest(i);
}
}
template<class REC, class CMPR>
inline void merge_heap_dh_obj<REC, CMPR>::delete_min_and_insert
(REC *nextelement_same_run)
{
if (nextelement_same_run == NULL) {
Heaparray[1].key = Heaparray[Heapsize].key;
Heaparray[1].run_id = Heaparray[Heapsize].run_id;
Heapsize--;
} else {
Heaparray[1].key = *nextelement_same_run;
}
Heapify(1);
}
template<class REC, class CMPR>
void merge_heap_dh_obj<REC, CMPR>::initialize () {
for ( TPIE_OS_SIZE_T i = Heapsize/2; i >= 1; i--){ Heapify(i); }
}
// ********************************************************************
// * A merge heap key-object base class *
// * Also serves as a full impelementation of a *
// * key-merge heap that uses a comparison operator < *
// ********************************************************************
// The merge_heap_dh_kop object maintains only the keys in its heap,
// and uses the member function "copy" of the user-provided class CMPR
// to copy these keys from each record.
template<class REC, class KEY, class CMPR>
class merge_heap_dh_kop{
protected:
heap_element<KEY> *Heaparray;
TPIE_OS_SIZE_T Heapsize;
TPIE_OS_SIZE_T maxHeapsize;
inline void Exchange(TPIE_OS_SIZE_T i, TPIE_OS_SIZE_T j);
inline void Heapify(TPIE_OS_SIZE_T i);
//This function will typically be overridden by subclasses
inline TPIE_OS_SIZE_T get_smallest(TPIE_OS_SIZE_T i);
CMPR *UsrObject;
public:
// Constructor/Destructor
merge_heap_dh_kop( CMPR* cmpptr) : UsrObject(cmpptr), Heaparray(NULL) {};
~merge_heap_dh_kop() {
//Cleanup if someone forgot de-allocate
//(abd) This seems to cause double free errors, but I don't know why
//This was just a safeguard anyways, turn off for now
//if(Heaparray != NULL){delete [] Heaparray;}
}
// Report size of Heap (number of elements)
TPIE_OS_SIZE_T sizeofheap(void) {return Heapsize;};
// Return the run with the minimum key.
inline TPIE_OS_SIZE_T get_min_run_id(void) {return Heaparray[1].run_id;};
void allocate (TPIE_OS_SIZE_T size);
void insert (REC *ptr, TPIE_OS_SIZE_T run_id);
void deallocate ();
// Delete the current minimum and insert the new item from the same
// source / run.
inline void delete_min_and_insert(REC *nextelement_same_run);
// Return main memory space usage per item
inline TPIE_OS_SIZE_T space_per_item(void) {
return sizeof(heap_element<REC>);
}
// Return fixed main memory space overhead, regardless of item count
inline TPIE_OS_SIZE_T space_overhead(void) {
// One extra array item is defined to make heap indexing easier
return sizeof(heap_ptr<REC>)+MM_manager.space_overhead();
}
// heapify's an initial array of elements
void initialize (void);
};
template<class REC, class KEY, class CMPR>
inline void merge_heap_dh_kop<REC,KEY,CMPR>::Exchange(TPIE_OS_SIZE_T i,
TPIE_OS_SIZE_T j)
{
KEY tmpkey;
TPIE_OS_SIZE_T tmpid;
tmpkey = Heaparray[i].key;
tmpid = Heaparray[i].run_id;
Heaparray[i].key = Heaparray[j].key;
Heaparray[i].run_id = Heaparray[j].run_id;
Heaparray[j].key = tmpkey;
Heaparray[j].run_id = tmpid;
}
template<class REC, class KEY, class CMPR>
inline void merge_heap_dh_kop<REC,KEY,CMPR>::delete_min_and_insert
(REC *nextelement_same_run)
{
if (nextelement_same_run == NULL) {
Heaparray[1].key = Heaparray[Heapsize].key;
Heaparray[1].run_id = Heaparray[Heapsize].run_id;
Heapsize--;
} else {
UsrObject->copy(&Heaparray[1].key, *nextelement_same_run);
}
Heapify(1);
}
// Allocate space for the heap
template<class REC, class KEY, class CMPR>
inline void merge_heap_dh_kop<REC,KEY,CMPR>::allocate ( TPIE_OS_SIZE_T size ) {
Heaparray = new heap_element<KEY> [size+1];
Heapsize = 0;
maxHeapsize = size;
}
// Copy an (initial) element into the heap array
template<class REC, class KEY, class CMPR>
inline void merge_heap_dh_kop<REC,KEY,CMPR>::insert (REC *ptr,
TPIE_OS_SIZE_T run_id)
{
UsrObject->copy(&Heaparray[Heapsize+1].key, *ptr);
Heaparray[Heapsize+1].run_id = run_id;
Heapsize++;
}
// Deallocate the space used by the heap
template<class REC, class KEY, class CMPR>
inline void merge_heap_dh_kop<REC,KEY,CMPR>::deallocate () {
if (Heaparray){
delete [] Heaparray;
Heaparray=NULL;
}
Heapsize = 0;
maxHeapsize = 0;
};
//Returns the index of the smallest element out of
//i, the left child of i, and the right child of i
template<class REC, class KEY, class CMPR>
inline TPIE_OS_SIZE_T merge_heap_dh_kop<REC,KEY,CMPR>::get_smallest(
TPIE_OS_SIZE_T i)
{
TPIE_OS_SIZE_T l,r, smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) &&
(Heaparray[l].key < Heaparray[i].key)) ? l : i;
smallest = ((r <= Heapsize) &&
(Heaparray[r].key < Heaparray[smallest].key))? r : smallest;
return smallest;
}
// This is the primary function; note that we have unfolded the
// recursion.
template<class REC, class KEY, class CMPR>
inline void merge_heap_dh_kop<REC,KEY,CMPR>::Heapify(TPIE_OS_SIZE_T i) {
TPIE_OS_SIZE_T smallest=get_smallest(i);
while (smallest != i) {
this->Exchange(i,smallest);
i = smallest;
smallest = get_smallest(i);
}
}
template<class REC, class KEY, class CMPR>
void merge_heap_dh_kop<REC,KEY,CMPR>::initialize ( ) {
for ( TPIE_OS_SIZE_T i = Heapsize/2; i >= 1; i--)
this->Heapify(i);
}
// ********************************************************************
// * A key-merge heap that uses a comparison object *
// ********************************************************************
// The merge_heap_dh_kobj object maintains only the keys in its heap,
// and uses the member function "copy" of the user-provided class CMPR
// to copy these keys from each record. It uses the member function
// "compare" of the user-provided class CMPR to determine the relative
// order of two such keys in the sort order.
template<class REC, class KEY, class CMPR>
class merge_heap_dh_kobj: public merge_heap_dh_kop<REC,KEY,CMPR>{
protected:
using merge_heap_dh_kop<REC,KEY,CMPR>::Heapsize;
using merge_heap_dh_kop<REC,KEY,CMPR>::Heaparray;
using merge_heap_dh_kop<REC,KEY,CMPR>::maxHeapsize;
using merge_heap_dh_kop<REC,KEY,CMPR>::UsrObject;
inline TPIE_OS_SIZE_T get_smallest(TPIE_OS_SIZE_T i);
inline void Heapify(TPIE_OS_SIZE_T);
public:
using merge_heap_dh_kop<REC,KEY,CMPR>::sizeofheap;
// Constructor initializes a pointer to the user's comparison object
// The object may contain dynamic data although the 'compare' method is const
// and therefore inline'able.
merge_heap_dh_kobj ( CMPR *cmptr ) :
merge_heap_dh_kop<REC, KEY, CMPR>(cmptr){};
~merge_heap_dh_kobj(){};
// heapify's an initial array of elements
void initialize (void);
// Delete the current minimum and insert the new item from the same
// source / run.
inline void delete_min_and_insert(REC *nextelement_same_run);
};
//Returns the index of the smallest element out of
//i, the left child of i, and the right child of i
template<class REC, class KEY, class CMPR>
inline TPIE_OS_SIZE_T merge_heap_dh_kobj<REC,KEY,CMPR>::get_smallest(
TPIE_OS_SIZE_T i)
{
TPIE_OS_SIZE_T l,r, smallest;
l = Left(i);
r = Right(i);
smallest = ((l <= Heapsize) &&
(UsrObject->compare(Heaparray[l].key,Heaparray[i].key)<0)) ? l : i;
smallest = ((r <= Heapsize) &&
(UsrObject->compare(Heaparray[r].key,Heaparray[smallest].key)<0)) ?
r : smallest;
return smallest;
}
template<class REC, class KEY, class CMPR>
inline void merge_heap_dh_kobj<REC, KEY, CMPR>::Heapify(TPIE_OS_SIZE_T i) {
TPIE_OS_SIZE_T smallest = get_smallest(i);
while (smallest != i) {
this->Exchange(i,smallest);
i = smallest;
smallest = get_smallest(i);
}
}
template<class REC, class KEY, class CMPR>
inline void merge_heap_dh_kobj<REC,KEY,CMPR>::delete_min_and_insert
(REC *nextelement_same_run)
{
if (nextelement_same_run == NULL) {
Heaparray[1].key = Heaparray[Heapsize].key;
Heaparray[1].run_id = Heaparray[Heapsize].run_id;
Heapsize--;
} else {
UsrObject->copy(&Heaparray[1].key, *nextelement_same_run);
}
Heapify(1);
}
template<class REC, class KEY, class CMPR>
void merge_heap_dh_kobj<REC, KEY, CMPR>::initialize () {
for ( TPIE_OS_SIZE_T i = Heapsize/2; i >= 1; i--){ Heapify(i); }
}
#undef Left
#undef Right
#undef Parent
/*
DEPRECATED: comparision function heaps
Earlier TPIE versions allowed a heap that uses C-style
comparison functions. However, comparison functions cannot be
inlined, so each comparison requires one function call. Given that the
comparison operator < and comparison object classes can be inlined and
have better performance while providing the exact same functionality,
comparison functions have been removed from TPIE. If you can provide us
with a compelling argument on why they should be in here, we may consider
adding them again, but you must demonstrate that comparision functions
can outperform other methods in at least some cases or give an example
were it is impossible to use a comparison operator or comparison object
*/
#endif // _MERGE_HEAP_DH_H
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: mm.h (plus contents from mm_imps.h, now deprecated)
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/30/94
//
// $Id: mm.h,v 1.3 2003/04/17 19:38:28 jan Exp $
//
#ifndef _MM_H
#define _MM_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// Get the base class, enums, etc...
#include <mm_base.h>
// Get an implementation definition
// For now only single address space memory management is supported.
#ifdef MM_IMP_REGISTER
#include <mm_register.h>
#else
#error No MM implementation selected.
#endif
#endif // _MM_H
+160
View File
@@ -0,0 +1,160 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: mm_base.cpp
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 9/2/94
//
#include <versions.h>
VERSION(mm_base_cpp,"$Id: mm_base.cpp,v 1.29 2004/10/27 19:13:54 adanner Exp $");
#include "lib_config.h"
#include <mm_base.h>
#include <tpie_log.h>
#include <mm_register.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// support for dmalloc (for tracking memory leaks)
#ifdef USE_DMALLOC
#define DMALLOC_DISABLE
#include <dmalloc.h>
#include <return.h>
#endif
#ifdef MM_BACKWARD_COMPATIBLE
int register_new = MM_IGNORE_MEMORY_EXCEEDED;
#endif
// SIZE_SPACE is to ensure alignment on quad word boundaries. It may be
// possible to check whether a machine needs this at configuration
// time or if dword alignment is ok. On the HP 9000, bus errors occur
// when loading doubles that are not qword aligned.
static const TPIE_OS_SIZE_T SIZE_SPACE=(sizeof(TPIE_OS_SIZE_T) > 8 ? sizeof(TPIE_OS_SIZE_T) : 8);
void *operator new (TPIE_OS_SIZE_T sz)
{
void *p;
#ifdef USE_DMALLOC
char *file;
GET_RET_ADDR(file);
#endif
if ((MM_manager.register_new != MM_IGNORE_MEMORY_EXCEEDED)
&& (MM_manager.register_allocation (sz + SIZE_SPACE) !=
MM_ERROR_NO_ERROR)) {
switch(MM_manager.register_new) {
case MM_ABORT_ON_MEMORY_EXCEEDED:
TP_LOG_FATAL_ID ("In operator new() - allocation request \"");
TP_LOG_FATAL ((TPIE_OS_LONG)(sz + SIZE_SPACE));
TP_LOG_FATAL ("\" plus previous allocation \"");
TP_LOG_FATAL ((TPIE_OS_LONG)(MM_manager.memory_used () - (sz + SIZE_SPACE)));
TP_LOG_FATAL ("\" exceeds user-defined limit \"");
TP_LOG_FATAL ((TPIE_OS_LONG)(MM_manager.memory_limit ()));
TP_LOG_FATAL ("\" \n");
TP_LOG_FLUSH_LOG;
cerr << "memory manager: memory allocation limit " <<
(TPIE_OS_LONG)MM_manager.memory_limit () << " exceeded "
<< "while allocating " << (TPIE_OS_LONG)sz << " bytes" << "\n";
#ifdef USE_DMALLOC
dmalloc_shutdown();
#endif
assert (0); // core dump if debugging
exit (1);
break;
case MM_WARN_ON_MEMORY_EXCEEDED:
TP_LOG_WARNING_ID ("In operator new() - allocation request \"");
TP_LOG_WARNING ((TPIE_OS_LONG)(sz + SIZE_SPACE));
TP_LOG_WARNING ("\" plus previous allocation \"");
TP_LOG_WARNING ((TPIE_OS_LONG)(MM_manager.memory_used () - (sz + SIZE_SPACE)));
TP_LOG_WARNING ("\" exceeds user-defined limit \"");
TP_LOG_WARNING ((TPIE_OS_LONG)(MM_manager.memory_limit ()));
TP_LOG_WARNING ("\" \n");
TP_LOG_FLUSH_LOG;
cerr << "memory manager: memory allocation limit " <<
(TPIE_OS_LONG)MM_manager.memory_limit () << " exceeded "
<< "while allocating " << (TPIE_OS_LONG)sz << " bytes" << "\n";
break;
case MM_IGNORE_MEMORY_EXCEEDED:
break;
}
}
#ifdef USE_DMALLOC
p = _malloc_leap(file, 0, sz + SIZE_SPACE);
#else
p = malloc(sz + SIZE_SPACE);
#endif
if (!p) {
TP_LOG_FATAL_ID ("Out of memory. Cannot continue.");
TP_LOG_FLUSH_LOG;
cerr << "out of memory while allocating " << (TPIE_OS_LONG)sz << " bytes" << "\n";
perror ("mm_base::new malloc");
assert(0);
exit (1);
}
*((size_t *) p) = sz;
return ((char *) p) + SIZE_SPACE;
}
void operator delete (void *ptr)
{
if (!ptr) {
TP_LOG_WARNING_ID ("operator delete was given a NULL pointer");
return;
}
if (MM_manager.register_new != MM_IGNORE_MEMORY_EXCEEDED) {
if (MM_manager.register_deallocation (
(TPIE_OS_SIZE_T)*((size_t *) (((char *) ptr) - SIZE_SPACE)) + SIZE_SPACE)
!= MM_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("In operator delete - MM_manager.register_deallocation failed");
}
}
void *p = ((char *)ptr) - SIZE_SPACE;
#ifdef USE_DMALLOC
char *file;
GET_RET_ADDR(file);
_free_leap(file, 0, p);
#else
free(p);
#endif
}
void operator delete[] (void *ptr) {
if (!ptr) {
TP_LOG_WARNING_ID ("operator delete [] was given a NULL pointer");
return;
}
if (MM_manager.register_new != MM_IGNORE_MEMORY_EXCEEDED) {
if (MM_manager.register_deallocation (
(TPIE_OS_SIZE_T)*((size_t *) (((char *) ptr) - SIZE_SPACE)) + SIZE_SPACE)
!= MM_ERROR_NO_ERROR) {
TP_LOG_WARNING_ID("In operator delete [] - MM_manager.register_deallocation failed");
}
}
void *p = ((char *)ptr) - SIZE_SPACE;
#ifdef USE_DMALLOC
char *file;
GET_RET_ADDR(file);
_free_leap(file, 0, p);
#else
free(p);
#endif
}
// return the overhead on each memory allocation request
int MM_register::space_overhead ()
{
return SIZE_SPACE;
}
#ifndef NDEBUG
TPIE_OS_SPACE_OVERHEAD_BODY
#endif
+71
View File
@@ -0,0 +1,71 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: mm_base.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/30/94
//
// $Id: mm_base.h,v 1.8 2005/04/22 13:52:50 jan Exp $
//
#ifndef _MM_BASE_H
#define _MM_BASE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <sys/types.h>
// dh. MM accounting modes
typedef enum {
MM_IGNORE_MEMORY_EXCEEDED=0,
MM_ABORT_ON_MEMORY_EXCEEDED,
MM_WARN_ON_MEMORY_EXCEEDED
} MM_mode;
// MM Error codes
enum MM_err {
MM_ERROR_NO_ERROR = 0,
MM_ERROR_INSUFFICIENT_SPACE,
MM_ERROR_UNDERFLOW,
MM_ERROR_EXCESSIVE_ALLOCATION
};
// types of memory usage queries we can make on streams (either BTE or MM)
enum MM_stream_usage {
// Overhead of the object without the buffer
MM_STREAM_USAGE_OVERHEAD = 1,
// Max amount ever used by a buffer
MM_STREAM_USAGE_BUFFER,
// Amount currently in use.
MM_STREAM_USAGE_CURRENT,
// Max amount that will ever be used.
MM_STREAM_USAGE_MAXIMUM,
// Maximum additional amount used by each substream created.
MM_STREAM_USAGE_SUBSTREAM
};
// The base class for pointers into memory being managed by memory
// managers. In a uniprocessor, these objects will simply contain
// pointers. In multiprocessors, they will be more complicated
// descriptions of the layout of memory.
class MM_ptr_base
{
public:
// This should return 1 to indicate a valid pointer and 0 to
// indicate an invalid one. It is useful for tests and
// assertions.
virtual operator int (void) = 0;
};
// The base class for all memory management objects.
class MM_manager_base
{
public:
// How much is currently available.
virtual MM_err available (TPIE_OS_SIZE_T *sz_a) = 0;
};
#endif // _MM_BASE_H
+240
View File
@@ -0,0 +1,240 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: mm_register.cpp
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/31/94
//
// A simple registration based memory manager.
#include <versions.h>
VERSION(mm_register_cpp,"$Id: mm_register.cpp,v 1.23 2005/07/07 20:37:39 adanner Exp $");
//#include <assert.h>
#include "lib_config.h"
#define MM_IMP_REGISTER
#include <mm.h>
#include <mm_register.h>
#ifdef REPORT_LARGE_MEMOPS
#include <iostream>
#endif
#ifdef MM_BACKWARD_COMPATIBLE
extern int register_new;
#endif
#include <stdlib.h>
MM_register::MM_register()
{
instances++;
tp_assert(instances == 1,
"Only 1 instance of MM_register_base should exist.");
}
MM_register::~MM_register(void)
{
tp_assert(instances == 1,
"Only 1 instance of MM_register_base should exist.");
instances--;
}
// check that new allocation request is below user-defined limit.
// This should be a private method, only called by operator new.
MM_err MM_register::register_allocation(TPIE_OS_SIZE_T request)
{
// quick hack to allow operation before limit is set
// XXX
if(!user_limit) {
return MM_ERROR_NO_ERROR;
}
used += request;
if (request > remaining) {
TP_LOG_WARNING("Memory allocation request: ");
TP_LOG_WARNING(static_cast<TPIE_OS_OUTPUT_SIZE_T>(request));
TP_LOG_WARNING(": User-specified memory limit exceeded.");
TP_LOG_FLUSH_LOG;
remaining = 0;
return MM_ERROR_INSUFFICIENT_SPACE;
}
remaining -= request;
TP_LOG_MEM_DEBUG("mm_register Allocated ");
TP_LOG_MEM_DEBUG(static_cast<TPIE_OS_OUTPUT_SIZE_T>(request));
TP_LOG_MEM_DEBUG("; ");
TP_LOG_MEM_DEBUG(static_cast<TPIE_OS_OUTPUT_SIZE_T>(remaining));
TP_LOG_MEM_DEBUG(" remaining.\n");
TP_LOG_FLUSH_LOG;
#ifdef REPORT_LARGE_MEMOPS
if(request > user_limit/10) {
cerr << "MEM alloc " << request
<< " (" << remaining << " remaining)" << endl;
}
#endif
return MM_ERROR_NO_ERROR;
}
// do the accounting for a memory deallocation request.
// This should be a private method, only called by operators
// delete and delete [].
MM_err MM_register::register_deallocation(TPIE_OS_SIZE_T sz)
{
remaining += sz;
if (sz > used) {
TP_LOG_WARNING("Error in deallocation sz=");
TP_LOG_WARNING((TPIE_OS_LONG)sz);
TP_LOG_WARNING(", remaining=");
TP_LOG_WARNING((TPIE_OS_LONG)remaining);
TP_LOG_WARNING(", user_limit=");
TP_LOG_WARNING((TPIE_OS_LONG)user_limit);
TP_LOG_WARNING("\n");
TP_LOG_FLUSH_LOG;
used = 0;
return MM_ERROR_UNDERFLOW;
}
used -= sz;
TP_LOG_MEM_DEBUG("mm_register De-allocated ");
TP_LOG_MEM_DEBUG((unsigned int)sz);
TP_LOG_MEM_DEBUG("; ");
TP_LOG_MEM_DEBUG((unsigned int)remaining);
TP_LOG_MEM_DEBUG(" now available.\n");
TP_LOG_FLUSH_LOG;
#ifdef REPORT_LARGE_MEMOPS
if(sz > user_limit/10) {
cerr << "MEM free " << sz
<< " (" << remaining << " remaining)" << endl;
}
#endif
return MM_ERROR_NO_ERROR;
}
#ifdef MM_BACKWARD_COMPATIBLE
// (Old) way to query how much memory is available
MM_err MM_register::available (TPIE_OS_SIZE_T *sz)
{
*sz = remaining;
return MM_ERROR_NO_ERROR;
}
// resize_heap has the same purpose as set_memory_limit.
// It is retained for backward compatibility.
// dh. 1999 09 29
MM_err MM_register::resize_heap(TPIE_OS_SIZE_T sz)
{
return set_memory_limit(sz);
}
#endif
// User-callable method to set allowable memory size
MM_err MM_register::set_memory_limit (TPIE_OS_SIZE_T new_limit)
{
// by default, we keep track and abort if memory limit exceeded
if (register_new == MM_IGNORE_MEMORY_EXCEEDED){
register_new = MM_ABORT_ON_MEMORY_EXCEEDED;
}
// dh. unless the user indicates otherwise
if (new_limit == 0){
register_new = MM_IGNORE_MEMORY_EXCEEDED;
remaining = used = user_limit = 0;
return MM_ERROR_NO_ERROR;
}
if (used > new_limit) {
return MM_ERROR_EXCESSIVE_ALLOCATION;
} else {
// These are unsigned, so be careful.
if (new_limit < user_limit) {
remaining -= user_limit - new_limit;
} else {
remaining += new_limit - user_limit;
}
user_limit = new_limit;
return MM_ERROR_NO_ERROR;
}
}
// dh. only warn if memory limit exceeded
void MM_register::warn_memory_limit()
{
register_new = MM_WARN_ON_MEMORY_EXCEEDED;
}
// dh. abort if memory limit exceeded
void MM_register::enforce_memory_limit()
{
register_new = MM_ABORT_ON_MEMORY_EXCEEDED;
}
// dh. ignore memory limit accounting
void MM_register::ignore_memory_limit()
{
register_new = MM_IGNORE_MEMORY_EXCEEDED;
}
// rw. provide accounting state
MM_mode MM_register::get_limit_mode() {
return register_new;
}
// dh. return the amount of memory available before user-specified
// memory limit exceeded
TPIE_OS_SIZE_T MM_register::memory_available()
{
return remaining;
}
size_t MM_register::memory_used()
{
return used;
}
size_t MM_register::memory_limit()
{
return user_limit;
}
// Instantiate the actual memory manager, and allocate the
// its static data members
MM_register MM_manager;
int MM_register::instances = 0; // Number of instances. (init)
// TPIE's "register memory requests" flag
MM_mode MM_register::register_new = MM_ABORT_ON_MEMORY_EXCEEDED;
// The counter of mm_register_init instances. It is implicity set to 0.
unsigned int mm_register_init::count;
// The constructor and destructor that ensure that the memory manager is
// created exactly once, and destroyed when appropriate.
mm_register_init::mm_register_init(void)
{
if (count++ == 0) {
MM_manager.set_memory_limit(MM_DEFAULT_MM_SIZE);
}
}
mm_register_init::~mm_register_init(void)
{
--count;
}
+106
View File
@@ -0,0 +1,106 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: mm_register.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/30/94
//
// $Id: mm_register.h,v 1.10 2004/08/12 12:35:32 jan Exp $
//
#ifndef _MM_REGISTER_H
#define _MM_REGISTER_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#define MM_REGISTER_VERSION 2
// To be defined later in this file.
class mm_register_init;
// Declarations of a very simple memory manager desgined to work with
// BTEs that rely on the underlying OS to manage physical memory.
// Examples include BTEs based on mmap() and the stdio library.
// Another type of BTE this MM would be useful for is one which is
// designed to make efficient use of a cache for programs running
// entirely in main memory.
class MM_register {
private:
// The number of instances of this class and descendents that exist.
static int instances;
// The amount of space remaining to be allocated.
TPIE_OS_SIZE_T remaining;
// The user-specified limit on memory.
TPIE_OS_SIZE_T user_limit;
// the amount that has been allocated.
TPIE_OS_SIZE_T used;
public:
// made public since Linux c++ doesn't like the fact that our new
// and delete operators don't throw exceptions. [tavi]
// flag indicates whether we are keeping track of memory or not
static MM_mode register_new;
MM_register();
~MM_register(void);
MM_err register_allocation (TPIE_OS_SIZE_T sz);
MM_err register_deallocation(TPIE_OS_SIZE_T sz);
#ifdef MM_BACKWARD_COMPATIBLE
// retained for backward compatibility
MM_err available (TPIE_OS_SIZE_T *sz);
MM_err resize_heap (TPIE_OS_SIZE_T sz);
#endif
MM_err set_memory_limit(TPIE_OS_SIZE_T sz); // dh.
void enforce_memory_limit (); // dh.
void ignore_memory_limit (); // dh.
void warn_memory_limit (); // dh.
MM_mode get_limit_mode();
TPIE_OS_SIZE_T memory_available (); // dh.
TPIE_OS_SIZE_T memory_used (); // dh.
TPIE_OS_SIZE_T memory_limit (); // dh.
int space_overhead (); // dh.
friend class mm_register_init;
//friend void * operator new(TPIE_OS_SIZE_T);
//friend void operator delete(void *);
//friend void operator delete[](void *);
};
// The default amount of memory we will allow to be allocated.
// 40MB
#define MM_DEFAULT_MM_SIZE (40<<20)
// Here is the single memory management object.
extern MM_register MM_manager;
// A class to make sure that MM_manager gets set up properly. It is
// based on the code in tpie_log.h that does the same thing for logs,
// which is in turn based on item 47 from sdm's book.
class mm_register_init {
private:
// The number of mm_register_init objects that exist.
static unsigned int count;
public:
mm_register_init(void);
~mm_register_init(void);
};
static mm_register_init source_file_mm_register_init;
#endif // _MM_REGISTER_H
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) 1995 Darren Erik Vengroff
//
// File: persist.h
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 4/7/95
//
// $Id: persist.h,v 1.3 2003/09/13 17:42:27 jan Exp $
//
// Persistence flags for TPIE streams.
//
#ifndef _PERSIST_H
#define _PERSIST_H
// Get definitions for working with Unix and Windows
#include <portability.h>
enum persistence {
// Delete the stream from the disk when it is destructed.
PERSIST_DELETE = 0,
// Do not delete the stream from the disk when it is destructed.
PERSIST_PERSISTENT = 1,
// Delete each block of data from the disk as it is read.
// If not supported by the OS (see portability.h), delete
// the stream when it is destructed (see PERSIST_DELETE).
PERSIST_READ_ONCE = TPIE_OS_PERSIST_READ_ONCE
};
#endif // _PERSIST_H
+13
View File
@@ -0,0 +1,13 @@
#include <portability.h>
//Needed for windows only
#ifdef _WIN32
ostream& operator<<(ostream& s, const TPIE_OS_OFFSET x){
char buf[30];
sprintf(buf,"%I64d",x);
return s << buf;
}
#endif
File diff suppressed because it is too large Load Diff
+459
View File
@@ -0,0 +1,459 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: pqueue_heap.h
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 10/4/94
//
// $Id: pqueue_heap.h,v 1.10 2005/01/14 18:36:24 tavi Exp $
//
// A priority queue class implemented as a binary heap.
//
#ifndef _PQUEUE_HEAP_H
#define _PQUEUE_HEAP_H
// Get definitions for working with Unix and Windows
#include <portability.h>
// The virtual base class that defines what priority queues must do.
template <class T, class P>
class pqueue
{
public:
// Is it full?
virtual bool full(void) = 0;
// How many elements?
virtual unsigned int num_elts(void) = 0;
// Insert
virtual bool insert(const T& elt, const P& prio) = 0;
// Min
virtual void get_min(T& elt, P& prio) = 0;
// Extract min.
virtual bool extract_min(T& elt, P& prio) = 0;
};
// Helper functions for navigating through a binary heap.
// The children of an element of the heap.
static inline unsigned int lchild(unsigned int index) {
return 2 * index;
}
static inline unsigned int rchild(unsigned int index) {
return 2 * index + 1;
}
// The parent of an element.
static inline unsigned int parent(unsigned int index) {
return index >> 1;
}
template <class T, class P>
struct q_elt {
T elt;
P priority;
};
// A base class for priority queues that use heaps.
template <class T, class P>
class pqueue_heap
{
protected:
// A pointer to the array of elements and their priorities.
q_elt<T,P> * elements;
// The number currently in the queue.
unsigned int cur_elts;
// The maximum number the queue can hold.
unsigned int max_elts;
// Fix up the heap after a deletion.
/* virtual void heapify(unsigned int root) = 0; */
public:
pqueue_heap(unsigned int size);
virtual ~pqueue_heap();
// Is it full?
bool full(void);
// How many elements?
unsigned int num_elts(void);
// Min
void get_min(T& elt, P& prio);
};
template <class T, class P>
pqueue_heap<T,P>::pqueue_heap(unsigned int size)
{
elements = new q_elt<T,P>[max_elts = size];
cur_elts = 0;
}
template <class T, class P>
pqueue_heap<T,P>::~pqueue_heap() {
delete [] elements;
cur_elts = 0;
max_elts = 0;
return;
}
template <class T, class P>
bool pqueue_heap<T,P>::full(void) {
return cur_elts == max_elts;
}
template <class T, class P>
unsigned int pqueue_heap<T,P>::num_elts(void) {
return cur_elts;
}
template <class T, class P>
void pqueue_heap<T,P>::get_min(T& elt, P& prio) {
elt = elements->elt;
prio = elements->priority;
}
// Comment: (jan) You must not use this version anymore.
// // A priority queue that uses a comparison function for comparing
// // priorities.
// End Comment.
// A priority queue that uses the builtin operator < for comparing
// priorities instead of a comparison function.
template <class T, class P>
class pqueue_heap_op : public pqueue_heap<T,P>
{
private:
void heapify(unsigned int root);
protected:
using pqueue_heap<T,P>::cur_elts;
using pqueue_heap<T,P>::max_elts;
using pqueue_heap<T,P>::elements;
public:
using pqueue_heap<T,P>::full;
using pqueue_heap<T,P>::num_elts;
pqueue_heap_op(unsigned int size);
virtual ~pqueue_heap_op(void) {};
// Insert
bool insert(const T& elt, const P& prio);
// Extract min.
bool extract_min(T& elt, P& prio);
};
template <class T, class P>
bool pqueue_heap_op<T,P>::extract_min(T& elt, P& prio) {
if (!cur_elts) {
return false;
}
elt = elements->elt;
prio = elements->priority;
elements[0] = elements[--cur_elts];
heapify(0);
return true;
}
template <class T, class P>
pqueue_heap_op<T,P>::pqueue_heap_op(unsigned int size) :
pqueue_heap<T,P>(size)
{
}
template <class T, class P>
bool pqueue_heap_op<T,P>::insert(const T& elt, const P& prio) {
unsigned int ii;
if (full()) {
return false;
}
for (ii = cur_elts++;
ii && (elements[parent(ii)].priority > prio);
ii = parent(ii)) {
elements[ii] = elements[parent(ii)];
}
elements[ii].priority = prio;
elements[ii].elt = elt;
return true;
}
template <class T, class P>
void pqueue_heap_op<T,P>::heapify(unsigned int root) {
unsigned int min_index = root;
unsigned int lc = lchild(root);
unsigned int rc = rchild(root);
if ((lc < cur_elts) && (elements[lc].priority <
elements[min_index].priority)) {
min_index = lc;
}
if ((rc < cur_elts) && (elements[rc].priority <
elements[min_index].priority)) {
min_index = rc;
}
if (min_index != root) {
q_elt<T,P> tmp_q = elements[min_index];
elements[min_index] = elements[root];
elements[root] = tmp_q;
heapify(min_index);
}
}
// A priority queue that uses a comparison object.
template <class T, class P, class CMPR>
class pqueue_heap_obj : public pqueue_heap<T,P>
{
private:
CMPR *cmp_o;
void heapify(unsigned int root);
protected:
using pqueue_heap<T,P>::cur_elts;
using pqueue_heap<T,P>::max_elts;
using pqueue_heap<T,P>::elements;
public:
using pqueue_heap<T,P>::full;
using pqueue_heap<T,P>::num_elts;
public:
pqueue_heap_obj(unsigned int size, CMPR *cmp);
virtual ~pqueue_heap_obj(void) {};
// Insert
bool insert(const T& elt, const P& prio);
// Extract min.
bool extract_min(T& elt, P& prio);
};
template <class T, class P, class CMPR>
bool pqueue_heap_obj<T,P,CMPR>::extract_min(T& elt, P& prio) {
if (!cur_elts) {
return false;
}
elt = elements->elt;
prio = elements->priority;
elements[0] = elements[--cur_elts];
heapify(0);
return true;
}
template <class T, class P, class CMPR>
pqueue_heap_obj<T,P,CMPR>::pqueue_heap_obj(unsigned int size, CMPR *cmp)
: pqueue_heap<T,P>(size)
{
cmp_o = cmp;
}
template <class T, class P, class CMPR>
bool pqueue_heap_obj<T,P,CMPR>::insert(const T& elt, const P& prio) {
unsigned int ii;
if (full()) {
return false;
}
for (ii = cur_elts++;
ii && (cmp_o->compare(elements[parent(ii)].priority, prio) > 0);
ii = parent(ii)) {
elements[ii] = elements[parent(ii)];
}
elements[ii].priority = prio;
elements[ii].elt = elt;
return true;
}
template <class T, class P, class CMPR>
void pqueue_heap_obj<T,P,CMPR>::heapify(unsigned int root) {
unsigned int min_index = root;
unsigned int lc = lchild(root);
unsigned int rc = rchild(root);
if ((lc < cur_elts) &&
(cmp_o->compare(elements[lc].priority,
elements[min_index].priority) < 0)) {
min_index = lc;
}
if ((rc < cur_elts) &&
(cmp_o->compare(elements[rc].priority,
elements[min_index].priority) < 0)) {
min_index = rc;
}
if (min_index != root) {
q_elt<T,P> tmp_q = elements[min_index];
elements[min_index] = elements[root];
elements[root] = tmp_q;
heapify(min_index);
}
}
// Comment: (jan) You must not use this version anymore.
template <class T, class P>
class pqueue_heap_cmp : public pqueue_heap<T,P>
{
private:
// A pointer to the function used to compare the priorities of
// elements.
int (*cmp_f)(const P&, const P&);
void heapify(unsigned int root);
protected:
using pqueue_heap<T,P>::cur_elts;
using pqueue_heap<T,P>::max_elts;
using pqueue_heap<T,P>::elements;
public:
using pqueue_heap<T,P>::full;
using pqueue_heap<T,P>::num_elts;
public:
pqueue_heap_cmp(unsigned int size, int (*cmp)(const P&, const P&));
virtual ~pqueue_heap_cmp(void) {}
// Insert
bool insert(const T& elt, const P& prio);
// Extract min.
bool extract_min(T& elt, P& prio);
};
template <class T, class P>
bool pqueue_heap_cmp<T,P>::extract_min(T& elt, P& prio)
{
if (!cur_elts) {
return false;
}
elt = elements->elt;
prio = elements->priority;
elements[0] = elements[--cur_elts];
heapify(0);
return true;
}
template <class T, class P>
pqueue_heap_cmp<T,P>::pqueue_heap_cmp(unsigned int size,
int (*cmp)(const P&, const P&)) :
pqueue_heap<T,P>(size) {
cmp_f = cmp;
}
template <class T, class P>
bool pqueue_heap_cmp<T,P>::insert(const T& elt, const P& prio)
{
unsigned int ii;
if (full()) {
return false;
}
for (ii = cur_elts++;
ii && (cmp_f(elements[parent(ii)].priority, prio) > 0);
ii = parent(ii))
{
elements[ii] = elements[parent(ii)];
}
elements[ii].priority = prio;
elements[ii].elt = elt;
return true;
}
template <class T, class P>
void pqueue_heap_cmp<T,P>::heapify(unsigned int root)
{
unsigned int min_index = root;
unsigned int lc = lchild(root);
unsigned int rc = rchild(root);
if ((lc < cur_elts) && (cmp_f(elements[lc].priority,
elements[min_index].priority) < 0))
{
min_index = lc;
}
if ((rc < cur_elts) && (cmp_f(elements[rc].priority,
elements[min_index].priority) < 0))
{
min_index = rc;
}
if (min_index != root)
{
q_elt<T,P> tmp_q = elements[min_index];
elements[min_index] = elements[root];
elements[root] = tmp_q;
heapify(min_index);
}
}
// // A priority queue that simply uses an array.
// End Comment.
#endif // _PQUEUE_HEAP_H
+306
View File
@@ -0,0 +1,306 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: quicksort.h
// Author: Darren Erik Vengroff <darrenv@eecs.umich.edu>
// Created: 9/28/94
//
// A basic implementation of quicksort for use in core by AMI_sort() on
// streams or substreams that are small enough.
//
// $Id: quicksort.h,v 1.23 2005/07/07 20:42:31 adanner Exp $
//
#ifndef _QUICKSORT_H
#define _QUICKSORT_H
// Get definitions for working with Unix and Windows
#include <portability.h>
//A simple class that facilitates doing key sorting followed
//by in-memory permuting to sort items in-memory. This is
//particularly useful when key size is much smaller than
//item size. Note that using this requires that the class Key
//have the comparison operators defined appropriately.
template<class Key>
class qsort_item {
public:
Key keyval;
unsigned int source;
friend int operator==(const qsort_item &x, const qsort_item &y)
{return (x.keyval == y.keyval);}
friend int operator!=(const qsort_item &x, const qsort_item &y)
{return (x.keyval != y.keyval);}
friend int operator<=(const qsort_item &x, const qsort_item &y)
{return (x.keyval <= y.keyval);}
friend int operator>=(const qsort_item &x, const qsort_item &y)
{return (x.keyval >= y.keyval);}
friend int operator<(const qsort_item &x, const qsort_item &y)
{return (x.keyval < y.keyval);}
friend int operator>(const qsort_item &x, const qsort_item &y)
{return (x.keyval > y.keyval);}
};
// A version that uses the < operator. This should be faster for
// intrinsic types such as int, where the compiler can generate very
// good code and avoid a function call inside the innermost loop of
// partition().
template<class T>
void partition_op(T *data, size_t len, size_t &partition);
template<class T>
void __quick_sort_op(T *data, size_t len,
size_t min_file_len = 2)
{
// On return from partition(), everything at or below this index
// will be less that or equal to everything above it.
// Furthermore, it will not be 0 since this will leave us to
// recurse on the whole array again.
size_t part_index;
if (len < min_file_len) {
return;
}
partition_op(data, len, part_index);
__quick_sort_op(data, part_index + 1, min_file_len);
__quick_sort_op(data + part_index + 1, len - part_index - 1, min_file_len);
}
template<class T>
void partition_op(T *data, size_t len, size_t &part_index)
{
T *ptpart, tpart;
T *p, *q;
T t0;
// Try to get a good partition value and avoid being bitten by already
// sorted input.
ptpart = data + (TPIE_OS_RANDOM() % len);
tpart = *ptpart;
*ptpart = data[0];
data[0] = tpart;
// Walk through the array and partition them.
for (p = data - 1, q = data + len; ; ) {
do {
q--;
} while (tpart < *q );
do {
p++;
} while (*p < tpart);
if (p < q) {
t0 = *p;
*p = *q;
*q = t0;
} else {
part_index = q - data;
break;
}
}
}
template<class T>
void insertion_sort_op(T *data, size_t len);
template<class T>
void quick_sort_op(T *data, size_t len,
size_t min_file_len = 20)
{
__quick_sort_op(data, len, min_file_len);
insertion_sort_op(data, len);
}
template<class T>
void insertion_sort_op(T *data, size_t len)
{
T *p, *q, test;
for (p = data + 1; p < data + len; p++) {
//for (q = p - 1, test = *p; *q > test; q--) { dh
for (q = p - 1, test = *p; test < *q; q--) {
*(q+1) = *q;
if (q == data) {
q--; // to make assignment below correct
break;
}
}
*(q+1) = test;
}
}
// A version that uses a comparison object.
template<class T, class CMPR>
void partition_obj(T *data, size_t len, size_t &partition,
CMPR *cmp);
template<class T, class CMPR>
void __quick_sort_obj(T *data, size_t len, CMPR *cmp,
size_t min_file_len = 2)
{
// On return from partition(), everything at or below this index
// will be less that or equal to everything above it.
// Furthermore, it will not be 0 since this will leave us to
// recurse on the whole array again.
size_t part_index;
if (len < min_file_len) {
return;
}
partition_obj(data, len, part_index, cmp);
__quick_sort_obj(data, part_index + 1, cmp, min_file_len);
__quick_sort_obj(data + part_index + 1, len - part_index - 1, cmp, min_file_len);
}
template<class T, class CMPR>
void partition_obj(T *data, size_t len, size_t &part_index,
CMPR *cmp)
{
T *ptpart, tpart;
T *p, *q;
T t0;
// Try to get a good partition value and avoid being bitten by already
// sorted input.
ptpart = data + (TPIE_OS_RANDOM() % len);
tpart = *ptpart;
*ptpart = data[0];
data[0] = tpart;
// Walk through the array and partition them.
for (p = data - 1, q = data + len; ; ) {
do {
q--;
} while (cmp->compare(tpart, *q) < 0);
do {
p++;
} while (cmp->compare(*p, tpart) < 0);
if (p < q) {
t0 = *p;
*p = *q;
*q = t0;
} else {
part_index = q - data;
break;
}
}
}
template<class T, class CMPR>
void insertion_sort_obj(T *data, size_t len,
CMPR *cmp);
template<class T, class CMPR>
void quick_sort_obj(T *data, size_t len,
CMPR *cmp,
size_t min_file_len = 20)
{
__quick_sort_obj(data, len, cmp, min_file_len);
insertion_sort_obj(data, len, cmp);
}
template<class T, class CMPR>
void insertion_sort_obj(T *data, size_t len,
CMPR *cmp)
{
T *p, *q, test;
for (p = data + 1; p < data + len; p++) {
for (q = p - 1, test = *p; (cmp->compare(test, *q) < 0); q--) {
*(q+1) = *q;
if (q==data) {
q--; // to make assignment below correct
break;
}
}
*(q+1) = test;
}
}
/*
DEPRECATED: quick_sort_cmp
Earlier TPIE versions allowed a quicksort that used a C-style
comparison function to sort. However, comparison functions cannot be
inlined, so each comparison requires one function call. Given that the
comparison operator < and comparison object classes can be inlined and
have better performance while providing the exact same functionality,
comparison functions have been removed from TPIE. If you can provide us
with a compelling argument on why they should be in here, we may consider
adding them again, but you must demonstrate that comparision functions
can outperform other methods in at least some cases or give an example
were it is impossible to use a comparison operator or comparison object
*/
#endif // _QUICKSORT_H
+92
View File
@@ -0,0 +1,92 @@
//
// File: stdio_stack.h
// Authors: Rakesh Barve
//
// Defining a stack based on bte_stdio separately specifically
// for use in block collection class and related apps.
// The reason we don't want to use ami_stack is because
// then the stack would be implemented as a BTE_STREAM, which
// may have large block size etc. which is undesirable for
// stacks related to block collections since such a stack is only
// a meta data structure accessed no more than once every block
// is created or destroyed.
//
// $Id: stdio_stack.h,v 1.7 2003/04/17 19:57:25 jan Exp $
//
#ifndef _STDIO_STACK_H
#define _STDIO_STACK_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <bte_stream_stdio.h>
template<class T>
class stdio_stack : public BTE_stream_stdio<T> {
public:
stdio_stack(char *path, BTE_stream_type type = BTE_WRITE_STREAM);
~stdio_stack(void);
BTE_err push(const T &t);
BTE_err pop(T **t);
};
template<class T>
stdio_stack<T>::stdio_stack(char *path,
BTE_stream_type type) :
BTE_stream_stdio<T>(path, type)
{
}
template<class T>
stdio_stack<T>::~stdio_stack(void)
{
}
template<class T>
BTE_err stdio_stack<T>::push(const T &t)
{
BTE_err ae;
TPIE_OS_OFFSET slen;
ae = truncate((slen = stream_len())+1);
if (ae != BTE_ERROR_NO_ERROR) {
return ae;
}
ae = seek(slen);
if (ae != BTE_ERROR_NO_ERROR) {
return ae;
}
return write_item(t);
}
template<class T>
BTE_err stdio_stack<T>::pop(T **t)
{
BTE_err ae;
TPIE_OS_OFFSET slen;
slen = stream_len();
ae = seek(slen-1);
if (ae != BTE_ERROR_NO_ERROR) {
return ae;
}
ae = read_item(t);
if (ae != BTE_ERROR_NO_ERROR) {
return ae;
}
return truncate(slen-1);
}
#endif // _stdio_stack_H
+24
View File
@@ -0,0 +1,24 @@
// Copyright (c) 1995 Darren Vengroff
//
// File: timer.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 1/11/95
//
// $Id: timer.h,v 1.2 2003/04/17 19:58:26 jan Exp $
//
// General definition of a virtual timer class.
//
#ifndef _TIMER_H
#define _TIMER_H
// Get definitions for working with Unix and Windows
#include <portability.h>
class timer {
public:
virtual void start(void) = 0;
virtual void stop(void) = 0;
virtual void reset(void) = 0;
};
#endif // _TIMER_H
+36
View File
@@ -0,0 +1,36 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: tpie_assert.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/12/94
//
// $Id: tpie_assert.h,v 1.10 2005/07/07 20:38:07 adanner Exp $
//
#ifndef _TPIE_ASSERT_H
#define _TPIE_ASSERT_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <tpie_log.h>
#include <assert.h>
#include <iostream>
#if DEBUG_ASSERTIONS
#define tp_assert(condition,message) { \
if (!(condition)) { \
TP_LOG_FATAL_ID("Assertion failed:"); \
TP_LOG_FATAL_ID(message); \
cerr << "Assertion failed: " << message << "\n"; \
assert(condition); \
} \
}
#else
#define tp_assert(condition,message)
#endif
#endif // _TPIE_ASSERT_H
+44
View File
@@ -0,0 +1,44 @@
//
// File: tpie_log.cpp
// Authors: Darren Erik Vengroff <dev@cs.duke.edu>
// Octavian Procopiuc <tavi@cs.duke.edu>
// Created: 5/12/94
//
#include <versions.h>
VERSION(tpie_log_cpp,"$Id: tpie_log.cpp,v 1.16 2004/08/12 12:53:43 jan Exp $");
// We are logging
#define TPL_LOGGING 1
#include <stdlib.h>
#include <time.h>
#include <tpie_tempnam.h>
#include <tpie_log.h>
#define TPLOGPFX "tpielog"
// Local initialization function. Create a permanent repository for the log
// file name. Should be called only once, by theLogName() below.
static char *__tpie_log_name() {
static char tln[128];
TPIE_OS_SRANDOM((unsigned int)TPIE_OS_TIME(NULL));
strncpy(tln, tpie_tempnam(TPLOGPFX, TPLOGDIR), 124);
strcat(tln, ".txt");
return tln;
}
char *tpie_log_name() {
static char *tln = __tpie_log_name();
return tln;
}
logstream &tpie_log() {
static logstream log(tpie_log_name(), TPIE_LOG_DEBUG, TPIE_LOG_DEBUG);
return log;
}
void tpie_log_init(TPIE_LOG_LEVEL level) {
TP_LOG_SET_THRESHOLD(level);
}
+92
View File
@@ -0,0 +1,92 @@
// Copyright (c) 1994 Darren Erik Vengroff
//
// File: tpie_log.h
// Author: Darren Erik Vengroff <dev@cs.duke.edu>
// Created: 5/12/94
//
// $Id: tpie_log.h,v 1.25 2005/07/07 20:37:39 adanner Exp $
//
#ifndef _TPIE_LOG_H
#define _TPIE_LOG_H
// Logging levels, from higest priority to lowest.
enum TPIE_LOG_LEVEL {
TPIE_LOG_FATAL = 0, // Fatal errors are always logged no matter what;
TPIE_LOG_WARNING, // Warning about some internal condition;
TPIE_LOG_APP_DEBUG, // Debugging info for the application only;
TPIE_LOG_DEBUG, // Debugging info.
TPIE_LOG_MEM_DEBUG // Memory allocation de-allocation.
};
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <logstream.h>
// The file name of the log stream.
char *tpie_log_name();
// Returns the only logstream object.
logstream& tpie_log();
// Initialize the log.
void tpie_log_init(TPIE_LOG_LEVEL level = TPIE_LOG_WARNING);
#if TPL_LOGGING
// Macros to simplify logging. The argument to the macro can be any type
// that log streams have an output operator for.
#define TP_LOG_FLUSH_LOG (!logstream::log_initialized || tpie_log().flush())
// eg: LOG_FATAL(LOG_ID_MSG)
#define TP_LOG_ID_MSG __FILE__ << " line " << __LINE__ << ": "
#define TP_LOG_FATAL(msg) \
(!logstream::log_initialized || tpie_log() << setpriority(TPIE_LOG_FATAL) << msg)
#define TP_LOG_WARNING(msg) \
(!logstream::log_initialized || tpie_log() << setpriority(TPIE_LOG_WARNING) << msg)
#define TP_LOG_APP_DEBUG(msg) \
(!logstream::log_initialized || tpie_log() << setpriority(TPIE_LOG_APP_DEBUG) << msg)
#define TP_LOG_DEBUG(msg) \
(!logstream::log_initialized || tpie_log() << setpriority(TPIE_LOG_DEBUG) << msg)
#define TP_LOG_MEM_DEBUG(msg) \
(!logstream::log_initialized || tpie_log() << setpriority(TPIE_LOG_MEM_DEBUG) << msg)
#define TP_LOG_FATAL_ID(msg) \
(TP_LOG_FATAL(TP_LOG_ID_MSG << msg << "\n"), TP_LOG_FLUSH_LOG)
#define TP_LOG_WARNING_ID(msg) \
(TP_LOG_WARNING(TP_LOG_ID_MSG << msg << "\n"), TP_LOG_FLUSH_LOG)
#define TP_LOG_APP_DEBUG_ID(msg) \
(TP_LOG_APP_DEBUG(TP_LOG_ID_MSG << msg << "\n"), TP_LOG_FLUSH_LOG)
#define TP_LOG_DEBUG_ID(msg) \
(TP_LOG_DEBUG(TP_LOG_ID_MSG << msg << "\n"), TP_LOG_FLUSH_LOG)
#define TP_LOG_MEM_DEBUG_ID(msg) \
(TP_LOG_MEM_DEBUG(TP_LOG_ID_MSG << msg << "\n"), TP_LOG_FLUSH_LOG)
#define TP_LOG_SET_THRESHOLD(level) (tpie_log() << setthreshold(level))
#else // !TPL_LOGGING
// We are not compiling logging.
#define TP_LOG_FATAL(msg)
#define TP_LOG_WARNING(msg)
#define TP_LOG_APP_DEBUG(msg)
#define TP_LOG_DEBUG(msg)
#define TP_LOG_MEM_DEBUG(msg)
#define TP_LOG_FATAL_ID(msg)
#define TP_LOG_WARNING_ID(msg)
#define TP_LOG_APP_DEBUG_ID(msg)
#define TP_LOG_DEBUG_ID(msg)
#define TP_LOG_MEM_DEBUG_ID(msg)
#define TP_LOG_SET_THRESHOLD(level)
#define TP_LOG_FLUSH_LOG {}
#endif // TPL_LOGGING
#endif // _TPIE_LOG_H
+73
View File
@@ -0,0 +1,73 @@
//
// File: tpie_stats.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: tpie_stats.h,v 1.5 2004/08/12 12:35:32 jan Exp $
//
// The tpie_stats class for recording statistics. The parameter C is
// the number of statistics to be recorded.
//
#ifndef _TPIE_STATS_H
#define _TPIE_STATS_H
// Get definitions for working with Unix and Windows
#include <portability.h>
template<int C>
class tpie_stats {
private:
// The array storing the C statistics.
TPIE_OS_OFFSET stats_[C];
public:
// Reset all counts to 0.
void reset() {
for (int i = 0; i < C; i++)
stats_[i] = 0;
}
// Default constructor. Set all counts to 0.
tpie_stats() {
reset();
}
// Copy constructor.
tpie_stats(const tpie_stats<C>& ts) {
for (int i = 0; i < C; i++)
stats_[i] = ts.stats_[i];
}
// Record ONE event of type t.
void record(int t) {
stats_[t]++;
}
// Record k events of type t.
void record(int t, TPIE_OS_OFFSET k) {
stats_[t] += k;
}
// Record the events stored in s.
void record(const tpie_stats<C>& s) {
for (int i = 0; i < C; i++)
stats_[i] += s.stats_[i];
}
// Set the number of type t events to k.
void set(int t, TPIE_OS_OFFSET k) {
stats_[t] = k;
}
// Inquire the number of type t events.
TPIE_OS_OFFSET get(int t) const {
return stats_[t];
}
// Destructor.
~tpie_stats() {}
};
template<int C>
const tpie_stats<C> operator-(const tpie_stats<C> & lhs,
const tpie_stats<C> & rhs) {
tpie_stats<C> res;
for (int i = 0; i < C; i++)
res.stats_[i] = lhs.stats_[i] - rhs.stats_[i];
return res;
}
#endif //_TPIE_STATS_H
+33
View File
@@ -0,0 +1,33 @@
// Copyright (C) 2001 Octavian Procopiuc
//
// File: tpie_stats_coll.h
// Authors: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: tpie_stats_coll.h,v 1.4 2003/04/17 20:05:10 jan Exp $
//
// Statistics for block collections.
#ifndef _TPIE_STATS_COLL_H
#define _TPIE_STATS_COLL_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <tpie_stats.h>
#define TPIE_STATS_COLLECTION_COUNT 9
enum TPIE_STATS_COLLECTION {
BLOCK_GET = 0,
BLOCK_PUT,
BLOCK_NEW,
BLOCK_DELETE,
BLOCK_SYNC,
COLLECTION_OPEN,
COLLECTION_CLOSE,
COLLECTION_CREATE,
COLLECTION_DELETE
};
typedef tpie_stats<TPIE_STATS_COLLECTION_COUNT> tpie_stats_collection;
#endif //_TPIE_STATS_COLL_H
+34
View File
@@ -0,0 +1,34 @@
//
// File: tpie_stats_stream.h
// Authors: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: tpie_stats_stream.h,v 1.3 2004/08/17 16:48:25 jan Exp $
//
// Statistics for streams.
#ifndef _TPIE_STATS_STREAM_H
#define _TPIE_STATS_STREAM_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <tpie_stats.h>
#define TPIE_STATS_STREAM_COUNT 11
enum TPIE_STATS_STREAM {
BLOCK_READ = 0,
BLOCK_WRITE,
ITEM_READ,
ITEM_WRITE,
ITEM_SEEK,
STREAM_OPEN,
STREAM_CLOSE,
STREAM_CREATE,
STREAM_DELETE,
SUBSTREAM_CREATE,
SUBSTREAM_DELETE
};
typedef tpie_stats<TPIE_STATS_STREAM_COUNT> tpie_stats_stream;
#endif //_TPIE_STATS_STREAM_H
+37
View File
@@ -0,0 +1,37 @@
// Copyright (C) 2001 Octavian Procopiuc
//
// File: tpie_stats_tree.h
// Author: Octavian Procopiuc <tavi@cs.duke.edu>
//
// $Id: tpie_stats_tree.h,v 1.2 2003/04/17 20:07:23 jan Exp $
//
//
#ifndef _TPIE_STATS_TREE_H
#define _TPIE_STATS_TREE_H
// Get definitions for working with Unix and Windows
#include <portability.h>
#include <tpie_stats.h>
#define TPIE_STATS_TREE_COUNT 14
enum TPIE_STATS_TREE {
LEAF_FETCH = 0,
LEAF_RELEASE,
LEAF_READ,
LEAF_WRITE,
LEAF_CREATE,
LEAF_DELETE,
LEAF_COUNT,
NODE_FETCH,
NODE_RELEASE,
NODE_READ,
NODE_WRITE,
NODE_CREATE,
NODE_DELETE,
NODE_COUNT
};
typedef tpie_stats<TPIE_STATS_TREE_COUNT> tpie_stats_tree;
#endif // _TPIE_STATS_TREE_H

Some files were not shown because too many files have changed in this diff Show More